예제 #1
0
    def __init__(self):
        super(Main, self).__init__()

        self._world = World(random.randint, self)
        self._world.setN(10)
        # Dodawanie organizmów
        self._world.add_organism(Human((10, 10)))
        org_types = [
            Antelope, Fox, Sheep, Turtle, Wolf, Belladonna, Dandelion, Grass,
            Guarana
        ]
        for org_type in org_types:
            self._init_org(5, org_type, random.randint)
        self._world.add_pending()

        # GUI
        # To okno
        self._menu_bar_h = 20
        x, y, w, h = 300, 50, 870, 600 + self._menu_bar_h
        self.setGeometry(x, y, w, h)
        self.setFixedSize(w, h)
        self.setWindowTitle("Tomasz Stasiak 155197")
        hbox = QtGui.QHBoxLayout(self)
        menu_bar = QtGui.QMenuBar(self)
        menu_bar.setGeometry(0, 0, w, self._menu_bar_h)

        # Akcje w menu
        self._game_start = menu_bar.addAction("Start gry")
        self._next_round = menu_bar.addAction("Następna runda")
        self._next_round.setVisible(False)
        save_game = menu_bar.addAction("Zapisz stan")
        load_game = menu_bar.addAction("Wczytaj stan")

        # Bindowanie kontrolek do akcji
        # Start gry
        QObject.connect(self._game_start, SIGNAL("triggered()"), self,
                        SLOT("start_game()"))
        QObject.connect(self._next_round, SIGNAL("triggered()"),
                        lambda: self._world.nextRound())
        QObject.connect(save_game, SIGNAL("triggered()"), self,
                        SLOT("save_game()"))
        QObject.connect(load_game, SIGNAL("triggered()"), self,
                        SLOT("load_game()"))

        # Insze kontrolki
        # Plansza gry
        self._game_board = GameBoard(self, 0, self._menu_bar_h)

        # Panel informaacyjny
        info_widget = QtGui.QWidget()
        self._info_panel = QtGui.QVBoxLayout(info_widget)
        self._info_panel.setAlignment(Qt.AlignTop)
        info_widget.setLayout(self._info_panel)

        # Dodanie ww. paneli
        hbox.addWidget(self._game_board)
        hbox.addWidget(info_widget)

        self.setLayout(hbox)
예제 #2
0
 def deserialize(self, world_xml):
     # noinspection PyBroadException
     try:
         world_xml = BeautifulSoup(world_xml, features="xml").world
         res_world = World(random.randint)
         res_world.setN(int(world_xml.get('size')))
         org_mapper = MapOrganism()
         for org in world_xml.findAll('organism'):
             tmp = org_mapper.deserialize(org)
             if tmp is not None:
                 res_world.add_organism(tmp)
         return res_world
     except:
         print("XML file corrupted")
         return None
예제 #3
0
 def create_new(self):
     if self.mode == PLAYER:
         create_new_player(self.file)
         self.file = c.PlayerFile()
     elif self.mode == UNIVERSE:
         mkdir(self.file.full_file)
         # Generate a new world
         new = World(c.WorldFile(self.file.name, name="Forest"))
         new.can_delete = False
         generate_world(new)
         new = IdleWorld(c.WorldFile(self.file.name, name="Idle World"))
         generate_world(new)
         del new
         self.file = c.UniverseFolder()
     self.draw_text()
     self.load_files()
예제 #4
0
    def __init__(self):
        super(Main, self).__init__()
        self._world = World(random.randint, self)
        self._world.setN(20)

        # Dodawanie organizmów
        # self._world.add_organism(Human((10, 10)))
        org_types = [Antelope, Fox, Sheep, Turtle, Wolf, Belladonna, Dandelion, Grass, Guarana]
        for org_type in org_types:
            self._init_org(10, org_type, random.randint)
        self._world.add_pending()
예제 #5
0
 def process_events(self, events, mouse, keys):
     if self.click_inventories(mouse):
         game_vars.write_block_data(self.block_pos, self.data)
     else:
         pos = pg.mouse.get_pos()
         if self.rect.collidepoint(
                 *pos) and game_vars.player.use_time <= 0:
             pos = [pos[0] - self.rect.x, pos[1] - self.rect.y]
             # Clicked create
             if self.create_rect.collidepoint(*pos):
                 if not self.invs[self.BIOME].empty and not self.invs[
                         self.SIZE].empty and self.name.valid:
                     for e in events:
                         if e.type == MOUSEBUTTONUP and e.button == BUTTON_LEFT:
                             events.remove(e)
                             new = World(self.name)
                             new.can_delete = True
                             items = [
                                 j.item_id for row in self.invs[
                                     self.BIOME].inv_items for j in row
                                 if j.is_item
                             ]
                             items += [
                                 self.invs[self.SIZE].inv_items[0]
                                 [0].item_id
                             ]
                             items += [i.BONUS_STRUCTURE] * self.invs[
                                 self.STRUCTURE].inv_items[0][0].amnt
                             WorldGenerator.generate_world(
                                 new, modifiers=items)
                             del new
                             from Player.Inventory import new_inventory
                             game_vars.write_block_data(
                                 self.block_pos,
                                 new_inventory(
                                     (1, WorldBuilder.INV_SPOTS)))
                             game_vars.player.set_active_ui(None)
         for e in events:
             if e.type == KEYDOWN:
                 self.name.type_char(e)
                 self.draw_name()
