def renderPlayers(background: pygame.Surface, players: dict): log = logInFile("Render.py", "renderPlayers") log() playerNames = players.keys() for name in playerNames: player = players[name] renderPlayer(background, player)
def renderEnemies(background: pygame.Surface, enemies: dict): log = logInFile("Render.py", "renderEnemies") log() enemyNames = enemies.keys() for name in enemyNames: enemy = enemies[name] renderEnemy(background, enemy)
def renderEnemy(background: pygame.Surface, enemy: Enemy): x, y = getScreenLocation(enemy.location) tileRect = pygame.Rect(x, y, Globals.TILE_WIDTH, Globals.TILE_HEIGHT) # tileColor = Colors.ENEMY log = logInFile("Render.py", "renderEnemy") log(enemy.name) enemyLetter = Globals.FONT.render(formatInitial(enemy.name), True, Colors.ENEMY) # pygame.draw.rect(background, tileColor, tileRect) background.blit(enemyLetter, tileRect.topleft)
def renderBoard(background: pygame.Surface, board: Board): # FIXME only Rooms should render door tiles log = logInFile("Render.py", "renderBoard") log() for tile in board.tiles: renderTile(background, tile) log("Players in board:", str(board.players)) renderPlayers(background, board.players) renderEnemies(background, board.enemies)
def renderTile(background: pygame.Surface, tile): x, y = getScreenLocation(tile.location) # absolute log = logInFile("Render.py", "renderTile") # log("Loc: ", str(tile.location), "PixelLoc: ", str((x, y))) tileRect = pygame.Rect(x, y, Globals.TILE_WIDTH, Globals.TILE_HEIGHT) tileColor = getTileColor(tile) pygame.draw.rect(background, tileColor, tileRect) if tile.hasKey: radius = Globals.TILE_WIDTH / 2 pygame.draw.circle(background, Colors.KEY, tileRect.center, radius)
def renderPlayer(background: pygame.Surface, player: Player): x, y = getScreenLocation(player.location) tileRect = pygame.Rect(x, y, Globals.TILE_WIDTH, Globals.TILE_HEIGHT) # tileColor = Colors.PLAYER # TODO Fonts slow down the loading log = logInFile("Render.py", "renderPlayer") log(player.name) playerLetter = Globals.FONT.render(formatInitial(player.name), True, Colors.BLACK) # TODO font color # pygame.draw.rect(background, tileColor, tileRect) background.blit(playerLetter, tileRect.topleft)
def registerPlayers(numClients: int, sock: socket.socket): """ Registers numClients amount of clients to the game on the socket as players and returns a dict of name -> connection. :params numClients: int :params sock: socket.socket """ CONNS = {} numPlayersConnected = 0 # Dish out connections + register sockets while numPlayersConnected < numClients: try: log = logInFile("Server.py", "Join loop") log("begin loop") # Block waiting for new connection conn, client_addr = sock.accept() log("Got a connection") introMsg = welcomeMsg('"Alon"') introMsg += "\nEnter your name" conn.sendall(introMsg.encode('utf-8')) name = conn.recv(1024).decode('utf-8').strip() log("Name received", name) CONNS[name] = conn # register the player's conn log("Connected players:", str(list(CONNS.keys()))) numPlayersConnected += 1 if numPlayersConnected < numClients: conn.sendall( "Welcome {}! Please wait for {} other players to join\n". format(name, numClients - numPlayersConnected).encode('utf-8')) except UnicodeDecodeError: log("unicode decode error") if name in CONNS.keys(): del CONNS[name] continue except ConnectionResetError: log("conn reset error") if name in CONNS.keys(): del CONNS[name] continue # raise PlayerDisconnect except BrokenPipeError: log("broken pipe error") if name in CONNS.keys(): del CONNS[name] continue # raise PlayerDisconnect return CONNS
def renderLevel(background: pygame.Surface, level: Level): """ Render the board in the level at the given board number onto the background. :param background: :param level: :param boardNumber: :return: """ log = logInFile("Render.py", "renderLevel") log() # TODO render entire level (including all boards) for board in level.boards: renderBoard(background, board)
import pygame from pygame.locals import * import argparse import math from Render import renderPlayerView, renderObserverView, renderStatusBar from Util import logInFile, genXRandCoords, getPlayer, getAllTiles, \ translateScreenLocation, whichBoardInLevel, locationInLevelBounds, \ getRandomRoomInLevel, getEnemiesInLevel, getAllEnemies, getAllPlayers, getRandCoordInLevel from Convert import convertJsonLevel from Create import addPlayersToBoard, addEnemiesToBoard from Types import * from Player import getVisibleTiles import GameManager import Enemy as EnemyMove log = logInFile("localSnarl.py") def populateEnemies(levelNum: int, levelEnemies: dict, game: Dungeon): for enemyName in levelEnemies.keys(): # Populate enemies in level enemyBoardNum = levelEnemies[enemyName][0] board: Board = game.levels[levelNum].boards[enemyBoardNum] game.levels[levelNum].boards[enemyBoardNum] = addEnemiesToBoard( board, { enemyName: levelEnemies[enemyName][1]}) def main(): DEFAULT_LEVEL = './tests/snarl.levels' numPlayers = 1 startLevel = 1
import pygame import sys from pygame.locals import * from Create import createDungeon, createLevel, createGenericBoardTiles from Types import * from Render import renderDungeon, renderLevel from Util import logInFile import Globals from example2 import exampleLevel2, players, enemies log = logInFile("main.py") def main(): # Initialize screen pygame.init() clock = pygame.time.Clock() screen = pygame.display.set_mode(Globals.SCREEN_DIMENSIONS) pygame.display.set_caption("Snarl Demo") # Fill background. Draw onto this background = pygame.Surface(screen.get_size()) background = background.convert() # converts Surface to single-pixel format background.fill(Globals.BG_COLOR) # render test level (400 x 300) dung = createDungeon(exampleLevel2, players, enemies) log("DUNGEON BELOW") log(str(dung.players), str(dung.levels[0].boards[0])) renderDungeon(background, dung)
from Types import * from GameManager import move from example2 import exampleDungeon2, exampleLevel2, player1 from Util import logInFile, locationInBounds log = logInFile("test_GameManager.py") """ Cases: - one normal, valid move of 1 space - one normal, valid move of 2 spaces - move into a wall - move onto a key (interaction) - move into an exit (interaction) (players moved) - move into an exit (from last level) - move into player - move onto enemy (interaction) - move into the void """ # TODO MAKE THINGS LOOK GOOD def testOneNormalMove(): orig = player1.location dest = (11, 2) res: Dungeon = move("Saleha", dest, exampleDungeon2) level: Level = res.levels[res.currLevel] board: Board = level.boards[level.currBoard] updatedPlayer: Player = board.players["Saleha"] assert updatedPlayer.location is not orig assert updatedPlayer.location == dest
from Types import * from Create import createDungeon, addPlayersToBoard, removePlayersFromBoard from Move import moveEntity from Rulechecker import destHasEnemy, destHasKey, destHasExit, destHasPlayer, \ destHasWall, playerCanMoveTo from Util import whichBoardInLevel, logInFile, getPlayer, genXRandCoords, \ getRandomRoomInLevel, getPlayersInLevel, getEnemy, getAllPlayers log = logInFile("GameManager.py") def move(entityName: str, destination: tuple, 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. Returns an unmodified game if the player cannot move based on game rules. :param entityName: str :param destination: tuple :param game: Dungeon """ currLevel: Level = game.levels[game.currLevel] 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,
def renderItems(background: pygame.Surface, items): log = logInFile("Render.py", "renderItems") log() for item in items: if not item.hasBeenAcquired: renderItem(background, item)
def start(args): log = logInFile("Server.py") # Global vars JSON_LEVELS = args['jsonLevels'] NUM_LEVELS = args['numLevels'] NUM_CLIENTS = args['numClients'] WAIT = args['wait'] OBSERVE = args['observe'] ADDRESS = args['address'] PORT = args['port'] # Ready to begin CONNS = {} SOCK = None try: SOCK = socket.socket(socket.AF_INET, socket.SOCK_STREAM) SOCK.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) SOCK.settimeout(WAIT) SOCK.bind((ADDRESS, PORT)) SOCK.listen(NUM_CLIENTS) CONNS = registerPlayers(NUM_CLIENTS, SOCK) # Setup game (Convert levels, populate players, populate enemies, # create dungeon) GAME, playerNames = setupGame(list(CONNS.keys()), JSON_LEVELS) # Broadcast initial game state broadcast(startLevelMsg(1, playerNames), CONNS) # Send initial player update msgs broadcastPlayerUpdates(CONNS, GAME) log("GAME INITIALIZED!") # Enter main loop while True: # Obtain the current level currLevel: Level = GAME.levels[GAME.currLevel] # Check if the level has been completed if isLevelOver(currLevel): GAME = advanceLevel(GAME) broadcastLevelOver(GAME, CONNS) # Check if game is over if GAME.isGameOver: broadcastGameOver(GAME, CONNS) broadcast("disconnect", CONNS) # Execute player turns for playerName in playerNames: try: player = getPlayer(currLevel, playerName) if player: initLoc = player.location playerTurn = GAME.levels[GAME.currLevel].playerTurn MAX_TRIES = 5 for tries in range(MAX_TRIES): time.sleep(1) playerMove = executeTurn(CONNS[playerName]) GAME = move(playerName, playerMove, GAME) playerAfter = getPlayer(currLevel, playerName) moveMade = not playerAfter or playerTurn != \ GAME.levels[ GAME.currLevel].playerTurn if moveMade: break # last turn and still no valid move made if not moveMade: if tries == (MAX_TRIES - 1): GAME = move(playerName, None, GAME) else: sendPlayer(playerName, "Invalid move. Try again...", CONNS) broadcastPlayerUpdates(CONNS, GAME) except PlayerDisconnect: # TODO broadcast?? log("PLAYER DISCONNECTED") player: Player = getPlayer(currLevel, playerName) playerBoardNum = whichBoardInLevel(currLevel, player.location) GAME = removePlayer(playerName, playerBoardNum, GAME) # TODO reset playerNames continue # Execute enemy turns currLevel: Level = GAME.levels[GAME.currLevel] for enemy in getEnemiesInLevel(currLevel): nextMove = enemyNextMove(enemy, GAME) GAME = move(enemy.name, nextMove, GAME, isPlayer=False) broadcastPlayerUpdates(CONNS, GAME) # for playerName in playerNames: # msg = enemy.name + " moved" # broadcast(playerUpdateMsg(playerName, GAME, msg), CONNS) except json.JSONDecodeError: print("Malformed level file. Check your formatting") closeConns(CONNS) if SOCK: SOCK.close() sys.exit(1) except KeyboardInterrupt: print("\nExiting...") closeConns(CONNS) if SOCK: SOCK.close() sys.exit(0)
from Messages import * from Convert import convertJsonLevel, addPlayersToBoard, addEnemiesToBoard from more_itertools import first_true """ The Snarl Server: - dish out connections - create new game - handle joins - start game - administer game - broadcast game - end game """ log = logInFile("Server.py") class PlayerDisconnect(Exception): pass def populatePlayers(level: Level, playerNames: list): numPlayers = len(playerNames) forbidden = [level.keyLocation, level.exitLocation] players = [] playerLocs = [] i = 0 while i < numPlayers: # Populate players in level playerName = playerNames[i] randBoardNum, randBoard = getRandomRoomInLevel(level)
from Types import * from Util import whichBoardInLevel, getLocationsAround, locationInBounds, \ locationInLevelBounds, isTileOnBoard, logInFile, getAllPlayers from more_itertools import unique_everseen log = logInFile("Rulechecker.py") def playerCanMoveTo(destination: tuple, player: Player, level: Level, numMoves: int): """ Check if the destination given can be moved to by a given player. (True if destination does not have a player and is traversable) :param destination: tuple :param player: Player :param level: Level :param numMoves: int """ # NOTE this gets all locations in the board destBoardNumber = whichBoardInLevel(level, destination) playerBoardNumber = whichBoardInLevel(level, player.location) possibleMoves = playerPossibleCardinalMoves(player.location, numMoves, level) if destination not in possibleMoves: return False board = level.boards[destBoardNumber] if isTileOnBoard(destination, board): if board.tiles[destination[0]][ destination[1]].tileType is not TileEnum.WALL \
def renderDungeon(background: pygame.Surface, dungeon: Dungeon): log = logInFile("Render.py", "renderDungeon") log() renderLevel(background, dungeon.levels[dungeon.currLevel])
import sys import json from Util import intifyTuple, whichBoardInLevel, locationInLevelBounds, \ isDoorLocation, isTraversable, isPlayerInGame, getPlayer, logInFile from Convert import convertJsonDungeon from Types import * import GameManager from Rulechecker import playerCanMoveTo, destHasKey, destHasExit, destHasEnemy, \ isCurrentLevelOver from more_itertools import first_true from Player import getFieldOfView log = logInFile("testManager.py") """ PLANNING SESSION: Flow: 0. [X] parse json 1. [X] Take in data, convert it to dungeon 2. [X] Init a manager-trace (MT) 3. [X] Output an initial player vision to each player (Loop), add those updates to MT 4. [X] Loop on turns Executing Moves (The given number of turns was performed) EXIT immediately if: - One of the move input streams is exhausted - The level is over a. we do the MOVE if need to execute another move (invalid) bb. `continue` if invalid else b. append the status (could be generated by acc on dungeon) c. append the updates
from Types import * from Util import intifyTuple, whichBoardInLevel, logInFile from Create import addPlayersToBoard, addEnemiesToBoard log = logInFile("Convert.py") """ What a json room looks like: { "type" : "room", "origin" : (point), "bounds" : (boundary-data), "layout" : (tile-layout) } """ def convertJsonPlayer(jsonPlayer: dict): newPlayer = Player(jsonPlayer["name"], intifyTuple(jsonPlayer["position"])) return newPlayer def convertJsonEnemy(jsonEnemy: dict): newEnemy = Enemy(jsonEnemy["name"], intifyTuple(jsonEnemy["position"])) return newEnemy def convertJsonRoom(origin: list, boundaryData: list, tileLayout: list): boardEnum = BoardEnum.ROOM dimensions = intifyTuple(tuple(boundaryData)) upperLeftCorner = intifyTuple(tuple(origin)) tiles = dict() doorLocations = []