2 # GemRB - Infinity Engine Emulator
3 # Copyright (C) 2003 The GemRB Project
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program 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 this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 # GUICommon.py - common functions for GUIScripts of all game types
25 from ie_restype
import RES_CHU
, RES_2DA
, RES_WMP
, RES_ARE
26 from ie_spells
import LS_MEMO
, LSR_KNOWN
, LSR_LEVEL
, LSR_STAT
27 from GUIDefines
import *
28 from ie_stats
import *
29 from ie_action
import ACT_QSLOT1
, ACT_QSLOT2
, ACT_QSLOT3
, ACT_QSLOT4
, ACT_QSLOT5
30 from ie_slots
import SLOT_ALL
37 def CloseOtherWindow (NewWindowFn
):
38 global OtherWindowFn
,NextWindowFn
40 GemRB
.LeaveContainer()
41 if OtherWindowFn
and OtherWindowFn
!= NewWindowFn
:
42 # allow detection of 'next window'
43 NextWindowFn
= NewWindowFn
44 # switching from a window to something else, call old function
46 OtherWindowFn
= NewWindowFn
49 # something is calling us with its own function, so
50 # it is closing down, return true
54 # new window, no need to do setup
55 OtherWindowFn
= NewWindowFn
60 width
= GemRB
.GetSystemVariable (SV_WIDTH
)
61 height
= GemRB
.GetSystemVariable (SV_HEIGHT
)
63 if GemRB
.GameType
== "pst":
68 # use a custom gui if there is one
69 gui
= "CGUI" + str(width
)[:2] + str(height
)[:2]
70 if GemRB
.HasResource (gui
, RES_CHU
, 1):
83 if GemRB
.HasResource (gui
, RES_CHU
, 1):
86 # fallback to the smallest resolution
90 GemRB
.RestParty(0,0,0)
93 def SelectFormation ():
94 GemRB
.GameSetFormation (GemRB
.GetVar ("Formation"))
97 def OpenFloatMenuWindow ():
99 import FloatMenuWindow
100 FloatMenuWindow
.OpenFloatMenuWindow()
102 GemRB
.GameControlSetTargetMode (TARGET_MODE_NONE
)
104 def GetActorPaperDoll (actor
):
105 anim_id
= GemRB
.GetPlayerStat (actor
, IE_ANIMATION_ID
)
106 level
= GemRB
.GetPlayerStat (actor
, IE_ARMOR_TYPE
)
107 row
= "0x%04X" %anim_id
108 which
= "LEVEL%d" %(level
+1)
109 doll
= CommonTables
.Pdolls
.GetValue (row
, which
)
111 print "GetActorPaperDoll: Missing paper doll for animation", row
, which
114 def SelectAllOnPress ():
115 GemRB
.GameSelectPC (0, 1)
118 #GemRB.SetPlayerStat(GemRB.GameGetFirstSelectedPC (),44,249990)
119 GemRB
.GamePause (2, 0)
121 def GetMageSpells (Kit
, Alignment
, Level
):
124 Table
= GemRB
.LoadTable ("aligns")
125 v
= Table
.FindValue (3, Alignment
)
126 Usability
= Kit | Table
.GetValue(v
, 5)
128 SpellsTable
= GemRB
.LoadTable ("spells")
129 for i
in range(SpellsTable
.GetValue ("MAGE", str(Level
), 1) ):
130 SpellName
= "SPWI%d%02d"%(Level
,i
+1)
131 ms
= GemRB
.GetSpell (SpellName
, 1)
135 if Usability
& ms
['SpellExclusion']:
139 if Kit
& (1 << ms
['SpellSchool']+5): # of matching specialist school
141 # Wild mage spells are of normal schools, so we have to find them
142 # separately. Generalists can learn any spell but the wild ones, so
143 # we check if the mage is wild and if a generalist wouldn't be able
144 # to learn the spell.
145 if Kit
== 0x8000 and (0x4000 & ms
['SpellExclusion']):
147 MageSpells
.append ([SpellName
, SpellType
])
151 def GetLearnableMageSpells (Kit
, Alignment
, Level
):
154 for Spell
in GetMageSpells (Kit
, Alignment
, Level
):
156 Learnable
.append (Spell
[0])
159 def GetLearnablePriestSpells (Class
, Alignment
, Level
):
162 Table
=GemRB
.LoadTable("aligns")
163 v
= Table
.FindValue(3, Alignment
)
164 #usability is the bitset we look for
165 Usability
=Table
.GetValue(v
, 5)
167 SpellsTable
= GemRB
.LoadTable ("spells")
168 for i
in range(SpellsTable
.GetValue ("PRIEST", str (Level
), 1) ):
169 SpellName
= "SPPR%d%02d"%(Level
,i
+1)
170 ms
= GemRB
.GetSpell(SpellName
, 1)
173 if Class
& ms
['SpellDivine']:
175 if Usability
& ms
['SpellExclusion']:
177 Learnable
.append (SpellName
)
180 def SetupSpellLevels (pc
, TableName
, Type
, Level
):
181 #don't die on a missing reference
182 #FIXME: try to do this in a non-hard way?
183 if not GemRB
.HasResource (TableName
, RES_2DA
):
184 if TableName
== "MXSPLDRU":
185 SetupSpellLevels (pc
, "MXSPLPRS", Type
, Level
)
188 Table
= GemRB
.LoadTable (TableName
)
189 for i
in range(Table
.GetColumnCount ()):
190 # do a string lookup since some tables don't have entries for all levels
191 value
= Table
.GetValue (str(Level
), str(i
+1), 1)
192 # specialist mages get an extra spell if they already know that level
193 # FIXME: get a general routine to find specialists
194 school
= GemRB
.GetVar("MAGESCHOOL")
195 if Type
== IE_SPELL_TYPE_WIZARD
and school
!= 0:
198 GemRB
.SetMemorizableSpellsCount (pc
, value
, Type
, i
)
201 def UnsetupSpellLevels (pc
, TableName
, Type
, Level
):
202 #don't die on a missing reference
203 #FIXME: try to do this in a non-hard way?
204 if not GemRB
.HasResource (TableName
, RES_2DA
):
205 if TableName
== "MXSPLDRU":
206 UnsetupSpellLevels (pc
, "MXSPLPRS", Type
, Level
)
209 Table
= GemRB
.LoadTable (TableName
)
210 for i
in range(Table
.GetColumnCount ()):
211 GemRB
.SetMemorizableSpellsCount (pc
, 0, Type
, i
)
214 def SetColorStat (Actor
, Stat
, Value
):
218 GemRB
.SetPlayerStat (Actor
, Stat
, t
)
221 def CheckStat100 (Actor
, Stat
, Diff
):
222 mystat
= GemRB
.GetPlayerStat (Actor
, Stat
)
223 goal
= GemRB
.Roll (1,100, Diff
)
228 def CheckStat20 (Actor
, Stat
, Diff
):
229 mystat
= GemRB
.GetPlayerStat (Actor
, Stat
)
230 goal
= GemRB
.Roll (1,20, Diff
)
236 return GemRB
.GameType
== "pst"
239 return GemRB
.GameType
== "iwd"
242 return GemRB
.GameType
== "how"
245 return GemRB
.GameType
== "iwd" or GemRB
.GameType
== "how"
248 return GemRB
.GameType
== "iwd2"
251 return GemRB
.GameType
== "bg1"
254 return GemRB
.GameType
== "bg2"
257 return GemRB
.HasResource ("worldm25", RES_WMP
) and GemRB
.GetVar("oldgame") == 0
260 return GemRB
.HasResource ("worldm25", RES_WMP
)
263 return GemRB
.HasResource ("expmap", RES_WMP
)
266 return GemRB
.HasResource ("ar9700", RES_ARE
)
268 def GetIWDSpellButtonCount ():
274 def SetGamedaysAndHourToken ():
275 currentTime
= GemRB
.GetGameTime()
276 days
= currentTime
/ 7200
277 hours
= (currentTime
% 7200) / 300
278 GemRB
.SetToken ('GAMEDAYS', str (days
))
279 GemRB
.SetToken ('HOUR', str (hours
))
281 # Returns -1 if not found; otherwise, the index of the spell
282 def HasSpell (Actor
, SpellType
, Level
, Ref
):
283 # loop through each spell in the spell level and check for a matching ref
284 for i
in range (GemRB
.GetKnownSpellsCount (Actor
, SpellType
, Level
)):
285 Spell
= GemRB
.GetKnownSpell(Actor
, SpellType
, Level
, i
)
286 if Spell
["SpellResRef"].upper() == Ref
.upper(): # ensure case is the same
292 # Adds class/kit abilities
293 def AddClassAbilities (pc
, table
, Level
=1, LevelDiff
=1, align
=-1):
294 TmpTable
= GemRB
.LoadTable (table
)
296 # gotta stay positive
297 if Level
-LevelDiff
< 0:
300 # we're doing alignment additions
303 iMax
= TmpTable
.GetRowCount ()
305 # alignment is expected to be the row required
309 # make sure we don't go out too far
310 jMin
= Level
-LevelDiff
312 if jMax
> TmpTable
.GetColumnCount ():
313 jMax
= TmpTable
.GetColumnCount ()
315 for i
in range(iMin
, iMax
):
316 # apply each spell from each new class
317 for j
in range (jMin
, jMax
):
318 ab
= TmpTable
.GetValue (i
, j
, 0)
319 if ab
and ab
!= "****":
320 # seems all SPINs act like GA_*
324 # apply spell (AP_) or gain spell (GA_)
326 GemRB
.ApplySpell (pc
, ab
[3:])
328 SpellIndex
= HasSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, ab
[3:])
330 GemRB
.LearnSpell (pc
, ab
[3:], LS_MEMO
)
332 # make room for one more memorization
333 max_mem_cnt
= GemRB
.GetMemorizableSpellsCount (pc
, IE_SPELL_TYPE_INNATE
, 0, 0)
334 GemRB
.SetMemorizableSpellsCount (pc
, max_mem_cnt
+1, IE_SPELL_TYPE_INNATE
, 0)
335 # memorize another spell instance
336 GemRB
.MemorizeSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, SpellIndex
)
338 print "ERROR, unknown class ability (type): ", ab
340 # remove all class abilities up to the given level
341 # for dual-classing mainly
342 def RemoveClassAbilities (pc
, table
, Level
):
343 TmpTable
= GemRB
.LoadTable (table
)
345 # gotta stay positive
349 # make sure we don't go out too far
351 if jMax
> TmpTable
.GetColumnCount ():
352 jMax
= TmpTable
.GetColumnCount ()
354 for i
in range(TmpTable
.GetRowCount ()):
355 for j
in range (jMax
):
356 ab
= TmpTable
.GetValue (i
, j
, 0)
357 if ab
and ab
!= "****":
359 SpellIndex
= HasSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, ab
[3:])
361 # seems all SPINs act like GA_*
365 # apply spell (AP_) or gain spell (GA_)?
367 GemRB
.RemoveEffects (pc
, ab
[3:])
370 # TODO: get the correct counts to avoid removing an innate ability
371 # given by more than one thing?
372 # RemoveSpell will unmemorize them all too
373 GemRB
.RemoveSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, SpellIndex
)
375 print "ERROR, unknown class ability (type): ", ab
377 def CannotLearnSlotSpell ():
378 pc
= GemRB
.GameGetSelectedPCSingle ()
380 # disqualify sorcerors immediately
381 if GemRB
.GetPlayerStat (pc
, IE_CLASS
) == 19:
384 slot_item
= GemRB
.GetSlotItem (pc
, GemRB
.GetVar ("ItemButton"))
385 spell_ref
= GemRB
.GetItem (slot_item
['ItemResRef'], pc
)['Spell']
386 spell
= GemRB
.GetSpell (spell_ref
)
388 # maybe she already knows this spell
389 if HasSpell (pc
, IE_SPELL_TYPE_WIZARD
, spell
['SpellLevel']-1, spell_ref
) != -1:
392 # level check (needs enough intelligence for this level of spell)
393 dumbness
= GemRB
.GetPlayerStat (pc
, IE_INT
)
394 if spell
['SpellLevel'] > GemRB
.GetAbilityBonus (IE_INT
, 1, dumbness
):
399 def UpdateInventorySlot (pc
, Button
, Slot
, Type
, Equipped
=False):
400 Button
.SetFont ("NUMBER")
401 Button
.SetBorder (0, 0,0,0,0, 128,128,255,64, 0,1)
402 Button
.SetBorder (1, 2,2,2,2, 32,32,255,0, 0,0)
403 Button
.SetBorder (2, 0,0,0,0, 255,128,128,64, 0,1)
404 Button
.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_TOP | IE_GUI_BUTTON_PICTURE
, OP_OR
)
408 Button
.SetFlags (IE_GUI_BUTTON_PICTURE
, OP_NAND
)
409 if Type
== "inventory":
410 Button
.SetTooltip (12013) # Personal Item
411 elif Type
== "ground":
412 Button
.SetTooltip (12011) # Ground Item
414 Button
.SetTooltip ("")
415 Button
.EnableBorder (0, 0)
416 Button
.EnableBorder (1, 0)
417 Button
.EnableBorder (2, 0)
419 item
= GemRB
.GetItem (Slot
['ItemResRef'])
420 identified
= Slot
["Flags"] & IE_INV_ITEM_IDENTIFIED
421 magical
= Slot
["Flags"] & IE_INV_ITEM_MAGICAL
423 # StackAmount holds the *maximum* item count in the stack while Usages0 holds the actual
424 if item
["StackAmount"] > 1:
425 Button
.SetText (str (Slot
["Usages0"]))
429 # auto-identify mundane items; the actual indentification will happen on transfer
430 if not identified
and item
["LoreToID"] == 0:
433 if not identified
or item
["ItemNameIdentified"] == -1:
434 Button
.SetTooltip (item
["ItemName"])
435 Button
.EnableBorder (0, 1)
436 Button
.EnableBorder (1, 0)
438 Button
.SetTooltip (item
["ItemNameIdentified"])
439 Button
.EnableBorder (0, 0)
441 Button
.EnableBorder (1, 1)
443 Button
.EnableBorder (1, 0)
445 if GemRB
.CanUseItemType (SLOT_ALL
, Slot
['ItemResRef'], pc
, Equipped
):
446 Button
.EnableBorder (2, 0)
448 Button
.EnableBorder (2, 1)
450 Button
.SetItemIcon (Slot
['ItemResRef'], 0)
454 def LearnPriestSpells (pc
, level
, mask
):
455 """Learns all the priest spells through the given spell level.
457 Mask distinguishes clerical and druidic spells."""
458 if level
> 7: # make sure we don't have too high a level
461 # go through each level
462 alignment
= GemRB
.GetPlayerStat (pc
, IE_ALIGNMENT
)
463 for i
in range (level
):
464 learnable
= GetLearnablePriestSpells (mask
, alignment
, i
+1)
466 for spell
in learnable
:
467 # if the spell isn't learned, learn it
468 if HasSpell (pc
, IE_SPELL_TYPE_PRIEST
, i
, spell
) < 0:
469 GemRB
.LearnSpell (pc
, spell
)
472 # PST uses a button, IWD2 two types, the rest are the same with two labels
473 def SetEncumbranceLabels (Window
, ControlID
, Control2ID
, pc
, invert_colors
= False):
474 """Displays the encumbrance as a ratio of current to maximum."""
476 # Getting the character's strength
477 sstr
= GemRB
.GetPlayerStat (pc
, IE_STR
)
478 ext_str
= GemRB
.GetPlayerStat (pc
, IE_STREXTRA
)
481 max_encumb
= CommonTables
.StrMod
.GetValue (sstr
, 3) + CommonTables
.StrModEx
.GetValue (ext_str
, 3)
482 encumbrance
= GemRB
.GetPlayerStat (pc
, IE_ENCUMBRANCE
)
484 Control
= Window
.GetControl (ControlID
)
486 # FIXME: there should be a space before LB symbol (':')
487 Control
.SetText (str (encumbrance
) + ":\n\n\n\n" + str (max_encumb
) + ":")
488 elif GameIsIWD2() and not Control2ID
:
489 Control
.SetText (str (encumbrance
) + "/" + str(max_encumb
) + GemRB
.GetString(39537))
491 Control
.SetText (str (encumbrance
) + ":")
492 if not Control2ID
: # shouldn't happen
493 print "Missing second control parameter to SetEncumbranceLabels!"
495 Control2
= Window
.GetControl (Control2ID
)
496 Control2
.SetText (str (max_encumb
) + ":")
498 ratio
= (0.0 + encumbrance
) / max_encumb
501 Control
.SetTextColor (255, 0, 0, True)
503 Control
.SetTextColor (255, 0, 0)
506 Control
.SetTextColor (255, 255, 0, True)
508 Control
.SetTextColor (255, 255, 0)
511 Control
.SetTextColor (255, 255, 255, True)
513 Control
.SetTextColor (255, 255, 255)
516 Control2
.SetTextColor (255, 0, 0)
520 def GetActorClassTitle (actor
):
521 """Returns the string representation of the actors class."""
523 ClassTitle
= GemRB
.GetPlayerStat (actor
, IE_TITLE1
)
526 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
527 ClassIndex
= CommonTables
.Classes
.FindValue ( 5, Class
)
528 KitIndex
= GetKitIndex (actor
)
529 Multi
= CommonTables
.Classes
.GetValue (ClassIndex
, 4)
530 Dual
= IsDualClassed (actor
, 1)
532 if Multi
and Dual
[0] == 0: # true multi class
533 ClassTitle
= CommonTables
.Classes
.GetValue (ClassIndex
, 2)
534 ClassTitle
= GemRB
.GetString (ClassTitle
)
536 if Dual
[0]: # dual class
537 # first (previous) kit or class of the dual class
539 ClassTitle
= CommonTables
.KitList
.GetValue (Dual
[1], 2)
541 ClassTitle
= CommonTables
.Classes
.GetValue (Dual
[1], 2)
542 ClassTitle
= GemRB
.GetString (ClassTitle
) + " / "
543 ClassTitle
+= GemRB
.GetString (CommonTables
.Classes
.GetValue (Dual
[2], 2))
544 else: # ordinary class or kit
546 ClassTitle
= CommonTables
.KitList
.GetValue (KitIndex
, 2)
548 ClassTitle
= CommonTables
.Classes
.GetValue (ClassIndex
, 2)
549 ClassTitle
= GemRB
.GetString (ClassTitle
)
551 ClassTitle
= GemRB
.GetString (ClassTitle
)
553 #GetActorClassTitle returns string now...
554 #if ClassTitle == "*":
560 def GetKitIndex (actor
):
561 """Return the index of the actors kit from KITLIST.2da.
563 Returns 0 if the class is not kitted."""
565 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
566 Kit
= GemRB
.GetPlayerStat (actor
, IE_KIT
)
569 if Kit
& 0xc000 == 0x4000:
570 KitIndex
= Kit
& 0xfff
572 # carefully looking for kit by the usability flag
573 # since the barbarian kit id clashes with the no-kit value
574 if KitIndex
== 0 and Kit
!= 0x4000:
575 KitIndex
= CommonTables
.KitList
.FindValue (6, Kit
)
581 def IsDualClassed(actor
, verbose
):
582 """Returns an array containing the dual class information.
584 Return[0] is 0 if not dualclassed, 1 if the old class is a kit, 2 otherwise.
585 Return[1] contains either the kit or class index of the old class.
586 Return[2] contains the class index of the new class.
587 If verbose is false, only Return[0] contains useable data."""
592 DualedFrom
= GemRB
.GetPlayerStat (actor
, IE_MC_FLAGS
) & MC_WAS_ANY_CLASS
595 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
596 ClassIndex
= CommonTables
.Classes
.FindValue (5, Class
)
597 Multi
= CommonTables
.Classes
.GetValue (ClassIndex
, 4)
599 KitIndex
= GetKitIndex (actor
)
601 if DualedFrom
> 0: # first (previous) class of the dual class
602 MCColumn
= CommonTables
.Classes
.GetColumnIndex ("MC_WAS_ID")
603 FirstClassIndex
= CommonTables
.Classes
.FindValue (MCColumn
, DualedFrom
)
606 DualInfo
.append (KitIndex
)
609 DualInfo
.append (FirstClassIndex
)
611 # use the first class of the multiclass bunch that isn't the same as the first class
613 for i
in range (1,16):
615 ClassIndex
= CommonTables
.Classes
.FindValue (5, i
)
616 if ClassIndex
== FirstClassIndex
:
619 DualInfo
.append (ClassIndex
)
622 if len(DualInfo
) != 3:
623 print "WARNING: Invalid dualclass combination, treating as a single class!"
624 print DualedFrom
, Class
, Multi
, KitIndex
, DualInfo
636 def IsDualSwap (actor
):
637 """Returns true if the dualed classes are reverse of expection.
639 This can happen, because the engine gives dualclass characters the same ID as
640 their multiclass counterpart (eg. FIGHTER_MAGE = 3). Logic would dictate that
641 the new and old class levels would be stored in IE_LEVEL and IE_LEVEL2,
642 respectively; however, if one duals from a fighter to a mage in the above
643 example, the levels would actually be in reverse of expectation."""
645 Dual
= IsDualClassed (actor
, 1)
651 # split the full class name into its individual parts
652 # i.e FIGHTER_MAGE becomes [FIGHTER, MAGE]
653 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
654 Class
= CommonTables
.Classes
.FindValue (5, Class
)
655 Class
= CommonTables
.Classes
.GetRowName (Class
)
656 Class
= Class
.split("_")
658 # get our old class name
660 BaseClass
= CommonTables
.Classes
.GetRowName (Dual
[1])
662 BaseClass
= GetKitIndex (actor
)
663 BaseClass
= CommonTables
.KitList
.GetValue (BaseClass
, 7)
664 BaseClass
= CommonTables
.Classes
.FindValue (5, BaseClass
)
665 BaseClass
= CommonTables
.Classes
.GetRowName (BaseClass
)
667 # if our old class is the first class, we need to swap
668 if Class
[0] == BaseClass
:
673 def IsMultiClassed (actor
, verbose
):
674 """Returns a tuple containing the multiclass information.
676 Return[0] contains the total number of classes.
677 Return[1-3] contain the ID of their respective classes.
678 If verbose is false, only Return[0] has useable data."""
680 # change this if it will ever be needed
685 ClassIndex
= CommonTables
.Classes
.FindValue (5, GemRB
.GetPlayerStat (actor
, IE_CLASS
))
686 IsMulti
= CommonTables
.Classes
.GetValue (ClassIndex
, 4) # 0 if not multi'd
687 IsDual
= IsDualClassed (actor
, 0)
689 # dual-class char's look like multi-class chars
690 if (IsMulti
== 0) or (IsDual
[0] > 0):
693 return (IsMulti
,-1,-1,-1)
695 # get all our classes (leave space for our number of classes in the return array)
698 Mask
= 1 # we're looking at multiples of 2
699 ClassNames
= CommonTables
.Classes
.GetRowName(ClassIndex
).split("_")
701 # loop through each class and test it as a mask
702 # TODO: make 16 dynamic? -- allows for custom classes (not just kits)
703 for i
in range (1, 16):
704 if IsMulti
&Mask
: # it's part of this class
705 #we need to place the classes in the array based on their order in the name,
706 #NOT the order they are detected in
707 CurrentName
= CommonTables
.Classes
.GetRowName (CommonTables
.Classes
.FindValue (5, i
));
708 for j
in range(len(ClassNames
)):
709 if ClassNames
[j
] == CurrentName
:
710 Classes
[j
] = i
# mask is (i-1)^2 where i is class id
711 NumClasses
= NumClasses
+1
712 Mask
= 1 << i
# shift to the next multiple of 2 for testing
714 # in case we couldn't figure out to which classes the multi belonged
719 return (NumClasses
, Classes
[0], Classes
[1], Classes
[2])
721 def RemoveKnownSpells (pc
, type, level1
=1, level2
=1, noslots
=0, kit
=0):
722 """Removes all known spells of a given type between two spell levels.
724 If noslots is true, all memorization counts are set to 0.
725 Kit is used to identify the priest spell mask of the spells to be removed;
726 this is only used when removing spells in a dualclass."""
728 # choose the correct limit based upon class type
729 if type == IE_SPELL_TYPE_WIZARD
:
731 elif type == IE_SPELL_TYPE_PRIEST
:
734 # make sure that we get the original kit, if we have one
736 originalkit
= GetKitIndex (pc
)
738 if originalkit
: # kitted; find the class value
739 originalkit
= CommonTables
.KitList
.GetValue (originalkit
, 7)
740 else: # just get the class value
741 originalkit
= GemRB
.GetPlayerStat (pc
, IE_CLASS
)
743 # this is is specifically for dual-classes and will not work to remove only one
744 # spell type from a ranger/cleric multi-class
745 if CommonTables
.ClassSkills
.GetValue (originalkit
, 0, 0) != "*": # knows druid spells
747 elif CommonTables
.ClassSkills
.GetValue (originalkit
, 1, 0) != "*": # knows cleric spells
749 else: # don't know any other spells
752 # don't know how this would happen, but better to be safe
753 if originalkit
== kit
:
755 elif type == IE_SPELL_TYPE_INNATE
:
757 else: # can't do anything if an improper spell type is sent
760 # make sure we're within parameters
761 if level1
< 1 or level2
> limit
or level1
> level2
:
764 # remove all spells for each level
765 for level
in range (level1
-1, level2
):
766 # we need the count because we remove each spell in reverse order
767 count
= GemRB
.GetKnownSpellsCount (pc
, type, level
)
770 for spell
in range (count
):
771 # see if we need to check for kit
772 if type == IE_SPELL_TYPE_PRIEST
and kit
:
773 # get the spell's ref data
774 ref
= GemRB
.GetKnownSpell (pc
, type, level
, mod
-spell
)
775 ref
= GemRB
.GetSpell (ref
['SpellResRef'], 1)
777 # we have to look at the originalkit as well specifically for ranger/cleric dual-classes
778 # we wouldn't want to remove all cleric spells and druid spells if we lost our cleric class
779 # only the cleric ones
780 if kit
&ref
['SpellDivine'] or (originalkit
and not originalkit
&ref
['SpellDivine']):
784 GemRB
.RemoveSpell (pc
, type, level
, mod
-spell
)
786 # remove memorization counts if desired
788 GemRB
.SetMemorizableSpellsCount (pc
, 0, type, level
)
793 def CanDualClass(actor
):
795 if GemRB
.GetPlayerStat (actor
, IE_RACE
) != 1:
798 # already dualclassed
799 Dual
= IsDualClassed (actor
,0)
803 DualClassTable
= GemRB
.LoadTable ("dualclas")
804 CurrentStatTable
= GemRB
.LoadTable ("abdcscrq")
805 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
806 ClassIndex
= CommonTables
.Classes
.FindValue (5, Class
)
807 ClassName
= CommonTables
.Classes
.GetRowName (ClassIndex
)
808 KitIndex
= GetKitIndex (actor
)
810 ClassTitle
= ClassName
812 ClassTitle
= CommonTables
.KitList
.GetValue (KitIndex
, 0)
813 Row
= DualClassTable
.GetRowIndex (ClassTitle
)
815 # a lookup table for the DualClassTable columns
816 classes
= [ "FIGHTER", "CLERIC", "MAGE", "THIEF", "DRUID", "RANGER" ]
819 for col
in range (0, DualClassTable
.GetColumnCount ()):
820 value
= DualClassTable
.GetValue (Row
, col
)
823 matches
.append (classes
[col
])
825 # cannot dc if all the columns of the DualClassTable are 0
827 print "CannotDualClass: all the columns of the DualClassTable are 0"
830 # if the only choice for dc is already the same as the actors base class
831 if Sum
== 1 and ClassName
in matches
and KitIndex
== 0:
832 print "CannotDualClass: the only choice for dc is already the same as the actors base class"
835 AlignmentTable
= GemRB
.LoadTable ("alignmnt")
836 AlignsTable
= GemRB
.LoadTable ("aligns")
837 Alignment
= GemRB
.GetPlayerStat (actor
, IE_ALIGNMENT
)
838 AlignmentColName
= AlignsTable
.FindValue (3, Alignment
)
839 AlignmentColName
= AlignsTable
.GetValue (AlignmentColName
, 4)
841 for classy
in matches
:
842 Sum
+= AlignmentTable
.GetValue (classy
, AlignmentColName
)
844 # cannot dc if all the available classes forbid the chars alignment
846 print "CannotDualClass: all the available classes forbid the chars alignment"
849 # check current class' stat limitations
850 ClassStatIndex
= CurrentStatTable
.GetRowIndex (ClassTitle
)
851 for stat
in range (6):
852 minimum
= CurrentStatTable
.GetValue (ClassStatIndex
, stat
)
853 name
= CurrentStatTable
.GetColumnName (stat
)
854 if GemRB
.GetPlayerStat (actor
, eval ("IE_" + name
[4:])) < minimum
:
855 print "CannotDualClass: current class' stat limitations are too big"
858 # check new class' stat limitations - make sure there are any good class choices
859 TargetStatTable
= GemRB
.LoadTable ("abdcdsrq")
860 for match
in matches
:
861 ClassStatIndex
= TargetStatTable
.GetRowIndex (match
)
862 for stat
in range (6):
863 minimum
= TargetStatTable
.GetValue (ClassStatIndex
, stat
)
864 name
= TargetStatTable
.GetColumnName (stat
)
865 if GemRB
.GetPlayerStat (actor
, eval ("IE_" + name
[4:])) < minimum
:
866 matches
.remove (match
)
868 if len(matches
) == 0:
869 print "CannotDualClass: no good new class choices"
872 # must be at least level 2
873 if GemRB
.GetPlayerStat (actor
, IE_LEVEL
) == 1:
874 print "CannotDualClass: level 1"
878 def SetupDamageInfo (pc
, Button
):
879 hp
= GemRB
.GetPlayerStat (pc
, IE_HITPOINTS
)
880 hp_max
= GemRB
.GetPlayerStat (pc
, IE_MAXHITPOINTS
)
881 state
= GemRB
.GetPlayerStat (pc
, IE_STATE_ID
)
886 ratio
= (hp
+0.0) / hp_max
888 if hp
< 1 or (state
& STATE_DEAD
):
889 Button
.SetOverlay (0, 64,64,64,200, 64,64,64,200)
891 Button
.SetOverlay (ratio
, 140,0,0,205, 128,0,0,200)
892 ratio_str
= "\n%d/%d" %(hp
, hp_max
)
893 Button
.SetTooltip (GemRB
.GetPlayerName (pc
, 1) + ratio_str
)
898 GameWindow
= GUIClasses
.GWindow(0)
899 GameControl
= GUIClasses
.GControl(0,0)