factored out the EFFv2 saving into EFFImporter
[gemrb.git] / gemrb / GUIScripts / iwd2 / Feats.py
blob6b0cdf3cca87ec5eace38af98b60324890e3ac31
1 # GemRB - Infinity Engine Emulator
2 # Copyright (C) 2003 The GemRB Project
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #character generation, skills (GUICG6)
20 import GemRB
21 from GUIDefines import *
22 from ie_stats import *
23 import CommonTables
25 FeatWindow = 0
26 TextAreaControl = 0
27 DoneButton = 0
28 FeatTable = 0
29 FeatReqTable = 0
30 TopIndex = 0
31 Level = 0
32 ClassColumn = 0
33 KitColumn = 0
34 RaceColumn = 0
35 FeatsClassColumn = 0
36 PointsLeft = 0
38 # returns the number of feat levels (for example cleave can be taken twice)
39 def MultiLevelFeat(feat):
40 global FeatReqTable
41 return FeatReqTable.GetValue(feat, "MAX_LEVEL")
43 # FIXME: CheckFeatCondition doesn't check for higher level prerequisites
44 # (eg. cleave2 needs +4 BAB and weapon specialisation needs 4 fighter levels)
46 # NOTE: cleave formula is now:
47 # HITBONUS>=4 OR FEAT_CLEAVE<1
49 # specialisation formulas:
50 # FIGHTERLEVEL>=4 OR FEAT_*<2
51 # The default operator was set to 4 (greater or equal), so the majority of the formulas
52 # don't need any more change
53 # Avenger
55 def IsFeatUsable(feat):
56 global FeatReqTable
58 a_value = FeatReqTable.GetValue(feat, "A_VALUE")
59 if a_value<0:
60 #string
61 a_stat = FeatReqTable.GetValue(feat, "A_STAT", 0)
62 else:
63 #stat
64 a_stat = FeatReqTable.GetValue(feat, "A_STAT",2)
65 b_stat = FeatReqTable.GetValue(feat, "B_STAT",2)
66 c_stat = FeatReqTable.GetValue(feat, "C_STAT",2)
67 d_stat = FeatReqTable.GetValue(feat, "D_STAT",2)
68 b_value = FeatReqTable.GetValue(feat, "B_VALUE")
69 c_value = FeatReqTable.GetValue(feat, "C_VALUE")
70 d_value = FeatReqTable.GetValue(feat, "D_VALUE")
71 a_op = FeatReqTable.GetValue(feat, "A_OP")
72 b_op = FeatReqTable.GetValue(feat, "B_OP")
73 c_op = FeatReqTable.GetValue(feat, "C_OP")
74 d_op = FeatReqTable.GetValue(feat, "D_OP")
75 slot = GemRB.GetVar("Slot")
77 return GemRB.CheckFeatCondition(slot, a_stat, a_value, b_stat, b_value, c_stat, c_value, d_stat, d_value, a_op, b_op, c_op, d_op)
79 # checks if a feat was granted due to class/kit/race and returns the number
80 # of granted levels. The bonuses aren't cumulative.
81 def GetBaseValue(feat):
82 global FeatsClassColumn, RaceColumn, KitName
84 Val1 = FeatTable.GetValue(feat, FeatsClassColumn)
85 Val2 = FeatTable.GetValue(feat, RaceColumn)
86 if Val2 < Val1:
87 Val = Val1
88 else:
89 Val = Val2
91 Val3 = 0
92 # only cleric kits have feat bonuses in the original, but the column names are shortened
93 KitName = KitName.replace("CLERIC_","C_")
94 KitColumn = FeatTable.GetColumnIndex(KitName)
95 if KitColumn != 0:
96 Val3 = FeatTable.GetValue(feat, KitColumn)
97 if Val3 > Val:
98 Val = Val3
100 return Val
102 def RedrawFeats():
103 global TopIndex, PointsLeft, FeatWindow, FeatReqTable
105 SumLabel = FeatWindow.GetControl(0x1000000c)
106 if PointsLeft == 0:
107 DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
108 SumLabel.SetTextColor(255, 255, 255)
109 else:
110 DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
111 SumLabel.SetTextColor(255, 255, 0)
113 SumLabel.SetText(str(PointsLeft) )
115 for i in range(0,10):
116 Pos=TopIndex+i
117 FeatName = FeatTable.GetValue(Pos, 1)
118 Label = FeatWindow.GetControl(0x10000001+i)
119 Label.SetText(FeatName)
121 FeatName=FeatTable.GetRowName(Pos) #row name
122 FeatValue = GemRB.GetVar("Feat "+str(Pos))
124 ButtonPlus = FeatWindow.GetControl(i*2+14)
125 ButtonMinus = FeatWindow.GetControl(i*2+15)
126 if FeatValue == 0:
127 ButtonMinus.SetState(IE_GUI_BUTTON_DISABLED)
128 # check if feat is usable - can be taken
129 if IsFeatUsable(FeatName):
130 ButtonPlus.SetState(IE_GUI_BUTTON_ENABLED)
131 Label.SetTextColor(255, 255, 255)
132 else:
133 ButtonPlus.SetState(IE_GUI_BUTTON_DISABLED)
134 Label.SetTextColor(150, 150, 150)
135 else:
136 # check for maximum if there are more feat levels
137 # FIXME also verify that the next level of the feat is usable
138 if MultiLevelFeat(FeatName) > FeatValue:
139 ButtonPlus.SetState(IE_GUI_BUTTON_ENABLED)
140 Label.SetTextColor(255, 255, 255)
141 else:
142 ButtonPlus.SetState(IE_GUI_BUTTON_DISABLED)
143 Label.SetTextColor(150, 150, 150)
144 BaseValue = GemRB.GetVar("BaseFeatValue " + str(Pos))
145 if FeatValue > BaseValue:
146 ButtonMinus.SetState(IE_GUI_BUTTON_ENABLED)
147 else:
148 ButtonMinus.SetState(IE_GUI_BUTTON_DISABLED)
150 if PointsLeft == 0:
151 ButtonPlus.SetState(IE_GUI_BUTTON_DISABLED)
152 Label.SetTextColor(150, 150, 150)
154 levels = FeatReqTable.GetValue(FeatName, "MAX_LEVEL")
155 FeatValueCounter = FeatValue
156 # count backwards, since the controls follow each other in rtl order,
157 # while we need to change the bams in ltr order
158 for j in range(4, -1, -1):
159 Star = FeatWindow.GetControl(i*5+j+36)
160 if 5 - j - 1 < levels:
161 # the star should be there, but which one?
162 if FeatValueCounter > 0:
163 # the full one - the character has already taken a level of this feat
164 Star.SetState(IE_GUI_BUTTON_LOCKED)
165 Star.SetBAM("GUIPFC", 0, 0, -1)
166 Star.SetFlags(IE_GUI_BUTTON_PICTURE, OP_OR)
167 FeatValueCounter = FeatValueCounter - 1
168 else:
169 # the empty one - the character hasn't taken any levels of this feat yet
170 Star.SetState(IE_GUI_BUTTON_LOCKED)
171 Star.SetBAM("GUIPFC", 0, 1, -1)
172 Star.SetFlags(IE_GUI_BUTTON_PICTURE, OP_OR)
173 else:
174 # no star, no bad bam crap
175 Star.SetState(IE_GUI_BUTTON_DISABLED)
176 Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_OR)
177 Star.SetFlags(IE_GUI_BUTTON_PICTURE, OP_NAND)
178 return
180 def ScrollBarPress():
181 global TopIndex
183 TopIndex = GemRB.GetVar("TopIndex")
184 RedrawFeats()
185 return
187 def OnLoad():
188 global FeatWindow, TextAreaControl, DoneButton, TopIndex
189 global FeatTable, FeatReqTable
190 global KitName, Level, PointsLeft
191 global ClassColumn, KitColumn, RaceColumn, FeatsClassColumn
193 GemRB.SetVar("Level",1) #for simplicity
195 Race = GemRB.GetVar("Race")
196 RaceColumn = CommonTables.Races.FindValue(3, Race)
197 RaceName = CommonTables.Races.GetRowName(RaceColumn)
198 # could use column ID as well, but they tend to change :)
199 RaceColumn = CommonTables.Races.GetValue(RaceName, "SKILL_COLUMN")
201 Class = GemRB.GetVar("Class") - 1
202 KitName = CommonTables.Classes.GetRowName(Class)
203 # classcolumn is base class or 0 if it is not a kit
204 ClassColumn = CommonTables.Classes.GetValue(Class, 3) - 1
205 if ClassColumn < 0: #it was already a base class
206 ClassColumn = Class
207 FeatsClassColumn = CommonTables.Classes.GetValue(Class, 2) + 2
208 else:
209 FeatsClassColumn = CommonTables.Classes.GetValue(Class, 3) + 2
211 FeatTable = GemRB.LoadTable("feats")
212 RowCount = FeatTable.GetRowCount()
213 FeatReqTable = GemRB.LoadTable("featreq")
215 for i in range(RowCount):
216 GemRB.SetVar("Feat "+str(i), GetBaseValue(i))
217 GemRB.SetVar("BaseFeatValue " + str(i), GetBaseValue(i))
219 FeatLevelTable = GemRB.LoadTable("featlvl")
220 FeatClassTable = GemRB.LoadTable("featclas")
221 #calculating the number of new feats for the next level
222 PointsLeft = 0
223 #levels start with 1
224 Level = GemRB.GetVar("Level")-1
226 #this one exists only for clerics
227 # Although it should be made extendable to all kits
228 # A FEAT_COLUMN is needed in classes.2da or better yet, a whole new 2da
229 if CommonTables.Classes.GetValue(Class, 3) == "CLERIC":
230 ClassColumn += 3
231 if KitColumn:
232 KitColumn = 3 + KitColumn + 11
234 #Always raise one level at once
235 PointsLeft += FeatLevelTable.GetValue(Level, 0)
236 PointsLeft += FeatClassTable.GetValue(Level, ClassColumn)
238 #racial abilities which seem to be hardcoded in the IWD2 engine
239 #are implemented in races.2da
240 if Level<1:
241 PointsLeft += CommonTables.Races.GetValue(RaceName,'FEATBONUS')
244 GemRB.SetToken("number",str(PointsLeft) )
246 GemRB.LoadWindowPack("GUICG", 800, 600)
247 FeatWindow = GemRB.LoadWindow(55)
248 for i in range(10):
249 Button = FeatWindow.GetControl(i+93)
250 Button.SetVarAssoc("Feat",i)
251 Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, JustPress)
253 Button = FeatWindow.GetControl(i*2+14)
254 Button.SetVarAssoc("Feat",i)
255 Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LeftPress)
257 Button = FeatWindow.GetControl(i*2+15)
258 Button.SetVarAssoc("Feat",i)
259 Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RightPress)
260 for j in range(5):
261 Star=FeatWindow.GetControl(i*5+j+36)
262 Star.SetState(IE_GUI_BUTTON_DISABLED)
263 Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
265 BackButton = FeatWindow.GetControl(105)
266 BackButton.SetText(15416)
267 BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
269 DoneButton = FeatWindow.GetControl(0)
270 DoneButton.SetText(36789)
271 DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
273 TextAreaControl = FeatWindow.GetControl(92)
274 TextAreaControl.SetText(36476)
276 ScrollBarControl = FeatWindow.GetControl(104)
277 ScrollBarControl.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
278 #decrease it with the number of controls on screen (list size)
279 TopIndex = 0
280 GemRB.SetVar("TopIndex",0)
281 ScrollBarControl.SetVarAssoc("TopIndex",RowCount-10)
282 ScrollBarControl.SetDefaultScrollBar ()
284 DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
285 BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
286 RedrawFeats()
287 FeatWindow.SetVisible(WINDOW_VISIBLE)
288 return
291 def JustPress():
292 Pos = GemRB.GetVar("Feat")+TopIndex
293 TextAreaControl.SetText(FeatTable.GetValue(Pos,2) )
294 return
296 def RightPress():
297 global PointsLeft
299 Pos = GemRB.GetVar("Feat")+TopIndex
301 TextAreaControl.SetText(FeatTable.GetValue(Pos,2) )
302 ActPoint = GemRB.GetVar("Feat "+str(Pos) )
303 BaseValue = GemRB.GetVar("BaseFeatValue " + str(Pos))
304 if ActPoint <= 0 or ActPoint <= BaseValue:
305 return
306 GemRB.SetVar("Feat "+str(Pos),ActPoint-1)
307 PointsLeft = PointsLeft + 1
308 RedrawFeats()
309 return
311 def LeftPress():
312 global PointsLeft
314 Pos = GemRB.GetVar("Feat")+TopIndex
316 TextAreaControl.SetText(FeatTable.GetValue(Pos,2) )
317 if PointsLeft < 1:
318 return
319 ActPoint = GemRB.GetVar("Feat "+str(Pos) )
320 # if ActPoint > Level: #Level is 0 for level 1
321 # return
322 GemRB.SetVar("Feat "+str(Pos), ActPoint+1)
323 PointsLeft = PointsLeft - 1
324 RedrawFeats()
325 return
327 def BackPress():
328 if FeatWindow:
329 FeatWindow.Unload()
330 for i in range(FeatTable.GetRowCount()):
331 GemRB.SetVar("Feat "+str(i),0)
332 GemRB.SetNextScript("Skills")
333 return
335 def NextPress():
336 GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
337 if FeatWindow:
338 FeatWindow.Unload()
339 GemRB.SetNextScript("CharGen7")
340 return
342 #Custom feat check functions
343 def Check_AnyOfThree(pl, ass, a, bs, b, cs, c, *garbage):
345 if GemRB.GetPlayerStat(pl, ass)==a: return True
346 if GemRB.GetPlayerStat(pl, bs)==b: return True
347 if GemRB.GetPlayerStat(pl, cs)==c: return True
348 return False
350 #Custom feat check functions
351 def Check_AnyOfThreeGE(pl, ass, a, bs, b, cs, c, *garbage):
353 if GemRB.GetPlayerStat(pl, ass)>=a: return True
354 if GemRB.GetPlayerStat(pl, bs)>=b: return True
355 if GemRB.GetPlayerStat(pl, cs)>=c: return True
356 return False
358 def Check_AllOfThreeGE(pl, ass, a, bs, b, cs, c, *garbage):
360 if GemRB.GetPlayerStat(pl, ass) < a: return False
361 if GemRB.GetPlayerStat(pl, bs) < b: return False
362 if GemRB.GetPlayerStat(pl, cs) < c: return False
363 return True
365 def Check_IsCaster(pl, *garbage):
366 # CLASSLEVELMAGE is IE_LEVEL2 (pst)
367 possible_casters = { IE_LEVEL2:1, IE_LEVELCLERIC:1, IE_LEVELDRUID:1,
368 IE_LEVELSORCEROR:1, IE_LEVELPALADIN:4, IE_LEVELRANGER:4, IE_LEVELBARD:2 }
369 Caster = False
371 for stat in possible_casters:
372 if GemRB.GetPlayerStat(pl, stat) >= possible_casters[stat]:
373 Caster = True
374 break
376 return Caster
378 # besides Concentration > 3, this feat requires Weapon Specialization in 2 weapons.
379 def Check_MaximizedAttacks(pl, a, ass, *garbage):
380 if GemRB.GetPlayerStat(pl, ass) < a: return False
381 # tuple of all weapon proficiency types
382 proficiencies = ( IE_FEAT_BASTARDSWORD, IE_FEAT_AXE, IE_FEAT_BOW, IE_FEAT_FLAIL,
383 IE_FEAT_GREAT_SWORD, IE_FEAT_HAMMER, IE_FEAT_LARGE_SWORD, IE_FEAT_POLEARM )
384 SpecializationCount = 0
386 for proficiency in proficiencies:
387 if GemRB.GetPlayerStat(pl, proficiency) == 3:
388 SpecializationCount += 1
390 return SpecializationCount >= 2