def getPlayerUpdate(playerName: str, game: Dungeon): """ PlayerUpdateType is: { "type": "player-update", "layout": (tile-layout), # getLayout? "position": (point), # getPlayer.location "objects": (object-list), # getLocationsAround.map(if destHasItem || desthasKey -> return object) [ { "type": "key", "position": [ 4, 2 ] }, { "type": "exit", "position": [ 7, 17 ] } ] "actors": (actor-position-list) # potentially getLocationsAround.map(if destHasEnemy -> return enemy) } 1. Finds 25 points: surrounding 24 of given point 2.Layput based on the 25 points 3. Will check if enemies exist on any of those 25 4. Will check if key or exit on any of those 25 """ player: Player = getPlayer(game.levels[game.currLevel], playerName) possMoves = getFieldOfView(player, game.levels[game.currLevel]) output = [ playerName, { "type": "player-update", "layout": getTileLayout(game, player), "position": [player.location[0], player.location[1]], "objects": getObjectsAroundPlayer(possMoves, game), "actors": getEnemiesAroundPlayer(possMoves, game) } ] return output
def moveEntity(level: Level, name: str, currBoard: int, newBoardNum: int, destination: tuple, isPlayer: bool): """ Move the Player/Enemy with the given name to the given destination in the level. :param level: Level :param name: str :param currBoard: int :param newBoardNum: int :param destination: tuple :param isPlayer: bool """ board = level.boards[currBoard] entity = getPlayer(level, name) if isPlayer else getEnemy(level, name) destInBoard = locationInBounds(destination, board.origin, board.dimensions) if destInBoard: entity.location = destination else: boardEntities = board.players if isPlayer else board.enemies del boardEntities[name] if newBoardNum != -1: otherBoard = level.boards[newBoardNum] entity.location = destination if isPlayer: otherBoard.players[name] = entity else: otherBoard.enemies[name] = entity level.currBoard = newBoardNum return level
def move(entityName: str, destination, game: Dungeon, isPlayer=True): """ Updates the game to reflect the movement of a player if they can move to the given destination in the game or skip a turn when destination is None. Returns an unmodified game if the player cannot move based on game rules. :param entityName: str :param destination: tuple | None :param game: Dungeon """ currLevel: Level = game.levels[game.currLevel] # Player move is skipped if destination is None: # only increment player turn updatedPlayerTurn = currLevel.playerTurn + 1 if updatedPlayerTurn == len(game.players) - 1: currLevel.playerTurn = 0 else: currLevel.playerTurn = updatedPlayerTurn return game entity = getPlayer(currLevel, entityName) if isPlayer else getEnemy( currLevel, entityName) currBoardNum = whichBoardInLevel(currLevel, entity.location) newBoardNum = whichBoardInLevel(currLevel, destination) numMoves = 2 if isPlayer and playerCanMoveTo(destination, entity, currLevel, numMoves): updatedLevel = moveEntity(currLevel, entityName, currBoardNum, newBoardNum, destination, isPlayer=True) if updatedLevel.playerTurn == len(game.players) - 1: updatedLevel.playerTurn = 0 updatedLevel.enemyTurn = updatedLevel.enemyTurn + 1 else: updatedLevel.playerTurn = updatedLevel.playerTurn + 1 updatedLevel.enemyTurn = updatedLevel.enemyTurn + 1 game.levels[game.currLevel] = updatedLevel updatedGame = interact(entityName, destination, game) return updatedGame elif not isPlayer: updatedLevel = moveEntity(currLevel, entityName, currBoardNum, newBoardNum, destination, isPlayer=False) game.levels[game.currLevel] = updatedLevel updatedGame = enemyInteract(entityName, destination, game) return updatedGame else: return game
def getFieldOfView(playerName: str, level: Level): player: Player = getPlayer(level, playerName) pLocRow, pLocCol = player.location origin = (pLocRow - 2, pLocCol - 2) fieldOfView = [] for r in range(5): for c in range(5): relLoc = (origin[0] + r, origin[1] + c) if locationInLevelBounds(level, relLoc): fieldOfView.append(relLoc) return fieldOfView
def executeTurns(game: Dungeon, maxNumTurns: int, jsonActorMoveListList: list, jsonNameList: list, jsonLevel: dict): # Send initial player updates to all players in game managerTrace = getPlayersUpdates(game, []) i = 0 while i < maxNumTurns: # 0. Exit checking if anyOutOfMoves(jsonActorMoveListList) or isCurrentLevelOver(game): break # 1. Run all moves in turn for j in range(len(jsonActorMoveListList) ): # execute each move for a player in a turn playerMoveList = jsonActorMoveListList[j] validMove = False while not validMove and len(playerMoveList) > 0: # do first move in playerMoveList, if valid, set flag move = playerMoveList[0] playerName = jsonNameList[j] if not isPlayerInGame(playerName, game): break currLevel: Level = game.levels[game.currLevel] player = getPlayer(currLevel, playerName) # pot bug?? dest = move["to"] # apply if not dest: # skipped turn validMove = True managerTrace.append([playerName, move, "OK"]) # pot bug adding?? managerTrace = getPlayersUpdates(game, managerTrace) elif playerCanMoveTo(intifyTuple(dest), player, currLevel, 2): GameManager.move(playerName, intifyTuple(dest), game) # pot bug not mutating?? managerTrace.append( getMoveStatus(currLevel, playerName, move)) # pot bug wrong return types?? managerTrace = getPlayersUpdates(game, managerTrace) validMove = True else: managerTrace.append([playerName, move, "Invalid"]) validMove = False del playerMoveList[0] i += 1 return managerTrace, game
def getPlayersInRoomAndLevel(boardNum: int, game: Dungeon): """ Returns a tuple of a list of the players alive at the given board number in the current level and a list of the players alive in the current level. :params boardNum: int :params game: Dungeon """ board: Board = game.levels[game.currLevel].boards[boardNum] playersInOurRoom = [] playersInLevel = [] for playerName in game.players: player = getPlayer(game.levels[game.currLevel], playerName) if player: playersInLevel.append(player) if player.location[0] in board.tiles.keys(): if player.location[1] in board.tiles[player.location[0]].keys(): playersInOurRoom.append(player) return playersInOurRoom, playersInLevel
def getFinalState(game: Dungeon, jsonLevel: dict, jsonEnemies: list): players = [] for playerName in game.players: if isPlayerInGame(playerName, game): player: Player = getPlayer(game.levels[game.currLevel], playerName) newPlayer = { "type": "player", "name": playerName, "position": player.location } players.append(newPlayer) exitLocked = not game.levels[game.currLevel].exitUnlocked return { "type": "state", "level": jsonLevel, "players": players, "adversaries": jsonEnemies, "exit-locked": exitLocked }
def main(): DEFAULT_LEVEL = './tests/snarl.levels' numPlayers = 1 startLevel = 1 jsonLevels = [] isObserving = False numLevels = 0 parser = argparse.ArgumentParser() # initialize # this is how you add an optional argument parser.add_argument("--levels", help="Enter the name of an input JSON Level file", nargs='?', const=DEFAULT_LEVEL, type=str) parser.add_argument("--players", help="Enter an amount of players 1-4", nargs='?', const=numPlayers, type=int) parser.add_argument("--start", nargs='?', help="Enter the number of the level to start the game from", const=startLevel, type=int) parser.add_argument("--observe", help="Observe the game", action="store_true") # this is called after you define your optional args args = parser.parse_args() # Parse level if args.levels: log('got levels flag', args.levels) with open(args.levels) as file: wholeFile = file.read() portions = wholeFile.split('\n\n') cleaned = list(filter(lambda port: port != '', portions)) numLevels = cleaned[0] jsonLevels = cleaned[1:] else: log("using default level") with open(DEFAULT_LEVEL) as file: wholeFile = file.read() portions = wholeFile.split('\n\n') cleaned = list(filter(lambda port: port != '', portions)) numLevels = cleaned[0] jsonLevels = cleaned[1:] if args.players: if 1 <= args.players <= 4: log('got players flag', str(args.players)) numPlayers = args.players else: print("Players must be from 1-4") sys.exit(1) if args.start and 0 < args.start <= len(jsonLevels): log('got start flag', args.start) startLevel = args.start if args.observe: log('got observe') isObserving = True try: rawJsonGameLevels = jsonLevels[(startLevel - 1):] jsonGameLevels = [json.loads(rawJsonLevel) for rawJsonLevel in rawJsonGameLevels] log("got+converted {} levels".format(len(jsonGameLevels))) # this has all the levels needed with the starting level as the first # element in the list levels = [convertJsonLevel(jsonLevel["rooms"], jsonLevel["hallways"], jsonLevel["objects"]) for jsonLevel in jsonGameLevels] playerNames = [input("Player {}, enter your name > ".format(i + 1)) for i in range(numPlayers)] log("Using levels", str(levels)) forbidden = [levels[0].keyLocation, levels[0].exitLocation] players = [] playerLocs = [] i = 0 while i < numPlayers: # Populate players in level playerName = playerNames[i] randBoardNum, randBoard = getRandomRoomInLevel(levels[0]) log("Rand board", str(randBoardNum), str(randBoard.dimensions)) loc = genXRandCoords(1, forbidden, randBoard.origin, randBoard.dimensions).pop() if loc not in playerLocs: player = Player(playerName, loc) players.append(player) levels[0].boards[randBoardNum] = addPlayersToBoard(randBoard, { playerName: player}) playerLocs.append(loc) i += 1 game = Dungeon(levels, playerNames, startLevel - 1, False) enemies = [] # LIST of dictionaries for each level for i in range(len(game.levels)): numZombies = math.floor((i + 1) / 2) + 1 numGhosts = math.floor(((i + 1) - 1) / 2) log("NUM ZOMBIES", str(numZombies), "NUM GHOSTS", str(numGhosts)) levelEnemies = {} enemyLocs = [] for zombie in range(numZombies): randBoardNum, randBoard = getRandomRoomInLevel(levels[i]) name = "zombie" + str((zombie + 1)) # log("BUILDIGN NEW ZOMBIEEE") # log("NAME: ", name) loc = genXRandCoords(1, playerLocs + forbidden + enemyLocs, randBoard.origin, randBoard.dimensions).pop() log("LOC", str(loc)) enemyLocs.append(loc) newZombie = Enemy(name, loc) levelEnemies[name] = (randBoardNum, newZombie) for ghost in range(numGhosts): randBoardNum, randBoard = getRandomRoomInLevel(levels[i]) name = "ghost" + str((ghost + 1)) loc = genXRandCoords(1, playerLocs + forbidden + enemyLocs, randBoard.origin, randBoard.dimensions) # loc = getRandCoordInLevel(level, True) enemyLocs.append(loc) newGhost = Enemy(name, loc, "ghost") levelEnemies[name] = (randBoardNum, newGhost) enemies.append(levelEnemies) populateEnemies(i, levelEnemies, game) # Initialize screen pygame.init() clock = pygame.time.Clock() screen = pygame.display.set_mode(Globals.SCREEN_DIMENSIONS, flags=pygame.RESIZABLE) pygame.display.set_caption("Local Snarl Demo") # Fill background. Draw onto this gameDisplaySize = ( Globals.GAME_DISPLAY_WIDTH, Globals.GAME_DISPLAY_HEIGHT) background = pygame.Surface(gameDisplaySize) statusBarSize = (Globals.STATUS_BAR_WIDTH, Globals.STATUS_BAR_HEIGHT) statusBar = pygame.Surface(statusBarSize) background = background.convert() # converts Surface to single-pixel statusBar = statusBar.convert() # converts Surface to single-pixel # format background.fill(Globals.BG_COLOR) statusBar.fill(Globals.BG_COLOR) currLevel0: Level = game.levels[game.currLevel] def getEnemies(enemiesDict: list): acc = [] for enemiesInLevel in enemiesDict: for enemyName in enemiesInLevel.keys(): acc.append(enemiesInLevel[enemyName][1]) return acc allEnemies = getEnemies(enemies) log("All enemies", str(allEnemies)) log("Level has this many boards", str(len(currLevel0.boards))) def locationInTiles(location: tuple, tiles: dict): lrow, lcol = location for row in tiles.keys(): if lrow == row: for col in tiles[row].keys(): if lcol == col: return True return False # if isObserving: # log("OBSERVER VIEW") # allTiles = getAllTiles(currLevel0) # view = ObserverView("observer", allTiles, [currLevel0.keyLocation], # [currLevel0.exitLocation], # players, allEnemies) # renderObserverView(background, view) # else: # log("PLAYER VIEW") # player: Player = getPlayer(currLevel0, playerNames[0]) # visibleTiles = getVisibleTiles(player, currLevel0) # nearbyEnemies = [enemy for enemy in allEnemies if # locationInTiles(enemy.location, visibleTiles)] # nearbyPlayers = [player for player in players if # locationInTiles(player.location, visibleTiles)] # view = PlayerView(playerNames[0], # visibleTiles, # player.location, # [currLevel0.keyLocation], # [currLevel0.exitLocation], # nearbyPlayers, nearbyEnemies) # # renderPlayerView(background, view) # Block events we don't care about and allow ones we do # (speeds processing) pygame.event.set_blocked(None) pygame.event.set_allowed([QUIT, MOUSEBUTTONDOWN, MOUSEBUTTONUP]) hasMoved = {} for player in game.players: hasMoved[player] = 0 while True: # pygame.time.wait(250) clock.tick(30) # cap at 30fps currLevel: Level = game.levels[game.currLevel] if game.isGameOver: log("The game is over! If you won, congrats!") sys.exit(0) log("Player turn is ", str(currLevel.playerTurn)) playerName = game.players[currLevel.playerTurn] player: Player = getPlayer(currLevel, playerName) log("Game players", str(game.players)) log("Level players", str(getAllPlayers(currLevel))) # player not in level (ejected or exited) if not player: log("Skipping turn") hasMoved[playerName] = 1 log("HASSSMOVVVEEEEDD POST ENEMIESS", str(hasMoved)) if currLevel.playerTurn == len(game.players) - 1: currLevel.playerTurn = 0 else: currLevel.playerTurn = currLevel.playerTurn + 1 # currLevel.playerTurn = currLevel.playerTurn + 1 # currLevel.enemyTurn = currLevel.enemyTurn + 1 continue log("Player turn", str(currLevel.playerTurn)) log("Enemy turn:", str(currLevel.enemyTurn)) # Move all enemies at beginning of rounds (all players moved) numPlayersInGame = len(game.players) - 1 # log("numPlayersInGame", str(numPlayersInGame)) # if currLevel.enemyTurn == numPlayersInGame: if all(hasMoved.values()): for enemy in getEnemiesInLevel(currLevel): nextMove = EnemyMove.enemyNextMove(enemy, game) log("Next move is", str(nextMove)) GameManager.move(enemy.name, nextMove, game, isPlayer=False) # currLevel.enemyTurn = -1 for moved in hasMoved.keys(): hasMoved[moved] = 0 log("HASSSMOVVVEEEEDD POST ENEMIESS", str(hasMoved)) board1: Board = currLevel.boards[ whichBoardInLevel(currLevel, player.location)] # Render appropriate view if isObserving: allTiles = getAllTiles(currLevel) allPlayers = getAllPlayers(currLevel) allEnemies = getAllEnemies(currLevel) view = ObserverView("observer", allTiles, [currLevel.keyLocation], [currLevel.exitLocation], allPlayers, allEnemies) renderObserverView(background, view) else: visibleTiles = getVisibleTiles(player, currLevel) visiblePlayers = [board1.players[pname] for pname in board1.players.keys() if locationInTiles( # FIXME bug when player next to you board1.players[pname].location, visibleTiles)] visibleEnemies = [board1.enemies[ename] for ename in board1.enemies.keys() if locationInTiles( board1.enemies[ename].location, visibleTiles)] newView = PlayerView(playerName, visibleTiles, player.location, [currLevel.keyLocation], [currLevel.exitLocation], visiblePlayers, visibleEnemies) renderPlayerView(background, newView) # Everyone gets a status bar (yay) renderStatusBar(statusBar, game) # handle user events for event in pygame.event.get(): if event.type == QUIT: sys.exit(0) if event.type == MOUSEBUTTONDOWN: # FIXME and not isObserving: newLoc = translateScreenLocation(event.pos) if not locationInLevelBounds(currLevel, newLoc): continue # log("Got click at", str(event.pos), "--->", str(newLoc)) GameManager.move(playerName, newLoc, game) hasMoved[playerName] = 1 log("HASSSMOVVVEEEEDD", str(hasMoved)) """ gameCollection ---> {gameName: dungeon} observerCollection ---> {observerName: gameName} when observe requested/game state change Observer.send(observerName, gameCollection[observerCollection[observerName]]) """ # map of keys pressed # keys = pygame.key.get_pressed() screen.blit(background, (0, Globals.STATUS_BAR_HEIGHT)) # render pixels to screen.blit(statusBar, (0, 0)) pygame.display.update() # update except FileNotFoundError: print("Couldn't find that level file. Try again") sys.exit(1) except json.JSONDecodeError: print("Malformed levels") sys.exit(1) except KeyboardInterrupt: print("\nExiting...") sys.exit(0)