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
20# LevelUp.py - scripts to control the level up functionality and windows
21import GemRB
22from GUIDefines import *
23from ie_stats import *
24from ie_restype import RES_2DA
25import GameCheck
26import GUICommon
27import Spellbook
28import CommonTables
29import LUSpellSelection
30import LUCommon
31if GameCheck.HasTOB():
32	import LUHLASelection
33import LUProfsSelection
34import LUSkillsSelection
35import Actor
36
37LevelUpWindow = None
38DoneButton = 0
39TextAreaControl = 0
40InfoCounter = 1
41NewProfPoints = 0
42NewSkillPoints = 0
43LevelDiff = 0
44Level = 0
45Classes = 0
46NumClasses = 0
47DualSwap = 0
48KitName = 0
49IsDual = 0
50IsMulti = 0
51pc = 0
52ClassName = 0
53actor = 0
54
55# old values (so we don't add too much)
56OldHPMax = 0		# << old maximum hitpoints
57OldSaves = [0]*5	# << old saves
58OldThaco = 0		# << old thac0 value
59OldLore = 0		# << old lore value
60OldDSpells = [0]*7	# << old divine spells per level
61OldWSpells = [0]*9	# << old wizard spells per level
62NewDSpells = [0]*7	# << new divine spells per level
63NewWSpells = [0]*9	# << new wizard spells per level
64DeltaDSpells = 0	# << total new divine spells
65DeltaWSpells = 0	# << total new wizard spells
66
67def OpenLevelUpWindow():
68	"""Sets up the level up window."""
69	import GUIREC
70
71	global LevelUpWindow, TextAreaControl, NewProfPoints, actor
72	global DoneButton
73	global NewSkillPoints, KitName, LevelDiff
74	global Level, Classes, NumClasses, DualSwap, IsMulti
75	global OldHPMax, OldSaves, OldLore, OldThaco, DeltaDSpells, DeltaWSpells
76	global NewDSpells, NewWSpells, OldDSpells, OldWSpells, pc, HLACount, ClassName, IsDual
77
78	LevelUpWindow = GemRB.LoadWindow (3)
79
80	if GameCheck.IsBG2():
81		InfoButton = LevelUpWindow.GetControl (125)
82		InfoButton.SetText (13707)
83		InfoButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUpInfoPress)
84		# hide "Character Generation"
85		LevelUpWindow.DeleteControl(0x1000007e)
86
87	DoneButton = LevelUpWindow.GetControl (0)
88	DoneButton.SetText (11962)
89	DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUpDonePress)
90	DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
91	DoneButton.MakeDefault()
92	# also disable closing by ESC, so we don't stack upgrades
93	DoneButton.MakeEscape ()
94
95	# name
96	pc = GemRB.GameGetSelectedPCSingle ()
97	actor = Actor.Actor(pc)
98	Label = LevelUpWindow.GetControl (0x10000000+90)
99	Label.SetText (GemRB.GetPlayerName (pc))
100
101	# some current values
102	OldHPMax = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS, 1)
103	OldThaco = GemRB.GetPlayerStat (pc, IE_TOHIT, 1)
104	OldLore = GemRB.GetPlayerStat (pc, IE_LORE, 1)
105	for i in range (5):
106		OldSaves[i] = GemRB.GetPlayerStat (pc, IE_SAVEVSDEATH+i, 1)
107
108	# class
109	Label = LevelUpWindow.GetControl (0x10000000+106)
110	Label.SetText (GUICommon.GetActorClassTitle (pc))
111	print("Title: " + GUICommon.GetActorClassTitle (pc) + "\tActor Title: " + actor.ClassTitle())
112
113	Class = GemRB.GetPlayerStat (pc, IE_CLASS)
114	print("Class: " + str(Class) + "\tActor Class: " + str(actor.classid))
115
116	# kit
117	ClassName = GUICommon.GetClassRowName (Class, "class")
118	Kit = GUICommon.GetKitIndex (pc)
119	print("Kit: " + str(Kit) + "\tActor Kit: " + str(actor.KitIndex()))
120	print("ClassName: " + ClassName + "\tActor ClassNames: " + str(actor.ClassNames()))
121
122	# need this for checking gnomes
123	RaceName = GemRB.GetPlayerStat (pc, IE_RACE, 1)
124	RaceName = CommonTables.Races.FindValue (3, RaceName)
125	RaceName = CommonTables.Races.GetRowName (RaceName)
126
127	# figure our our proficiency table and index
128	if Kit == 0:
129		KitName = ClassName
130	else:
131		#rowname is just a number, the kitname is the first data column
132		KitName = CommonTables.KitList.GetValue(Kit, 0)
133
134	# our multiclass variables
135	IsMulti = GUICommon.IsMultiClassed (pc, 1)
136	Classes = [IsMulti[1], IsMulti[2], IsMulti[3]]
137	NumClasses = IsMulti[0] # 2 or 3 if IsMulti; 0 otherwise
138	IsMulti = NumClasses > 1
139	IsDual = 0
140	DualSwap = 0
141
142	# not multi, check dual
143	if not IsMulti:
144		# check if we're dual classed
145		IsDual = GUICommon.IsDualClassed (pc, 1)
146		Classes = []
147
148		# either dual or single only care about 1 class
149		NumClasses = 1
150
151		# not dual, must be single
152		if IsDual[0] == 0:
153			Classes = [Class]
154		else: # resolve kits to classes (new class goes first)
155			if IsDual[0] == 3: # 1st kit
156				Classes.append (CommonTables.KitList.GetValue (IsDual[2], 7))
157			else: # 1st class
158				Classes.append (CommonTables.Classes.GetValue (IsDual[2], 5))
159			if IsDual[0] == 1: # 2nd kit
160				Classes.append (CommonTables.KitList.GetValue (IsDual[1], 7))
161			else: # 2nd class
162				Classes.append (CommonTables.Classes.GetValue (IsDual[1], 5))
163
164		# store a boolean for IsDual
165		IsDual = IsDual[0] > 0
166
167	print("NumClasses: " + str(NumClasses) + "\tActor NumClasses: " + str(actor.NumClasses()))
168
169	if IsDual:
170		# convert the classes from indicies to class id's
171		DualSwap = GUICommon.IsDualSwap (pc)
172		ClassName = GUICommon.GetClassRowName (Classes[0], "index")
173		KitName = ClassName # for simplicity throughout the code
174
175	print("Classes: " + str(Classes) + "\tActor Classes: " + str(actor.Classes()))
176	print("IsDual: " + str(IsDual > 0) + "\tActor IsDual: " + str(actor.isdual))
177
178	# get the next target levels and difference between levels
179	Level = LUCommon.GetNextLevels(pc, Classes)
180	LevelDiff = LUCommon.GetLevelDiff(pc, Level)
181
182	# clear some globals, since we may get called multiple times with different classes
183	DeltaWSpells = 0
184	DeltaDSpells = 0
185	OldDSpells = [0]*7
186	OldWSpells = [0]*9
187	NewDSpells = [0]*7
188	NewWSpells = [0]*9
189
190	# calculate the new spells (results are stored in global variables)
191	GetNewSpells(pc, Classes, Level, LevelDiff, Kit)
192
193# this is handled by core
194#		# setup class bonuses for this class
195#		if IsMulti or IsDual or Kit == 0:
196#			ABTable = CommonTables.ClassSkills.GetValue (TmpClassName, "ABILITIES")
197#		else: # single-classed with a kit
198#			ABTable = CommonTables.KitList.GetValue (str(Kit), "ABILITIES")
199#
200#		# add the abilites if we have a table to ref
201#		if ABTable != "*" and GemRB.HasResource (ABTable, RES_2DA, 1):
202#			GUICommon.AddClassAbilities (pc, ABTable, Level[i], LevelDiff[i])
203
204	print("Actor CurrentLevels:" + str(actor.Levels()))
205	print("Levels: " + str(Level) + "\tActor NextLevels: " + str(actor.NextLevels()))
206	print("LevelDiffs: " + str(LevelDiff) + "\tActor LevelDiffs: " + str(actor.LevelDiffs()))
207
208	#update our saves, thaco, hp and lore
209	LUCommon.SetupSavingThrows (pc, Level)
210	LUCommon.SetupThaco (pc, Level)
211	LUCommon.SetupLore (pc, LevelDiff)
212	LUCommon.SetupHP (pc, Level, LevelDiff)
213
214	# we set up these labels so late, so they can show the new HP
215	if GameCheck.IsBG1() or GameCheck.IsIWD1():
216		# armorclass
217		Label = LevelUpWindow.GetControl (0x10000057)
218		Label.SetText (str (GemRB.GetPlayerStat (pc, IE_ARMORCLASS)))
219		Label.SetTooltip (17183)
220
221		# hp now
222		Label = LevelUpWindow.GetControl (0x10000058)
223		Label.SetText (str (GemRB.GetPlayerStat (pc, IE_HITPOINTS)))
224		Label.SetTooltip (17184)
225
226		# hp max
227		Label = LevelUpWindow.GetControl (0x10000059)
228		Label.SetText (str (GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)))
229		Label.SetTooltip (17378)
230
231	# use total levels for HLAs
232	HLACount = 0
233	if GameCheck.HasTOB(): # make sure SoA doesn't try to get it
234		HLATable = GemRB.LoadTable("lunumab")
235		# we need to check each level against a multi value (this is kinda screwy)
236		if actor.multiclass:
237			print("Actor HLA Names: " + str(["MULTI" + str(actor.NumClasses()) + name \
238				for name in actor.ClassNames()]))
239		else:
240			print("Actor HLA Names: " + str(actor.ClassNames()))
241
242		for i in range (NumClasses):
243			if IsMulti:
244				# get the row name for lookup ex. MULTI2FIGHTER, MULTI3THIEF
245				MultiName = GUICommon.GetClassRowName (Classes[i], "class")
246				MultiName = "MULTI" + str(NumClasses) + MultiName
247			else:
248				MultiName = ClassName
249
250			# if we can't learn for this class, we can't learn at all
251			FirstLevel = HLATable.GetValue (MultiName, "FIRST_LEVEL", GTV_INT)
252			if Level[i] < FirstLevel:
253				HLACount = 0
254				break
255
256			if (Level[i] - LevelDiff[i]) < FirstLevel:
257				# count only from FirstLevel up
258				HLACount += (Level[i] - FirstLevel + 1)
259			else:
260				HLACount += LevelDiff[i]
261
262		# set values required by the hla level up code
263		HLACount = HLACount // HLATable.GetValue (ClassName, "RATE", GTV_INT)
264		GemRB.SetVar ("HLACount", HLACount)
265	if GameCheck.IsBG2():
266		HLAButton = LevelUpWindow.GetControl (126)
267		if HLACount:
268			HLAButton.SetText (4954)
269			HLAButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUpHLAPress)
270		else:
271			HLAButton.SetFlags (IE_GUI_BUTTON_DISABLED, OP_OR)
272
273	# setup our profs
274	Level1 = []
275	for i in range (len (Level)):
276		Level1.append (Level[i]-LevelDiff[i])
277	if GameCheck.IsBG2():
278		LUProfsSelection.SetupProfsWindow (pc, LUProfsSelection.LUPROFS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level)
279	else:
280		LUProfsSelection.SetupProfsWindow (pc, LUProfsSelection.LUPROFS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level, 0, False, 0)
281	NewProfPoints = GemRB.GetVar ("ProfsPointsLeft")
282
283	#we autohide the skills and let SetupSkillsWindow show them if needbe
284	for i in range (4):
285		HideSkills (i)
286	if GameCheck.IsBG2():
287		LUSkillsSelection.SetupSkillsWindow (pc, LUSkillsSelection.LUSKILLS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level)
288	else:
289		LUSkillsSelection.SetupSkillsWindow (pc, LUSkillsSelection.LUSKILLS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level, 0, False)
290	NewSkillPoints = GemRB.GetVar ("SkillPointsLeft")
291
292	if GameCheck.IsBG2():
293		TextAreaControl = LevelUpWindow.GetControl(110)
294		TextAreaControl.SetText(GetLevelUpNews())
295	else:
296		TextAreaControl = LevelUpWindow.GetControl(42)
297		TextAreaControl.SetText(GUIREC.GetStatOverview(pc, LevelDiff))
298
299	RedrawSkills()
300	LevelUpWindow.ShowModal (MODAL_SHADOW_GRAY)
301
302	# if we have a sorcerer who can learn spells, we need to do spell selection
303	for c in range(NumClasses):
304		if Spellbook.HasSorcererBook (pc, Classes[c]) and DeltaWSpells > 0:
305			LUSpellSelection.OpenSpellsWindow (pc, "SPLSRCKN", Level[c], LevelDiff[c])
306
307def HideSkills(i):
308	"""Hides the given skill label from view."""
309	global LevelUpWindow
310
311	Label = LevelUpWindow.GetControl (0x10000000+32+i)
312	Label.SetText ("")
313	Button1 = LevelUpWindow.GetControl(i*2+17)
314	Button1.SetState(IE_GUI_BUTTON_DISABLED)
315	Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
316	Button2 = LevelUpWindow.GetControl(i*2+18)
317	Button2.SetState(IE_GUI_BUTTON_DISABLED)
318	Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
319	Label = LevelUpWindow.GetControl(0x10000000+43+i)
320	Label.SetText("")
321
322def RedrawSkills():
323	"""Redraws the entire window.
324
325	Called whenever a state changes, such as a proficiency or skill being
326	added or taken away."""
327
328	global DoneButton, LevelUpWindow, HLACount
329
330	# we need to disable the HLA button if we don't have any HLAs left
331	HLACount = GemRB.GetVar ("HLACount")
332	if GameCheck.IsBG2() and HLACount == 0:
333		# turn the HLA button off
334		HLAButton = LevelUpWindow.GetControl (126)
335		HLAButton.SetState(IE_GUI_BUTTON_DISABLED)
336
337	# enable the done button if they've allocated all points
338	# sorcerer spell selection (if applicable) comes after hitting the done button
339	ProfPointsLeft = GemRB.GetVar ("ProfsPointsLeft")
340	SkillPointsLeft = GemRB.GetVar ("SkillPointsLeft")
341	if ProfPointsLeft == 0 and SkillPointsLeft == 0 and HLACount == 0:
342		DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
343	else:
344		DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
345	return
346
347def GetLevelUpNews():
348	"""Returns a string containing improvements gain on level up.
349
350	These include: HP, spells per level, and lore, among others."""
351
352	News = GemRB.GetString (5259) + '\n\n'
353
354	# display if our class has been reactivated
355	if IsDual:
356		if (Level[0] - LevelDiff[0]) <= Level[1] and Level[0] > Level[1]:
357			News = GemRB.GetString (5261) + '\n\n'
358
359	# 5271 - Additional weapon proficiencies
360	if NewProfPoints > 0:
361		News += GemRB.GetString (5271) + ": " + str(NewProfPoints) + '\n\n'
362
363	# temps to compare all our new saves against (we get the best of our saving throws)
364	LOHGain = 0
365	BackstabMult = 0
366
367	for i in range(NumClasses):
368		# get the class name
369		TmpClassName = GUICommon.GetClassRowName (Classes[i], "class")
370
371		# backstab
372		# NOTE: Stalkers and assassins should get the correct mods at the correct levels based
373		#	on AP_SPCL332 in their respective CLAB files.
374		# APND: Stalkers appear to get the correct mod at the correct levels; however, because they start
375		#	at backstab multi x1, they are x1 too many at their respective levels.
376		if Classes[i] == 4 and GemRB.GetPlayerStat (pc, IE_BACKSTABDAMAGEMULTIPLIER, 1) > 1: # we have a thief who can backstab (2 at level 1)
377			# save the backstab multiplier if we have a thief
378			BackstabTable = GemRB.LoadTable ("BACKSTAB")
379			BackstabMult = BackstabTable.GetValue (0, Level[i])
380
381		# lay on hands
382		LOHTable = CommonTables.ClassSkills.GetValue (TmpClassName, "LAYHANDS")
383		if LOHTable != "*":
384			# inquisitors and undead hunters don't get lay on hands out the chute, whereas cavaliers
385			# and unkitted paladins do; therefore, we check for the existence of lay on hands to ensure
386			# the character should get the new value; LoH is defined in GA_SPCL211 if anyone wants to
387			# make a pally kit with LoH
388			if (Spellbook.HasSpell (pc, IE_SPELL_TYPE_INNATE, 0, "SPCL211") >= 0):
389				LOHTable = GemRB.LoadTable (LOHTable)
390				LOHGain = LOHTable.GetValue (0, Level[i]) - LOHTable.GetValue (0, Level[i]-LevelDiff[i])
391
392	# saving throws
393		# 5277 death
394		# 5278 wand
395		# 5279 polymorph
396		# 5282 breath
397		# 5292 spell
398	# include in news if the save is updated
399	Changed = 0
400	for i in range (5):
401		CurrentSave = GemRB.GetPlayerStat (pc, IE_SAVEVSDEATH+i, 1)
402		SaveString = 5277+i
403		if i == 3:
404			SaveString = 5282
405		elif i == 4:
406			SaveString = 5292
407
408		if CurrentSave < OldSaves[i]:
409			News += GemRB.GetString (SaveString) + ": " + str(OldSaves[i]-CurrentSave) + '\n'
410			Changed = 1
411	if Changed:
412		News += '\n'
413
414	# 5305 - THAC0 Reduced by
415	# only output if there is a change in thaco
416	NewThaco = GemRB.GetPlayerStat (pc, IE_TOHIT, 1)
417	if (NewThaco < OldThaco):
418		News += GemRB.GetString (5305) + ": " + str(OldThaco-NewThaco) + '\n\n'
419
420	# new spell slots
421		# 5373 - Additional Priest Spells
422		# 5374 - Additional Mage Spells
423		# 61269 - Level <LEVEL> Spells
424	if DeltaDSpells > 0: # new divine spells
425		News += GemRB.GetString (5373) + '\n'
426		for i in range (len (NewDSpells)):
427			# only display classes with new spells
428			if (NewDSpells[i]-OldDSpells[i]) == 0:
429				continue
430			GemRB.SetToken("level", str(i+1))
431			News += GemRB.GetString(61269)+": " + str(NewDSpells[i]-OldDSpells[i]) + '\n'
432		News += '\n'
433	if DeltaWSpells > 0: # new wizard spells
434		News += GemRB.GetString (5374) + '\n'
435		for i in range (len (NewWSpells)):
436			# only display classes with new spells
437			if (NewWSpells[i]-OldWSpells[i]) == 0:
438				continue
439			GemRB.SetToken("level", str(i+1))
440			News += GemRB.GetString(61269)+": " + str(NewWSpells[i]-OldWSpells[i]) + '\n'
441		News += '\n'
442
443	# 5375 - Backstab Multiplier Increased by
444	# this auto-updates... we just need to inform of the update
445	BSGain = BackstabMult - GemRB.GetPlayerStat (pc, IE_BACKSTABDAMAGEMULTIPLIER, 1)
446	if (BSGain > 0):
447		News += GemRB.GetString (5375) + ": " + str(BSGain) + '\n\n'
448
449	# 5376 - Lay on Hands increased
450	if LOHGain > 0:
451		News += GemRB.GetString (5376) + ": " + str(LOHGain) + '\n\n'
452
453	# 5293 - HP increased by
454	if (OldHPMax != GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS, 1)):
455		News += GemRB.GetString (5293) + ": " + str(GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS, 1) - OldHPMax) + '\n'
456
457	# 5377 - Lore Increased by
458	# add the lore gain if we haven't done so already
459	NewLore = GemRB.GetPlayerStat (pc, IE_LORE, 1)
460	if NewLore > OldLore:
461		News += GemRB.GetString (5377) + ": " + str(NewLore-OldLore) + '\n\n'
462
463	# 5378 - Additional Skill Points
464	# ranger and bard skill(point) gain is not mentioned here in the original
465	if NewSkillPoints > 0:
466		News += GemRB.GetString (5378) + ": " + str(NewSkillPoints) + '\n'
467
468	return News
469
470def LevelUpInfoPress():
471	"""Displays new abilites gained on level up.
472
473	Alternates between overall and modified stats."""
474	import GUIREC
475	global LevelUpWindow, TextAreaControl, InfoCounter, LevelDiff
476
477	if InfoCounter % 2:
478		# call GetStatOverview with the new levels, so the future overview is shown
479		# TODO: show only xp, levels, thac0, #att, lore, reputation, backstab, saving throws
480		TextAreaControl.SetText(GUIREC.GetStatOverview(pc, LevelDiff))
481	else:
482		TextAreaControl.SetText(GetLevelUpNews())
483	InfoCounter += 1
484	return
485
486# save the results
487def LevelUpDonePress():
488	"""Updates the PC with the new choices.
489
490	Closes the window when finished."""
491	import GUICommonWindows
492	import GUIREC
493
494	# proficiencies
495	LUProfsSelection.ProfsSave (pc)
496
497	# skills
498	LUSkillsSelection.SkillsSave (pc)
499
500	# level
501	if DualSwap: # swap the IE_LEVELs around if a backward dual
502		GemRB.SetPlayerStat (pc, IE_LEVEL2, Level[0])
503		GemRB.SetPlayerStat (pc, IE_LEVEL, Level[1])
504	else:
505		GemRB.SetPlayerStat (pc, IE_LEVEL, Level[0])
506		GemRB.SetPlayerStat (pc, IE_LEVEL2, Level[1])
507	GemRB.SetPlayerStat (pc, IE_LEVEL3, Level[2])
508
509	print("Levels:", Level[0], "/", Level[1], "/", Level[2])
510
511	# spells
512	SaveNewSpells()
513
514	# hlas
515	# level, xp and other stuff by the core?
516
517	# 5261 - Regained abilities from inactive class
518	if IsDual: # we're dual classed
519		print("activation?")
520		if (Level[0] - LevelDiff[0]) <= Level[1] and Level[0] > Level[1]: # our new classes now surpasses our old class
521			print("reactivating base class")
522			ReactivateBaseClass ()
523
524	if LevelUpWindow:
525		LevelUpWindow.Close()
526	GUICommonWindows.UpdatePortraitWindow ()
527	return
528
529def LevelUpHLAPress ():
530	"""Opens the HLA selection window."""
531
532	# we can turn the button off and temporarily set HLACount to 0
533	# because there is no cancel button on the HLA window; therefore,
534	# it's guaranteed to come back as 0
535	TmpCount = GemRB.GetVar ("HLACount")
536	GemRB.SetVar ("HLACount", 0)
537	RedrawSkills ()
538	GemRB.SetVar ("HLACount", TmpCount)
539
540	LUHLASelection.OpenHLAWindow (pc, NumClasses, Classes, Level)
541	return
542
543def ReactivateBaseClass ():
544	"""Regains all abilities of the base dual-class.
545
546	Proficiencies, THACO, saves, spells, and innate abilites,
547	most noteably."""
548
549	# we construct the Classes array, so that the active class is always first and the base is second
550	ClassName = GUICommon.GetClassRowName (Classes[1], "class")
551
552	# force reinitialization of the actionbar by forcing the PCF to run
553	ClassID = GemRB.GetPlayerStat (pc, IE_CLASS)
554	GemRB.SetPlayerStat (pc, IE_CLASS, 0, 0)
555	GemRB.SetPlayerStat (pc, IE_CLASS, ClassID)
556
557	# reactivate all our proficiencies
558	TmpTable = GemRB.LoadTable ("weapprof")
559	ProfsTableOffset = 0
560	if GameCheck.IsBG2 ():
561		ProfsTableOffset = 8 # skip bg1 weapprof.2da proficiencies
562	ProfCount = TmpTable.GetRowCount () - ProfsTableOffset
563	for i in range(ProfCount):
564		ProfID = TmpTable.GetValue (i+ProfsTableOffset, 0)
565		if GameCheck.IsBG1():
566			ProfID = ProfID + IE_PROFICIENCYBASTARDSWORD
567		Value = GemRB.GetPlayerStat (pc, ProfID)
568		OldProf = (Value & 0x38) >> 3
569		NewProf = Value & 0x07
570		if OldProf > NewProf:
571			Value = (OldProf << 3) | OldProf
572			print("Value:", Value)
573			if GameCheck.IsBG2():
574				GemRB.ApplyEffect (pc, "Proficiency", Value, ProfID )
575			else:
576				GemRB.SetPlayerStat (pc, ProfID, Value)
577
578	# see if this thac0 is lower than our current thac0
579	ThacoTable = GemRB.LoadTable ("THAC0")
580	TmpThaco = ThacoTable.GetValue(Classes[1]-1, Level[1]-1, GTV_INT)
581	if TmpThaco < GemRB.GetPlayerStat (pc, IE_TOHIT, 1):
582		GemRB.SetPlayerStat (pc, IE_TOHIT, TmpThaco)
583
584	# see if all our saves are lower than our current saves
585	SavesTable = CommonTables.Classes.GetValue (ClassName, "SAVE", GTV_STR)
586	SavesTable = GemRB.LoadTable (SavesTable)
587	for i in range (5):
588		# see if this save is lower than our old save
589		TmpSave = SavesTable.GetValue (i, Level[1]-1)
590		if TmpSave < GemRB.GetPlayerStat (pc, IE_SAVEVSDEATH+i, 1):
591			GemRB.SetPlayerStat (pc, IE_SAVEVSDEATH+i, TmpSave)
592
593	# see if we're a caster
594	SpellTables = [CommonTables.ClassSkills.GetValue (ClassName, "DRUIDSPELL", GTV_STR), CommonTables.ClassSkills.GetValue (ClassName, "CLERICSPELL", GTV_STR), CommonTables.ClassSkills.GetValue (ClassName, "MAGESPELL", GTV_STR)]
595	if SpellTables[2] != "*": # casts mage spells
596		# set up our memorizations
597		SpellTable = GemRB.LoadTable (SpellTables[2])
598		for i in range (9):
599			# if we can cast more spells at this level (should be always), then update
600			NumSpells = SpellTable.GetValue (Level[1]-1, i)
601			if NumSpells > GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_WIZARD, i, 1):
602				GemRB.SetMemorizableSpellsCount (pc, NumSpells, IE_SPELL_TYPE_WIZARD, i)
603	elif SpellTables[1] != "*" or SpellTables[0] != "*": # casts priest spells
604		# get the correct table and mask
605		if SpellTables[1] != "*": # clerical spells
606			SpellTable = GemRB.LoadTable (SpellTables[1])
607			ClassMask = 0x4000
608		else: # druidic spells
609			if not GemRB.HasResource(SpellTables[0], RES_2DA):
610				SpellTables[0] = "MXSPLPRS"
611			SpellTable = GemRB.LoadTable (SpellTables[0])
612			ClassMask = 0x8000
613
614		# loop through each spell level
615		for i in range (7):
616			# update if we can cast more spells at this level
617			NumSpells = SpellTable.GetValue (str(Level[1]), str(i+1), GTV_INT)
618			if not NumSpells:
619				continue
620			if NumSpells > GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, i, 1):
621				GemRB.SetMemorizableSpellsCount (pc, NumSpells, IE_SPELL_TYPE_PRIEST, i)
622
623			# also re-learn the spells if we have to
624			# WARNING: this fixes the error whereby rangers dualed to clerics still got all druid spells
625			#	they will now only get druid spells up to the level they could cast
626			#	this should probably be noted somewhere (ranger/cleric multis still function the same,
627			#	but that could be remedied if desired)
628			Learnable = Spellbook.GetLearnablePriestSpells(ClassMask, GemRB.GetPlayerStat (pc, IE_ALIGNMENT), i+1)
629			for k in range (len (Learnable)): # loop through all the learnable spells
630				if Spellbook.HasSpell (pc, IE_SPELL_TYPE_PRIEST, i, Learnable[k]) < 0: # only write it if we don't yet know it
631					GemRB.LearnSpell(pc, Learnable[k])
632
633def GetNewSpells(actor, Classes, Level, LevelDiff, Kit=0):
634	'''Scan each class for new spells. Values are stored in the global variables'''
635
636	global DeltaDSpells, DeltaWSpells, NewDSpells, NewWSpells, OldDSpells, OldWSpells
637
638	# clear the globals, since we may get called multiple times with different classes
639	HaveCleric = 0
640	DeltaWSpells = 0
641	DeltaDSpells = 0
642	OldDSpells = [0]*7
643	OldWSpells = [0]*9
644	NewDSpells = [0]*7
645	NewWSpells = [0]*9
646
647	# run through each class to detect new spells
648	for i in range(len(Classes)):
649
650		TmpClassName = GUICommon.GetClassRowName (Classes[i], "class")
651
652		# save our current and next spell amounts
653		StartLevel = Level[i] - LevelDiff[i]
654		DruidTable = CommonTables.ClassSkills.GetValue (TmpClassName, "DRUIDSPELL", GTV_STR)
655		ClericTable = CommonTables.ClassSkills.GetValue (TmpClassName, "CLERICSPELL", GTV_STR)
656		MageTable = CommonTables.ClassSkills.GetValue (TmpClassName, "MAGESPELL", GTV_STR)
657
658		# see if we have mage spells
659		if MageTable != "*":
660			# we get 1 extra spell per level if we're a specialist
661			Specialist = 0
662			if CommonTables.KitList.GetValue (Kit, 7) == 1: # see if we're a kitted mage
663				Specialist = 1
664
665			if Spellbook.HasSorcererBook (actor, Classes[i]):
666				MageTable = "SPLSRCKN"
667
668			MageTable = GemRB.LoadTable (MageTable)
669			# loop through each spell level and save the amount possible to cast (current)
670			for j in range (MageTable.GetColumnCount ()):
671				NewWSpells[j] = MageTable.GetValue (str(Level[i]), str(j+1), GTV_INT)
672				OldWSpells[j] = MageTable.GetValue (str(StartLevel), str(j+1), GTV_INT)
673				if NewWSpells[j] > 0: # don't want specialist to get 1 in levels they should have 0
674					NewWSpells[j] += Specialist
675				if OldWSpells[j] > 0:
676					OldWSpells[j] += Specialist
677			DeltaWSpells = sum(NewWSpells)-sum(OldWSpells)
678		elif ClericTable != "*":
679			# check for cleric spells
680			if not GemRB.HasResource(ClericTable, RES_2DA, 1):
681				ClericTable = "MXSPLPRS" # iwd1 doesn't have a DRUIDSPELL column in the table
682			ClericTable = GemRB.LoadTable (ClericTable)
683			HaveCleric = 1
684			# same as above
685			for j in range (ClericTable.GetColumnCount ()):
686				NewDSpells[j] = ClericTable.GetValue (str(Level[i]), str(j+1), GTV_INT)
687				OldDSpells[j] = ClericTable.GetValue (str(StartLevel), str(j+1), GTV_INT)
688			DeltaDSpells = sum(NewDSpells)-sum(OldDSpells)
689		elif DruidTable != "*":
690			# clerics have precedence in multis (ranger/cleric)
691			if HaveCleric == 0:
692				#use MXSPLPRS if we can't find the resource (SoA fix)
693				if not GemRB.HasResource (DruidTable, RES_2DA):
694					DruidTable = "MXSPLPRS"
695
696				# check druid spells
697				DruidTable = GemRB.LoadTable (DruidTable)
698				# same as above
699				for j in range (DruidTable.GetColumnCount ()):
700					NewDSpells[j] = DruidTable.GetValue (str(Level[i]), str(j+1), GTV_INT)
701					OldDSpells[j] = DruidTable.GetValue (str(StartLevel), str(j+1), GTV_INT)
702				DeltaDSpells = sum(NewDSpells)-sum(OldDSpells)
703
704def SaveNewSpells():
705	# save our number of memorizable spells per level
706	if DeltaWSpells > 0:
707		# loop through each wizard spell level
708		for i in range(len(NewWSpells)):
709			if NewWSpells[i] > 0: # we have new spells, so update
710				GemRB.SetMemorizableSpellsCount(pc, NewWSpells[i], IE_SPELL_TYPE_WIZARD, i)
711
712	# save our number of memorizable priest spells
713	if DeltaDSpells > 0: # druids and clerics count
714		for i in range (len(NewDSpells)):
715			# get each update
716			if NewDSpells[i] > 0:
717				GemRB.SetMemorizableSpellsCount (pc, NewDSpells[i], IE_SPELL_TYPE_PRIEST, i)
718
719			# learn all the spells we're given, but don't have, up to our given casting level
720			# bonus spells don't count in determining if we can use this level
721			if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, i, 0) > 0: # we can memorize spells of this level
722				for j in range(NumClasses): # loop through each class
723					TmpClassName = GUICommon.GetClassRowName (Classes[j], "class")
724					IsDruid = CommonTables.ClassSkills.GetValue (TmpClassName, "DRUIDSPELL", GTV_STR)
725					IsCleric = CommonTables.ClassSkills.GetValue (TmpClassName, "CLERICSPELL", GTV_STR)
726					if IsCleric == "*" and IsDruid == "*": # no divine spells (so mage/cleric multis don't screw up)
727						continue
728					elif IsCleric == "*": # druid spells
729						ClassFlag = 0x8000
730					else: # cleric spells
731						ClassFlag = 0x4000
732
733					Learnable = Spellbook.GetLearnablePriestSpells(ClassFlag, GemRB.GetPlayerStat (pc, IE_ALIGNMENT), i+1)
734					for k in range(len(Learnable)): # loop through all the learnable spells
735						if Spellbook.HasSpell (pc, IE_SPELL_TYPE_PRIEST, i, Learnable[k]) < 0: # only write it if we don't yet know it
736							GemRB.LearnSpell(pc, Learnable[k])
737