1# -*-python-*- 2# GemRB - Infinity Engine Emulator 3# Copyright (C) 2003-2004 The GemRB Project 4# 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. 9# 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. 14# 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. 18# 19# LUProfsSelection.py - Modular selection of proficiencies 20 21import GemRB 22from GUIDefines import * 23from ie_stats import * 24import GameCheck 25import GUICommon 26import CommonTables 27 28#the different types possible 29LUPROFS_TYPE_LEVELUP = 1 30LUPROFS_TYPE_DUALCLASS = 2 31LUPROFS_TYPE_CHARGEN = 4 32 33#refs to the script calling this 34ProfsWindow = 0 35ProfsCallback = 0 36 37#all our offsets for the various parts of the window 38ProfsOffsetSum = 0 39ProfsOffsetButton1 = 0 40ProfsOffsetLabel = 0 41ProfsOffsetStar = 0 42ProfsOffsetPress = 0 43Profs2ndOffsetButton1 = Profs2ndOffsetStar = Profs2ndOffsetLabel = -1 44ClassNameSave = 0 45OddIDs = 0 46 47#internal variables 48ProfsPointsLeft = 0 49ProfsNumButtons = 0 50ProfsTopIndex = 0 51ProfsTextArea = 0 52ProfsColumn = 0 53ProfsTable = 0 54ProfCount = 0 55 56# the table offset is used in bg2, since in the end they made it use different 57# profs than in bg1, but left the table entries intact 58ProfsTableOffset = 0 59ProfsScrollBar = None 60 61# iwd1/bg1 table that holds the allowed proficiency <-> class mapping 62ClassWeaponsTable = None 63 64ProfsType = None 65 66def SetupProfsWindow (pc, proftype, window, callback, level1=[0,0,0], level2=[1,1,1], classid=0, scroll=True, profTableOffset=8): 67 """Opens the proficiency selection window. 68 69 type specifies the type of selection we are doing; choices are above. 70 window specifies the window to be updated. 71 callback specifies the function to call on changes. 72 classid is sent only during dualclassing to specify the new class.""" 73 74 global ProfsOffsetSum, ProfsOffsetButton1, ProfsOffsetLabel, ProfsOffsetStar, OddIDs 75 global ProfsOffsetPress, ProfsPointsLeft, ProfsNumButtons, ProfsTopIndex 76 global ProfsScrollBar, ProfsTableOffset, ProfsType 77 global ProfsWindow, ProfsCallback, ProfsTextArea, ProfsColumn, ProfsTable, ProfCount 78 global Profs2ndOffsetButton1, Profs2ndOffsetStar, Profs2ndOffsetLabel, ClassNameSave, ClassWeaponsTable 79 80 # make sure we're within ranges 81 GemRB.SetVar ("ProfsPointsLeft", 0) 82 if not window or not callback or len(level1)!=len(level2): 83 return 84 85 # save the values we'll point to 86 ProfsWindow = window 87 ProfsCallback = callback 88 ProfsTableOffset = profTableOffset 89 ProfsType = type 90 91 if proftype == LUPROFS_TYPE_CHARGEN and (GameCheck.IsBG1() or GameCheck.IsBG2()): #chargen 92 ProfsOffsetSum = 9 93 ProfsOffsetButton1 = 11 94 ProfsOffsetStar = 27 95 ProfsOffsetLabel = 1 96 ProfsOffsetPress = 69 97 ProfsNumButtons = 8 98 ProfsTextArea = ProfsWindow.GetControl (68) 99 ProfsTextArea.SetText (9588) 100 if (scroll): 101 ProfsScrollBar = ProfsWindow.GetControl (78) 102 103 if GameCheck.IsBG2() or GameCheck.IsIWD2(): 104 import CharGenCommon 105 CharGenCommon.PositionCharGenWin (ProfsWindow) 106 elif proftype == LUPROFS_TYPE_LEVELUP and GameCheck.IsBG2(): #levelup 107 ProfsOffsetSum = 36 108 ProfsOffsetButton1 = 1 109 ProfsOffsetStar = 48 110 ProfsOffsetLabel = 24 111 ProfsOffsetPress = 112 112 ProfsNumButtons = 7 113 ProfsTextArea = ProfsWindow.GetControl (110) 114 ProfsScrollBar = ProfsWindow.GetControl (108) 115 elif proftype == LUPROFS_TYPE_LEVELUP and GameCheck.IsBG1(): #levelup 116 ProfsOffsetSum = 36 117 ProfsOffsetButton1 = 1 118 ProfsOffsetStar = 48 119 ProfsOffsetLabel = 24 120 ProfsOffsetPress = 17 121 ProfsNumButtons = 8 122 ProfsTextArea = ProfsWindow.GetControl (42) 123 if (scroll): 124 ProfsScrollBar = ProfsWindow.GetControl (108) 125 elif proftype == LUPROFS_TYPE_LEVELUP and GameCheck.IsIWD1(): #levelup 126 ProfsOffsetSum = 36 127 ProfsOffsetButton1 = 1 128 ProfsOffsetStar = 48 129 ProfsOffsetLabel = 24 130 ProfsOffsetPress = -1 131 ProfsNumButtons = 15 # 8+7, the 7 are done with the following vars 132 Profs2ndOffsetButton1 = 150 133 Profs2ndOffsetStar = 115 134 Profs2ndOffsetLabel = 108 135 ProfsTextArea = ProfsWindow.GetControl (42) 136 if (scroll): 137 ProfsScrollBar = ProfsWindow.GetControl (108) 138 OddIDs = 0 139 elif proftype == LUPROFS_TYPE_DUALCLASS and GameCheck.IsIWD1(): #dualclass 140 ProfsOffsetSum = 40 141 ProfsOffsetButton1 = 50 142 ProfsOffsetStar = 0 143 ProfsOffsetLabel = 41 144 ProfsOffsetPress = -1 #66 145 ProfsNumButtons = 15 146 Profs2ndOffsetButton1 = 78 147 Profs2ndOffsetStar = 92 148 Profs2ndOffsetLabel = 126 149 ProfsTextArea = ProfsWindow.GetControl (74) 150 ProfsTextArea.SetText (9588) 151 if (scroll): 152 ProfsScrollBar = ProfsWindow.GetControl (None) 153 OddIDs = 1 154 elif proftype == LUPROFS_TYPE_DUALCLASS and GameCheck.IsBG1(): #dualclass 155 ProfsOffsetSum = 40 156 ProfsOffsetButton1 = 50 157 ProfsOffsetStar = 0 158 ProfsOffsetLabel = 41 159 ProfsOffsetPress = -1 #FIXME 160 ProfsNumButtons = 8 161 ProfsTextArea = ProfsWindow.GetControl (74) 162 ProfsTextArea.SetText (9588) 163 if (scroll): 164 ProfsScrollBar = ProfsWindow.GetControl (None) 165 elif proftype == LUPROFS_TYPE_DUALCLASS: #dualclass 166 ProfsOffsetSum = 40 167 ProfsOffsetButton1 = 50 168 ProfsOffsetStar = 0 169 ProfsOffsetLabel = 41 170 ProfsOffsetPress = 66 171 ProfsNumButtons = 8 172 ProfsTextArea = ProfsWindow.GetControl (74) 173 ProfsTextArea.SetText (9588) 174 if (scroll): 175 ProfsScrollBar = ProfsWindow.GetControl (78) 176 else: #unknown 177 return 178 179 #nullify internal variables 180 GemRB.SetVar ("ProfsTopIndex", 0) 181 ProfsPointsLeft = 0 182 ProfsTopIndex = 0 183 184 ProfsTable = GemRB.LoadTable ("profs") 185 if GameCheck.IsIWD1() or GameCheck.IsBG1(): 186 ClassWeaponsTable = GemRB.LoadTable ("clasweap") 187 else: 188 ClassWeaponsTable = None 189 190 #get the class name 191 IsDual = GUICommon.IsDualClassed (pc, 1) 192 if classid: #for dual classes when we can't get the class dualing to 193 Class = classid 194 elif IsDual[0] == 3: 195 Class = CommonTables.KitList.GetValue (IsDual[2], 7) 196 elif IsDual[0]: 197 Class = GUICommon.GetClassRowName(IsDual[2], "index") 198 Class = CommonTables.Classes.GetValue (Class, "ID") 199 else: 200 Class = GemRB.GetPlayerStat (pc, IE_CLASS) 201 ClassName = GUICommon.GetClassRowName (Class, "class") 202 203 # profs.2da has entries for everyone, so no need to muck around 204 ProfsRate = ProfsTable.GetValue (ClassName, "RATE") 205 206 #figure out how many prof points we have 207 if sum (level1) == 0: #character is being generated (either chargen or dual) 208 ProfsPointsLeft = ProfsTable.GetValue (ClassName, "FIRST_LEVEL") 209 210 ProfIndex = 0 211 IsMulti = GUICommon.IsMultiClassed (pc, 1) 212 if IsMulti[0] > 1: 213 if sum (level1) == 0: #character is being generated (either chargen or dual) 214 # don't give too many points; exact formula unknown, but this works with f/m and f/m/t 215 ProfsPointsLeft += sum(level2) // IsMulti[0] // ProfsRate 216 else: 217 # just look at the fastest prof-gaining class in the bunch and consider their level 218 # eg. a m/t should get a point at 3/4 and 7/8 (rate of 4) 219 BestRate = 100 220 for cls in IsMulti[1:]: 221 if cls == 0: 222 break 223 ClsName = GUICommon.GetClassRowName (cls, "class") 224 Rate = ProfsTable.GetValue (ClsName, "RATE") 225 if Rate < BestRate: 226 BestRate = Rate 227 ProfIndex = IsMulti[1:].index(cls) 228 229 ProfsPointsLeft += level2[ProfIndex] // ProfsRate - level1[ProfIndex] // ProfsRate 230 else: 231 if GUICommon.IsDualSwap (pc): 232 ProfIndex = 1 233 234 #we need these 2 number to floor before subtracting 235 ProfsPointsLeft += level2[ProfIndex] // ProfsRate - level1[ProfIndex] // ProfsRate 236 237 #setup prof vars for passing between functions 238 ProfsTable = GemRB.LoadTable ("weapprof") 239 240 # weapprof has no sorcerer entry 241 if ClassName == "SORCERER": 242 ClassName = "MAGE" 243 ClassNameSave = ClassName 244 245 # if we have the classweapons table, use it 246 if ClassWeaponsTable: 247 ProfsColumn = ClassWeaponsTable.GetRowIndex (ClassName) 248 else: 249 Kit = GUICommon.GetKitIndex (pc) 250 if Kit and proftype != LUPROFS_TYPE_DUALCLASS and IsMulti[0]<2 and IsDual[0] in [0, 3]: 251 #if we do kit with dualclass, we'll get the old kit (usually) 252 #also don't want to worry about kitted multis 253 ProfsColumn = CommonTables.KitList.GetValue (Kit, 5) 254 else: 255 ProfsColumn = ProfsTable.GetColumnIndex (ClassName) 256 257 #setup some basic counts 258 RowCount = ProfsTable.GetRowCount () - ProfsTableOffset 259 ProfCount = RowCount-ProfsNumButtons #decrease it with the number of controls 260 261 ProfsAssignable = 0 262 TwoWeapIndex = ProfsTable.GetRowIndex ("2WEAPON") 263 for i in range(RowCount): 264 ProfName = ProfsTable.GetValue (i+ProfsTableOffset, 1) 265 #decrease it with the number of invalid proficiencies 266 if ProfName > 0x1000000 or ProfName <= 0: 267 ProfCount -= 1 268 269 #we only need the low 3 bits for proficiencies on levelup; otherwise 270 #we just set them all to 0 271 currentprof = 0 272 if proftype == LUPROFS_TYPE_LEVELUP: 273 stat = ProfsTable.GetValue (i+ProfsTableOffset, 0) 274 if GameCheck.IsBG1(): 275 stat = stat + IE_PROFICIENCYBASTARDSWORD 276 currentprof = GemRB.GetPlayerStat (pc, stat)&0x07 277 else: 278 #rangers always get 2 points in 2 weapons style 279 if (i+ProfsTableOffset) == TwoWeapIndex and "RANGER" in ClassName.split("_"): 280 currentprof = 2 281 GemRB.SetVar ("Prof "+str(i), currentprof) 282 GemRB.SetVar ("ProfBase "+str(i), currentprof) 283 284 #see if we can assign to this prof 285 if ClassWeaponsTable: 286 # this table has profs as rows, so ignore the weird use of the column var 287 # it also has different ordering than ProfsTable 288 col = ClassWeaponsTable.GetColumnIndex (ProfsTable.GetRowName(i)) 289 maxprof = ClassWeaponsTable.GetValue (ProfsColumn, col) 290 else: 291 maxprof = ProfsTable.GetValue(i+ProfsTableOffset, ProfsColumn) 292 if maxprof > currentprof: 293 ProfsAssignable += maxprof-currentprof 294 295 #correct the profs left if we can't assign that much 296 if ProfsPointsLeft > ProfsAssignable: 297 ProfsPointsLeft = ProfsAssignable 298 GemRB.SetVar ("ProfsPointsLeft", ProfsPointsLeft) 299 300 # setup the +/- and info controls 301 for i in range (ProfsNumButtons): 302 if ProfsOffsetPress != -1: 303 Button=ProfsWindow.GetControl(i+ProfsOffsetPress) 304 Button.SetVarAssoc("Prof", i) 305 Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProfsJustPress) 306 307 cid = i*2+ProfsOffsetButton1 308 if Profs2ndOffsetButton1 != -1 and i > 7: 309 cid = (i-8)*2+Profs2ndOffsetButton1 310 311 Button=ProfsWindow.GetControl(cid) 312 Button.SetVarAssoc("Prof", i) 313 Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProfsLeftPress) 314 Button.SetActionInterval (200) 315 316 Button=ProfsWindow.GetControl(cid+1) 317 Button.SetVarAssoc("Prof", i) 318 Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProfsRightPress) 319 Button.SetActionInterval (200) 320 321 if(ProfsScrollBar): 322 # proficiencies scrollbar 323 ProfsScrollBar.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, ProfsScrollBarPress) 324 ProfsWindow.SetEventProxy(ProfsScrollBar) 325 ProfsScrollBar.SetVarAssoc ("ProfsTopIndex", ProfCount) 326 ProfsRedraw (1) 327 return 328 329def ProfsRedraw (first=0): 330 """Redraws the proficiencies part of the window. 331 332 If first is true, it skips ahead to the first assignable proficiency.""" 333 334 global ProfsTopIndex, ProfsScrollBar 335 336 ProfSumLabel = ProfsWindow.GetControl(0x10000000+ProfsOffsetSum) 337 ProfSumLabel.SetText(str(ProfsPointsLeft)) 338 SkipProfs = [] 339 340 for i in range(ProfsNumButtons): 341 Pos=ProfsTopIndex+i 342 ProfName = ProfsTable.GetValue(Pos+ProfsTableOffset, 1) #we add the bg1 skill count offset 343 if ClassWeaponsTable: # iwd 344 MaxProf = ClassWeaponsTable.GetValue (ClassNameSave, ProfsTable.GetRowName(Pos)) 345 else: 346 MaxProf = ProfsTable.GetValue(Pos+ProfsTableOffset, ProfsColumn) 347 ProfName = ProfsTable.GetValue(Pos+ProfsTableOffset, 1) 348 if ProfName > 0x1000000 or ProfName < 0: 349 MaxProf = 0 350 351 cid = i*2+ProfsOffsetButton1 352 if Profs2ndOffsetButton1 != -1 and i > 7: 353 cid = (i-8)*2+Profs2ndOffsetButton1 354 Button1=ProfsWindow.GetControl (cid) 355 Button2=ProfsWindow.GetControl (cid+1) 356 if MaxProf == 0: 357 Button1.SetState(IE_GUI_BUTTON_DISABLED) 358 Button2.SetState(IE_GUI_BUTTON_DISABLED) 359 Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR) 360 Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR) 361 if GameCheck.IsBG2() and (i==0 or ((i-1) in SkipProfs)): 362 SkipProfs.append (i) 363 else: 364 Button1.SetState(IE_GUI_BUTTON_ENABLED) 365 Button2.SetState(IE_GUI_BUTTON_ENABLED) 366 Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND) 367 Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND) 368 369 cid = 0x10000000 + ProfsOffsetLabel + i 370 if Profs2ndOffsetLabel != -1 and i > 7: 371 if OddIDs: 372 cid = 0x10000000 + Profs2ndOffsetLabel + 2*(i - 8) 373 else: 374 cid = 0x10000000 + Profs2ndOffsetLabel + i - 8 375 376 Label=ProfsWindow.GetControl (cid) 377 Label.SetText(ProfName) 378 379 ActPoint = GemRB.GetVar("Prof "+str(Pos) ) 380 for j in range(5): #5 is maximum distributable 381 cid = i*5 + j + ProfsOffsetStar 382 if Profs2ndOffsetStar != -1 and i > 7: 383 cid = (i-8)*5 + j + Profs2ndOffsetStar 384 Star=ProfsWindow.GetControl (cid) 385 Star.SetSprites("GUIPFC", 0, 0, 0, 0, 0) 386 if ActPoint > j: 387 Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND) 388 else: 389 Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR) 390 391 if first and len (SkipProfs): 392 ProfsTopIndex += SkipProfs[-1]+1 393 GemRB.SetVar ("ProfsTopIndex", ProfsTopIndex) 394 if (ProfsScrollBar): 395 ProfsScrollBar.SetVarAssoc ("ProfsTopIndex", ProfCount) 396 ProfsRedraw () 397 return 398 399def ProfsScrollBarPress(): 400 """Scrolls the window by reassigning ProfsTopIndex.""" 401 402 global ProfsTopIndex 403 404 ProfsTopIndex = GemRB.GetVar ("ProfsTopIndex") 405 ProfsRedraw () 406 return 407 408def ProfsJustPress(btn, val): 409 """Updates the text area with a description of the proficiency.""" 410 Pos = val+ProfsTopIndex 411 ProfsTextArea.SetText (ProfsTable.GetValue(Pos+ProfsTableOffset, 2) ) 412 return 413 414def ProfsRightPress(btn, val): 415 """Decrease the current proficiency by one.""" 416 417 global ProfsPointsLeft 418 419 Pos = val+ProfsTopIndex 420 ProfsTextArea.SetText(ProfsTable.GetValue(Pos+ProfsTableOffset, 2) ) 421 ActPoint = GemRB.GetVar("Prof "+str(Pos) ) 422 MinPoint = GemRB.GetVar ("ProfBase "+str(Pos) ) 423 if ActPoint <= 0 or ActPoint <= MinPoint: 424 return 425 GemRB.SetVar("Prof "+str(Pos),ActPoint-1) 426 ProfsPointsLeft += 1 427 GemRB.SetVar ("ProfsPointsLeft", ProfsPointsLeft) 428 ProfsRedraw () 429 ProfsCallback () 430 return 431 432def ProfsLeftPress(btn, val): 433 """Increases the current proficiency by one.""" 434 435 global ProfsPointsLeft 436 437 Pos = val+ProfsTopIndex 438 ProfsTextArea.SetText(ProfsTable.GetValue(Pos+ProfsTableOffset, 2) ) 439 if ProfsPointsLeft == 0: 440 return 441 if GameCheck.IsIWD1() or GameCheck.IsBG1(): 442 ProfMaxTable = GemRB.LoadTable ("profsmax") 443 if ProfsType == LUPROFS_TYPE_CHARGEN: 444 MaxProf = ProfMaxTable.GetValue(ClassNameSave, "FIRST_LEVEL") 445 else: 446 MaxProf = ProfMaxTable.GetValue(ClassNameSave, "OTHER_LEVELS") 447 else: 448 MaxProf = ProfsTable.GetValue(Pos+ProfsTableOffset, ProfsColumn) 449 if MaxProf>5: 450 MaxProf = 5 451 452 ActPoint = GemRB.GetVar("Prof "+str(Pos) ) 453 if ActPoint >= MaxProf: 454 return 455 GemRB.SetVar("Prof "+str(Pos),ActPoint+1) 456 ProfsPointsLeft -= 1 457 GemRB.SetVar ("ProfsPointsLeft", ProfsPointsLeft) 458 ProfsRedraw () 459 ProfsCallback () 460 return 461 462def ProfsSave (pc, proftype = LUPROFS_TYPE_LEVELUP): 463 """Updates the actor with the new proficiencies.""" 464 465 ProfCount = ProfsTable.GetRowCount () - ProfsTableOffset 466 for i in range(ProfCount): # skip bg1 weapprof.2da proficiencies 467 ProfID = ProfsTable.GetValue (i+ProfsTableOffset, 0) 468 if GameCheck.IsBG1(): 469 ProfID = ProfID + IE_PROFICIENCYBASTARDSWORD 470 SaveProf = GemRB.GetVar ("Prof "+str(i)) 471 472 if proftype == LUPROFS_TYPE_CHARGEN and GameCheck.IsBG2(): 473 GemRB.DispelEffect (pc, "Proficiency", ProfID) 474 else: 475 if proftype != LUPROFS_TYPE_DUALCLASS: 476 OldProf = GemRB.GetPlayerStat (pc, ProfID) & 0x38 477 SaveProf = OldProf | SaveProf 478 else: # gotta move the old prof to the back for dual class 479 OldProf = GemRB.GetPlayerStat (pc, ProfID) & 0x07 480 SaveProf = (OldProf << 3) | SaveProf 481 482 GemRB.SetPlayerStat (pc, ProfID, SaveProf) 483 if GameCheck.IsBG2() and (proftype == LUPROFS_TYPE_LEVELUP or proftype == LUPROFS_TYPE_CHARGEN): 484 if SaveProf: 485 GemRB.ApplyEffect (pc, "Proficiency", SaveProf, ProfID) 486 return 487 488def ProfsNullify (): 489 """Resets all of the internal variables to 0.""" 490 491 global ProfsTable 492 if not ProfsTable: 493 ProfsTable = GemRB.LoadTable ("weapprof") 494 for i in range (ProfsTable.GetRowCount()-ProfsTableOffset+1): #skip bg1 profs 495 GemRB.SetVar ("Prof "+str(i), 0) 496 GemRB.SetVar ("ProfBase "+str(i), 0) 497 return 498