Load and Save work correctly now. It was a problem with endianess. I'm not sure...
[asgard.git] / fighter.py
blobb9fa5b032dabf9358101075d5d77633b8f720ae7
1 ########################################################
2 #Copyright (c) 2006 Russ Adams, Sean Eubanks, Asgard Contributors
3 #This file is part of Asgard.
5 #Asgard is free software; you can redistribute it and/or modify
6 #it under the terms of the GNU General Public License as published by
7 #the Free Software Foundation; either version 2 of the License, or
8 #(at your option) any later version.
10 #Asgard is distributed in the hope that it will be useful,
11 #but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 #GNU General Public License for more details.
15 #You should have received a copy of the GNU General Public License
16 #along with Asgard; if not, write to the Free Software
17 #Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 ########################################################
19 from random import *
20 from equiption import *
21 from event import *
22 from status import *
23 from stat import *
24 from statistic import *
25 import libxml2
26 import struct
27 from utilities import *
28 from struct import pack
29 class Fighter:
31 def __init__(self,fTree=None,binary=None):
32 """ Construct a Fighter object from a job type XML tree and either a fighter type XML tree or packed binary data. """
34 # If there was a fighter tree specified
35 if fTree != None:
36 # Fighter members
37 self.__name = fTree.xpathEval("//name")[0].content
38 self.__playable = (fTree.xpathEval("//playable")[0].content == "True")
39 self.__jobType = fTree.xpathEval("//job")[0].content
41 # Find correct job XML file
42 j = fTree.xpathEval("//job")[0].content + '.xml'
44 # Create Job XML Tree
45 jTree = libxml2.parseFile('./data/job/'+j)
47 # Equiption
48 head = fTree.xpathEval("//equiption/head")[0].content
49 body = fTree.xpathEval("//equiption/body")[0].content
50 left = fTree.xpathEval("//equiption/left")[0].content
51 right = fTree.xpathEval("//equiption/right")[0].content
52 self.__equiption = Equiption(head,body,left,right)
54 #Statistics
55 self.__stats = []
56 statTree = fTree.xpathEval("//stat")
57 for statElem in statTree:
59 current = 0
60 afterBattle = False
62 # The following might should be moved to the Statistics Constructor in the future
63 sname = statElem.prop("name")
65 afterBattle = (statElem.prop("afterbattle") == "True")
67 if statElem.prop("current") != None:
68 current = int(statElem.prop("current"))
70 if statElem.prop("max") != None:
71 max = int(statElem.prop("max"))
73 if afterBattle == False and statElem.prop("current") == None:
74 current = max
76 (plus,chance,growthRate) = jobStatQuery(jTree, sname)
78 stat = Statistic(sname,current,max,afterBattle,plus,chance,growthRate)
79 self.__stats.append(stat)
81 elementTree = fTree.xpathEval("//modifiers/element")
82 statusTree = fTree.xpathEval("//modifiers/status")
83 self.__elemMod = []
84 for elementNode in elementTree:
85 self.__elemMod.append((elementNode.prop("name"),elementNode.prop("mod")))
87 self.__statusMod = []
88 for statusNode in statusTree:
89 self.__statusMod.append((statusNode.prop("name"),statusNode.prop("chance")))
91 jTree.freeDoc()
92 fTree.freeDoc()
93 elif binary != None:
95 # for the sake of more legible code
96 # break it up
98 # unpack data
100 (name,job,head,body,right,left,level,exp,exptnl_current,exptnl_max,hp_current,hp_max,mp_current,mp_max,strg,dex,agl,spd,wis,wil) = struct.unpack("!20s20s20s20s20s20sB7I6B",binary)
102 #These might be useful later.
103 #print "Name:" + name
104 #print "Job" + job
105 #print "Head" + head
106 #print "Body" + body
107 #print "Right" + right
108 #print "Left" + left
109 #print "Level" + str(level)
110 #print "Exp" + str(exp)
111 #print "Exptnl_current" + str(exptnl_current)
112 #print "Exptnl_max" + str(exptnl_max)
113 #print "Hp_Current" + str(hp_current)
114 #print "Hp_Max" + str(hp_max)
115 #print "Mp_Current" + str(mp_current)
116 #print "Mp_Max" + str(mp_max)
117 #print "Str" + str(strg)
118 #print "Dex" + str(dex)
119 #print "Agl" + str(agl)
120 #print "Spd" + str(spd)
121 #print "Wis" + str(wis)
122 #print "Wil" + str(wil)
124 # Meta Data
125 self.__name = name.strip('\0')
126 self.__jobType = job.strip('\0')
127 self.__playable = True
129 # Create Job XML Tree
130 jTree = libxml2.parseFile('./data/job/'+self.__jobType+'.xml')
132 # Equiption
133 self.__equiption = Equiption(head.strip('\0'), body.strip('\0'), left.strip('\0'), right.strip('\0'))
135 #Stats
136 self.__stats = []
137 (plus,chance,growthRate) = jobStatQuery(jTree, "level")
138 self.__stats.append(Statistic("level",level,level,False,plus,chance,growthRate))
140 (plus,chance,growthRate) = jobStatQuery(jTree, "exp")
141 self.__stats.append(Statistic("exp",level,level,False,plus,chance,growthRate))
143 (plus,chance,growthRate) = jobStatQuery(jTree, "exptnl")
144 self.__stats.append(Statistic("exptnl",exptnl_current,exptnl_max,True,plus,chance,growthRate))
146 (plus,chance,growthRate) = jobStatQuery(jTree, "hp")
147 self.__stats.append(Statistic("hp",hp_current,hp_max,True,plus,chance,growthRate))
149 (plus,chance,growthRate) = jobStatQuery(jTree, "mp")
150 self.__stats.append(Statistic("mp",mp_current,mp_max,True,plus,chance,growthRate))
152 (plus,chance,growthRate) = jobStatQuery(jTree, "str")
153 self.__stats.append(Statistic("str",strg,strg,False,plus,chance,growthRate))
155 (plus,chance,growthRate) = jobStatQuery(jTree, "dex")
156 self.__stats.append(Statistic("dex",dex,dex,False,plus,chance,growthRate))
158 (plus,chance,growthRate) = jobStatQuery(jTree, "agl")
159 self.__stats.append(Statistic("agl",agl,agl,False,plus,chance,growthRate))
161 (plus,chance,growthRate) = jobStatQuery(jTree, "spd")
162 self.__stats.append(Statistic("spd",spd,spd,False,plus,chance,growthRate))
164 (plus,chance,growthRate) = jobStatQuery(jTree, "wis")
165 self.__stats.append(Statistic("wis",wis,wis,False,plus,chance,growthRate))
167 (plus,chance,growthRate) = jobStatQuery(jTree, "wil")
168 self.__stats.append(Statistic("wil",wis,wis,False,plus,chance,growthRate))
170 jTree.freeDoc()
172 self.__elemMod = []
173 self.__statusMod = []
175 # tom calculation
176 spd = self.getStat("spd").getCurrent()
177 tom = round((1/float(spd)*100))
178 self.__stats.append(Statistic("tom",tom,tom,False,0,0,0))
180 # set canmove to true
181 self.__stats.append(Statistic("canMove",1,1,False,0,0,0))
183 # set fighter ai
184 if self.__jobType == 'monster':
185 self.__stats.append(Statistic("ai",1,1,False,0,0,0))
186 else:
187 self.__stats.append(Statistic("ai",0,0,False,0,0,0))
189 # initialize statuses list
190 self.__status = []
192 def toBinaryString(self):
193 """Convert Fighter data to binary string."""
194 binStr = pack("!B20s20s20s20s20s20sB7I6B",0,self.__name,self.__jobType,self.__equiption.getHead(),self.__equiption.getArmor(),self.__equiption.getRight(),self.__equiption.getLeft(),self.getStat("level").getMax(),self.getStat("exp").getCurrent(),self.getStat("exptnl").getCurrent(),self.getStat("exptnl").getMax(),self.getStat("hp").getCurrent(),self.getStat("hp").getMax(),self.getStat("mp").getCurrent(),self.getStat("mp").getMax(),self.getStat("str").getMax(),self.getStat("dex").getMax(),self.getStat("agl").getMax(),self.getStat("spd").getMax(),self.getStat("wis").getMax(),self.getStat("wil").getMax())
195 return binStr
197 def getName(self):
198 """Get fighter's name."""
199 return self.__name
201 def setName(self, n):
202 """Change fighter's name."""
203 self.__name = n
205 def getJobType(self):
206 """Get Fighter's job type"""
207 return self.__jobType
209 def setJobType(self,jobType):
210 """ Set Fighter's job type"""
211 self.__jobType = jobType
213 def getEquiption(self):
214 """Get equiption for fighter."""
215 return self.__equiption
217 def setEquiption(self, eq):
218 """Set equiption for fighter."""
219 self.__equiption = eq
221 def getPlayable(self):
222 """Is fighter playable?"""
223 return self.__playable
225 def setPlayable(self, plyble):
226 """Set fighter's playability."""
227 self.__playable = plyble
229 def getAttack(self):
230 """Compute value of attack."""
231 return self.getStat("str").getCurrent() + self.__equiption.computeAtkMod()
233 def getDefense(self):
234 """Compute value of defense."""
235 return self.__equiption.computeDefMod()
237 def getEvade(self):
238 """Compute value of evade."""
239 return self.getStat("agl").getCurrent() + self.__equiption.computeEvaMod()
242 def makeEvent(self, eparty, fparty, currentTime, cont):
243 """ Check if ready to make move.
245 If so, select random fighter from enemy party and return.
247 if self.getStat("tom").getCurrent() != currentTime:
248 return None
251 etypes = cont.getBattle().getEventTypeList()
253 # Can fighter move? (asleep?, paralysed?, dead?)
255 if self.getStat("canMove").getCurrent() != 1:
256 return None
259 # Generate per-turn status transactions
260 for s in self.__status:
261 s.genPerTurn()
263 d = []
265 # Get ai of fighter
266 ai = self.getStat("ai").getCurrent()
268 # Playable fighters (ai=0) have battle menu
269 if ai == 0:
270 # Show battle menu and choose event from battle menu
271 command = cont.getBattleCommand(eparty,fparty,self)
272 d.append(command[0])
273 t = command[1]
275 # Monsters
276 elif ai == 1:
277 x = randint(0,eparty.sizeOfParty()-1)
278 while not eparty.getFighter(x).isAlive():
279 x = randint(0,eparty.sizeOfParty()-1)
281 d.append(eparty.getFighter(x))
283 x = randint(0,len(self.getEventTypes(etypes))-1)
284 t = cont.getBattle().getEventType(self.getEventTypes(etypes)[x])
285 # Madness (attack whatever!)
286 elif ai == 2:
287 x = randint(0,eparty.sizeOfParty()-1)
288 y = randint(0,fparty.sizeOfParty()-1)
289 while not eparty.getFighter(x).isAlive():
290 x = randint(0,eparty.sizeOfParty()-1)
291 while not fparty.getFighter(y).isAlive():
292 y = randint(0,fparty.sizeOfParty()-1)
293 # randint needs a range from SMALL to BIG
294 if x <= y:
295 target = randint(x,y)
296 elif x > y:
297 target = randint(y,x)
298 # Attacking friendly
299 if target == y:
300 d.append(fparty.getFighter(y))
301 # Attacking enemy
302 elif target == x:
303 d.append(eparty.getFighter(x))
305 x = randint(0,len(self.getEventTypes(etypes))-1)
306 t = cont.getBattle().getEventType(self.getEventTypes(etypes)[x])
308 return Event(self,d,[],t)
310 def isAlive(self):
311 """If hp is greater than 0... return true"""
312 return (self.getStat("hp").getCurrent() > 0)
314 def getEventTypes(self,eventTypes):
315 """Return list of events available to fighter."""
316 etypes = []
318 if self.__jobType == "blackmage":
319 etypes = ["attack","fire1","ice1","lit1","poison","sleep","rabies","quit"]
320 elif self.__jobType == "swordsman":
321 etypes = ["attack","cure1","heal","life","fog","harm","quit"]
322 elif self.__name == "Roc":
323 etypes = ["attack","lit1"]
324 elif self.__name == "Imp":
325 etypes = ["attack"]
326 elif self.__name == "Briaru":
327 etypes = ["attack","chokeout","sleep"]
328 elif self.__name == "Wolf":
329 etypes = ["attack","rabies"]
330 else:
331 etypes = ["attack"]
333 # remove the event types that don't have enough mp.
334 found = True
335 while found:
336 found = False
337 for s_etype in etypes:
338 for o_etype in eventTypes:
339 if o_etype.getName() == s_etype:
340 if o_etype.getMpCost() > self.getStat("mp").getCurrent():
341 etypes.remove(s_etype)
342 found = True
343 break
345 return etypes
348 def receiveExp(self,exp):
349 """Fighter receives experience for leveling up."""
350 # Add on exp
351 exp_s = self.getStat("exp")
352 exp_s.setCurrent(exp_s.getCurrent() + exp)
353 exp_s.setMax(exp_s.getCurrent())
355 # Tax off exp from expTillNextLevel
356 exptnl_s = self.getStat("exptnl")
357 exptnl_s.setCurrent(exptnl_s.getCurrent() - exp)
359 # Level up?
360 while exptnl_s.getCurrent() <= 0:
361 # Increase level number
362 level_s = self.getStat("level")
363 level_s.setCurrent(level_s.getCurrent() + 1)
365 ## Increase stats...
367 # Increase hp?
368 x = randint(1,100)
369 hp = self.getStat("hp")
370 if x <= hp.getChance() * 100:
371 print "test1"
372 hp.setMax(hp.getMax() + hp.getPlus())
374 # Increase strength?
375 x = randint(1,100)
376 str = self.getStat("str")
377 if x <= str.getChance() * 100:
378 str.setMax(str.getMax() + str.getPlus())
379 str.setCurrent(str.getMax())
382 # Increase speed?
383 x = randint(1,100)
384 spd = self.getStat("spd")
385 if x <= spd.getChance() * 100:
386 spd.setMax(spd.getMax() + spd.getPlus())
387 spd.setCurrent(spd.getMax())
389 # Increase dexterity?
390 x = randint(1,100)
391 dex = self.getStat("dex")
392 if x <= dex.getChance() * 100:
393 dex.setMax(dex.getMax() + dex.getPlus())
394 dex.setCurrent(dex.getMax())
396 # Increase agility?
397 x = randint(1,100)
398 agl = self.getStat("agl")
399 if x <= agl.getChance() * 100:
400 agl.setMax(agl.getMax() + agl.getPlus())
401 agl.setCurrent(agl.getMax())
403 # Increase wisdom?
404 x = randint(1,100)
405 wis = self.getStat("wis")
406 if x <= wis.getChance() * 100:
407 wis.setMax(wis.getMax() + wis.getPlus())
408 wis.setCurrent(wis.getMax())
410 # Increase will?
411 x = randint(1,100)
412 wil = self.getStat("wil")
413 if x <= wil.getChance() * 100:
414 wil.setMax(wil.getMax() + wil.getPlus())
415 wil.setCurrent(wil.getMax())
417 # Set new exp till next level maximum
418 exptnl_s.setMax(int(exptnl_s.getMax() * (1 + exptnl_s.getGrowthRate())))
420 # Carry over any experience that didn't go towards getting the last level
421 exptnl_s.setCurrent(int(exptnl_s.getMax() + exptnl_s.getCurrent()))
423 def getElemModifier(self,elem):
424 for elemMod in self.__elemMod:
425 if elem == elemMod[0]:
426 return elemMod[1]
428 return 0.0
430 def getStatusModifier(self,stat):
431 for statusMod in self.__statusMod:
432 if stat == statusMod[0]:
433 return statusMod[1]
435 return 1.0
437 def getStatus(self):
438 """Get status(es) of fighter."""
439 return self.__status
441 def setStatus(self,s):
442 """Set status(es) of fighter."""
443 self.__status = s
445 def addStatus(self,s):
446 """Add a status to status list for fighter."""
447 found = False
448 for i in self.__status:
449 if i.getName() == s:
450 found = True
451 break
453 if not found:
454 self.__status.append(s)
455 s.genEntrance()
456 else:
457 print 'ERROR: Status already in list!'
459 def removeStatus(self,s):
460 """Remove statuses from status list for fighter."""
461 found = False
462 for i in self.__status:
463 if i.getName() == s:
464 found = True
465 break
467 if found == True:
468 i.genExit()
469 self.__status.remove(i)
470 else:
471 print 'ERROR: Status was not in list!'
473 def getStat(self,statName):
474 for stat in self.__stats:
475 if stat.getName() == statName:
476 return stat
477 print 'ERROR: No such Fighter stat.'
479 def getAllStats(self):
480 return self.__stats
482 def setAllStats(self,stats):
483 self.__stats = stats