예제 #6
0
    def __init__(self):
        super(Main, self).__init__()

        self._world = World(random.randint, self)
        self._world.setN(20)
        # Dodawanie organizmów
        self._world.add_organism(Human((10, 10)))
        org_types = [Antelope, Fox, Sheep, Turtle, Wolf, Belladonna, Dandelion, Grass, Guarana]
        for org_type in org_types:
            self._init_org(5, org_type, random.randint)
        self._world.add_pending()

        # GUI
        # To okno
        self._menu_bar_h = 20
        x, y, w, h = 300, 50, 870, 600 + self._menu_bar_h
        self.setGeometry(x, y, w, h)
        self.setFixedSize(w, h)
        self.setWindowTitle("Tomasz Stasiak 155197")
        hbox = QtGui.QHBoxLayout(self)
        menu_bar = QtGui.QMenuBar(self)
        menu_bar.setGeometry(0, 0, w, self._menu_bar_h)

        # Akcje w menu
        self._game_start = menu_bar.addAction("Start gry")
        self._next_round = menu_bar.addAction("Następna runda")
        self._next_round.setVisible(False)
        save_game = menu_bar.addAction("Zapisz stan")
        load_game = menu_bar.addAction("Wczytaj stan")

        # Bindowanie kontrolek do akcji
        # Start gry
        QObject.connect(self._game_start, SIGNAL("triggered()"), self, SLOT("start_game()"))
        QObject.connect(self._next_round, SIGNAL("triggered()"), lambda: self._world.nextRound())
        QObject.connect(save_game, SIGNAL("triggered()"), self, SLOT("save_game()"))
        QObject.connect(load_game, SIGNAL("triggered()"), self, SLOT("load_game()"))

        # Insze kontrolki
        # Plansza gry
        self._game_board = GameBoard(self, 0, self._menu_bar_h)

        # Panel informaacyjny
        info_widget = QtGui.QWidget()
        self._info_panel = QtGui.QVBoxLayout(info_widget)
        self._info_panel.setAlignment(Qt.AlignTop)
        info_widget.setLayout(self._info_panel)

        # Dodanie ww. paneli
        hbox.addWidget(self._game_board)
        hbox.addWidget(info_widget)

        self.setLayout(hbox)
예제 #7
0
def main():
    world = World(time=5, FPS=60, size=(100, 100))
    position = np.array([-100, 100])
    qaulity = 1
    rigidBody = RigidBody(position, qaulity)
    rigidBody.forced(qaulity * np.square(np.array([10, 0])) / 2)
    world.add_object(rigidBody)
    world.show()
예제 #8
0
파일: XML.py 프로젝트: drherobrine/worldsim
 def deserialize(self, world_xml):
     # noinspection PyBroadException
     try:
         world_xml = BeautifulSoup(world_xml, features="xml").world
         res_world = World(random.randint)
         res_world.setN(int(world_xml.get('size')))
         org_mapper = MapOrganism()
         for org in world_xml.findAll('organism'):
             tmp = org_mapper.deserialize(org)
             if tmp is not None:
                 res_world.add_organism(tmp)
         return res_world
     except:
         print("XML file corrupted")
         return None
예제 #9
0
'''
Created on Jan 20, 2013

@author: Nathan
'''

from World.World import World

if __name__ == '__main__':
    world = World((2000, 2000))
    world.simulate()
예제 #10
0
class Main(QtGui.QWidget, Controller):
    def __init__(self):
        super(Main, self).__init__()

        self._world = World(random.randint, self)
        self._world.setN(10)
        # Dodawanie organizmów
        self._world.add_organism(Human((10, 10)))
        org_types = [
            Antelope, Fox, Sheep, Turtle, Wolf, Belladonna, Dandelion, Grass,
            Guarana
        ]
        for org_type in org_types:
            self._init_org(5, org_type, random.randint)
        self._world.add_pending()

        # GUI
        # To okno
        self._menu_bar_h = 20
        x, y, w, h = 300, 50, 870, 600 + self._menu_bar_h
        self.setGeometry(x, y, w, h)
        self.setFixedSize(w, h)
        self.setWindowTitle("Tomasz Stasiak 155197")
        hbox = QtGui.QHBoxLayout(self)
        menu_bar = QtGui.QMenuBar(self)
        menu_bar.setGeometry(0, 0, w, self._menu_bar_h)

        # Akcje w menu
        self._game_start = menu_bar.addAction("Start gry")
        self._next_round = menu_bar.addAction("Następna runda")
        self._next_round.setVisible(False)
        save_game = menu_bar.addAction("Zapisz stan")
        load_game = menu_bar.addAction("Wczytaj stan")

        # Bindowanie kontrolek do akcji
        # Start gry
        QObject.connect(self._game_start, SIGNAL("triggered()"), self,
                        SLOT("start_game()"))
        QObject.connect(self._next_round, SIGNAL("triggered()"),
                        lambda: self._world.nextRound())
        QObject.connect(save_game, SIGNAL("triggered()"), self,
                        SLOT("save_game()"))
        QObject.connect(load_game, SIGNAL("triggered()"), self,
                        SLOT("load_game()"))

        # Insze kontrolki
        # Plansza gry
        self._game_board = GameBoard(self, 0, self._menu_bar_h)

        # Panel informaacyjny
        info_widget = QtGui.QWidget()
        self._info_panel = QtGui.QVBoxLayout(info_widget)
        self._info_panel.setAlignment(Qt.AlignTop)
        info_widget.setLayout(self._info_panel)

        # Dodanie ww. paneli
        hbox.addWidget(self._game_board)
        hbox.addWidget(info_widget)

        self.setLayout(hbox)

    # Przycisk startu gry
    def start_game(self):
        self._world.draw_world()
        # Ukrycie tego przycisku
        self._next_round.setVisible(True)
        self._game_start.setVisible(False)

    # Zapis stanu gry
    def save_game(self):
        world_mapper = MapWorld()
        try:
            with open('world.xml', 'w+') as file:
                file.write(world_mapper.serialize(self._world))
        except (OSError, IOError):
            print("Nie można zapisać pliku")

    # Wczytywanie stanu gry
    def load_game(self):
        try:
            with open('world.xml', 'r') as file:
                map_world = MapWorld()
                tmp = map_world.deserialize(file.read())
                if tmp is not None:
                    tmp._controller = self
                    self._world = tmp
                    self.start_game()
        except (OSError, IOError):
            print("Nie można wczytać pliku")

    # Losowa inicjalizacja organizmami
    def _init_org(self, count, org_type, rand):
        for i in range(count):
            x, y = rand(0, self._world.getN()), rand(0, self._world.getN())
            if self._world.is_free((x, y)):
                new_org = org_type((x, y))
                self._world.add_organism(new_org)

    # Metody kontrolera
    def reset_map(self):
        self._game_board._should_redraw = True
        self._game_board._draw_at.clear()

    # Wypisywanie informacji
    def print_info(self, text):
        self._info_panel.addWidget(QtGui.QLabel(text))

    # Czyszczenie informacji
    def clear_info(self):
        for i in reversed(range(self._info_panel.count())):
            self._info_panel.itemAt(i).widget().deleteLater()

    # Wypisanie czegoś
    def print(self, xy, text):
        x, y = xy
        self._game_board.draw_at(x, y, QtGui.QColor(self.map_to_color(text)))
        self._game_board.update()

    # Wczytywanie znaku/komendy etc.
    def read_char(self):
        wnd = HumanMoveWnd(self)
        wnd.exec_()
        return self._res_key

    def show_and_raise(self):
        self.show()
        self.raise_()

    _game_board = GameBoard
    _info_panel = QtGui.QVBoxLayout
    _res_key = 0xDEADC0DE
    _world = World
    field_width = 29
