def getInventory(self): dLog( "inv getInventory: " + self.getItemId() + " " + str(self) + " -- " + str(self._inventory), Inventory._instanceDebug, ) return self._inventory
def getAttributesThatShouldntBeSaved(self): atts = [] if hasattr(self, "attributesThatShouldntBeSaved"): atts += self.attributesThatShouldntBeSaved # Dont save empty inventories for rooms if self.getType() != "Room": return atts # everything below is for rooms only if hasattr(self, "_inventory"): if len(self.getInventory()) == 0: dLog( "getAttributesThatShouldntBeSaved: adding _inventory " + "to unsaved attributes", self._instanceDebug, ) atts.append("_inventory") else: dLog( "getAttributesThatShouldntBeSaved: preserving room " + "inventory (count=" + str(len(self.getInventory())) + ")", self._instanceDebug, ) return atts
def autoPopulateInventory(self): """ create creature inventory * randomly pick the number of items from the numOfItemsDropped list * for each of those, randomly pick an objNum from the itemCatalog * for each of those, load the object and add it to the inventory * typically run at creature load time """ if not self._permanent: self.clearInventory() # Randomly select the itemCount itemCount = int(getRandomItemFromList(self._numOfItemsCarried)) if not itemCount: # 0 items carried return True for itemNum in range(1, itemCount + 1): # +1 due to exclusive ranges itemId = getRandomItemFromList(self._itemCatalog) if not itemId: continue if "/" not in itemId: continue dLog("creature.autoPopulateInventory: obj = " + itemId) oType, oNum = itemId.split("/") obj1 = ObjectFactory(oType, oNum) obj1.load() self.addToInventory(obj1) return True
def writePickleFile(self, filename): with open(filename, "wb") as outputfilehandle: try: pickle.dump(self, outputfilehandle, pickle.DEFAULT_PROTOCOL) except TypeError: dLog(self.debug(), self._debugStorage) traceback.print_exc()
def readJsonFile(self, filename, logStr=""): logPrefix = "readJsonFile: " with open(filename, "r") as filehandle: loadedItem = filehandle.read() thawedDict = jsonpickle.decode(loadedItem) if isinstance(thawedDict, dict): logger.warn(logPrefix + "Loaded content is of type: " + str(type(thawedDict)) + " which probably means " + " that it was saved or loaded incorrectly") # filter out attributes that should be excluded for onevar in self.getAttributesThatShouldntBeSaved(): if hasattr(thawedDict, onevar): dLog( logPrefix + " ignoring " + logStr + "attribute " + onevar + " during import", self._debugStorage, ) thawedDict = list(filter((onevar).__ne__, thawedDict)) else: # imported content is a known object (Creature, Char, etc) jsonDict = {} for onevar in vars(thawedDict): # filter out attributes that should be excluded if self.attributeShouldBeIgnored(onevar): continue jsonDict[onevar] = getattr(thawedDict, onevar) thawedDict = jsonDict return thawedDict return None
def _castFails(self): """ do everything related to failed spells """ msg = "Spell failed! " + self.getFailedReason() self._spoolOut(msg) dLog("magic.castFails: " + msg, self._instanceDebug) if self.spellName == "turn": self.charObj.setVulnerable(True)
def panicIfNeeded(self, charObj): """ If character has less than X percent health remaining, run away """ dLog("panicIfNeeded: " + str(charObj.getHitPointPercent()) + " <? 10", True) if charObj.getHitPoints() < 30 and charObj.getHitPointPercent() <= 10: self.charMsg(charObj, "Panic! ") self.run(charObj) return True return False
def load(self, requestedAttNames=[], logStr="", fileType="pickle"): """ load from persistant storage - load data into tmp object - iterate through the attributes assigning all, except the ones that we specificly exclude, to the current object - values of excluded objects are not overwritten """ if logStr != "": logStr += " " # append a space for easy logging self.setDataFilename() # We may muck with the object data. Before we do, store # the datafile info as a local variable so that there is no conflict. filename = self._datafile logPrefix = self.__class__.__name__ + " load: " if filename == "": logger.error(logPrefix + " Could not determine " + "filename for loading " + logStr) return False dLog(logPrefix + "Loading " + filename + "...", self._debugStorage) if self.dataFileExists(): # read the persisted content if re.search("\\.json$", filename): loadedDict = self.readJsonFile(filename, logStr) else: loadedDict = self.readPickleFile(filename, logStr) if not loadedDict: logger.error("storage.load - Could not get loaded instance") # Add attributes to current class object, based on revised list self.addAttributesToSelf(loadedDict, requestedAttNames, logStr) dLog( logPrefix + " loaded " + logStr + str(self.getId()) + " - " + self.describe(), self._debugStorage, ) self.initTmpAttributes() self.fixAttributes() self.postLoad() if self.isValid(): return True else: logger.error(logPrefix + logStr + str(self.getId()) + " is not valid") else: logger.warning(logPrefix + " " + logStr + "datafile doesn't exist at " + self._datafile) return False
def attributeShouldBeIgnored(self, attName, logStr=""): """ Return True if given attribute should be ignored """ logPrefix = "attributeShouldBeIgnored: " if attName in self.getAttributesThatShouldntBeSaved(): dLog( logPrefix + " ignoring " + logStr + "attribute " + attName + " during import", self._debugStorage, ) return True return False
def displayItems(self, charObj): # noqa C901 """ show items in current room """ buf = self.describeInvAsList( showDm=charObj.isDm(), showHidden=charObj.canSeeHidden(), showInvisible=charObj.canSeeInvisible(), sortList=False, ) dLog("displayItems:" + buf, False) if buf != "": buf = "You see " + buf return buf
def writeJSonFile(self, filename): jsonpickle.set_encoder_options("json", sort_keys=True, indent=4, ensure_ascii=False) frozen = jsonpickle.encode(self, max_depth=10) with open(filename, "w") as filehandle: try: filehandle.write(frozen) # json.dump(frozen, filehandle) except TypeError: dLog(self.debug(), self._debugStorage) traceback.print_exc()
def getHitMsg(self, atkDict, damage): """ return the hit message based on the attacker and target """ logPrefix = "combat.hitMsg: " msg = (atkDict["msgPrefix"] + atkDict["attackerSubject"] + " " + atkDict["attackerVerb"] + " " + atkDict["targetSubject"] + " for " + str(damage) + " damage.\n") # Construct separate message where all subjects are identified by name debugMsg = (atkDict["attackerName"] + " hits " + atkDict["targetName"] + " for " + str(damage) + " damage") dLog(logPrefix + debugMsg, self._instanceDebug) return msg
def _selfCriteriaAreMet(self): logPrefix = "magic.selfCriteria: Failed - " cName = self.charObj.getName() if self.levelRequired < 1: msg = "You can't cast " + self.spellName self.failedReason += msg + "\n" dLog(logPrefix + msg + ", " + cName, self._instanceDebug) return False if self.charObj.getLevel() < self.levelRequired: msg = ("a level " + str(self.charObj.getLevel()) + " " + self.charObj.getClassName() + " can not cast " + self.spellName + " which requires level " + str(self.levelRequired)) self.failedReason += msg + "\n" dLog(logPrefix + msg + ", " + cName, self._instanceDebug) return False if self.limitedNumberPerDay and not self.charObj.getLimitedSpellCount( ): msg = "You have used all of your limited spells for today" self.failedReason += msg + "\n" dLog(logPrefix + msg + ", " + cName, self._instanceDebug) return False if self._requiresmana and self.charObj.getMana() < self.mana: msg = "You don't have enough mana for that" self.failedReason += msg + "\n" dLog( logPrefix + msg + "(char=" + str(self.charObj.getMana()) + " spell=" + str(self.mana) + ")" + ", " + cName, self._instanceDebug, ) return False return True
def _targetCriteriaAreMet(self): logPrefix = "magic.targetCriteria: Failed - " cName = self.charObj.getName() if self.targetObj.isMagic(): msg = "magical target can not be affected by spell" self.failedReason += msg + "\n" dLog(logPrefix + msg + ", " + cName, self._instanceDebug) return False spellTargets = SpellDict[self.spellName]["spellTargets"] if self.targetObj.getType() not in spellTargets: msg = (self.targetObj.getType() + " is not a valid target for " + "spell " + self.spellName) self.failedReason += msg + "\n" dLog(logPrefix + msg + ", " + cName, self._instanceDebug) return False return True
def cmdloop(self): """ cmd method override - Lobby cmd loop requires user to be authenticated """ stop = False line = "" self.preloop() while not stop: if self.client.promptForCommand(self.getCmdPrompt()): # send/rcv line = self.client.getInputStr() self._lastinput = line dLog("LOBBY cmd = " + line, self.lobbyObj.debug()) self.precmd(line) stop = self.onecmd(line) self.postcmd(stop, line) else: stop = True self.postloop()
def loadPermanents(self): """ Load/instanciate permanents, and add them to the tmp lists """ idsOfPermsInRoom = [x.getItemId() for x in self.getInventory()] permList = self.getPermanentList() for perm in self.getPermanents(permList): permId = perm.getItemId() if permId not in idsOfPermsInRoom: dLog( "loadPermanents: loading perm =" + str(permId), self._instanceDebug ) self.addToInventory(perm) else: dLog( "loadPermanents: perm " + str(permId) + " already exists in room", self._instanceDebug, ) return True
def applyCritOrDD(self, damage, target, notify=[]): logPrefix = "applyCritOrDD: " buf = "" if self.checkForCrit(): buf = "Critical Damage!\n" damage = max(1, damage) * 3 # critical hit dLog(logPrefix + "critical damage (*3)", self._instanceDebug) elif self.checkForDD(target): buf = "Double Damage!\n" damage = max(1, damage) * 2 # double damage dLog(logPrefix + "double damage (*2)", self._instanceDebug) if buf != "": for recipient in notify: if recipient.getType() == "Character": self.charMsg(recipient, buf) return damage
def applyDamage(self, charObj, attacker, target, damage, attackCmd): """ apply damage to target or player """ logPrefix = "combat.applyDamage: " if damage: # Parse attackers/targets to get a set of attack/target words atkDict = self.getattackMsgDict(charObj, attacker, target, attackCmd) # Display the message about the hit self.charMsg(charObj, self.getHitMsg(atkDict, damage)) if target.damageIsLethal(damage): debugMsg = (atkDict["attackerName"] + " does lethal damage to " + atkDict["targetSubject"]) dLog(logPrefix + debugMsg, self._instanceDebug) if isinstance(target, Character): self.applyPlayerDamage(charObj, attacker, damage) else: self.applyCreatureDamage(charObj, target, damage, attackCmd)
def addAttributesToSelf(self, loadedDict, requestedAttNames=[], logStr=""): """ Add attributes to current class object """ logPrefix = "addAttributesToSelf: " if not isinstance(loadedDict, dict): logger.error(logPrefix + "loadedDict is not a dict." + " Its of type " + str(type(loadedDict))) for key, value in zip(loadedDict.keys(), loadedDict.values()): # If specific attributes are requested, skip all attributes that # do not match. if len(requestedAttNames) > 0: if key not in requestedAttNames: continue setattr(self, key, value) buf = "imported " + logStr + "attribute " + key if (isinstance(value, str) or isinstance(value, int) or isinstance(value, list)): buf += "=" + str(value) dLog(logPrefix + " " + buf + "\n", self._debugStorage)
def _promptForChant(self): """ Prompts player for chant. Returns true if chant is correct """ if self.chant == "": # empty chant means none is required return True if self.givenChant == "": prompt = "Enter Chant: " playerInput = self.charObj.client.promptForInput(prompt) else: playerInput = self.givenChant # Test if chant is correct if playerInput.lower() == self.chant.lower(): return True msg = "The divine are unimpressed with your chant" self.failedReason += msg + "\n" dLog("_promptForChant Failed: bad chant", self._instanceDebug) return False
def creatureAttacksPlayer(self, creatureObj, charObj=None): """ single creature attacks a player """ logPrefix = "combat.creatureAttacksPlayer: " if not charObj: charObj = creatureObj.getCurrentlyAttacking() dLog( logPrefix + creatureObj.describe() + " is attacking " + charObj.getName(), self._instanceDebug, ) if not charObj: return False # toDa: right now, creatures are sticky, but they should adhere to # attackLast if charObj != creatureObj.getCurrentlyAttacking(): creatureObj.setCurrentlyAttacking(charObj) # initiate attack if not creatureObj.canAttack(): dLog( logPrefix + creatureObj.getName() + " can't attack " + charObj.getName(), self._instanceDebug, ) return False secs = random.randint(self._creatureMinSecsBetweenAttacks, self._creatureMaxSecsBetweenAttacks) dLog(logPrefix + "setting creature secs to " + str(secs), self._instanceDebug) creatureObj.setSecondsUntilNextAttack(secs) creatureObj.setLastAttack() if self.misses(creatureObj, charObj): # if creature doesn't hit self.charMsg(charObj, creatureObj.describe() + " misses you!\n") # notify other players in the room damage = 0 # see if we need to bump the dodge percentage if creatureObj.getLevel() >= charObj.getLevel(): charObj.rollToBumpSkillForLevel("_dodge", percentChance=3) elif creatureObj.fumbles(): self.charMsg(charObj, creatureObj.describe() + " fumbles!\n") damage = 0 else: # calculate attack damage damage = self.attackDamage(creatureObj, charObj) # reduce charges of armor/shield protection charObj.decreaseChargeOfEquippedProtection() self.applyDamage(charObj, creatureObj, charObj, damage, attackCmd="attack") return None
def cast(self, roomObj): """ Returns true if spell was successfully cast """ logPrefix = "magic.cast: " # Do everything that comes before the spell's affects self._preCastTasks() if not self.charObj.canAttack(): msg = "You can't attack" self.failedReason += msg + "\n" dLog( logPrefix + self.charObj.getName() + " can't attack", self._instanceDebug, ) return False if not self.succeeded: self._castFails() return False self._spoolOut("You cast " + self.spellName + "\n") if self.spellType == "health": self.targetObj.addHP(self.getHealth()) elif self.spellType == "room": self.targetObj.joinRoom(self.getRoom()) elif self.spellType == "damage": self.inflictDamage() elif self.spellType == "alteration": self.alterations() elif self.spellType == "info": self.infos() elif self.spellType == "ask": self.ask() else: self._spoolOut("not implemented yet\n") self._postCastTasks() return True
def setDataFilename(self, dfStr=""): """ sets the data file name. If no datafilename arg is provided, generate the datafile name based on the class and ID. We can also override this method to use a different filename """ id = "" logPrefix = __class__.__name__ + " setDataFilename: " if dfStr == "": # generate the data file name based on class and id try: id = self.getId() except AttributeError: pass if not id: logger.error(logPrefix + "Could not retrieve Id to " + "generate filename") return False if id == "": logger.error(logPrefix + "ID is empty while generating " + "datafile name") return False if hasattr(self, "_fileextension"): extension = self._fileextension else: extension = ".pickle" self._datafile = os.path.abspath(DATADIR + "/" + self.__class__.__name__ + "/" + str(id) + extension) else: # set data file name to name provided self._datafile = os.path.abspath(str(dfStr)) dLog(logPrefix + "_datafile = " + self._datafile, self._debugStorage) return True
def readyForEncounter(self): """ returns true if the room is ready for an encounter """ debugPrefix = "Room readyForEncounter (" + str(self.getId()) + "): " # Room has no encounter time. Will never be ready if not self._encounterRate: dLog(debugPrefix + "Room has no encounter rate", self._instanceDebug) return False # Room has no encounter list. Will never be ready if not self._encounterList: dLog(debugPrefix + "Room has no creatureList", self._instanceDebug) return False # Check if the appropriate amount of time has pased if self._timeOfLastEncounter != getNeverDate(): secsSinceLastEncounter = secsSinceDate(self._timeOfLastEncounter) pctRateAdj = (self._encounterRate - 100) / 100 secsAdj = self._baseEncounterTime * pctRateAdj + random.randint(-5, 5) secsBetweenEncounters = self._baseEncounterTime - secsAdj timeLeft = int(secsBetweenEncounters - secsSinceLastEncounter) if timeLeft > 0: dLog( debugPrefix + "Encounter discarded due to time - " + str(timeLeft) + " secs left", self._instanceDebug, ) return False # % chance that room will have no encounter this time if random.randint(1, 5) == 5: self.setLastEncounter() dLog(debugPrefix + "Encounter randomly discarded", self._instanceDebug) return False dLog(debugPrefix + "Room is ready for encounter", self._instanceDebug) return True
def engageTarget(self, charObj, target): """ Character locks on to creature and creature begins to defend * If both are already fighting each other, do nothing. """ logPrefix = "engageTarget: " if target.getCurrentlyAttacking() != charObj: if not isinstance(target, Character): # creature begins to attack player if target.attacksBack(): target.setLastAttack() secs = int( random.randint( self._creatureMinSecsBetweenAttacks, self._creatureMaxSecsBetweenAttacks, ) / 2) target.setSecondsUntilNextAttack(secs) target.setCurrentlyAttacking(charObj) dLog( logPrefix + target.describe() + " engages " + charObj.describe(), self._instanceDebug, ) # attacker becomes locked on to target if charObj.getCurrentlyAttacking() != target: charObj.setCurrentlyAttacking(target) dLog( logPrefix + charObj.describe() + " engages " + target.describe(), self._instanceDebug, ) self.othersInRoomMsg( charObj, charObj.getRoom(), charObj.getName() + " attacks " + target.describe() + "\n", )
def removeNonPermanents(self, removeTmpPermFlag=False): """ remove any non permanents from inventory """ logPrefix = "removeNonPermanents: " + str(self) + str(self.getItemId()) + ": " itemsInRoom = self.getInventory().copy() if removeTmpPermFlag: dLog( logPrefix + "inv= " + " ,".join( [ x.getItemId() + "(" + str(x.persistsThroughOneRoomLoad()) + ")" for x in itemsInRoom ] ), self._instanceDebug, ) for obj in itemsInRoom: if obj.persistsThroughOneRoomLoad(): if removeTmpPermFlag: # Do not remove object - let it be for one room load, but # remove the property that permits this. obj.setPersistThroughOneRoomLoad(False) dLog( logPrefix + "Preserving tmpPermanent " + str(obj.getItemId()) + " but removing persist flag", self._instanceDebug, ) elif obj.isPermanent(): # Object is permanent. Don't remove it. dLog( logPrefix + "Preserving permanent " + obj.getItemId(), self._instanceDebug, ) else: dLog( logPrefix + "Removing non-permanent " + obj.getItemId(), self._instanceDebug, ) self.removeFromInventory(obj) self.save() return True
def canAttack(self, allowOptOut=True): """ returns true if the creature is ready for an attack """ debugPrefix = "Creature.canAttack (" + str(self.getId()) + "): " # Creature has no attack speed. Will never be ready if not self.getAttackRate(): dLog(debugPrefix + "Creature has no attack rate", self._instanceDebug) return False # Check if the appropriate amount of time has pased timeLeft = int((self.getSecondsUntilNextAttack() / (self.getAttackRate() / 100)) - secsSinceDate(self.getLastAttackDate())) if timeLeft > 0: dLog( debugPrefix + "Attack discarded due to time - " + str(timeLeft) + " secs left", self._instanceDebug, ) return False # % chance that creature does not attack if allowOptOut: randX = random.randint(1, 10) if randX == 1: self.setLastAttackDate() dLog( debugPrefix + "Creature randomly chose not to attack (" + str(randX) + " = 1)", self._instanceDebug, ) return False dLog(debugPrefix + "Creature is ready for attack", self._instanceDebug) return True
def attackDamage(self, attackerObj, opponentObj, attackCmd="attack"): """ determine the amount of damage dealt by a creature or characters attack """ logPrefix = "combat.AttackDamage: " dLog( logPrefix + "Calculating attackDamage for " + attackerObj.describe() + " attacking " + opponentObj.describe() + " with cmd " + attackCmd + "...", self._instanceDebug, ) weaponDamage = attackerObj.getEquippedWeaponDamage() damagePercent = int( self.calcDmgPct(attackerObj, opponentObj, attackCmd=attackCmd) / 100) damage = weaponDamage * damagePercent dLog( logPrefix + "weapon damage(" + str(weaponDamage) + ") * damagePercent(" + str(damagePercent) + ") = preAcDamage(" + str(damage) + ")", self._instanceDebug, ) damage = opponentObj.acDamageReduction(damage) # check for crit or double damage damage = self.applyCritOrDD(damage, opponentObj, [attackerObj, opponentObj]) damage = int(damage) dLog(logPrefix + "Total damage(" + str(damage) + ")", self._instanceDebug) return damage
def setSpellAttributes(self): """ Set this instance's attributes based on the particular spell being cast and the corresponding values in SpellDict. * Special considerations: - levelRequired - set based on character's className - formulas, we execute them and store the result as an attribute. We get the attribute name from the formulaResultAtt attribute. Overly complicated? Maybe, but the object comes out looking clean. """ for att in SpellDict[self.spellName].keys(): if att == "levelRequired": className = self.charObj.getClassName() setattr(self, att, SpellDict[self.spellName][att][className]) elif att == "formula": attName = SpellDict[self.spellName]["formulaResultAtt"] if attName == "": continue # execute formula and stick it in an attribute dLog("magic: formula = " + att, self._instanceDebug) dLog( "magic: level=" + str(self.charObj.getLevel()) + " - Int=" + str(self.charObj.getIntelligence()), self._instanceDebug, ) setattr(self, attName, eval(SpellDict[self.spellName][att])) dLog( "magic: spell=" + self.spellName + " - damage=" + str(getattr(self, attName)), self._instanceDebug, ) elif att == "formulaResultAtt": # This is used for formula results, so don't set it as att continue else: setattr(self, att, SpellDict[self.spellName][att])
def save(self, logStr=""): """ save to persistant storage """ if logStr != "": logStr += " " # append a space for easy logging self.setDataFilename() # We are about to muck with the object data. Before we do, store # the datafile info as a local variable so that there is no conflict. filename = self._datafile logPrefix = self.__class__.__name__ + " save: " if filename == "": logger.error(logPrefix + "Could not determine filename " + " while saving " + logStr + str(self.getId())) return False if not self.isValid(): # if the instance we are saving is not valid logger.error(logPrefix + "Save aborted - " + logStr + str(self.getId()) + " is not valid") return False # create directory path = Path(os.path.dirname(self._datafile)) path.mkdir(parents=True, exist_ok=True) # some attributes should not be, or can not be pickled, so we # store the values before we save, then we will restore them # immediately after. tmpStore = {} for attName in self.getAttributesThatShouldntBeSaved() + [ "_datafile", "_instanceDebug", ]: try: tmpStore[attName] = getattr(self, attName) if hasattr(self, attName): delattr(self, attName) dLog( logPrefix + "Ignoring " + attName + " during save", self._debugStorage, ) except AttributeError: dLog( logPrefix + "Could not ignore " + attName + " during save", self._debugStorage, ) # persist content - create data file if re.search("\\.json$", filename): self.writeJSonFile(filename) else: self.writePickleFile(filename) # Restore attributes that we temporarily set aside when saving. for attName in tmpStore.keys(): setattr(self, attName, tmpStore[attName]) dLog( logPrefix + "saved " + logStr + " - " + str(self.getId()), self._debugStorage, ) return True