def add_monster(self, name=None, level=None, tolerance=None, location=None, region=None, ai=None): #Handle Default Objects if not level: level = self.paneLevel if not tolerance: if level == 0: tolerance = 0 level = 1 else: tolerance = 1 if not location: location = Location(self.location, (random.randrange(PANE_X), random.randrange(PANE_Y))) ignore_max = True if level > 9 else False level = min(level, 9) if name != "Devil" else level # print ignore_max person = TheoryCraft.getMonster(level=level, name=name, tolerance=tolerance, ignoreMaxLevel=ignore_max) person.location = location if not region: region = Region() region("ADD", "CIRCLE", person.location, PANE_Y/4) if ai: print "add_monster(ai) not implemented" person.ai.add("wander", person.movementSpeed, pid=id(person), region=region, move_chance=1.0 / (person.movementSpeed)) self.person[id(person)] = person
def load_monsters(self, monsters=None): ''' Parameters: monsters: A list of tuples with a monster dehydrated string and location object: [("dehydrated_string", Location()), ...] ''' if monsters != None: for monster in monsters: person = TheoryCraft.rehydrateMonster(monster[0], save=True) person.location = monster[1] self.person[id(person)] = person else: #TODO: Make this better. We're creating monsters from scratch here pass random.seed(self.seed + str(self.location) + "load_monsters") for i in range(0): self.add_monster()
def server_loop(self): ''' Network.py fills SDF.queue with commands from the clients server_loop handles them in the general pattern of validating the command, updating the game state, and then broadcasting the command to everyone else who needs to act on it. ''' while not self.SDF.queue.empty(): port, command = self.SDF.queue.get() if command.type == "PERSON" and command.action == "REMOVE" and port \ and not hasattr(command, 'id'): command.id = self.player[port] if hasattr(command, 'id') and (command.id in self.person and self.person[command.id].cPane): self.CS.handle(port, command) continue if hasattr(command, 'id') and not (command.type == "PERSON" and command.action in \ ["CREATE", "LOAD"]) and command.id not in self.person: continue ###### CreatePerson ###### if command.type == "PERSON" and (command.action == "CREATE" or command.action == "LOAD"): self.load_pane(command.location.pane) if command.action == "LOAD": person = TheoryCraft.rehydratePlayer(command.details) elif command.action == "CREATE": person = TheoryCraft.convertFromDetails(command.details) person.id = id(person) command.id = person.id person.ai.startup(self) if port: self.player[port] = person.id self.person[person.id] = person self.pane[command.location.pane].person.append(person.id) if port: self.broadcast(Command("UPDATE", "SEED", seed=self.world.seed), command.id) self.broadcast(Command("UPDATE", "TURNTIME", turnTime=self.turnTime), command.id) # Send command to each player in the affected pane self.broadcast(command, -command.id) # Send list of players to the issuing client if port: for i in self.pane[command.location.pane].person: if i != command.id: comm = Command("PERSON", "CREATE", id=i, \ location=self.person[i].location, \ details=self.person[i].dehydrate()) self.broadcast(comm, port=port) self.send_world_items(command.location, port=port) ###### MovePerson ###### if command.type == "PERSON" and command.action == "MOVE": self.load_pane(command.location.pane) portal=command.portal if hasattr(command, "portal") else None iLoc = command.iLocation if hasattr(command, "iLocation") else None if command.id in self.person: if iLoc: self.person[command.id].iLocation = iLoc else: iLoc = self.person[command.id].iLocation # If this is a legal move request if self.tile_is_open(command.location, pid=command.id, portal=portal, iLoc=iLoc): if not portal: #Check for entities that have a trigger() attribute p = iLoc if iLoc in self.pane else command.location.pane if command.location.pane in self.pane else None if command.location.pane in self.pane and not iLoc: ent_list = self.pane[command.location.pane].get_trigger_entities(command.location) elif iLoc in self.pane: ent_list = self.pane[iLoc].get_trigger_entities(command.location) for entity in ent_list: portal_loc, new_loc = entity.trigger() # print "PORTAL LOCATION: " + str(portal_loc) # self.SDF.queue.put((None, Command("PERSON", "MOVE", id=command.id, portal=entity, iLocation=portal_loc, location=new_loc))) #"SHOP" and command.action == "REQUESTSHOP" if entity.portal_type == Portal.SHOP: self.SDF.queue.put((None, Command("SHOP", "REQUESTSHOP", id=command.id))) # If the origin and destination are in the same pane if self.person[command.id].location.pane == command.location.pane: # Update location and broadcast self.person[command.id].location = command.location self.broadcast(command, -command.id, exclude=True if port else False) else: # Remove person from players' person tables, and pane's person list self.pane[self.person[command.id].location.pane].person.remove(command.id) self.broadcast(Command("PERSON", "REMOVE", id=command.id), -command.id) # Update location in server memory self.person[command.id].location = command.location # Add player to new pane lists and send to clients in the affected pane self.pane[command.location.pane].person.append(command.id) command.action = "CREATE" command.details = self.person[command.id].dehydrate() self.broadcast(command, pane=command.location.pane) # Send list of players to the issuing client if command.id in self.player.values(): for i in self.pane[command.location.pane].person: if i != command.id: comm = Command("PERSON", "CREATE", id=i, \ location=self.person[i].location, \ details=self.person[i].dehydrate()) self.broadcast(comm, command.id) # HANDLE SENDING SPECIFIC PANE THINGS HERE self.send_world_items(command.location, pid=command.id) self.unload_panes() # Check for combat range and initiate combat states if command.id in self.player.values(): for person in self.pane[self.person[command.id].location.pane].person: if self.person[command.id].location.in_melee_range( \ self.person[person].location) and \ self.person[person].team == "Monsters": self.CS.startCombat(command.id, person) else: if port: command.location = self.person[command.id].location command.details = True self.broadcast(command, port=port) ###### RemovePerson ###### if command.type == "PERSON" and command.action == "REMOVE": if command.id in self.person: if port: command.id = self.player[port] del self.player[port] self.pane[self.person[command.id].location.pane].person.remove(command.id) self.broadcast(command, -command.id) del self.person[command.id] self.unload_panes() ###### Levelup Player ###### if command.type == "PERSON" and command.action == "REPLACE": newPerson = TheoryCraft.rehydratePlayer(command.player) newPerson.location = self.person[command.id].location newPerson.id = command.id self.person[command.id] = newPerson self.broadcast(command, -command.id, exclude=True) #### Shop Creation #### if command.type == "SHOP" and command.action == "REQUESTSHOP": activePlayer = self.person[command.id] requestedPane = self.pane[activePlayer.location.pane] if self.world.is_town_pane(activePlayer.location): currentShop = None if requestedPane not in self.shops: level = requestedPane.paneLevel currentShop = shop.Shop(level=level) self.shops[requestedPane] = currentShop else: currentShop = self.shops[requestedPane] action = Command("SHOP", "OPEN", id=command.id, shopLevel=currentShop.level, shopSeed=currentShop.seed) self.broadcast(action, pid=command.id) #### Respec #### if command.type == "RESPEC" and command.action == "REQUESTRESPEC": activePlayer = self.person[command.id] if self.world.is_town_pane(activePlayer.location): if activePlayer.ironman: text = "You are on Ironman mode, and are thus too cool to respec!" action = Command("UPDATE", "TEXT", text=text, color='white') self.broadcast(action, pid=activePlayer.id) elif activePlayer.level > 1: oldExp = activePlayer._experience newHero = TheoryCraft.resetPlayerLevel(activePlayer) newHero.location = self.person[command.id].location newHero.id = command.id newHero._experience = oldExp self.person[command.id] = newHero jerky = newHero.dehydrate() # Send the newHero to the client. action = Command("PERSON", "REPLACE", id=command.id, player=jerky) self.broadcast(action, pid=-command.id) ###### Get Item / Open Chest ###### if command.type == "PERSON" and command.action == "BASHCHEST": activePlayer = self.person[command.id] currentPane = self.pane[activePlayer.location.pane] chest, loc = currentPane.get_treasure_chest(activePlayer.location) if chest: if chest.locked: success = chest.bash(activePlayer) if success: chest.locked = False text = activePlayer.name + " successfully bashed open a chest." action = Command("UPDATE", "TEXT", text=text, color='lightskyblue') self.broadcast(action, pid=-activePlayer.id) else: text = activePlayer.name + " failed to bash open a chest." action = Command("UPDATE", "TEXT", text=text, color='red') self.broadcast(action, pid=-activePlayer.id) else: text = "No need to bash -- chest is unlocked." action = Command("UPDATE", "TEXT", text=text, color='white') self.broadcast(action, pid=activePlayer.id) if command.type == "PERSON" and command.action == "OPEN": activePlayer = self.person[command.id] currentPane = self.pane[activePlayer.location.pane] chest, loc = currentPane.get_treasure_chest(activePlayer.location) if chest and chest.locked: picklockSuccess = chest.pickLock(activePlayer) if picklockSuccess: chest.locked = False text = activePlayer.name + " successfully unlocked a chest." action = Command("UPDATE", "TEXT", text=text, color='lightskyblue') self.broadcast(action, pid=-activePlayer.id) else: text = "The chest is locked" if activePlayer.lockpicking > 0: # This is a class that *could* unlock it with more Cunning. text += " and you lack the Cunning to unlock it." action = Command("UPDATE", "TEXT", text=text, color='red') self.broadcast(action, pid=-activePlayer.id) return if chest and not chest.locked: inventories = chest.open(self.get_nearby_players(command.id)) currentPane.remove_chest(loc) #Notify clients in the affected pane for p, i in self.player.iteritems(): #Replace this with list of players on current pane if self.person[i].location.pane == self.person[command.id].location.pane: #Send animation request... action_animate = Command(type="ENTITY", action="ANIMATE", location=loc) self.broadcast(action_animate, port=p) action_remove = Command(type="CHEST", action="REMOVE", location=loc) self.broadcast(action_remove, port=p) thisPlayer = self.person[self.player[p]] itemList = inventories[thisPlayer] if not itemList: action = Command("UPDATE", "TEXT", text='Chest was empty', color='lightskyblue') self.broadcast(action, port=p) for item in itemList: equipped = False action = None if isinstance(item, int): action = Command("ITEM", "CREATE", itemIdentifier=item, id=thisPlayer.id) else: action = Command("ITEM", "CREATE", itemIdentifier=item.identifier, id=thisPlayer.id) self.broadcast(action, port=p) if thisPlayer.shouldAutoEquip(item): thisPlayer.equip(item) action = Command("ITEM", "EQUIP", itemIdentifier=item.identifier, id=thisPlayer.id) self.broadcast(action, port=p) equipped = True text = '' if isinstance(item, int): text = 'Found ' + `item` + ' pieces of gold.' else: text = 'Found item: ' + item.displayName if equipped: text ='Found and equipped item: ' + item.displayName action = Command("UPDATE", "TEXT", text=text, color='lightskyblue') self.broadcast(action, port=p) thisPlayer.refreshAP() action = Command("PERSON", "UPDATE", id=thisPlayer.id, AP=thisPlayer.AP, totalAP=thisPlayer.totalAP) self.broadcast(action, port=p)
def __init__(self, pane, pane_focus, monster, num_players): ''' A subpane of the current pane. It will contain 10x6 of the original tiles which turn into 3x3 grids on the CombatPane. Dimensions are 30x18 (there will be a border around the pane) Member Variables: pane_focus: a Location object with the current pane and the tile of the monster of focus. This is the location from which we entered combat. If this location is far enough away from a border, sub pane will be centered from here. ''' super(CombatPane, self).__init__(pane.seed, (0,0), False) self.objects = dict() #Fixed combat/overworld passability bug self.paneCharacterLevel = 1 self.open_region = Region("SQUARE", Location(self.location, (0, 0)), Location(self.location, (PANE_X-1, PANE_Y-1))) loc_x = pane_focus.tile[0] loc_y = pane_focus.tile[1] dx_min = min(loc_x - 5, 0) #yields 0 if far enough away from edge, dy_min = min(loc_y - 3, 0) #and negative if too close # print "dx_min, dy_min: " + str(dx_min) + ", " + str(dy_min) dx_max = max(loc_x - 26, 0) #yields 0 if far enough away from edge, dy_max = max(loc_y - 14, 0) #and positive if too close # print "dx_max, dy_max: " + str(dx_max) + ", " + str(dy_max) dx = dx_min + dx_max - 1 dy = dy_min + dy_max - 1 loc_x -= dx loc_y -= dy self.focus_location = Location(PANE_X/2 + dx*3, PANE_Y/2 + dy*3) fx = max(0, min(PANE_X - 1, self.focus_location.tile[0])) fy = max(0, min(PANE_Y - 1, self.focus_location.tile[1])) self.focus_location = Location(fx, fy) i = 2 for x in range(loc_x-4, loc_x+6): j = 2 for y in range(loc_y-2, loc_y+4): # print "(" + str(x) + ", " + str(y) + ")" if (x,y) in pane.objects: self.add_zoomed_obstacle((i, j), pane.objects[(x,y)]) #Remove areas where there are obstacles self.open_region("SUB", "SQUARE", Location(self.location, (i-1, j-1)), Location(self.location, (i+1, j+1))) j+=3 i+=3 self.load_background_images() if monster: monsters = TheoryCraft.generateMonsterGroup(monster, numberOfPlayers=num_players) self.place_monsters(monsters, self.focus_location) # for id, monster in self.person.iteritems(): # loc = monster.location # self.traps_region("SUB", "SQUARE", loc, loc) #Remove areas where players can enter for (x, y) in CombatPane.CombatEntrances: self.open_region("SUB", "SQUARE", Location(self.location, (x-1, y-1)), Location(self.location, (x+1, y+1))) self.place_traps(num_players)