예제 #11
0
 def CreateWorld(self):
     l1 = Location("A")
     l1.addBuilding(Building("B1", l1))
     l2 = Location("B")
     l2.addBuilding(Building("B2", l2))
     l3 = Location("C")
     l3.addBuilding(Building("B3", l3))
     l4 = Location("D")
     l4.addBuilding(Building("B4", l4))
     l5 = Location("E")
     l5.addBuilding(Building("B5", l5))
     l6 = Location("F")
     l6.addBuilding(Building("B6", l6))
     l7 = Location("G")
     l7.addBuilding(Building("B7", l7))
     l8 = Location("H")
     l8.addBuilding(Building("B8", l8))
     l9 = Location("I")
     l9.addBuilding(Building("B9", l9))
     l10 = Location("J")
     l10.addBuilding(Building("B10", l10))
     l11 = Location("K")
     l11.addBuilding(Building("B11", l11))
     l12 = Location("L")
     l12.addBuilding(Building("B12", l12))  
     f1 = Location("M")
     f1.addBuilding(FuelStation("Fuelstation 1", f1))
     f2 = Location("N")
     f2.addBuilding(FuelStation("Fuelstation 2", f2))
 
     'create the roads connecting these locations'
     r1 = Road("R1", 0, 20, 120, 180)
     r2 = Road("R2", 0, 40, 40, 60)    
     r3 = Road("R3", 0, 35, 30, 50)    
     r4 = Road("R4", 0, 20, 60, 80)
     r5 = Road("R5", 15, 18, 30, 45)    
     r6 = Road("R6", 0, 35, 60, 80)
     r7 = Road("R7", 0, 60, 100, 140)    
     r8 = Road("R8", 0, 15, 50, 60)    
     r9 = Road("R9", 10, 50, 50, 60)
     r10 = Road("R10", 0, 53, 100, 140)    
     r11 = Road("R11", 10, 12, 30, 50)
     r12 = Road("R12", 5, 5, 60, 80) 
     r13 = Road("R13", -12, 8, 30, 45)
     r14 = Road("R14", -30, 10, 10, 30)
     r15 = Road("R15", 5, 5, 60, 80)
     r16 = Road("R16", -20, 12, 30, 40)
     r17 = Road("R17", 0, 10, 60, 90)
     r18 = Road("R18", 0, 20, 60, 90)
     r19 = Road("R19", 0, 120, 120, 180)    
 
     'add the roads to the locations, to they know which roads they have and which locations they border'        
     world = World()
     world.addConnection(l1, l2, r1)
     world.addConnection(l1, l3, r2)
     world.addConnection(l1, l4, r3)
     world.addConnection(l2, l3, r4)
     world.addConnection(l2, l9, r5)
     world.addConnection(l3, l4, r6)
     world.addConnection(l3, l10, r7)
     world.addConnection(l4, l5, r8)
     world.addConnection(l5, l6, r9)
     world.addConnection(l5, l11, r10)
     world.addConnection(l6, l7, r11)
     world.addConnection(l6, f2, r12)
     world.addConnection(l7, l8, r13)
     world.addConnection(l8, l11, r14)
     world.addConnection(l8, f2, r15)
     world.addConnection(l9, l10, r16)
     world.addConnection(l10, f1, r17)
     world.addConnection(l11, f1, r18)
     world.addConnection(l11, l12, r19)
        
     return world   
