Mad fighters cannot choose Quit option in battle.
[asgard.git] / fighter.py
blob9b85b189420e2154b971a052ec26db6c36ce854c
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 # fighter cannot choose QUIT while mad (only for playable fighters)
306 if self.__jobType != "monster":
307 x = randint(0,len(self.getEventTypes(etypes))-2)
308 else:
309 x = randint(0,len(self.getEventTypes(etypes))-1)
310 t = cont.getBattle().getEventType(self.getEventTypes(etypes)[x])
312 return Event(self,d,[],t)
314 def isAlive(self):
315 """If hp is greater than 0... return true"""
316 return (self.getStat("hp").getCurrent() > 0)
318 def getEventTypes(self,eventTypes):
319 """Return list of events available to fighter."""
320 etypes = []
322 if self.__jobType == "blackmage":
323 etypes = ["attack","fire1","ice1","lit1","poison","sleep","rabies","quit"]
324 elif self.__jobType == "swordsman":
325 etypes = ["attack","cure1","heal","life","fog","harm","quit"]
326 elif self.__name == "Roc":
327 etypes = ["attack","lit1"]
328 elif self.__name == "Imp":
329 etypes = ["attack"]
330 elif self.__name == "Briaru":
331 etypes = ["attack","chokeout","sleep"]
332 elif self.__name == "Wolf":
333 etypes = ["attack","rabies"]
334 elif self.__name == "Griffiend":
335 etypes = ["attack","fright"]
336 elif self.__name == "Stonos":
337 etypes = ["attack"]
338 elif self.__name == "Slithera":
339 etypes = ["attack"]
340 else:
341 etypes = ["attack"]
343 # remove the event types that don't have enough mp.
344 found = True
345 while found:
346 found = False
347 for s_etype in etypes:
348 for o_etype in eventTypes:
349 if o_etype.getName() == s_etype:
350 if o_etype.getMpCost() > self.getStat("mp").getCurrent():
351 etypes.remove(s_etype)
352 found = True
353 break
355 return etypes
358 def receiveExp(self,exp):
359 """Fighter receives experience for leveling up."""
360 # Add on exp
361 exp_s = self.getStat("exp")
362 exp_s.setCurrent(exp_s.getCurrent() + exp)
363 exp_s.setMax(exp_s.getCurrent())
365 # Tax off exp from expTillNextLevel
366 exptnl_s = self.getStat("exptnl")
367 exptnl_s.setCurrent(exptnl_s.getCurrent() - exp)
369 # Level up?
370 while exptnl_s.getCurrent() <= 0:
371 # Increase level number
372 level_s = self.getStat("level")
373 level_s.setMax(level_s.getMax() + 1)
374 level_s.setCurrent(level_s.getMax())
376 ## Increase stats...
378 # Increase hp?
379 x = randint(1,100)
380 hp = self.getStat("hp")
381 if x <= hp.getChance() * 100:
382 hp.setMax(hp.getMax() + hp.getPlus())
384 # Increase strength?
385 x = randint(1,100)
386 str = self.getStat("str")
387 if x <= str.getChance() * 100:
388 str.setMax(str.getMax() + str.getPlus())
389 str.setCurrent(str.getMax())
392 # Increase speed?
393 x = randint(1,100)
394 spd = self.getStat("spd")
395 if x <= spd.getChance() * 100:
396 spd.setMax(spd.getMax() + spd.getPlus())
397 spd.setCurrent(spd.getMax())
399 # Increase dexterity?
400 x = randint(1,100)
401 dex = self.getStat("dex")
402 if x <= dex.getChance() * 100:
403 dex.setMax(dex.getMax() + dex.getPlus())
404 dex.setCurrent(dex.getMax())
406 # Increase agility?
407 x = randint(1,100)
408 agl = self.getStat("agl")
409 if x <= agl.getChance() * 100:
410 agl.setMax(agl.getMax() + agl.getPlus())
411 agl.setCurrent(agl.getMax())
413 # Increase wisdom?
414 x = randint(1,100)
415 wis = self.getStat("wis")
416 if x <= wis.getChance() * 100:
417 wis.setMax(wis.getMax() + wis.getPlus())
418 wis.setCurrent(wis.getMax())
420 # Increase will?
421 x = randint(1,100)
422 wil = self.getStat("wil")
423 if x <= wil.getChance() * 100:
424 wil.setMax(wil.getMax() + wil.getPlus())
425 wil.setCurrent(wil.getMax())
427 # Set new exp till next level maximum
428 exptnl_s.setMax(int(exptnl_s.getMax() * (1 + exptnl_s.getGrowthRate())))
430 # Carry over any experience that didn't go towards getting the last level
431 exptnl_s.setCurrent(int(exptnl_s.getMax() + exptnl_s.getCurrent()))
433 def getElemModifier(self,elem):
434 for elemMod in self.__elemMod:
435 if elem == elemMod[0]:
436 return elemMod[1]
438 return 0.0
440 def getStatusModifier(self,stat):
441 for statusMod in self.__statusMod:
442 if stat == statusMod[0]:
443 return statusMod[1]
445 return 1.0
447 def getStatus(self):
448 """Get status(es) of fighter."""
449 return self.__status
451 def setStatus(self,s):
452 """Set status(es) of fighter."""
453 self.__status = s
455 def addStatus(self,s):
456 """Add a status to status list for fighter."""
457 found = False
458 for i in self.__status:
459 if i.getName() == s:
460 found = True
461 break
463 if not found:
464 self.__status.append(s)
465 s.genEntrance()
466 else:
467 print 'ERROR: Status already in list!'
469 def removeStatus(self,s):
470 """Remove statuses from status list for fighter."""
471 found = False
472 for i in self.__status:
473 if i.getName() == s:
474 found = True
475 break
477 if found == True:
478 i.genExit()
479 self.__status.remove(i)
480 else:
481 print 'ERROR: Status was not in list!'
483 def getStat(self,statName):
484 for stat in self.__stats:
485 if stat.getName() == statName:
486 return stat
487 print 'ERROR: No such Fighter stat.'
489 def getAllStats(self):
490 return self.__stats
492 def setAllStats(self,stats):
493 self.__stats = stats