from copy import deepcopy
import random
from game.common.action import Action
from game.common.avatar import Avatar
from game.common.enums import *
from game.common.player import Player
import game.config as config # this is for turns
from game.utils.thread import CommunicationThread
from game.controllers.movement_controller import MovementController
from game.controllers.controller import Controller
from game.controllers.interact_controller import InteractController
from game.common.map.game_board import GameBoard
from game.config import MAX_NUMBER_OF_ACTIONS_PER_TURN
from game.utils.vector import Vector
[docs]class MasterController(Controller):
"""
`Master Controller Notes:`
Give Client Objects:
Takes a list of Player objects and places each one in the game world.
Game Loop Logic:
Increments the turn count as the game plays (look at the engine to see how it's controlled more).
Interpret Current Turn Data:
This accesses the gameboard in the first turn of the game and generates the game's seed.
Client Turn Arguments:
There are lines of code commented out that create Action Objects instead of using the enum. If your project
needs Actions Objects instead of the enums, comment out the enums and use Objects as necessary.
Turn Logic:
This method executes every movement and interact behavior from every client in the game. This is done by
using every other type of Controller object that was created in the project that needs to be managed
here (InteractController, MovementController, other game-specific controllers, etc.).
Create Turn Log:
This method creates a dictionary that stores the turn, all client objects, and the gameboard's JSON file to
be used as the turn log.
Return Final Results:
This method creates a dictionary that stores a list of the clients' JSON files. This represents the final
results of the game.
"""
def __init__(self):
super().__init__()
self.game_over: bool = False
# self.event_timer = GameStats.event_timer # anything related to events are commented it out until made
# self.event_times: tuple[int, int] | None = None
self.turn: int = 1
self.current_world_data: dict = None
self.movement_controller: MovementController = MovementController()
self.interact_controller: InteractController = InteractController()
# Receives all clients for the purpose of giving them the objects they will control
[docs] def give_clients_objects(self, clients: list[Player], world: dict):
# starting_positions = [[3, 3], [3, 9]] # would be done in generate game
gb: GameBoard = world['game_board']
avatars: list[tuple[Vector, list[Avatar]]] = gb.get_objects(ObjectType.AVATAR)
for avatar, client in zip(avatars, clients):
avatar[1][0].position = avatar[0]
client.avatar = avatar[1][0]
# Generator function. Given a key:value pair where the key is the identifier for the current world and the value is
# the state of the world, returns the key that will give the appropriate world information
[docs] def game_loop_logic(self, start=1):
self.turn = start
# Basic loop from 1 to max turns
while True:
# Wait until the next call to give the number
yield str(self.turn)
# Increment the turn counter by 1
self.turn += 1
# Receives world data from the generated game log and is responsible for interpreting it
[docs] def interpret_current_turn_data(self, clients: list[Player], world: dict, turn):
self.current_world_data = world
if turn == 1:
random.seed(world['game_board'].seed)
# self.event_times = random.randrange(162, 172), random.randrange(329, 339)
# Receive a specific client and send them what they get per turn. Also obfuscates necessary objects.
[docs] def client_turn_arguments(self, client: Player, turn):
# turn_action: Action = Action()
# client.action: Action = turn_action
# ^if you want to use action as an object instead of an enum
turn_actions: list[ActionType] = []
client.actions = turn_actions
# Create deep copies of all objects sent to the player
current_world = deepcopy(self.current_world_data["game_board"]) # what is current world and copy avatar
copy_avatar = deepcopy(client.avatar)
# Obfuscate data in objects that that player should not be able to see
# Currently world data isn't obfuscated at all
args = (self.turn, turn_actions, current_world, copy_avatar)
return args
# Perform the main logic that happens per turn
[docs] def turn_logic(self, clients: list[Player], turn):
for client in clients:
for i in range(MAX_NUMBER_OF_ACTIONS_PER_TURN):
try:
self.movement_controller.handle_actions(client.actions[i], client, self.current_world_data["game_board"])
self.interact_controller.handle_actions(client.actions[i], client, self.current_world_data["game_board"])
except IndexError:
pass
# checks event logic at the end of round
# self.handle_events(clients)
# comment out for now, nothing is in place for event types yet
# def handle_events(self, clients):
# If it is time to run an event, master controller picks an event to run
# if self.turn == self.event_times[0] or self.turn == self.event_times[1]:
# self.current_world_data["game_map"].generate_event(EventType.example, EventType.example)
# event type.example is just a placeholder for now
# Return serialized version of game
[docs] def create_turn_log(self, clients: list[Player], turn: int):
data = dict()
data['tick'] = turn
data['clients'] = [client.to_json() for client in clients]
# Add things that should be thrown into the turn logs here
data['game_board'] = self.current_world_data["game_board"].to_json()
return data
# Gather necessary data together in results file
[docs] def return_final_results(self, clients: list[Player], turn):
data = dict()
data['players'] = list()
# Determine results
for client in clients:
data['players'].append(client.to_json())
return data