예제 #12
0
class Main(appier.App, Controller):

    def __init__(self):
        super(Main, self).__init__()
        self._world = World(random.randint, self)
        self._world.setN(20)

        # Dodawanie organizmów
        # self._world.add_organism(Human((10, 10)))
        org_types = [Antelope, Fox, Sheep, Turtle, Wolf, Belladonna, Dandelion, Grass, Guarana]
        for org_type in org_types:
            self._init_org(10, org_type, random.randint)
        self._world.add_pending()

    # Losowa inicjalizacja organizmami
    def _init_org(self, count, org_type, rand):
        for i in range(count):
            x, y = rand(0, self._world.getN()), rand(0, self._world.getN())
            if self._world.is_free((x, y)):
                new_org = org_type((x, y))
                self._world.add_organism(new_org)

    # konwersja na tablicę 2d
    @staticmethod
    def _get(orgs, n):
        """
        :type orgs: list[Organism]
        """
        res = [[None] * n for i in range(n)]
        for org in orgs:
            x, y = org.pos()
            res[x][y] = org
        return res

    # Strona domowa
    @appier.route('/', 'GET')
    def home(self):
        return self.template(
            'index.html',
            world_size=range(self._world.getN()),
            organisms=self._get(self._world._organisms, self._world.getN())
        )

    @appier.route('/human-color', 'GET')
    def human_color(self):
        h = Human(0, 0)
        return self.map_to_color(h._SYMBOL)

    @appier.route('/organisms', 'GET')
    def get_organisms(self):
        organisms = []
        for org in self._printed:
            organisms.append(json.dumps(org))
        return json.dumps(organisms)

    @appier.route('/log', 'GET')
    def get_log(self):
        res = json.dumps(self._log)
        self._log.clear()

        return res

    @appier.route('/next-round', 'GET')
    def next_round(self):
        self._world.nextRound()

    # Wyświetlenie na planszy
    def print(self, xy, text):
        x, y = xy
        self._printed.append((x, y, self.map_to_color(text)))

    # Czyszczenie informacji dodatkowych
    def clear_info(self):
        pass

    # Wypisywanie informacji
    def print_info(self, text):
        self._log.append(text)

    # Wczytywanie znaku (ruchu) człowieka
    def read_char(self):
        pass

    # Resetowanie mapki -> nieużywane
    def reset_map(self):
        pass

    _printed = []
    _world = World
    _log = []
예제 #13
0
class Main(QtGui.QWidget, Controller):
    def __init__(self):
        super(Main, self).__init__()

        self._world = World(random.randint, self)
        self._world.setN(20)
        # Dodawanie organizmów
        self._world.add_organism(Human((10, 10)))
        org_types = [Antelope, Fox, Sheep, Turtle, Wolf, Belladonna, Dandelion, Grass, Guarana]
        for org_type in org_types:
            self._init_org(5, org_type, random.randint)
        self._world.add_pending()

        # GUI
        # To okno
        self._menu_bar_h = 20
        x, y, w, h = 300, 50, 870, 600 + self._menu_bar_h
        self.setGeometry(x, y, w, h)
        self.setFixedSize(w, h)
        self.setWindowTitle("Tomasz Stasiak 155197")
        hbox = QtGui.QHBoxLayout(self)
        menu_bar = QtGui.QMenuBar(self)
        menu_bar.setGeometry(0, 0, w, self._menu_bar_h)

        # Akcje w menu
        self._game_start = menu_bar.addAction("Start gry")
        self._next_round = menu_bar.addAction("Następna runda")
        self._next_round.setVisible(False)
        save_game = menu_bar.addAction("Zapisz stan")
        load_game = menu_bar.addAction("Wczytaj stan")

        # Bindowanie kontrolek do akcji
        # Start gry
        QObject.connect(self._game_start, SIGNAL("triggered()"), self, SLOT("start_game()"))
        QObject.connect(self._next_round, SIGNAL("triggered()"), lambda: self._world.nextRound())
        QObject.connect(save_game, SIGNAL("triggered()"), self, SLOT("save_game()"))
        QObject.connect(load_game, SIGNAL("triggered()"), self, SLOT("load_game()"))

        # Insze kontrolki
        # Plansza gry
        self._game_board = GameBoard(self, 0, self._menu_bar_h)

        # Panel informaacyjny
        info_widget = QtGui.QWidget()
        self._info_panel = QtGui.QVBoxLayout(info_widget)
        self._info_panel.setAlignment(Qt.AlignTop)
        info_widget.setLayout(self._info_panel)

        # Dodanie ww. paneli
        hbox.addWidget(self._game_board)
        hbox.addWidget(info_widget)

        self.setLayout(hbox)

    # Przycisk startu gry
    def start_game(self):
        self._world.draw_world()
        # Ukrycie tego przycisku
        self._next_round.setVisible(True)
        self._game_start.setVisible(False)

    # Zapis stanu gry
    def save_game(self):
        world_mapper = MapWorld()
        try:
            with open('world.xml', 'w+') as file:
                file.write(world_mapper.serialize(self._world))
        except (OSError, IOError):
            print("Nie można zapisać pliku")

    # Wczytywanie stanu gry
    def load_game(self):
        try:
            with open('world.xml', 'r') as file:
                map_world = MapWorld()
                tmp = map_world.deserialize(file.read())
                if tmp is not None:
                    tmp._controller = self
                    self._world = tmp
                    self.start_game()
        except (OSError, IOError):
            print("Nie można wczytać pliku")

    # Losowa inicjalizacja organizmami
    def _init_org(self, count, org_type, rand):
        for i in range(count):
            x, y = rand(0, self._world.getN()), rand(0, self._world.getN())
            if self._world.is_free((x, y)):
                new_org = org_type((x, y))
                self._world.add_organism(new_org)

    # Metody kontrolera
    def reset_map(self):
        self._game_board._should_redraw = True
        self._game_board._draw_at.clear()

    # Wypisywanie informacji
    def print_info(self, text):
        self._info_panel.addWidget(QtGui.QLabel(text))

    # Czyszczenie informacji
    def clear_info(self):
        for i in reversed(range(self._info_panel.count())):
            self._info_panel.itemAt(i).widget().deleteLater()

    # Wypisanie czegoś
    def print(self, xy, text):
        x, y = xy
        self._game_board.draw_at(x, y, QtGui.QColor(self.map_to_color(text)))
        self._game_board.update()

    # Wczytywanie znaku/komendy etc.
    def read_char(self):
        wnd = HumanMoveWnd(self)
        wnd.exec_()
        return self._res_key

    def show_and_raise(self):
        self.show()
        self.raise_()

    _game_board = GameBoard
    _info_panel = QtGui.QVBoxLayout
    _res_key = 0xDEADC0DE
    _world = World
    field_width = 29
