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)
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
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()
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()
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()
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)
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()
''' Created on Jan 20, 2013 @author: Nathan ''' from World.World import World if __name__ == '__main__': world = World((2000, 2000)) world.simulate()
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
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
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 = []
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
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() # Основной цикл
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()