Source Code

The entire source code for VillageWars 1.7.1 is displayed below, organized alphabetically by filename. Because of this, this webpage is extremely long. Use the table of contents to find what you are looking for.

VillageWars is constantly undergoing updates, so the code shown here might not be the most recent edition. Do NOT try to play the game by copying and pasting the following into python files and running them. This will not work because these files need the images as well to work properly.

main.py

import os, sys, json
import subprocess


with open('preferences.json', 'r') as fo:
    preferences = json.loads(fo.read())
    py = preferences.get('py', 'python')
    path = preferences.get('path', [])
    sys.path.extend(path)



missing_modules = []
try:
    import pygame
except:
    missing_modules.append('pygame')
try:
    import requests
except:
    missing_modules.append('requests')
try:
    import bs4
except:
    missing_modules.append('beautifulsoup4')
try:
    import pymsgbox
except:
    missing_modules.append('pymsgbox')
try:
    import pyperclip
except:
    missing_modules.append('pyperclip')
try:
    import progress_bar
except:
    missing_modules.append('progress_bar')
try:
    import send2trash
except:
    missing_modules.append('send2trash')
try:
    import zipfile2
except:
    missing_modules.append('zipfile2')
try:
    import websockets
except:
    missing_modules.append('websockets')

try:
    fo = open('../version screenshots/version_info.json', 'r')
    fo.close()
except OSError:
    missing_modules.append('version_info.json')



os.chdir('src')
#pymsgbox.alert('*'.join(missing_modules))
#print(missing_modules)
if len(missing_modules) > 0:
    os.system(py + ' -m setup ' + '*'.join(missing_modules))

else:
    os.system(py + ' -m VillageWarsClient')
    input()

src

The src file folder contains most of the source code for VillageWars.

animations.py

import pygame

class Animation(pygame.sprite.Sprite):
    def __init__(self, item):
        super().__init__(self.gp)
        self.frame = 0
        self.len = 3
        self.server = (item.server if hasattr(item, 'server') else item.channel.server)
        self.size = 50
        self.x = item.x
        self.y = item.y
        
    def __len__(self):
        return self.len

    def update(self):
        self.frame += 1
        if len(self) == self.frame:
            self.kill()
        else:
            for p in self.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    rect = pygame.Rect(0, 0, self.size, self.size)
                    rect.center = (p.character.get_x(self), p.character.get_y(self))
                    if screen.colliderect(rect):
                        p.to_send.append({'action':'animation',
                                    'coords':(p.character.get_x(self), p.character.get_y(self)),
                                    'name':self.__class__.__name__,
                                    'frame':self.frame})


class Splash(Animation):
    def __init__(self, balloon):
        super().__init__(balloon)
        self.size = 30

class Explosion(Animation):
    def __init__(self, item):
        super().__init__(item)
        self.size = 64

class BAM(Animation):
    def __init__(self, TNT):
        super().__init__(TNT)
        self.size = 360
        self.len = 6

background.py

class Background():

    def __init__(self, server):
        self.server = server
        self.x = 0
        self.y = 0
        

    def update(self):

        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_setting', 'coords':(p.character.get_x(self), p.character.get_y(self))})

balloon.py

import pygame
import toolbox as t
from animations import *
import random as r


class Balloon(pygame.sprite.Sprite):
    def __init__(self, server, owner):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.server = server
        self.owner = owner
        self._owner = owner.channel.username
        self.speed = owner.balloon_speed
        self.x = owner.x
        self.y = owner.y
        self.angle = owner.angle
        self.damage = owner.attack
        self.knockback = owner.knockback

        self.image = pygame.image.load('../assets/balloon.png')
        self.type = ('normal' if self.damage < 20 else 'op')
        if self.owner.shot_speed < 8:
            self.type = ('speedy' if self.damage < 20 else 'speedy+op')


    def update(self):
        
        self.move()

        if self.alive():
            for p in self.server.players:
                if not p.pending:
                    p.to_send.append({'action':'draw_balloon',
                        'coords':(p.character.get_x(self), p.character.get_y(self)),
                        'angle':self.angle,
                        'type':self.type})

        self.test_collide()        

    def test_collide(self):
        result = False
        
        obstacles = self.server.obs

        if self.x > 7000 or self.x < -1000 or self.y > 4400 or self.y < -500:
            self.kill()
            return

        self.rect = self.image.get_rect(center=(self.x, self.y))

        for item in obstacles:
            if hasattr(item, 'image') and item.image == 'barrel' and not item.sedentary:
                continue
            try:
                if self.rect.colliderect(item.innerrect) and not (self.__class__ == Arrow  and item.__class__.__name__ == 'ArcheryTower') and not (self.__class__ == Arrow  and item.owner == self.owner):
                    try:
                        item.getHurt(self.damage, self.owner)
                    except:
                        item.getHurt(self.damage, self.owner, 0)
                    self.pop()
            except:
                pass
        for item in self.server.bushes:
            if self.rect.colliderect(item.innerrect) and not (self.__class__ == Arrow  and item.owner == self.owner):
                item.getHurt(self.damage, self.owner)
                self.pop()
                
                

                    
        for p in self.server.players:
            if not p.pending:
                if self.rect.colliderect(p.character.rect) and p != self.owner.channel and p.character.dead == False:
                    if self.__class__ == Arrow:
                        result = p.character.getHurt(self.damage, self._owner, self.angle, self.knockback, '<Victim> was shot by <Attacker>')
                    else:
                        result = p.character.getHurt(self.damage, self.owner, self.angle, self.knockback)
                    if result == 'repelled':
                        bb = type(self)(self.server, p.character)
                        bb.damage = self.owner.attack
                        bb.knockback = self.owner.knockback
                        bb.angle = self.angle + 180 + r.randint(-20, 20)
                        bb.speed = self.owner.balloon_speed
                        self.kill()
                        if hasattr(bb, 'bb'):
                            bb.bb.damage = self.owner.attack
                            bb.bb.knockback = self.owner.knockback
                            bb.bb.angle = self.angle + 180 + r.randint(-20, 20)
                            bb.bb.speed = self.owner.balloon_speed
                        return
                    else: 
                        self.pop()

                    
        for npc in self.server.NPCs:
            if npc.__class__.__name__ == 'Robot':
                if self.rect.colliderect(npc.rect) and not (self.owner == npc.player) and not npc.dead:
                    npc.getHurt(self.damage, self.angle, self.knockback)
                    self.pop()

        
        for npc in self.server.event.NPCs:
            if self.rect.colliderect(npc.rect):
                result = npc.getHurt(self.owner, self.damage, self.angle, self.knockback)
                if result and result[0] == 'repelled' and self.server.event.__class__.__name__ == 'BarbarianRaid':
                    BounceBalloon(self, result[1])
                    self.kill()
                else:
                    self.pop()   

    def move(self):
        dist = self.speed

        while dist > 16:

            x, y = t.getDir(self.angle, 16)
        
            self.x += x
            self.y += y

            dist -= 16
            
            self.test_collide()

            #print(self.alive())
            if not self.alive():
                return

        x, y = t.getDir(self.angle, dist)
        
        self.x += x
        self.y += y

    def pop(self):
        for p in self.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                screen.center = p.character.rect.center
                if screen.colliderect(self.rect):
                    if self.type == 'normal' or self.type == 'speedy':
                        p.to_send.append({'action':'sound', 'sound':'splash'})
                    elif self.type == 'op' or self.type == 'speedy+op':
                        p.to_send.append({'action':'sound', 'sound':'opsplash'})
                    
        Splash(self)
        self.kill()



class Arrow(Balloon):
    def __init__(self, server=None, tower=None):
        if tower.__class__.__name__ == 'Character':
            self.bb = Balloon(server=server, owner=tower)
            return None
        Balloon.__init__(self, tower.server, tower.owner)
        self.x = tower.innerrect.center[0]
        self.y = tower.innerrect.center[1]
        self.speed = tower.balloon_speed
        self.angle = tower.angle
        self.damage = tower.attack
        self.knockback = tower.knockback
        self._owner = tower.owner.channel.username + "'s Archery Tower"
        self.type = 'normal'

class Bolt(pygame.sprite.Sprite):
    def __init__(self, server=None, archer=None):
        pygame.sprite.Sprite.__init__(self, Balloon.gp)
        if archer.__class__.__name__ == 'Character':
            self.server = archer.channel.server
            self.speed = archer.balloon_speed
            self.x = archer.x
            self.y = archer.y
            self.angle = archer.angle
            self.damage = 10
            self.knockback = 50
            self.owner = archer
            self.archer = None
        else:
            self.server = archer.event.server
            self.speed = 17
            self.x = archer.x
            self.y = archer.y
            self.angle = archer.angle
            self.damage = 10
            self.knockback = 50
            self.owner = 'A Barbarian Archer'
            self.archer = archer

        self.image = pygame.image.load('../assets/balloon.png')


    def update(self):
        
        self.move()

        if self.alive():
            for p in self.server.players:
                if not p.pending:
                    p.to_send.append({'action':'draw_balloon',
                        'coords':(p.character.get_x(self), p.character.get_y(self)),
                        'angle':self.angle,
                        'type':'bolt'})

        self.test_collide()

    def test_collide(self):
        obstacles = self.server.obs
        
        if self.x > 7000 or self.x < -1000 or self.y > 4400 or self.y < -500:
            self.kill()
            return

        self.rect = self.image.get_rect(center=(self.x, self.y))

        for item in obstacles:
            if self.rect.colliderect(item.innerrect):
                item.getHurt(self.damage, self.owner)
                self.pop()
                
                
            elif item.isBuilding() and not (not self.archer and item.owner != self.owner.channel):
                if self.rect.colliderect(item.rect):
                    item.getHurt(self.damage, self.owner)
                    self.pop()

                    
        for p in self.server.players:
            if not (not self.archer and p.character == self.owner):  # If not the player shooting the bolt
                if not p.pending:
                    if self.rect.colliderect(p.character.rect) and p.character.dead == False:
                        if self.archer:
                            result = p.character.getHurt(self.damage, 'A Barbarian Archer', self.angle, self.knockback, msg='<Attacker> shot <Victim> and stole their gold and food.')
                            if p.character.dead and result != 'BAM':
                                self.archer.gold += p.character.gold
                                self.archer.food += p.character.food
                                p.character.gold = 0
                                p.character.food = 0

                            
                        else:
                            result = p.character.getHurt(self.damage, self.owner, self.angle, self.knockback, msg='<Attacker> shot <Victim> with a barbarian crossbow.')

                        if result == 'repelled':
                            bb = type(self)(archer=p.character)
                            bb.speed = 17
                            bb.damage = 10
                            bb.knockback = 50
                            bb.angle = self.angle + 180 + r.randint(-20, 20)
                            self.kill()
                            return
                        else: 
                            self.pop()

                    
        for npc in self.server.NPCs:
            if npc.__class__.__name__ == 'Robot' and not (self.archer and npc.player == self.owner):
                if self.rect.colliderect(npc.rect) and not npc.dead:
                    npc.getHurt(self.damage, self.angle, self.knockback)
                    self.pop()

        if self.server.event.__class__.__name__ != 'BarbarianRaid' or not self.archer:
            for npc in self.server.event.NPCs:
                if self.rect.colliderect(npc.rect):
                    npc.getHurt(self.owner, self.damage, self.angle, self.knockback)
                    self.pop()

    move = Balloon.move

    def pop(self):
        self.kill()

class BounceBalloon(pygame.sprite.Sprite):
    def __init__(self, old_self, angle):
        pygame.sprite.Sprite.__init__(self, Balloon.gp)
        self.server = old_self.server
        self.owner = 'A Barbarian'
        self.speed = old_self.speed
        self.x = old_self.x
        self.y = old_self.y
        self.angle = angle
        self.damage = old_self.damage
        self.knockback = old_self.knockback
        self.mock_owner = old_self.owner
        self.type = old_self.type

        self.image = pygame.image.load('../assets/balloon.png')


    def update(self):
        
        self.move()

        if self.alive():
            for p in self.server.players:
                if not p.pending:
                    p.to_send.append({'action':'draw_balloon',
                        'coords':(p.character.get_x(self), p.character.get_y(self)),
                        'angle':self.angle,
                        'type':self.type})

        self.test_collide()

        

    def test_collide(self):
        obstacles = self.server.obs

        if self.x > 7000 or self.x < -1000 or self.y > 4400 or self.y < -500:
            self.kill()
            return

        self.rect = self.image.get_rect(center=(self.x, self.y))

        for item in obstacles:
            if self.rect.colliderect(item.innerrect):
                item.getHurt(self.damage, self.owner)
                self.pop()
                
                
            elif item.isBuilding():
                if self.rect.colliderect(item.rect):
                    item.getHurt(self.damage, self.owner)
                    self.pop()

                    
                    
        for p in self.server.players:
            if not p.pending:
                if self.rect.colliderect(p.character.rect) and p.character.dead == False:
                    
                    result = p.character.getHurt(self.damage, self.owner, self.angle, self.knockback)

                    if result == 'repelled':
                        bb = Balloon(self.server, p.character)
                        bb.damage = self.damage
                        bb.knockback = self.knockback
                        bb.angle = self.angle + 180 + r.randint(-20, 20)
                        bb.speed = self.speed
                        self.kill()
                        return
                    else: 
                        self.pop()

                    
        for npc in self.server.NPCs:
            if npc.__class__.__name__ == 'Robot':
                if self.rect.colliderect(npc.rect) and not npc.dead:
                    npc.getHurt(self.damage, self.angle, self.knockback)
                    self.pop()

    move = Balloon.move

    def pop(self):
        for p in self.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                screen.center = p.character.rect.center
                if screen.colliderect(self.rect):
                    p.to_send.append({'action':'sound', 'sound':'splash'})
                    
        Splash(self)
        self.kill()

building.py

import pygame
import toolbox as t
import math
import random as r
from NPC import *
import os
#from InnPC import *

try:
    DISHES = len(os.listdir('../assets/meals')) - 2
except:
    DISHES = 1

class Building(pygame.sprite.Sprite):
    dimensions = (600, 600)
    def __init__(self, server, x, y, owner):
        '''
        owner must be client channel, not character.

        '''
        try:
            pygame.sprite.Sprite.__init__(self, self.gp)
        except:
            pygame.sprite.Sprite.__init__(self, pygame.sprite.Group())
        self.x = x
        self.y = y
        self.type = self.__class__.__qualname__
        self.server = server
        self.level = 1
        self.owner = owner
        self.max_health = self.health = 50
        self.state = 'alive'
        self.dimensions = type(self).dimensions

        self.p = pygame.Surface((200, 200))
        self.innerrect = self.p.get_rect(center=(x,y))
        self.rect = pygame.Surface((50, 50)).get_rect(midtop=(self.innerrect.midbottom[0], self.innerrect.midbottom[1] + 110))

        

    def post_init(self, rect):
        self.dimensions = type(self).dimensions
        self.dimensions = rect.size
        self.dim_rect = rect
        self.server.building_blocks.append(self.dim_rect)
        self.server.obs.append(self)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Number of non-broken buildings', str(len(channel.get_buildings()))),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.Out)],
                              'simp':['Heal (5 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              '4th_info':('Number of non-broken buildings', str(len(channel.get_buildings()))),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}
        

    def update(self):
        

        for p in self.server.players:
            if not p.pending:
                player = p.character
                angle = t.getAngle(self.rect.x, self.rect.y, player.x, player.y)

                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.dim_rect.size
                rect.topleft = (p.character.get_x(self.dim_rect), p.character.get_y(self.dim_rect))
                if screen.colliderect(rect):
                    p.to_send.append({'action':'draw_building',
                        'image':'house',
                        'coords':(player.get_x(self), player.get_y(self)),
                        'health':self.health,
                        'max_health':self.max_health,
                        'angle':angle,
                        'color':self.owner.color,
                        'dimensions':self.dimensions,
                        'type':self.type,
                        'state':self.state,
                        'level':self.level})



    def getHurt(self, damage, attacker):
        
        if self.health > 0 and attacker != self.owner.character:
            self.health -= damage
            if self.health < 1:
                self.state = 'broken'
                if attacker.__class__.__name__ == 'Character':
                    self.owner.message = attacker.channel.username + ' has broken one of your ' + self.type + 's.'
                    attacker.destroyed += 1
                else:
                    self.owner.message = attacker + ' has broken one of your ' + self.type + 's.'
                self.owner.message_count = 150
                self.owner.message_color = (255,0,0)
                self.health = 0

                self.die()
                for p in self.server.players:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.to_send.append({'action':'sound', 'sound':'building'})

    def die(self):
        pass

    def isBuilding(self):
        return True


    def Out(self, character):
        
        character.channel.in_window = False
        character.shoot_cooldown = 20
        character.channel.to_send.append({'action':'sound', 'sound':'cw'})


    def heal(self, player):
        if player != self.owner.character:
            player.channel.message = "You can't heal someone else's building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        
        elif player.food > 4:
            if self.health < self.max_health:
                self.health += 10
                if self.health > self.max_health:
                    self.health = self.max_health
                player.channel.message = 'You just healed this building 10 health for 5 food.'
                player.channel.message_count = 120
                player.channel.message_color = (255,205,0)
                player.food -= 5
            else:
                player.channel.message = 'This building is already at full health!'
                player.channel.message_count = 150
                player.channel.message_color = (50,50,255)
        else:
            player.channel.message = "You don't have enough food to heal this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)


    def clear(self, player):
        self.Out(player)
        if player.gold > 1:
            player.channel.message = 'Cleared building for 2 gold'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 2
            if self.dim_rect in self.server.building_blocks:
                self.server.building_blocks.remove(self.dim_rect)
            if self in self.server.obs:
                self.server.obs.remove(self)
            self.kill()
            
        else:
            player.channel.message = "You don't have enough gold to clear this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        



class CentralBuilding(Building):
    dimensions = (675, 550)
    def __init__(self, server, x, y, owner, rect=None):
        Building.__init__(self, server, x, y, owner)
        self.type = 'Central Building'
        self.max_health = self.health = 420
        self.dimensions = type(self).dimensions
        self.info = ('The Central Building is for buying other buildings.', 'If all your buildings are destroyed, you will not respawn.')

        if rect is None:
            rect = pygame.Rect(0, 0, 675, 550)
            rect.midtop = (x, y-round(self.p.get_height()/2))
        self.post_init(rect)


    def update(self):
        Building.update(self)
        if False: # Makes building move, lol
            self.dim_rect.x += 3
            self.rect.x += 3
            self.x += 3
            self.innerrect.x += 3

    
        
        
    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Number of non-broken buildings', str(len(channel.get_buildings()))),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.buy_central_building), (2, self.buy_fitness_center), (3, self.buy_balloonist), (4, self.buy_farmers_guild), (5, self.buy_miners_guild), (6, self.buy_construction_site), (7, self.buy_restaurant), (8, self.buy_inn), (9, self.Out)],
                              'simp':['Heal (5 food)', 'Portable Central Building (10 gold, 10 food)', 'Fitness Center (50 food)', 'Balloonist (30 gold)', "Farmer's Guild (25 food)", "Miner's Guild (25 gold)", 'Construction Site (60 gold)', 'Restaurant (80 food)', 'Inn (100 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              'health':(self.health, self.max_health),
                              'building':self,
                              '4th_info':('Number of non-broken buildings', str(len(channel.get_buildings()))),
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}

    def buy_central_building(self, player):
        if player.food > 9:
            if player.gold > 9:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = PortableCentralBuilding
                player.channel.message = 'You just bought a Portable Central Building for 10 gold and 10 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.gold -= 10
                player.food -= 10
            else:
                player.channel.message = "You don't have enough gold to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        
        else:
            if player.gold > 9:
                player.channel.message = "You don't have enough food to buy this!"
            else:
                player.channel.message = "You can't afford this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'

    def buy_fitness_center(self, player):
        if player.food > 49:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = FitnessCenter
            player.channel.message = 'You just bought a Fitness Center for 50 food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 50
        else:
            player.channel.message = "You don't have enough food to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_farmers_guild(self, player):
        if player.food > 24:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = FarmersGuild
            player.channel.message = "You just bought a Farmer's Guild for 25 food."
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 25
        else:
            player.channel.message = "You don't have enough food to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_miners_guild(self, player):
        if player.gold > 24:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = MinersGuild
            player.channel.message = "You just bought a Miner's Guild for 25 gold."
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 25
        else:
            player.channel.message = "You don't have enough gold to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'
        


    def buy_balloonist(self, player):
        if player.gold > 29:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = Balloonist
            player.channel.message = 'You just bought a Balloonist for 30 gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 30
        else:
            player.channel.message = "You don't have enough gold to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'

    def buy_construction_site(self, player):
        if player.gold > 59:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = ConstructionSite
            player.channel.message = 'You just bought a Construction Site for 60 gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 60
        else:
            player.channel.message = "You don't have enough gold to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'
    def buy_restaurant(self, player):
        if player.food > 79:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = Restaurant
            player.channel.message = 'You just bought a Restaurant for 80 food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 80
        else:
            player.channel.message = "You don't have enough food to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_inn(self, player):

        
        yes = True
        for b in player.channel.get_buildings():
            if b.type == 'Inn':
                yes = False
                player.channel.message = 'You already have an Inn! You can only have one at a time.'
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        if yes:
            if player.food > 99:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = Inn
                player.channel.message = 'You just bought an Inn for 100 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.food -= 100
            else:
                player.channel.message = "You don't have enough food to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'



    


class PortableCentralBuilding(Building):
    dimensions = (560, 560)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.type = 'Portable Central Building'
        self.max_health = self.health = 110
        self.dimensions = type(self).dimensions
        self.info = ('This building works the same way as the regular Central Building,', 'so you can use it as a portable central building.')

        self.post_init(rect)


    def update(self):
        Building.update(self)



    
        
        
    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Number of non-broken buildings', str(len(channel.get_buildings()))),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.buy_central_building), (2, self.buy_fitness_center), (3, self.buy_balloonist), (4, self.buy_farmers_guild), (5, self.buy_miners_guild), (6, self.buy_construction_site), (7, self.buy_restraunt), (8, self.buy_inn), (9, self.Out)],
                              'simp':['Heal (5 food)', 'Portable Central Building (10 gold, 10 food)', 'Fitness Center (50 food)', 'Balloonist (30 gold)', "Farmer's Guild (25 food)", "Miner's Guild (25 gold)", 'Construction Site (60 gold)', 'Restaurant (80 food)', 'Inn (100 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              'health':(self.health, self.max_health),
                              'building':self,
                              '4th_info':('Number of non-broken buildings', str(len(channel.get_buildings()))),
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}

    def buy_central_building(self, player):
        if player.food > 9:
            if player.gold > 9:
    
                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = PortableCentralBuilding
                player.channel.message = 'You just bought a Portable Central Building for 10 gold and 10 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.gold -= 10
                player.food -= 10
            else:
                player.channel.message = "You don't have enough gold to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        
        else:
            if player.gold > 9:
                player.channel.message = "You don't have enough food to buy this!"
            else:
                player.channel.message = "You can't afford this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'
    

    def buy_fitness_center(self, player):
        if player.food > 49:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = FitnessCenter
            player.channel.message = 'You just bought a Fitness Center for 50 food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 50
        else:
            player.channel.message = "You don't have enough food to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_inn(self, player):

        
        yes = True
        for b in player.channel.get_buildings():
            if b.type == 'Inn':
                yes = False
                player.channel.message = 'You already have an Inn! You can only have one at a time.'
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        if yes:
            if player.food > 99:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = Inn
                player.channel.message = 'You just bought an Inn for 100 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.food -= 100
            else:
                player.channel.message = "You don't have enough food to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_farmers_guild(self, player):
        if player.food > 24:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = FarmersGuild
            player.channel.message = "You just bought a Farmer's Guild for 25 food."
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 25
        else:
            player.channel.message = "You don't have enough food to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_miners_guild(self, player):
        if player.gold > 24:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = MinersGuild
            player.channel.message = "You just bought a Miner's Guild for 25 gold."
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 25
        else:
            player.channel.message = "You don't have enough gold to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'
        


    def buy_balloonist(self, player):
        if player.gold > 29:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = Balloonist
            player.channel.message = 'You just bought a Balloonist for 30 gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 30
        else:
            player.channel.message = "You don't have enough gold to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'

    def buy_construction_site(self, player):
        if player.gold > 59:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = ConstructionSite
            player.channel.message = 'You just bought a Construction Site for 60 gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 60
        else:
            player.channel.message = "You don't have enough gold to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'gold'
    def buy_restraunt(self, player):
        if player.food > 79:

            player.channel.text = 'Right click to place building'
            player.channel.build_to_place = Restaurant
            player.channel.message = 'You just bought a Restaurant for 80 food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 80
        else:
            player.channel.message = "You don't have enough food to buy this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'


class RunningTrack(Building):
    dimensions = (700, 620)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building increases your speed! Upgrade it to increase it', 'even more. (You can only have one Running Track at a time)')
        self.type = 'Running Track'
        self.health = self.max_health = 180
        self.dimensions = type(self).dimensions
        self.owner.character.speed += 3
        self.owner.character.moving += 3
        self.upgrade_cost = 90

        self.post_init(rect)

    def level_up(self, player):
        if self.level == 3:
            return
        if player.food > self.upgrade_cost - 1:
            self.level += 1
            self.owner.character.speed += 3
            self.owner.character.moving += 3
            player.channel.message = 'You upgraded this Running Track to level ' + str(self.level) + ' for ' + str(self.upgrade_cost) + ' food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= self.upgrade_cost
            self.upgrade_cost += 30
            self.health = self.max_health = self.max_health + 40
            
        else:
            player.channel.message = "You don't have enough food to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)

        
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            if self.level < 3:
                channel.window = {'text':self.info,
                                  '4th_info':('Speed', str(self.owner.character.speed)),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.level_up), (1, self.heal), (2, self.Out)],
                                  'simp':['Upgrade (' + str(self.upgrade_cost) + ' food)', 'Heal (5 food)', 'Out']}
            else:
                channel.window = {'text':self.info,
                                  '4th_info':('Speed', str(self.owner.character.speed)),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.heal), (1, self.Out)],
                                  'simp':['Heal (5 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              '4th_info':('Speed', str(self.owner.character.speed)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.heal), (2, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}

        
    def getHurt(self, damage, attacker):
        
        if self.health > 0 and attacker != self.owner.character:
            self.health -= damage
            if self.health < 1:
                self.state = 'broken'
                if attacker.__class__.__name__ == 'Character':
                    self.owner.message = attacker.channel.username + ' has broken one of your ' + self.type + 's.'
                    attacker.destroyed += 1
                else:
                    self.owner.message = attacker + ' has broken one of your ' + self.type + 's.'
                self.owner.message_count = 150
                self.owner.message_color = (255,0,0)
                self.health = 0

                self.die()
                for p in self.server.players:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.Send({'action':'sound', 'sound':'building'})



                
                self.owner.character.speed = 8
                self.owner.character.moving = 8
                
class Gym(Building):
    dimensions = (700, 620)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building increases your resistance! Upgrade it to increase it', 'even more. (You can only have one Gym at a time)')
        self.type = 'Gym'
        self.health = self.max_health = 180
        self.dimensions = type(self).dimensions
        self.owner.character.strength += 2
        
        self.upgrade_cost = 90

        self.post_init(rect)

    def level_up(self, player):
        if self.level == 3:
            return
        if player.food > self.upgrade_cost - 1:
            self.level += 1
            self.owner.character.strength += 2
            
            player.channel.message = 'You upgraded this Gym to level ' + str(self.level) + ' for ' + str(self.upgrade_cost) + ' food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= self.upgrade_cost
            self.upgrade_cost += 30
            self.health = self.max_health = self.max_health + 40
            
        else:
            player.channel.message = "You don't have enough food to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)

        
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            if self.level < 3:
                channel.window = {'text':self.info,
                                  '4th_info':('Resistance', str(self.owner.character.strength)),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.level_up), (1, self.heal), (2, self.Out)],
                                  'simp':['Upgrade (' + str(self.upgrade_cost) + ' food)', 'Heal (5 food)', 'Out']}
            else:
                channel.window = {'text':self.info,
                                  '4th_info':('Resistance', str(self.owner.character.strength)),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.heal), (1, self.Out)],
                                  'simp':['Heal (5 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                                  '4th_info':('Resistance', str(self.owner.character.strength)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}
            
    def getHurt(self, damage, attacker):
        
        if self.health > 0 and attacker != self.owner.character:
            self.health -= damage
            if self.health < 1:
                self.state = 'broken'
                if attacker.__class__.__name__ == 'Character':
                    self.owner.message = attacker.channel.username + ' has broken one of your ' + self.type + 's.'
                    attacker.destroyed += 1
                else:
                    self.owner.message = attacker + ' has broken one of your ' + self.type + 's.'
                self.owner.message_count = 150
                self.owner.message_color = (255,0,0)
                self.health = 0

                self.die()
                for p in self.server.players:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.Send({'action':'sound', 'sound':'building'})
                self.owner.character.strength = 0
                

class HealthCenter(Building):
    dimensions = (700, 620)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building increases your maximum health! Upgrade it to increase it', 'even more. (You can only have one Health Center at a time)')
        self.type = 'Health Center'
        self.health = self.max_health = 180
        self.dimensions = type(self).dimensions
        self.owner.character.max_health += 30
        self.owner.character.health += 30
        
        self.upgrade_cost = 90

        self.post_init(rect)

    def level_up(self, player):
        if self.level == 3:
            return
        if player.food > self.upgrade_cost - 1:
            self.level += 1
            self.owner.character.max_health += 30
            self.owner.character.health += 30
            
            player.channel.message = 'You upgraded this Health Center to level ' + str(self.level) + ' for ' + str(self.upgrade_cost) + ' food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= self.upgrade_cost
            self.upgrade_cost += 30
            self.health = self.max_health = self.max_health + 40
            
        else:
            player.channel.message = "You don't have enough food to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)

        
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            if self.level < 3:
                channel.window = {'text':self.info,
                                  '4th_info':('Player health', ('%s/%s' % (self.owner.character.health, self.owner.character.max_health))),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.level_up), (1, self.heal), (2, self.Out)],
                                  'simp':['Upgrade (' + str(self.upgrade_cost) + ' food)', 'Heal (5 food)', 'Out']}
            else:
                channel.window = {'text':self.info,
                                  '4th_info':('Player health', ('%s/%s' % (self.owner.character.health, self.owner.character.max_health))),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.heal), (1, self.Out)],
                                  'simp':['Heal (5 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                                  '4th_info':('Player health', ('%s/%s' % (self.owner.character.health, self.owner.character.max_health))),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}
            
    def getHurt(self, damage, attacker):
        
        if self.health > 0 and attacker != self.owner.character:
            self.health -= damage
            if self.health < 1:
                self.state = 'broken'
                if attacker.__class__.__name__ == 'Character':
                    self.owner.message = attacker.channel.username + ' has broken one of your ' + self.type + 's.'
                    attacker.destroyed += 1
                else:
                    self.owner.message = attacker + ' has broken one of your ' + self.type + 's.'
                self.owner.message_count = 150
                self.owner.message_color = (255,0,0)
                self.health = 0

                self.die()
                for p in self.server.players:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.Send({'action':'sound', 'sound':'building'})
                self.owner.character.max_health = 100
                if self.owner.character.health > 100:
                    self.owner.character.health = 100
                




class FitnessCenter(Building):
    dimensions = (585, 600)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building lets you buy the 3 fitness', 'buildings: Running Track, Gym and Health Center.')
        self.type = 'Fitness Center'
        self.health = self.max_health = 140
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def buy_running_track(self, player):
        yes = True
        for b in player.channel.get_buildings():
            if b.type == 'Running Track':
                yes = False
                player.channel.message = 'You already have a Running Track! You can only have one at a time.'
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
                

        if yes:
            if player.food > 74:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = RunningTrack
                player.channel.message = 'You just bought a Running Track for 75 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.food -= 75
            else:
                player.channel.message = "You don't have enough food to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_gym(self, player):
        yes = True
        for b in player.channel.get_buildings():
            if b.type == 'Gym':
                yes = False
                player.channel.message = 'You already have a Gym! You can only have one at a time.'
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
                

        if yes:
            if player.food > 74:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = Gym
                player.channel.message = 'You just bought a Gym for 75 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.food -= 75
            else:
                player.channel.message = "You don't have enough food to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'

    def buy_health_center(self, player):
        yes = True
        for b in player.channel.get_buildings():
            if b.type == 'Health Center':
                yes = False
                player.channel.message = 'You already have a Health Center! You can only have one at a time.'
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
                

        if yes:
            if player.food > 74:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = HealthCenter
                player.channel.message = 'You just bought a Health Center for 75 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.food -= 75
            else:
                player.channel.message = "You don't have enough food to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        self.Out(player)
        player.spence = 'food'


    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Number of fitness buildings', ('%s/3' % len([b for b in self.owner.get_buildings() if b.type in ('Health Center', 'Gym', 'Running Track')]))),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.buy_running_track), (2, self.buy_gym), (3, self.buy_health_center), (4, self.Out)],
                              'simp':['Heal (5 food)', 'Running Track (75 food)', 'Gym (75 food)', 'Health Center (75 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Number of fitness buildings', ('%s/3' % len([b for b in self.owner.get_buildings() if b.type in ('Health Center', 'Gym', 'Running Track')]))),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}
                              


class Balloonist(Building):
    dimensions = (500, 495)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building lets you upgrade your attack stats.', '')
        self.type = 'Balloonist'
        self.health = self.max_health = 120
        self.dimensions = type(self).dimensions
        self.upgrade_cost = 50


        self.post_init(rect)

    def level_up(self, player):
        if player.gold > self.upgrade_cost - 1:
            self.level += 1
            
            
            player.channel.message = 'You upgraded this Balloonist to level ' + str(self.level) + ' for ' + str(self.upgrade_cost) + ' gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= self.upgrade_cost
            self.upgrade_cost += 20
            self.health = self.max_health = self.max_health + 50
            
        else:
            player.channel.message = "You don't have enough gold to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Attack Damage', str(self.owner.character.attack)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.level_up), (1, self.heal), (2, self.upgrade_balloon_damage), (3, self.upgrade_balloon_speed), (4, self.Out)],
                              'simp':['Upgrade (' + str(self.upgrade_cost) + ' gold)', 'Heal (5 food)', ' + Balloon Attack (' + str(channel.character.damage_cost) + ' gold)', ' + Balloon Speed (' + str(channel.character.speed_cost) + ' gold)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              '4th_info':('Attack Damage', str(self.owner.character.attack)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}

    def upgrade_balloon_speed(self, player):
        if player.gold > player.speed_cost - 1:
                
            player.balloon_speed += 3
            player.knockback += 8
            
            player.channel.message = "You upgraded your balloons' speed for " + str(self.owner.character.speed_cost) + " gold."
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= player.speed_cost

            player.speed_cost += 2

            
        else:
            player.channel.message = "You don't have enough gold to upgrade your balloons' speed!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    
    def upgrade_balloon_damage(self, player):
        if player.gold > player.damage_cost - 1:
            
            player.attack += 1
            
            player.channel.message = "You upgraded your attack for " + str(self.owner.character.damage_cost) + " gold."
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= player.damage_cost

            player.damage_cost += 10

            
        else:
            player.channel.message = "You don't have enough gold to upgrade your attack!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)




class FarmersGuild(Building):
    dimensions = (600, 500)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building sends farmers out to collect wheat for you.', '')
        self.type = "Farmer's Guild"
        self.health = self.max_health = 140
        self.dimensions = type(self).dimensions
        self.farmer = Farmer(self)

        self.post_init(rect)

    def getHurt(self, damage, attacker):
        
        if self.health > 0 and attacker != self.owner.character:
            self.health -= damage
            if self.health < 1:
                self.state = 'broken'
                if attacker.__class__.__name__ == 'Character':
                    self.owner.message = attacker.channel.username + ' has broken one of your ' + self.type + 's.'
                    attacker.destroyed += 1
                else:
                    self.owner.message = attacker + ' has broken one of your ' + self.type + 's.'
                self.owner.message_count = 150
                self.owner.message_color = (255,0,0)
                self.health = 0

                self.die()
                for p in self.server.players:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.Send({'action':'sound', 'sound':'building'})
                for farmer in self.server.NPCs:
                    if farmer.building == self:
                        farmer.kill()
                
    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Food Production Rate', str(1250 - self.farmer.farm.production)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.Out)],
                              'simp':['Heal (5 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              '4th_info':('Food Production Rate', str(1250 - self.farmer.farm.production)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}

    




class MinersGuild(Building):
    dimensions = (500, 600)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building sends miners out to mine gold for you.', '')
        self.type = "Miner's Guild"
        self.health = self.max_health = 140
        self.dimensions = type(self).dimensions
        self.miner = Miner(self)
        

        self.post_init(rect)

    def getHurt(self, damage, attacker):
        
        if self.health > 0 and attacker != self.owner.character:
            self.health -= damage
            if self.health < 1:
                self.state = 'broken'
                if attacker.__class__.__name__ == 'Character':
                    self.owner.message = attacker.channel.username + ' has broken one of your ' + self.type + 's.'
                    attacker.destroyed += 1
                else:
                    self.owner.message = attacker + ' has broken one of your ' + self.type + 's.'
                self.owner.message_count = 150
                self.owner.message_color = (255,0,0)
                self.health = 0

                self.die()
                for p in self.server.players:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.Send({'action':'sound', 'sound':'building'})
                for miner in self.server.NPCs:
                    if miner.building == self:
                        miner.kill()
                

    


    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Gold Discovery Rate', str(1250 - self.miner.farm.production)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.Out)],
                              'simp':['Heal (5 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              '4th_info':('Gold Discovery Rate', str(1250 - self.miner.farm.production)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}



        



def builder(b, p, has_builder):
    if b.isBuilding(object()):
        dimensions = b.dimensions
        if has_builder:
            dimensions = (b.dimensions[0]*0.8, b.dimensions[1]*0.8)
        x, y = p.x, p.y - 140
        image = pygame.Surface((200, 200))
        width, height = dimensions
        rect = pygame.Rect(0, 0, width, height)
        rect.midtop = (x, y-round(image.get_height()/2))

    else:
        x, y = p.x, p.y - 240
        surf = pygame.Surface((360, 360))
        rect = surf.get_rect(center=(x, y))

    if rect.left < 0 or rect.right > 6000 or rect.top < 0 or rect.bottom > 3900:
        return False, rect
    
    
    
    for obs in p.channel.server.building_blocks:
        if rect.colliderect(obs):
            return False, rect
    return True, rect



class ConstructionSite(Building):
    dimensions = (560, 560)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building lets you buy defences for your village. Crates, Gates, Archery Towers,', 'Robot Factories...')
        self.type = 'Construction Site'
        self.health = self.max_health = 160
        self.dimensions = type(self).dimensions
        self.upgradable = True

        self.post_init(rect)

    def buy_crate(self, player):
        if player.food > 1:
            player.channel.message = 'You bought a crate for 2 food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 2
            player.crates += 1
        else:
            player.channel.message = "You don't have enough food for this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        
        self.Out(player)

    def buy_gate(self, player):
        if player.gold > 39:
            player.channel.message = 'You bought a gate for 40 gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 40
            player.channel.text = 'Press x to place gate'
        else:
            player.channel.message = "You don't have enough gold for this!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        
        self.Out(player)

    def buy_tnt(self, player):
        if player.gold > 99:
            if player.food > 99:
                player.channel.message = 'You bought TNT for 100 gold and 100 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.gold -= 100
                player.food -= 100
                player.channel.text = 'Press x to place TNT'
            else:
                player.channel.message = "You don't have enough food for this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        else:
            if player.food > 99:
                player.channel.message = "You don't have enough gold for this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
            else:
                player.channel.message = "You can't afford this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        
        self.Out(player)

    def buy_archery_tower(self, player):
        if player.food > 19:
            if player.gold > 109:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = ArcheryTower
                player.channel.message = 'You just bought an Archery Tower for 110 gold and 20 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.gold -= 110
                player.food -= 20
                player.spence = 'gold'
            else:
                player.channel.message = "You don't have enough gold to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        
        else:
            if player.gold > 109:
                player.channel.message = "You don't have enough food to buy this!"
            else:
                player.channel.message = "You can't afford an Archery Tower!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def level_up(self, player):
        if player.gold > 29:
            self.level = 2
            
            
            player.channel.message = 'You upgraded this Construction Site to level 2 for 30 gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 30
            self.upgradable = False
            self.health = self.max_health = 200
            
        else:
            player.channel.message = "You don't have enough gold to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def buy_robot_factory(self, player):
        if player.food > 4:
            if player.gold > 114:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = RobotFactory
                player.channel.message = 'You just bought a Robot Factory for 115 gold and 5 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.gold -= 115
                player.food -= 5
                player.spence = 'gold'
            else:
                player.channel.message = "You don't have enough gold to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        
        else:
            if player.gold > 114:
                player.channel.message = "You don't have enough food to buy this!"
            else:
                player.channel.message = "You can't afford this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def buy_barrel_maker(self, player):
        if player.food > 64:
            if player.gold > 49:

                player.channel.text = 'Right click to place building'
                player.channel.build_to_place = BarrelMaker
                player.channel.message = 'You just bought a Barrel Maker for 50 gold and 65 food.'
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.gold -= 50
                player.food -= 65
                player.spence = 'food'
            else:
                player.channel.message = "You don't have enough gold to buy this!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        
        else:
            if player.gold > 49:
                player.channel.message = "You don't have enough food to buy this!"
            else:
                player.channel.message = "You can't afford this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        



    def open_window(self, channel):
        if self.state == 'alive':
            if self.upgradable:
                channel.in_window = True
                channel.window = {'text':self.info,
                              '4th_info':('Upgraded', 'False'),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.level_up), (1, self.heal), (2, self.buy_crate), (3, self.buy_gate), (4, self.buy_tnt), (5, self.Out)],
                              'simp':['Upgrade (30 gold)', 'Heal (5 food)', 'Crate (2 food)', 'Gate (40 gold)', 'TNT (100 gold, 100 food)', 'Out']}
            else:
                channel.in_window = True
                channel.window = {'text':self.info,
                              '4th_info':('Upgraded', 'True'),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.buy_crate), (2, self.buy_gate), (3, self.buy_tnt), (4, self.buy_archery_tower), (5, self.buy_robot_factory), (6, self.buy_barrel_maker), (7, self.Out)],
                              'simp':['Heal (5 food)', 'Crate (2 food)', 'Gate (40 gold)', 'TNT (100 gold, 100 food)', 'Archery Tower (110 gold, 20 food)', 'Robot Factory (115 gold, 5 food)', 'Barrel Maker (50 gold, 65 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              '4th_info':('Upgraded', str(self.level == 2)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}




class Restaurant(Building):
    dimensions = (600, 580)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building lets you buy meals, which heal you when you eat them.', '')
        self.type = 'Restaurant'
        self.health = self.max_health = 220
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def buy_meal(self, player):

        if player.food > 11:
                if player.meal:
                    player.channel.message = "You already have a meal! Eat it before buying another one."
                    player.channel.message_count = 150
                    player.channel.message_color = (0,0,255)

                else:
                    player.meal = True
                    player.meal_type = r.randrange(DISHES)
                    player.channel.message = "You bought a meal for 12 food."
                    player.channel.message_count = 150
                    player.channel.message_color = (255,205,0)
                    player.food -= 12



            
        else:
            player.channel.message = "You don't have enough food to buy a meal!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def dine_in(self, player):

        if player.food > 5:
                if player.meal:
                    player.channel.message = "You already have a meal! Eat it before ordering one."
                    player.channel.message_count = 150
                    player.channel.message_color = (0,0,255)

                else:
                    player.channel.message = "You ate a meal for 6 food."
                    player.channel.message_count = 150
                    player.channel.message_color = (255,205,0)
                    player.food -= 6
                    player.dine_in()



            
        else:
            player.channel.message = "You don't have enough food to order a meal!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)



    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Need meal', ('no' if channel.character.meal else 'yes')),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.buy_meal), (2, self.dine_in), (3, self.Out)],
                              'simp':['Heal (5 food)', 'Take out (12 food)', 'Order meal (6 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Need meal', ('no' if channel.character.meal else 'yes')),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}


class RobotFactory(Building):
    dimensions = (504, 500)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building sends out several little robots who attack', 'other players. It\'s a good way to defend your village!')
        self.type = 'Robot Factory'
        self.health = self.max_health = 120
        self.dimensions = type(self).dimensions

        self.post_init(rect)

        Robot(self)
        Robot(self)
        Robot(self)

        self.num_bots = 3

    def level_up(self, player):
        if player.gold > 39:
            self.level  += 1
            
            
            player.channel.message = 'You upgraded this Robot Factory to level ' + str(self.level) + ' for 40 gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= 40
            self.upgradable = False
            if self.max_health < 320: self.max_health += 50
            self.health = self.max_health

            Robot(self)
            self.num_bots += 1
            
        else:
            player.channel.message = "You don't have enough gold to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Robots', str(self.num_bots)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.level_up), (2, self.Out)],
                              'simp':['Heal (5 food)', 'Upgrade (40 gold)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              'health':(self.health, self.max_health),
                              'building':self,
                              '4th_info':('Robots', str(self.num_bots)),
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}

    def die(self):
        for npc in self.server.NPCs:
            if type(npc).__name__ == 'Robot' and npc.factory == self:
                npc.kill()

from InnPC import *

class Inn(Building):
    dimensions = (550, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building welcomes different NPCs that can trade with you.', '')
        self.type = 'Inn'
        self.health = self.max_health = 240
        self.dimensions = type(self).dimensions
        self.NPC = None
        self.count = r.randint(250, 750)
        

        self.post_init(rect)


    def level_up(self, player):
        if player.food > 99:
            self.level = 2
            
            
            player.channel.message = 'You upgraded this Inn to level 2 for 100 food.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.food -= 100
            self.upgradable = False
            self.max_health = 340
            self.health = 340
            
        else:
            player.channel.message = "You don't have enough food to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            if self.NPC:
                if self.level == 1:
                    channel.window = {'text':self.info,
                                          '4th_info':('Hosting', self.NPC.type),
                                          'health':(self.health, self.max_health),
                                          'building':self,
                                          'level':self.level,
                                          'options':[(0, self.heal), (1, self.level_up), (2, self.kick_npc), (3, self.build_house), (4, self.Out)],
                                          'simp':['Heal (5 food)', 'Upgrade (100 food)', 'Kick %s (free)' % (self.NPC.type), 'Build the %s a house (%s gold, %s food)' % (self.NPC.type, self.NPC.cost[0], self.NPC.cost[1]), 'Out']}
                else:
                    channel.window = {'text':self.info,
                                          '4th_info':('Hosting', self.NPC.type),
                                          'health':(self.health, self.max_health),
                                          'building':self,
                                          'level':self.level,
                                          'options':[(0, self.heal), (1, self.kick_npc), (2, self.build_house), (3, self.Out)],
                                          'simp':['Heal (5 food)', 'Kick %s (free)' % (self.NPC.type), 'Build the %s a house (%s gold, %s food)' % (self.NPC.type, self.NPC.cost[0], self.NPC.cost[1]), 'Out']}
            else:
                if self.level == 1:
                    channel.window = {'text':self.info,
                                      '4th_info':('Hosting', 'Nobody'),
                                      'health':(self.health, self.max_health),
                                      'building':self,
                                      'level':self.level,
                                      'options':[(0, self.heal), (1, self.level_up), (2, self.Out)],
                                      'simp':['Heal (5 food)', 'Upgrade (100 food)', 'Out']}
                else:
                    channel.window = {'text':self.info,
                                      '4th_info':('Hosting', 'Nobody'),
                                      'health':(self.health, self.max_health),
                                      'building':self,
                                      'level':self.level,
                                      'options':[(0, self.heal), (1, self.Out)],
                                      'simp':['Heal (5 food)', 'Out']}
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              'health':(self.health, self.max_health),
                              'building':self,
                              '4th_info':('Hosting', ('Nobody' if self.NPC is None else self.NPC.type)),
                              'level':self.level,
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}

    def update(self):
        super().update()
        if self.state != 'broken':
            if self.NPC is not None:
                self.NPC.update()
            else:
                if self.server.event.__class__.__name__ != 'BarbarianRaid':
                    self.count -= 1
                    if self.count == 0:
                        self.NPC = get_innpc(self)
                

    def kick_npc(self, player):
        if player.channel == self.owner:
            name = self.NPC.type
            self.NPC.depart()
            player.channel.message = 'You have kicked the %s.' % name
            player.channel.message_color = (128,0,128)
        else:
            player.channel.message = "This isn't your Inn!"
            player.channel.message_count = 160
            player.channel.message_color = (255,0,0)
        self.Out(player)

    def build_house(self, player):
        if player == self.owner.character:
            if player.food > self.NPC.cost[1]:
                if player.gold > self.NPC.cost[0]:

                    player.channel.text = 'Right click to place building'
                    player.channel.build_to_place = self.NPC.building
                    NPC = self.NPC
                    self.NPC.depart()
                    player.channel.message = 'You just bought the %s a house for %s gold and %s food.' % (NPC.type, NPC.cost[0], NPC.cost[1])
                    player.channel.message_count = 150
                    player.channel.message_color = (255,205,0)
                    player.gold -= NPC.cost[0]
                    player.food -= NPC.cost[1]
                    player.spence = ('gold' if NPC.cost[0] >= NPC.cost[1] else 'food')
                else:
                    player.channel.message = "You don't have enough gold to buy this!"
                    player.channel.message_count = 150
                    player.channel.message_color = (255,0,0)
                
            else:
                if player.gold > self.NPC.cost[0]:
                    player.channel.message = "You don't have enough food to buy this!"
                else:
                    player.channel.message = "You can't afford this building!"
                player.channel.message_count = 150
                player.channel.message_color = (255,0,0)
        else:
            player.channel.message = "This isn't your Inn!"
            player.channel.message_count = 160
            player.channel.message_color = (255,0,0)
        self.Out(player)
        
           
class BarrelMaker(Building):
    dimensions = (670, 440)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building lets you buy barrels!! Z to hide. Z+Click to throw.', '(: (: (: (: (: (: (: (:')
        self.type = 'Barrel Maker'
        self.health = self.max_health = 250
        
        self.upgrade_cost = 100

        self.post_init(rect)

    def level_up(self, player):
        if player.gold > self.upgrade_cost - 1:
            self.level += 1
            player.channel.message = 'You upgraded this Barrel Maker to level ' + str(self.level) + ' for ' + str(self.upgrade_cost) + ' gold.'
            player.channel.message_count = 150
            player.channel.message_color = (255,205,0)
            player.gold -= self.upgrade_cost
            self.upgrade_cost += 20
            if self.level < 15:
                self.health = self.max_health = self.max_health + 20
            
        else:
            player.channel.message = "You don't have enough gold to upgrade this building!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)

        
        self.Out(player)

    def buy_barrel(self, player):

        if player.food > 16:
            if player.barrel_health > 0:
                player.channel.message = "You already have a barrel! Get rid of it before buying a new one."
                player.channel.message_count = 150
                player.channel.message_color = (0,0,255)

            else:
                player.channel.message = "You bought a barrel! (17 food)"
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.food -= 17
                player.barrel_health = player.barrel_max_health = 10 + (5 * self.level)
                player.explosive = False



            
        else:
            player.channel.message = "You don't have enough food to buy a barrel!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)
        
    def buy_barrel_tnt(self, player):

        if player.gold > 149:
            if player.barrel_health > 0:
                player.channel.message = "You already have a barrel! Get rid of it before buying a NEW one."
                player.channel.message_count = 150
                player.channel.message_color = (0,0,255)

            else:
                player.channel.message = "You bought an explosive barrel! (150 gold)"
                player.channel.message_count = 150
                player.channel.message_color = (255,205,0)
                player.gold -= 150
                player.barrel_health = player.barrel_max_health = 10 + (5 * self.level)
                player.explosive = True



            
        else:
            player.channel.message = "You don't have enough gold to buy this quality barrel!"
            player.channel.message_count = 150
            player.channel.message_color = (255,0,0)
        self.Out(player)



    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Barrel Health', str(channel.character.barrel_health)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.level_up), (1, self.heal), (2, self.buy_barrel), (3, self.buy_barrel_tnt), (4, self.Out)],
                              'simp':['Upgrade (%s gold)' % self.upgrade_cost, 'Heal (5 food)', 'Barrel (17 food)', 'Explosive Barrel (150 gold)', 'Out']}
            
        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'upgradable':False,
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Barrel Health', str(channel.character.barrel_health)),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 gold)', 'Out']}            


                
            
__ALL__ = ['CentralBuilding', 'PortableCentralBuilding', 'FitnessCenter', 'Balloonist', 'Gym', 'RunningTrack', 'HealthCenter', 'BarrelMaker', 'ConstructionSite', 'RobotFactory', 'Restaurant', 'MinersGuild', 'FarmersGuild', 'Inn']

events.py

import pygame
from random import *
import toolbox as t
from balloon import Bolt
import InnPC

class Event():
    def __init__(self, server):
        self.server = server
        self.NPCs = []
    def update(self):
        pass


class BarbarianRaid():
    def __init__(self, server, num=None, spawn=None):
        self.server = server
        self.server.event = self

        for p in server.players:
            for b in p.get_buildings():
                if b.__class__.__name__ == 'Inn':
                    if b.NPC:
                        b.NPC.depart()
        if not num:
            self.num_barbarians = randint(4, 7)
            for p in self.server.playing:
                self.num_barbarians += 2
        else:
            self.num_barbarians = num
        if spawn is None:
            self.spawn = choice(['North', 'East', 'South', 'West'])
        else:
            self.spawn = spawn
        for p in self.server.players:
            p.message = 'A tribe of %s barbarians is aproaching from the %s!' % (self.num_barbarians, self.spawn)
            p.message_color = (128,0,128)
            p.message_count = 160
        
        self.barbarians = []
        self.NPCs = self.barbarians
        self.leader = BarbarianLeader(self)
        for b in range(self.num_barbarians - 1):
            Barbarian(self)

        for p in self.server.players:
            if not p.pending:
                p.Send({'action':'music_change', 'music':'barbarianraid'})

    def end(self, attacker=None):
        self.barbarians.clear()
        for p in self.server.players:
            if not p.pending:
                p.Send({'action':'music_change', 'music':'village'})
        if attacker:
            for b in attacker.channel.get_buildings():
                if b.__class__.__name__ == 'Inn':
                    npcs = [InnPC.Retired_barbarian, InnPC.Adventurer]
                    bds = attacker.channel.get_buildings()
                    for NPC in npcs[:]:
                        if NPC.building in [bd.__class__ for bd in bds]:
                            npcs.remove(NPC)
                    if npcs:
                        b.NPC = choice(npcs)(b)
        self.server.event = Event(self.server)

    def get_coords(self, r=240):
        coords = {'North':(3000, 400),
                'South':(3000, 3500),
                'East':(5600, 1800),
                'West':(400, 1800)
                }
        x, y = coords[self.spawn]

        starting = True
        while x < 50 or x > 5950 or y < 50 or y > 3850 or starting:
            x += randint(-r, r)
            y += randint(-r, r)
            starting = False

        
            

        for b in self.barbarians:
            if b.rect.colliderect(b.surf.get_rect(center=(x, y))):
                return self.get_coords(r=r+100)

        return x, y

    def update(self):
        if True:#try:
            for b in self.barbarians:
                b.update()
        #except:
            #self.end()
    

def Barbarian(event):
    eval(choice(['BarbarianArcher', 'BarbarianSwordsman', 'BarbarianSwordsman']) + '(event)')


class BarbarianLeader():
    def __init__(self, event):
        self.hurt = 0
        self.shield_count = 0
        self.shield_angle = 0
        self.speed = 2
        self.resistance = 2
        self.health = 100
        self.angle = 0
        self.touching = False
        
        self.event = event
        self.surf = pygame.Surface((50, 50))
        self.rect = self.surf.get_rect(center=(0, 0))
        self.x, self.y = self.event.get_coords()
        self.event.barbarians.append(self)
        self.surf = pygame.Surface((50, 50))
        self.rect = self.surf.get_rect(center=(self.x, self.y))

        for p in self.event.server.playing:
            self.resistance += 1.5

    @property
    def innerrect(self):
        return self.rect

    def explode(self, player, tnt):
        self.getHurt(player, 60, t.getAngle(tnt.x, tnt.y, self.x, self.y), 120)
        
    def update(self):
        if self.hurt:
            self.hurt -= 1
        if self.shield_count:
            self.shield_count -= 1

        

        touching = False
        for ob in self.event.server.obs:
            if self.rect.colliderect(ob.innerrect):
                touching = True
                if not self.touching:
                    self.getHurt(None, 12, self.angle + 180, 100)
        self.touching = touching
            

        maxdist = 10000
        player = None
        playerdist = maxdist
        for p in self.event.server.players:
            if not p.pending and not p.character.dead:
                if t.getDist(self.x, self.y, p.character.x, p.character.y) < maxdist:
                    maxdist = t.getDist(self.x, self.y, p.character.x, p.character.y)
                    player = p
                    playerdist = maxdist

        if player == None:
            for p in self.event.server.players:
                if not p.pending:
                    p.to_send.append({'action':'draw_barbarian',
                                  'type':'leader',
                                  'hurt':self.hurt,
                                  'coords':(p.character.get_x(self), p.character.get_y(self)),
                                  'angle':self.angle,
                                  'health':self.health,
                                  'shield':(self.shield_angle if self.shield_count else None)})
            return

        self.angle = t.getAngle(self.x, self.y, player.character.x, player.character.y)

        if playerdist < 400:
            self.speed = 1
        else:
            self.speed = 2

        x, y = t.getDir(self.angle, total=self.speed)

        self.x += x
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.event.server.obs + self.event.barbarians:
            if rect.colliderect(ob.innerrect) and ob != self:
                if not self.touching:
                    self.x -= x
        self.y += y
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.event.server.obs + self.event.barbarians:
            if rect.colliderect(ob.innerrect) and ob != self:
                if not self.touching:
                    self.y -= y

        self.rect = self.surf.get_rect(center=(self.x, self.y))



        for p in self.event.server.players:
            if not p.pending:
                if p.character.rect.colliderect(self.rect) and not p.character.dead:
                    result = p.character.getHurt(10, 'The Barbarian Leader', self.angle, 50)
                    if p.character.dead and result != 'BAM':
                        p.character.gold = 0
                        p.character.food = 0
        
        for p in self.event.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_barbarian',
                                  'type':'leader',
                                  'hurt':self.hurt,
                                  'coords':(p.character.get_x(self), p.character.get_y(self)),
                                  'angle':self.angle,
                                  'health':self.health,
                                  'shield':(self.shield_angle if self.shield_count else None)})
                
    def getHurt(self, attacker, damage, angle, knockback):
        if randint(0, 2) < 2 and attacker != self:
            self.shield_angle = angle + 180
            self.shield_count = 20
            for p in self.event.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    rect = pygame.Rect(0, 0, 1, 1)
                    rect.size = self.rect.size
                    rect.topleft = (p.character.get_x(self.rect), p.character.get_y(self.rect))
                    if screen.colliderect(rect):
                        p.to_send.append({'action':'sound','sound':'bump'})
            return ('repelled', self.shield_angle + randint(-20, 20))
        else:
            x, y = t.getDir(angle, total=(self.speed+knockback))

            self.x += x
            rect = self.surf.get_rect(center=(self.x, self.y))
            for ob in self.event.server.obs + self.event.barbarians:
                if rect.colliderect(ob.innerrect) and ob != self:
                    if not self.touching:
                        self.x -= x
            self.y += y
            rect = self.surf.get_rect(center=(self.x, self.y))
            for ob in self.event.server.obs + self.event.barbarians:
                if rect.colliderect(ob.innerrect) and ob != self:
                    if not self.touching:
                        self.y -= y

            self.hurt = 10

            self.rect = self.surf.get_rect(center=(self.x, self.y))

            self.health -= damage - self.resistance
            if self.health <= 0:
                if self in self.event.barbarians:
                    self.event.barbarians.remove(self)
                self.health = 0
                self.event.end(attacker)
                self.event.server.event = Event(self.event.server)
                for p in self.event.server.players:
                    if not p.pending:
                        p.message = 'The Barbarian tribe has been defeated.'
                        p.message_color = (128,0,128)
                        p.message_count = 160
                try:
                    attacker.gold += 300
                    attacker.food += 300
                except:
                    pass


class BarbarianArcher():
    
    def __init__(self, event):
        self.hurt = 0
        self.shield_count = 0
        self.shield_angle = 0
        self.gold = 50
        self.food = 50
        self.attack = 10
        self.knockback = 40
        self.balloon_speed = 17
        self.shoot_cooldown = 0
        self.speed = 2
        self.health = 100
        self.angle = 0
        
        self.event = event
        self.surf = pygame.Surface((50, 50))
        self.event.barbarians.append(self)
        self.rect = self.surf.get_rect(center=(0, 0))
        self.x, self.y = self.event.get_coords()
        self.surf = pygame.Surface((50, 50))
        self.rect = self.surf.get_rect(center=(self.x, self.y))
        
        

    @property
    def innerrect(self):
        return self.rect

    def explode(self, player, tnt):
        self.getHurt(player, 60, t.getAngle(tnt.x, tnt.y, self.x, self.y), 120)
        
    def update(self):
        if self.hurt:
            self.hurt -= 1
        if self.shield_count:
            self.shield_count -= 1
        if self.shoot_cooldown:
            self.shoot_cooldown -= 1

        

        for ob in self.event.server.obs:
            if self.rect.colliderect(ob.innerrect):
                self.x = self.event.leader.x
                self.y = self.event.leader.y
                condition = True
                while condition:
                    condition = False
                    x, y = t.getDir(self.angle, total=self.speed)
                    self.x += x
                    self.y += y
                    self.rect = self.surf.get_rect(center=(self.x, self.y))
                    
                    for ob in self.event.server.obs + self.event.barbarians:
                        if self.rect.colliderect(ob.innerrect) and ob != self:
                            condition = True
                    

                    
            
        maxdist = 10000
        playerdist = maxdist
        player = None
        for p in self.event.server.players:
            if not p.pending and not p.character.dead:
                if t.getDist(self.x, self.y, p.character.x, p.character.y) < maxdist:
                    maxdist = t.getDist(self.x, self.y, p.character.x, p.character.y)
                    player = p
                    playerdist = maxdist

        if player == None:
            for p in self.event.server.players:
                if not p.pending:
                    p.to_send.append({'action':'draw_barbarian',
                                  'type':'archer',
                                  'hurt':self.hurt,
                                  'coords':(p.character.get_x(self), p.character.get_y(self)),
                                  'angle':self.angle,
                                  'health':self.health,
                                  'shield':(self.shield_angle if self.shield_count else None)})
            return

        self.angle = t.getAngle(self.x, self.y, player.character.x, player.character.y)

        if playerdist < 350:
            self.speed = 0
            self.shoot()
        else:
            self.speed = 2

        x, y = t.getDir(self.angle, total=self.speed)

        self.x += x
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.event.server.obs + self.event.barbarians:
            if rect.colliderect(ob.innerrect) and ob != self:
                self.x -= x
        self.y += y
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.event.server.obs + self.event.barbarians:
            if rect.colliderect(ob.innerrect) and ob != self:
                self.y -= y

        self.rect = self.surf.get_rect(center=(self.x, self.y))

        for item in self.event.server.bushes:
            if self.rect.colliderect(item.innerrect):
                if item.__class__.__name__ == 'SpikyBush':
                    self.getHurt(self, 4.4, self.angle + 180, 0)
                else:
                    self.getHurt(self, 4, self.angle + 180, 0)

        for p in self.event.server.players:
            if not p.pending:
                if p.character.rect.colliderect(self.rect) and not p.character.dead:
                    result = p.character.getHurt(10, 'a Barbarian Archer', self.angle, 50, msg='<Victim> ran into <Attacker>. Their gold and food were stolen.')
                    if p.character.dead and result != 'BAM':
                        self.gold += p.character.gold
                        self.food += p.character.food
                        p.character.gold = 0
                        p.character.food = 0

        
        for p in self.event.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_barbarian',
                                  'type':'archer',
                                  'hurt':self.hurt,
                                  'coords':(p.character.get_x(self), p.character.get_y(self)),
                                  'angle':self.angle,
                                  'health':self.health,
                                  'shield':(self.shield_angle if self.shield_count else None)})

    def getHurt(self, attacker, damage, angle, knockback):
        if randint(0, 2) == 0 and attacker != self:
            self.shield_angle = angle + 180
            self.shield_count = 20
            for p in self.event.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    rect = pygame.Rect(0, 0, 1, 1)
                    rect.size = self.rect.size
                    rect.topleft = (p.character.get_x(self.rect), p.character.get_y(self.rect))
                    if screen.colliderect(rect):
                        p.to_send.append({'action':'sound','sound':'bump'})
            return ('repelled', self.shield_angle + randint(-20, 20))
        else:
        
            x, y = t.getDir(angle, total=(self.speed+knockback))

            self.x += x
            rect = self.surf.get_rect(center=(self.x, self.y))
            for ob in self.event.server.obs + self.event.barbarians:
                if rect.colliderect(ob.innerrect) and ob != self:
                    self.x -= x
            self.y += y
            rect = self.surf.get_rect(center=(self.x, self.y))
            for ob in self.event.server.obs + self.event.barbarians:
                if rect.colliderect(ob.innerrect) and ob != self:
                    self.y -= y
            self.hurt = 10
            self.rect = self.surf.get_rect(center=(self.x, self.y))

            self.health -= damage - 4
            if self.health <= 0:
                self.health = 0
                if self in self.event.barbarians:
                    self.event.barbarians.remove(self)
                for p in self.event.server.players:
                    if not p.pending:
                        p.message = 'A Barbarian Archer has been defeated!'
                        p.message_color = (255,205,0)
                        p.message_count = 160
                attacker.gold += self.gold
                attacker.food += self.food

    def shoot(self):
        if not self.shoot_cooldown:
            self.shoot_cooldown = 50
            Bolt(archer=self)


class BarbarianSwordsman():
    def __init__(self, event):
        self.hurt = 0
        self.angle = 0
        self.speed = 5
        self.health = 100
        self.shield_count = 0
        self.shield_angle = 0
        self.gold = 50
        self.food = 50
        self.speed = 2
        
        self.event = event
        self.surf = pygame.Surface((50, 50))
        self.event.barbarians.append(self)
        self.rect = self.surf.get_rect(center=(0, 0))
        self.x, self.y = self.event.get_coords()
        self.rect = self.surf.get_rect(center=(self.x, self.y))
        


    @property
    def innerrect(self):
        return self.rect

    def explode(self, player, tnt):
        self.getHurt(player, 60, t.getAngle(tnt.x, tnt.y, self.x, self.y), 120)
        
    def update(self):
        if self.hurt:
            self.hurt -= 1
        if self.shield_count:
            self.shield_count -= 1


        for ob in self.event.server.obs:
            if self.rect.colliderect(ob.innerrect):
                self.x = self.event.leader.x
                self.y = self.event.leader.y
                condition = True
                while condition:
                    condition = False
                    x, y = t.getDir(self.angle, total=self.speed)
                    self.x += x
                    self.y += y
                    self.rect = self.surf.get_rect(center=(self.x, self.y))
                    for ob in self.event.server.obs + self.event.barbarians:
                        if self.rect.colliderect(ob.innerrect) and ob != self:
                            condition = True
            
        maxdist = 10000
        playerdist = maxdist
        player = None
        for p in self.event.server.players:
            if not p.pending and not p.character.dead:
                if t.getDist(self.x, self.y, p.character.x, p.character.y) < maxdist:
                    maxdist = t.getDist(self.x, self.y, p.character.x, p.character.y)
                    player = p
                    playerdist = maxdist

        if player == None:
            for p in self.event.server.players:
                if not p.pending:
                    p.to_send.append({'action':'draw_barbarian',
                                  'type':'swordsman',
                                  'hurt':self.hurt,
                                  'coords':(p.character.get_x(self), p.character.get_y(self)),
                                  'angle':self.angle,
                                  'health':self.health,
                                  'shield':(self.shield_angle if self.shield_count else None)})
            return

        self.angle = t.getAngle(self.x, self.y, player.character.x, player.character.y)

        if playerdist < 350:
            self.speed = 5
        else:
            self.speed = 2

        x, y = t.getDir(self.angle, total=self.speed)

        self.x += x
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.event.server.obs + self.event.barbarians:
            if rect.colliderect(ob.innerrect) and ob != self:
                self.x -= x
        self.y += y
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.event.server.obs + self.event.barbarians:
            if rect.colliderect(ob.innerrect) and ob != self:
                self.y -= y

        self.rect = self.surf.get_rect(center=(self.x, self.y))

        for item in self.event.server.bushes:
            if self.rect.colliderect(item.innerrect):
                if item.__class__.__name__ == 'SpikyBush':
                    self.getHurt(self, 4.4, self.angle + 180, 0)
                else:
                    self.getHurt(self, 4, self.angle + 180, 0)

        for p in self.event.server.players:
            if not p.pending:
                if p.character.rect.colliderect(self.rect) and not p.character.dead:
                    result = p.character.getHurt(10, 'A Barbarian Swordsman', self.angle, 50, msg='<Attacker> killed <Victim> and stole their gold and food.')
                    if p.character.dead and result != 'BAM':
                        self.gold += p.character.gold
                        self.food += p.character.food
                        p.character.gold = 0
                        p.character.food = 0



        
        for p in self.event.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_barbarian',
                                  'type':'swordsman',
                                  'hurt':self.hurt,
                                  'coords':(p.character.get_x(self), p.character.get_y(self)),
                                  'angle':self.angle,
                                  'health':self.health,
                                  'shield':(self.shield_angle if self.shield_count else None)})

    def getHurt(self, attacker, damage, angle, knockback):

        if randint(0, 2) == 0 and attacker != self:
            self.shield_angle = angle + 180
            self.shield_count = 20
            for p in self.event.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    rect = pygame.Rect(0, 0, 1, 1)
                    rect.size = self.rect.size
                    rect.topleft = (p.character.get_x(self.rect), p.character.get_y(self.rect))
                    if screen.colliderect(rect):
                        p.to_send.append({'action':'sound','sound':'bump'})
            return ('repelled', self.shield_angle + randint(-20, 20))
        else:
        
            x, y = t.getDir(angle, total=(self.speed+knockback))

            self.x += x
            rect = self.surf.get_rect(center=(self.x, self.y))
            for ob in self.event.server.obs + self.event.barbarians:
                if rect.colliderect(ob.innerrect) and ob != self:
                    self.x -= x
            self.y += y
            rect = self.surf.get_rect(center=(self.x, self.y))
            for ob in self.event.server.obs + self.event.barbarians:
                if rect.colliderect(ob.innerrect) and ob != self:
                    self.y -= y
            self.hurt = 10
            self.rect = self.surf.get_rect(center=(self.x, self.y))

            self.health -= damage - 4
            if self.health <= 0:
                self.health = 0
                if self in self.event.barbarians:
                    self.event.barbarians.remove(self)
                for p in self.event.server.players:
                    if not p.pending:
                        p.message = 'A Barbarian Swordsman has been defeated!'
                        p.message_color = (255,205,0)
                        p.message_count = 160
                attacker.gold += self.gold
                attacker.food += self.food

GameClient.py

import pygame
import pygame as p
from pygame.locals import *

import shelve
import random
import json
import time
import threading
import re
import sys
import os
import __main__
from pymsgbox import alert, confirm, prompt, password
import toolbox as t


from net2web import Client as ParentClient

toolbox = t

answer = ''

with open('../preferences.json', 'r') as fo:
    preferences = json.loads(fo.read())
    py = preferences.get('py', 'python')
    MAP_TEXTURE = preferences.get('map', 'grass')
    

def get_setting(filepath):
    im = p.transform.scale(p.image.load(filepath), (300,300))
    walls = p.image.load('../assets/texture building/walls.png')
    setting = p.Surface((6000,3900))
    width = 6000//300
    height = 3900//300
    for y in range(height):
        for x in range(width):
            setting.blit(im, (x*300, y*300))
    setting2 = setting.__copy__()
    setting2.blit(walls, (0,0))
    return setting2, setting

def hack(self):
    fo = shelve.open('database/admin')
    admin_passwords = fo['passwords']
    fo.close()
    command = prompt('Enter the command:', title='VillageWars Console')
    if command is not None:
        attempt = password('You need an admin password to proceed.', title='VillageWars Console')
        if attempt in admin_passwords:
            self.Send({'action':'hack', 'command':command})
            alert('Password Accepted: Command Sent', title='VillageWars Console')
        else:
            alert('Password Incorrect', title='VillageWars Console')

def find_cursor(cursor_param):
    regex = re.compile(r'constant: SYSTEM_CURSOR_(.*)\)>')
    res = regex.search(repr(cursor_param))
    return res.group(1)


class MyGameClient(ParentClient):
    def __init__(self, host, port, screen, clock, username, version, color, skin, xp):

        """
        Client constructor function. This is the code that runs once
        when a new client is made.
        """
        super().__init__(host=host, port=port)

        self.color = color
        # Start the game
        pygame.init()
        pygame.mixer.pre_init(buffer=64)
        game_width = 1000
        game_height = 650
        self.started = False
        self.screen = screen
        self.clock = clock
        self.skin = skin
        self.xp = xp

        self.fps_expected = 30

        self.toDraw = []
        self.toDrawNext = []

        self.disconnected = 0

        x, y, width, height = 700, 0, 300, 50
        self.dropdown_open = False
        self.dropdown_rect = pygame.Rect(x, y, width, height)
        
        self.gamemodes = ['Classic', 'Express', 'Immediate', 'OP']
        self.gamemode = 'Classic'

        self.back_pic = p.image.load('../assets/BG_SciFi.png')
        self.ocean_pics = [p.transform.scale(p.image.load('../assets/ocean/' + i), (1000, 1000)) for i in os.listdir('../assets/ocean/')]
        self.ocean_frame = 0
        self.players = [(p.transform.scale(p.image.load('../assets/Skins/%s.png' % (i)), (50, 50)), p.transform.scale(p.image.load('../assets/Skins/%sh.png' % (i)), (50, 50))) for i in range(len(os.listdir('../assets/Skins'))//2)]
        self.archer = p.transform.scale(p.image.load('../assets/Enemy_04.png'), (50, 50))

        self.bolt = p.image.load('../assets/barbarians/bolt.png')

        self.startgame_icon = p.image.load('../assets/Startgame_btn.png')
        self.startgame_icon_over = p.image.load('../assets/Startgame_btn_over.png')
        self.startgame_icon_locked = p.image.load('../assets/Startgame_btn_locked.png')

        self.balloon = p.image.load('../assets/balloon.png')
        self.op_balloon = p.image.load('../assets/balloon2.png')
        self.speedy_plus_op = p.image.load('../assets/Drop.png')
        self.speedy_balloon = p.image.load('../assets/DropSmall.png')
        self.robot = p.transform.scale(p.image.load('../assets/Enemy_05.png'), (50, 50))
        self.robot_hurt = p.transform.scale(p.image.load('../assets/Enemy_05_hurt.png'), (50, 50))

        self.animations = {
            'Splash':(p.image.load('../assets/splash1.png'), p.image.load('../assets/splash2.png'), p.image.load('../assets/splash3.png')),
            'Explosion':(p.image.load('../assets/explode1.png'), p.image.load('../assets/explode2.png'), p.image.load('../assets/explode3.png')),
            'BAM':(p.transform.scale(p.image.load('../assets/LargeExplosion1.png'), (360, 360)), p.transform.scale(p.image.load('../assets/LargeExplosion1.png'), (360, 360)), p.transform.scale(p.image.load('../assets/LargeExplosion2.png'), (360, 360)), p.transform.scale(p.image.load('../assets/LargeExplosion2.png'), (360, 360)), p.transform.scale(p.image.load('../assets/LargeExplosion3.png'), (360, 360)), p.transform.scale(p.image.load('../assets/LargeExplosion3.png'), (360, 360)))
            }

        
        
        self.red = p.image.load('../assets/buildings/red.png')
        self.blue = p.image.load('../assets/buildings/blue.png')
        self.green = p.image.load('../assets/buildings/green.png')
        self.yellow = p.image.load('../assets/buildings/yellow.png')
        self.black = p.image.load('../assets/buildings/black.png')
        self.preview = p.image.load('../assets/buildings/preview.png')

        self.house = p.image.load('../assets/buildings/House.png')
        self.house_burnt = p.image.load('../assets/buildings/House_burnt.png')

        self.building_person = p.transform.scale(p.image.load('../assets/Skins/4.png'), (50, 50))
  
        self.miner = p.transform.scale(p.image.load('../assets/Skins/2.png'), (50, 50))
        self.farmer = p.transform.scale(p.image.load('../assets/Skins/4.png'), (50, 50))

        self.barbarians = {
            'leader':[p.image.load('../assets/barbarians/leader.png'), p.image.load('../assets/barbarians/leaderh.png'), p.image.load('../assets/barbarians/leaderm.png')],
            'archer':[p.image.load('../assets/barbarians/archer.png'), p.image.load('../assets/barbarians/archerh.png')],
            'swordsman':[p.image.load('../assets/barbarians/swordsman.png'), p.image.load('../assets/barbarians/swordsmanh.png')]
            }
        self.barbarian_range = p.transform.scale(p.image.load('../assets/barbarians/range.png'), (700, 700))
        self.shield = p.image.load('../assets/barbarians/shield.png')
        self.barbarian_banner = p.transform.scale(p.image.load('../assets/barbarians/banner.png'), (45, 90))

        for i, b in enumerate(self.barbarians['leader']):
            self.barbarians['leader'][i] = p.transform.scale(b, (50, 50))
        for i, b in enumerate(self.barbarians['archer']):
            self.barbarians['archer'][i] = p.transform.scale(b, (50, 50))
        for i, b in enumerate(self.barbarians['swordsman']):
            self.barbarians['swordsman'][i] = p.transform.scale(b, (50, 50))
        
        self.startgame_rect = self.startgame_icon.get_rect(topleft=(445, 400))

        self.Setting, self.no_walls_setting = get_setting('../assets/texture building/%s.png' % (MAP_TEXTURE))

        self.food_image = p.image.load('../assets/food.png')
        self.food_used = p.image.load('../assets/food_used.png')
        self.food_mine = p.image.load('../assets/food_mine.png')

        self.plate = p.transform.scale(p.image.load('../assets/meals/plate.png'), (100, 100))
        self.meals = [
            p.transform.scale(p.image.load('../assets/meals/meal1.png'), (100, 100)),
            p.transform.scale(p.image.load('../assets/meals/meal2.png'), (100, 100)),
            p.transform.scale(p.image.load('../assets/meals/meal3.png'), (100, 100)),
            p.transform.scale(p.image.load('../assets/meals/meal4.png'), (100, 100)),
                     ]
        self.meals = [p.transform.scale(p.image.load('../assets/meals/meal%s.png' % (num - 1)), (100, 100)) for num in range(len(os.listdir('../assets/meals'))) if num not in (0, 1)]

        self.gold_image = p.transform.scale(p.image.load('../assets/gold.png'), (45, 45))
        self.gold_mine = p.transform.scale(p.image.load('../assets/gold_mine.png'), (45, 45))

        self.mine = p.image.load('../assets/mine.png')

        self.x = p.image.load('../assets/x.png')
        self.x_hov = p.image.load('../assets/x_hov.png')

        self.tree = p.transform.scale(p.image.load('../assets/trees/tree1.png'), (200, 280))
        self.sappling = p.image.load('../assets/trees/sappling.png')

        self.large_font = p.font.SysFont('default', 80)
        self.medium_font = p.font.SysFont('default', 40)
        self.small_font = p.font.SysFont('default', 20)

        self.crate = p.transform.scale(p.image.load('../assets/crate.png'), (50, 50))
        self.barrel = p.transform.scale(p.image.load('../assets/Barrel.png'), (50, 50))
        self.barrel_tnt = p.transform.scale(p.image.load('../assets/BarrelTNT.png'), (50, 50))
        self.barrel_broken = p.transform.scale(p.image.load('../assets/BarrelRubble.png'), (50, 50))
        
        self.tnt = p.transform.scale(p.image.load('../assets/TNT.png'), (50, 50))
        self.gate = p.image.load('../assets/gate.png')
        self.spiky_bush = p.image.load('../assets/spiky_bush.png')

        self.ready = False
        self.ready_text = self.small_font.render('READY', True, (0,255,0))
        self.not_ready_text = self.small_font.render('NOT READY', True, (255,0,0))

        self.host_text = self.small_font.render('host', True, (255,0,0))
        self.escape_count = 0
        self.username = username

        p.mixer.init()
        self.sound_be = pygame.mixer.Sound('../assets/sfx/explosion-big.wav') # be for big explosion
        self.sound_se = pygame.mixer.Sound('../assets/sfx/explosion-small.wav') # se for small explosion
        self.sound_sp = pygame.mixer.Sound('../assets/sfx/splash.wav') # sp for splash
        self.sound_sh = pygame.mixer.Sound('../assets/sfx/splash-heavy.wav') # sh for splash heavy
        self.sound_shot = pygame.mixer.Sound('../assets/sfx/shot.wav')
        self.sound_bump = pygame.mixer.Sound('../assets/sfx/bump.wav')
        self.sound_ba = pygame.mixer.Sound('../assets/sfx/break.wav')  # ba for barrel
        self.sound_bb = pygame.mixer.Sound('../assets/sfx/building.wav') # bb for broken building
        self.sound_ow = pygame.mixer.Sound('../assets/sfx/open_window.wav')
        self.sound_cw = pygame.mixer.Sound('../assets/sfx/close_window.wav')

        self.musics = {
            'steppingpebbles':'../assets/sfx/stepPebblesLoop.mp3',
            'village':'../assets/sfx/villageLoop.mp3',
            'barbarianraid':'../assets/sfx/War song.mp3'
            }

        self.paused = False
        self.F3 = False

        with open('tips.json', 'r') as tipfile:
            tips = json.loads(tipfile.read())
            self.tips = tips['basic_tips']
            num_rare = random.choice([0,0,0,0,1,1,1,2,2,3])
            random.shuffle(tips['rare_tips'])
            for i, rare_tip in enumerate(tips['rare_tips'][:]):
                tips['rare_tips'][i] = '+' + rare_tip
            self.tips.extend(tips['rare_tips'][:num_rare])
        self.tip_frame = 0
        self.tip = 0


        self.achievement_pic = p.transform.scale(p.image.load('../assets/achievement.png'), (225, 50))
        
        self.achievement = [0, 'None']

        self.version = version
        

    def update(self):
        """
        Client update function. This is the function that runs over and
        over again, once every frame of the game.
        """

        self.mouseX, self.mouseY = p.mouse.get_pos()[:2]
        
        self._connected = False
        self.Pump()
        global running

        if answer == 'OK':
            p.quit()
            exit()

        global cursor
        old_cursor = p.mouse.get_cursor()
        if not find_cursor(cursor) == find_cursor(old_cursor):
            p.mouse.set_cursor(cursor)
        cursor = p.cursors.Cursor(pygame.SYSTEM_CURSOR_ARROW)

        if self.achievement[0]:
            self.achievement[0] -= 1
            if self.achievement[0] >= 90:
                x = (225/30) * (120-self.achievement[0])
            elif self.achievement[0] <= 30:
                x = (225/30) * self.achievement[0]
            else:
                x = 225
            self.toDraw.append((self.achievement_pic, (1000-x, 55)))
            text = self.small_font.render(self.achievement[1], True, (0,0,0))
            text_rect = text.get_rect(midtop = (1120-x, 85))
            self.toDraw.append([text, text_rect])

        if not self._connected:
            self.disconnected += 1
        else:
            self.disconnected = 0

        if self.escape_count:
            self.escape_count -= 1

        if self.disconnected == 50 and self.connected:
            c = confirm('Connection is low.', title='VillageWars', buttons=['Quit', 'Continue'])
            if c == 'Quit':
                p.quit()
                sys.exit()
            #p.mixer.music.stop()
            #self.screen.blit(p.image.load('../assets/Disconnected.png'), (0, 0))
            #pygame.display.flip()
            
            #while True:                
            #    for e in p.event.get():
            #        if e.type == QUIT:
            #            p.quit()
            #            sys.exit()
            #            
            #    fps = self.clock.get_fps()
            #    self.clock.tick(30)
            #    pygame.display.set_caption("VillageWars fps: " + str(fps))
        
        self.tip_frame += 1
        if self.tip_frame == 250:
            self.tip_frame = 0
            self.tip += 1
            if self.tip == len(self.tips):
                self.tip = 0
                random.shuffle(self.tips)
                if 'Always get a Miner\'s Guild and a Farmer\'s Guild first!' in self.tips:
                    self.tips.remove('Always get a Miner\'s Guild and a Farmer\'s Guild first!')

        
        # Set running to False if the player clicks the X
        global music_playing
        
        for event in pygame.event.get():
            if event.type == QUIT:
                def confirm_exit():
                    global answer
                    answer = confirm('The game is still going. Are you sure you want to quit?')
                thread = threading.Thread(target=confirm_exit)
                thread.start()
                
            if event.type == KEYUP and event.key == K_F1 and self.started:
                music_playing = not music_playing
                if music_playing:
                    p.mixer.music.pause()
                else:
                    p.mixer.music.play(-1, 0.0)
            if event.type == KEYUP and event.key == K_F2:
                num = len(os.listdir('../run/screenshots')) + 1
                name = 'sreenshot' + str(num)
                p.image.save(self.screen, '../run/screenshots/%s.png' % (name))
                alert('Screenshot saved as %s.png' % (name))
            if event.type == KEYUP and event.key == K_F6:
                self.Send({'action':'F6'})
            if event.type == KEYUP and event.key == K_F10:
                hack(self)
            if event.type == KEYUP and event.key == K_F3:
                self.F3 = not self.F3
            if event.type == KEYUP and event.key == K_F8:
                self.Send({'action':'startgame'})  # Cheaty way to start the server before everyone's ready
            #if event.type == KEYUP and event.key == K_ESCAPE:  # Removed because this is BAD!
            #    if not self.escape_count:
            #        self.Send({'action':'pause'})
            #        self.escape_count = 30
            if event.type == MOUSEMOTION:
                self.mouseX = event.pos[0]
                self.mouseY = event.pos[1]
            if event.type == MOUSEBUTTONUP:
                if self.dropdown_rect.collidepoint(event.pos):
                    self.dropdown_open = not self.dropdown_open
                else:
                    self.dropdown_open = False
        _keys = p.key.get_pressed()
        keys = []
        for i in range(len(list(_keys))):
            keys.append(_keys[i])
        mouse = list(p.mouse.get_pos())
        mouse_click = p.mouse.get_pressed()
        mouse.append(mouse_click[0])
        mouse.append(mouse_click[1])
        mouse.append(mouse_click[2])

        self.Send({'action': 'keys', 'keys': keys, 'mouse':mouse})

        
        
        
        _screen = p.Surface((1000, 650))
        for image in self.toDraw:
            _screen.blit(image[0], image[1])
        if self.paused:
            _screen.blit(self.paused_image, (0, 0))
        
        self.screen.blit(_screen, (0, 0))
        
        
            
        # Tell pygame to update the screen
        pygame.display.flip()

        fps = self.clock.get_fps()
        self.Send({'action':'fps', 'fps':fps})
        self.clock.tick(self.fps_expected)
        pygame.display.set_caption("VillageWars " + self.version + " fps: " + str(fps))


    def ShutDown(self):
        """
        Client ShutDown function. Disconnects and closes the game.
        """
        self.connected = False
        sys.exit()
        
        
                
    #####################################
    ### Client-side Network functions ###
    #####################################
    """
    Each one of these "Network_" functions defines a command
    that the server will tell you (the client) to do.
    """
    def Network_hack_fail(self, data):
        print('Exception:', data['msg'])

    def Network_gamemode(self, data):
        self.gamemode = data['gamemode']
        
    def Network_receive(self, data):
        #print(time.time() - data['timestamp'])
        self.diconnected = 0
        self._connected = True
        if True: #time.time() - data['timestamp'] < 0.5:
            self.fps_expected = 30
            for i in data['data']:
                exec('self.Network_' + i['action'] + '(' + repr(i) + ')')
        else:
            self.fps_expected = 60
    

    def Network_fall(self, data):
        
        self.Setting = self.no_walls_setting
        p.mixer.music.stop()
        p.mixer.music.load('../assets/sfx/villageLoop.mp3')
        global music_playing
        if music_playing:
            p.mixer.music.play(-1, 0.0)

    def Network_achievement(self, data):
        self.achievement = [120, data['type']]

    def Network_pause(self, data):
        self.paused = not self.paused

    def Network_flip(self, data):
        if not self.paused:           
            self.toDraw.clear()
            self.toDraw = self.toDrawNext[:]
            self.toDrawNext.clear()
        
        
    def Network_draw_setting(self, data):
        self.toDrawNext.append([self.Setting, data['coords']])

        
    def Network_draw_lobby_background(self, data):
        self.toDrawNext.append([self.Setting, data['coords']])
        image = self.crate
        for i in range(1000//50):
            rect = image.get_rect(center=(data['x'] + i*50, data['y']))
            self.toDrawNext.append([image, rect])
        for i in range(1000//50):
            rect = image.get_rect(center=(data['x'] + i*50, data['y'] + 650))
            self.toDrawNext.append([image, rect])
        for i in range(700//50):
            rect = image.get_rect(center=(data['x'], data['y'] + i*50))
            self.toDrawNext.append([image, rect])
        for i in range(700//50):
            rect = image.get_rect(center=(data['x'] + 1000, data['y'] + i*50))
            self.toDrawNext.append([image, rect])
        image = p.Surface((500, 100), SRCALPHA)
        
        rect = image.get_rect(center=(data['x']+500, data['y']+500))
        if rect.collidepoint((500,325)):
            image.fill((200,200,250,128))
            if not self.ready:
                self.ready = not self.ready
                self.Send({'action':'ready', 'ready':self.ready})
                
        else:
            image.fill((0,0,200,128))
            if self.ready:
                self.ready = not self.ready
                self.Send({'action':'ready', 'ready':self.ready})
        self.toDrawNext.append([image, rect])


        
        
        

    def Network_draw_background(self, data):
        self.toDrawNext.append([self.ocean_pics[self.ocean_frame], (0, 0)])
        self.ocean_frame += 1
        self.ocean_frame %= 40

    def Network_preview(self, data):

        if not data['ArcheryTower?']:
            image = self.house
            rect = image.get_rect(center=(500, 185))
            cage = self.preview

            cage = p.transform.scale(cage, data['dimensions'])
            cage_rect = cage.get_rect(midtop=rect.midtop)
            self.toDrawNext.append([cage, cage_rect])
        else:
            cage = p.transform.scale(self.preview, (360, 360))
            cage_rect = cage.get_rect(center=(500, 85))
            self.toDrawNext.append([cage, cage_rect])


    def Network_draw_avatar(self, data):
        avatar_pic = self.players[data['skin']][0]
        avatar_rect = avatar_pic.get_rect(center=data['coords'])
        avatar_pic, avatar_rect = t.rotate(avatar_pic, avatar_rect, data.get('angle', 0))
        self.toDrawNext.append([avatar_pic, avatar_rect])

        text = self.medium_font.render(data['username'], True, data['color'])
        text_rect = text.get_rect(midbottom = avatar_rect.midtop)
        text_rect.y -= 4
        self.toDrawNext.append([text, text_rect])

        #if data['host']:
            #mouse = p.mouse.get_pos()
            #click = p.mouse.get_pressed()

            #image = self.x
            #rect = image.get_rect(center=avatar_rect.topright)
            #if rect.collidepoint(mouse):
            #    global cursor
            #    cursor = p.cursors.Cursor(pygame.SYSTEM_CURSOR_HAND)
            #    image = self.x_hov
            #    if click[0]:
            #        self.Send({'action':'hack', 'command':'kick(self, "%s")' % (data['username'])})

            #self.toDrawNext.append((image, rect))
        

    def Network_victory(self, data):
        p.mixer.music.stop()
        victory = p.mixer.Sound('../assets/sfx/victory.mp3')
        victory.play()

    def Network_animation(self, data):
        image = self.animations[data['name']][data['frame']]
        rect = image.get_rect(center=data['coords'])
        self.toDrawNext.append((image, rect))

    def Network_congrats(self, data):
        screen = p.Surface((1000, 650))
        screen.fill((255,255,0))
        self.toDrawNext.append([screen, (0, 0)])
        if data['color'] == (255,255,0): data['color'] = (0,0,0)
        name = self.large_font.render('You won the game!', True, data['color'])
        text_rect = name.get_rect(midtop=(500, 30))
        self.toDrawNext.append([name, text_rect])

        name = self.medium_font.render('Kills: ' + str(data['kills']), True, (0,0,0))
        text_rect = name.get_rect(topleft=(400, 200))
        self.toDrawNext.append([name, text_rect])
        name = self.medium_font.render('Eliminations: ' + str(data['eliminations']), True, (0,0,0))
        text_rect = name.get_rect(topleft=(400, 320))
        self.toDrawNext.append([name, text_rect])
        name = self.medium_font.render('Buildings Destroyed: ' + str(data['destroyed']), True, (0,0,0))
        text_rect = name.get_rect(topleft=(400, 440))
        self.toDrawNext.append([name, text_rect])
        name = self.medium_font.render('Deaths: ' + str(data['deaths']), True, (255,0,0))
        text_rect = name.get_rect(topleft=(400, 560))
        self.toDrawNext.append([name, text_rect])

        keys = p.key.get_pressed()
        if keys[K_ESCAPE]:
            global running
            running = False

    def Network_end(self, data):
        p.mixer.music.stop()
        screen = p.Surface((1000, 650))
        screen.fill((128, 128, 128))
        self.toDrawNext.append([screen, (0, 0)])
        name = self.large_font.render(data['winner'] + ' won the game.', True, (0,0,0))
        text_rect = name.get_rect(midtop=(500, 30))
        self.toDrawNext.append([name, text_rect])

        name = self.medium_font.render('Kills: ' + str(data['kills']), True, (0,0,0))
        text_rect = name.get_rect(topleft=(400, 200))
        self.toDrawNext.append([name, text_rect])
        name = self.medium_font.render('Eliminations: ' + str(data['eliminations']), True, (0,0,0))
        text_rect = name.get_rect(topleft=(400, 320))
        self.toDrawNext.append([name, text_rect])
        name = self.medium_font.render('Buildings Destroyed: ' + str(data['destroyed']), True, (0,0,0))
        text_rect = name.get_rect(topleft=(400, 440))
        self.toDrawNext.append([name, text_rect])
        name = self.medium_font.render('Deaths: ' + str(data['deaths']), True, (255,0,0))
        text_rect = name.get_rect(topleft=(400, 560))
        self.toDrawNext.append([name, text_rect])
        
    def Network_ingame(self, data):
        self.toDrawNext.append([self.Setting, data['coords']])

    def Network_music_change(self, data):
        p.mixer.music.stop()
        p.mixer.music.load(self.musics[data['music']])
        global music_playing
        if music_playing:
            p.mixer.music.play(-1, 0.0)


    def Network_draw_barbarian(self, data):
        if self.F3:
            image = self.barbarian_range
            rect = image.get_rect(center=data['coords'])
            self.toDrawNext.insert(6, (image, rect))
        
        image = self.barbarians[data['type']][bool(data['hurt'])]
        rect = image.get_rect(center=data['coords'])
        image, rect = t.rotate(image, rect, data['angle'])

        if data['type'] == 'leader':
            banner = self.barbarian_banner
            banner_rect = banner.get_rect(bottomleft=rect.center)
            self.toDrawNext.append((banner, banner_rect))
        
        self.toDrawNext.append((image, rect))

        username_text = self.small_font.render('Barbarian ' + data['type'].title(), True, (0,0,0))
        text_rect = username_text.get_rect(midbottom = (rect.midtop[0], rect.midtop[1] - 15))
        self.toDrawNext.append([username_text, text_rect])


        if data['shield'] != None:
            image = self.shield
            rect = image.get_rect(center=data['coords'])
            image, rect = t.rotate(image, rect, data['shield'])
            self.toDrawNext.append((image, rect))
            
        

        
        health_bar = p.Surface((50, 10))
        health_bar.fill((255, 0, 0))
        health_rect = health_bar.get_rect(midbottom=rect.midtop)
        
        green_bar = p.Surface((int(data['health']/2), 10))
        green_bar.fill((0,255,0))
        health_bar.blit(green_bar, (0, 0))
        
        self.toDrawNext.append([health_bar, health_rect])

    def Network_hud(self, data):
        gold = self.medium_font.render('Gold: ' + str(data['gold']), True, (255,205,0))
        gold_rect = gold.get_rect(topright = (950,260))
        self.toDrawNext.append([gold, gold_rect])
            
        food = self.medium_font.render('Food: ' + str(data['food']), True, (5,255,5))
        food_rect = food.get_rect(topright = (950,300))
        self.toDrawNext.append([food, food_rect])

        if self.F3:
            x = round(data['x'])
            y = round(data['y'])
            coords = self.medium_font.render('x=' + str(x) + ', y=' + str(y), True, (0,0,0))
            coords_rect = coords.get_rect(topleft = (50,100))
            self.toDrawNext.append([coords, coords_rect])
            if x > 3000:
                if y > 1950:
                    q = 'Bottom-right'
                else:
                    q = 'Top-right'
            else:
                if y > 1950:
                    q = 'Bottom-left'
                else:
                    q = 'Top-left'
            coords = self.medium_font.render('Quadrant: ' + q, True, (0,0,0))
            coords_rect = coords.get_rect(topleft = (50,130))
            self.toDrawNext.append([coords, coords_rect])
            if -45 <= data['angle'] <= 45:
                d = 'East (towards positive x)'
            elif 45 <= data['angle'] <= 135:
                d = 'North (towards negative y)'
            elif -135 <= data['angle'] <= -45:
                d = 'South (towards positive y)'
            elif -135 >= data['angle'] or data['angle'] >= 135:
                d = 'West (towards negative x)'

            text = self.medium_font.render('Facing: ' + d, True, (0,0,0))
            text_rect = text.get_rect(topleft = (50,160))
            self.toDrawNext.append([text, text_rect])

            gametime = data.get('gametime')
            if gametime:
                gametime = round(gametime)
                seconds = gametime % 60 + 1
                minutes = gametime // 60
                #print(seconds, minutes)
                if seconds == 60:
                    seconds = 0
                    minutes += 1
                if minutes > 0 and seconds > 9:
                    gametime = str(minutes) + ':' + str(seconds)
                elif seconds > 9:
                    gametime = str(seconds)
                else:
                    if minutes > 0:
                        gametime = str(minutes) + ':' + '0' + str(seconds)
                    else:
                        gametime = str(seconds)
                text = self.medium_font.render('Game Time: ' + gametime, True, (0,0,0))
                text_rect = text.get_rect(topleft = (50,190))
                self.toDrawNext.append([text, text_rect])

            fps = self.clock.get_fps()
            text = self.medium_font.render('FPS: ' + str(fps), True, (0,0,0))
            text_rect = text.get_rect(topleft = (50,220))
            self.toDrawNext.append([text, text_rect])

        if data['food?']:
            image = self.meals[data['type']]
        else:
            image = self.plate

        mouse = p.mouse.get_pos()

        rect = image.get_rect(center=(890, 130))
        if rect.collidepoint(mouse) and image != self.plate:
            image = p.transform.scale(image, (110, 110))
            rect = image.get_rect(center=(890, 130))
            pressed = p.mouse.get_pressed()
            if pressed[0]:
                self.Send({'action':'eat'})

        self.toDrawNext.append([image, rect])

        
        
        if data['crates'] > 0:
            text = self.medium_font.render('x ' + str(data['crates']), True, (0,0,0))
            text_rect = text.get_rect(midleft=(940, 20))
            self.toDrawNext.append([text, text_rect])
            self.toDrawNext.append([p.transform.scale(self.crate, (30, 30)), (900, 5)])

            
        
        if data['spiky_bushes'] > 0:
            text = self.medium_font.render('x ' + str(data['spiky_bushes']), True, (0,0,0))
            text_rect = text.get_rect(midleft=(940, 75))
            self.toDrawNext.append([text, text_rect])
            self.toDrawNext.append([p.transform.scale(self.spiky_bush, (30, 30)), (900, 60)])

    def Network_draw_player(self, data):
        global cursor
        cursor = p.cursors.Cursor(pygame.SYSTEM_CURSOR_CROSSHAIR)
        if data.get('in_barrel', (0,False))[1]:
            if data['in_barrel'][0] == True:
                
                image = self.players[data['skin']][data['hurt']]
                rect = image.get_rect(center=(data['coords']))
                image, rect = t.rotate(image, rect, data['angle'])
                self.toDrawNext.append([image, rect])
                image = self.barrel_broken
            elif data['in_barrel'][0] == False:
                image = self.barrel
            else:
                image = self.barrel_tnt
            rect = image.get_rect(center=(data['coords']))
        else:
            image = self.players[data['skin']][data['hurt']]
            rect = image.get_rect(center=(data['coords']))
            image, rect = t.rotate(image, rect, data['angle'])
        self.toDrawNext.append([image, rect])

        username_text = self.small_font.render(data['username'], True, data['color'])
        text_rect = username_text.get_rect(midbottom = (rect.midtop[0], rect.midtop[1] - 15))
        if not data.get('in_barrel', False)[1]:
            
            self.toDrawNext.append([username_text, text_rect])

            
            health_bar = p.Surface((int(data['max_health'] / 2), 10))
            health_bar.fill((255, 0, 0))
            health_rect = health_bar.get_rect(midbottom=rect.midtop)
            
            green_bar = p.Surface((int(data['health']/2), 10))
            green_bar.fill((0,255,0))
            health_bar.blit(green_bar, (0, 0))
            
            self.toDrawNext.append([health_bar, health_rect])
            
            if data['shield'] != None:
                image = self.shield
                rect = image.get_rect(center=data['coords'])
                image, rect = t.rotate(image, rect, data['shield'])
                self.toDrawNext.append((image, rect))

        if self.F3:
            health = self.small_font.render(str(data['health']) + '/' + str(data['max_health']), True, (240,240,240))
            text_rect = health.get_rect(midbottom = (text_rect.midtop[0], text_rect.midtop[1] - 4))
            self.toDrawNext.append([health, text_rect])

        

        


    def Network_draw_NPC(self, data):
        if data['image'] == 'farmer':
            image = self.farmer
            name = 'Farmer'
        elif data['image'] == 'miner':
            image = self.miner
            name = 'Miner'
        rect = image.get_rect(topleft=(data['coords']))
        image, rect = t.rotate(image, rect, data['angle'])
        self.toDrawNext.append([image, rect])


        
        username_text = self.small_font.render(name, True, data['color'])
        text_rect = username_text.get_rect(midbottom = (rect.midtop[0], rect.midtop[1] - 15))
        self.toDrawNext.append([username_text, text_rect])

        status_text = self.small_font.render(data['status'], True, (0,0,0))
        text_rect = status_text.get_rect(midbottom = (rect.midtop[0], rect.midtop[1] + 5))
        self.toDrawNext.append([status_text, text_rect])

    def Network_draw_InnPC(self, data):
        
        image = self.players[0][0]
        rect = image.get_rect(center=(data['coords']))
        image, rect = t.rotate(image, rect, data['angle'])
        self.toDrawNext.append([image, rect])
        
        username_text = self.small_font.render(data['type'], True, data['color'])
        text_rect = username_text.get_rect(midbottom = (rect.midtop[0], rect.midtop[1] - 15))
        self.toDrawNext.append([username_text, text_rect])

        health_bar = p.Surface((50, 10))
        health_bar.fill((255,0,0))
        health_rect = health_bar.get_rect(midbottom=rect.midtop)
        
        green_bar = p.Surface((round(data['health'] / 2), 10))
        green_bar.fill((0,255,0))
        health_bar.blit(green_bar, (0, 0))
        
        self.toDrawNext.append([health_bar, health_rect])
    def Network_draw_robot(self, data):
        if data['image'] == 'regular':
            image = self.robot
        elif data['image'] == 'hurt':
            image = self.robot_hurt
        
        rect = image.get_rect(center=(data['coords']))
        image, rect = t.rotate(image, rect, data['angle'])
        self.toDrawNext.append([image, rect])


        
        username_text = self.small_font.render(data['name'], True, data['color'])
        text_rect = username_text.get_rect(midbottom = (rect.midtop[0], rect.midtop[1] - 15))
        self.toDrawNext.append([username_text, text_rect])


        health_bar = p.Surface((50, 10))
        health_bar.fill((255,0,0))
        health_rect = health_bar.get_rect(midbottom=rect.midtop)
        
        green_bar = p.Surface((round(data['health'] / 2), 10))
        green_bar.fill((0,255,0))
        health_bar.blit(green_bar, (0, 0))
        
        self.toDrawNext.append([health_bar, health_rect])


    def Network_draw_farm(self, data):
        if data['state'] == 'good':
            image = self.food_image
        elif data['state'] == 'mine':
            image = self.food_mine
        else:
            image = self.food_used
        self.toDrawNext.append([image, data['coords']])

    def Network_draw_gold(self, data):
        image = None
        if data['state'] == 'good':
            image = self.gold_image
        elif data['state'] == 'mine':
            image = self.gold_mine
        if image != None:
            self.toDrawNext.append([image, data['coords']])

    def Network_draw_mine(self, data):
        image = self.mine
        if data['right'] == True:
            image = p.transform.flip(image, True, False)
        self.toDrawNext.append([image, data['coords']])
    

    def Network_draw_balloon(self, data):
        balloon_type = data.get('type', 'default')
        image = self.balloon
        if balloon_type == 'bolt':
            image = self.bolt
        if balloon_type == 'op':
            image = self.op_balloon
        if balloon_type == 'speedy':
            image = self.speedy_balloon
        if balloon_type == 'speedy+op':
            image = self.speedy_plus_op
        rect = image.get_rect(center=(data['coords']))
        image, rect = t.rotate(image, rect, data['angle'])
        self.toDrawNext.append([image, rect])

        
    def Network_draw_building(self, data):
        
        if data['image'] == 'house':
            if data['state'] == 'alive':
                image = self.house
            else:
                image = self.house_burnt
        rect = image.get_rect(center=data['coords'])
        

        color = tuple(data['color'])
        if color == (255,0,0):
            cage = self.red
        elif color == (0,255,0):
            cage = self.green
        elif color == (0,0,255):
            cage = self.blue
        elif color == (255,255,0):
            cage = self.yellow
        elif color == (0,0,0):
            cage = self.black
        

        cage = p.transform.scale(cage, data['dimensions'])
        cage_rect = cage.get_rect(midtop=rect.midtop)
        self.toDrawNext.append([cage, cage_rect])

        self.toDrawNext.append([image, rect])

        if data['state'] == 'alive':
            person = self.building_person
            person_rect = image.get_rect(midtop=(rect.midbottom[0], rect.midbottom[1] + 50))
            person, person_rect = t.rotate(person, person_rect, data['angle'])

            self.toDrawNext.append([person, person_rect])

            name = self.small_font.render(data['type'], True, data['color'])
            text_rect = name.get_rect(midbottom = (person_rect.midtop[0], person_rect.midtop[1] - 30))
            self.toDrawNext.append([name, text_rect])

            name = self.small_font.render('(Level ' + str(data['level']) + ')', True, data['color'])
            text_rect = name.get_rect(midbottom = (person_rect.midtop[0], person_rect.midtop[1] - 15))
            self.toDrawNext.append([name, text_rect])

            health_bar = p.Surface((int(data['max_health'] / 2), 10))
            health_bar.fill((255, 0, 0))
            health_rect = health_bar.get_rect(midbottom=person_rect.midtop)
            
            green_bar = p.Surface((int(data['health']/2), 10))
            green_bar.fill((0,255,0))
            health_bar.blit(green_bar, (0, 0))
            
            self.toDrawNext.append([health_bar, health_rect])

    def Network_archery_tower(self, data):
        

        color = tuple(data['color'])
        if color == (255,0,0):
            cage = self.red
        elif color == (0,255,0):
            cage = self.green
        elif color == (0,0,255):
            cage = self.blue
        elif color == (255,255,0):
            cage = self.yellow
        elif color == (0,0,0):
            cage = self.black

        cage = p.transform.scale(cage, (360, 360))
        cage_rect = cage.get_rect(center=data['coords'])
        self.toDrawNext.append([cage, cage_rect])

        
        
        person = self.archer
        person_rect = person.get_rect(midtop=(data['coords']))
        person, person_rect = t.rotate(person, person_rect, data['angle'])
        if data['state'] == 'alive':
            self.toDrawNext.append([person, person_rect])

        if data['state'] == 'alive':
            name = self.small_font.render('Archery Tower', True, data['color'])
        else:
            name = self.small_font.render('Archery Tower (Broken)', True, data['color'])
        text_rect = name.get_rect(midbottom = (person_rect.midtop[0], person_rect.midtop[1] - 15))
        self.toDrawNext.append([name, text_rect])

        if data['state'] == 'alive':
            health_bar = p.Surface((150, 10))
            health_rect = health_bar.get_rect(midbottom=person_rect.midtop)
            health_bar.fill((255,0,0))
            
            green_bar = p.Surface((int(data['health']/2), 10))
            green_bar.fill((0,255,0))
            health_bar.blit(green_bar, (0, 0))
            
            self.toDrawNext.append([health_bar, health_rect])

    def Network_sound(self, data):
        if data['sound'] == 'TNT':
            self.sound_be.play()
        if data['sound'] == 'shot':
            self.sound_shot.play()
        if data['sound'] == 'die':
            self.sound_se.play()
        if data['sound'] == 'splash':
            self.sound_sp.play()
        if data['sound'] == 'opsplash':
            self.sound_sh.play()
        if data['sound'] == 'bump':
            self.sound_bump.play()
        if data['sound'] == 'building':
            self.sound_bb.play()
        if data['sound'] == 'ow':
            self.sound_ow.play()
        if data['sound'] == 'cw':
            self.sound_cw.play()
        if data['sound'] == 'barrel':
            self.sound_ba.play()


    def Network_num_buildings(self, data):
        y = 500
        x = 990
        for user in data['users']:
            username = self.small_font.render(user['name'] + ' has ' + str(user['bs']) + ' buildings.', True, user['color'])
            text_rect = username.get_rect(topright=(x, y+user['num']*30))
            self.toDrawNext.append([username, text_rect])
    

    def Network_chat(self, data):
        y = 20
        for message in reversed(data['messages']):
            color = list(message['color'])
            #color.append(message['fade'])
            m = self.medium_font.render(message['message'], True, color)
            m.set_alpha(message['fade'])
            text_rect = m.get_rect(topleft=(20, y))
            self.toDrawNext.append([m, text_rect])
            y += 30

    def Network_startgame(self, data):
        p.mixer.music.stop()
        p.mixer.music.load('../assets/sfx/stepPebblesLoop.mp3')
        self.started = True
        global music_playing
        if music_playing:
            p.mixer.music.play(-1, 0.00)

    def Network_text(self, data):

        if data['text']:
            name = self.medium_font.render(data['text'], True, (0,0,255))
        else:
            if self.tips[self.tip].startswith('+'):
                name = self.medium_font.render('TIP: ' + self.tips[self.tip][1:], True, (255,0,128))
            else:
                name = self.medium_font.render('TIP: ' + self.tips[self.tip], True, (255,205,0))            
        text_rect = name.get_rect(midbottom=(500, 500))
        self.toDrawNext.append([name, text_rect])
    
    

    def Network_time(self, data):
        text = self.medium_font.render(data['time'], True, (0,0,0))
        text_rect = text.get_rect(midbottom=(500, 640))
        self.toDrawNext.append([text, text_rect])

    def Network_draw_obstacle(self, data):
        if data['image'] == 'tree':
            image = self.tree
        if data['image'] == 'sappling':
            image = self.sappling
        if data['image'] == 'crate':
            image = self.crate
        if data['image'] == 'vine':
            image = self.vine
        if data['image'] == 'gate':
            if data['rotated?']:
                image = p.transform.rotate(self.gate, 90)
            else:
                image = self.gate
        if data['image'] == 'spiky bush':
            image = self.spiky_bush
        if data['image'] == 'TNT':
            image = self.tnt
        if data['image'] == 'barrel':
            image = self.barrel_broken if data['max_health'] / (1+data['health']) > 2 else self.barrel
            if data.get('explosive', False):
                image = self.barrel_tnt
            

        rect = image.get_rect(center=data['coords'])
        image, rect = t.rotate(image, rect, data.get('angle', 0))
        self.toDrawNext.append([image, rect])

        if data['image'] != 'barrel' and data['image'] != 'crate' and data['image'] != 'gate' and data['image'] != 'TNT' and data['image'] != 'spiky bush' and data['image'] != 'vine':
            health_bar = p.Surface((int(data['max_health']/2), 12))
            health_bar.fill((255, 0, 0))
            health_rect = health_bar.get_rect(midbottom=rect.midtop)
        
            green_bar = p.Surface((int(data['health']/2), 12))
        
            green_bar.fill((0,255,0))
            health_bar.blit(green_bar, (0, 0))
        
            self.toDrawNext.append([health_bar, health_rect])

    def Network_draw_window(self, data):
        grey = p.Surface((610, 618))
        large_rect = grey.get_rect(topleft=(195, 4))
        grey.fill((128,128,128))
        self.toDrawNext.append([grey, large_rect])

        if large_rect.collidepoint(p.mouse.get_pos()):
            global cursor
            cursor = p.cursors.Cursor(pygame.SYSTEM_CURSOR_ARROW)

        dark_grey = p.Surface((600, 606))
        rect = dark_grey.get_rect(topleft=(200, 10))
        dark_grey.fill((95,95,95))
        self.toDrawNext.append([dark_grey, rect])

        x = 200
        y = 200

        info = self.small_font.render(data['window']['info'][0], True, (0,0,0))
        text_rect = info.get_rect(topleft=(210,30))
        self.toDrawNext.append([info, text_rect])
        info = self.small_font.render(data['window']['info'][1], True, (0,0,0))
        text_rect = info.get_rect(topleft=(210,55))
        self.toDrawNext.append([info, text_rect])

        info = self.small_font.render('Owner:', True, (255,0,255))
        text_rect = info.get_rect(topleft=(210,90))
        self.toDrawNext.append([info, text_rect])
        info = self.small_font.render('Health:', True, (255,0,255))
        text_rect = info.get_rect(topleft=(210,110))
        self.toDrawNext.append([info, text_rect])
        info = self.small_font.render('Level:', True, (255,0,255))
        text_rect = info.get_rect(topleft=(210,130))
        self.toDrawNext.append([info, text_rect])
        info = self.small_font.render(data['window']['4th_info'][0] + ':', True, (255,0,255))
        big_text_rect = info.get_rect(topleft=(210,150))
        self.toDrawNext.append([info, big_text_rect])
        right = big_text_rect.topright[0] + 30

        info = self.small_font.render(data['window']['owner'], True, data['window']['color'])
        text_rect = info.get_rect(topleft=(right,90))
        self.toDrawNext.append([info, text_rect])

        info = self.small_font.render(str(data['window']['health'][0]) + '/' + str(data['window']['health'][1]), True, (0,0,0))
        text_rect = info.get_rect(topleft=(right,110))
        self.toDrawNext.append([info, text_rect])

        info = self.small_font.render(str(data['window']['level']), True, (0,0,0))
        text_rect = info.get_rect(topleft=(right,130))
        self.toDrawNext.append([info, text_rect])

        info = self.small_font.render(data['window']['4th_info'][1], True, (0,0,0))
        text_rect = info.get_rect(topleft=(right,150))
        self.toDrawNext.append([info, text_rect])

        for Y in range(len(data['window']['options'])):
            black = p.Surface((600, 2))
            black_rect = black.get_rect(topleft=(200,y+Y*40))
            grey = p.Surface((600, 44))
            rect = grey.get_rect(topleft=(200, y+Y*40))
            grey.fill((155,155,155))
            mouse = p.mouse.get_pos()
            if rect.collidepoint(mouse):
                grey.fill((250, 250, 255))
            self.toDrawNext.append([grey, rect])
            self.toDrawNext.append([black, black_rect])

            name = self.medium_font.render(data['window']['options'][Y], True, (0,0,0))
            text_rect = name.get_rect(midtop=(500, y+Y*40+5))
            self.toDrawNext.append([name, text_rect])


    def Network_draw_innpc_window(self, data):
        grey = p.Surface((610, 618))
        large_rect = grey.get_rect(topleft=(195, 4))
        grey.fill((128,128,128))
        self.toDrawNext.append([grey, large_rect])

        dark_grey = p.Surface((600, 606))
        rect = dark_grey.get_rect(topleft=(200, 10))
        dark_grey.fill((95,95,95))
        self.toDrawNext.append([dark_grey, rect])

        x = 210
        y = 30

        for Y, thing in enumerate(data['window']['info']):
            info = self.small_font.render(thing, True, (0,0,0))
            text_rect = info.get_rect(topleft=(x, y + Y*15))
            self.toDrawNext.append([info, text_rect])

        x = 200
        y = 200
        for Y in range(len(data['window']['options'])):
            black = p.Surface((600, 2))
            black_rect = black.get_rect(topleft=(200,y+Y*40))
            grey = p.Surface((600, 44))
            rect = grey.get_rect(topleft=(200, y+Y*40))
            grey.fill((155,155,155))
            mouse = p.mouse.get_pos()
            if rect.collidepoint(mouse):
                grey.fill((250, 250, 255))
            self.toDrawNext.append([grey, rect])
            self.toDrawNext.append([black, black_rect])

            name = self.medium_font.render(data['window']['options'][Y], True, (0,0,0))
            text_rect = name.get_rect(midtop=(500, y+Y*40+5))
            self.toDrawNext.append([name, text_rect])

    def Network_WARNING_outdated_client(self, data):
        info = self.medium_font.render('WARNING: Outdated Client', True, (255,0,0))
        text_rect = info.get_rect(midbottom=(400,600))

        box = p.Surface((text_rect.width + 10, 85))
        box.fill((255,255,255))
        rect = box.get_rect(midbottom=(400, 650))
        self.toDrawNext.append([box, rect])

        self.toDrawNext.append([info, text_rect])
        info = self.medium_font.render('Server: %s - Client: %s' % (data['version'], self.version), True, (255,0,0))
        text_rect = info.get_rect(midbottom=(400,635))
        self.toDrawNext.append([info, text_rect])

    


    def Network_WARNING_outdated_server(self, data):
        info = self.medium_font.render('WARNING: Outdated Server', True, (255,0,0))
        text_rect = info.get_rect(midbottom=(400,600))

        box = p.Surface((text_rect.width + 10, 85))
        box.fill((255,255,255))
        rect = box.get_rect(midbottom=(400, 650))
        self.toDrawNext.append([box, rect])

        self.toDrawNext.append([info, text_rect])
        info = self.medium_font.render('Server: %s - Client: %s' % (data['version'], self.version), True, (255,0,0))
        text_rect = info.get_rect(midbottom=(400,635))
        self.toDrawNext.append([info, text_rect])

        
        

    def Network_display_host(self, data):
        x, y, width, height = 700, 0, 300, 50
        # Check if the dropdown is clicked
        mouse_pos = p.mouse.get_pos()
        mouse = p.mouse.get_pressed()
                
        if self.dropdown_open:
            # Display the dropdown options
            for i, option in enumerate(self.gamemodes):
                option_rect = pygame.Rect(x, y + (height * (i+1)), width, height)
                text_surface = self.medium_font.render(option, True, (255, 255, 255))
                text_rect = text_surface.get_rect(center=option_rect.center)

                surf = p.Surface((width, height), p.SRCALPHA)
                if option_rect.collidepoint(mouse_pos):
                    surf.fill((200,200,200,200))
                else:
                    surf.fill((128,128,128,200))
                self.toDrawNext.append((surf, option_rect))
                self.toDrawNext.append((text_surface, text_rect))

                if option_rect.collidepoint(mouse_pos) and mouse[0]:
                    # Change the selected option
                    self.gamemode = option
                    self.Send({"action": "change_gamemode", "gamemode": self.gamemode})
        
        text_surface = self.medium_font.render(('Gamemode' if self.dropdown_open else self.gamemode), True, (255, 255, 255))
        text_rect = text_surface.get_rect(center=self.dropdown_rect.center)
        surf = p.Surface((width, height), p.SRCALPHA)
        if self.dropdown_rect.collidepoint(mouse_pos):
            if self.dropdown_open:
                surf.fill((200,200,200))
            else:
                surf.fill((200,200,200,200))
        else:
            if self.dropdown_open:
                surf.fill((128,128,128))
            else:
                surf.fill((128,128,128,200))
            
        self.toDrawNext.append((surf, self.dropdown_rect))
        self.toDrawNext.append((text_surface, text_rect))


        

    def Network_connected(self, data):
        """
        Network_connected runs when you successfully connect to the server
        """
        self.Send({'action':'version', 'version':self.version})
        self.Send({'action':'init', 'username':self.username, 'status':'JG', 'color':self.color, 'skin':self.skin, 'xp':self.xp})
        __main__.stop_loading_circle = True
        print("Joined game")

        p.mixer.music.stop()
        
    
    def Network_error(self, data):
        """
        Network_error runs when there is a server error
        """
        print('error:', data['error'][1])
        self.ShutDown()
    
    def Network_disconnected(self, data):
        """
        Network_disconnected runs when you disconnect from the server
        """
        self.ShutDown()

    def Network_kicked(self, data):
        self.ShutDown()
        p.quit()
        exit()






    
def main(screen, clock, username, version, userInfo, ip, port=5555, musicPlaying=True):
    global running, cursor, music_playing
    running = True
    cursor = p.cursors.Cursor(p.SYSTEM_CURSOR_ARROW)
    music_playing = musicPlaying
    print(ip)
    cursor = p.cursors.Cursor(pygame.SYSTEM_CURSOR_ARROW)
    client = MyGameClient(ip, port, screen, clock, username, version, userInfo['color'], userInfo['skin'], userInfo['xp'])
    while running:
        client.update()
    client.ShutDown()
    p.mixer.music.stop()
    return music_playing


if __name__ == '__main__':
    print('You probably want to run main.py instead of this.')

hacking.py

from building import *

def kick(self, playername):
    for p in self.server.players:
        if p.username == playername:
            p.Send({'action':'kicked'})
    
    print('Kicked ' + playername)
    del self.server.players[playername]


def makeWallsFall(self):
    self.server.upwall.count = 10
    self.server.leftwall.count = 10


def kill(self, playername):
    for p in self.server.players:
        if p.username == playername:
            break
    p = p.character
    p.getHurt(300, self.character, 0, 0, msg="<Attacker> killed <Victim> using cheats")


def win(self):
    self.server.terminate(self)


def message(self, theMessage, color=(0,0,0)):
    for p in self.server.players:
        p.message = message
        p.message_color = color
        p.message_count = 160

def redybarb(self):
    self.character.attack = 200
    self.character.has_shield = True
    makeWallsFall(self)
    BarbarianRaid(self.server, 10)

InnPC.py

import pygame
import toolbox as t
import random as r
from building import *
from events import *

INNSTART = 300
INNEND = 900

def get_innpc(inn):
    npcs = [Botanist, Merchant, Rancher, Alchemist]
    if inn.level > 1:
        npcs.extend([Mayor, Repairer, Builder, Advanced_balloonist])
    bds = inn.owner.get_buildings()
    for NPC in npcs[:]:
        if NPC.building in [bd.__class__ for bd in bds]:
            npcs.remove(NPC)
        
    try:
        return r.choice(npcs)(inn)
    except:
        return


class Builders(Building):
    dimensions = (490 * 0.8, 490 * 0.8)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Builder. As long as this buildings isn\'t broken, all the buildings you buy will be smaller.', '')
        self.type = 'Builder\'s'
        self.health = self.max_health = 300
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def minute(self, player):
        if not self.server.fallen:
            if player.food > 90:
                player.food -= 90
                player.channel.message = 'You added a minute till the Walls fall.'
                player.channel.message_color = (255, 205, 0)
                player.channel.message_count = 160
                self.server.upwall.count += 30*60
                self.server.leftwall.count += 30*60
                self.server.barbarian_count += 30*60
                
            else:
                player.channel.message = 'You don\'t have enough food to do this.'
                player.channel.message_color = (255,0,0)
                player.channel.message_count = 150
        else:
            player.channel.message = 'The Walls have already fallen!'
            player.channel.message_color = (0,0,255)
            player.channel.message_count = 150
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            if self.server.fallen:
                channel.window = {'text':self.info,
                                  '4th_info':('Walls Fallen', 'True'),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.heal), (1, self.Out)],
                                  'simp':('Heal', 'Out')}
            else:
                channel.window = {'text':self.info,
                                  '4th_info':('Time till the Walls fall', self.server.getTime()),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.heal), (1, self.minute), (2, self.Out)],
                                  'simp':('Heal', 'Add a minute till the Walls fall (90 food)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Walls Fallen', str(self.server.fallen)),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}

class RepairCenter(Building):
    dimensions = (490, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Repairer, so that you can repair your village with ease!', '')
        self.type = 'Repair Center'
        self.health = self.max_health = 290
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def repair(self, player):
        if player.gold > 49:
            player.gold -= 50
            player.channel.message = 'Your village has been repaired for 50 gold.'
            player.channel.message_color = (255, 205, 0)
            player.channel.message_count = 160
            for b in player.channel.get_buildings():
                b.health = b.max_health
            
        else:
            player.channel.message = 'You don\'t have enough gold!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    




    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Buildings needing repair', str(len([b for b in channel.get_buildings() if b.health < b.max_health]))),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.repair), (2, self.Out)],
                              'simp':('Heal', 'Repair Village (50 gold)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Buildings needing repair', str(len([b for b in channel.get_buildings() if b.health < b.max_health]))),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}


class AdvancedBalloonistBuilding(Building):
    dimensions = (490, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Advanced Balloonist NPC, so that you can upgrade your shot speed easily.', '')
        self.type = 'Advanced Balloonist'
        self.health = self.max_health = 300
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def speedgun(self, player):
        if player.shot_speed != 1:
            if player.gold > 279:
                player.gold -= 280
                player.channel.message = 'You increased your shoot speed for 280 gold.'
                player.channel.message_color = (255, 205, 0)
                player.channel.message_count = 160
                player.shot_speed -= 1
                
            else:
                player.channel.message = 'You don\'t have enough gold to do this.'
                player.channel.message_color = (255,0,0)
                player.channel.message_count = 150
        else:
            player.channel.message = 'Your balloon gun is already at its max! (Nobody\'s gonna stop you now)'
            player.channel.message_color = (0,0,255)
            player.channel.message_count = 150
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Shot Speed', str(16-channel.character.shot_speed)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.speedgun), (2, self.Out)],
                              'simp':('Heal', '+ Shot Speed (280 gold)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Shot Speed', str(16-channel.character.shot_speed)),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}


class TownHall(Building):
    dimensions = (560, 540)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building has 500 HP! It\'ll also repair itself fully if it hasn\'t been attacked in a minute.', '')
        self.type = 'Town Hall'
        self.health = self.max_health = 500
        self.dimensions = type(self).dimensions
        self.count = 0

        self.post_init(rect)

    def update(self):
        super().update()
        if self.count:
            self.count -= 1
            if not self.count:
                self.health = self.max_health

    def getHurt(self, damage, attacker):
        super().getHurt(damage, attacker)
        self.count = 1800


class AlchemistsLab(Building):
    dimensions = (490, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Alchemist, so that you can always trade with it.', '')
        self.type = 'Alchemist\'s Lab'
        self.health = self.max_health = 220
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def rate1(self, player):
        if player.gold > 9:
            mine = None
            for b in self.owner.get_buildings():
                if b.__class__.__name__ == 'MinersGuild':
                    mine = b.miner.farm
                    break
            if mine is None:
                player.channel.message = 'Get a Miner\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if not mine.production == 250:
                player.gold -= 10
                player.channel.message = 'You have increased your Gold Discovery Rate by 10 for 10 gold.'
                player.channel.message_color = (255,205,0)
                player.channel.message_count = 150
                
                mine.production -= 10
                if mine.production < 250:
                    mine.production = 250
            else:
                player.channel.message = 'The Gold Discovery Rate is at its maximum!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
        else:
            player.channel.message = 'You don\'t have enough gold!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    def rate2(self, player):
        if player.gold > 149:
            mine = None
            for b in self.owner.get_buildings():
                if b.__class__.__name__ == 'MinersGuild':
                    mine = b.miner.farm
                    break
            if mine is None:
                player.channel.message = 'Get a Miner\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if not mine.production == 250:
                player.gold -= 150
                player.channel.message = 'You have increased your Gold Discovery Rate by 250 for 150 gold.'
                player.channel.message_color = (255,205,0)
                player.channel.message_count = 150
                
                mine.production -= 250
                if mine.production < 250:
                    mine.production = 250
            else:
                player.channel.message = 'The Gold Discovery Rate is at its maximum!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
            
        else:
            player.channel.message = 'You don\'t have enough gold!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    




    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Gold', str(channel.character.gold)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.rate1), (2, self.rate2), (3, self.Out)],
                              'simp':('Heal', 'Increase Gold Discovery Rate by 10 (10 gold)', 'Increase Gold Discovery Rate by 250 (150 gold)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Gold', str(channel.character.gold)),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}

class Ranch(Building):
    dimensions = (490, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Rancher, so that you can always trade with it.', '')
        self.type = 'Ranch'
        self.health = self.max_health = 220
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def rate1(self, player):
        if player.food > 9:
            farm = None
            for b in self.owner.get_buildings():
                if b.__class__.__name__ == 'FarmersGuild':
                    farm = b.farmer.farm
                    break
            if farm is None:
                player.channel.message = 'Get a Farmer\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if not farm.production == 250:
                player.food -= 10
                player.channel.message = 'You have increased your Food Production Rate by 10 for 10 food.'
                player.channel.message_color = (255,205,0)
                player.channel.message_count = 150
                
                farm.production -= 10
                if farm.production < 250:
                    farm.production = 250
            else:
                player.channel.message = 'The Food Production Rate is at its maximum!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
        else:
            player.channel.message = 'You don\'t have enough food!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    def rate2(self, player):
        if player.food > 149:
            farm = None
            for b in self.owner.get_buildings():
                if b.__class__.__name__ == 'FarmersGuild':
                    farm = b.farmer.farm
                    break
            if farm is None:
                player.channel.message = 'Get a Farmer\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if not farm.production == 250:
                player.food -= 150
                player.channel.message = 'You have increased your Food Production Rate by 250 for 150 food.'
                player.channel.message_color = (255,205,0)
                player.channel.message_count = 150
                
                farm.production -= 250
                if farm.production < 250:
                    farm.production = 250
            else:
                player.channel.message = 'The Food Production Rate is at its maximum!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
            
        else:
            player.channel.message = 'You don\'t have enough food!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)



    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Food', str(channel.character.food)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.rate1), (2, self.rate2), (3, self.Out)],
                              'simp':('Heal', 'Increase Food Production Rate by 10 (10 food)', 'Increase Food Production Rate by 250 (150 food)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Gold', str(channel.character.gold)),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}


class BotanistsLab(Building):
    dimensions = (490, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Botanist, so that you can always buy from it.', '')
        self.type = 'Botanist\'s Lab'
        self.health = self.max_health = 220
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def spiky_bush(self, player):
        if player.food > 0:
            player.food -= 1
            player.channel.message = 'You have bought a spiky bush for 1 food.'
            player.spiky_bushes += 1
            player.channel.message_color = (255,205,0)
            player.channel.message_count = 150
            player.garden_xp += 1
        else:
            player.channel.message = 'You don\'t have enough food to buy this!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    def sappling(self, player):
        if player.food > 6:
            player.food -= 7
            player.channel.message = 'You have bought a sappling for 7 food.'
            player.channel.text = 'Press c to place sappling'
            player.channel.message_color = (255,205,0)
            player.channel.message_count = 150
            player.garden_xp += 20
        else:
            player.channel.message = 'You don\'t have enough food to buy this!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    def vine(self, player):
        if player.food > 14:
            player.food -= 15
            player.channel.message = 'You have bought an invasive vine for 15 food.'
            player.channel.text = 'Press c to place invasive vine'
            player.channel.message_color = (255,205,0)
            player.channel.message_count = 150
        else:
            player.channel.message = 'You don\'t have enough food to buy this!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)




    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Gardening XP', str(channel.character.garden_xp)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.sappling), (2, self.spiky_bush), (3, self.Out)],
                              'simp':('Heal', 'Buy a sappling (7 food)', 'Buy a spiky bush (1 food)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Gardening XP', str(channel.character.garden_xp)),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}

class Market(Building):
    dimensions = (490, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Merchant, so that you can always trade', 'gold for food and food for gold.')
        self.type = 'Market'
        self.health = self.max_health = 220
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def tradegold(self, player):
        if player.food > 9:
            player.food -= 10
            player.gold += 10
            player.channel.message = 'You have traded 10 food for 10 gold.'
            player.channel.message_color = (255,205,0)
            player.channel.message_count = 150
        else:
            player.channel.message = 'You don\'t have enough food to trade!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)
    def tradefood(self, player):
        if player.gold > 9:
            player.gold -= 10
            player.food += 10
            player.channel.message = 'You have traded 10 gold for 10 food.'
            player.channel.message_color = (255,205,0)
            player.channel.message_count = 150
        else:
            player.channel.message = 'You don\'t have enough gold to trade!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    
    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Resources', str(self.owner.character.gold) + ' gold, ' + str(self.owner.character.food) + ' food'),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.tradegold), (2, self.tradefood), (3, self.Out)],
                              'simp':('Heal', 'Trade 10 food for 10 gold', 'Trade 10 gold for 10 food', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Resources', str(self.owner.character.gold) + ' gold, ' + str(self.owner.character.food) + ' food'),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}

    
class RetiredBarbarianOutpost(Building):
    dimensions = (600, 490)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Retired Barbarian.', '')
        self.type = 'Retired Barbarian Outpost'
        self.health = self.max_health = 160
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def crossbow(self, player):
        if player.barbshoot_cooldown >= 0:
            player.channel.message = 'You already have a barbarian crossbow!'
            player.channel.message_color = (0,0,255)
            player.channel.message_count = 150
        elif player.food > 99:
            player.food -= 100
            player.channel.message = 'You bought a barbarian crossbow for 100 food.'
            player.channel.message_color = (255, 205, 0)
            player.channel.message_count = 160
            player.barbshoot_cooldown = 50
        else:
            player.channel.message = 'You don\'t have enough food to buy this.'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    def shield(self, player):
        if player.has_shield:
            player.channel.message = 'You already have a shield!'
            player.channel.message_color = (0,0,255)
            player.channel.message_count = 150
        elif player.food > 159:
            player.food -= 160
            player.channel.message = 'You bought a Barbarian Shield for 160 food.'
            player.channel.message_color = (255, 205, 0)
            player.channel.message_count = 160
            player.has_shield = True
        else:
            player.channel.message = 'You don\'t have enough food to buy this.'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)    




    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            channel.window = {'text':self.info,
                              '4th_info':('Need Shield', str(not channel.character.has_shield)),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              'options':[(0, self.heal), (1, self.shield), (2, self.crossbow), (3, self.Out)],
                              'simp':('Heal', 'Buy a shield (160 food)', 'Buy a barbarian crossbow (100 food)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Need Shield', str(not channel.character.has_shield)),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}


class AdventuringCenter(Building):
    dimensions = (510, 400)
    def __init__(self, server, x, y, owner, rect):
        Building.__init__(self, server, x, y, owner)
        self.info = ('This building is for the Adventurer. Having this building is the best.', '')
        self.type = 'Adventuring Center'
        self.health = self.max_health = 300
        self.dimensions = type(self).dimensions

        self.post_init(rect)

    def barbarianRaid(self, player):
        if player.gold > 44:
            player.gold -= 45
            BarbarianRaid(self.server)
            
        else:
            player.channel.message = 'You don\'t have enough gold!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    def barbarianRaidHuge(self, player):
        if player.gold > 99:
            player.gold -= 100
            BarbarianRaid(self.server, 19 + randint(0, 4))
            
        else:
            player.channel.message = 'You don\'t have enough gold!'
            player.channel.message_color = (255,0,0)
            player.channel.message_count = 150
        self.Out(player)

    def end(self, player):
        for p in self.server.players:
            p.message = player.channel.username + ' has ended the current event.'
            p.message_color = (255, 205, 0)
            p.message_count = 160
            p.to_send.append({'action':'music_change', 'music':'village'})
        self.server.event = Event(self.server)
        self.Out(player)

    def open_window(self, channel):
        if self.state == 'alive':
            channel.in_window = True
            if self.server.event.__class__ != Event:
                channel.window = {'text':self.info,
                                  '4th_info':('Current Event', self.server.event.__class__.__name__),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.heal), (1, self.end), (2, self.Out)],
                                  'simp':('Heal', 'End Current Event (Free)', 'Out')}
            else:
                channel.window = {'text':self.info,
                                  '4th_info':('Buildings needing repair', str(len([b for b in channel.get_buildings() if b.health < b.max_health]))),
                                  'health':(self.health, self.max_health),
                                  'building':self,
                                  'level':self.level,
                                  'options':[(0, self.heal), (1, self.barbarianRaid), (2, self.barbarianRaidHuge), (3, self.Out)],
                                  'simp':('Heal', 'Trigger a Barbarian Raid (45 gold)', 'Trigger a huge Barbarian Raid (100 gold)', 'Out')}

        elif self.state == 'broken':
            channel.in_window = True
            channel.window = {'text':('This building is broken.', ''),
                              'health':(self.health, self.max_health),
                              'building':self,
                              'level':self.level,
                              '4th_info':('Buildings needing repair', str(len([b for b in channel.get_buildings() if b.health < b.max_health]))),
                              'options':[(0, self.clear), (1, self.Out)],
                              'simp':['Clear (2 food)', 'Out']}



class InnPC():
    def __init__(self, inn):
        self.inn = inn
        self.type = type(self).__name__.replace('_', ' ').title()
        self.server = inn.server
        self.angle = 0
        self.x = self.inn.x + r.randint(-100, 100)
        self.y = self.inn.y + 130
        self.health = 100
        p = self.inn.owner
        p.message = 'The %s has arrived!' % self.type
        p.message_count = 160
        p.message_color = (128,0,128)
        self.count = r.randint(30*60*1, 30*60*5)
        self.rect = pygame.Surface((50, 50)).get_rect(center=(self.x, self.y))
        self.building = None
        
    @property
    def window(self):
        return {'text':(self.type + ':', 'Hello, there!'),
                'options':(('Out', self.Out),)}


    def Out(self, channel):
        channel.in_window = False
        channel.in_innpc_window = False
        channel.Send({'action':'sound', 'sound':'cw'})

    def update(self):
        self.count -= 1
        if self.count == 0:
            self.depart()
        self.angle = t.getAngle(self.x, self.y, self.inn.owner.character.x, self.inn.owner.character.y)
        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_InnPC',
                                  'type':self.type,
                                  'coords':(p.character.get_x(self), p.character.get_y(self)),
                                  'angle':self.angle,
                                  'color':self.inn.owner.color,
                                  'health':self.health})

    def depart(self, comeback=True):
        self.inn.NPC = None
        p = self.inn.owner
        p.message = 'The %s has departed.' % self.type
        p.message_count = 160
        p.message_color = (128,0,128)
        if comeback:
            self.inn.count = r.randint(INNSTART, INNEND)
            if self.inn.level > 1 and self.inn.count > 150:
                self.inn.count -= 150


class Botanist(InnPC):
    building = BotanistsLab
    def __init__(self, inn):
        super().__init__(inn)
        self.cost = (0, 70)
        self.building = BotanistsLab

    @property
    def window(self):
        return {'text':(self.type + ':', 'Hello, there! I sell sapplings, that will grow into any of the trees that the game started with!',
                                         'I also sell spiky bushes. You might have heard of crates, they are the same size except that',
                                         'people can go through the spiky bush, it\'s just that they take damage.'),
                'options':(('Buy a sappling (7 food)', self.sappling), ('Buy a spiky bush (1 food)', self.spiky_bush), ('Out', self.Out))}


    def spiky_bush(self, channel):
        if channel.character.food > 0:
            channel.character.food -= 1
            channel.message = 'You have bought a spiky bush for 1 food.'
            channel.character.spiky_bushes += 1
            channel.message_color = (255,205,0)
            channel.message_count = 150
            channel.character.garden_xp += 1
        else:
            channel.message = 'You don\'t have enough food to buy this!'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)

    def sappling(self, channel):
        if channel.character.food > 6:
            channel.character.food -= 7
            channel.message = 'You have bought a sappling for 7 food.'
            channel.text = 'Press c to place sappling'
            channel.message_color = (255,205,0)
            channel.message_count = 150
            channel.character.garden_xp += 20
        else:
            channel.message = 'You don\'t have enough food to buy this!'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)

    



class Alchemist(InnPC):
    building = AlchemistsLab
    def __init__(self, inn):
        super().__init__(inn)
        self.cost = (80, 0)
        self.building = AlchemistsLab

    @property
    def window(self):
        return {'text':(self.type + ':', 'Greetings. I can change stone into gold. I might do so for you, for a little money.'),
                'options':(('Increase Gold Discovery Rate by 10 (10 gold)', self.rate1), ('Increase Gold Discovery Rate by 250 (150 gold)', self.rate2), ('Out', self.Out))}

    def rate1(self, channel):
        if channel.character.gold > 9:
            mine = None
            for b in self.inn.owner.get_buildings():
                if b.__class__.__name__ == 'MinersGuild':
                    mine = b.miner.farm
                    break
            if mine is None:
                player.channel.message = 'Get a Miner\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if mine and not mine.production == 250:
                channel.character.gold -= 10
                channel.message = 'You have increased your Gold Discovery Rate by 10 for 10 gold.'
                channel.message_color = (255,205,0)
                channel.message_count = 150
                mine.production -= 10
                if mine.production < 250:
                    mine.production = 250
            else:
                channel.message = 'The Gold Discovery Rate is at its maximum!'
                channel.message_color = (0,0,255)
                channel.message_count = 150
            
        else:
            channel.message = 'You don\'t have enough gold!'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)

    def rate2(self, channel):
        if channel.character.gold > 149:
            mine = None
            for b in self.inn.owner.get_buildings():
                if b.__class__.__name__ == 'MinersGuild':
                    mine = b.miner.farm
                    break
            if mine is None:
                player.channel.message = 'Get a Miner\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if mine and not mine.production == 250:
                channel.character.gold -= 150
                channel.message = 'You have increased your Gold Discovery Rate by 250 for 150 gold.'
                channel.message_color = (255,205,0)
                channel.message_count = 150
                mine.production -= 250
                if mine.production < 250:
                    mine.production = 250
            else:
                channel.message = 'The Gold Discovery Rate is at its maximum!'
                channel.message_color = (0,0,255)
                channel.message_count = 150
            
        else:
            channel.message = 'You don\'t have enough gold!'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)

class Rancher(InnPC):
    building = Ranch
    def __init__(self, inn):
        super().__init__(inn)
        self.cost = (0, 80)
        self.building = Ranch

    @property
    def window(self):
        return {'text':(self.type + ':', 'Howdy, folks! I know better techniques for growing food. I could sell some of those ideas, if you want.'),
                'options':(('Increase Food Production Rate by 10 (10 food)', self.rate1), ('Increase Food Production Rate by 250 (150 food)', self.rate2), ('Out', self.Out))}

    def rate1(self, channel):
        if channel.character.food > 9:
            farm = None
            for b in self.inn.owner.get_buildings():
                if b.__class__.__name__ == 'FarmersGuild':
                    farm = b.farmer.farm
                    break
            if farm is None:
                player.channel.message = 'Get a Farmer\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if not farm.production == 250:
                channel.character.food -= 10
                channel.message = 'You have increased your Food Production Rate by 10 for 10 food.'
                channel.message_color = (255,205,0)
                channel.message_count = 150
                
                farm.production -= 10
                if farm.production < 250:
                    farm.production = 250
            else:
                channel.message = 'The Food Production Rate is at its maximum!'
                channel.message_color = (0,0,255)
                channel.message_count = 150
                
        else:
            channel.message = 'You don\'t have enough food!'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)

    def rate2(self, channel):
        if channel.character.food > 149:
            farm = None
            for b in self.inn.owner.get_buildings():
                if b.__class__.__name__ == 'FarmersGuild':
                    farm = b.farmer.farm
                    break
            if farm is None:
                player.channel.message = 'Get a Farmer\'s Guild first!'
                player.channel.message_color = (0,0,255)
                player.channel.message_count = 150
                self.Out(player)
                return
            if farm and not farm.production == 250:
                channel.character.food -= 150
                channel.message = 'You have increased your Food Production Rate by 250 for 150 food.'
                channel.message_color = (255,205,0)
                channel.message_count = 150
                
                farm.production -= 250
                if farm.production < 250:
                    farm.production = 250
            else:
                channel.message = 'The Food Production Rate is at its maximum!'
                channel.message_color = (0,0,255)
                channel.message_count = 150
                
            
        else:
            channel.message = 'You don\'t have enough food!'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)


class Merchant(InnPC):
    building = Market
    def __init__(self, inn):
        super().__init__(inn)
        self.building = Market
        self.cost = (27, 27)

    @property
    def window(self):
        return {'text':(self.type + ':', 'Hello, there! Give me food or gold and I\'ll give you the other.'),
                'options':(('Trade 10 food for 10 gold', self.gold), ('Trade 10 gold for 10 food', self.food), ('Out', self.Out))}

    def gold(self, channel):
        if channel.character.food > 9:
            channel.character.food -= 10
            channel.character.gold += 10
            channel.message = 'You have traded 10 food for 10 gold.'
            channel.message_color = (255,205,0)
            channel.message_count = 150
        else:
            channel.message = 'You don\'t have enough food to trade.'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)

    def food(self, channel):
        if channel.character.gold > 9:
            channel.character.gold -= 10
            channel.character.food += 10
            channel.message = 'You have traded 10 gold for 10 food.'
            channel.message_color = (255,205,0)
            channel.message_count = 150
        else:
            channel.message = 'You don\'t have enough gold to trade.'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)



class Mayor(InnPC):
    building = TownHall
    def __init__(self, inn):
        super().__init__(inn)
        self.building = TownHall
        self.cost = (100, 50)

    @property
    def window(self):
        return {'text':(self.type + ':', 'I\'m in politics. Maybe I\'m useless to you for now, but build me a town hall and you won\'t regret\nit. It\'ll have five hundred health and will repair itself.'),
                'options':(('Out', self.Out),)}

class Retired_barbarian(InnPC):
    building = RetiredBarbarianOutpost
    def __init__(self, inn):
        super().__init__(inn)
        self.building = RetiredBarbarianOutpost
        self.cost = (0, 20)

    @property
    def window(self):
        return {'text':(self.type + ':', 'Hello. I\'m a barbarian. What\'s that? No, I mean I used to be a barbarian. I see you\'ve had the honor', 'of defeating the enemy tribe of my old one. As a reward, I am selling you some useful barbarian stuff.'),
                'options':(('Out', self.Out), ('Buy a Barbarian Shield (160 food)', self.shield), ('Buy a Barbarian Crossbow (100 food)', self.crossbow))}

    def shield(self, channel):
        if channel.character.has_shield:
            channel.message = 'You already have a shield!'
            channel.message_color = (0,0,255)
            channel.message_count = 150
        elif channel.character.food > 159:
            channel.character.food -= 160
            channel.message = 'You bought a Barbarian Shield for 160 food.'
            channel.message_color = (255,205,0)
            channel.message_count = 150
            channel.character.has_shield = True
        else:
            channel.message = 'You don\'t have enough food to buy this.'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)

    def crossbow(self, channel):
        if channel.character.barbshoot_cooldown >= 0:
            channel.message = 'You already have a barbarian crossbow!'
            channel.message_color = (0,0,255)
            channel.message_count = 150
        elif channel.character.food > 99:
            channel.character.food -= 100
            channel.message = 'You bought a barbarian crossbow for 100 food.'
            channel.message_color = (255,205,0)
            channel.message_count = 150
            channel.character.barbshoot_cooldown = 50
        else:
            channel.message = 'You don\'t have enough food to buy this.'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)


class Adventurer(InnPC):
    building = AdventuringCenter
    def __init__(self, inn):
        super().__init__(inn)
        self.building = AdventuringCenter
        self.cost = (40, 40)

    @property
    def window(self):
        return {'text':(self.type + ':', 'Hello, I\'m the Adventurer. See for youself what I can do!'),
                'options':(('Out', self.Out), ('Trigger a Barbarian Raid (45 gold)', self.barbarianRaid), ('Trigger a huge Babarian Raid (100 gold)', self.barbarianRaidHuge))}

    def barbarianRaid(self, channel):
        if channel.character.gold > 44:
            channel.character.gold -= 45
            BarbarianRaid(self.server)
        else:
            channel.message = 'You don\'t have enough gold to trigger a Barbarian Raid.'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)
        
    def barbarianRaidHuge(self, channel):
        if channel.character.gold > 99:
            channel.character.gold -= 100
            BarbarianRaid(self.server, 19 + randint(0, 4))
        else:
            channel.message = 'You don\'t have enough gold to trigger a huge Barbarian Raid.'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)



class Repairer(InnPC):
    building = RepairCenter
    def __init__(self, inn):
        super().__init__(inn)
        self.building = RepairCenter
        self.cost = (10, 60)

    @property
    def window(self):
        return {'text':(self.type + ':', 'Hello, there! Low on food? I\'ll repair everything in your village for just 50 gold!'),
                'options':(('Repair Village (50 gold)', self.repair), ('Out', self.Out))}

    def repair(self, channel):
        if channel.character.gold > 49:
            channel.character.gold -= 50
            channel.message = 'You repaired your entire village for 50 gold.'
            channel.message_color = (255,205,0)
            channel.message_count = 150
            for b in channel.get_buildings():
                b.health = b.max_health
        else:
            channel.message = 'You don\'t have enough gold to repair your village.'
            channel.message_color = (255,0,0)
            channel.message_count = 150
        self.Out(channel)




class Builder(InnPC):
    building = Builders
    def __init__(self, inn):
        super().__init__(inn)
        self.building = Builders
        self.cost = (30, 0)

    @property
    def window(self):
        base = {'text':(self.type + ':', 'As long as I\'m at the Inn, all the buildings you buy are smaller!')}
        if self.inn.server.fallen:
            base['options'] = (('Out', self.Out),)
        else:
            base['options'] = (('Add a minute till the Walls fall (100 food)', self.minute), ('Out', self.Out))
        return base
                

    def minute(self, channel):
        if not self.inn.server.fallen:
            if channel.character.food > 99:
                channel.character.food -= 100
                channel.message = 'You added a minute till the Walls fall.'
                channel.message_color = (255,205,0)
                channel.message_count = 150
                self.inn.server.upwall.count += 30*60
                self.inn.server.leftwall.count += 30*60
                self.inn.server.barbarian_count += 30*60
                
            else:
                channel.message = 'You don\'t have enough food to do this.'
                channel.message_color = (255,0,0)
                channel.message_count = 150
        else:
            channel.message = 'The Walls have already fallen!'
            channel.message_color = (0,0,255)
            channel.message_count = 150
        self.Out(channel)



class Advanced_balloonist(InnPC):
    building = AdvancedBalloonistBuilding
    def __init__(self, inn):
        super().__init__(inn)
        self.building = AdvancedBalloonistBuilding
        self.cost = (140, 0)

    @property
    def window(self):
        base = {'text':(self.type + ':', 'I\'m much more talented than the regular balloonist. You\'re lucky you got me!')}
        base['options'] = (('+ Shot Speed (280 gold)', self.speedgun), ('Out', self.Out))
        return base
                

    def speedgun(self, channel):
        player = channel.character
        if player.shot_speed != 1:
            if player.gold > 279:
                player.gold -= 280
                player.channel.message = 'You increased your shoot speed for 280 gold.'
                player.channel.message_color = (255, 205, 0)
                player.channel.message_count = 160
                player.shot_speed -= 1
                
            else:
                player.channel.message = 'You don\'t have enough gold to do this.'
                player.channel.message_color = (255,0,0)
                player.channel.message_count = 150
        else:
            player.channel.message = 'Your balloon gun is already at its max! (Nobody\'s gonna stop you now)'
            player.channel.message_color = (0,0,255)
            player.channel.message_count = 150
        self.Out(channel)

__ALL__ = ['AdventuringCenter',
'RetiredBarbarianOutpost',
'Market',
'BotanistsLab',
'Ranch',
'AlchemistsLab',
'TownHall',
'AdvancedBalloonistBuilding',
'RepairCenter',
'Builders',]

lobbyAvatar.py

import pygame as p
import toolbox as t

class LobbyAvatar():
    def __init__(self, coords):
        self.x, self.y = coords
        self.x_flip = 500-self.x # Center on screen 
        self.y_flip = 325-self.y #   (1000, 650)
        self.angle = 0
        self.speed =  8
        self.ready = False

    @property
    def coords(self):
        return self.x, self.y


    

    def get_x(self, item):
        if hasattr(item, 'x'):
            return item.x + self.x_flip
        return item + self.x_flip
        

    def get_y(self, item):
        if hasattr(item, 'y'):
            return item.y + self.y_flip
        return item + self.y_flip

    def move_x(self, amount=None):
        if amount is None:
            amount = self.speed
        
        self.x += amount
        self.x_flip -= amount

        if self.x < 50:
            self.x -= amount
            self.x_flip += amount
        if self.x > 950:
            self.x -= amount
            self.x_flip += amount
        
    def move_y(self, amount=None):
        if amount is None:
            amount = self.speed
        
        self.y += amount
        self.y_flip -= amount

        if self.y < 50:
            self.y -= amount
            self.y_flip += amount
        if self.y > 600:
            self.y -= amount
            self.y_flip += amount

    def move(self):
        x, y = t.getDir(self.angle, self.speed)
        self.move_x(round(x))
        self.move_y(round(y))

    def HandleInput(self, keys, mouse):
        if keys[p.K_SPACE]:
            self.move()
        self.angle = t.getAngle(500, 325, mouse[0], mouse[1])

menu.py

import pygame as p
import shelve
import math as m
import random
import webbrowser
from time import monotonic, sleep
import os
import sys
import pygame
import threading
import os
import re
import json
import zipfile2
import toolbox as t
from typer import Typing
from pygame.locals import *
from pymsgbox import *
import __main__
import requests, bs4

with open('../preferences.json', 'r') as fo:
    preferences = json.loads(fo.read())
    py = preferences.get('py', 'python')

def version_update(screen, clock, active):
    global FINISHED, download_size, downloaded_bytes

    grey_bar = p.Surface((800, 50))
    grey_bar.fill((95, 95, 95))
    grey_bar_rect = grey_bar.get_rect(center=(500, 300))
    blue_bar_pos = grey_bar_rect.topleft
    title = p.transform.scale(p.image.load('../assets/title page/attempt 1.png'), (500, 100))

    fo = open('tips.json', 'r')
    all_tips = json.loads(fo.read())
    fo.close()
    tips = []
    for tip in all_tips['basic_tips']:
        tip = font40.render(tip, True, (255,205,0))
        tip_rect = tip.get_rect(midbottom=(500, 620))
        tips.append((tip, tip_rect))
    for tip in all_tips['rare_tips']:
        tip = font40.render(tip, True, (255,0,128))
        tip_rect = tip.get_rect(midbottom=(500, 620))
        tips.append((tip, tip_rect))
    random.shuffle(tips)
    
    
    while True:        
        try:
            perc = downloaded_bytes/download_size*100
        except NameError:
            perc = 0
            
        title_rect = title.get_rect(midtop=(500, (10 * (m.sin(monotonic() * 2))) + 30)) # Update title location

        for e in p.event.get():
            if e.type == QUIT and confirm('Are you sure you want to cancel this update?', buttons=['Yes', 'No']) == 'Yes':
                p.quit()
                sys.exit()
        screen.fill((255,255,255))

            
        try:
            blue_bar = p.Surface((perc/100*800, 50))
        except:
            blue_bar = p.Surface((0, 50))
        blue_bar.fill((10, 10, 250))

        try:
            out_of = '%s / %s' % (downloaded_bytes, download_size)
        except:
            out_of = '%s / %s' % (0, '?')

            
        screen.blit(title, title_rect)
        screen.blit(grey_bar, blue_bar_pos)
        screen.blit(blue_bar, blue_bar_pos)
        
        perctext = p.font.SysFont('default', 40).render(str(round(perc, 1)) + ' %', True, (255,128,0))
        perctextrect = perctext.get_rect(midtop=(500, 330))
        screen.blit(perctext, perctextrect)
        
        #perctext = p.font.SysFont('default', 30).render(out_of, True, (255,66,66))
        #perctextrect = perctext.get_rect(midtop=(500, 400))
        #screen.blit(perctext, perctextrect)
        
        tip = tips[int((monotonic()/6)%len(tips))]
        screen.blit(tip[0], tip[1])
            
        p.display.set_caption('VillageWars %s --> %s Update:  %s' % (__main__.__version__, active, out_of))

        p.display.update()
        clock.tick(60)


        if FINISHED:
            alert('Version %s has been installed\nsuccessfuly!' % active, title='VillageWars')
            with open('../preferences.json', 'r') as fo:
                preferences = json.loads(fo.read())
                py = preferences.get('py', 'python')
                path = preferences.get('path', [])
                map = preferences.get('map', 'grass')
            os.chdir('../../%s' % active)
            with open('../preferences.json', 'w') as fo:
                preferences = {'map':map, 'py':py, 'path':path}
                fo.write(json.dumps(preferences))
            p.quit()      
            os.system(py + ' -m main.py')
            sys.exit()
                
    

def download_active_version(active):
    global zipfile2
    global FINISHED, download_size, downloaded_bytes
    FINISHED = False
    print('Sent files request')
    res = requests.get('http://villagewars.pythonanywhere.com/download_active_version', stream=True)
    res.raise_for_status()
    download_size = int(res.headers['Content-length'])
    downloaded_bytes = 0
    fo = open('../run/downloads/new_version.zip', 'wb')
    for chunk in res.iter_content(chunk_size=2048):
        if chunk: # filter out keep-alive new chunks
            len_chunk = len(chunk)
            fo.write(chunk)
            downloaded_bytes += len_chunk

    fo.close()  # Found commented out, i hope it wasn't important because it crashes the update
    fo = zipfile2.ZipFile('../run/downloads/new_version.zip', 'r')
    
    os.makedirs('../../%s' % active, exist_ok=True)
    fo.extractall('../../%s' % active)
    fo.close()
    FINISHED = True

def load(result):
    href = result['href']
    name = result['name']
    res = requests.get(href)
    soup = bs4.BeautifulSoup(res.text, 'html.parser')
    elem = soup.select('.mw-parser-output')[0]
    return name + '\n' + elem.text

def load_results(search):
    results = []
    try:
        res = requests.get('https://villagewars.fandom.com/wiki/Special:Search?query=%s&scope=internal&navigationSearch=true&so=trending' % search)
        soup = bs4.BeautifulSoup(res.text, 'html.parser')
        elements = soup.select('.unified-search__result__title')  # All search responses
        results = []
        for elem in elements:
             results.append({'href':elem.get('href'), 'name':elem.get('data-title')})
        global guide_results, scroll
        scroll = 0
        guide_results = True
    except:
        alert('You need to be connected\nto the Internet.', title='VillageWars')
    return results
    
def runMenu(screen, clock):
    global mouse, click, keys, open_menu_surf, open_menu_hover_surf, options, tab, menu_space, font40, creds, font30, scroll, guide_results, carry_to
    active = '0.0.1'

    mouse = p.mouse.get_pos()
    click = p.mouse.get_pressed()
    keys = p.key.get_pressed()
    width, height = screen.get_width(), screen.get_height()
    
    font40 = p.font.SysFont('default', 40)
    font200 = p.font.SysFont('default', 200)
    font30 = p.font.SysFont('default', 30)
    font100 = p.font.SysFont('default', 100)
    title = p.transform.scale(p.image.load('../assets/title page/attempt 1.png'), (500, 100))
    menu_space = 30

    open_menu_surf = p.transform.scale(p.image.load('../assets/title page/open menu.png'), (22, 22))
    open_menu_hover_surf = p.transform.scale(p.image.load('../assets/title page/open menu_hover.png'), (22, 22))

    background = p.image.load('../assets/optional/{}.png'.format(random.randrange(3)))
    background.set_alpha(180)
    background_rect = background.get_rect(topright=(1000,0))

    logIn_button = t.Button(screen, 'title page/log in to play.png', 'title page/log in to play_hover.png', 'title page/log in to play_down.png', midtop=(((width-menu_space)/2) + menu_space, 400))
    #logInToPlay = p.image.load('../assets/title page/log in to play.png')
    #logInToPlay_hover = p.image.load('../assets/title page/log in to play_hover.png')

    #darker_surf = p.image.load('../assets/paused.png')
    #credits_fade = p.image.load('../assets/title page/credits fade.png')

    font80 = p.font.SysFont('default', 80)

    tab = 'Home'
    guide_results = True

    fo = open('splash.txt')
    splash = [s[:-1] for s in fo.readlines()]
    fo.close()
    splash = splash[random.randrange(len(splash))]
    #splash = 'Ha!'
    #print(len(splash))
    splashFont = p.font.SysFont('default', 80-len(splash))
    splash = splashFont.render(splash, True, (250,250,100))

    creds = []
    results = []
    searcher = Typing(block=[])

    scroll = 0

    with open('../../version screenshots/version_info.json', 'r') as data_file:
        version_data = json.loads(data_file.read())
        active = version_data['active']
        version_data = version_data['versions']
    
    mag_glass = p.transform.scale(p.image.load('../assets/title page/search.png'), (30, 30))
    search_activated = False

    release_screenshots = {file[:-4]: p.transform.scale(p.image.load('../../version screenshots/' + file), (250, 162)) for file in os.listdir('../../version screenshots') if file != 'version_info.json'}
    lovable_versions = [folder for folder in os.listdir('../../') if (not folder.endswith('.txt') and '.' in folder and folder in version_data)]
    play_version_buttons = {}
    for version in lovable_versions:
        play_version_buttons[version] = t.Button(screen, 'play version.png', 'play version_hov.png', 'play version_down.png')
    install_version_button = t.Button(screen, 'install_version.png', 'install_version_hov.png', 'install_version_down.png', topleft=(-1000, -650))
    release_screenshots['default'] = p.transform.scale(p.image.load('../run/pre-downloaded/default.png'), (250, 162))

    
    while True:
        options = ['Home', 'Language', 'Statistics', 'Releases', 'Guide', 'Credits', 'Feedback']
        options.remove(tab)
        
        for event in pygame.event.get():
            if event.type == QUIT:
                p.quit()
                sys.exit()
            if tab == 'Guide' and event.type == KEYUP and event.key == K_RETURN and search_activated:
                search = searcher.result
                search_activated = False
                results = load_results(search)
                
            if event.type == KEYUP and event.key == K_F1:
                p.mixer.music.load('../assets/sfx/menu.mp3')
                p.mixer.music.play(-1, 0.0)
            if tab == 'Guide' and event.type in (KEYDOWN, KEYUP) and event.key in (K_RSHIFT, K_LSHIFT):
                searcher.shift()
            if tab == 'Guide' and event.type in (KEYDOWN, KEYUP) and event.key in (K_RCTRL, K_LCTRL):
                searcher.shift()
            if tab == 'Guide' and event.type == KEYUP:
                searcher.type(event)
            if event.type == MOUSEBUTTONDOWN:
                if tab in ('Guide', 'Releases'):
                    if event.button == 5:
                        scroll -= 20
                    elif event.button == 4:
                        if scroll < 0:
                            scroll += 20
            if tab == 'Home' and logIn_button.handle_event(event):
                do_tabs(screen, menu_space)
                result = runLogIn(screen, clock)
                if type(result) == dict:
                    return result
            if tab == 'Releases':# and menu_space == 30:
                for version in play_version_buttons:
                    button = play_version_buttons[version]
                    if button.x != 0 and button.handle_event(event):
                        os.chdir('../../%s/src' % version)
                        try:
                            os.system(py + ' -m VillageWarsClient')
                        except:     
                            os.system(py + ' -m SuperShooterWar_startgame')
                        p.quit()
                        sys.exit()
                        #alert('Not Implemented.', title='VillageWars')
                if active != __main__.__version__ and install_version_button.handle_event(event):
                    if confirm('A version installation is about to begin. Click OK to start it. This may take several minutes.', title='VillageWars') == 'OK':
                        thread = threading.Thread(target=download_active_version, args=[active])
                        thread.start()
                        version_update(screen, clock, active)
                        lovable_versions = [folder for folder in os.listdir('../../') if (not folder.endswith('.txt') and '.' in folder)]
                        play_version_buttons = {}
                        for version in lovable_versions:
                            play_version_buttons[version] = t.Button(screen, 'play version.png', 'play version_hov.png', 'play version_down.png')
                    
        mouse = p.mouse.get_pos()
        click = p.mouse.get_pressed()
        keys = p.key.get_pressed()
        width, height = screen.get_width(), screen.get_height()

        title_rect = title.get_rect(midtop=(((width-menu_space)/2) + menu_space, (10 * (m.sin(monotonic() * 2))) + 30 + scroll)) # Update title location
        splash_rect = splash.get_rect(midtop=((100 * (m.sin((monotonic()+1) * 2.5))) + ((width-menu_space)/2) + menu_space, 180))
        

        
        if tab == 'Home':
            scroll = 0
            screen.fill((255,255,255))
            screen.blit(background, background_rect)
            screen.blit(title, title_rect)
            screen.blit(splash, splash_rect)
            logIn_button.draw()
            
            #if logIn_rect.collidepoint(mouse):
            #    screen.blit(logInToPlay_hover, logIn_rect)
            #    if click[0]:
                    
            #else:
            #    screen.blit(logInToPlay, logIn_rect)

            
        if tab == 'Language':
            screen.fill((255,255,255))
            text_surf = font40.render('Not Implemented yet.', True, (0,0,0))
            rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, 300 + scroll))
            screen.blit(text_surf, rect)
            
        if tab == 'Statistics':
            screen.fill((255,255,255))
            text_surf = font40.render('Not Implemented yet.', True, (0,0,0))
            rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, 300 + scroll))
            screen.blit(text_surf, rect)
            
        if tab == 'Releases':
            
            screen.fill((20,20,20))
            #screen.blit(title, title_rect)
            y = 100
            
                
            for version in version_data:
                if version == __main__.__version__:
                    text_surf = font40.render('Current version - %s - %s' % (version, version_data[version]['description']), True, (255,0,0))
                    rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(text_surf, rect)
                    y += 50
                    if 'date' in version_data[version]:
                        text_surf = font40.render(version_data[version]['date'], True, (128,128,255))
                        rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                        screen.blit(text_surf, rect)
                        y += 40
                    screenshot = release_screenshots.get(version, release_screenshots['default'])
                    rect = screenshot.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(screenshot, rect)
                    y += rect.height + 50
                    if carry_to:
                        scroll = (-y) + height // 2 + 200
                        carry_to = False
                    if t.getVersionInt(active) > t.getVersionInt(__main__.__version__):
                        text_surf = font40.render('New updates available below!', True, (255,128,255))
                        rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                        screen.blit(text_surf, rect)
                        y += 80
                    
                elif t.getVersionInt(version) > t.getVersionInt(active): #__main__.__version__):
                    text_surf = font40.render('Version %s - %s' % (version, version_data[version]['description']), True, (255,200,200))
                    rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(text_surf, rect)
                    y += 50
                elif t.getVersionInt(version) > t.getVersionInt(__main__.__version__):
                    text_surf = font40.render('Version %s - %s' % (version, version_data[version]['description']), True, (255,255,255))
                    rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(text_surf, rect)
                    y += 50
                    if 'date' in version_data[version]:
                        text_surf = font40.render(version_data[version]['date'], True, (255,128,128))
                        rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                        screen.blit(text_surf, rect)
                        y += 40
                    screenshot = release_screenshots.get(version, release_screenshots['default'])
                    rect = screenshot.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(screenshot, rect)
                    if version in play_version_buttons:
                        play_version_buttons[version].x = 700
                        play_version_buttons[version].y = y + 50 + scroll
                        play_version_buttons[version].draw()
                    if version == active:
                        install_version_button.x = 700
                        install_version_button.y = y + 50 + scroll
                        install_version_button.draw()
                        
                    
                    y += rect.height + 50
                else:
                    text_surf = font40.render('Version %s - %s' % (version, version_data[version]['description']), True, (255,255,255))
                    rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(text_surf, rect)
                    y += 50
                    if 'date' in version_data[version]:
                        text_surf = font40.render(version_data[version]['date'], True, (128,128,255))
                        rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                        screen.blit(text_surf, rect)
                        y += 40
                    screenshot = release_screenshots.get(version, release_screenshots['default'])
                    rect = screenshot.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(screenshot, rect)
                    if version in play_version_buttons:
                        play_version_buttons[version].x = 700
                        play_version_buttons[version].y = y + 50 + scroll
                        play_version_buttons[version].draw()
                    y += rect.height + 50
                if version == active:
                    text_surf = font40.render('See below for upcoming releases!', True, (255,128,255))
                    rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, y + scroll))
                    screen.blit(text_surf, rect)
                    y += 80
                    
                #p.draw.rect(install_version_button.rect, (0,255,0))
            
        if tab == 'Guide':
            screen.fill((255,255,255))
            #screen.blit(title, title_rect)

            text_surf = font40.render('This is a work in progress.', True, (0,0,0))
            rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, 100 + scroll))
            screen.blit(text_surf, rect)
            text_surf = font40.render('To view the output better, see villagewars.fandom.com', True, (0,0,0))
            rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, 150 + scroll))
            screen.blit(text_surf, rect)

            if guide_results:
                
                y = 250
                for result in results:
                    text = font40.render(result['name'], True, (60,0,62))
                    rect = text.get_rect(topleft=(250, y + scroll))
                    if rect.collidepoint(mouse):
                        text = font40.render(result['name'], True, (120,0,124))
                        if click[0]:
                            guide_results = False
                            result_text = load(result)
                            results = []

                    screen.blit(text, rect)
                    y += 50
            else:
                y = 300
                for i in result_text.split('\n'):
                    text_surf = font40.render(i, True, (0,0,0))
                    rect = text_surf.get_rect(topleft=(200, y + scroll))
                    screen.blit(text_surf, rect)
                    y += 50

            surf = p.Surface((width, 50))
            surf.fill((200,0,0))
            screen.blit(surf, (0,0))

            surf = p.Surface((width-menu_space-50, 40))
            white_rect = surf.get_rect(topleft=(menu_space + 25, 5))
            if white_rect.collidepoint(mouse):
                if not search_activated:
                    surf.fill((240,240,240))
                else:
                    surf.fill((255,255,255))
                if click[0]:
                    search_activated = True
            else:
                surf.fill((255,255,255))
                if click[0]:
                    search_activated = False
            screen.blit(surf, white_rect)

            mag_rect = mag_glass.get_rect(topleft=(width-65, 10))
            if mag_rect.collidepoint(mouse):
                scaled = p.transform.scale(mag_glass, (35, 35))
                scaled_rect = scaled.get_rect(center=mag_rect.center)
                screen.blit(scaled, scaled_rect)
                if click[0]:
                    search = searcher.result
                    results = load_results(search)
            else:
                screen.blit(mag_glass, mag_rect)

            if search_activated:
                text = font40.render(searcher.text(), True, (0,0,0))
            else:
                if searcher.result:
                    text = font40.render(searcher.result, True, (0,0,0))
                else:
                    text = font40.render('Click here to search', True, (188,188,188))
            rect = text.get_rect(midleft=(white_rect.midleft[0] + 5, white_rect.midleft[1]))
            screen.blit(text, rect)
            
            
            
            
            
        if tab == 'Credits':
            screen.fill((0,0,0))

            
            
            if creds == []:
                Credit.space = 650
                
                creds.append(Credit('- Credits -', creds, font=font200, y=250))
                
                creds.append(Credit('VillageWars', creds, font=font100, y=200))
                
                creds.append(Credit('Creator: Aaron McCormick', creds, font=font40, y=100))
                
                creds.append(Credit('Programming:', creds, font=font40, y=80))

                creds.append(Credit('Surpervisor: Aaron McCormick', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Game Mechanics (Client + Server): Aaron McCormick', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Graphical Representation: pygame (module)', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Windows Messages: pymsgbox (module)', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Client-Server pumping: PodSixNet (module)', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Copy and paste with Windows clipboard: pyperclip (module)', creds, font=font40, y=80, rjust=True))

                creds.append(Credit('Graphics:', creds, font=font40, y=80))

                creds.append(Credit('Player Skins, Balloons(+ Water Droplets), Crates, TNT,', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('                            Robots: Codakid Game Dev 2', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Map, Gates, Buildings, Building Borders: Aaron McCormick', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Walls, Trees, Spiky Bushes: opengameart.org', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Barbarians: Aaron McCormick, inspired by skin number 2', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('                                   (Codakid Game Dev 2)', creds, font=font40, y=80, rjust=True))
                creds.append(Credit('Music and Sound Effects:', creds, font=font40, y=80))

                creds.append(Credit('All Sound Effects (splashes, bump, explosions, shots):', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('                                    Codakid Game Dev 2', creds, font=font40, y=50, rjust=True))
                
                creds.append(Credit('Stepping Pebbles (Before the Walls fall): Matthew Pablo', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('                                      (opengameart.org)', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('After the Walls fall: opengameart.org', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('During the Barbarian Raid: opengameart.org', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Menu Music: opengameart.org', creds, font=font40, y=80, rjust=True))

                creds.append(Credit('Exteral Server Management:', creds, font=font40, y=80))
                
                creds.append(Credit('Programming: Aaron McCormick', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Support: Colin McCormick', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Module: Flask', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Application: Pythonanywhere.com', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Website: villagewars.pythonanywhere.com', creds, font=font40, y=80, rjust=True))

                creds.append(Credit('Co-designers (Idea givers):', creds, font=font40, y=80))

                creds.append(Credit('Caden McCormick, Lucas McComick, David McCormick,', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Angela McCormick, Charlie Garland, Owen Garland,', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Samuel Mawson, John Mawson, Colin McCormick,', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Nathan Dauner, Vincent Santoni, Jonathan Hostteter,', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('Gavin McCormick', creds, font=font40, y=50, rjust=True))
                creds.append(Credit('If you would like to see your name here, send me an email', creds, font=font30, y=30, color=(255,100,255)))
                creds.append(Credit('at [email protected] giving me ideas! Your idea won\'t', creds, font=font30, y=30, color=(255,100,255)))
                creds.append(Credit('necessarily be added, but your name will be added here!', creds, font=font30, y=80, color=(255,100,255)))

                #fo = shelve.open('database/data')
                #users = dict(fo)
                #fo.close()
                print(1)
                if __main__.INTERNET:
                    print(2)
                    try:
                        res = requests.get(__main__.flask_application + 'all_users')
                        res.raise_for_status()
                        print(3)
                    except:
                        __main__.INTERNET = False
                else:
                    alert('You need to be connected\nto the Internet.', title='VillageWars')
                    tab = 'Home'
                if __main__.INTERNET:
                    print(4)
                    users = res.text[4:]
                    users = eval(users)
                        
                    creds.append(Credit('First %s Accounts:' % len(users), creds, font=font40, y=80))
                        
                    for i, user in enumerate(list(users)):
                        if i+1 == len(users):
                            creds.append(Credit('%s) %s (%s)' % (i+1, user, users[user].get('name', 'No Name')), creds, font=font40, y=60, rjust=False))
                        else:
                            name = users[user].get('name', 'No Name')
                            if user in ('f', 'ModestNoob', 'ff'):
                                name = 'Aaron McCormick, test account'
                            if user == 'ProHacker':
                                name = 'Aaron McCormick, fake games account'
                            if user == 'Warpedillager':
                                name = 'Aaron McCormick, stunts account'
                            creds.append(Credit('%s) %s (%s)' % (i+1, user, name), creds, font=font40, y=50, rjust=False))
                        
                        

                    creds.append(Credit('...and new accounts are created every month!', creds, font=font40, y=200, rjust=True, color=(128,128,255)))                               

                    creds.append(Credit('Thank you everyone!', creds, font=font100, y=0, color=(255,255,5)))
                        
            for credit in creds: 
                credit.update(screen, keys=keys)
            
            
        if tab == 'Feedback':
            screen.fill((255,255,255))
            text_surf = font40.render('To send feedback, email me at [email protected].', True, (0,0,0))
            rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, 100 + scroll))
            screen.blit(text_surf, rect)
            text_surf = font40.render('I appreciate ideas, feedback, and support!', True, (0,0,0))
            rect = text_surf.get_rect(midtop=((width-menu_space)//2+menu_space, 150 + scroll))
            screen.blit(text_surf, rect)


        do_tabs(screen, menu_space)

        
        
            

        p.display.update()
        if keys[K_F9]:
            p.display.set_caption('VillageWars ' + __main__.__version__ + ' - FPS: ' + str(round(clock.get_fps(), 1)))
        else:
            p.display.set_caption('VillageWars ' + __main__.__version__)
        clock.tick(60)
    
    return t.getMyIP(), 5555, 'sign in'

class Credit():
    space = 0
    def __init__(self, text, creds, y=0, font=None, rjust=False, color=(255,255,255)):
        self.y = type(self).space
        type(self).space += y
        self.surf = font.render(text, True, color)
        self.credits = creds
        self.rjust = rjust
    def update(self, screen, keys=0):
        global menu_space
        if not (keys[K_PAGEDOWN] or keys[K_DOWN]):
            self.y -= 0.6
        else:
            self.y -= 3
        if self.rjust:
            self.rect = self.surf.get_rect(topleft=((1000-menu_space)/2+menu_space-360, self.y))
        else:
            self.rect = self.surf.get_rect(midtop=((1000-menu_space)/2+menu_space, self.y))
        self.surf.set_alpha(min([((650-self.rect.y)/650*255), 255]))
        screen.blit(self.surf, self.rect)
        if self.y < -200:
            self.credits.remove(self)

def do_tabs(screen, width=1000, height=650):
    global mouse, click, keys, open_menu_surf, open_menu_hover_surf, options, tab, menu_space, font40, creds, scroll, carry_to
    menu_surf = p.Surface((menu_space, height))
    menu_surf.fill((50,0,50))
    screen.blit(menu_surf, (0,0))   
    if menu_space == 30:
        rect = open_menu_surf.get_rect(topleft=(4,4))
    else:
        rect = open_menu_surf.get_rect(midtop=(100,4))
    if rect.collidepoint(mouse):
        screen.blit(open_menu_hover_surf, rect)
        if click[0]:
            if menu_space == 30:
                menu_space = 200
            else:
                menu_space = 30
    else:
        screen.blit(open_menu_surf, rect)

    if menu_space == 200:
        x = 10
        y = 85
        for option in options:
            rect_surf = p.Surface((200, 100))  # rectangle around text in menu
            rect = rect_surf.get_rect(topleft=(0, y-35))
            if rect.collidepoint(mouse):
                rect_surf.fill((200,50,200))
                screen.blit(rect_surf, rect)
                surf = font40.render(option, True, (255,255,255))  # text surf
                if click[0]:
                    tab = option
                    menu_space = 30
                    scroll = 0
                    
                    carry_to = True
                    if tab == 'Credits':
                        creds = []
            else:
                surf = font40.render(option, True, (255,255,180))  # text surf
            rect = surf.get_rect(midtop=(100, y))
            screen.blit(surf, rect)
            y += 100
            

def submitLogIn(username, password):
    u = []
    if len(username) == 0:
        u.append('username')
    if len(password) == 0:
        u.append('password')
    if len(u) == 0:
        return {'create':False, 'username':username, 'password':password}
    return u
        

def runLogIn(screen, clock):
    global font40, font30

    logInPopup = p.image.load('../assets/title page/log in popup.png')

    darker = p.image.load('../assets/paused.png')
    for i in range(2):
        screen.blit(darker, (0,0))

    username_typer = Typing(block=['@', '/', '.'])
    password_typer = Typing(block=[])

    typingOn = None
    fontUnderline = p.font.SysFont('default', 35, italic=True)

    logIn_surf = p.Surface((240, 50))
    logIn_surf.fill((0,141,15))
    logIn_surf_hov = p.Surface((240, 50))
    logIn_surf_hov.fill((10,10,255))
    logIn_surf_down = p.Surface((240, 50))
    logIn_surf_down.fill((60,60,255))
    rect = logIn_surf.get_rect(topleft=(0,0))
    
    text_surf = font40.render('Log In!', True, (255,150,150))
    rect = text_surf.get_rect(center=rect.center)
    logIn_surf_hov.blit(text_surf, rect)
    logIn_surf_down.blit(text_surf, rect)
    text_surf = font40.render('Log In!', True, (0,0,0))
    rect = text_surf.get_rect(center=rect.center)
    logIn_surf.blit(text_surf, rect)
    
    logIn_button = t.Button.from_surf(screen, logIn_surf, logIn_surf_hov, logIn_surf_down, midtop=((1000-menu_space)//2+menu_space, 400))

    u_prob = False
    p_prob = False
    
    while True:


        

        
        for event in pygame.event.get():
            if event.type == QUIT:
                p.quit()
                sys.exit()
            if event.type == KEYUP and event.key == K_ESCAPE:
                return 'Cancel'
            elif event.type == KEYUP:
                if typingOn:
                    typingOn.type(event)
            if event.type in (KEYUP, KEYDOWN) and event.key in (K_RSHIFT, K_LSHIFT):
                username_typer.shift()
                password_typer.shift()
            if event.type in (KEYUP, KEYDOWN) and event.key in (K_RCTRL, K_LCTRL):
                username_typer.ctrl()
                password_typer.ctrl()
            if event.type == KEYUP and event.key == (K_TAB):
                if typingOn is None:
                    typingOn = username_typer
                if typingOn == username_typer:
                    typingOn = password_typer
                else:
                    typingOn = username_typer
            if event.type == KEYUP and event.key == K_RETURN:
                if typingOn == username_typer:
                    typingOn = password_typer
                elif typingOn == password_typer:
                    result = submitLogIn(username_typer.result, password_typer.result)
                    if type(result) == dict:
                        return result
                    if 'username' in result:
                        u_prob = True
                    if 'password' in result:
                        p_prob = True
            if logIn_button.handle_event(event):
                result = submitLogIn(username_typer.result, password_typer.result)
                if type(result) == dict:
                    return result
                elif 'username' in result:
                    u_prob = True
                if 'password' in result:
                    p_prob = True
                
        mouse = p.mouse.get_pos()
        click = p.mouse.get_pressed()
        keys = p.key.get_pressed()
        width, height = screen.get_width(), screen.get_height()

        logInRect = logInPopup.get_rect(center=((1000-menu_space)//2+menu_space, height // 2))

        screen.blit(logInPopup, logInRect)

        username_rect = p.Surface((320, 50)).get_rect(topleft=((1000-menu_space)//2+menu_space-160, 150))
        if username_rect.collidepoint(mouse) and typingOn != username_typer:
            surf = p.Surface((320, 50))
            surf.fill((100,100,255))
            surf.set_alpha(50)
            screen.blit(surf, username_rect)
            if click[0]:
                typingOn = username_typer
        password_rect = p.Surface((320, 50)).get_rect(topleft=((1000-menu_space)//2+menu_space-160, 300))
        if password_rect.collidepoint(mouse) and typingOn != password_typer:
            surf = p.Surface((320, 50))
            surf.fill((100,100,255))
            surf.set_alpha(50)
            screen.blit(surf, password_rect)
            if click[0]:
                typingOn = password_typer

        if typingOn == username_typer:
            username_surf = font40.render(username_typer.text(), True, (0,0,0))
            rect = username_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 160))
        elif typingOn != username_typer:
            if username_typer.text() in ('|','') and u_prob:
                username_surf = font40.render('Fill this out first.', True, (250,0,0))
            else:
                username_surf = font40.render(username_typer.result, True, (0,0,0))
            rect = username_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 160))
            if rect.right > (1000-menu_space)//2+menu_space+160:
                username_typer.typing = username_typer.typing[:-2] + username_typer.character
        try:
            screen.blit(username_surf, rect)
        except:
            pass

        surf = font40.render('Enter your username:', True, (255,255,255))
        rect = surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 110))
        screen.blit(surf, rect)
        surf = font40.render('Enter your password:', True, (255,255,255))
        rect = surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 260))
        screen.blit(surf, rect)
        
        if typingOn == password_typer:
            if password_typer.text().endswith('|'):
                password_surf = font40.render(len(password_typer.result) * '*' + '|', True, (0,0,0))
            else:
                password_surf = font40.render(len(password_typer.result) * '*', True, (0,0,0))
            rect = password_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 310))
        elif typingOn != password_typer:
            if password_typer.result in ('|', '') and p_prob:
                password_surf = font40.render('Fill this out first.', True, (250,0,0))
            else:
                password_surf = font40.render(len(password_typer.result) * '|', True, (0,0,0))
            rect = password_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 310))
            if rect.right > (1000-menu_space)//2+menu_space+160:
                password_typer.typing = password_typer.typing[:-2] + password_typer.character
        try:
            screen.blit(password_surf, rect)
        except:
            pass

        logIn_button.draw()
        #rect = p.Surface((240, 50)).get_rect(midtop=((1000-menu_space)//2+menu_space, 400))
        #if rect.collidepoint(mouse):
        #    surf = p.Surface((240,50))
        #    surf.fill((10,10,255))
        #    screen.blit(surf, rect)
        #    text_surf = font40.render('Log In!', True, (255,150,150))
        #    rect = text_surf.get_rect(center=rect.center)
        #    screen.blit(text_surf, rect)
        #    if click[0]:
        #        result = submitLogIn(username_typer.result, password_typer.result)
        #        if type(result) == dict:
        #            return result
        #        elif 'username' in result:
        #            u_prob = True
        #        if 'password' in result:
        #            p_prob = True
        #else:
        #    text_surf = font40.render('Log In!', True, (0,0,0))
        #    rect = text_surf.get_rect(center=rect.center)
        #    screen.blit(text_surf, rect)


        slump_rect = p.Surface((300, 80)).get_rect(midbottom=((1000-menu_space)//2+menu_space, 580))
        if slump_rect.collidepoint(mouse):
            text_surf = fontUnderline.render("Don't have an account?", True, (128,128,255))
            rect = text_surf.get_rect(midbottom=((1000-menu_space)//2+menu_space, 540))
            screen.blit(text_surf, rect)
            text_surf = fontUnderline.render("Create one here!", True, (128,128,255))
            rect = text_surf.get_rect(midbottom=((1000-menu_space)//2+menu_space, 580))
            screen.blit(text_surf, rect)
            if click[0]:
                return runCreateAccount(screen, clock)
        else:
            text_surf = fontUnderline.render("Don't have an account?", True, (0,0,255))
            rect = text_surf.get_rect(midbottom=((1000-menu_space)//2+menu_space, 540))
            screen.blit(text_surf, rect)
            text_surf = fontUnderline.render("Create one here!", True, (0,0,255))
            rect = text_surf.get_rect(midbottom=((1000-menu_space)//2+menu_space, 580))
            screen.blit(text_surf, rect)

        p.display.update()
        if keys[K_F9]:
            p.display.set_caption('VillageWars ' + __main__.__version__ + ' - FPS: ' + str(round(clock.get_fps(), 1)))
        else:
            p.display.set_caption('VillageWars ' + __main__.__version__)
        clock.tick(60)

def runCreateAccount(screen, clock):
    global font40, font30

    logInPopup = p.image.load('../assets/title page/create account popup.png')

    #darker = p.image.load('../assets/paused.png')
    #for i in range(2):
    #    screen.blit(darker, (0,0))

    username_typer = Typing(block=['@', '/', '.', ' '])
    password_typer = Typing(block=[])
    cpassword_typer = Typing(block=[])
    name_typer = Typing(block=['@', '/'])
    email_typer = Typing(block=['/', ' '])

    u_prob = False
    p_prob = False
    c_prob = False
    n_prob = False
    match_prob = False

    typingOn = None
    fontUnderline = p.font.SysFont('default', 35, italic=True)

    surf = p.Surface((242, 57))
    surf.fill((10,10,255))
    surf_hov = p.Surface((242, 57))
    surf_hov.fill((50,50,255))
    surf_down = p.Surface((242, 57))
    surf_down.fill((80,80,255))
    rect = surf.get_rect(topleft=(0,0))
    
    text_surf = font40.render('Create Account!', True, (255,150,150))
    rect = text_surf.get_rect(center=rect.center)
    surf_hov.blit(text_surf, rect)
    surf_down.blit(text_surf, rect)
    text_surf = font40.render('Create Account!', True, (0,0,0))
    rect = text_surf.get_rect(center=rect.center)
    surf.blit(text_surf, rect)
    
    create_button = t.Button.from_surf(screen, surf, surf_hov, surf_down, midtop=((1000-menu_space)//2+menu_space, 543))
    
    while True:


        

        
        for event in pygame.event.get():
            if event.type == QUIT:
                p.quit()
                sys.exit()
            if event.type == KEYUP and event.key == K_ESCAPE:
                return 'Cancel'
            elif event.type == KEYUP:
                if typingOn:
                    typingOn.type(event)
            if event.type in (KEYUP, KEYDOWN) and event.key in (K_RSHIFT, K_LSHIFT):
                username_typer.shift()
                password_typer.shift()
                cpassword_typer.shift()
                name_typer.shift()
                email_typer.shift()
            if event.type in (KEYUP, KEYDOWN) and event.key in (K_RCTRL, K_LCTRL):
                username_typer.ctrl()
                password_typer.ctrl()
                cpassword_typer.ctrl()
                name_typer.ctrl()
                email_typer.ctrl()
            if event.type == KEYUP and event.key == (K_TAB):
                if typingOn is None:
                    typingOn = username_typer
                if typingOn == username_typer:
                    typingOn = password_typer
                elif typingOn == password_typer:
                    typingOn = cpassword_typer
                elif typingOn == cpassword_typer:
                    typingOn = name_typer
                elif typingOn == name_typer:
                    typingOn = email_typer
                elif typingOn == email_typer:
                    typingOn = username_typer
            if event.type == KEYUP and event.key == K_RETURN:
                if typingOn == username_typer:
                    typingOn = password_typer
                elif typingOn == password_typer:
                    typingOn = cpassword_typer
                elif typingOn == cpassword_typer:
                    typingOn = name_typer
                elif typingOn == name_typer:
                    typingOn = email_typer
                else:
                    result = submitCreate(username_typer.result, password_typer.result, cpassword_typer.result, name_typer.result, email=email_typer.result)
                    if type(result) == dict:
                        return result
                    elif 'username' in result:
                        u_prob = True
                    if 'password' in result:
                        p_prob = True
                    if 'cpassword' in result:
                        c_prob = True
                    if 'name' in result:
                        n_prob = True
                    match_prob = 'match' in result
                    if match_prob:
                        password_typer = Typing(block=[])
                        cpassword_typer = Typing(block=[])
                        p_prob = False
                        c_prob = False
            if create_button.handle_event(event):
                result = submitCreate(username_typer.result, password_typer.result, cpassword_typer.result, name_typer.result, email=email_typer.result)
                if type(result) == dict:
                    return result
                elif 'username' in result:
                    u_prob = True
                if 'password' in result:
                    p_prob = True
                if 'cpassword' in result:
                    c_prob = True
                if 'name' in result:
                    n_prob = True
                match_prob = 'match' in result
                if match_prob:
                    password_typer = Typing(block=[])
                    cpassword_typer = Typing(block=[])
                    p_prob = False
                    c_prob = False
                
        mouse = p.mouse.get_pos()
        click = p.mouse.get_pressed()
        keys = p.key.get_pressed()
        width, height = screen.get_width(), screen.get_height()

        logInRect = logInPopup.get_rect(center=((1000-menu_space)//2+menu_space, height // 2))

        screen.blit(logInPopup, logInRect)


        
        
        # Show the box for the inputs, and change active input if needed

        username_rect = p.Surface((320, 50)).get_rect(topleft=((1000-menu_space)//2+menu_space-160, 60))
        surf = p.Surface((320, 50))
        surf.fill((255,255,255))
        screen.blit(surf, username_rect)
        if username_rect.collidepoint(mouse) and typingOn != username_typer:            
            surf = p.Surface((320, 50))
            surf.fill((100,100,255))
            surf.set_alpha(50)
            screen.blit(surf, username_rect)
            if click[0]:
                typingOn = username_typer
                
        password_rect = p.Surface((320, 50)).get_rect(topleft=((1000-menu_space)//2+menu_space-160, 160))
        surf = p.Surface((320, 50))
        surf.fill((255,255,255))
        screen.blit(surf, password_rect)
        if password_rect.collidepoint(mouse) and typingOn != password_typer:
            surf = p.Surface((320, 50))
            surf.fill((100,100,255))
            surf.set_alpha(50)
            screen.blit(surf, password_rect)
            if click[0]:
                typingOn = password_typer
                
        cpassword_rect = p.Surface((320, 50)).get_rect(topleft=((1000-menu_space)//2+menu_space-160, 260))
        surf = p.Surface((320, 50))
        surf.fill((255,255,255))
        screen.blit(surf, cpassword_rect)
        if cpassword_rect.collidepoint(mouse) and typingOn != cpassword_typer:          
            surf = p.Surface((320, 50))
            surf.fill((100,100,255))
            surf.set_alpha(50)
            screen.blit(surf, cpassword_rect)
            if click[0]:
                typingOn = cpassword_typer

        name_rect = p.Surface((320, 50)).get_rect(topleft=((1000-menu_space)//2+menu_space-160, 360))
        surf = p.Surface((320, 50))
        surf.fill((255,255,255))
        screen.blit(surf, name_rect)
        if name_rect.collidepoint(mouse) and typingOn != name_typer:
            surf = p.Surface((320, 50))
            surf.fill((100,100,255))
            surf.set_alpha(50)
            screen.blit(surf, name_rect)
            if click[0]:
                typingOn = name_typer
                
        email_rect = p.Surface((320, 50)).get_rect(topleft=((1000-menu_space)//2+menu_space-160, 460))
        surf = p.Surface((320, 50))
        surf.fill((255,255,255))
        screen.blit(surf, email_rect)
        if email_rect.collidepoint(mouse) and typingOn != email_typer:
            
            surf = p.Surface((320, 50))
            surf.fill((100,100,255))
            surf.set_alpha(50)
            screen.blit(surf, email_rect)
            if click[0]:
                typingOn = email_typer

        # Write username input on the screen
        if typingOn == username_typer:
            username_surf = font40.render(username_typer.text(), True, (0,0,0))
            rect = username_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 70))
        elif typingOn != username_typer:
            if username_typer.text() in ('|','') and u_prob:
                username_surf = font40.render('Fill this out first.', True, (250,0,0))
            else:
                username_surf = font40.render(username_typer.result, True, (0,0,0))
            rect = username_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 70))
        if rect.right > (1000-menu_space)//2+menu_space+160:
            username_typer.typing = username_typer.typing[:-2] + username_typer.character
        try:
            screen.blit(username_surf, rect)
        except:
            pass

        # Write password input on the screen
        if typingOn == password_typer:
            if password_typer.text().endswith('|'):
                password_surf = font40.render(len(password_typer.result) * '*' + '|', True, (0,0,0))
            else:
                password_surf = font40.render(len(password_typer.result) * '*', True, (0,0,0))
            rect = password_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 170))
        elif typingOn != password_typer:
            if password_typer.text() in ('|','') and p_prob:
                password_surf = font40.render('Fill this out first.', True, (250,0,0))
            else:
                password_surf = font40.render(len(password_typer.result) * '*', True, (0,0,0))
        rect = password_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 170))
        if rect.right > (1000-menu_space)//2+menu_space+160:
            password_typer.typing = password_typer.typing[:-2] + password_typer.character
        try:
            screen.blit(password_surf, rect)
        except:
            pass

        # Write cpassword input on the screen
        if typingOn == cpassword_typer:
            if password_typer.text().endswith('|'):
                cpassword_surf = font40.render(len(cpassword_typer.result) * '*' + '|', True, (0,0,0))
            else:
                cpassword_surf = font40.render(len(cpassword_typer.result) * '*', True, (0,0,0))
            rect = cpassword_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 270))
        elif typingOn != cpassword_typer:
            if cpassword_typer.text() in ('|','') and c_prob:
                cpassword_surf = font40.render('Fill this out first.', True, (250,0,0))
            elif cpassword_typer.text() in ('|','') and match_prob:
                cpassword_surf = font30.render('Passwords must match.', True, (250,0,0))
            else:
                cpassword_surf = font40.render(len(cpassword_typer.result) * '*', True, (0,0,0))
        rect = cpassword_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 270))
        if rect.right > (1000-menu_space)//2+menu_space+160:
            cpassword_typer.typing = cpassword_typer.typing[:-2] + cpassword_typer.character
        try:
            screen.blit(cpassword_surf, rect)
        except:
            pass

        # Write name input on the screen
        if typingOn == name_typer:
            name_surf = font40.render(name_typer.text(), True, (0,0,0))
            rect = cpassword_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 370))
        elif typingOn != name_typer:
            if name_typer.text() in ('|','') and n_prob:
                name_surf = font40.render('Fill this out first.', True, (250,0,0))
            else:
                name_surf = font40.render(name_typer.result, True, (0,0,0))
        rect = name_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 370))
        if rect.right > (1000-menu_space)//2+menu_space+160:
            name_typer.typing = name_typer.typing[:-2] + name_typer.character
        try:
            screen.blit(name_surf, rect)
        except:
            pass

        # Write email input on the screen
        if typingOn == email_typer:
            email_surf = font40.render(email_typer.text(), True, (0,0,0))
            rect = email_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 470))
        elif typingOn != email_typer:
            if email_typer.result != '':
                email_surf = font40.render(email_typer.result, True, (0,0,0))
            else:
                email_surf = font40.render('Optional', True, (160,160,160))
        rect = email_surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 470))
        if rect.right > (1000-menu_space)//2+menu_space+160:
            email_typer.typing = email_typer.typing[:-2] + email_typer.character
        try:
            screen.blit(email_surf, rect)
        except:
            pass

        

        surf = font40.render('Create your username:', True, (255,255,255))
        rect = surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 25))
        screen.blit(surf, rect)
        surf = font40.render('Enter your password:', True, (255,255,255))
        rect = surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 125))
        screen.blit(surf, rect)
        surf = font40.render('Confirm your password:', True, (255,255,255))
        rect = surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 225))
        screen.blit(surf, rect)
        surf = font40.render('Enter your name:', True, (255,255,255))
        rect = surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 325))
        screen.blit(surf, rect)
        surf = font40.render('Enter your email address:', True, (255,255,255))
        rect = surf.get_rect(topleft=((1000-menu_space)//2+menu_space-160, 425))
        screen.blit(surf, rect)

        create_button.draw()
        #rect = p.Surface((242, 57)).get_rect(midtop=((1000-menu_space)//2+menu_space, 543))
        #if rect.collidepoint(mouse):
        #    surf = p.Surface((242, 57))
        #    surf.fill((50,50,255))
        #    screen.blit(surf, rect)
        #    text_surf = font40.render('Create Account!', True, (255,150,150))
        #    rect = text_surf.get_rect(center=rect.center)
        #    screen.blit(text_surf, rect)
        #    if click[0]:
        #        result = submitCreate(username_typer.result, password_typer.result, cpassword_typer.result, name_typer.result, email=email_typer.result)
        #        if type(result) == dict:
        #            return result
        #        elif 'username' in result:
        #            u_prob = True
        #        if 'password' in result:
        #            p_prob = True
        #        if 'cpassword' in result:
        #            c_prob = True
        #        if 'name' in result:
        #            n_prob = True
        #        match_prob = 'match' in result
        #        if match_prob:
        #            password_typer = Typing(block=[])
        #            cpassword_typer = Typing(block=[])
        #            p_prob = False
        #            c_prob = False
        #        p.mouse.set_pos(mouse[0], 540)
        #else:
        #    surf = p.Surface((242, 57))
        #    surf.fill((10,10,255))
        #    screen.blit(surf, rect)
        #    text_surf = font40.render('Create Account!', True, (0,0,0))
        #    rect = text_surf.get_rect(center=rect.center)
        #    screen.blit(text_surf, rect)


        p.display.update()
        if keys[K_F9]:
            p.display.set_caption('VillageWars ' + __main__.__version__ + ' - FPS: ' + str(round(clock.get_fps(), 1)))
        else:
            p.display.set_caption('VillageWars ' + __main__.__version__)
        clock.tick(60)

def submitCreate(username, password, cpassword, name, email=None):
    u = []
    if len(username) == 0:
        u.append('username')
    if len(password) == 0:
        u.append('password')
    if len(cpassword) == 0:
        u.append('cpassword')
    if password != cpassword:
        u.append('match')
    if len(name) == 0:
        u.append('name')
    if len(u) == 0:
        if not email:
            email = None
        return {'create':True, 'username':username, 'password':password, 'name':name, 'email':email}
    return u

net2web

The net2web module (developed by Aaron McCormick) contains code for making an easy websocket server-client connection handler for games.

__init__.py

from .server import Server, Channel
from .client import Client


from .toolbox import getmyip, Clock

client.py

import websockets
import asyncio
import json
import secrets
import os
import signal
import threading
import sys
import socket
import time
import logging as log

from .toolbox import Clock



class BaseClient:
    def __init__(self, host=None, port=None):
        if host:
            self.host = host
        else:
            self.host = 'water-warzone-0fc31e47a670.herokuapp.com',
        if port:
            self.port = port
        else:
            self.port = 5555
        self.websocket = None
        self.messages = []
        self.to_send = []
        self.clock = Clock(30)

        
    def start(self):
        asyncio.run(self.main())

    async def send_messages(self):
        while True:
            try:
                messages_to_send = self.to_send[:]
                if len(messages_to_send) == 0:
                    await asyncio.sleep(self.clock.get_tick())
                    continue
                self.to_send = []
                compilation = {'messages':[]}
                for message in messages_to_send:
                    compilation['messages'].append(message)
                self.to_send = []
                await self.send(compilation)
                
                if len(compilation['messages']) > 0:
                    log.debug('Sent %s messages' % len(compilation))
            except (websockets.exceptions.ConnectionClosedError, websockets.ConnectionClosedOK):
                    break
        

    async def send(self, data):
        """
        Send a message.

        """
        await self.websocket.send(json.dumps(data))


    async def recv(self):
        while True:
            try:
                
                message = await self.websocket.recv()
                messages = json.loads(message)
                for event in messages['messages']:
                    self.messages.append(event)
            except (websockets.exceptions.ConnectionClosedError, websockets.ConnectionClosedOK):
                    log.error('Connection Closed from server-side')
                    self.messages.append({'action':'disconnected'})
                    break

    async def main(self):
        try:
            ip = socket.gethostbyname(self.host)
            if ip.startswith('192.168.') or ip.startswith('10.') or ip.startswith('172.') or ip.startswith('127.0.0.1'):
                URI = 'ws://' + self.host + ':' + str(self.port) + '/'
            else:
                URI = 'wss://' + self.host + '/'
        except socket.error:
            URI = 'ws://' + self.host + '/'
        
        log.info('Connecting to ' + URI + '...')
        async with websockets.connect(URI) as websocket:
            self.websocket = websocket
            self.to_send.append({'action':'connection'})
            log.debug('Sending initial message')
            task1 = asyncio.create_task(self.recv())
            task2 = asyncio.create_task(self.send_messages())

            # Wait for tasks to complete
            await asyncio.gather(task1, task2)

        
    async def error(self, message):
        """
        Send an error message.

        """
        event = {
            "action": "error",
            "message": message,
        }
        self.to_send.append(json.dumps(event))
        
    async def response(self, message):
        """
        Send an error message.

        """
        event = {
            "action": "confirm",
            "message": message,
        }
        self.to_send.append(json.dumps(event))

class Client:
    def __init__(self, host=None, port=None):
        self.async_client = BaseClient(host=host, port=port)
        self.async_client.thread = threading.Thread(target=self.async_client.start)
        self.async_client.thread.setDaemon(True)
        self.async_client.thread.start()
        self._connected = False
        
    @property
    def connected(self):
        return self._connected
        
    def pump(self):
        num = 0  # There's a small chance we will receive a new message during the pump
        while len(self.async_client.messages):
            message = self.async_client.messages.pop(0)
            num += 1
            if message['action'] == 'connected':
                self._connected = True
            if message['action'] == 'disconnected':
                self.Network_disconnected(message)
                log.info('Disconnection.')
                sys.exit()
            if hasattr(self, 'Network_' + message['action']):
                getattr(self, 'Network_' + message['action'])(message)  # Calls the Network function to handle an action. `message` will hold the data.
                log.debug('Calling Network_' + message['action'])
            else:
                log.warning('No Network_' + message['action'] + ' found.')
        if num > 0:
            log.debug('Pumped %s messages successfully' % num)
            return
    Pump = pump  # Compatibility with PodSixNet
    
    def error(self, message):
        self.async_client.error(message)
        
    def send(self, data, force=False):
        if self.connected or force:
            self.async_client.to_send.append(data)
        else:
            log.debug('Not yet connected, failed to send action "' + data['action'] + '"')
            
    Send = send  # Compatible with PodSixNet
        

    def Network_error(self, data):
        log.error('Error:', data['message'])
        
    def Network_connected(self, data):
        log.info('Connected!')
    def Network_disconnected(self, data):
        pass

server.py

import websockets
import asyncio
import json
import secrets
import os
import signal
import threading
import secrets
import time

import logging as log

from .toolbox import Clock


class BaseChannel:
    def __init__(self, host=None, port=None):
        self.websocket = None
        self.messages = []
        self.to_send = []
        self.id = secrets.token_hex()
        self.clock = Clock(30)

    async def send(self, data):
        """
        Send a message.

        """
        await self.websocket.send(json.dumps(data))

    async def send_messages(self):
        while True:
            try:
                messages_to_send = self.to_send[:]
                if len(messages_to_send) == 0:
                    await asyncio.sleep(self.clock.get_tick())
                    continue
                self.to_send = []
                compilation = {'messages':[]}
                for message in messages_to_send:
                    compilation['messages'].append(message)
                await self.send(compilation)
                
                if len(compilation['messages']) > 0:
                    log.debug('Sent %s messages' % len(compilation))
            except (websockets.exceptions.ConnectionClosedError, websockets.ConnectionClosedOK):
                    break
        
    async def recv(self):
        while True:
            try:
                message = await self.websocket.recv()
            
                messages = json.loads(message)
                for event in messages['messages']:
                    self.messages.append(event)
            except (websockets.exceptions.ConnectionClosedError, websockets.ConnectionClosedOK):
                    log.info('Connection Closed from client-side')
                    self.messages.append({'action':'disconnected'})
                    break

    async def handler(self, websocket):
        """
        Handle a connection and dispatch it according to who is connecting.
        """
        self.websocket = websocket
        task1 = asyncio.create_task(self.recv())
        task2 = asyncio.create_task(self.send_messages())

        # Wait for tasks to complete
        await asyncio.gather(task1, task2)

    
        
    def error(self, message):
        """
        Send an error message.
        """
        event = {
            "action": "error",
            "message": message,
        }
        self.to_send.append(json.dumps(event))
        
    async def response(self, message):
        """
        Send an error message.

        """
        event = {
            "action": "confirm",
            "message": message,
        }
        await self.websocket.send(json.dumps(event))

class Channel:
    def __init__(self, server):
        self.server = server
        self.async_server = BaseChannel()
        self._connected = False
        self._warned = []
        
    @property
    def connected(self):
        return self._connected
        
    def pump(self):
        num = 0  # There's a small chance we will receive a new message during the pump
        while len(self.async_server.messages):
            message = self.async_server.messages.pop(0)
            num += 1
            if message['action'] == 'connection':
                self._connected = True
                self.send({'action':'connected'})
                self.server.connection(self)
            if message['action'] == 'disconnected':
                self._connected = False
                self.server.players.remove(self)
                self.server.disconnection(self)
            if hasattr(self, 'Network_' + message['action']):
                getattr(self, 'Network_' + message['action'])(message)  # Calls the `Network_<action>` function to handle an action. `message` will hold the data.
                log.debug('Calling Network_' + message['action'])
            else:
                log.warning('No Network_' + message['action'] + ' found.')
        if num > 0:
            log.debug('Pumped %s messages successfully' % num)
            return
    def error(self, message):
        self.async_server.error(message)

    def send(self, data, force=False):
        if self.connected or force:
            self.async_server.to_send.append(data)
        else:
            log.debug('Not yet connected, failed to send action "' + data['action'] + '"')

    Send = send  # Compatibility with PodSixNet

    def Network_error(self, data):
        log.error('Error:', data['message'])

    def Network_connection(self, data):
        pass
        
    def Network_disconnected(self, data):
        pass

    def __hash__(self):
        return hash(self.async_client.id)

class BaseServer:
    def __init__(self, server, host=None, port=None):
        self.server = server
        if host:
            self.host = host
        else:
            self.host = 'water-warzone-0fc31e47a670.herokuapp.com',
        if port:
            self.port = port
        else:
            self.port = os.environ.get("PORT", "5555")
        self.channels = []

    def start(self):
        asyncio.run(self.main())

    async def main(self):

        # Set the stop condition when receiving SIGTERM.
        loop = asyncio.get_running_loop()
        stop = loop.create_future()
        try:
            loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)
        except:
            log.warning('No SIGTERM')

        async with websockets.serve(self.handler, "", self.port):
            await stop
            
    async def handler(self, websocket):
        """
        Handle a connection and dispatch it according to who is connecting.
        """
        try:
            new_channel = self.server.ChannelClass(self.server)
            self.channels.append(new_channel)
            await new_channel.async_server.handler(websocket)
        except websockets.exceptions.InvalidHandshake as e:
            error_message = "Failed to open a WebSocket connection: invalid Connection header: close."
            # Customize the error message or perform any other desired actions
            # ...
            # Send the error message back to the client if needed
            await websocket.send(error_message)


    

class Server():
    def __init__(self, host=None, port=None):
        self.async_server = BaseServer(self, host=host, port=port)
        self.async_server.thread = threading.Thread(target=self.async_server.start)
        self.async_server.thread.setDaemon(True)
        self.async_server.thread.start()
        if not hasattr(self, 'ChannelClass'):
            self.ChannelClass = Channel
        
    @property
    def players(self):
        return self.async_server.channels

    def pump(self):
        
        for player in self.players:
            player.pump()
            
    Pump = pump  # Compatibility with PodSixNet

    def connection(self, channel):
        log.info('Channel connected!')

    def disconnection(self, channel):
        log.info('Channel disconnected')

toolbox.py

import time
import socket
import requests


class Clock:
    def __init__(self, fps=30):
        self.frame_delay = 1 / fps
        self.start_time = time.time()
        self.fps = fps
        
    def __call__(self, fps=None):
        if fps and fps != self.fps:
            self.frame_delay = 1 / fps
            self.fps = fps
        time.sleep(self.get_tick())  # Delay for the remaining time

    def get_tick(self):
        end_time = time.time()
        frame_time = end_time - self.start_time
        self.start_time = end_time
        if frame_time < self.frame_delay:
            return self.frame_delay - frame_time
        return 0
        
        
def getmyip(local=True):
    """
    Finds the IP address of the local computer
    """
    if local:
        return socket.gethostbyname(socket.gethostname())

    else:
        url = 'https://api.ipify.org?format=json'
        res = requests.get(url)
        res.raise_for_status()
        data = res.json()
        return data['ip']

NPC.py

import pygame
import random as r
import toolbox as t
from balloon import Arrow
from animations import *

class Farmer(pygame.sprite.Sprite):
    def __init__(self, building):
        try:
            pygame.sprite.Sprite.__init__(self, self.gp)
        except:
            pass
        self.building = building
        self.x, self.y = building.rect.x, building.rect.y
        self.setout()
        self.speed = 2
        self.obs = []
        self.food = 0

        self.rect = None


    @property
    def innerrect(self):
        return self.rect

    def setout(self):
        self.status = 'Going to Farms'
        sm_dist = 8000
        x = y = 0
        for farm in self.building.server.resources:
            if farm.__class__.__name__ == 'Farm':
                dx, dy = r.randint(farm.x + 90, farm.x + 310), farm.y + 50
                dist = t.getDist(self.x, self.y, dx, dy)
                if dist < sm_dist:
                    sm_dist = dist
                    x, y = dx, dy
                    self.farm = farm
        self.dest_x = x
        self.dest_y = y
        self.angle = t.getAngle(self.x, self.y, x, y)

    def start_gather(self):
        self.status = 'Gathering Food'
        sm_dist = 1000
        for food in self.farm.foods:
            if food.image == 'good':
                dist = t.getDist(self.x, self.y, food.x, food.y)
                if dist < sm_dist:
                    sm_dist = dist
                    self.the_food = food
                    self.dest_x = food.x
                    self.dest_y = food.y

    def mine(self):
        try:
            self.the_food.mine(self)
            if self.the_food.image == 'used':
                self.start_gather()
        except:
            self.start_gather()

    def at_dest(self):
        return t.getDist(self.x, self.y, self.dest_x, self.dest_y) < 10

    def go_back(self):
        self.status = 'Returning to Guild'
        self.dest_x, self.dest_y = self.building.rect.center

    def update(self):

        
        self.obs = self.building.server.obs
        
        self.angle = t.getAngle(self.x, self.y, self.dest_x, self.dest_y)
        x, y = t.getDir(self.angle, self.speed)
        
        lx, ly = self.x, self.y
        if self.status != 'Gathering Food':
            self.x += x
            self.rect = pygame.Rect(self.x, self.y, 50, 50)
            for ob in self.obs:
                if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                    self.x -= x
            
            self.y += y
            self.rect = pygame.Rect(self.x, self.y, 50, 50)
            for ob in self.obs:
                if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                    self.y -= y
            if round(lx) == round(self.x) and round(ly) == round(self.y) and self.status != 'Gathering Food':
                blocked = True
                while blocked:
                    blocked = False
                    
                    self.x += x
                    self.rect = pygame.Rect(self.x, self.y, 50, 50)
                    for ob in self.obs:
                        if self.rect.colliderect(ob.innerrect):
                            blocked = True
            
                    self.y += y
                    self.rect = pygame.Rect(self.x, self.y, 50, 50)
                    for ob in self.obs:
                        if self.rect.colliderect(ob.innerrect):
                            blocked = True
                

        if self.status == 'Gathering Food':
            if self.food < 20:
                self.mine()
            else:
                self.go_back()
            

        if self.at_dest():
            if self.status == 'Going to Farms':
                self.start_gather()
            elif self.status == 'Returning to Guild':
                self.food = 0
                self.building.owner.character.food += 20
                self.setout()
        
        for p in self.building.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.rect.size
                rect.topleft = (p.character.get_x(self.rect), p.character.get_y(self.rect))
                if screen.colliderect(rect):
                    if self.status == 'Gathering Food':
                        p.to_send.append({'action':'draw_NPC',
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'image':'farmer',
                            'status':'Gathering Food : ' + str(self.food) + '/20',
                            'color':self.building.owner.color,
                            'angle':self.angle})
                    else:
                        p.to_send.append({'action':'draw_NPC',
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'image':'farmer',
                            'status':self.status,
                            'color':self.building.owner.color,
                            'angle':self.angle})
    def explode(self, angle, knockback):
        x, y = t.getDir(angle, knockback)
        self.x += x
        self.rect = pygame.Rect(self.x, self.y, 50, 50)
        for ob in self.obs:
            if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                self.x -= x
            
        self.y += y
        self.rect = pygame.Rect(self.x, self.y, 50, 50)
        for ob in self.obs:
            if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                self.y -= y


class Miner(pygame.sprite.Sprite):
    def __init__(self, building):
        try:
            pygame.sprite.Sprite.__init__(self, self.gp)
        except:
            pass
        self.building = building
        self.x, self.y = building.rect.x, building.rect.y
        self.setout()
        self.speed = 2
        self.gold = 0
        self.obs = []

    @property
    def innerrect(self):
        return self.rect        

    def setout(self):
        self.status = 'Heading to Mines'
        sm_dist = 8000
        x = y = 0
        for farm in self.building.server.resources:
            if farm.__class__.__name__ == 'Mine':
                dx, dy = farm.x+100, r.randint(farm.y + 50, farm.y + 400)
                dist = t.getDist(self.x, self.y, dx, dy)
                if dist < sm_dist:
                    sm_dist = dist
                    x, y = dx, dy
                    self.farm = farm
        self.dest_x = x
        self.dest_y = y
        self.angle = t.getAngle(self.x, self.y, x, y)

    def start_gather(self):
        self.status = 'Mining'
        sm_dist = 1000
        for food in self.farm.golds:
            if food.image == 'good':
                dist = t.getDist(self.x, self.y, food.x, food.y)
                if dist < sm_dist:
                    sm_dist = dist
                    self.the_food = food
                    self.dest_x = food.x
                    self.dest_y = food.y

    def mine(self):
        try:
            self.the_food.collect(self)
            if self.the_food.image == 'used':
                self.start_gather()
        except:
            self.start_gather()

    def at_dest(self):
        return t.getDist(self.x, self.y, self.dest_x, self.dest_y) < 10

    def go_back(self):
        self.status = 'Returning to Guild'
        self.dest_x, self.dest_y = self.building.rect.center

    def update(self):
        self.obs = self.building.server.obs
        self.angle = t.getAngle(self.x, self.y, self.dest_x, self.dest_y)
        x, y = t.getDir(self.angle, self.speed)
        
        lx, ly = self.x, self.y
        if self.status != 'Mining':
            self.x += x
            self.rect = pygame.Rect(self.x, self.y, 50, 50)
            for ob in self.obs:
                if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                    self.x -= x
            
            self.y += y
            self.rect = pygame.Rect(self.x, self.y, 50, 50)
            for ob in self.obs:
                if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                    self.y -= y
            if round(lx) == round(self.x) and round(ly) == round(self.y) and self.status != 'Mining':
                blocked = True
                while blocked:
                    blocked = False
                    
                    self.x += x
                    self.rect = pygame.Rect(self.x, self.y, 50, 50)
                    for ob in self.obs:
                        if self.rect.colliderect(ob.innerrect):
                            blocked = True
            
                    self.y += y
                    self.rect = pygame.Rect(self.x, self.y, 50, 50)
                    for ob in self.obs:
                        if self.rect.colliderect(ob.innerrect):
                            blocked = True
                

        if self.status == 'Mining':
            if self.gold < 20:
                self.mine()
            else:
                self.go_back()
            

        if self.at_dest():
            if self.status == 'Heading to Mines':
                self.start_gather()
            elif self.status == 'Returning to Guild':
                self.gold = 0
                self.building.owner.character.gold += 20
                self.setout()
        
        for p in self.building.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.rect.size
                rect.topleft = (p.character.get_x(self.rect), p.character.get_y(self.rect))
                if screen.colliderect(rect):
                    if self.status == 'Mining':
                        p.to_send.append({'action':'draw_NPC',
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'image':'miner',
                            'status':'Mining : ' + str(self.gold) + '/20',
                            'color':self.building.owner.color,
                            'angle':self.angle})
                    else:
                        p.to_send.append({'action':'draw_NPC',
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'image':'miner',
                            'status':self.status,
                            'color':self.building.owner.color,
                            'angle':self.angle})
                        
    def explode(self, angle, knockback):
        x, y = t.getDir(angle, knockback)
        self.x += x
        self.rect = pygame.Rect(self.x, self.y, 50, 50)
        for ob in self.obs:
            if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                self.x -= x
            
        self.y += y
        self.rect = pygame.Rect(self.x, self.y, 50, 50)
        for ob in self.obs:
            if self.rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.building.owner):
                self.y -= y


class ArcheryTower(pygame.sprite.Sprite):
    dimensions = (360, 360)
    def __init__(self, server, x, y, channel, rect):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.building = self
        
        self.server = server
        self.x, self.y = x, y - 100
        self.owner = channel.character
        self.health = 300
        self.dimensions = (360, 360)
        
        self.innerrect = rect
        self.server.building_blocks.append(self.innerrect)
        self.server.obs.append(self)

        self.balloon_speed = 45
        self.attack = 24
        self.knockback = 32

        self.shoot_cooldown = 60
        self.state = 'alive'

        self.attacking = False

    def update(self):
        self.lookx = self.owner.x
        self.looky = self.owner.y
        self.attacking = False

        for npc in self.server.event.NPCs:
            if t.getDist(self.x, self.y, npc.x, npc.y) < 800:
                    self.lookx = npc.x
                    self.looky = npc.y
                    self.attacking = True

                    
        for player in self.server.players:
            if not player.pending:
                if t.getDist(self.x, self.y, player.character.x, player.character.y) < 800 and player != self.owner.channel and player.character.dead == False:
                    self.lookx = player.character.x
                    self.looky = player.character.y
                    self.attacking = True

        
                
        if self.attacking == False:
            close_dist = 100000
            
            for robot in self.server.NPCs:
                if robot.__class__.__name__ == 'Robot' and robot.factory.owner != self.owner.channel and t.getDist(self.x, self.y, robot.x, robot.y) < close_dist:
                    self.lookx = robot.x
                    self.looky = robot.y
                    close_dist = t.getDist(self.x, self.y, robot.x, robot.y)
                    self.attacking = True

        self.angle = t.getAngle(self.x, self.y, self.lookx, self.looky)
        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1

        if self.shoot_cooldown < 1 and self.attacking and self.state == 'alive':
            Arrow(tower=self)
            self.shoot_cooldown = 60


        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'archery_tower',
                    'coords':(p.character.get_x(self), p.character.get_y(self)),
                    'angle':self.angle,
                    'color':self.owner.channel.color,
                    'health':self.health,
                    'state':self.state})


    def isBuilding(self):
        return False
    
    def getHurt(self, damage, attacker):
        if attacker != self.owner:
            self.health -= damage
            if self.health < 1:
                self.state = 'broken'
                if attacker.__class__.__name__ == 'Character':
                    attacker.destroyed += 1
                    self.owner.channel.message = attacker.channel.username + ' has broken one of your Archery Towers.'
                else:
                    self.owner.channel.message = attacker + ' has broken one of your Archery Towers.'
                    
                self.owner.channel.message_count = 150
                self.owner.channel.message_color = (255,0,0)
                self.server.obs.remove(self)

    def explode(self, player):
        if self.state == 'broken':
            self.server.building_blocks.remove(self.innerrect)
            self.kill()
        else:
            self.getHurt(80, player)
            if self.state == 'broken':
                self.server.building_blocks.remove(self.innerrect)
                self.kill()





class Robot(pygame.sprite.Sprite):
    def __init__(self, factory):
        try:
            pygame.sprite.Sprite.__init__(self, self.gp)
        except:
            pass

        self.server = factory.server
        self.factory = factory
        self.player = factory.owner.character
        self.angle = r.randint(0, 359)

        self.x = self.factory.x
        self.y = self.factory.y + 140
        
        self.fav_dir = r.choice((5, -5))
        self.angry_test = 20
        self.regular_test = 2
        self.angry = False

        self.speed = 4
        self.health = 100
        self.hurt = 0

        self.dead = 0

        self.obs = []

        self.state = 'Peaceful'
        self.surf = pygame.Surface((50, 50))
        self.rect = self.surf.get_rect(center=(self.x, self.y))

        self.safety_zone = pygame.Surface((3400, 2190)).get_rect(center=(self.factory.x, self.factory.y))

        self.range = pygame.Surface((3000, 2000)).get_rect(center=(self.factory.x, self.factory.y))

    @property
    def innerrect(self):
        return self.rect

    @property
    def building(self):
        return self.factory


        

    def update(self):
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.obs:
            if rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.factory.owner):
                self.getHurt(100, 0, 0)

        
        self.obs = self.server.obs
        for robot in self.server.NPCs:
            if robot.__class__.__name__ == 'Robot' and robot.factory.owner != self.factory.owner:
                self.obs.append(robot)

        if self.hurt:
            self.hurt -= 1

        if self.dead:
            self.dead -= 1

        if not self.dead:
            self.AI()

            for p in self.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    rect = pygame.Rect(0, 0, 1, 1)
                    rect.size = self.rect.size
                    rect.topleft = (p.character.get_x(self.rect), p.character.get_y(self.rect))
                    if screen.colliderect(rect):
                        p.to_send.append({'action':'draw_robot',
                                'coords':(p.character.get_x(self), p.character.get_y(self)),
                                'name':'Robot',
                                'health':self.health,
                                'image':('hurt' if self.hurt else 'regular'),
                                'color':self.factory.owner.color,
                                'angle':self.angle})


            for p in self.server.players:
                if not p.pending and self.rect.colliderect(p.character.rect) and p != self.factory.owner and p.character.dead == False:
                    self.getHurt(0, self.angle + 180, 50)
                    p.character.getHurt(10, self.factory.owner.username + "'s Robots", self.angle, 2)


    def AI(self):

        if not self.rect.colliderect(self.safety_zone):
            self.state = 'Returning'
            self.angle = t.getAngle(self.x, self.y, self.factory.x, self.factory.y + 140)
        
        if self.state == 'Returning':
            if round(self.x) == round(self.factory.x) and round(self.y) == round(self.factory.y):
                self.state = 'Peaceful'

        if self.state == 'Attacking':
            speed = self.speed
        else:
            speed = 2


        origx = self.x

        
        x, y = t.getDir(self.angle, total=speed)

        self.x += x
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.obs:
            if rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.factory.owner):
                self.x -= x

        origy = self.y
            
        self.y += y
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.obs:
            if rect.colliderect(ob.innerrect) and (ob.__class__.__name__ != 'Gate' or ob.owner.channel != self.factory.owner):
                self.y -= y

        for item in self.server.bushes:
            if self.rect.colliderect(item.innerrect):
                if item.__class__.__name__ == 'SpikyBush':
                    self.getHurt(1, self.angle + 180, 1.5)
                else:
                    self.getHurt(0, self.angle + 180, 1.5)
            
        self.rect = self.surf.get_rect(center=(self.x, self.y))

        suspects = []
        for p in self.server.players:
            if not p.pending:
                if p.character.rect.colliderect(self.range) and not p.character.dead and not p == self.factory.owner:
                    suspects.append(p.character)
        if len(suspects) > 1:
            closest = suspects[0]
            for suspect in suspects[1:]:
                if t.getDist(self.x, self.y, suspect.x, suspect.y) < t.getDist(self.x, self.y, closest.x, closest.y):
                    closest = suspect
                    suspect = closest
        elif len(suspects) == 1:
            suspect = suspects[0]

        else:
            suspect = None

        if suspect != None:
            self.state = 'Attacking'
            self.attacking = suspect
            self.angle = t.getAngle(self.x, self.y, self.attacking.x, self.attacking.y) + r.randint(-2,2)


    def init(self):

        self.x = self.factory.x
        self.y = self.factory.y + 140

        self.state = 'Peaceful'
        
        self.health = 100

    def getHurt(self, damage, angle, knockback):

        self.health -= damage
        if self.health <= 0:
            Explosion(self)
            self.dead = 200
            for p in self.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.to_send.append({'action':'sound', 'sound':'die'})

            self.init()

        if damage != 0:
            self.hurt = 8
        
        x, y = t.getDir(angle, knockback)

        self.x += x
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.obs:
            if rect.colliderect(ob.innerrect):
                self.x -= x


        
        self.y += y
        rect = self.surf.get_rect(center=(self.x, self.y))
        for ob in self.obs:
            if rect.colliderect(ob.innerrect):
                self.y -= y

    def explode(self, angle, knockback):
        self.getHurt(80, angle, knockback)

obstacle.py

import pygame
import toolbox as t
from animations import *
import random as r

class Obstacle(pygame.sprite.Sprite):
    def __init__(self, server, x, y):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.server = server
        self.image = None
        self.dimentions = (50, 50)
        self.x = x
        self.y = y
        self.max_health = self.health = 300
        self.owner = None

    def update(self):
        

        for p in self.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.innerrect.size
                rect.topleft = (p.character.get_x(self.innerrect), p.character.get_y(self.innerrect))
                if screen.colliderect(rect):
                    p.to_send.append({'action':'draw_obstacle',
                            'image':self.image,
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'health':self.health,
                            'max_health':self.max_health})

    def getHurt(self, damage, attacker):
        if self.health > 0:
            self.health -= damage
            if self.health < 1:
                self.server.building_blocks.remove(self.innerrect)
                self.server.obs.remove(self)
                self.kill()

    def isBuilding(self):
        return False


    def explode(self):
        self.getHurt(80, None)


class Tree(pygame.sprite.Sprite):
    def __init__(self, server, x, y):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.server = server
        self.image = None
        self.x = x
        self.y = y
        self.max_health = self.health = 300
        self.owner = None
        self.surf = pygame.Surface((200, 280))
        self.rect = self.surf.get_rect(center=(x,y))
        self.innerrect = pygame.Surface((50, 120)).get_rect(midtop=self.rect.center)
        
        server.building_blocks.append(self.innerrect)
        server.obs.append(self)

    def update(self):
        

        for p in self.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.rect.size
                rect.center = (p.character.get_x(self), p.character.get_y(self))
                if screen.colliderect(rect):
                    p.to_send.append({'action':'draw_obstacle',
                            'image':'tree',
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'health':self.health,
                            'max_health':self.max_health})

    def getHurt(self, damage, attacker):
        if self.health > 0:
            self.health -= damage
            if self.health < 1:
                self.server.building_blocks.remove(self.innerrect)
                self.server.obs.remove(self)
                self.kill()

    def isBuilding(self):
        return False


    def explode(self):
        self.getHurt(160, None)


class Sappling(pygame.sprite.Sprite):
    def __init__(self, server, x, y):
        pygame.sprite.Sprite.__init__(self, Tree.gp)
        self.server = server
        self.x = x
        self.count = 100 #30*60*1
        self.y = y
        self.max_health = self.health = 120
        self.owner = None
        self.surf = pygame.Surface((60, 90))
        self.rect = self.surf.get_rect(center=(x,y))
        
        server.building_blocks.append(self.rect)

    def update(self):
        self.count -= 1
        if not self.count:
            self.server.building_blocks.remove(self.rect)
            Tree(self.server, self.x, self.y)
            self.kill()
            return

        for p in self.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.rect.size
                rect.center = (p.character.get_x(self), p.character.get_y(self))
                if screen.colliderect(rect):
                    p.to_send.append({'action':'draw_obstacle',
                            'image':'sappling',
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'health':self.health,
                            'max_health':self.max_health})

    def getHurt(self, damage, attacker):
        if self.health > 0:
            self.health -= damage
            if self.health < 1:
                self.server.building_blocks.remove(self.innerrect)
                self.server.obs.remove(self)
                self.kill()

    def isBuilding(self):
        return False


    def explode(self):
        self.getHurt(120, None)


        

class Boulder(Obstacle):
    def __init__(self, server, x, y):
        Obstacle.__init__(self, server, x, y)
        self.image = 'boulder'
        self.max_health = self.health = r.randint(18, 32) * 10
        self.p = pygame.image.load('../assets/boulder.png')
        self.innerrect = self.p.get_rect(center=(x,y))
        server.building_blocks.append(self.innerrect)
        server.obs.append(self)


class Crate(Obstacle):
    def __init__(self, player, x, y):
        Obstacle.__init__(self, player.channel.server, x, y)
        self.image = 'crate'
        self.max_health = self.health = 800
        self.p = pygame.Surface((50, 50))
        self.innerrect = self.p.get_rect(center=(x,y))
        self.server.building_blocks.append(self.innerrect)
        self.server.obs.append(self)
        self.owner = player

    def explode(self):
        self.server.building_blocks.remove(self.innerrect)
        self.server.obs.remove(self)
        self.kill()

    def update(self):
        
        

        for p in self.server.players:
            
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.innerrect.size
                rect.topleft = (p.character.get_x(self.innerrect), p.character.get_y(self.innerrect))
                if screen.colliderect(rect):

                    p.to_send.append({'action':'draw_obstacle',
                                'image':self.image,
                                'coords':(p.character.get_x(self), p.character.get_y(self)),
                                'health':self.health,
                                'max_health':self.max_health})

class Barrel(Obstacle):
    def __init__(self, player, x, y, angle, health=15, max_health=15, explosive=False):
        Obstacle.__init__(self, player.channel.server, x, y)
        self.image = 'barrel'
        self.explosive = explosive
        self.max_health = max_health
        self.health = health
        self.p = pygame.Surface((50, 50))
        self.innerrect = self.p.get_rect(center=(x,y))
        
        self.player = player
        self.obs = self.player.channel.server.obs
        self.angle = angle
        self.speed = 16
        self.sedentary = False
        self.roll_angle = angle 
        self.damage = self.health
        self.knockback = 80

        if -90 <= angle <= 90:
            self.roll_direction = -10
        else:
            self.roll_direction = 10

        

    def explode(self):
        if self.innerrect in self.server.building_blocks:
            self.server.building_blocks.remove(self.innerrect)
            self.server.obs.remove(self)
        self.kill()

    def update(self):
        
        self.damage = self.health
        if not self.sedentary:
            self.roll_angle += self.roll_direction
            self.move(self.angle)
        

        for p in self.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.innerrect.size
                rect.topleft = (p.character.get_x(self), p.character.get_y(self))
                if screen.colliderect(rect):

                    p.to_send.append({'action':'draw_obstacle',
                            'image':self.image,
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'health':self.health,
                            'max_health':self.max_health,
                            'angle':self.roll_angle,
                            'explosive':self.explosive})
    def sedent(self):
        if self.explosive:
            self.this_explode()
            self.kill()
            return
        self.sedentary = True
        self.server.building_blocks.append(self.innerrect)
        self.server.obs.append(self)
        self.getHurt(10, 'Santa Clause') # Placeholder
        for p in self.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                rect = pygame.Rect(0, 0, 1, 1)
                rect.size = self.innerrect.size
                rect.topleft = (p.character.get_x(self), p.character.get_y(self))
                if screen.colliderect(rect):
                    p.to_send.append({'action':'sound','sound':'barrel'})

    def this_explode(self): # warning: self may be player object
        BAM(self)
        try:
            self.server
        except:
            self.server = self.channel.server
            self.innerrect = self.rect
            self.player = self
        for p in self.server.players:
            screen = pygame.Rect(0, 0, 1000, 650)
            screen.center = p.character.rect.center
            if screen.colliderect(self.innerrect):
                p.Send({'action':'sound', 'sound':'TNT'})

        if True:  # Too lazy to unindent
            ray = pygame.Rect(0, 0, 350, 350)
            ray.center = self.x, self.y
            for item in self.server.obstacles:
                if item != self and item.innerrect.colliderect(ray):
                    item.explode()
            for item in self.server.trees:
                if item != self and item.innerrect.colliderect(ray):
                    item.explode()
            for item in self.server.buildings:
                if item.innerrect.colliderect(ray):
                    item.getHurt(120, self.player)
                elif item.rect.colliderect(ray):
                    item.getHurt(120, self.player)
            print('Explosion')
            for item in self.server.players:
                print(item.username)
                if item.character.rect.colliderect(ray):
                    print(item.username.upper())
                    item.character.getHurt(80, self.player, t.getAngle(self.x, self.y, item.character.x, item.character.y), 120, '<Victim> was blown up by <Attacker>')
                    
            for item in self.server.NPCs:
                if item.innerrect.colliderect(ray):
                    if item.__class__.__name__ == 'ArcheryTower':
                        item.explode(self.player)
                    else:
                        item.explode(t.getAngle(self.x, self.y, item.x, item.y), 80)
            for item in self.server.event.NPCs:
                if item.rect.colliderect(ray):
                    item.explode(self.player, self)
            for item in self.server.bushes:
                if item.rect.colliderect(ray):
                    item.explode()
        if self.player == self:
            del self.server
            del self.innerrect
            del self.player
        
                    
    def move(self, angle):
        obs = self.obs[:]

        x, y = t.getDir(angle, self.speed)
        self.move_x(round(x), obs)
        self.move_y(round(y), obs)
        for item in obs:
            rect = item.innerrect if hasattr(item, 'innerrect') else item.rect
            if self.innerrect.colliderect(rect):
                try:
                    item.getHurt(self.damage, self.player)
                except:
                    item.getHurt(self.damage, self.player, self.angle, self.knockback)
                self.sedent()
                return
        for p in self.server.players:
            if self.innerrect.colliderect(p.character.rect) and p.character != self.player:
                self.sedent()
                p.character.getHurt(self.damage, self.player, self.angle, self.knockback, msg='<Victim> got rolled over.')
                return
        for b in self.server.bushes:
            if self.innerrect.colliderect(b.rect):
                self.sedent()
                b.getHurt(self.damage, self.player)
                return
        for npc in self.server.NPCs:
            if npc.__class__.__name__ == 'Robot' and self.innerrect.colliderect(npc.rect):
                self.sedent()
                npc.getHurt(self.damage, self.angle, self.knockback)
                return
            if npc.__class__.__name__ == 'ArcheryTower' and self.innerrect.colliderect(npc.innerrect):
                self.sedent()
                npc.getHurt(self.damage, self.angle, self.knockback)
                return
        for npc in self.server.event.NPCs:
            if self.innerrect.colliderect(npc.rect):
                self.sedent()
                result = npc.getHurt(self.player, self.damage, self.angle, self.knockback)
        
        

    def move_x(self, a, obs):
        self.innerrect.x += a
        self.x, self.y = self.innerrect.center
        
                
    def move_y(self, a, obs):
        self.innerrect.y += a
        self.x, self.y = self.innerrect.center
    


class Vine(Obstacle):
    def __init__(self, player, x, y, direction=None, speed=r.randint(25, 110)):
        Obstacle.__init__(self, player.channel.server, x, y)
        self.image = 'vine'
        self.max_health = self.health = speed * 2
        self.p = pygame.Surface((50, 50))
        self.innerrect = self.p.get_rect(center=(x,y))
        self.server.building_blocks.append(self.innerrect)
        self.server.obs.append(self)
        self.owner = player
        self.speed = speed
        self.count = self.speed

        
        dirs = [(0, 50), (50, 0), (-50, 0), (0, -50)]
        if direction:
            dirs.remove(direction)
        self.dir = r.choice(dirs)
        for v in self.server.obstacles:
            if v.__class__ == Vine and self.innerrect.colliderect(v.innerrect) and v != self:
                v.count = 1
                v.health = v.max_health
                v.dir = self.dir
                v.owner = self.owner
                self.explode()

    def explode(self):
        if self.innerrect in self.server.building_blocks:
            self.server.building_blocks.remove(self.innerrect)
        if self in self.server.obs:
            self.server.obs.remove(self)
        self.kill()

    def update(self):
        if self.count:
            self.count -= 1
            if not self.count:
                Vine(self.owner, self.x + self.dir[0], self.y + self.dir[1], direction=self.dir, speed=self.speed)
        

        for b in self.server.buildings:
            if not b.owner == self.owner.channel and self.innerrect.colliderect(b.innerrect):
                b.getHurt(0.02, self.owner.channel.username + '\'s invasive vine')
        
        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_obstacle',
                            'image':self.image,
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'health':self.health,
                            'max_health':self.max_health})


class SpikyBush(pygame.sprite.Sprite):
    def __init__(self, player, x, y):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.server = player.channel.server
        self.x = x
        self.y = y
        self.dimensions = (50, 50)
        self.max_health = self.health = 260
        self.p = pygame.Surface(self.dimensions)
        self.rect = self.p.get_rect(center=(x,y))
        self.server.building_blocks.append(self.innerrect)
        self.owner = player

    @property
    def innerrect(self):
        return self.rect

    def explode(self):
        self.server.building_blocks.remove(self.innerrect)
        self.kill()

    def update(self):
        
        

        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_obstacle',
                            'image':'spiky bush',
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'health':self.health,
                            'max_health':self.max_health})

    def getHurt(self, damage, attacker):
        if self.health > 0:
            self.health -= damage
            if self.health < 1:
                self.explode()

    def isBuilding(self):
        return False

class Gate(Obstacle):
    def __init__(self, player, x, y, rot):
        Obstacle.__init__(self, player.channel.server, x, y)
        self.image = 'gate'
        self.max_health = self.health = 1000
        if rot == False:
            self.p = pygame.Surface((100, 200))
        else:
            self.p = pygame.Surface((200, 100))
        self.innerrect = self.p.get_rect(center=(x,y))
        self.server.building_blocks.append(self.innerrect)
        self.server.obs.append(self)
        self.owner = player
        self.rotated = rot

    def explode(self):
        self.getHurt(900, None)

    def update(self):

        

        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_obstacle',
                            'image':self.image,
                            'coords':(p.character.get_x(self), p.character.get_y(self)),
                            'health':self.health,
                            'max_health':self.max_health,
                            'rotated?':self.rotated})

class TNT(pygame.sprite.Sprite):
    def __init__(self, player, x, y):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.health = 130
        self.x = x
        self.y = y
        self.p = pygame.Surface((50, 50))
        self.innerrect = self.p.get_rect(center=(x,y))
        self.server = player.channel.server
        self.server.building_blocks.append(self.innerrect)
        self.server.obs.append(self)
        self.owner = player
        #player.channel.achievement('You used TNT! (Watch out)')

    def update(self):
        self.health -= 1
        if self.health == 0:
            BAM(self)
            for p in self.server.players:
                screen = pygame.Rect(0, 0, 1000, 650)
                screen.center = p.character.rect.center
                if screen.colliderect(self.innerrect):
                    p.Send({'action':'sound', 'sound':'TNT'})
            self.server.building_blocks.remove(self.innerrect)
            self.server.obs.remove(self)
            
            ray = pygame.Rect(0, 0, 400, 400)
            ray.center = self.x, self.y
            for item in self.server.obstacles:
                if item != self and item.innerrect.colliderect(ray):
                    item.explode()
            for item in self.server.trees:
                if item != self and item.innerrect.colliderect(ray):
                    item.explode()
            for item in self.server.buildings:
                if item.innerrect.colliderect(ray):
                    item.getHurt(120, self.owner)
                elif item.rect.colliderect(ray):
                    item.getHurt(120, self.owner)
            for item in self.server.players:
                if item.character.rect.colliderect(ray):
                    item.character.getHurt(80, self.owner, t.getAngle(self.x, self.y, item.character.x, item.character.y), 120, '<Victim> was blown up by <Attacker>')
            for item in self.server.NPCs:
                if item.innerrect.colliderect(ray):
                    if item.__class__.__name__ == 'ArcheryTower':
                        item.explode(self.owner)
                    else:
                        item.explode(t.getAngle(self.x, self.y, item.x, item.y), 80)
            for item in self.server.event.NPCs:
                if item.rect.colliderect(ray):
                    item.explode(self.owner, self)

            for item in self.server.bushes:
                if item.rect.colliderect(ray):
                    item.explode()
                
            self.kill()

        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_obstacle',
                            'image':'TNT',
                            'coords':(p.character.get_x(self), p.character.get_y(self))})
    def isBuilding(self):
        return False
    def getHurt(self, damage, attacker):
        pass
    def explode(self):
        ''' Ha! '''
        pass

class Block():
    def __init__(self, topleft, size):
        self.innerrect = pygame.Rect(topleft[0], topleft[1], size[0], size[1])
        self.owner = None
    def isBuilding(self):
        return False
    def getHurt(self, damage, attacker):
        pass

player.py

import pygame
import time
import toolbox as t
from balloon import Balloon, Bolt
from obstacle import *
from building import *
from animations import *
from NPC import ArcheryTower
import __main__

class Character():



    def __init__(self, channel, x, y):

        self.channel = channel
        self.image = pygame.Surface((50, 50))
        self.x = x
        self.y = y
        self.x_flip = 500-x
        self.y_flip = 325-y
        self.angle = 0
        self.speed = self.moving = 8
        
        self.health = self.max_health = 100
        self.hurt_count = 1
        self.dead = False
        self.explosive = True

        if self.channel.username == 'ModestNoob' or self.channel.username == 'f':
            self.gold = 100000
            self.food = 100000
        elif __main__.GAMEMODE == 'OP':
            self.gold = 1600
            self.food = 1600
        elif __main__.GAMEMODE == 'Immediate':
            self.gold = 100
            self.food = 100
        elif __main__.GAMEMODE == 'Mutated':
            self.gold = 45
            self.food = 25
        elif __main__.GAMEMODE == 'Express':
            self.gold = 100
            self.food = 100
        else:
            self.gold = 0
            self.food = 0
        
        self.strength = 0
        self.obs = []
        self.rect = self.image.get_rect(center=(self.x, self.y))

        self.shoot_cooldown = 0
        self.barbshoot_cooldown = -1
        self.shot_speed = 15
        self.crate_cooldown = 0
        self.crate_hold = False
        self.spiky_bush_hold = False
        self.invincible = False

        self.respawn_count = 0
        self.meal = False
        self.meal_type = 0

        self.crates = 0
        self.spiky_bushes = 0
        self.spence = 'gold'

        self.garden_xp = 0

        self.shield_count = 0
        self.shield_angle = 0
        self.has_shield = False
        self.barrel_health = 0
        self.in_barrel = False
        self.barrel_max_health = 0


        ### Stat Side ###

        
        self.attack = 10
        self.damage_cost = 30
        
        self.balloon_speed = 16
        self.speed_cost = 10
        
        self.knockback = 10
        self.knockback_cost = 10

        self._kills = 0
        self._destroyed = 0
        self._deaths = 0
        self._eliminations = 0



    ### Info Side - Statistics ( Soon to be updated ) ###

    @property
    def kills(self):
        return self._kills
    @kills.setter
    def kills(self, value):
        self._kills = value
    @property
    def destroyed(self):
        return self._destroyed
    @destroyed.setter
    def destroyed(self, value):
        self._destroyed = value
    @property
    def deaths(self):
        return self._deaths
    @deaths.setter
    def deaths(self, value):
        self._deaths = value
    @property
    def eliminations(self):
        return self._eliminations
    @eliminations.setter
    def eliminations(self, value):
        self._eliminations = value

    @property
    def statistics(self):
        return (self.channel.username, 'kills', self.kills)
        
    @property
    def have_builder(self):
        true = False
        for inn in self.channel.get_buildings():
            if inn.__class__.__name__ == 'Inn':
                if inn.NPC.__class__.__name__ == 'Builder':
                    true = True
                    break
            if inn.type == 'Builder\'s':
                true = True
                break
        return true
        

    def get_x(self, item):

        try:
            return item.x + self.x_flip
        except:
            return item + self.x_flip
        

    def get_y(self, item):

        try:
            return item.y + self.y_flip
        except:
            return item + self.y_flip

    def move_x(self, a, type_=None):
        
        self.x += a
        self.x_flip -= a

        self.rect = self.image.get_rect(center=(self.x, self.y))


        if not self.dead:
            for item in self.obs:
                thr = False
                if item.__class__ == Gate and item.owner == self:
                    thr = True
                if self.rect.colliderect(item.innerrect) and not thr:
                    self.x -= a
                    self.x_flip += a

                
                    

    def move_y(self, a, type_=None):
        self.y += a
        self.y_flip -= a

        self.rect = self.image.get_rect(center=(self.x, self.y))

        if not self.dead:
            for item in self.obs:
                thr = False
                if item.__class__ == Gate and item.owner == self:
                    thr = True
                if self.rect.colliderect(item.innerrect) and not thr:
                    self.y -= a
                    self.y_flip += a
            for item in self.channel.server.bushes:
                if self.rect.colliderect(item.innerrect) and type_ != 'hurt':
                    if item.__class__.__name__ == 'SpikyBush':
                        self.getHurt(1, 'a spiky bush', self.angle + 180, 4, msg='<Victim> fell in <Attacker>.')
                    else:
                        self.getHurt(0, '', self.angle + 180, 4)
             
                    
        
    def move(self, angle):
        x, y = t.getDir(angle, self.get_speed())
        self.move_x(round(x))
        self.move_y(round(y))

    def update(self):
        
        if self.channel.build_to_place:

            self.channel.to_send.append({'action':'preview', 'dimensions':((self.channel.build_to_place.dimensions[0]*0.8, self.channel.build_to_place.dimensions[1]*0.8) if self.have_builder else self.channel.build_to_place.dimensions), 'ArcheryTower?':self.channel.build_to_place==ArcheryTower})
        
        self.rect = self.image.get_rect(center=(self.x, self.y))
        if not self.dead:
            for item in self.obs:
                if item.__class__ == Crate and self.rect.colliderect(item.innerrect):
                    x, y = t.getDir(self.angle + 180, 40)
                    self.x += x
                    self.x_flip -= x
                    self.y += y
                    self.y_flip -= y
                else:
                    thr = False
                    if item.__class__ == Gate and item.owner == self:
                        thr = True
                    if self.rect.colliderect(item.innerrect) and not thr:
                        self.suffocate()


        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1
        if self.crate_cooldown > 0:
            self.crate_cooldown -= 1
        if self.hurt_count > 1:
            self.hurt_count -= 1
        if self.shield_count:
            self.shield_count -= 1
        if self.barbshoot_cooldown:
            self.barbshoot_cooldown -= 1

        

        if self.respawn_count > 0:
            self.respawn_count -= 1
            if self.respawn_count == 0:
                self.respawn()
        
        self.obs = self.channel.server.obs
        
        
        if not self.dead:

            if not self.barbshoot_cooldown:
                self.barbshoot()
            
            for p in self.channel.server.players:
                if not p.pending:
                        
                    barrel_image = self.barrel_max_health / (self.barrel_health+1)>2
                    if self.explosive:
                        barrel_image = 'TNT'
                    p.to_send.append({'action':'draw_player',
                                'hurt':int(bool(self.hurt_count - 1)),
                                'coords':(p.character.get_x(self), p.character.get_y(self)),
                                'angle':self.angle,
                                'color':self.channel.color,
                                'username':self.channel.username,
                                'health':self.health,
                                'max_health':self.max_health,
                                'skin':self.channel.skin,
                                'shield':(self.shield_angle if self.shield_count else None),
                                'in_barrel':(barrel_image, self.in_barrel)})
                    

    def hud(self):
        return {            'action':'hud',
                            
                            'x':self.x,
                            'y':self.y,
                            'angle':self.angle,
                            'color':self.channel.color,
                            'username':self.channel.username,
                            'health':self.health,
                            'gold':self.gold,
                            'food':self.food,
                            'max_health':self.max_health,
                            'food?':self.meal,
                            'type':self.meal_type,
                            'crates':self.crates,
                            'spiky_bushes':self.spiky_bushes,
                            'gametime':time.time() - self.channel.server.starttime,
                            }

    def barbshoot(self):
        
        dire = 650
        player = None
        for p in self.channel.server.players:
            if p.pending:
                if t.getDist(self.x, self.y, p.character.x, p.character.y) < dire and p != self.channel:
                    player = p.character
                    dire = t.getDist(self.x, self.y, p.character.x, p.character.y)
        if player:
            self.angle = t.getAngle(self.x, self.y, player.x, player.y)
            Bolt(archer=self)
            self.barbshoot_cooldown = 50
        

    def HandleInput(self, keys, mouse):
        if self.barrel_health and not self.dead and not self.channel.in_window and not self.channel.in_innpc_window:
            change = False
            if self.in_barrel:
                if not keys[pygame.K_z]:
                    change = True
            else:
                if keys[pygame.K_z]:
                    change = True
            if change:
                self.in_barrel = not self.in_barrel
                for p in self.channel.server.players:
                    if not p.pending:
                        screen = pygame.Rect(0, 0, 1000, 650)
                        rect = pygame.Rect(0, 0, 1, 1)
                        rect.size = self.rect.size
                        rect.topleft = (p.character.get_x(self), p.character.get_y(self))
                        if screen.colliderect(rect):
                            p.to_send.append({'action':'sound','sound':'barrel'})
        else:
            self.in_barrel = False

        
        beg_building = self.channel.build_to_place
        beg_gold = self.gold
        beg_food = self.food
        if not self.channel.in_window:
            if not keys[pygame.K_x] and self.crate_hold:
                self.crate_hold = False
            if not keys[pygame.K_c] and self.spiky_bush_hold:
                self.spiky_bush_hold = False

            self.angle = t.getAngle(500, 325, mouse[0], mouse[1])

            #if keys[pygame.K_a]:
            #    self.channel.achievement('You pressed the A button!')
            #if keys[pygame.K_8]:
            #    self.channel.achievement('You pressed the 8 button!')
                
            if keys[pygame.K_SPACE] and not self.in_barrel:
                self.move(self.angle)

            

            if keys[pygame.K_c] and not self.dead:
                if self.channel.text == 'Press c to place sappling':
                    Sappling(self.channel.server, self.x, self.y)
                    self.channel.text = ''
                    self.spiky_bush_hold = True
                elif self.spiky_bush_hold == False and self.spiky_bushes > 0:
                    x, y = self.x, self.y
                    move = t.getDir(self.angle, 80)
                    x += move[0]
                    y += move[1]
                        
                    SpikyBush(self, x, y)
                    self.spiky_bush_hold = True
                    self.spiky_bushes -= 1

            if keys[pygame.K_x]:
                if self.channel.text == 'Press x to place gate':
                    self.channel.text = ''
                    x, y = self.x, self.y
                    move = t.getDir(self.angle, 100)
                    x += move[0]
                    y += move[1]
                    rotated = False
                    if 45 < self.angle < 135 or -45 > self.angle > -135:
                        rotated = True
                    
                    Gate(self, x, y, rotated)
                    self.crate_hold = True

                elif self.channel.text == 'Press x to place TNT':
                    self.channel.text = ''
                    x, y = self.x, self.y
                    move = t.getDir(self.angle, 80)
                    x += move[0]
                    y += move[1]
                    
                    TNT(self, x, y)
                    self.crate_hold = True
                    
                elif not self.dead and self.crate_hold == False and self.crates > 0:
                    x, y = self.x, self.y
                    move = t.getDir(self.angle, 80)
                    x += move[0]
                    y += move[1]
                    
                    Crate(self, x, y)
                    self.crate_hold = True
                    self.crates -= 1

            if keys[pygame.K_DELETE] and self.channel.build_to_place != None:
                self.channel.build_to_place = None
                self.channel.text = ''
                if self.spence == 'gold':
                    self.channel.message = 'You deleted the building, you gain 10 gold.'
                    self.gold += 10
                else:
                    self.channel.message = 'You deleted the building, you gain 10 food.'
                    self.food += 10
                self.channel.message_count = 150
                self.channel.message_color = (255,100,0)

            
            if mouse[2] and not self.dead:

                not_shoot = False
                if self.dead == False:
                    for farm in self.channel.server.resources:
                        if farm.HandleInput(mouse, self):
                            not_shoot = True
                if self.in_barrel:
                    Barrel(self, self.x, self.y, self.angle, self.barrel_health, self.barrel_max_health, self.explosive)
                    self.in_barrel = False
                    self.barrel_health = 0
                    self.shoot_cooldown = self.shot_speed * 2
                    for p in self.channel.server.players:
                        if not p.pending:
                            screen = pygame.Rect(0, 0, 1000, 650)
                            rect = pygame.Rect(0, 0, 1, 1)
                            rect.size = self.rect.size
                            rect.topleft = (p.character.get_x(self), p.character.get_y(self))
                            if screen.colliderect(rect):
                                p.to_send.append({'action':'sound','sound':'shot'})
                    return

                if not not_shoot:
                    self.shoot()

        if self.channel.in_window and mouse[2] and not self.dead and not self.channel.in_innpc_window:
            not_farm = True
            not_shoot = True
            for option in self.channel.window['options']:
                
                x = 200
                y = 200 + option[0] * 40
                rect = pygame.Rect(x, y, 600, 50)

                if rect.collidepoint((mouse[0], mouse[1])):
                    option[1](self)
                    
                    break
                
        if self.channel.in_window and mouse[2] and not self.dead and self.channel.in_innpc_window:
            for i, option in enumerate(self.channel.window['options']):
                
                x = 200
                y = 200 + i * 40
                rect = pygame.Rect(x, y, 600, 50)

                if rect.collidepoint((mouse[0], mouse[1])):
                    option[1](self.channel)
                    break


        if mouse[4] and not self.dead:
            
            if self.channel.build_to_place != None:
                result = builder(self.channel.build_to_place, self, self.have_builder)
                to_build = result[0]
                if to_build:
                    b = self.channel.build_to_place(self.channel.server, self.x, self.y - 140, self.channel, result[1])

                    self.channel.text = ''
                    self.channel.build_to_place = None
                    try:
                        self.channel.message = 'You have placed a ' + b.type + '.'
                    except:
                        self.channel.message = 'You have placed an Archery Tower.'
                    self.channel.message_count = 150
                    self.channel.message_color = (255,205,0)
                else:
                    
                    self.channel.message = 'You cannot place this building here.'
                    self.channel.message_count = 100
                    self.channel.message_color = (255,0,0)
            elif not self.in_barrel and not self.channel.in_window and not self.channel.in_innpc_window:
                for b in self.channel.server.buildings:
                    rect = b.p.get_rect(center=(self.get_x(b), self.get_y(b)))
                    if rect.collidepoint((mouse[0], mouse[1])):
                        b.open_window(self.channel)
                        self.channel.to_send.append({'action':'sound', 'sound':'ow'})

                    else:
                        rect = b.rect.copy()
                        rect.x, rect.y = self.get_x(rect), self.get_y(rect)
                        if rect.collidepoint((mouse[0], mouse[1])):
                            b.open_window(self.channel)
                            self.channel.to_send.append({'action':'sound', 'sound':'ow'})

                    if b.__class__ == Inn and b.NPC != None and b.owner == self.channel:
                        rect = pygame.Rect(0, 0, 0, 0)
                        rect.topleft = self.get_x(b.NPC.rect), self.get_y(b.NPC.rect)
                        rect.size = b.NPC.rect.size
                        if rect.collidepoint((mouse[0], mouse[1])):
                            self.channel.in_window = True
                            self.channel.in_innpc_window = True
                            self.channel.window = b.NPC.window
                            self.channel.to_send.append({'action':'sound', 'sound':'ow'})
        if self.channel.message == 'The Gold Discovery Rate is at its maximum!' and not [b for b in self.channel.get_buildings() if b.type == 'Miner\'s Guild']:
            self.channel.message = 'Get a Miner\'s Guild first.'
        if self.channel.message == 'The Food Production Rate is at its maximum!' and not [b for b in self.channel.get_buildings() if b.type == 'Farmer\'s Guild']:
            self.channel.message = 'Get a Farmer\'s Guild first.'

        if __main__.GAMEMODE == 'Mutated' and self.channel.build_to_place != beg_building and self.channel.build_to_place is not None:
            self.channel.message = 'No buildings in Mutated Gamemode. Returning %s gold, %s food' % (beg_gold - self.gold, beg_food - self.food)
            self.channel.message_color = (255,128,0)
            self.channel.build_to_place = beg_building
            self.gold = beg_gold
            self.food = beg_food
            self.channel.text = ''



    def eat(self):
        if not self.dead:
            self.meal = False
            self.health = self.max_health
            self.channel.message = 'You ate your meal, your health bar is now full again.'
            self.channel.message_color = (255,205,0)
    def dine_in(self):
        self.health = self.max_health
            
        
    def getHurt(self, damage, attacker, angle, knockback, msg = '<Attacker> splashed <Victim> too hard.'):
        if self.in_barrel:
            if self.explosive:
                self.in_barrel = False
                self.barrel_health = 0
                #self.explosive = False
                Barrel.this_explode(self)
                return 'BAM'
            else:
                self.barrel_health -= damage
                if self.barrel_health < 0 or msg == '<Victim> was blown up by <Attacker>':
                    self.barrel_health = 0
                    self.in_barrel = False
                    for p in self.channel.server.players:
                        if not p.pending:
                            screen = pygame.Rect(0, 0, 1000, 650)
                            rect = pygame.Rect(0, 0, 1, 1)
                            rect.size = self.rect.size
                            rect.topleft = (p.character.get_x(self), p.character.get_y(self))
                            if screen.colliderect(rect):
                                p.to_send.append({'action':'sound','sound':'barrel'})
            return

                
        if (randint(0, 2) == 0 and self.has_shield) or self.invincible:
            self.shield_angle = angle + 180
            self.shield_count = 20
            for p in self.channel.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    rect = pygame.Rect(0, 0, 1, 1)
                    rect.size = self.rect.size
                    rect.topleft = (p.character.get_x(self), p.character.get_y(self))
                    if screen.colliderect(rect):
                        p.to_send.append({'action':'sound','sound':'bump'})
            if msg == '<Victim> got rolled over.':
                x, y = t.getDir(angle, knockback)
                self.move_x(x)
                self.move_y(y)
            return 'repelled'
        final_damage = (damage - self.strength)
        if final_damage < 0:
            final_damage = 0
        self.health -= final_damage
        x, y = t.getDir(angle, knockback)
        self.move_x(x, 'hurt')
        self.move_y(y, 'hurt')
        self.hurt_count = 8
        if not self.health > 0:
            Explosion(self)
            for p in self.channel.server.players:
                screen = pygame.Rect(0, 0, 1000, 650)
                screen.center = p.character.rect.center
                if screen.colliderect(self.rect):
                    p.to_send.append({'action':'sound', 'sound':'die'})
            self.channel.text = ''
            self.channel.build_to_place = None
            self.dead = True
            
            self.deaths += 1
            self.barrel_health = 0
            self.crates = 0
            self.spiky_bushes = 0
            self.meal = False
            self.moving = 16
            if not isinstance(attacker, str):
                attacker.kills += 1
                username = attacker.channel.username
            else:
                username = attacker

            msg = msg.replace('<Attacker>', username).replace('<Victim>', self.channel.username)
            for channel in self.channel.server.players:
                channel.message = msg
                channel.message_count = 150
                channel.message_color = (255,205,0)
                if channel == self.channel:
                    channel.message_color = (255,0,0)
            if len(self.channel.get_buildings()) != 0:
                self.respawn_count = 200
            else:
                if not isinstance(attacker, str):
                    attacker.eliminations += 1
                guys = []
                for channel in self.channel.server.players:
                    channel.message = username + ' has eliminated ' + self.channel.username + ' from the game.'
                    channel.message_count = 150
                    channel.message_color = (255,205,0)
                    if channel == self.channel:
                        channel.message = username + ' has eliminated you from the game!'
                        channel.message_color = (255,0,0)
                    if channel.character.dead == False:
                        guys.append(channel)
                    elif channel.character.respawn_count > 0:
                        guys.append(channel)
                if len(guys) == 1:
                    self.channel.server.terminate(guys[0])

        self.channel.in_window = False
        self.channel.window = None

    def suffocate(self):
        Explosion(self)
        for p in self.channel.server.players:
            screen = pygame.Rect(0, 0, 1000, 650)
            screen.center = p.character.rect.center
            if screen.colliderect(self.rect):
                p.to_send.append({'action':'sound', 'sound':'die'})
        self.channel.text = ''
        self.channel.build_to_place = None
        self.dead = True
            
        self.deaths += 1
        self.crates = 0
        self.meal = False
        self.moving = 16

        for channel in self.channel.server.players:
            channel.message = self.channel.username + ' suffocated.'
            channel.message_count = 150
            channel.message_color = (255,205,0)
            if channel == self.channel:
                channel.message_color = (255,0,0)

        if len(self.channel.get_buildings()) != 0:
            self.respawn_count = 200
        else:
            guys = []
            for channel in self.channel.server.players:
                channel.message = self.channel.username + ' has been eliminated from the game by suffocation.'
                channel.message_count = 150
                channel.message_color = (255,205,0)
                if channel == self.channel:
                    channel.message = 'You suffocated and were eliminated from the game!'
                    channel.message_color = (255,0,0)
                if channel.character.dead == False:
                    guys.append(channel)
                elif channel.character.respawn_count > 0:
                    guys.append(channel)
            if len(guys) == 1:
                self.channel.server.terminate(guys[0])



    def shoot(self):
        if self.shoot_cooldown == 0:
            Balloon(self.channel.server, self)
            self.shoot_cooldown = self.shot_speed
            for p in self.channel.server.players:
                if not p.pending:
                    screen = pygame.Rect(0, 0, 1000, 650)
                    screen.center = p.character.rect.center
                    if screen.colliderect(self.rect):
                        p.to_send.append({'action':'sound', 'sound':'shot'})


    def respawn(self):
        self.dead = False
        self.x, self.y = self.channel.server.ST_COORDS[self.channel.loc_number]
        self.x_flip = 500-self.x
        self.y_flip = 325-self.y
        self.moving = self.speed
        self.channel.message = 'You respawned. You respawn as long as you have buildings alive.'
        self.channel.message_count = 150
        self.channel.message_color = (128,255,5)
        self.health = self.max_health


    def get_speed(self):
        speed = self.moving
        speed /= self.channel.fps
        speed *= 30

        return speed

resources.py

import pygame
import pygame as p
import random as r

class Farm(pygame.sprite.Sprite):
    def __init__(self, server, coords):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.x = coords[0]
        self.y = coords[1]
        self.server = server
        self.foods = pygame.sprite.Group()
        server.building_blocks.append(pygame.Rect(self.x, self.y, 400, 150))
        self.production = 1000
        

        coords = []
        while len(list(self.foods)) < 32:
            food = Wheat(self, self.foods)
            if (food.x, food.y) not in coords:
                coords.append((food.x, food.y))
            else:
                food.kill()
        

        del coords
    

    def update(self): 
        self.foods.update()

    def HandleInput(self, mouse, player):
        for food in self.foods:
            food.drawrect.x, food.drawrect.y = player.get_x(food), player.get_y(food)
            if food.drawrect.collidepoint(mouse[:2]) and bool(mouse[2]) and (food.image == 'good' or food.image == 'mine'):
                if not player.dead:
                    food.mine(player)
                    return True
                    
        return False


class Wheat(pygame.sprite.Sprite):
    def __init__(self, farm, gp):
        pygame.sprite.Sprite.__init__(self, gp)
        self.farm = farm
        self.x = r.randint(farm.x, farm.x + 375)
        self.y = r.randint(farm.y, farm.y + 40)
        self.image = 'good'
        self.count = 0
        self.mining = 15
        self.drawrect = p.Rect(0,0,45,45)

    def pick(self, player):
        self.image = 'used'
        self.count = self.farm.production
        player.food += r.randint(1,4)


    def mine(self, player):
        if self.image == 'good' or self.image == 'mine':
            self.image = 'mine'
            self.mining -= 1
            if self.mining == 0:
                self.pick(player)


    def update(self):
        

            
        if self.count > 0:
            self.count -= 1
            if self.count == 0:
                self.image = 'good'
                self.mining = 15
        
        for p in self.farm.server.players:
            if not p.pending:
                self.drawrect.x, self.drawrect.y = p.character.get_x(self), p.character.get_y(self)
                screen = pygame.Rect(0, 0, 1000, 650)
                if screen.colliderect(self.drawrect):
                    p.to_send.append({'action':'draw_farm',
                        'coords':(p.character.get_x(self), p.character.get_y(self)),
                        'state':self.image})



class Mine(pygame.sprite.Sprite):
    def __init__(self, server, coords):
        pygame.sprite.Sprite.__init__(self, self.gp)
        self.x = coords[0]
        self.y = coords[1]
        self.server = server
        self.golds = pygame.sprite.Group()
        server.building_blocks.append(pygame.Rect(self.x, self.y, 200, 600))
        self.production = 1000

        self.right = self.x > 3000

        wall = MineWalls(coords, (200, 18))
        server.obs.append(wall)
        wall = MineWalls((self.x, self.y + 570), (200, 30))
        server.obs.append(wall)

        if not self.right:
            wall = MineWalls(coords, (30, 600))
            server.obs.append(wall)
        else:
            wall = MineWalls((self.x + 170, self.y), (30, 600))
            server.obs.append(wall)

        coords = []
        while len(list(self.golds)) < 32:
            gold = Gold(self, self.golds)
            if (gold.x, gold.y) not in coords:
                coords.append((gold.x, gold.y))
            else:
                gold.kill()

        del coords
        


    def update(self):
        for p in self.server.players:
            if not p.pending:
                p.to_send.append({'action':'draw_mine',
                    'coords':(p.character.get_x(self), p.character.get_y(self)),
                    'right':self.right})
            
        self.golds.update()

    def HandleInput(self, mouse, player):
        for gold in self.golds:
            gold.drawrect.x, gold.drawrect.y = player.get_x(gold), player.get_y(gold)
            if gold.drawrect.collidepoint(mouse[:2]) and bool(mouse[2]) and (gold.image == 'good' or gold.image == 'mine'):
                if not player.dead:
                    gold.collect(player)
                    return True

                    
        return False
    

class Gold(pygame.sprite.Sprite):
    def __init__(self, mine, gp):
        pygame.sprite.Sprite.__init__(self, gp)
        self.mine = mine
        self.x = r.randint(mine.x + 30, mine.x + 140)
        self.y = r.randint(mine.y + 15, mine.y + 540)
        self.image = 'good'
        self.count = 0
        self.mining = 30
        self.drawrect = p.Rect(0,0,45,30)


    def pick(self, player):
        self.image = 'used'
        self.count = self.mine.production
        player.gold += r.randint(1,3)


    def collect(self, player):
        if self.image == 'good' or self.image == 'mine':
            self.image = 'mine'
            self.mining -= 1
            if self.mining == 0:
                self.pick(player)


    def update(self):
        

            
        if self.count > 0:
            self.count -= 1
            if self.count == 0:
                self.image = 'good'
                self.mining = 30
        
        for p in self.mine.server.players:
            if not p.pending:
                screen = pygame.Rect(0, 0, 1000, 650)
                self.drawrect.x, self.drawrect.y = p.character.get_x(self), p.character.get_y(self)
                if screen.colliderect(self.drawrect):
                    p.to_send.append({'action':'draw_gold',
                        'coords':(p.character.get_x(self), p.character.get_y(self)),
                        'state':self.image})




class MineWalls():
    def __init__(self, topleft, size):
        self.innerrect = pygame.Rect(topleft[0], topleft[1], size[0], size[1])
        self.owner = None
    def isBuilding(self):
        return False
    def getHurt(self, damage, attacker):
        pass

setup.py

import os
import sys
import json
try:
    import text_decoration
except ImportError:  # Doesn't matter
    class text_decoration:
        def rgb(*args): return args[0]
        def holiday(): return ''

with open('../preferences.json', 'r') as fo:
    preferences = json.loads(fo.read())
    py = preferences.get('py', 'python')
    newpath = preferences.get('path', [])
    sys.path.extend(newpath)
    
version = f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}'
print('Running python version', version)

print()
print()

print('Welcome to VillageWars!')

print(text_decoration.holiday())
print()

print('I hope you enjoy playing it!')

print()

print()
print('Would you like to set preferences? (y/n)')
answer = input('> ').strip()[0].lower()
print()
assert answer in ('y', 'n')
if answer == 'y':
    
    print('What would you like your map\'s theme to be? (please enter one of "' + text_decoration.rgb('grass', (0,200,0)) + '" (default), "' + text_decoration.rgb('pink', (255,200,200)) + '", or "' + text_decoration.rgb('snow', (255,255,255)) + '")')
    map = input('> ').lower()
    while map not in ['grass', 'pink', 'snow']:
        print('That is invalid. Please enter one of "' + text_decoration.rgb('grass', (0,200,0)) + '" (default), "' + text_decoration.rgb('pink', (255,200,200)) + '", or "' + text_decoration.rgb('snow', (255,255,255)) + '".')
        map = input('> ').lower()
    print()
    print('Map set to "' + map + '"')
    print()
    print('Do you have a prefered python interpreter? (default is "python", but may vary)')
    interp = input('> ').lower()
    if interp not in ['python', 'py', 'python3', '', 'python.exe', 'py.exe']:
        print('That is an unusual interpreter. Please type it again to confirm.')
        interp = input('> ').lower()
    if interp == '':
        interp = 'python'
    py = interp
    print()
    print('Interpreter set to "' + interp + '"')
    print()
    print('Would you like to specify a missing system path? (type y for yes, n for no, and s to see what is already on the system path)')
    answer = 's'
    while answer == 's':
        answer = input('> ')[0].lower()
        print()
        if answer == 'n': print('Ok, on to the next step.\n')
        elif answer == 's':
            print('This is your system path so far:')
            print(sys.path)
            print()
        elif answer == 'y':
            print(r'Please enter a directory: (ex: C:\Users\myusername\Appdata\Python3\site-packages)')
            path = input('> ')
            newpath.append(path)
            print()
            print('Thank you! Successfully added that to the system path.')
        else:
            print('Please enter one of "y", "n", or "s".')
    preferences = {'map':map, 'py':interp, 'path':newpath}
    with open('../preferences.json', 'w') as fo:
        fo.write(json.dumps(preferences))
else:
    print('Ok, see https://villagewars.fandom.com/wiki/Setting_Preferences to learn how to set them later.')

version_info = False
if len(sys.argv) > 1:
    missing_modules = sys.argv[1].split('*')
    if 'version_info.json' in missing_modules:
        missing_modules.remove('version_info.json')
        version_info = True
else:
    missing_modules = []


print('You are missing a few elements necessary to start VillageWars.')
print('Would you like these elements to be installed? (y/n)')
answer = input('> ').strip()[0].lower()
if answer == 'n':
    print('Ok, well, see you later then!')
    print()
    input('Press enter to exit')
    sys.exit()
    
assert answer == 'y'
print()
print('You are missing the following third-party modules from VillageWars:')
print()
for module in missing_modules:
    print(module)
print()
for module in missing_modules:
    input('Press enter to install %s' % module)
    exit_code = os.system('{} -m pip install {}'.format(py, module))
    if exit_code == 1:  # maybe `py` doesn't work.
        exit_code = os.system('{} -m pip install {}'.format(py, module))
    if exit_code == 1:
        print('Hmmm. Is seems like there was an error. Check your internet connection, then try again. If the problem persists, call Aaron.')
        print()
        input('Press enter to exit.')
        sys.exit()
        
    print()

from progress_bar import InitBar as Bar
import send2trash
import requests

    

print()
input('Press enter to continue')    
    
print()
if version_info:
    print('You\'re missing the version information documents that let you update VillageWars. Hold on while those documents are fetched.')
    print()
    input('Press enter to continue')
    print()
    print('Collecting version_info.json')
    print()
    bar = Bar('Downloading:')
    bar(0)
    res = requests.get('http://villagewars.pythonanywhere.com/download_version_info', stream=True)
    res.raise_for_status()
    bar(10)
    try:
        download_size = int(res.headers['Content-length'])
    except KeyError:
        download_size = 2048
    downloaded_bytes = 0
    os.makedirs('../../version screenshots', exist_ok=True)
    fo = open('../../version screenshots/version_info.json', 'wb')
    for chunk in res.iter_content(chunk_size=32):
        if chunk: # filter out keep-alive new chunks
            len_chunk = len(chunk)
            fo.write(chunk)
            downloaded_bytes += len_chunk
            bar(10 + downloaded_bytes/download_size*90)
    bar(100)
    del bar
    print('Successfully downloaded version information')
            
    fo.close()

print()
print('Successfully installed all necessary elements')
print()
#print('By the way, we are looking for French translators to help me translate the game and the wiki into French. To help me with the wiki, head to villagewars.fandom.com to familiarize yourself with the English wiki and prepare yourself for translating. Don\'t worry, I\'ll help too.')
print('Would you like your files to be refreshed? (Replace out-of-date files) (y/n)')
answer = input('> ').strip()[0].lower()
print()
assert answer in ('y', 'n')
if answer == 'n':
    print('Ok, you\'re ready to play VillageWars!')
if answer == 'y':
    files = [os.path.abspath('../run/screenshots/' + file) for file in os.listdir('../run/screenshots')]
    files.extend([os.path.abspath('../run/compressed/' + file) for file in os.listdir('../run/compressed')])
    files.extend([os.path.abspath('../run/downloads/' + file) for file in os.listdir('../run/downloads')])
    if len(files):
        print('You have %s files to unlink' % len(files))
    
        print()
        os.chdir('../')
        for file in files:
            print(os.path.relpath(file).replace('\\', '/'))
        os.chdir('src')
        print()
        input('Press enter to continue')
        print()
        #print('Unlinking %s files:' % len(files), end=' ')
        bar = Bar('Unlinking %s files' % len(files))
        bar(0)
        for i, file in enumerate(files):
            send2trash.send2trash(file)
            bar(((i+1)/len(files)*100))
        del bar
        print()
        print('Sucessfully refreshed your files')
    else:
        print('Nevermind, you don\'t have any files to refresh.')
        print()
        print('You\'re ready to play VillageWars!')

print()
input('Press enter to continue to VillageWars')
os.chdir('../')
os.system(py + ' -m main')

toolbox.py

import pygame
from pygame.locals import *
import math
import socket, subprocess

def black_and_white(surface, change=0):
    width = surface.get_width()
    height = surface.get_height()
    surf = surface.__copy__()
    for x in range(width):
        for y in range(height):
            color = list(surface.get_at((x, y)))
            alpha = color.pop()
            color = [max(0, min(255, (sum(color)/3)+change)) for i in range(3)]
            color.append(alpha)
            #if alpha > 200:
            surf.set_at((x, y), tuple(color))
    return surf
    
    width = surface.get_width()
    height = surface.get_height()
    surf = pygame.Surface((width, height))
    surf.fill((100,100,100))
    for x in range(width):
        for y in range(height):
            color = list(surface.get_at((x, y)))
            alpha = color.pop()
            color = [sum(color)/3 for i in range(3)]
            color.append(alpha)
            #if alpha > 200:
            surf.set_at((x, y), tuple(color))
    
    return surf

class Button:
    def __init__(self, screen, image='', image_hover='', image_down='', image_off='', off=False, assets='../assets/', accepts=None, **kwargs): # give pygame.Rect kwargs
        if accepts is None:
            self.accepts = [1] # Left Click only
        else:
            self.accepts = accepts
        assert type(assets) == str
        self.screen = screen
        try:
            self.image = pygame.image.load(assets + image)
        except FileNotFoundError:
            self.image = pygame.Surface((0,0))
        try:
            self.image_hov = pygame.image.load(assets + image_hover)
        except FileNotFoundError:
            pass
        try:
            self.image_down = pygame.image.load(assets + image_down)
        except FileNotFoundError:
            pass
        try:
            self.image_no = pygame.image.load(assets + image_off)
        except FileNotFoundError:
            pass
        self.on = not off
        try:
            self.rect = self.image.get_rect(**kwargs)
        except Exception as exc:
            self.rect = self.image_off.get_rect(**kwargs)

        self.state = ('idle' if self.on else 'off')

    @property
    def x(self):
        return self.rect.x
    @x.setter
    def x(self, value):
        self.rect.x = value
    @property
    def y(self):
        return self.rect.y
    @y.setter
    def y(self, value):
        self.rect.y = value

    @classmethod
    def from_surf(cls, screen, image=None, image_hover=None, image_down=None, image_off=None, off=False, **kwargs):
        button = Button(screen, assets='filenotfounderror.png', off=off)
        if image:
            button.image = image
        if image_hover:
            button.image_hov = image_hover
        if image_down:
            button.image_down = image_down
        if image_off:
            button.image_no = image_off
        button.rect = button.image.get_rect(**kwargs)
        return button

    def handle_event(self, event):
        in_coords = self.rect.collidepoint(pygame.mouse.get_pos()[:2])
        
        if event.type not in (MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION):
            return
        
        if self.state == 'idle':
            if event.type == pygame.MOUSEBUTTONDOWN and in_coords and event.button in self.accepts:
                self.state = 'armed'
        elif self.state == 'armed':
            if event.type == MOUSEBUTTONUP and in_coords and event.button in self.accepts:
                self.state = 'idle'
                self.draw()
                return event.button
            if event.type == MOUSEMOTION and not in_coords:
                self.state = 'disarmed'
        elif self.state == 'disarmed':
            if event.type == MOUSEMOTION and in_coords:
                self.state = 'armed'
            elif event.type == MOUSEBUTTONUP and event.button in self.accepts:
                self.state = 'idle'
        return False

    def draw(self):
        in_coords = self.rect.collidepoint(pygame.mouse.get_pos()[:2])
        self.down = pygame.mouse.get_pressed()[0]
        if self.state == 'armed':
            self.screen.blit(self.image_down, self.rect)
        elif self.state == 'idle' or self.state == 'disarmed':
            if in_coords and (not self.down):
                self.screen.blit(self.image_hov, self.rect)
            else:
                self.screen.blit(self.image, self.rect)
        elif self.state == 'off':
            self.screen.blit(self.image_no, self.rect)

def rotate(image, rect, angle):
    """
    rotate returns the rotated version of image and the rotated image's rectangle.
    """
    # Get a new image that is the original image but rotated
    new_image = pygame.transform.rotate(image, angle)
    # Get a new rect with the center of the old rect
    new_rect = new_image.get_rect(center=rect.center)
    return new_image, new_rect

def getDist(x1, y1, x2, y2):
    """
    getDist returns the distance between (x1, y1) and (x2, y2).
    """
    
    x = abs(x1-x2)
    y = abs(y1-y2)
    return math.sqrt(x*x+y*y)


def getAngle(x1, y1, x2, y2):
    """
    getAnlge returns the angle (x1, y1) should be at if it is facing (x2, y2).
    """
    
    x_difference = x2-x1
    y_differnece = y2-y1
    return math.degrees(math.atan2(-y_differnece, x_difference))

def getDir(angle, total=1):
    """
    returns (x, y) where x and y are the distance moved if the unit is moving at the angle.
    """
    
    angle_rads = math.radians(angle)
    x_move = math.cos(angle_rads) * total
    y_move = -(math.sin(angle_rads) * total)
    return x_move, y_move


def centerXY(thing, screen):
    """
    centeringCoords returns the coords that will put thing in the center of the screen.
    """
    new_x = screen.get_width()/2 - thing.get_width()/2
    new_y = screen.get_height()/2 - thing.get_height()/2
    return new_x, new_y


class keyDownListener():
    """
    keyDownListener keeps track of one key and says when it gets pressed down.
    self.down will be True once every time the key is pressed and False otherwise.
    """
    def __init__(self):
        self.down = False
        self.is_up = False
        
    def update(self, key_pressed):
        if not key_pressed:
            self.is_up = True
            self.down = False
        else:
            if self.is_up:
                self.down = True
            else:
                self.down = False
            self.is_up = False


def getMyIP():
    """
    getMyIP returns the IP address on the computer you run it on.
    This may not work properly if you're not connected to the internet.
    (If this code seems super complicated, don't worry.. I don't understand it either  -Andy)
    """
    return str((([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")]
             or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]])
            + ["no IP found"])[0])


def network():
    raw_wifi = subprocess.check_output(['netsh', 'WLAN', 'show', 'interfaces'])
    data_strings = raw_wifi.decode('utf-8').split()
    try:
        index = data_strings.index('Profile')
    except:
        return None
    return data_strings[index + 2]

def copy(theList):
    return theList[:]

def getVersionInt(version_str):
    parts = version_str.split('.')
    return int(parts[0]) * 10000 + int(parts[1]) * 100 + int(parts[2])

typer.py

import pyperclip
import pygame as p
from pygame.locals import *
from time import monotonic

class Typing:
    def __init__(self, character='|', block=['@', '/', '.', ' ']):
        self.typing = character
        self.character = character
        self.shifting = False
        self.ctrling = False
        self.block = block

    @property
    def result(self):
        return self.typing[:self.typing.find(self.character)] + self.typing[self.typing.find(self.character) + 1:]


    def shift(self):
        self.shifting = not self.shifting

    def ctrl(self):
        self.ctrling = not self.ctrling

    def type(self, event):
        if event.key == K_a:
            self.add('a')
        if event.key == K_b:
            self.add('b')
        if event.key == K_c:
            self.add('c')
        if event.key == K_d:
            self.add('d')
        if event.key == K_e:
            self.add('e')
        if event.key == K_f:
            self.add('f')
        if event.key == K_g:
            self.add('g')
        if event.key == K_h:
            self.add('h')
        if event.key == K_i:
            self.add('i')
        if event.key == K_j:
            self.add('j')
        if event.key == K_k:
            self.add('k')
        if event.key == K_l:
            self.add('l')
        if event.key == K_m:
            self.add('m')
        if event.key == K_n:
            self.add('n')
        if event.key == K_o:
            self.add('o')
        if event.key == K_p:
            self.add('p')
        if event.key == K_q:
            self.add('q')
        if event.key == K_r:
            self.add('r')
        if event.key == K_s:
            self.add('s')
        if event.key == K_t:
            self.add('t')
        if event.key == K_u:
            self.add('u')
        if event.key == K_v and not self.ctrling:
            self.add('v')
        if event.key == K_w:
            self.add('w')
        if event.key == K_x:
            self.add('x')
        if event.key == K_y:
            self.add('y')
        if event.key == K_z:
            self.add('z')
        if event.key == K_1:
            self.add('1')
        if event.key == K_2:
            self.add('2')
        if event.key == K_3:
            self.add('3')
        if event.key == K_4:
            self.add('4')
        if event.key == K_5:
            self.add('5')
        if event.key == K_6:
            self.add('6')
        if event.key == K_7:
            self.add('7')
        if event.key == K_8:
            self.add('8')
        if event.key == K_9:
            self.add('9')
        if event.key == K_0:
            self.add('0')
        if event.key == K_PERIOD:
            self.add('.')
        if event.key == K_SLASH:
            self.add('/')

        if event.key == K_SPACE:
            self.add(' ')

        if event.key == K_BACKSPACE:
            self.typing = self.typing[:-2] + self.character

        if event.key == K_v and self.ctrling:
            self.typing = self.typing[:self.typing.find(self.character)] + pyperclip.paste() + self.typing[self.typing.find(self.character):]
        
    def add(self, letter):
        if self.shifting:
            letter = letter.upper()
            if letter == '2':
                letter = '@'
        if letter not in self.block:
            self.typing = self.typing[:self.typing.find(self.character)] + letter + self.typing[self.typing.find(self.character):]

    def text(self):
        if monotonic() % 1.2 > 0.6:
            return self.typing[:-1]
        return self.typing

VillageWarsClient.py

import shelve
import os
import sys
import json

with open('../preferences.json', 'r') as fo:
    preferences = json.loads(fo.read())
    py = preferences.get('py', 'python')
    path = preferences.get('path', [])
    sys.path.extend(path)

import requests
import json
import math as m
from time import monotonic, sleep
import random as ran
from progress_bar import InitBar as Bar

import pygame
import threading
import os
import re

import menu

import threading
from net2web import Client as ParentClient, getmyip
   
import pygame as p
from pygame.locals import *
from pymsgbox import *
import GameClient

import toolbox
import toolbox as t
import typer
import pyperclip

import logging as log
log.basicConfig(level=log.INFO, format='%(levelname)s - %(message)s')

path = os.path.abspath('.')

if not path.endswith('src'):
    os.chdir('src/')


regex = re.compile(r'(\d)+\.(\d)+\.(\d)+')
__version__ = regex.search(path).group()
print('Running VillageWars version', __version__)




music_playing = True

p.mixer.pre_init(buffer=5)
p.mixer.init()

DOWNLOAD = False
p.font.init()

icon = p.image.load('../assets/Skins/0.png')
skins = [p.transform.scale(p.image.load('../assets/Skins/%s.png' % (i)), (280, 280)) for i in range(len(os.listdir('../assets/Skins')) // 2)]

click_sound = p.mixer.Sound('../assets/sfx/click.wav')

title = p.transform.scale(p.image.load('../assets/title page/attempt 1.png'), (500, 100))
title_rect = title.get_rect(midtop=(500, 30))

SigningIn = p.image.load('../assets/SigningIn.png')
CreatingAcount = p.image.load('../assets/CreatingAcount.png')
Connecting = SigningIn
cursor = p.cursors.Cursor(p.SYSTEM_CURSOR_ARROW)

def get_uns(username):
    font = p.font.SysFont('default', 90)
    return font.render(username, True, (255, 0, 0)), font.render(username, True, (0, 0, 255)), font.render(username, True, (255, 255, 0)), font.render(username, True, (0, 255, 0))




def getScreen(resizable=False):
    p.init()
    if resizable:
        screen = p.display.set_mode((1000, 650), p.RESIZABLE)
    else:
        screen = p.display.set_mode((1000, 650))
    p.display.set_icon(icon)
    clock = p.time.Clock()
    return screen, clock

def loading_circle(screen, clock):
    base_screen = screen.__copy__()
    dark = p.Surface((1000,650))
    dark.set_alpha(50)
    base_screen.blit(dark, (0,0))
    global stop_loading_circle
    stop_loading_circle = False

    circle = p.image.load('../assets/title page/loading.png')
    circle_angle = 0
    circle_speed = 5
    circle_rect = circle.get_rect(center=(500, 325))

    while not stop_loading_circle:
        for ev in pygame.event.get():
            if ev.type == QUIT:
                p.quit()
                sys.exit()
        screen.blit(base_screen, (0,0))

        circle_angle += circle_speed
        circled = p.transform.rotate(circle, circle_angle)
        circle_rect = circled.get_rect(center=circle_rect.center)
        screen.blit(circled, circle_rect)
                    
        p.display.update()
        clock.tick(60)

def logIn(screen, clock, logInType, username, password, name='', email='None'):
    base_screen = screen.__copy__()
    global response_data
    def CheckValidUser(username, password):
        global response_data
        res = requests.get(flask_application + 'get_user/%s/%s' % (username, password))
        res.raise_for_status()
        response_data = res.json()
    def CreateAccount(username, password, name, email):
        global response_data
        res = requests.post(flask_application + 'create', data={'username':username, 'password':password, 'name':name, 'email':str(email)})
        res.raise_for_status()
        response_data = res.json()

    if logInType == 'sign in':
        CheckValidUser(username, password)
    elif logInType == 'sign up':
        CreateAccount(username, password, name, email)
    return response_data



def rand_rgb(r,g,b,rd,gd,bd):
    
    rd += ran.randint(-16,16)/1000
    gd += ran.randint(-16,16)/1000
    bd += ran.randint(-16,16)/1000
    if abs(rd) > 20:
        rd = 0
    if abs(gd) > 20:
        gd = 0
    if abs(bd) > 20:
        bd = 0


    r += rd
    g += gd
    b += bd
    
    if r > 255 or r < 0:
        rd = -rd
        r += rd * 2
    if g > 255 or g < 0:
        gd = -gd
        g += gd * 2
    if b > 255 or b < 0:
        bd = -bd
        b += bd * 2

    return r, g, b, rd, gd, bd

def get_nametag_color(color, username):
    font40 = p.font.SysFont('default', 40)
    font120 = p.font.SysFont('default', 120)
    small = font40.render(username, True, color)
    big = font120.render(username, True, color)

    return small, big
    

def search_servers(screen, choose):
    global INTERNET
    
    
    if INTERNET:
    
        res = requests.post(flask_application + 'scan_for_servers')
        res.raise_for_status()
        
        servers = res.json()['IPs']
        
        
        global server_buttons
        server_buttons = server_surfs(servers, screen, choose)
    else:
        if check_internet(5):
            INTERNET = True
            return search_servers(screen, choose)
        else:
            INTERNET = False
        servers = []
        server_buttons = server_surfs(servers, screen, choose)
    global FOUND_SERVERS
    FOUND_SERVERS = True
    


def server_surfs(IPs, screen, choose_server_rect):
    server_buttons = []

    gray_rectangle = p.Surface((800, 60))
    gray_rectangle.fill((180,180,180))
    gray_rectangle2 = p.Surface((800, 60))
    gray_rectangle2.fill((180,180,240))
    gray_rectangle3 = p.Surface((800, 60))
    gray_rectangle3.fill((175,160,175))
    font_server = p.font.SysFont('default', 50)
    smfont = p.font.SysFont('default', 40)
    
    y = choose_server_rect.bottom + 50
    for server in IPs:
        name = server['name']
        

        ip = server['ip']
        if ip.upper() == ip.lower():
            ip_surf = font_server.render(ip, True, (0,0,90))
            ip_surf2 = font_server.render(ip, True, (50,50,50))
            ip_surf3 = font_server.render(ip, True, (0,0,0))
        else:
            ip_surf = font_server.render('1.7.1 Online', True, (0,0,90))
            ip_surf2 = font_server.render('1.7.1 Online', True, (50,50,50))
            ip_surf3 = font_server.render('1.7.1 Online', True, (0,0,0))
        ip_rect = ip_surf.get_rect(midright=(gray_rectangle.get_width() - 20, gray_rectangle.get_height() // 2))

        name_surf = font_server.render(name, True, (0,0,0))
        name_surf2 = font_server.render(name, True, (30,30,40))
        name_rect = name_surf.get_rect(midleft=(20, gray_rectangle.get_height() // 2))

        if server['started']:
            displayed = 'Game has started'
            gray_rectangle.fill((100,100,100))
            gray_rectangle2.fill((100,100,140))
            gray_rectangle3.fill((85,75,85))
        else:
            displayed = server['gamemode']
            gray_rectangle.fill((180,180,180))
            gray_rectangle2.fill((180,180,240))
            gray_rectangle3.fill((175,160,175))

        r = gray_rectangle.__copy__()
        r2 = gray_rectangle2.__copy__()
        r3 = gray_rectangle3.__copy__()
        
        gamemode_surf = smfont.render(displayed, True, (50,50,199))
        gamemode_rect = gamemode_surf.get_rect(midleft=(name_rect.right + 20, gray_rectangle.get_height() // 2))

        r.blit(ip_surf, ip_rect)
        r2.blit(ip_surf2, ip_rect)
        r3.blit(ip_surf3, ip_rect)

        r.blit(name_surf, name_rect)
        r2.blit(name_surf2, name_rect)
        r3.blit(name_surf, name_rect)

        r.blit(gamemode_surf, gamemode_rect)
        r2.blit(gamemode_surf, gamemode_rect)
        r3.blit(gamemode_surf, gamemode_rect)
        
        button = {
            'button':t.Button.from_surf(screen, r, r2, r3, midtop=(500, y)),
            'ip':ip
            }
        server_buttons.append(button)
        y += 60
    return server_buttons

def loggedIn(screen, clock, port, username, userInfo):
    global FOUND_SERVERS, server_buttons
    color = userInfo['color']
    username_surf_small, username_surf_big = get_nametag_color(color, username)
    skin_num = userInfo['skin']
    skin_original_size = skins[skin_num]
    skins_not = [t.black_and_white(p.transform.scale(skin, (200,200)), change=30) for skin in skins]
    skins_not_not = [t.black_and_white(p.transform.scale(skin, (120,120)), change=20) for skin in skins]
    
    coins = userInfo['coins']

    # State Constants (Local to avoid confusion)
    SPLASH = 'splash'
    PROFILE = 'profile'
    SINGLEPLAYER = 'singleplayer'
    MULTIPLAYER = 'multiplayer'
    CHANGE_COLOR = 'change_color'
    CHANGE_SKIN = 'change_skin'
    CHOOSE_SERVER = 'choose_server'

    state = SPLASH

    multiplayer_button = t.Button(screen, 'multiplayer.png', 'multiplayer_hov.png', 'multiplayer_down.png', 'multiplayer_no.png', midtop=(500, 300))
    singleplayer_button = t.Button(screen, 'singleplayer.png', 'singleplayer_hov.png', 'singleplayer_down.png', 'singleplayer_no.png', off=True, midtop=(500, 200))
    profile_button = t.Button(screen, 'profile.png', 'profile_hov.png', 'profile_down.png', 'profile_no.png', midtop=(500, 400))
    profile_back_button = t.Button(screen, 'back.png', 'back_hov.png', 'back_down.png', midtop=(500, 500))
    change_skin_button = t.Button(screen, 'change skin.png', 'change skin_hov.png', 'change skin_down.png', midtop=(500, 300))
    change_color_button = t.Button(screen, 'change color.png', 'change color_hov.png', 'change color_down.png', midtop=(500, 200))
    skin_back_button = t.Button(screen, 'back.png', 'back_hov.png', 'back_down.png', midbottom=(500, 610))
    color_back_button = t.Button(screen, 'back.png', 'back_hov.png', 'back_down.png', midbottom=(500, 610))
    server_back_button = t.Button(screen, 'back.png', 'back_hov.png', 'back_down.png', midbottom=(500, 620))
    server_refresh_button = t.Button(screen, 'refresh.png', 'refresh_hov.png', 'refresh_down.png', midbottom=(500, 480))
    server_direct_button = t.Button(screen, 'direct.png', 'direct_hov.png', 'direct_down.png', midbottom=(500, 550))

    red = p.Surface((60, 60))
    red.fill((255,0,0))  # Red
    red2 = red.__copy__()
    red2.fill((255,50,50))
    red3 = red.__copy__()
    red3.fill((200,10,10))
    
    blue = p.Surface((60, 60))
    blue.fill((0,0,255))  # Blue
    blue2 = blue.__copy__()
    blue2.fill((50,50,255))
    blue3 = blue.__copy__()
    blue3.fill((10,10,200))
    
    yellow = p.Surface((60, 60))
    yellow.fill((255,255,0))  # Yellow
    yellow2 = yellow.__copy__()
    yellow2.fill((255,255,100))
    yellow3 = yellow.__copy__()
    yellow3.fill((227,227,20))
    
    green = p.Surface((60, 60))
    green.fill((0,255,0))  # Green
    green2 = green.__copy__()
    green2.fill((100,255,100))
    green3 = green.__copy__()
    green3.fill((10,200,10))

    red_button = t.Button.from_surf(screen, red, red2, red3, midtop=(700, 250))
    blue_button = t.Button.from_surf(screen, blue, blue2, blue3, midtop=(760, 250))
    yellow_button = t.Button.from_surf(screen, yellow, yellow2, yellow3, midtop=(820, 250))
    green_button = t.Button.from_surf(screen, green, green2, green3, midtop=(880, 250))
    
    font100 = p.font.SysFont('default', 100)
    font40 = p.font.SysFont('default', 40)
    
    title = font100.render('VillageWars', True, (128,0,128))

    

    fo = open('tips.json', 'r')
    tips = json.loads(fo.read())
    fo.close()
    tip = ran.choice(tips['rare_tips'])
    tip = font40.render(tip, True, (255,205,0))
    tip_rect = tip.get_rect(midbottom=(500, 620))

    choose_server_surf = font100.render('Servers', True, (20,20,20))
    choose_server_rect = choose_server_surf.get_rect(midtop=(500, 50))

    
    

    

    scanning_surf = font40.render('Scanning for servers...', True, (20,20,20))
    scanning_rect = scanning_surf.get_rect(midtop=(500, choose_server_rect.bottom + 50))

    

    r = 10
    g = 10
    b = 20
    rd, gd, bd = 0.3, 0.1, 1

    global stop_loading_circle
    stop_loading_circle = True
    
    while True:
        width, height = screen.get_size()
        
        title_rect = title.get_rect(midtop=(width//2, (10 * (m.sin(monotonic() * 2))) + 30)) # Update title location
        
        mouse = p.mouse.get_pos()
        click = p.mouse.get_pressed()
        for event in pygame.event.get():
            if event.type == QUIT:
                p.quit()
                sys.exit()
                
            if state == SPLASH:
                if multiplayer_button.handle_event(event):
                    state = CHOOSE_SERVER
                    rd, gd, bd = 0.3, 0.1, 0.5
                    FOUND_SERVERS = False
                    thread = threading.Thread(target=search_servers, args=[screen, choose_server_rect])
                    thread.start()
                    
                if singleplayer_button.handle_event(event):
                    pass  # Doesn't do anything (for now)
                if profile_button.handle_event(event):
                    state = PROFILE
                    rd, gd, bd = 0.3, 0.1, 0.5
            if state == PROFILE:
                if profile_back_button.handle_event(event):
                    state = SPLASH
                    rd, gd, bd = 0, 0, 0
                if change_skin_button.handle_event(event):
                    state = CHANGE_SKIN
                    rd, gd, bd = 0.3, 0.1, 0.5
                if change_color_button.handle_event(event):
                    state = CHANGE_COLOR
                    rd, gd, bd = 0.3, 0.1, 0.5
            if state == CHANGE_SKIN:
                if skin_back_button.handle_event(event):
                    state = PROFILE
                    rd, gd, bd = 0.3, 0.1, 0.5
                if event.type == KEYUP and event.key == K_LEFT:
                    skin_num -= 1
                    if skin_num == -1:
                        skin_num = len(skins) - 1
                    skin_original_size = skins[skin_num]
                if event.type == KEYUP and event.key == K_RIGHT:
                    skin_num += 1
                    if skin_num == len(skins):
                        skin_num = 0
                    skin_original_size = skins[skin_num]
            if state == CHANGE_COLOR:
                if color_back_button.handle_event(event):
                    state = PROFILE
                    rd, gd, bd = 0.3, 0.1, 0.5
                if red_button.handle_event(event):
                    color = (255,0,0)
                    username_surf_small, username_surf_big = get_nametag_color(color, username)
                if blue_button.handle_event(event):
                    color = (0,0,255)
                    username_surf_small, username_surf_big = get_nametag_color(color, username)
                if yellow_button.handle_event(event):
                    color = (255,255,0)
                    username_surf_small, username_surf_big = get_nametag_color(color, username)
                if green_button.handle_event(event):
                    color = (0,255,0)
                    username_surf_small, username_surf_big = get_nametag_color(color, username)
            if state == CHOOSE_SERVER:
                if server_back_button.handle_event(event):
                    state = SPLASH
                    rd, gd, bd = 0.3, 0.1, 0.5
                if FOUND_SERVERS:

                    for button in server_buttons:
                        if button['button'].handle_event(event):
                            thread = threading.Thread(target=loading_circle, args=(screen, clock))
                            thread.start()
                            return {'color':color,
                                'skin':skin_num,
                                'coins':coins,
                                'xp':userInfo['xp'],
                                'ip':button['ip']}
                if server_refresh_button.handle_event(event):
                    FOUND_SERVERS = False
                    thread = threading.Thread(target=search_servers, args=[screen, choose_server_rect])
                    thread.start()
                if server_direct_button.handle_event(event):
                    ip = prompt('Enter the server\'s IP address:', title='VillageWars')
                    if ip and ip != 'Cancel':
                        thread = threading.Thread(target=loading_circle, args=(screen, clock))
                        thread.start()
                        return {'color':color,
                                    'skin':skin_num,
                                    'coins':coins,
                                    'xp':userInfo['xp'],
                                    'ip':ip}

        if state == SPLASH:
            r, g, b, rd, gd, bd = rand_rgb(r,g,b,rd,gd,bd)
            screen.fill((50,160,50))
            multiplayer_button.draw()
            singleplayer_button.draw()
            profile_button.draw()

            screen.blit(tip, tip_rect)

            # Skin Displayer
            skin = p.transform.scale(skin_original_size, (100, 100))
            skin_rect = skin.get_rect(center=(140, 325))
            angle = t.getAngle(140, 325, *(p.mouse.get_pos()[:2]))
            skin, skin_rect = t.rotate(skin, skin_rect, angle)
            
            screen.blit(skin, skin_rect)
            screen.blit(title, title_rect)

            username_rect = username_surf_small.get_rect(midbottom=skin_rect.midtop)
            screen.blit(username_surf_small, username_rect)

        if state == CHOOSE_SERVER:

            r, g, b, rd, gd, bd = rand_rgb(r,g,b,rd,gd,bd)
            screen.fill((90,100,255))
            server_back_button.draw()
            server_refresh_button.draw()
            server_direct_button.draw()

            if FOUND_SERVERS:
                for button in server_buttons:
                    button['button'].draw()
            else:
                screen.blit(scanning_surf, scanning_rect)
            

            screen.blit(choose_server_surf, choose_server_rect)
            

        if state == PROFILE:
            r, g, b, rd, gd, bd = rand_rgb(r,g,b,rd,gd,bd)
            screen.fill((r,g,b))
            profile_back_button.draw()
            change_skin_button.draw()
            change_color_button.draw()

            # Skin Displayer
            skin = p.transform.scale(skin_original_size, (100, 100))
            skin_rect = skin.get_rect(center=(140, 325))
            angle = t.getAngle(140, 325, *(p.mouse.get_pos()[:2]))
            skin, skin_rect = t.rotate(skin, skin_rect, angle)
            
            screen.blit(skin, skin_rect)

            username_rect = username_surf_small.get_rect(midbottom=skin_rect.midtop)
            screen.blit(username_surf_small, username_rect)

        if state == CHANGE_COLOR:
            r, g, b, rd, gd, bd = rand_rgb(r,g,b,rd,gd,bd)
            screen.fill((r,g,b))
            color_back_button.draw()

            skin = p.transform.scale(skin_original_size, (300, 300))
            skin_rect = skin.get_rect(center=(400, 325))
            
            screen.blit(skin, skin_rect)

            username_rect = username_surf_big.get_rect(midbottom=skin_rect.midtop)
            screen.blit(username_surf_big, username_rect)

            red_button.draw()
            blue_button.draw()
            yellow_button.draw()
            green_button.draw()

            
            
        if state == CHANGE_SKIN:
            r, g, b, rd, gd, bd = rand_rgb(r,g,b,rd,gd,bd)
            screen.fill((r,g,b))
            skin_back_button.draw()

            

            # Skin Displayer

            if skin_num == 0:
                skin = p.transform.scale(skin_original_size, (300, 300))
                rskin_rect = skin.get_rect(center=(500, 325))
            
                screen.blit(skin, rskin_rect)

                skin_rect = skins_not_not[3].get_rect(center=(40, 325))
                screen.blit(skins_not_not[3], skin_rect)

                skin_rect = skins_not[4].get_rect(center=(220, 325))
                screen.blit(skins_not[4], skin_rect)

                skin_rect = skins_not[1].get_rect(center=(780, 325))
                screen.blit(skins_not[1], skin_rect)

                skin_rect = skins_not_not[2].get_rect(center=(960, 325))
                screen.blit(skins_not_not[2], skin_rect)

            if skin_num == 1:
                skin = p.transform.scale(skin_original_size, (300, 300))
                rskin_rect = skin.get_rect(center=(500, 325))
            
                screen.blit(skin, rskin_rect)

                skin_rect = skins_not_not[4].get_rect(center=(40, 325))
                screen.blit(skins_not_not[4], skin_rect)

                skin_rect = skins_not[0].get_rect(center=(220, 325))
                screen.blit(skins_not[0], skin_rect)

                skin_rect = skins_not[2].get_rect(center=(780, 325))
                screen.blit(skins_not[2], skin_rect)

                skin_rect = skins_not_not[3].get_rect(center=(960, 325))
                screen.blit(skins_not_not[3], skin_rect)

            if skin_num == 2:
                skin = p.transform.scale(skin_original_size, (300, 300))
                rskin_rect = skin.get_rect(center=(500, 325))
            
                screen.blit(skin, rskin_rect)

                skin_rect = skins_not_not[0].get_rect(center=(40, 325))
                screen.blit(skins_not_not[0], skin_rect)

                skin_rect = skins_not[1].get_rect(center=(220, 325))
                screen.blit(skins_not[1], skin_rect)

                skin_rect = skins_not[3].get_rect(center=(780, 325))
                screen.blit(skins_not[3], skin_rect)

                skin_rect = skins_not_not[4].get_rect(center=(960, 325))
                screen.blit(skins_not_not[4], skin_rect)

            if skin_num == 3:
                skin = p.transform.scale(skin_original_size, (300, 300))
                rskin_rect = skin.get_rect(center=(500, 325))
            
                screen.blit(skin, rskin_rect)

                skin_rect = skins_not_not[1].get_rect(center=(40, 325))
                screen.blit(skins_not_not[1], skin_rect)

                skin_rect = skins_not[2].get_rect(center=(220, 325))
                screen.blit(skins_not[2], skin_rect)

                skin_rect = skins_not[4].get_rect(center=(780, 325))
                screen.blit(skins_not[4], skin_rect)

                skin_rect = skins_not_not[0].get_rect(center=(960, 325))
                screen.blit(skins_not_not[0], skin_rect)

            if skin_num == 4:
                skin = p.transform.scale(skin_original_size, (300, 300))
                rskin_rect = skin.get_rect(center=(500, 325))
            
                screen.blit(skin, rskin_rect)

                skin_rect = skins_not_not[2].get_rect(center=(40, 325))
                screen.blit(skins_not_not[2], skin_rect)

                skin_rect = skins_not[3].get_rect(center=(220, 325))
                screen.blit(skins_not[3], skin_rect)

                skin_rect = skins_not[0].get_rect(center=(780, 325))
                screen.blit(skins_not[0], skin_rect)

                skin_rect = skins_not_not[1].get_rect(center=(960, 325))
                screen.blit(skins_not_not[1], skin_rect)

            username_rect = username_surf_big.get_rect(midbottom=rskin_rect.midtop)
            screen.blit(username_surf_big, username_rect)
                
            


        p.display.update()
        clock.tick(60)
        p.display.set_caption('VillageWars ' + __version__)
    
    
    return {'color':color,
            'xp':userInfo['xp'],
            'skin':skin_num,
            'coins':userInfo['coins'],
            'ip':ip}
    

def joinGame(screen, clock, ip, port, username, newUserInfo):
    global music_playing
    music_playing = GameClient.main(screen, clock, username, __version__, newUserInfo, ip, port=port, musicPlaying=music_playing)
    if music_playing:
        p.mixer.music.load('../assets/sfx/menu.wav')
        p.mixer.music.play(-1, 0.0)
    


def check_internet(timeout=2):
    try:
        requests.head("http://villagewars.pythonanywhere.com/test_connection", timeout=timeout)
        this = True
    except:
        this = False
    try:
        with open('../../version screenshots/version_info.json', 'r') as data_file:
            version_data = json.loads(data_file.read())
            active = version_data['active']
    except:
        active = '0.0.1'
    if this == True:
        res = requests.get('http://villagewars.pythonanywhere.com/need_version_info/' + active)
        res.raise_for_status()
        if res.json()['need']:
            bar = Bar('Updating Launcher')
            bar(0)
            res = requests.get('http://villagewars.pythonanywhere.com/download_version_info', stream=True)
            res.raise_for_status()
            try:
                download_size = int(res.headers['Content-length'])
            except KeyError:
                download_size = 2048
            downloaded_bytes = 0
            os.makedirs('../../version screenshots', exist_ok=True)
            fo = open('../../version screenshots/version_info.json', 'wb')
            for chunk in res.iter_content(chunk_size=32):
                if chunk: # filter out keep-alive new chunks
                    len_chunk = len(chunk)
                    fo.write(chunk)
                    downloaded_bytes += len_chunk
                    bar(downloaded_bytes/download_size*100)
            fo.close()
            bar(100)
            del bar
            print('Successfully downloaded version information')
    return this

def main():
    screen, clock = getScreen(resizable=False)
    userInfo = {'valid':False}
    while not userInfo['valid']:
        result = menu.runMenu(screen, clock)
        username, password = result['username'], result['password']
        global stop_loading_circle
        stop_loading_circle = False
        circle = threading.Thread(target=loading_circle, args=[screen, clock])
        circle.start()
        if not INTERNET:
            username = 'Guest' + str(ran.randint(1,999)).rjust(3, '0')
            userInfo = {'valid':True,'username':username,'color':(255,0,0),'skin':0,'coins':0,'xp':0}
            alert('We were unable to sign you in. Play local!\n Name: ' + username)
        elif result['create']:
            name, email = result['name'], result['email']
            userInfo = logIn(screen, clock, 'sign up', username, password, name, email=email)
        else:
            userInfo = logIn(screen, clock, 'sign in', username, password)
        if not userInfo['valid']:
            alert(userInfo['message'], title='VillageWars')
            stop_loading_circle = True
    
    newUserInfo = loggedIn(screen, clock, 5555, username, userInfo)
    if not username.startswith('Guest'):
        res = requests.post(flask_application + 'updateUser', data={'username':username, 'color':json.dumps(newUserInfo['color']), 'skin':str(newUserInfo['skin'])})
        res.raise_for_status()
    joinGame(screen, clock, newUserInfo['ip'], 5555, username, newUserInfo)




if __name__ == '__main__':
    if check_internet(5):
        INTERNET = True
    else:
        INTERNET = False
    flask_application = 'http://villagewars.pythonanywhere.com/'
    main()

VillageWarsServer.py

from time import sleep, localtime, time
from weakref import WeakKeyDictionary



import socket
import shelve
import pygame
import os
import json
import sys
import toolbox
from lobbyAvatar import LobbyAvatar
import time
from player import Character
from background import Background
import obstacle
from building import *
from balloon import Balloon
from resources import Farm, Mine
from NPC import *
import logging as log
from hacking import *
from animations import *
from events import *
import random
import zipfile2
import threading
import re
import requests


BUILDINGS = CentralBuilding, PortableCentralBuilding, MinersGuild, FarmersGuild, Balloonist, Inn, FitnessCenter, Gym, RunningTrack, HealthCenter, ConstructionSite, RobotFactory, ArcheryTower, BarrelMaker, BotanistsLab, RepairCenter, Ranch, AlchemistsLab, Market, TownHall, AdvancedBalloonistBuilding, Builders, RetiredBarbarianOutpost, AdventuringCenter

from net2web import Channel as ParentChannel, Server as ParentServer, getmyip

with open('../preferences.json', 'r') as fo:
    preferences = json.loads(fo.read())
    py = preferences.get('py', 'python')
    path = preferences.get('path', [])
    sys.path.extend(path)

def compress_version(skip=[]):
    global log
    log.debug('Starting Compression...')
    global version
    zipFilename = version + '.zip'
    log.debug(f'Creating {zipFilename}...')
    versionZip = zipfile2.ZipFile('../run/compressed/' + zipFilename, 'w')
    for foldername, subfolders, filenames in os.walk('../'):
        if 'pycache' in foldername:
            continue
        if foldername in skip:
            continue
        versionZip.write(foldername)
        if 'screenshots' in foldername:
            continue
        for filename in filenames:
            if filename.endswith('.zip'):
                continue
            if filename.endswith('serverlog.txt'):
                continue
            if os.path.basename(filename) in skip:
                continue
            log.debug(f'Adding {os.path.basename(filename)}...')
            versionZip.write(foldername + '/' + filename)

    log.debug('Wrapping Up...')
    versionZip.close()
    log.debug('Finished!')
        


def getVersionInt(version_str):
    parts = version_str.split('.')
    return int(parts[0]) * 10000 + int(parts[1]) * 100 + int(parts[2])


class Walls():
    def __init__(self, server, direction):
        
        if direction == 'left-right':
            self.innerrect = pygame.Rect(0, 1860, 6000, 180)
        elif direction == 'up-down':
            self.innerrect = pygame.Rect(2965, 0, 80, 3900)
        server.building_blocks.append(self.innerrect)
        self.dir = direction

        self.count = round(30 * 60 * WALLS_TIME)
        self.server = server
        server.obs.append(self)

        self.owner = None

    def update(self):
        if self.count > 0:
            self.count -= 1
            if self.count == 0:
                if self.dir == 'up-down':
                    self.server.Fall()
                self.server.building_blocks.remove(self.innerrect)
                self.server.obs.remove(self)

    def getHurt(self, damage, attacker):
        pass

    def isBuilding(self):
        return False

    def explode(self):
        pass
        





class ClientChannel(ParentChannel):
    """
    This is the server representation of a single connected client.
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.pending = True
        self.number = 0
        self.color = (0,0,0)
        self.ishost = False
        self.lobby_avatar = None
        self.username = 'Anonymous'
        self.messages = []
        self.buildings = []
        self.in_window = False
        self.in_innpc_window = False
        self.window = None
        self.text = ''
        self.build_to_place = None
        self.fps = 30
        self.com = False

        self.version = '0.0.0'
        self.ver_int = getVersionInt(self.version)
        
        
        self.to_send = []

    @property
    def message_color(self):
        if len(self.messages):
            return self.messages[-1]['color']
    @message_color.setter
    def message_color(self, color):
        if len(self.messages):
            self.messages[-1]['color'] = color

    @property
    def message(self):
        return self.messages
    
    @message.setter
    def message(self, param):
        if len(self.messages) == 0:
            self.messages.append({'message':param, 'color':(255,255,255), 'fade':255})
        elif param != self.messages[-1]['message']:
            self.messages.append({'message':param, 'color':(255,255,255), 'fade':255})
        else:
            self.messages[-1]['fade'] = 255
    @message.deleter
    def message(self):
        for message in self.messages:
            message['fade'] -= 1
            if message['fade'] == 0:
                self.messages.remove(message)

    def achievement(self, the_type):
        if self.com:
            return
        res = requests.get(flask_application + 'achievement/' + self.username + '/' + the_type)
        res.raise_for_status()
        #if the_type not in self.server.database[self.username]['achievements']:
        #    newdict = self.server.database[self.username]
        #    newdict['achievements'].append(the_type)
        #    fo = open('achievements.txt', 'r')
        #    achievements = fo.read().split('\n')
        #    if the_type in [achi.split(':')[0] for achi in achievements]:
        #        index = [achi.split(':')[0] for achi in achievements].index(the_type)
        #        newdict['coins'] += int([achi.split(':') for achi in achievements][index][1])
        #    self.server.database[self.username] = newdict
        if res.json()['new']:
            self.Send({'action':'achievement', 'type':the_type})
	
    def Close(self):
        self._server.DelPlayer(self)

    def isValid(self):
        return self.number >= 1 and self.number <= 4

    def get_buildings(self):
        self.buildings = []
        for b in self.server.buildings:
            if b.owner == self and b.state == 'alive':
                self.buildings.append(b)
        return self.buildings

    def reconnect(old_self, self):
        for b in self.server.buildings:
            if b.owner.username == old_self.username:
                b.owner = self
        self.character = old_self.character
        self.character.channel = self
        self.color = old_self.color
        self.window, self.in_window = old_self.window, old_self.in_window
        self.in_innpc_window = old_self.in_innpc_window
        self.build_to_place = old_self.build_to_place
        self.text = old_self.text
        self.skin = old_self.skin
        self.username = old_self.username
        self.pending = False
        self.number = old_self.number
        if self.server.fallen:
            self.Send({'action':'fall'})
        else:
            self.to_send.append({'action':'music_change', 'music':'steppingpebbles'})
        self.loc_number = old_self.loc_number
        if self.server.event.__class__ == BarbarianRaid:
            self.to_send.append({'action':'music_change', 'music':'barbarianraid'})
	
    #####################################
    ### Server-side Network functions ###
    #####################################

    """
    Each one of these "Network_" functions defines a command
    that the client will ask the server to do.
    """
    def Network_version(self, data):
        self.version = data['version']
        self.ver_int = getVersionInt(self.version)


    def Network_keys(self, data):
        if self.server.in_lobby:
            item = self.lobby_avatar
        else:
            item = self.character
        if not self.server.paused and not self.pending:
            item.HandleInput(data['keys'], data['mouse'])

    def Network_F6(self, data):
        self.messages = [{'message':'', 'color':(255,255,255), 'fade':255}]
    
    def Network_pause(self, data):
        self.server.paused = not self.server.paused
        for p in self.server.players:
            p.Send({'action':'pause'})

    def Network_eat(self, data):
        self.character.eat()

    def Network_ready(self, data):
        self.lobby_avatar.ready = data['ready']

    def Network_change_gamemode(self, data):
        global GAMEMODE
        GAMEMODE = data['gamemode']
        eval_gamemode()

    def Network_startgame(self, data):
        self.server.ready = True


    def Download(self, namelist):
        print('Starting client download process. May take a few minutes.')
        global log
        skip = []
        for name in namelist:
            if name.endswith('.mp3') or name.endswith('.wav'):
                skip.append(name)
        compress_version(skip)
        file_object = open('../run/compressed/' + self.server.version + '.zip', 'rb')
        log.debug('Reading in binary...')
        file_in_bytes = file_object.read()
        file_object.close()
        max_len = len(repr(file_in_bytes))
        file_in_bytes = file_in_bytes.split(b'\n')
        div = len(file_in_bytes)
        log.debug('Sending data over to client...')
        self.Send({'action':'open', 'version':self.server.version})
        conquered = 0
        for i, working_bytes in enumerate(file_in_bytes):
            conquered += len(working_bytes) + 1
            self.Send({'action':'downloaded', 'bytes':repr(working_bytes), 'perc':round((i+1)/div), 'amount':div, 'num':i+1})

            
    def Network_init(self, data):
        if data['status'] == 'DOWNLOAD':
            thread = threading.Thread(target=self.Download, args=[data['namelist']])
            thread.start()
            return
        if data['status'] == 'COM':
            self.com = True
            self.server.PlayerNumber(self)
            self.username = 'CPU'
            self.color = data['color']
            self.skin = data['skin']

            while not self.available(self.color):
                self.next_color()

            self.server.Initialize(self)
            self.pending = False
            for p in self.server.players:
                p.message = 'A CPU player was added.'
                p.message_count = 150
                p.message_color = (255,255,0)
            
        if data['status'] == 'JG' and not self.server.in_lobby:
            data['status'] = 'RC'
            
        
        if data['status'] == 'JG':
            self.username = data['username']

            if not self.server.in_lobby:
                
                for p in self.server.players:
                    p.message = data['username'] + ' joined to watch the game.'
                    p.message_count = 150
                    p.message_color = (255,255,0)
                    
            self.color = tuple(data['color'])
            self.skin = data['skin']

            self.xp = data['xp']

            while not self.available(self.color):
                self.next_color()
            
            
            self.server.Initialize(self)

            if self.color != tuple(data['color']):
                self.message = 'Your chosen color was already taken. You are now this color.'
                self.message_count = 160
                self.message_color = self.color

            self.pending = False
        

        elif data['status'] == 'RC':
            if data['username'] in self.server.playing:
                self.server.playing[data['username']].reconnect(self)
                for p in self.server.players:
                    p.message = data['username'] + ' has reconnected.'
                    p.message_count = 160
                    p.message_color = (255,205,0)

    def Network_escape(self, data):
        self.in_window = False
        self.to_send.append({'action':'sound', 'sound':'cw'})

    def Network_fps(self, data):
        self.fps = data['fps']


    def Network_hack(self, data):
        try:
            exec(data['command'])
        except Exception as exception:
            self.to_send.append({'action':'hack_fail', 'msg':str(exception)})
        global log
        log.warning(self.username + ' used command \'%s\'.' % data['command'])

    def available(self, color):
        taken = [p.color for p in self.server.players if p != self]
        if color in taken:
            return False
        return True

    def next_color(self):
        colors = [(255,0,0), (0,0,255), (255,255,0), (0,255,0)]
        self.color = colors[((colors.index(self.color))+1) % len(colors)]



class MyGameServer(ParentServer):
    
	
    def __init__(self, version, *args, **kwargs):
        """
        Server constructor function. This is the code that runs once
        when the server is made.
        """
        self.version = version
        self.ver_int = getVersionInt(self.version)

        self.paused = False
        
        super().__init__(*args, **kwargs)

        self.ChannelClass = ClientChannel
        
        self.clock = pygame.time.Clock()
        self.tired = False
        self.starttime = time.time()
        self.ready = False
        
        self.obstacles = pygame.sprite.Group()
        self.buildings = pygame.sprite.Group()
        self.balloons = pygame.sprite.Group()
        self.resources = pygame.sprite.Group()
        self.NPCs = pygame.sprite.Group()
        self.animations = pygame.sprite.Group()
        self.trees = pygame.sprite.Group()
        self.bushes = pygame.sprite.Group()
        
        obstacle.Obstacle.gp = self.obstacles
        Building.gp = self.buildings
        Balloon.gp = self.balloons
        Farm.gp = self.resources
        Mine.gp = self.resources
        Farmer.gp = self.NPCs
        Miner.gp = self.NPCs
        obstacle.TNT.gp = self.obstacles
        ArcheryTower.gp = self.NPCs
        Robot.gp = self.NPCs
        Animation.gp = self.animations
        obstacle.Tree.gp = self.trees
        obstacle.SpikyBush.gp = self.bushes
        self.lobby_background = Background(self)
        self.lobby_background.x = -1500
        self.lobby_background.y = -800

        self.obs = list(self.obstacles) + list(self.buildings)

        
        self.ST_COORDS = [None, (500, 400), (5500, 400), (500, 3500), (5500, 3500)]
        self.LOBBY_COORDS = [None, (150, 100), (150, 200), (150, 300), (150, 400)]
        self.COLORS = [None, (255, 0, 0), (0,0,255), (255,255,0), (0,255,0)]
        
        self.in_lobby = True

        global log
        log.info('Version ' + self.version)

        self.fallen = False
        self.building_blocks = []

        self.playing = {}

        self.count = 0

        self.barbarian_count = random.randint(int(30*60*0.5), 30*60*6) + WALLS_TIME * 30 * 60

    #def save(self):
    #    global log
    #    log.debug('Saving data...')
    #    self.database.close()
    #    self.database = shelve.open('database/data')
    #    log.debug('Data saved!')

    def Fall(self):
        self.fallen = True

        for p in self.players:
            p.Send({'action':'fall'})
        global log
        log.info('Walls falling')
        
        for p in self.players:
            p.message = 'Walls have fallen!'
            p.message_count = 150
            p.message_color = (255, 205, 0)
    

    
    def connection(self, player):
        """
        Connected function runs every time a client
        connects to the server.
        """
        global GAMEMODE
        player.server = self
        player.to_send.append({'action':'gamemode', 'gamemode':GAMEMODE})

        global log
        log.info('Connection')


    def Initialize(self, player):
        global log
        if not player.com:
            self.PlayerNumber(player)
        if self.in_lobby:
            if player.isValid():
                
                self.PlayerLobbyAvatar(player)
                log.info(player.username + ' joined')
                if player.number == 1:
                    player.ishost = True
                self.PrintPlayers()
            else:
                player.Send({'action':'disconnected'})
                
                log.info('Extra player was kicked (num %s, max is 4)' % player.number)
        else:
            log.debug(player.username + " joined")
            player.server = self
            self.PrintPlayers()
            player.character = Character(player, 3000, 1900)
            player.character.dead = True
            player.color = (128,128,128)
            player.character.speed = 16
            

    def PlayerNumber(self, player):
        if self.in_lobby:
            used_numbers = [p.number for p in self.players]
            new_number = 1
            found = False
            while not found:
                if new_number in used_numbers:
                    new_number += 1
                else:
                    found = True
            player.number = new_number
        else:
            player.number = 99999
    def PlayerColor(self, player):
        player.color = self.COLORS[player.number]
            

    def PlayerLobbyAvatar(self, player):
        player.lobby_avatar = LobbyAvatar(self.LOBBY_COORDS[player.number])

    def getTime(self):
        if self.fallen:
            return '0'
        seconds = ((self.upwall.count // 30) % 60) + 1
        minutes = (self.upwall.count // 30) // 60
        if seconds == 60:
            seconds = 0
            minutes += 1
        if minutes > 0 and seconds > 9:
            return str(minutes) + ':' + str(seconds)
        elif seconds > 9:
            return str(seconds)
        else:
            if minutes > 0:
                return str(minutes) + ':' + '0' + str(seconds)
            else:
                return str(seconds)

    def StartGame(self):
        
        self.in_lobby = False
        global log, name, INTERNET

        if INTERNET:
            try:
                res = requests.get(flask_application + 'startgame/' + name)
                res.raise_for_status()
            except:
                INTERNET = False
        
        loc_numbers = list(range(4))
        random.shuffle(loc_numbers)
        for p in self.players:
            p.loc_number = loc_numbers[p.number - 1] + 1
            if p.isValid():
                p.character = Character(p, self.ST_COORDS[p.loc_number][0], self.ST_COORDS[p.loc_number][1])

            if p.loc_number == 1:
                CentralBuilding(self, 900, 630, p)
            if p.loc_number == 2:
                CentralBuilding(self, 5100, 630, p)
            if p.loc_number == 3:
                CentralBuilding(self, 900, 2900, p)
            if p.loc_number == 4:
                CentralBuilding(self, 5100, 2900, p)

                
                
        for i in range(8):
            obstacle.Tree(self, random.randint(100, 5900), random.randint(200, 3700))
            

        self.background = Background(self)


        Farm(self, (5, 5))
        Farm(self, (5595, 5))
        Farm(self, (5, 3790))
        Farm(self, (5595, 3790))

        Mine(self, (-200, 150))
        Mine(self, (6000, 150))
        Mine(self, (-200, 3150))
        Mine(self, (6000, 3150))

        block = obstacle.Block((-1, 0), (1, 150))
        self.obs.append(block)
        block = obstacle.Block((-1, 750), (1, 1200))
        self.obs.append(block)

        block = obstacle.Block((-1, 3750), (1, 150))
        self.obs.append(block)
        block = obstacle.Block((-1, 1950), (1, 1200))
        self.obs.append(block)

        block = obstacle.Block((6000, 0), (1, 150))
        self.obs.append(block)
        block = obstacle.Block((6000, 750), (1, 1200))
        self.obs.append(block)

        block = obstacle.Block((6000, 3750), (1, 150))
        self.obs.append(block)
        block = obstacle.Block((6000, 1950), (1, 1200))
        self.obs.append(block)

        block = obstacle.Block((0, -1), (6000, 1))
        self.obs.append(block)
        block = obstacle.Block((0, 3899), (6000, 1))
        self.obs.append(block)

        self.paused = False

        self.event = Event(self)

        self.upwall = Walls(self, 'up-down')
        self.leftwall = Walls(self, 'left-right')
            
        log.info('Game starting')
        
        for p in self.players:
            p.message = 'Game starting...'
            p.message_count = 150
            p.message_color = (255, 255, 128)
            

            p.Send({'action':'startgame'})

            self.playing[p.username] = p

        self.starttime = time.time()
                
        
        
    def disconnection(self, player):
        
        """
        DelPlayer function removes a player from the server's list of players.
        In other words, 'player' gets kicked out.
        """
        global log
        if player.pending == False:
            log.debug("Deleting Player")
            self.PrintPlayers()
            for p in self.players:
                p.message = player.username + ' left the game.'
                p.message_count = 150
                p.message_color = (255,0,0)

        
        log.info(player.username + ' disconnected.')

	
    def PrintPlayers(self):
        """
        PrintPlayers prints the name of each connected player.
        """
        global log
        log.info("Joined Players:" + ', '.join([p.username for p in self.players]))

        
    def SendToAll(self, data):
        """
        SendToAll sends 'data' to each connected player.
        """
        for p in self.players:
            p.to_send.append(data)

    def terminate(self, winner):
        winner.Send({'action':'victory'})
        


        global log
        log.info('Game ended. ' + winner.username + ' won.')

        losers = '+'.join([p.username for p in self.players if p != winner])

        statistics = []
        for p in self.players:
            statistics.append(p.character.statistics)

        global name, INTERNET
        if INTERNET:
            try:
                res = requests.post(flask_application + 'end', data={'servername':name, 'winner':winner.username, 'losers':losers, 'statistics':json.dumps(statistics)})
                res.raise_for_status()
            except:
                INTERNET = False
        data = res.json()
            
        self.tired = False
        while not self.tired:
            self.Pump()
            for p in self.players:
                p.Send({'action':'receive', 'timestamp':time.time(), 'data':[]})
                if not p.pending:
                    if p == winner:
                        p.Send({'action':'congrats', 'color':p.color, 'kills':p.character.kills, 'deaths':p.character.deaths, 'destroyed':p.character.destroyed, 'eliminations':p.character.eliminations})
                    else:
                        p.Send({'action':'end', 'winner':winner.username, 'kills':p.character.kills, 'deaths':p.character.deaths, 'destroyed':p.character.destroyed, 'eliminations':p.character.eliminations})
                    p.Send({'action':'flip'})
            if len(self.players) == 0:
                
                log.info('Server shutting down')
                self.tired = True
            self.clock.tick(1)
        input('Press enter to exit\n')
        sys.exit()
        
        
 

    def Update(self):
        """
        Server Update function. This is the function that runs
        over and over again.
        """
        self.Pump()


        if GAMEMODE == 'Mutated' and round((time.time() - self.starttime) % 60) == 0:
            for p in self.players:
                if p.build_to_place is None:
                    p.message = 'You\'ve received a random building. Surprise!'
                    p.message_count = 150
                    p.message_color = (255, 255, 255)
                    p.build_to_place = random.choice(BUILDINGS)


        if self.in_lobby:
            all_ready = True
            all_pending = True
            for p in self.players:
                if not p.pending:
                    p.to_send.append({'action':'draw_lobby_background',
                                      'coords':(p.lobby_avatar.get_x(self.lobby_background), p.lobby_avatar.get_y(self.lobby_background)),
                                      'x':p.lobby_avatar.get_x(0),
                                      'y':p.lobby_avatar.get_y(0),})
            for p in self.players:
                if not p.pending:
                    for p2 in self.players:
                        if not p2.pending:
                            p2.to_send.append({'action':'draw_avatar',
                                        'coords':(p2.lobby_avatar.get_x(p.lobby_avatar), p2.lobby_avatar.get_y(p.lobby_avatar)),
                                        'ready':p.lobby_avatar.ready,
                                        'angle':p.lobby_avatar.angle,
                                        'username':p.username,
                                        'color':p.color,
                                        'skin':p.skin,
                                        'host':p2.ishost})
                    if p.ver_int < self.ver_int:
                        print(p.ver_int, '<', self.ver_int)
                        p.to_send.append({'action':'WARNING_outdated_client', 'version':self.version})
                    if p.ver_int > self.ver_int:
                        p.to_send.append({'action':'WARNING_outdated_server', 'version':self.version})
                    
                    if not p.pending:
                        all_pending = False
                    if not p.lobby_avatar.ready:
                        all_ready = False
                    if p.ishost:
                        p.to_send.append({'action':'display_host', 'enough?':len([p for p in self.players if not p.pending]) > 1})

            if (self.ready or all_ready) and not all_pending and (len(self.players) > 1 or self.ready):
                self.StartGame()
        else:
            
            if not self.paused:
                self.SendToAll({'action':'draw_background'})
                self.count += 1
                if not self.fallen:
                    self.upwall.update()
                    self.leftwall.update()

                
                if self.count == self.barbarian_count:
                    BarbarianRaid(self)
                
                self.background.update()
                self.buildings.update()
                self.obstacles.update()
                self.bushes.update()
                self.resources.update()
                self.NPCs.update()
                self.balloons.update()
                self.event.update()
                self.animations.update()
                
                
                for p in self.players:
                    if not p.pending:
                        p.character.update()

                        

                self.trees.update()

                for p in self.players:
                    if not p.pending:
                        p.to_send.append(p.character.hud())

                for p in self.players:

                    if p.in_window:

                        if p.in_innpc_window:
                            window = {'info':p.window['text'],
                                  'options':[item[0] for item in p.window['options']]}
                            p.to_send.append({'action':'draw_innpc_window',
                                       'window':window})
                    
                        else:
                            window = {'info':p.window['text'],
                                  'owner':p.window['building'].owner.username,
                                  '4th_info':p.window.get('4th_info', ('Error', '4th Info missing!')),
                                  'health':(round(p.window['health'][0]), p.window['health'][1]),
                                  'options':p.window['simp'],
                                  'level':p.window['level'],
                                  'color':p.window['building'].owner.color}

                    
                    
                            p.to_send.append({'action':'draw_window',
                                           'window':window})
                    

                    
                    
                        
                        
                    if not p.in_window and not (p.text == '' and self.fallen):
                        p.to_send.append({'action':'text','text':p.text})

                        

                users = []
                for p in self.players:
                    if not p.pending:
                        users.append({'name':p.username, 'color':p.color, 'num':p.number, 'bs':str(len(p.get_buildings()))})
                self.SendToAll({'action':'num_buildings', 'users':users})
                if not self.fallen:
                    self.SendToAll({'action':'time', 'time':self.getTime()})
        for p in self.players:
            del p.message
            p.to_send.append({'action':'chat', 'messages':p.message})


        for p in self.players:
            p.to_send.append({'action':'flip', 'paused':self.paused})
        self.clock.tick(30)
        
        for p in self.players:
            p.Send({'action':'receive', 'data':p.to_send, 'timestamp':round(time.time())})
            p.to_send = []
        
        
        fps = self.clock.get_fps()


def check_internet(timeout=2):
    try:
        requests.head("http://villagewars.pythonanywhere.com/test_connection", timeout=timeout)
        return True
    except:
        return False





log.basicConfig(level=log.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
log.info('Getting ready...')


if check_internet(5):
    INTERNET = True
    ip = getmyip()
else:
    INTERNET = False
    try:
        ip = getmyip()
    except:
        ip = '127.0.0.1'
    
port = 5555

path = os.path.abspath('../')
regex = re.compile(r'(\d)+\.(\d)+\.(\d)+')
__version__ = regex.search(path).group()
version = __version__

# Change the gamemode to one of these: 'Classic' or 'Express' or 'Extended' or 'OP' or 'Mutated' or 'Immediate'
GAMEMODE = input('Gamemode = ') or 'Classic'
print('Gamemode set to', GAMEMODE)

def eval_gamemode():
    global WALLS_TIME
    if GAMEMODE == 'Classic':
        WALLS_TIME = 10  # In minutes
    elif GAMEMODE == 'Express':
        WALLS_TIME = 3
    elif GAMEMODE == 'Extended':
        WALLS_TIME = 12
    elif GAMEMODE == 'OP':
        WALLS_TIME = 6
    elif GAMEMODE == 'Mutated':
        WALLS_TIME = random.randint(3, 6)
    elif GAMEMODE == 'Immediate':
        WALLS_TIME = 0.1 # Six seconds
eval_gamemode()
hostname = socket.gethostname()
name = input('Please input the server\'s name (leave blank for "%s") : ' % hostname)
if name == '':
    name = hostname


server = MyGameServer(version, host=ip, port=port)
flask_application = 'http://villagewars.pythonanywhere.com/'


if INTERNET:
    res = requests.get(flask_application + 'setserver/' + name + '/' + ip + '/' + GAMEMODE)
    res.raise_for_status()

STARTTIME = time.monotonic()

log.info(('Server "%s" launched with ip ' % name) + ip)

def send_confirmation():
    global INTERNET
    if INTERNET:
        try:
            res = requests.get(flask_application + 'confirm_server/' + name)
            res.raise_for_status()
        except:
            INTERNET = False

"""This is the loop that keeps going until the server is killed"""
while not server.tired:
    server.Update()
    if time.monotonic() - STARTTIME > 100:  # Send Confirmation that the server is running (1 minute, 40 seconds)
        thread = threading.Thread(target=send_confirmation)
        thread.start()
        STARTTIME = time.monotonic()

VillageWarsUpload.py

import toolbox
import os
import zipfile2 as zipfile
import sys
import logging as log
import pymsgbox
import re
import requests

SKIP = []

path = os.path.abspath('../')
regex = re.compile(r'(\d)+\.(\d)+\.(\d)+')
__version__ = regex.search(path).group()
version = __version__

log.basicConfig(level=log.DEBUG, format='%(levelname)s - %(message)s')

def compress_version(skip=None):
    if skip == None:
        skip = []
    log.debug('Starting Compression...')
    global version
    zipFilename = version + '.zip'
    log.debug(f'Creating {zipFilename}...')
    versionZip = zipfile.ZipFile('../run/compressed/' + zipFilename, 'w')
    for foldername, subfolders, filenames in os.walk('../'):
        if 'pycache' in foldername:
            continue
        con = False
        for i in skip:
            if i in foldername:
                con = True
        if con:
            continue
        versionZip.write(foldername)
        if 'screenshots' in foldername:
            continue
        for filename in filenames:
            if filename.endswith('.zip'):
                continue
            if filename.endswith('serverlog.txt'):
                continue
            if os.path.basename(filename) in skip:
                continue
            log.debug(f'Adding {os.path.basename(filename)}...')
            versionZip.write(foldername + '/' + filename)

    log.debug('Final compression...')
    versionZip.close()
    log.debug(f'Successfully compressed {zipFilename}!')
    log.debug('Uploading...')
    url = 'https://villagewars.pythonanywhere.com/upload_version/' + version
    payload={}
    files=[('file',(zipFilename,open('../run/compressed/' + zipFilename,'rb'),'image/zip'))]
    response = requests.post(url, files=files)
    log.debug('Successfully Uploaded to server')
    log.debug('Sending version info...')
    url = 'https://villagewars.pythonanywhere.com/upload_version_info'
    payload={}
    files=[('file',('version_info.json',open('../../version screenshots/version_info.json','r'),'text/json'))]
    response = requests.post(url, files=files)
    log.debug('Successfully uploaded version info')
    response.raise_for_status()
    

print('This will upload the current version the the VillageWars server. Are you sure you want to do this? (y/n)')
a = input('> ').strip()[0].lower()
print()
if a == 'n':
    print('Ok, thanks!')
    print()
    input('Press enter to exit')
assert a == 'y'

compress_version(skip=SKIP)