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
24 from ie_restype
import RES_CHU
, RES_2DA
, RES_WMP
, RES_ARE
25 from ie_spells
import LS_MEMO
, LSR_KNOWN
, LSR_LEVEL
, LSR_STAT
26 from GUIDefines
import *
27 from ie_stats
import *
28 from ie_action
import ACT_QSLOT1
, ACT_QSLOT2
, ACT_QSLOT3
, ACT_QSLOT4
, ACT_QSLOT5
29 from ie_slots
import SLOT_ANY
33 # only used in SetEncumbranceLabels, but that is called very often
34 StrModTable
= StrModExTable
= None
35 ClassTable
= KitListTable
= ClassSkillsTable
= RaceTable
= NextLevelTable
= None
36 AppearanceAvatarTable
= None
38 def CloseOtherWindow (NewWindowFn
):
41 GemRB
.LeaveContainer()
42 if OtherWindowFn
and OtherWindowFn
!= NewWindowFn
:
44 OtherWindowFn
= NewWindowFn
50 OtherWindowFn
= NewWindowFn
54 width
= GemRB
.GetSystemVariable (SV_WIDTH
)
55 height
= GemRB
.GetSystemVariable (SV_HEIGHT
)
57 if GemRB
.GameType
== "pst":
62 # use a custom gui if there is one
63 gui
= "CGUI" + str(width
)[:2] + str(height
)[:2]
64 if GemRB
.HasResource (gui
, RES_CHU
, 1):
77 if GemRB
.HasResource (gui
, RES_CHU
, 1):
80 # fallback to the smallest resolution
84 GemRB
.RestParty(0,0,0)
87 def SelectFormation ():
88 GemRB
.GameSetFormation (GemRB
.GetVar ("Formation"))
91 def ActionStopPressed ():
92 for i
in range (PARTY_SIZE
):
93 if GemRB
.GameIsPCSelected (i
+ 1):
94 GemRB
.ClearActions (i
+ 1)
97 def OpenFloatMenuWindow ():
98 GemRB
.GameControlSetTargetMode (TARGET_MODE_NONE
)
100 def ActionTalkPressed ():
101 GemRB
.GameControlSetTargetMode (TARGET_MODE_TALK
,GA_NO_DEAD|GA_NO_ENEMY|GA_NO_HIDDEN
)
103 def ActionAttackPressed ():
104 GemRB
.GameControlSetTargetMode (TARGET_MODE_ATTACK
,GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN
)
106 def ActionDefendPressed ():
107 GemRB
.GameControlSetTargetMode (TARGET_MODE_DEFEND
,GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN
)
109 def ActionThievingPressed ():
110 GemRB
.GameControlSetTargetMode (TARGET_MODE_PICK
, GA_NO_DEAD|GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN
)
112 def ActionQItemPressed (action
):
113 """Uses the given quick item."""
114 pc
= GemRB
.GameGetFirstSelectedPC ()
116 GemRB
.UseItem (pc
, -2, action
)
119 def ActionQItem1Pressed ():
120 ActionQItemPressed (ACT_QSLOT1
)
123 def ActionQItem2Pressed ():
124 ActionQItemPressed (ACT_QSLOT2
)
127 def ActionQItem3Pressed ():
128 ActionQItemPressed (ACT_QSLOT3
)
131 def ActionQItem4Pressed ():
132 ActionQItemPressed (ACT_QSLOT4
)
135 def ActionQItem5Pressed ():
136 ActionQItemPressed (ACT_QSLOT5
)
139 def GetActorPaperDoll (actor
):
140 anim_id
= GemRB
.GetPlayerStat (actor
, IE_ANIMATION_ID
)
141 level
= GemRB
.GetPlayerStat (actor
, IE_ARMOR_TYPE
)
142 row
= "0x%04X" %anim_id
143 which
= "LEVEL%d" %(level
+1)
144 return AppearanceAvatarTable
.GetValue (row
, which
)
146 def SelectAllOnPress ():
147 GemRB
.GameSelectPC (0, 1)
150 #GemRB.SetPlayerStat(GemRB.GameGetFirstSelectedPC (),44,249990)
151 GemRB
.GamePause (2, 0)
153 def GetMageSpells (Kit
, Alignment
, Level
):
156 Table
= GemRB
.LoadTable ("aligns")
157 v
= Table
.FindValue (3, Alignment
)
158 Usability
= Kit | Table
.GetValue(v
, 5)
160 SpellsTable
= GemRB
.LoadTable ("spells")
161 for i
in range(SpellsTable
.GetValue ("MAGE", str(Level
), 1) ):
162 SpellName
= "SPWI%d%02d"%(Level
,i
+1)
163 ms
= GemRB
.GetSpell (SpellName
, 1)
167 if Usability
& ms
['SpellExclusion']:
171 if Kit
& (1 << ms
['SpellSchool']+5): # of matching specialist school
173 # Wild mage spells are of normal schools, so we have to find them
174 # separately. Generalists can learn any spell but the wild ones, so
175 # we check if the mage is wild and if a generalist wouldn't be able
176 # to learn the spell.
177 if Kit
== 0x8000 and (0x4000 & ms
['SpellExclusion']):
179 MageSpells
.append ([SpellName
, SpellType
])
183 def GetLearnableMageSpells (Kit
, Alignment
, Level
):
186 for Spell
in GetMageSpells (Kit
, Alignment
, Level
):
188 Learnable
.append (Spell
[0])
191 def GetLearnablePriestSpells (Class
, Alignment
, Level
):
194 Table
=GemRB
.LoadTable("aligns")
195 v
= Table
.FindValue(3, Alignment
)
196 #usability is the bitset we look for
197 Usability
=Table
.GetValue(v
, 5)
199 SpellsTable
= GemRB
.LoadTable ("spells")
200 for i
in range(SpellsTable
.GetValue ("PRIEST", str (Level
), 1) ):
201 SpellName
= "SPPR%d%02d"%(Level
,i
+1)
202 ms
= GemRB
.GetSpell(SpellName
, 1)
205 if Class
& ms
['SpellDivine']:
207 if Usability
& ms
['SpellExclusion']:
209 Learnable
.append (SpellName
)
212 def SetupSpellLevels (pc
, TableName
, Type
, Level
):
213 #don't die on a missing reference
214 #FIXME: try to do this in a non-hard way?
215 if not GemRB
.HasResource (TableName
, RES_2DA
):
216 if TableName
== "MXSPLDRU":
217 SetupSpellLevels (pc
, "MXSPLPRS", Type
, Level
)
220 Table
= GemRB
.LoadTable (TableName
)
221 for i
in range(Table
.GetColumnCount ()):
222 # do a string lookup since some tables don't have entries for all levels
223 value
= Table
.GetValue (str(Level
), str(i
+1), 1)
224 # specialist mages get an extra spell if they already know that level
225 # FIXME: get a general routine to find specialists
226 school
= GemRB
.GetVar("MAGESCHOOL")
227 if Type
== IE_SPELL_TYPE_WIZARD
and school
!= 0:
230 GemRB
.SetMemorizableSpellsCount (pc
, value
, Type
, i
)
233 def UnsetupSpellLevels (pc
, TableName
, Type
, Level
):
234 #don't die on a missing reference
235 #FIXME: try to do this in a non-hard way?
236 if not GemRB
.HasResource (TableName
, RES_2DA
):
237 if TableName
== "MXSPLDRU":
238 UnsetupSpellLevels (pc
, "MXSPLPRS", Type
, Level
)
241 Table
= GemRB
.LoadTable (TableName
)
242 for i
in range(Table
.GetColumnCount ()):
243 GemRB
.SetMemorizableSpellsCount (pc
, 0, Type
, i
)
246 def SetColorStat (Actor
, Stat
, Value
):
250 GemRB
.SetPlayerStat (Actor
, Stat
, t
)
253 def CheckStat100 (Actor
, Stat
, Diff
):
254 mystat
= GemRB
.GetPlayerStat (Actor
, Stat
)
255 goal
= GemRB
.Roll (1,100, Diff
)
260 def CheckStat20 (Actor
, Stat
, Diff
):
261 mystat
= GemRB
.GetPlayerStat (Actor
, Stat
)
262 goal
= GemRB
.Roll (1,20, Diff
)
268 return GemRB
.GameType
== "pst"
271 return GemRB
.GameType
== "iwd"
274 return GemRB
.GameType
== "how"
277 return GemRB
.GameType
== "iwd" or GemRB
.GameType
== "how"
280 return GemRB
.GameType
== "iwd2"
283 return GemRB
.GameType
== "bg1"
286 return GemRB
.GameType
== "bg2"
289 return GemRB
.HasResource ("worldm25", RES_WMP
) and GemRB
.GetVar("oldgame") == 0
292 return GemRB
.HasResource ("worldm25", RES_WMP
)
295 return GemRB
.HasResource ("expmap", RES_WMP
)
298 return GemRB
.HasResource ("ar9700", RES_ARE
)
300 def GetIWDSpellButtonCount ():
306 def SetGamedaysAndHourToken ():
307 currentTime
= GemRB
.GetGameTime()
308 days
= currentTime
/ 7200
309 hours
= (currentTime
% 7200) / 300
310 GemRB
.SetToken ('GAMEDAYS', str (days
))
311 GemRB
.SetToken ('HOUR', str (hours
))
313 # Returns -1 if not found; otherwise, the index of the spell
314 def HasSpell (Actor
, SpellType
, Level
, Ref
):
315 # loop through each spell in the spell level and check for a matching ref
316 for i
in range (GemRB
.GetKnownSpellsCount (Actor
, SpellType
, Level
)):
317 Spell
= GemRB
.GetKnownSpell(Actor
, SpellType
, Level
, i
)
318 if Spell
["SpellResRef"].upper() == Ref
.upper(): # ensure case is the same
324 # Adds class/kit abilities
325 def AddClassAbilities (pc
, table
, Level
=1, LevelDiff
=1, align
=-1):
326 TmpTable
= GemRB
.LoadTable (table
)
328 # gotta stay positive
329 if Level
-LevelDiff
< 0:
332 # we're doing alignment additions
335 iMax
= TmpTable
.GetRowCount ()
337 # alignment is expected to be the row required
341 # make sure we don't go out too far
342 jMin
= Level
-LevelDiff
344 if jMax
> TmpTable
.GetColumnCount ():
345 jMax
= TmpTable
.GetColumnCount ()
347 for i
in range(iMin
, iMax
):
348 # apply each spell from each new class
349 for j
in range (jMin
, jMax
):
350 ab
= TmpTable
.GetValue (i
, j
, 0)
351 if ab
and ab
!= "****":
352 # seems all SPINs act like GA_*
356 # apply spell (AP_) or gain spell (GA_)
358 GemRB
.ApplySpell (pc
, ab
[3:])
360 SpellIndex
= HasSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, ab
[3:])
361 if SpellIndex
< 0: # don't know it yet
362 GemRB
.LearnSpell (pc
, ab
[3:], LS_MEMO
)
363 else: # memorize another one
364 GemRB
.MemorizeSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, SpellIndex
)
366 print "ERROR, unknown class ability (type): ", ab
368 # remove all class abilities up to a give level
369 # for dual-classing mainly
370 def RemoveClassAbilities (pc
, table
, Level
):
371 TmpTable
= GemRB
.LoadTable (table
)
373 # gotta stay positive
377 # make sure we don't go out too far
379 if jMax
> TmpTable
.GetColumnCount ():
380 jMax
= TmpTable
.GetColumnCount ()
382 for i
in range(TmpTable
.GetRowCount ()):
383 for j
in range (jMax
):
384 ab
= TmpTable
.GetValue (i
, j
, 0)
385 if ab
and ab
!= "****":
387 SpellIndex
= HasSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, ab
[3:])
389 # seems all SPINs act like GA_*
393 # apply spell (AP_) or gain spell (GA_)?
396 GemRB
.RemoveEffects (pc
, ab
[3:])
399 # TODO: get the correct counts to avoid removing an innate ability
400 # given by more than one thing?
401 GemRB
.UnmemorizeSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, SpellIndex
)
402 GemRB
.RemoveSpell (pc
, IE_SPELL_TYPE_INNATE
, 0, SpellIndex
)
404 print "ERROR, unknown class ability (type): ", ab
406 def CannotLearnSlotSpell ():
407 pc
= GemRB
.GameGetSelectedPCSingle ()
409 # disqualify sorcerors immediately
410 if GemRB
.GetPlayerStat (pc
, IE_CLASS
) == 19:
413 slot_item
= GemRB
.GetSlotItem (pc
, GemRB
.GetVar ("ItemButton"))
414 spell_ref
= GemRB
.GetItem (slot_item
['ItemResRef'], pc
)['Spell']
415 spell
= GemRB
.GetSpell (spell_ref
)
417 # maybe she already knows this spell
418 if HasSpell (pc
, IE_SPELL_TYPE_WIZARD
, spell
['SpellLevel']-1, spell_ref
) != -1:
421 # level check (needs enough intelligence for this level of spell)
422 dumbness
= GemRB
.GetPlayerStat (pc
, IE_INT
)
423 if spell
['SpellLevel'] > GemRB
.GetAbilityBonus (IE_INT
, 1, dumbness
):
428 def UpdateInventorySlot (pc
, Button
, Slot
, Type
):
429 Button
.SetFont ("NUMBER")
430 Button
.SetBorder (0, 0,0,0,0, 128,128,255,64, 0,1)
431 Button
.SetBorder (1, 2,2,2,2, 32,32,255,0, 0,0)
432 Button
.SetBorder (2, 0,0,0,0, 255,128,128,64, 0,1)
433 Button
.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_TOP | IE_GUI_BUTTON_PICTURE
, OP_OR
)
437 Button
.SetFlags (IE_GUI_BUTTON_PICTURE
, OP_NAND
)
438 if Type
== "inventory":
439 Button
.SetTooltip (12013) # Personal Item
440 elif Type
== "ground":
441 Button
.SetTooltip (12011) # Ground Item
443 Button
.SetTooltip ("")
444 Button
.EnableBorder (0, 0)
445 Button
.EnableBorder (1, 0)
446 Button
.EnableBorder (2, 0)
448 item
= GemRB
.GetItem (Slot
['ItemResRef'])
449 identified
= Slot
["Flags"] & IE_INV_ITEM_IDENTIFIED
450 magical
= Slot
["Flags"] & IE_INV_ITEM_MAGICAL
452 # TODO: figure out this mess
453 # if item["StackAmount"] > 1:
454 # Button.SetText (str (item["StackAmount"])) # wrong for potions, correct for arrows
455 # if Slot["Usages0"] > 1:
456 # Button.SetText (str (Slot["Usages0"])) # this has the correct value for potions, but not for gems (0)
457 if item
["StackAmount"] > 1:
458 Button
.SetText (str (Slot
["Usages0"]))
462 if not identified
or item
["ItemNameIdentified"] == -1:
463 Button
.SetTooltip (item
["ItemName"])
464 Button
.EnableBorder (0, 1)
465 Button
.EnableBorder (1, 0)
467 Button
.SetTooltip (item
["ItemNameIdentified"])
468 Button
.EnableBorder (0, 0)
470 Button
.EnableBorder (1, 1)
472 Button
.EnableBorder (1, 0)
474 if GemRB
.CanUseItemType (SLOT_ANY
, Slot
['ItemResRef'], pc
):
475 Button
.EnableBorder (2, 0)
477 Button
.EnableBorder (2, 1)
479 Button
.SetItemIcon (Slot
['ItemResRef'], 0)
483 def LearnPriestSpells (pc
, level
, mask
):
484 """Learns all the priest spells through the given spell level.
486 Mask distinguishes clerical and druidic spells."""
487 if level
> 7: # make sure we don't have too high a level
490 # go through each level
491 alignment
= GemRB
.GetPlayerStat (pc
, IE_ALIGNMENT
)
492 for i
in range (level
):
493 learnable
= GetLearnablePriestSpells (mask
, alignment
, i
+1)
495 for spell
in learnable
:
496 # if the spell isn't learned, learn it
497 if HasSpell (pc
, IE_SPELL_TYPE_PRIEST
, i
, spell
) < 0:
498 GemRB
.LearnSpell (pc
, spell
)
501 # PST uses a button, IWD2 two types, the rest are the same with two labels
502 def SetEncumbranceLabels (Window
, ControlID
, Control2ID
, pc
):
503 """Displays the encumbrance as a ratio of current to maximum."""
505 # Getting the character's strength
506 sstr
= GemRB
.GetPlayerStat (pc
, IE_STR
)
507 ext_str
= GemRB
.GetPlayerStat (pc
, IE_STREXTRA
)
510 max_encumb
= StrModTable
.GetValue (sstr
, 3) + StrModExTable
.GetValue (ext_str
, 3)
511 encumbrance
= GemRB
.GetPlayerStat (pc
, IE_ENCUMBRANCE
)
513 Control
= Window
.GetControl (ControlID
)
515 # FIXME: there should be a space before LB symbol (':')
516 Control
.SetText (str (encumbrance
) + ":\n\n\n\n" + str (max_encumb
) + ":")
517 elif GameIsIWD2() and not Control2ID
:
518 Control
.SetText (str (encumbrance
) + "/" + str(max_encumb
) + GemRB
.GetString(39537))
520 Control
.SetText (str (encumbrance
) + ":")
521 if not Control2ID
: # shouldn't happen
522 print "Missing second control parameter to SetEncumbranceLabels!"
524 Control2
= Window
.GetControl (Control2ID
)
525 Control2
.SetText (str (max_encumb
) + ":")
527 ratio
= (0.0 + encumbrance
) / max_encumb
529 Control
.SetTextColor (255, 0, 0)
531 Control
.SetTextColor (255, 255, 0)
533 Control
.SetTextColor (255, 255, 255)
536 Control2
.SetTextColor (255, 0, 0)
540 def GetActorClassTitle (actor
):
541 """Returns the string representation of the actors class."""
543 ClassTitle
= GemRB
.GetPlayerStat (actor
, IE_TITLE1
)
546 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
547 ClassIndex
= ClassTable
.FindValue ( 5, Class
)
548 KitIndex
= GetKitIndex (actor
)
549 Multi
= ClassTable
.GetValue (ClassIndex
, 4)
550 Dual
= IsDualClassed (actor
, 1)
552 if Multi
and Dual
[0] == 0: # true multi class
553 ClassTitle
= ClassTable
.GetValue (ClassIndex
, 2)
554 ClassTitle
= GemRB
.GetString (ClassTitle
)
556 if Dual
[0]: # dual class
557 # first (previous) kit or class of the dual class
559 ClassTitle
= KitListTable
.GetValue (Dual
[1], 2)
561 ClassTitle
= ClassTable
.GetValue (Dual
[1], 2)
562 ClassTitle
= GemRB
.GetString (ClassTitle
) + " / "
563 ClassTitle
+= GemRB
.GetString (ClassTable
.GetValue (Dual
[2], 2))
564 else: # ordinary class or kit
566 ClassTitle
= KitListTable
.GetValue (KitIndex
, 2)
568 ClassTitle
= ClassTable
.GetValue (ClassIndex
, 2)
569 ClassTitle
= GemRB
.GetString (ClassTitle
)
571 ClassTitle
= GemRB
.GetString (ClassTitle
)
573 #GetActorClassTitle returns string now...
574 #if ClassTitle == "*":
580 def GetKitIndex (actor
):
581 """Return the index of the actors kit from KITLIST.2da.
583 Returns 0 if the class is not kitted."""
585 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
586 Kit
= GemRB
.GetPlayerStat (actor
, IE_KIT
)
589 if Kit
& 0xc000 == 0x4000:
590 KitIndex
= Kit
& 0xfff
592 # carefully looking for kit by the usability flag
593 # since the barbarian kit id clashes with the no-kit value
594 if KitIndex
== 0 and Kit
!= 0x4000:
595 KitIndex
= KitListTable
.FindValue (6, Kit
)
601 def IsDualClassed(actor
, verbose
):
602 """Returns an array containing the dual class information.
604 Return[0] is 0 if not dualclassed, 1 if the old class is a kit, 2 otherwise.
605 Return[1] contains either the kit or class index of the old class.
606 Return[2] contains the class index of the new class.
607 If verbose is false, only Return[0] contains useable data."""
609 Dual
= GemRB
.GetPlayerStat (actor
, IE_MC_FLAGS
)
610 Dual
= Dual
& ~
(MC_EXPORTABLE|MC_PLOT_CRITICAL|MC_BEENINPARTY|MC_HIDDEN
)
613 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
614 ClassIndex
= ClassTable
.FindValue (5, Class
)
615 Multi
= ClassTable
.GetValue (ClassIndex
, 4)
617 KitIndex
= GetKitIndex (actor
)
619 if (Dual
& MC_WAS_ANY_CLASS
) > 0: # first (previous) class of the dual class
620 MCColumn
= ClassTable
.GetColumnIndex ("MC_WAS_ID")
621 FirstClassIndex
= ClassTable
.FindValue (MCColumn
, Dual
& MC_WAS_ANY_CLASS
)
624 DualInfo
.append (KitIndex
)
627 DualInfo
.append (FirstClassIndex
)
629 # use the first class of the multiclass bunch that isn't the same as the first class
631 for i
in range (1,16):
633 ClassIndex
= ClassTable
.FindValue (5, i
)
634 if ClassIndex
== FirstClassIndex
:
637 DualInfo
.append (ClassIndex
)
644 if (Dual
& MC_WAS_ANY_CLASS
) > 0:
649 def IsDualSwap (actor
):
650 """Returns true if the dualed classes are reverse of expection.
652 This can happen, because the engine gives dualclass characters the same ID as
653 their multiclass counterpart (eg. FIGHTER_MAGE = 3). Logic would dictate that
654 the new and old class levels would be stored in IE_LEVEL and IE_LEVEL2,
655 respectively; however, if one duals from a fighter to a mage in the above
656 example, the levels would actually be in reverse of expectation."""
658 Dual
= IsDualClassed (actor
, 1)
664 # split the full class name into its individual parts
665 # i.e FIGHTER_MAGE becomes [FIGHTER, MAGE]
666 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
667 Class
= ClassTable
.FindValue (5, Class
)
668 Class
= ClassTable
.GetRowName (Class
)
669 Class
= Class
.split("_")
671 # get our old class name
673 BaseClass
= ClassTable
.GetRowName (Dual
[1])
675 BaseClass
= GetKitIndex (actor
)
676 BaseClass
= KitListTable
.GetValue (BaseClass
, 7)
677 BaseClass
= ClassTable
.FindValue (5, BaseClass
)
678 BaseClass
= ClassTable
.GetRowName (BaseClass
)
680 # if our old class is the first class, we need to swap
681 if Class
[0] == BaseClass
:
686 def IsMultiClassed (actor
, verbose
):
687 """Returns a tuple containing the multiclass information.
689 Return[0] contains the total number of classes.
690 Return[1-3] contain the ID of their respective classes.
691 If verbose is false, only Return[0] has useable data."""
694 ClassIndex
= ClassTable
.FindValue (5, GemRB
.GetPlayerStat (actor
, IE_CLASS
))
695 IsMulti
= ClassTable
.GetValue (ClassIndex
, 4) # 0 if not multi'd
696 IsDual
= IsDualClassed (actor
, 0)
698 # dual-class char's look like multi-class chars
699 if (IsMulti
== 0) or (IsDual
[0] > 0):
702 return (IsMulti
,-1,-1,-1)
704 # get all our classes (leave space for our number of classes in the return array)
707 Mask
= 1 # we're looking at multiples of 2
708 ClassNames
= ClassTable
.GetRowName(ClassIndex
).split("_")
710 # loop through each class and test it as a mask
711 # TODO: make 16 dynamic? -- allows for custom classes (not just kits)
712 for i
in range (1, 16):
713 if IsMulti
&Mask
: # it's part of this class
714 #we need to place the classes in the array based on their order in the name,
715 #NOT the order they are detected in
716 CurrentName
= ClassTable
.GetRowName (ClassTable
.FindValue (5, i
));
717 for j
in range(len(ClassNames
)):
718 if ClassNames
[j
] == CurrentName
:
719 Classes
[j
] = i
# mask is (i-1)^2 where i is class id
720 NumClasses
= NumClasses
+1
721 Mask
= 1 << i
# shift to the next multiple of 2 for testing
723 # in case we couldn't figure out to which classes the multi belonged
728 return (NumClasses
, Classes
[0], Classes
[1], Classes
[2])
730 def RemoveKnownSpells (pc
, type, level1
=1, level2
=1, noslots
=0, kit
=0):
731 """Removes all known spells of a given type between two spell levels.
733 If noslots is true, all memorization counts are set to 0.
734 Kit is used to identify the priest spell mask of the spells to be removed;
735 this is only used when removing spells in a dualclass."""
737 # choose the correct limit based upon class type
738 if type == IE_SPELL_TYPE_WIZARD
:
740 elif type == IE_SPELL_TYPE_PRIEST
:
743 # make sure that we get the original kit, if we have one
745 originalkit
= GetKitIndex (pc
)
747 if originalkit
: # kitted; find the class value
748 originalkit
= KitListTable
.GetValue (originalkit
, 7)
749 else: # just get the class value
750 originalkit
= GemRB
.GetPlayerStat (pc
, IE_CLASS
)
752 # this is is specifically for dual-classes and will not work to remove only one
753 # spell type from a ranger/cleric multi-class
754 if ClassSkillsTable
.GetValue (originalkit
, 0, 0) != "*": # knows druid spells
756 elif ClassSkillsTable
.GetValue (originalkit
, 1, 0) != "*": # knows cleric spells
758 else: # don't know any other spells
761 # don't know how this would happen, but better to be safe
762 if originalkit
== kit
:
764 elif type == IE_SPELL_TYPE_INNATE
:
766 else: # can't do anything if an improper spell type is sent
769 # make sure we're within parameters
770 if level1
< 1 or level2
> limit
or level1
> level2
:
773 # remove all spells for each level
774 for level
in range (level1
-1, level2
):
775 # we need the count because we remove each spell in reverse order
776 count
= GemRB
.GetKnownSpellsCount (pc
, type, level
)
779 for spell
in range (count
):
780 # see if we need to check for kit
781 if type == IE_SPELL_TYPE_PRIEST
and kit
:
782 # get the spell's ref data
783 ref
= GemRB
.GetKnownSpell (pc
, type, level
, mod
-spell
)
784 ref
= GemRB
.GetSpell (ref
['SpellResRef'], 1)
786 # we have to look at the originalkit as well specifically for ranger/cleric dual-classes
787 # we wouldn't want to remove all cleric spells and druid spells if we lost our cleric class
788 # only the cleric ones
789 if kit
&ref
['SpellDivine'] or (originalkit
and not originalkit
&ref
['SpellDivine']):
793 GemRB
.RemoveSpell (pc
, type, level
, mod
-spell
)
795 # remove memorization counts if desired
797 GemRB
.SetMemorizableSpellsCount (pc
, 0, type, level
)
802 def CanDualClass(actor
):
804 if GemRB
.GetPlayerStat (actor
, IE_RACE
) != 1:
807 # already dualclassed
808 Dual
= IsDualClassed (actor
,0)
812 DualClassTable
= GemRB
.LoadTable ("dualclas")
813 CurrentStatTable
= GemRB
.LoadTable ("abdcscrq")
814 Class
= GemRB
.GetPlayerStat (actor
, IE_CLASS
)
815 ClassIndex
= ClassTable
.FindValue (5, Class
)
816 ClassName
= ClassTable
.GetRowName (ClassIndex
)
817 KitIndex
= GetKitIndex (actor
)
819 ClassTitle
= ClassName
821 ClassTitle
= KitListTable
.GetValue (KitIndex
, 0)
822 Row
= DualClassTable
.GetRowIndex (ClassTitle
)
824 # a lookup table for the DualClassTable columns
825 classes
= [ "FIGHTER", "CLERIC", "MAGE", "THIEF", "DRUID", "RANGER" ]
828 for col
in range (0, DualClassTable
.GetColumnCount ()):
829 value
= DualClassTable
.GetValue (Row
, col
)
832 matches
.append (classes
[col
])
834 # cannot dc if all the columns of the DualClassTable are 0
836 print "CannotDualClass: all the columns of the DualClassTable are 0"
839 # if the only choice for dc is already the same as the actors base class
840 if Sum
== 1 and ClassName
in matches
and KitIndex
== 0:
841 print "CannotDualClass: the only choice for dc is already the same as the actors base class"
844 AlignmentTable
= GemRB
.LoadTable ("alignmnt")
845 AlignsTable
= GemRB
.LoadTable ("aligns")
846 Alignment
= GemRB
.GetPlayerStat (actor
, IE_ALIGNMENT
)
847 AlignmentColName
= AlignsTable
.FindValue (3, Alignment
)
848 AlignmentColName
= AlignsTable
.GetValue (AlignmentColName
, 4)
850 for classy
in matches
:
851 Sum
+= AlignmentTable
.GetValue (classy
, AlignmentColName
)
853 # cannot dc if all the available classes forbid the chars alignment
855 print "CannotDualClass: all the available classes forbid the chars alignment"
858 # check current class' stat limitations
859 ClassStatIndex
= CurrentStatTable
.GetRowIndex (ClassTitle
)
860 for stat
in range (6):
861 minimum
= CurrentStatTable
.GetValue (ClassStatIndex
, stat
)
862 name
= CurrentStatTable
.GetColumnName (stat
)
863 if GemRB
.GetPlayerStat (actor
, eval ("IE_" + name
[4:])) < minimum
:
864 print "CannotDualClass: current class' stat limitations are too big"
867 # check new class' stat limitations - make sure there are any good class choices
868 TargetStatTable
= GemRB
.LoadTable ("abdcdsrq")
869 for match
in matches
:
870 ClassStatIndex
= TargetStatTable
.GetRowIndex (match
)
871 for stat
in range (6):
872 minimum
= TargetStatTable
.GetValue (ClassStatIndex
, stat
)
873 name
= TargetStatTable
.GetColumnName (stat
)
874 if GemRB
.GetPlayerStat (actor
, eval ("IE_" + name
[4:])) < minimum
:
875 matches
.remove (match
)
877 if len(matches
) == 0:
878 print "CannotDualClass: no good new class choices"
881 # must be at least level 2
882 if GemRB
.GetPlayerStat (actor
, IE_LEVEL
) == 1:
883 print "CannotDualClass: level 1"
887 def LoadCommonTables():
888 global ClassTable
, KitListTable
, ClassSkillsTable
, RaceTable
, NextLevelTable
889 global AppearanceAvatarTable
, StrModExTable
, StrModTable
891 print # so the following output isn't appended to an existing line
893 ClassTable
= GemRB
.LoadTable ("classes")
894 if not KitListTable
and GemRB
.HasResource("kitlist", RES_2DA
):
895 KitListTable
= GemRB
.LoadTable ("kitlist")
896 if not ClassSkillsTable
:
897 ClassSkillsTable
= GemRB
.LoadTable ("clskills")
899 RaceTable
= GemRB
.LoadTable ("races")
900 if not NextLevelTable
:
901 NextLevelTable
= GemRB
.LoadTable ("xplevel")
902 if not AppearanceAvatarTable
and GemRB
.HasResource("pdolls", RES_2DA
):
903 AppearanceAvatarTable
= GemRB
.LoadTable ("pdolls")
905 StrModTable
= GemRB
.LoadTable ("strmod")
906 StrModExTable
= GemRB
.LoadTable ("strmodex")
910 GameWindow
= GUIClasses
.GWindow(0)
911 GameControl
= GUIClasses
.GControl(0,0)