예제 #14
0
    def __init__(self,
                 window_surface,
                 is_server,
                 multi,
                 image_loader: ImageLoader,
                 sound_loader: SoundLoader,
                 map_loader: MapLoader,
                 start_map: Map = None,
                 connect_to_ip: str = None,
                 server_ip: str = None,
                 client_ip: str = None,
                 client_port: int = None,
                 dedicated: bool = False,
                 client_name: str = None) -> None:
        """
        Если multi = False, значит никакой работы с сервером и клиентом проводиться не будет.
        Елси multi = True:
            Если is_server = True, значит будет запущен сервер.
            Если is_server = False, значит будет запущен клиент.
        """
        self.window_surface = window_surface

        minimal_dimention = min(
            self.window_surface.get_width(),
            self.window_surface.get_height())  # Наименьшая сторона окна
        self.game_surface = pygame.Surface(
            (minimal_dimention, minimal_dimention))

        self.any_popup_box_lock = threading.Lock()
        self.main_cycle_lock = threading.Lock()

        # Выравнивание по центру:
        self.game_rect = pygame.Rect(
            self.window_surface.get_width() / 2 - minimal_dimention / 2,
            self.window_surface.get_height() / 2 - minimal_dimention / 2,
            minimal_dimention, minimal_dimention)

        self.clock = pygame.time.Clock()
        self.image_loader = image_loader
        self.sound_loader = sound_loader
        self.map_loader = map_loader

        self.chat_history = ChatHistory()

        self.game_running = True

        self.is_server = is_server
        self.multi = multi
        self.is_dedicated = dedicated

        self.world = World(self, self.game_surface, self.image_loader, True)

        if start_map is not None:
            start_map_id = start_map.map_id
        else:
            start_map_id = self.map_loader.get_map_id_by_name(
                START_MAP_NAME)  # Стартовый ID карты

        if self.is_server and multi:
            # Запуск сервера для мультиплеера
            if not self.is_dedicated:
                self.gui = GUI(self)
            self.server_ip = server_ip
            self.world.set_ready_for_server()
            self.serverside_sender = DataSenderServerSide(self)
            self.create_serverside_server()
            self.world.load_world_map_by_map_id(start_map_id)
            self.world.clear_changes()
            self.server_waiting_started = False  # Флаг, который принимает значение True после первой
            # попытки ожидания клиентов.
            self.set_default_buttons(is_server=True)
        elif not self.is_server and self.multi:
            # Запуск клиента для мультиплеера
            self.gui = GUI(self)
            self.clientside_sender = DataSenderClientSide(self)
            self.client_ip = client_ip
            self.client_port = client_port
            self.create_clientside_server()
            self.connect_to_ip = connect_to_ip
            self.is_connected = False
            self.game_started = False
            self.has_already_tried_to_connect = False
            self.server_button_pressed = False  # Флаг для однократной отработки нажатия кнопки подключения
            self.set_default_buttons(is_server=False)
            if client_name is not None:
                self.clientside_sender.player_name = client_name
        elif not multi:
            # Запуск одиночки
            self.gui = GUI(self)
            self.world.load_world_map_by_map_id(start_map_id)
            self.world.spawn_player()
            self.world.players[0].add_color(PLAYER_TANKS_COLORS[0])
            self.world.center_camera_on_player()
            self.game_started = True  # TODO: Временно
            self.set_default_buttons(is_server=True)

        self.main_cycle()  # Основной цикл
