def otherInfo(self, tree, entry): owner = dataModel.playerNameGet(self.game, entry['owner']) othr = tree.insert("", 'end', image=self.photo, text=entry['name'], values=[owner, entry['type']])
def starInfo(self, tree, entry): owner = dataModel.playerNameGet(self.game, entry['owner']) desc = "Star holding " + str(int(entry['BP']['cur'])) + " BP" if (entry['BP']['perturn'] > 0): desc += "(+" + str(int(entry['BP']['perturn'])) + " per turn)" star = tree.insert("", 'end', image=self.photo, text=entry['name'], values=[owner, desc])
def shipInfo(self, tree, entry): if (entry['WG']['max']): text = "Warp" + entry['type'] if (not entry['WG']['cur']): text += "(Drive Currently Damaged)" else: text = "System" + entry['type'] title = "techLevel " + str(entry['techLevel']) text1 = "Moves left: " + str(entry['moves']['cur']) text1 += ", " text1 += "PowerDrive: " + str(entry['PD']['cur']) + "/" + str( entry['PD']['max']) text2 = "Shields " + str(entry['S']['cur']) + "/" + str( entry['S']['max']) text2 += ", " text2 += "Beams: " + str(entry['B']['cur']) + "/" + str( entry['B']['max']) text3 = "Tubes: " + str(entry['T']['cur']) + "/" + str( entry['T']['max']) text3 += " - " text3 += "Missiles: " + str(entry['M']['cur']) + "/" + str( entry['M']['max']) text4 = "SystemRacks: " + str(entry['SR']['cur']) + "/" + str( entry['SR']['max']) text4 += ", " text4 += "Holds: " + str(entry['H']['cur']) + "/" + str( entry['H']['max']) text4 += "[" + str(entry['Hauled']) + "]" owner = dataModel.playerNameGet(self.game, entry['owner']) base = tree.insert("", 'end', image=self.photo, text=entry['name'], values=[owner, text]) line = tree.insert(base, 'end', text=title, values=["", text1]) line = tree.insert(base, 'end', text="", values=["", text2]) line = tree.insert(base, 'end', text="", values=["", text3]) line = tree.insert(base, 'end', text="", values=["", text4])
def parseCmd(self, jsonStr): try: root = json.loads(jsonStr) except Exception as error: print("GServer:", "JSON parse error for ", jsonStr, "\n") return False cmd = root['cmd'] cmdStr = cmd['cmd'] if (self.cmdStr != cmdStr): print("GServer: (%s) CMD: %s PHASE: %s" % (cmd['plid'], cmdStr, self.game['state']['phase'])) self.cmdStr = cmdStr if cmdStr == 'quit': # Quit the game and exit the server process # This cmd doesn't save anything. Call save if you want to save self.log( dataModel.playerNameGet(self.game, cmd['plid']) + " Ended the game") self.game['state']['phase'] = "quitting" self.gameContinues = False elif cmdStr == 'ping': # simple test to see if server responds. So do nothing and respond pass elif cmdStr == 'newplayer': # New player requests to join. # I guess we would look at the game state? Hmmm # What about a player joining a previously saved game? # Input? Player name and potentially player specific options # What player specific options? IP:port? newPlayer = cmd['name'] startingBases = cmd['bases'] color = cmd['color'] if int(cmd['plid']) == 0: # If they didn't provide a playerid create one plid = max( [0] + [player['plid'] for player in self.game['playerList']]) + 1 else: plid = cmd['plid'] print("GServer:", "newPlayer", newPlayer, plid, startingBases) player = dataModel.playerTableGet(self.game, plid) if player is None: # Brand new player. If the game is "accepting players" # Add the new player if ((self.game['state']['phase'] is None) or (self.game['state']['phase'] == "creating")): self.game['playerList'].append({ 'name': newPlayer, 'phase': "creating", 'color': color, 'plid': plid }) player = dataModel.playerTableGet(self.game, plid) player['color'] = color self.log(newPlayer + " is " + color + " and starts with" + str(startingBases)) for base in startingBases: ownIt = dataModel.findBase(self.game, base) if ownIt: print(newPlayer, "owns", base) ownIt['owner'] = plid #give them everything at that base baseItems = dataModel.findObjectsAt( self.game, ownIt['location']['x'], ownIt['location']['y']) for baseItem in baseItems: baseItem['owner'] = plid else: self.log(newPlayer + " Error: Missing base " + base) else: print("GServer:", "may not join on going game!") self.log(newPlayer + " rejected from game in phase " + self.game['state']['phase']) else: # Normally the player shouldn't exist! But they do for the # sample game (or if reconnecting to a game) if (player['phase'] is None): assert ((self.game['state']['phase'] is None) or (self.game['state']['phase'] == "creating")) player['phase'] = "creating" print("GServer: I don't think this should happen anymore") else: self.log(newPlayer + " is rejoining game") elif cmdStr == 'newgame': # What to do? Offer to save current game? NO! this is the server, # do as commanded # erase current game/dictionary # Start empty # Input? Would be list of options for game # Should we prevent this? Perhaps block it? Only permit it # if there is only one connected player? # # Need to be given the name of the game? # Warn/Error if current game hasn't been saved (is dirty) plid = cmd['plid'] gamename = cmd['gamename'] name = cmd['name'] self.game = dataModel.defaultGame() self.game['state']['phase'] = "creating" self.log(name + " Created a new game") # TODO probably need things like game options??? # TODO lots of options, right? elif cmdStr == 'start': # Basically just change state so players can begin building # and playing self.log("Starting game. Players should build now.") assert (self.game['state']['phase'] == "creating") self.game['state']['phase'] = "build" changeAllPlayerPhase(self.game, "creating", "build") elif cmdStr == 'buildship': # Now we get to fun stuff # Check for proper state to see if player permitted to build. # Validate a legal build. Does player have the money? # Input? ... this is a lot... The entire ship? We would have # to calculate the cost based on the options and validate it. # We would have to deduct the cost from the players total. # Of course need to see if it is a valid ship to begin with # TODO lots of parameters! ship = cmd['ship'] baseName = cmd['base'] assert (self.game['state']['phase'] == "build") #lets do this the lazy way first. just append the ship to the list! print("GServer:", "ship to append: The ", ship['name']) #print(ship) #How much will this ship cost? cost = 0 cost += ship['PD']['max'] cost += ship['B']['max'] cost += ship['S']['max'] cost += ship['E']['max'] cost += ship['T']['max'] cost += ship['M']['max'] / 3 cost += ship['A']['max'] / 2 cost += ship['C']['max'] cost += ship['SH']['max'] / 6 cost += ship['SR']['max'] cost += ship['H']['max'] cost += ship['R']['max'] * 5 if ship['WG']['max'] == True: cost += 5 print("GServer cost:", cost) #TODO: This next section is bad. we don't check to see if the player #who sent the command owns the base, or is in the right spot. #what we should really do is have this command only take the ship, #and have the base we work with just be any base on the ship's hex #owned by the right player #find the base in the game base = dataModel.findBase(self.game, baseName) if (base is not None): #does the base have enough to pay for the ship? if base['BP']['cur'] >= cost: #subtract the cost... #print (base) base['BP']['cur'] -= cost #print (base) #and build the ship! self.game['objects']['shipList'].append(ship) self.log( dataModel.playerNameGet(self.game, cmd['plid']) + " Built the '" + ship['name'] + "' at " + baseName) else: print( "GServer cheater!:", "base({}) can't afford ship({})".format( base['BP']['cur'], cost)) elif cmdStr == 'ready': # A generic cmd used to end several phases plid = cmd['plid'] print("GServer:", "player", plid, "done with phase", self.game['state']['phase']) # Based on current phase what do we do? if (self.game['state']['phase'] == "creating"): # Record ready for given player changePlayerPhase(self.game, plid, "creating", "waiting") elif (self.game['state']['phase'] == "build"): # Given player can no longer build and must wait # When all players ready AUTO move to move phase # Record ready for given player changePlayerPhase(self.game, plid, "build", "waiting") if areAllPlayersInPhase(self.game, "waiting"): self.game['state']['phase'] = "move" changeAllPlayerPhase(self.game, "waiting", "move") # Reset all ships so that can move again for ship in self.game['objects']['shipList']: ship['moves']['cur'] = math.ceil(ship['PD']['cur'] / 2) elif (self.game['state']['phase'] == "move"): # Given player can no longer move and must wait changePlayerPhase(self.game, plid, "move", "waiting") # When all players ready AUTO move to combat phase if areAllPlayersInPhase(self.game, "waiting"): self.game['state']['phase'] = "combat" changeAllPlayerPhase(self.game, "waiting", "combat") elif (self.game['state']['phase'] == "combat"): # Given player finished submitting orders must wait changePlayerPhase(self.game, plid, "combat", "waiting") # When all players ready AUTO move to ... something # damage selection phase if there was combat # end turn if there wasn't if areAllPlayersInPhase(self.game, "waiting"): if (self.game['orders']): resolveCombat(self, self.game, self.game['orders']) self.game['state']['phase'] = "damageselection" changeAllPlayerPhase(self.game, "waiting", "damageselection") else: # Skip "SystemShipRearrangement" # Start turn sequence over again # Collect build points, Check victory conditions harvest(self.game) winner = checkForVictory(self.game) if (winner): self.game['state']['phase'] = "victory" changeAllPlayerPhase(self.game, "waiting", "loser") changePlayerPhase(self.game, winner, "loser", "winner") self.log( "Admiral " + dataModel.playerNameGet(self.game, winner) + " is victorious") else: self.game['state']['phase'] = "build" changeAllPlayerPhase(self.game, "waiting", "build") # What if there is no combat? Probably go on to build # self.game['state']['phase'] = "build" elif (self.game['state']['phase'] == "damageselection"): # Given player finished allocating damage to ships # Are they being honest? isHonest = True for ship in self.game['objects']['shipList']: print(" ship name ", ship['name'], " dam ", ship['damage']) if (ship['owner'] == plid) and (ship['damage'] > 0): isHonest = False break if (isHonest): changePlayerPhase(self.game, plid, "damageselection", "waiting") else: self.log( dataModel.playerNameGet(self.game, plid) + " Still has damage to allocate") # When all players ready AUTO move to the next round of combat if areAllPlayersInPhase(self.game, "waiting"): self.game['state']['phase'] = "combat" changeAllPlayerPhase(self.game, "waiting", "combat") # Erase any existing orders. They no longer have any use self.game['orders'] = {} # UNUSED? FIXME elif (self.game['state']['phase'] == "battle"): # Given player can no longer give orders and must wait # When all players ready AUTO move to damageSelection phase # or resolve combat phase? Is there such a phase? self.game['state']['phase'] = "damageselection" else: print("GServer:", "Invalid phase for 'ready'") assert (False) elif cmdStr == 'moveship': # Input? ShipID, new location of ship? Movement vector? # Is it legal to move? (Proper turn sequence. Valid location on # map? Currently in combat? Does move cause combat? (meaning ship # halts immediately)) # Deduct movement from ship name = cmd['name'] x = cmd['x'] y = cmd['y'] assert (self.game['state']['phase'] == "move") ship = dataModel.findShip(self.game, name) if (ship is None): print("GServer:", "error: Ship not found", name) return False si, sj, sk = ijk.XYtoIJK(x, y) fi, fj, fk = ijk.XYtoIJK(ship['location']['x'], ship['location']['y']) delta = int((abs(si - fi) + abs(sj - fj) + abs(sk - fk)) / 2) #the above only works if no warplines are involved #if they go to one of these locations, it only costs one warpEnds = dataModel.getWarpLineEnd(self.game, ship['location']['x'], ship['location']['y']) if [x, y] in warpEnds: delta = 1 print("GServer:", " delta", delta, ship['location']['x'], ship['location']['y'], x, y) #can we actually move this far? if (delta <= ship['moves']['cur']): self.log( dataModel.playerNameGet(self.game, cmd['plid']) + " Moved the '" + name + "' from (" + str(ship['location']['x']) + "," + str(ship['location']['y']) + ")" + " to (" + str(x) + "," + str(y) + ")") ship['location']['x'] = x ship['location']['y'] = y ship['moves']['cur'] = ship['moves']['cur'] - delta #are there any system ships stored on this ship? #TODO: make sure we don't move too many ships for carried_name in ship['carried_ships']: #all we have is the name. We need to find the actual ship object in the data model carried_ship = dataModel.findShip(self.game, carried_name) #update the position of the carried ship carried_ship['location']['x'] = x carried_ship['location']['y'] = y #now we start a battle, if we can. #Is there anything here that can trigger a battle? for otherShip in self.game['objects']['shipList']: if otherShip['name'] != name: if otherShip['location']['x'] == x and otherShip[ 'location']['y'] == y: if otherShip['owner'] != ship['owner']: ship['moves']['cur'] = 0 #if we want wierd simultaneous movement, both ships stop otherShip['moves']['cur'] = 0 #set a flag, or have everyone else figure it out for themselves? elif cmdStr == 'loadship': #this is as simple as can be. #find both ships, add the name of 1 to the carried list of 2 ship = dataModel.findShip(self.game, cmd['shipName']) mother = dataModel.findShip(self.game, cmd['motherName']) #are these guys in the same square if ship['location']['x'] == mother['location']['x'] and ship[ 'location']['y'] == mother['location']['y']: mother['carried_ships'].append(ship['name']) self.log( dataModel.playerNameGet(self.game, cmd['plid']) + " loaded the '" + cmd['shipName'] + " onto the '" + cmd['motherName']) elif cmdStr == 'unloadship': #this is as simple as can be. #find the mother, and remove the name of the first ship mother = dataModel.findShip(self.game, cmd['motherName']) #are these guys in the same square mother['carried_ships'].remove(cmd['shipName']) self.log( dataModel.playerNameGet(self.game, cmd['plid']) + " unloaded the '" + cmd['shipName'] + " from the '" + cmd['motherName']) elif cmdStr == 'loadcargo': #this is as simple as can be. #find both ships, add the name of 1 to the carried list of 2 ship = dataModel.findShip(self.game, cmd['shipName']) star = dataModel.findObjectsInListAtLoc( self.game['objects']['starList'], ship['location']['x'], ship['location']['y']) base = dataModel.findObjectsInListAtLoc( self.game['objects']['starBaseList'], ship['location']['x'], ship['location']['y']) if base != None: target = base else: target = star shipment = int(cmd['shipment']) #are these guys in the same square if ship['location']['x'] == target['location']['x'] and ship[ 'location']['y'] == target['location']['y']: target['BP']['cur'] -= shipment ship['Hauled'] += shipment if (shipment > 0): action = "loading" else: action = "unloading" self.log( dataModel.playerNameGet(self.game, cmd['plid']) + ":" + ship['name'] + " " + action + " at " + target['name']) elif cmdStr == 'combatorders': # Per ship? All ships? # A combat instruction # Check for proper state. Are there existing orders? # Have all ships in combat been given orders? # Have all opponents ships in *this* combat been given orders? # Input? ShipID, combat command (fire, move, shields ...) # TODO many more parameters plid = cmd['plid'] battleOrders = cmd['battleOrders'] print("battleOrders:", battleOrders) assert (self.game['state']['phase'] == "combat") #Every player gives a list of orders, for all ships involved in a #Conflict. Once both players have sent orders, we start processing. self.game['orders'][plid] = battleOrders # Set a flag for every ship that is RETREATing. # This flag assumes the retreat was successful. # Prove it false. When another ship stops # you from succeeding the other ship will turn the # flag off. for myShip, shipOrders, in battleOrders.items(): myTactic = shipOrders['tactic'][0] print("order for ", myShip, myTactic) if (myTactic == 'RETREAT'): ship = dataModel.findShip(self.game, myShip) ship['damage'] = -1 # When all players ready (have submitted orders) # AUTO move to damageselection phase # When all damage has been selected ... by all players ... # move to battle .... or retreat ... or combat over ... or next # battle? elif cmdStr == 'acceptdamage': # Deduct combat damage # Input? ShipID, damage deducted from each component # Did they deduct enough damage? # Do they have more ships with damage to deduct? # TODO many more parameters # A ship that has been destroyed should be removed # (perhaps moved to a "dead" list) plid = cmd['plid'] newShip = cmd['ship'] print("ship update:", newShip['name'], newShip) assert (self.game['state']['phase'] == "damageselection") # maybe findShip could return the index too? # lookup = dataModel.findShip(self.game, newShip['name']) for i, ship in enumerate(self.game['objects']['shipList']): if ship['name'] == newShip['name']: index = i break assert (index is not None) if (newShip['damage'] > 0): # the damage must have been more than the ship # could take; therefore, destroy it. self.log("The '" + newShip['name'] + " Exploded!") del self.game['objects']['shipList'][index] else: # Replace existing ship self.game['objects']['shipList'][index] = newShip elif cmdStr == 'acceptretreat': self.acceptRetreat(cmd['plid'], cmd['ship'], cmd['x'], cmd['y']) elif cmdStr == 'savegame': # Write file ... who is responsible for game save? # I Don't want to load/restore game that is stored on the server # ... well, I would like to but that API would be a pain. # I'd have to do all the directory I/O work. Remember the game # server has no UI. So I would read files and directories # off of the server and then let the client navigate, select and # choose save/load # # Given a file name write that file to some save location # Mark the game as saved (not dirty) # It is possible that the name of the game (name of the file) # is already part of the game so you wouldn't need to provide # the name. # # Server does nothing with saveGame. The client must save the game print("GServer:", "saveGame") self.log("savegame isn't implemented") elif cmdStr == 'restoregame': # Because games are saved/restored on client ... # This would do nothing. Just like saveGame # # Given the name of a file/game. # restore that game overwriting the current game # Warn/Error if current game hasn't been saved (is dirty) print("GServer:", "restoreGame") self.log( dataModel.playerNameGet(self.game, cmd['plid']) + " Restored a saved game") self.game = cmd['game'] elif cmdStr == 'listgames': # Because games are saved/restored on client ... # This would do nothing. Just like saveGame # # List all of the saved games print("GServer:", "listGames") self.log("listgames isn't implemented") elif cmdStr == 'loadgame': # Offer to save current game? # Bring up selection dialog # load game overwriting existing game # # Like, newGame check for permission. # Input? Would be an entire JSON game # We should probably do some kind of validation but # this would just be pulling in the JSON and # translating it to the dict. # # Warn/Error if current game hasn't been saved (is dirty) print("GServer:", "loadGame") self.log("loadgame isn't implemented") elif cmdStr == 'playerleave': # Player is quitting # We could check for permission but I don't think so. # This is informational # Input? Player name? Some unique player key code for security? print("GServer:", "playerLeaving") self.log("playerleave isn't implemented") else: print("GServer:", "Not a legal command '", cmdStr, "'") return False return True
def resolveCombat(logger, game, orders): # For testing #for ship in game['objects']['shipList']: # ship['damage'] = 5 print("ALLorders:") print(orders) # What are the combat orders? # Find each combat order. # Find target of each order (an order can have multiple targets) # Find the orders for each target. # Match them up and resolve - so I need a simple function # for one v one orders to resolve against the chart # Table of orders is: # Array of players (dict) # For each player there is an array of ships (dict) # For each ship there is an order # each order can have a unique target for player, playerOrders in orders.items(): for myShip, shipOrders, in playerOrders.items(): if (not shipOrders): print("ERROR:", myShip, "has no orders") print("Should assert ... but must fix client") continue if 'conquer' in shipOrders: # This is a change of ownership. for baseName in shipOrders['conquer']: logger.log( dataModel.playerNameGet(game, player) + ": conquers " + baseName + " with " + myShip) base = dataModel.findBase(game, baseName) base['owner'] = player continue pretty = dataModel.prettyOrders(shipOrders) logger.log( dataModel.playerNameGet(game, player) + ": " + myShip + ": " + pretty) myPower = shipOrders['beams'][1] if (myPower > 0): myTactic = shipOrders['tactic'][0] myDrive = shipOrders['tactic'][1] myTarget = shipOrders['beams'][0] figureStuffOut(logger, game, orders, myShip, myPower, myTactic, myDrive, myTarget) else: for missile in shipOrders['missiles']: myPower = 2 myTactic = 'ATTACK' myDrive = missile[1] myTarget = missile[0] if (myDrive > 0): # Need ship to deduct missiles ship = dataModel.findShip(game, myShip) assert (ship) assert (ship['M']['cur'] > 0) ship['M']['cur'] -= 1 figureStuffOut(logger, game, orders, myShip, myPower, myTactic, myDrive, myTarget)