def load(filename, req_book=None): """ Static method to load a map file. This will open the file once and read in a bit of data to determine which Eschalon game the mapfile comes from, and calls the appropriate constructor to return the object. If req_book is passed in, it will raise a LoadException if the detected Book number doesn't match. This will also raise a LoadException if it's unable to determine the version (generally due to being passed something that's not a map file). Note that this method does not actually read in the entire map file. It does "preload" the map name, however, so that it can be easily referenced in lists. Use .read() on the resulting map object to actually read in the map data. """ # Get some information about the filename (detected_book, detected_mapname, df) = Map.get_mapinfo(filename) # See if we're required to conform to a specific book if (req_book is not None and detected_book != req_book): raise LoadException('This utility can only load Book %d map files; this file is from Book %d' % (req_book, detected_book)) # Now actually return the object if detected_book == 1: c.switch_to_book(1) return B1Map(df) elif detected_book == 2: c.switch_to_book(2) return B2Map(df) elif detected_book == 3: c.switch_to_book(3) return B3Map(df) else: raise LoadException('Unknown book version found for "%s"; perhaps it is not an Eschalon map file' % (filename))
def new(filename, book, map_df=None, ent_df=None): """ Sets up a new, blank Map object with the given book. Will raise a LoadException if we're passed a book we don't know about. Optionally pass in a datafile object to load our data from. """ if map_df is None: df = Savefile(filename) else: df = map_df if book == 1: c.switch_to_book(1) return B1Map(df, ent_df) elif book == 2: c.switch_to_book(2) return B2Map(df, ent_df) elif book == 3: c.switch_to_book(3) return B3Map(df, ent_df) else: raise LoadException('Unknown book version specified: %d' % (book))
def load(filename, req_book=None): """ Static method to load a map file. This will open the file once and read in a bit of data to determine which Eschalon game the mapfile comes from, and calls the appropriate constructor to return the object. If req_book is passed in, it will raise a LoadException if the detected Book number doesn't match. This will also raise a LoadException if it's unable to determine the version (generally due to being passed something that's not a map file). Note that this method does not actually read in the entire map file. It does "preload" the map name, however, so that it can be easily referenced in lists. Use .read() on the resulting map object to actually read in the map data. """ # Get some information about the filename (detected_book, detected_mapname, df) = Map.get_mapinfo(filename) # See if we're required to conform to a specific book if (req_book is not None and detected_book != req_book): raise LoadException( 'This utility can only load Book %d map files; this file is from Book %d' % (req_book, detected_book)) # Now actually return the object if detected_book == 1: c.switch_to_book(1) return B1Map(df) elif detected_book == 2: c.switch_to_book(2) return B2Map(df) elif detected_book == 3: c.switch_to_book(3) return B3Map(df) else: raise LoadException( 'Unknown book version found for "%s"; perhaps it is not an Eschalon map file' % (filename))
class Savename(object): book = None def __init__(self, df): """ A fresh object. """ self.savename = '' self.savedate = '' self.savetime = '' self.mapname = '' self.totalsecs = 0 self.totalturns = 0 self.totaldays = 0 self.coloration = 0 self.narratives = [] # Has the player seen this narrative? 0=no, 1=yes self.quests = [] self.npcs = [] # Has the player talked to this NPC? 0=no, 1=yes self.quicktravel = 0 self.options = [] # volume controls, tactical grid, etc. self.unknowns = [] self.df = df def replicate(self): newsn = Savename.load(self.df.filename, self.book) if self.book == 1: newsn = B1Savename(Savefile(self.df.filename)) elif self.book == 2: newsn = B2Savename(Savefile(self.df.filename)) elif self.book == 3: newsn = B3Savename(Savefile(self.df.filename)) # Single vals (no need to do actual replication) newsn.savename = self.savename newsn.savedate = self.savedate newsn.savetime = self.savetime newsn.mapname = self.mapname newsn.totalsecs = self.totalsecs newsn.totalturns = self.totalturns newsn.totaldays = self.totaldays newsn.coloration = self.coloration newsn.quicktravel = self.quicktravel # Lists that need copying for val in self.options: newsn.options.append(val) for val in self.narratives: newsn.narratives.append(val) for val in self.quests: newsn.quests.append(val) for val in self.npcs: newsn.npcs.append(val) # Call out to the subclass replication function self._sub_replicate(newsn) # Now return our duplicated object return newsn def _sub_replicate(self, newsn): """ Just a stub function for superclasses to override, to replicate any superclass-specific data """ pass def write(self): raise NotImplementedError( 'Writing savenames is not currently supported') @staticmethod def load(filename, book=None, req_book=None): """ Static method to load a savename file. This will open the file once and read in a bit of data to determine whether this is a Book 1 character file or a Book 2 character file, and then call the appropriate constructor and return the object. The individual Book constructors expect to be passed in an """ df = Savefile(filename) # First figure out what format to load, if needed if book is None: try: df.open_r() name = df.readstr() date = df.readstr() time = df.readstr() map_or_version = df.readstr() df.close() except (IOError, struct.error), e: raise LoadException(str(e)) if map_or_version.startswith('book3'): book = 3 elif map_or_version in B1Constants.maps: book = 1 else: book = 2 # See if we're required to conform to a specific book if (req_book is not None and book != req_book): raise LoadException( 'This utility can only load Book %d Character files; this file is from Book %d' % (req_book, book)) # Now actually return the object if book == 1: c.switch_to_book(1) return B1Savename(df) elif book == 2: c.switch_to_book(2) return B2Savename(df) else: c.switch_to_book(3) return B3Savename(df)
if not Map.is_ascii(strings[9]): book = 2 elif not Map.is_ascii(strings[10]): book = 1 else: book = 3 # See if we're required to conform to a specific book if req_book is not None and book != req_book: raise LoadException( "This utility can only load Book %d map files; this file is from Book %d" % (req_book, book) ) # Now actually return the object if book == 1: c.switch_to_book(1) return B1Map(df) elif book == 2: c.switch_to_book(2) return B2Map(df) elif book == 3: c.switch_to_book(3) return B3Map(df) class B1Map(Map): """ Book 1 Map definitions """ book = 1
class Character(object): """ The base Character class. Interestingly, some items which are NOT stored in the char file: * Which map the character's currently on (orientation/position ARE stored here though) * Time of day in the game world * Total time spent playing the game Note that the base class does not define read() and write() methods, which are left up to the specific Book classes (the theory being that the actual underlying formats can be rather different, and it doesn't really make sense to try and work around that. """ book = None form_elements = [] def __init__(self, df): """ A fresh object. """ #self.book = c.book self.name = '' self.strength = -1 self.dexterity = -1 self.endurance = -1 self.speed = -1 self.intelligence = -1 self.wisdom = -1 self.perception = -1 self.concentration = -1 self.skills = {} self.maxhp = -1 self.maxmana = -1 self.curhp = -1 self.curmana = -1 self.experience = -1 self.level = -1 self.gold = -1 self.torches = -1 self.torchused = -1 self.readyslots = [] self.inventory = [] for i in range(self.inv_rows): self.inventory.append([]) for j in range(self.inv_cols): self.inventory[i].append(Item.new(c.book)) self.readyitems = [] for i in range(self.ready_rows * self.ready_cols): self.readyitems.append(Item.new(c.book)) self.curinvcol = 0 self.curinvrow = 0 self.quiver = Item.new(c.book) self.helm = Item.new(c.book) self.cloak = Item.new(c.book) self.amulet = Item.new(c.book) self.torso = Item.new(c.book) self.weap_prim = Item.new(c.book) self.belt = Item.new(c.book) self.gauntlet = Item.new(c.book) self.legs = Item.new(c.book) self.ring1 = Item.new(c.book) self.ring2 = Item.new(c.book) self.shield = Item.new(c.book) self.feet = Item.new(c.book) self.spells = [] self.orientation = -1 self.xpos = -1 self.ypos = -1 self.fxblock = [] self.picid = -1 self.statuses = [] self.extra_att_points = -1 self.extra_skill_points = -1 self.df = df def set_inv_size(self, rows, cols, ready_rows, ready_cols): """ Sets the size of the inventory array """ self.inv_rows = rows self.inv_cols = cols self.ready_rows = ready_rows self.ready_cols = ready_cols def replicate(self): newchar = Character.load(self.df.filename, self.book) if self.book == 1: newchar = B1Character(Savefile(self.df.filename)) elif self.book == 2: newchar = B2Character(Savefile(self.df.filename)) elif self.book == 3: newchar = B3Character(Savefile(self.df.filename)) # Single vals (no need to do actual replication) #newchar.book = self.book newchar.inv_rows = self.inv_rows newchar.inv_cols = self.inv_cols newchar.name = self.name newchar.strength = self.strength newchar.dexterity = self.dexterity newchar.endurance = self.endurance newchar.speed = self.speed newchar.intelligence = self.intelligence newchar.wisdom = self.wisdom newchar.perception = self.perception newchar.concentration = self.concentration newchar.maxhp = self.maxhp newchar.maxmana = self.maxmana newchar.curhp = self.curhp newchar.curmana = self.curmana newchar.experience = self.experience newchar.level = self.level newchar.gold = self.gold newchar.torches = self.torches newchar.torchused = self.torchused newchar.curinvcol = self.curinvcol newchar.curinvrow = self.curinvrow newchar.orientation = self.orientation newchar.xpos = self.xpos newchar.ypos = self.ypos newchar.picid = self.picid newchar.extra_att_points = self.extra_att_points newchar.extra_skill_points = self.extra_skill_points # Lists that need copying for val in self.spells: newchar.spells.append(val) for val in self.fxblock: newchar.fxblock.append(val) for val in self.statuses: newchar.statuses.append(val) # More complex lists that need copying for val in self.readyslots: newchar.readyslots.append([val[0], val[1]]) # Dicts that need copying for key, val in self.skills.iteritems(): newchar.skills[key] = val # Objects that need copying for i in range(self.inv_rows): for j in range(self.inv_cols): newchar.inventory[i][j] = self.inventory[i][j].replicate() for i in range(self.ready_rows * self.ready_cols): newchar.readyitems[i] = self.readyitems[i].replicate() newchar.quiver = self.quiver.replicate() newchar.helm = self.helm.replicate() newchar.cloak = self.cloak.replicate() newchar.amulet = self.amulet.replicate() newchar.torso = self.torso.replicate() newchar.weap_prim = self.weap_prim.replicate() newchar.belt = self.belt.replicate() newchar.gauntlet = self.gauntlet.replicate() newchar.legs = self.legs.replicate() newchar.ring1 = self.ring1.replicate() newchar.ring2 = self.ring2.replicate() newchar.shield = self.shield.replicate() newchar.feet = self.feet.replicate() # Call out to the subclass replication function self._sub_replicate(newchar) # Now return our duplicated object return newchar def _sub_replicate(self, newchar): """ Just a stub function for superclasses to override, to replicate any superclass-specific data """ pass def setGold(self,goldValue): """ Alter gold to new amount. """ self.gold = goldValue def setMaxMana(self,manaValue): """ Alter max mana value & set current to max. Note that equipped-item modifiers will raise the actual in-game maximums. """ self.maxmana = manaValue if (self.curmana < manaValue): self.setCurMana(manaValue) def setCurMana(self,manaValue): """ Replenish mana to input value. """ self.curmana = manaValue def setMaxHp(self,hpValue): """ Alter max HP & set current to max. Note that equipped-item modifiers will raise the actual in-game maximums. """ self.maxhp = hpValue if (self.curhp < hpValue): self.setCurHp(hpValue) def setCurHp(self,hpValue): """ Replenish HP to input value. """ self.curhp = hpValue def clearDiseases(self): """ Clear all diseases. Also clears out severe injuries/curses/etc on Book 2 chars """ if self.book == 1: self.disease = 0x0000 else: self.permstatuses = self.permstatuses & 0xFFFF0000 def resetHunger(self): """ Resets hunger and thirst; only valid for Book 2 characters, of course. """ if self.book > 1: self.hunger = 1000 self.thirst = 1000 def addskill(self, skillnum, level): """ Add a new skill at a given level. """ self.skills[skillnum] = level def addreadyslot(self, spell, level): """ Add a new spell to a 'ready' slot. """ self.readyslots.append([spell, level]) def additem(self): """ Add a new item, assuming that the items are stored in a left-to-right, top-to-bottom format on the inventory screen. """ self.inventory[self.curinvrow][self.curinvcol].read(self.df) self.curinvcol = self.curinvcol + 1 if (self.curinvcol == self.inv_cols): self.curinvcol = 0 self.curinvrow = self.curinvrow + 1 @staticmethod def load(filename, book=None, req_book=None): """ Static method to load a character file. This will open the file once and read in a bit of data to determine whether this is a Book 1 character file or a Book 2 character file, and then call the appropriate constructor and return the object. The individual Book constructors expect to be passed in an """ df = Savefile(filename) # First figure out what format to load, if needed if book is None: # The initial "zero" padding in Book 1 is four bytes, and only one byte in # Book 2. Since the next bit of data is the character name, as a string, # if the second byte of the file is 00, we'll assume that it's a Book 1 file, # and Book 2 otherwise. try: df.open_r() initital = df.readuchar() second = df.readuchar() df.close() except (IOError, struct.error), e: raise LoadException(str(e)) if second == 0: book = 1 else: book = 2 # See if we're required to conform to a specific book if (req_book is not None and book != req_book): raise LoadException('This utility can only load Book %d Character files; this file is from Book %d' % (req_book, book)) # Now actually return the object if book == 1: c.switch_to_book(1) return B1Character(df) elif book == 2: c.switch_to_book(2) return B2Character(df) else: c.switch_to_book(3) return B3Character(df)