예제 #15
0
class Game:
    clientside_sender: DataSenderClientSide = None  # Отправители пакетов
    serverside_sender: DataSenderServerSide = None
    clientside_server: socketserver.UDPServer = None
    serverside_server: socketserver.UDPServer = None

    window_surface: Surface = None  # Основная поверхность

    image_loader: ImageLoader = None  # Загрузчик изображений
    sound_loader: SoundLoader = None  # Загрузчик звуков
    map_loader: MapLoader = None  # Загрузчик карт

    world: World = None  # Мир

    main_cycle_lock: threading.Lock()

    is_dedicated: bool = None  # Является ли выделенным сервером?
    is_server: bool = None
    is_connected: bool = None  # Подключён ли клиент к серверу
    multi: bool = None  # Сетевой режим или нет?
    game_started: bool = None  # Флаг начала игры
    game_running: bool = None  # Флаг запущенной игры

    connect_to_ip: str = None  # Адрес, к которому будет пытаться подключиться клиент
    client_ip: str = None  # Адрес, на котором расположен клиент
    # TODO: подумать, куда засунуть эту переменную V V V
    client_world_object_id: int = None  # ID объекта, которым управляет данный клиент
    client_port: int = None  # Порт клиента
    server_ip: str = None  # Адрес, на котором расположен сервер

    any_popup_box: PopupBox = None  # Любой PopupBox должен быть здесь
    any_popup_box_lock: threading.Lock = None  # Lock для критической секции
    gui: GUI = None  # Весь GUI игры находится здесь.
    need_to_return_to_menu: bool = False  # Установка этого флага позволяет вернуться в меню после завершения игры

    button_actions: Dict[
        str,
        tuple] = None  # Словарь "кнопка - дейсвтие", который используется в process_inputs

    chat_history: ChatHistory = None  # История чата

    need_to_act: bool = True  # Флаг необходимости обновлять юниты TODO: попытаться избавиться от этого?
    need_to_quit: bool = False  # Флаг необходимости выхода из игры TODO: попытаться избавиться от этого?
    # Используется в GUI v v v
    need_to_process_inputs: bool = True  # Флаг необходимости считывать управление TODO: попытаться избавиться от этого?

    def __init__(self,
                 window_surface,
                 is_server,
                 multi,
                 image_loader: ImageLoader,
                 sound_loader: SoundLoader,
                 map_loader: MapLoader,
                 start_map: Map = None,
                 connect_to_ip: str = None,
                 server_ip: str = None,
                 client_ip: str = None,
                 client_port: int = None,
                 dedicated: bool = False,
                 client_name: str = None) -> None:
        """
        Если multi = False, значит никакой работы с сервером и клиентом проводиться не будет.
        Елси multi = True:
            Если is_server = True, значит будет запущен сервер.
            Если is_server = False, значит будет запущен клиент.
        """
        self.window_surface = window_surface

        minimal_dimention = min(
            self.window_surface.get_width(),
            self.window_surface.get_height())  # Наименьшая сторона окна
        self.game_surface = pygame.Surface(
            (minimal_dimention, minimal_dimention))

        self.any_popup_box_lock = threading.Lock()
        self.main_cycle_lock = threading.Lock()

        # Выравнивание по центру:
        self.game_rect = pygame.Rect(
            self.window_surface.get_width() / 2 - minimal_dimention / 2,
            self.window_surface.get_height() / 2 - minimal_dimention / 2,
            minimal_dimention, minimal_dimention)

        self.clock = pygame.time.Clock()
        self.image_loader = image_loader
        self.sound_loader = sound_loader
        self.map_loader = map_loader

        self.chat_history = ChatHistory()

        self.game_running = True

        self.is_server = is_server
        self.multi = multi
        self.is_dedicated = dedicated

        self.world = World(self, self.game_surface, self.image_loader, True)

        if start_map is not None:
            start_map_id = start_map.map_id
        else:
            start_map_id = self.map_loader.get_map_id_by_name(
                START_MAP_NAME)  # Стартовый ID карты

        if self.is_server and multi:
            # Запуск сервера для мультиплеера
            if not self.is_dedicated:
                self.gui = GUI(self)
            self.server_ip = server_ip
            self.world.set_ready_for_server()
            self.serverside_sender = DataSenderServerSide(self)
            self.create_serverside_server()
            self.world.load_world_map_by_map_id(start_map_id)
            self.world.clear_changes()
            self.server_waiting_started = False  # Флаг, который принимает значение True после первой
            # попытки ожидания клиентов.
            self.set_default_buttons(is_server=True)
        elif not self.is_server and self.multi:
            # Запуск клиента для мультиплеера
            self.gui = GUI(self)
            self.clientside_sender = DataSenderClientSide(self)
            self.client_ip = client_ip
            self.client_port = client_port
            self.create_clientside_server()
            self.connect_to_ip = connect_to_ip
            self.is_connected = False
            self.game_started = False
            self.has_already_tried_to_connect = False
            self.server_button_pressed = False  # Флаг для однократной отработки нажатия кнопки подключения
            self.set_default_buttons(is_server=False)
            if client_name is not None:
                self.clientside_sender.player_name = client_name
        elif not multi:
            # Запуск одиночки
            self.gui = GUI(self)
            self.world.load_world_map_by_map_id(start_map_id)
            self.world.spawn_player()
            self.world.players[0].add_color(PLAYER_TANKS_COLORS[0])
            self.world.center_camera_on_player()
            self.game_started = True  # TODO: Временно
            self.set_default_buttons(is_server=True)

        self.main_cycle()  # Основной цикл

    def send_changes_and_clear(self) -> None:
        self.serverside_sender.send_changes()
        self.world.clear_changes()

    def wait_for_players(self, num_of_player_to_start: int = 2) -> None:
        """
        Реализует цикл ожидания игроков. Пока не наберётся num_of_player_to_start игроков, игра не начнётся.
        :param num_of_player_to_start: Число игроков для начала.
        """
        def check_players_ready():
            for client in self.serverside_sender.clients:
                if not client.ready:
                    # Если хотя бы один клиент не готов
                    return False
            return True

        if self.serverside_sender.clients.__len__() < num_of_player_to_start:
            return
        if not check_players_ready():
            # Пока игроки не будут готовы, ждём.
            return
        # Как только к нам подключилост достаточное количество игроков, спавним их и центруем камеру
        for (i, player) in enumerate(self.serverside_sender.clients):
            self.world.spawn_player(i)

        self.send_changes_and_clear(
        )  # Отправляем только сообщение о создании танков игроков

        # Отправка клиентам ID их танков, чтобы они могли сами центрировать камеры
        id_players_ip_combo: dict = dict()
        for (i, client) in enumerate(self.serverside_sender.clients):
            id_players_ip_combo[client.ip_port_combo] = self.world.players[
                client.player_id].world_id
        self.serverside_sender.send_event(EVENT_SERVER_SEND_PLAYERS_TANKS_IDS,
                                          id_players_ip_combo)

        self.world.center_camera_on_player()

        self.serverside_sender.send_event(EVENT_SERVER_GAME_STARTED)

        remove_server_started_popupbox(self)
        self.game_started = True

    def create_clientside_server(self) -> None:
        """
        Запускает сервер на стороне клиента.
        """
        class MyUDPHandlerClientSideWithObject(MyUDPHandlerClientSide
                                               ):  # Костыль(?)
            parent_game = self  # Передаю ссылку на объект
            port = self.client_port

        HOST, PORT = self.client_ip, self.client_port
        # Созадём севрер
        self.clientside_server = socketserver.UDPServer(
            (HOST, PORT), MyUDPHandlerClientSideWithObject)
        server_thread = threading.Thread(
            target=self.clientside_server.serve_forever)  # Создаём поток
        server_thread.setDaemon(True)
        server_thread.start()  # Запускаем поток

        print("Clientside server was started")

    def create_serverside_server(self):
        """
        Запускает сервер на стороне сервера.
        """
        class MyUDPHandlerServerSideWithObject(MyUDPHandlerServerSide
                                               ):  # Костыль(?)
            parent_game = self  # Передаю ссылку на объект

        HOST, PORT = self.server_ip, 9998
        self.serverside_server = socketserver.UDPServer(
            (HOST, PORT), MyUDPHandlerServerSideWithObject)  # Созадём севрер
        server_thread = threading.Thread(
            target=self.serverside_server.serve_forever)  # Создаём поток
        server_thread.setDaemon(True)
        server_thread.start()  # Запускаем поток

        print("Serverside server was started")

    def set_default_buttons(self, is_server: bool) -> None:
        """
        Привязывает клавиши к event-ам, заполняя словарь.
        :param is_server: Является ли текущий Game сервером.
        """
        self.button_actions = dict()
        if is_server:
            # Если задаём для сервера или одиночки
            self.button_actions[MOVE_RIGHT] = (
                (lambda: self.button_move_player("RIGHT", False)),
                (lambda: self.reset_move_player_direction("RIGHT", False)))
            self.button_actions[MOVE_LEFT] = (
                (lambda: self.button_move_player("LEFT", False)),
                (lambda: self.reset_move_player_direction("LEFT", False)))
            self.button_actions[MOVE_UP] = (
                (lambda: self.button_move_player("UP", False)),
                (lambda: self.reset_move_player_direction("UP", False)))
            self.button_actions[MOVE_DOWN] = (
                (lambda: self.button_move_player("DOWN", False)),
                (lambda: self.reset_move_player_direction("DOWN", False)))
            self.button_actions[SHOOT] = ((
                lambda: self.world.create_bullet(self.world.players[0])), None)
        else:
            # TODO: Избавиться от этой переменной и класса.
            class MoveVar:
                direction: str = None

            move_var = MoveVar(
            )  # Переменная для хранения текущего направления движения танка

            # Если задаём для клиента
            self.button_actions[MOVE_RIGHT] = (
                (lambda: self.button_move_player(
                    "RIGHT", True, local_var=move_var)),
                (lambda: self.reset_move_player_direction(
                    "RIGHT", True, local_var=move_var)))
            self.button_actions[MOVE_LEFT] = ((lambda: self.button_move_player(
                "LEFT", True, local_var=move_var)), (
                    lambda: self.reset_move_player_direction(
                        "LEFT", True, local_var=move_var)))
            self.button_actions[MOVE_UP] = ((
                lambda: self.button_move_player("UP", True, local_var=move_var)
            ), (lambda: self.reset_move_player_direction(
                "UP", True, local_var=move_var)))
            self.button_actions[MOVE_DOWN] = ((lambda: self.button_move_player(
                "DOWN", True, local_var=move_var)), (
                    lambda: self.reset_move_player_direction(
                        "DOWN", True, local_var=move_var)))
            self.button_actions[SHOOT] = ((
                lambda: self.clientside_sender.send_button("SHOOT")), None)
            self.button_actions[CHAT_BUTTON] = (self.gui.show_chat_sender,
                                                None)
            self.button_actions[FOLD_UNFOLD_CHATLOG] = (
                self.gui.change_chatlog_action, self.gui.reset_button)

    def process_inputs(self) -> None:
        """
        Обрабатывает все нажатые клавиши, выполняя необходимые действия.
        """
        # Кнопка в self.button_actions должна храниться так:
        # "код клавиши из PyGame": ("действие при нажатой", "действие при ненажатой")
        # Любое из действий может быть None!
        # Действие "NONE" может иметь только одно действие "при ненажатой".

        if self.any_popup_box is not None and self.any_popup_box.blocking:
            # Если есть блокирующий popup_box, ничего не делаем.
            return

        keyboard_pressed = pygame.key.get_pressed()
        any_pressed = False  # Была ли нажата хоть одна клавиша

        for button in self.button_actions:
            if keyboard_pressed[
                    button]:  # Если указанная клавиша нажата, выполняется действие при нажатии
                if self.button_actions[button][0] is not None:
                    self.button_actions[button][0]()
            else:  # Если указанная клавиша не нажата, выполняется действие при ненажатой
                if self.button_actions[button][1] is not None:
                    self.button_actions[button][1]()

        if not any_pressed:
            if "NONE" in self.button_actions and self.button_actions["NONE"][
                    1] is not None:
                self.button_actions["NONE"][1](
                )  # Действие при отсутствии нажатий

    def button_move_player(self,
                           direction,
                           remote,
                           player_id=0,
                           local_var=None) -> None:
        """
        Вызывается, когда нажата кнопка движения.
        Либо посылает на сервер указание, что нажали определённую кнопку движения, либо двигает 0-ого игрока
        (в случае сервера или одиночки).
        :param local_var: Переменная, необходимая в случае remote=True,
         туда записывается последнее направление движения.
        :param player_id: Айди игрока, для которого это выполняется.
        :param direction: Направление движения
        :param remote: Если True - нужно отправлять данные на сервер, если False - локальные изменения.
        """
        if remote:
            # Если мы отправляем движение на сервер
            if local_var.direction is None or local_var.direction == direction:
                self.clientside_sender.send_button("MOVE_" + direction)

                # Смотри класс MoveVar в методе set_default_buttons:
                local_var.direction = direction
        else:
            current_player = self.world.players[player_id]
            # Если мы сами себе сервер
            if current_player.last_pressed_direction is None or current_player.last_pressed_direction == direction:
                self.world.move_player_to(player_id, direction)
                current_player.last_pressed_direction = direction

    def reset_move_player_direction(self,
                                    direction,
                                    remote,
                                    player_id=0,
                                    local_var=None) -> None:
        """
        Сбрасывает кнопку движения для игрока в локальной игре.
        :param local_var: Эта переменная используется при remote=True. Содержит текущее направление движения.
        :param remote: Если True - нужно применять доп.логику для сохранения состояния,
         если False - локальные изменения.
        :param player_id: Айди игрока, для которого это выполняется.
        :param direction: Направление, которое нужно сбросить.
        """
        if remote:
            if local_var.direction == direction:
                local_var.direction = None
        else:
            if self.world.players[
                    player_id].last_pressed_direction == direction:
                self.world.players[player_id].last_pressed_direction = None

    def send_chat_message(self, msg_str: str) -> None:
        """
        Отправляет сообщение со стороны клиента.
        :param msg_str: Сообщение для отправки.
        """
        self.clientside_sender.send_event(EVENT_CLIENT_SEND_CHAT_MESSAGE,
                                          msg_str)

    def main_cycle(self) -> None:
        """
        Главный цикл - здесь происходит отрисовка объектов, обработка событий и все проверки.
        """
        while self.game_running:
            with self.main_cycle_lock:
                self.clock.tick(
                    targetFPS)  # Требуемый FPS и соответствующая задержка
                self.window_surface.fill(DARK_GREY)
                self.game_surface.fill(BLACK)

                # Обработка событий:
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        self.stop_game()
                    # Обработка всплывающих окон:
                    with self.any_popup_box_lock:
                        if self.any_popup_box is not None:
                            self.any_popup_box.handle_event(event)
                    if self.gui is not None:
                        self.gui.handle_event(event)
                        if self.gui.blocking:
                            self.need_to_process_inputs = False
                        else:
                            self.need_to_process_inputs = True
                if self.need_to_quit:
                    self.stop_game()

                if self.is_server and not self.game_started:
                    if not self.server_waiting_started:
                        add_server_started_popupbox(self)
                        self.server_waiting_started = True
                    # Пока сервер не стартанул
                    self.wait_for_players(
                        int(self.world.world_map.properties["max_players"]))

                if self.game_started:
                    if self.need_to_process_inputs:
                        self.process_inputs()

                    # TODO: подумать, куда засунуть это V V V
                    if self.multi and not self.is_server:
                        # Центируемся на своём танке
                        try:
                            self.world.camera.smart_center_on(
                                self.world.objects_id_dict[
                                    self.client_world_object_id])
                        except KeyError:
                            # Не можем сцентрироваться - видимо, танка уже нет.
                            pass

                # Попытка подключиться к серверу при запуске клиента:
                if not self.is_server and not self.is_connected and self.multi and not self.has_already_tried_to_connect:
                    self.clientside_sender.ask_for_ok(self.connect_to_ip)
                    self.has_already_tried_to_connect = True

                # if self.game_started and (not self.multi or not self.is_server or not self.is_dedicated):
                if self.game_started:
                    # Отрисовка происходит только, когда игра началась И
                    #  либо игра одиночная
                    #  либо игра - клиент
                    #  либо игра - не выделенный сервер
                    self.world.draw()

                if self.game_started and (self.is_server or
                                          not self.multi) and self.need_to_act:
                    self.world.act()
                    # Проверка на конец игры:
                    game_over_dict = self.world.check_game_over()
                    if game_over_dict is not None:
                        # Проверка на конец игры здесь.
                        self.game_over(game_over_dict)
                    # Спавн врагов:
                    if self.world.enemy_spawn_timer.is_ready(
                    ) and self.world.enemies_remains > 0:
                        if self.world.create_enemy():
                            # Если получилось заспавнить врага
                            self.world.enemies_remains -= 1
                            self.world.enemy_spawn_timer.reset()
                    # Если уровень завершён.
                    if self.world.check_level_over():
                        if "next_map" in self.world.world_map.properties:
                            next_map_name = self.world.world_map.properties[
                                "next_map"]
                            next_map_id = self.map_loader.get_map_id_by_name(
                                next_map_name)
                            self.world.reload_map(next_map_id)
                            self.world.clear_changes(
                            )  # На всякий случай очищаем изменения.
                        else:
                            # Если нет следующего уровня, выводим экран победы
                            if self.is_server and self.multi:
                                # ...либо клиентам.
                                self.serverside_sender.send_event(
                                    EVENT_GAME_WIN)
                                self.stop_game(send_to_server=False)
                            else:
                                # ...либо себе.
                                add_you_win_popupbox(self)
                                self.game_started = False

                    # Изменения в мире:
                    if (changes := self.world.get_changes()
                        ).__len__() > 0 and self.multi:
                        if CHANGES_DEBUG:
                            print(changes)
                        self.serverside_sender.send_changes()
                    self.world.clear_changes()

                self.window_surface.blit(self.game_surface, self.game_rect)

                if self.game_started and self.gui is not None:
                    self.gui.draw()
                    self.gui.update()

                # Отрисовка и обновление popupBox-а:
                with self.any_popup_box_lock:
                    if self.any_popup_box is not None:
                        self.any_popup_box.draw()
                        self.any_popup_box.update()

                pygame.display.update()