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# LUSkillSelection.py - selection of thief skills
20
21import GemRB
22from GUIDefines import *
23from ie_stats import *
24import GameCheck
25import GUICommon
26import CommonTables
27
28#constants
29LUSKILLS_TYPE_LEVELUP = 1
30LUSKILLS_TYPE_CHARGEN = 2
31LUSKILLS_TYPE_DUALCLASS = 4
32LUSKILLS_MAX = 250
33
34#refs to the script calling this
35SkillsWindow = 0
36SkillsCallback = 0
37
38#offsets to the various parts of all 3 windows
39SkillsOffsetPress = 0
40SkillsOffsetButton1 = 0
41SkillsOffsetName = 0
42SkillsOffsetPoints = 0
43SkillsOffsetSum = 0
44SkillsNumButtons = 0
45
46#The number of steps between each control ID for the skill label and points of each skill
47#Set to 2 for PST, as they are 'interleaved' ie label/amount/label/amount
48SkillsLabelIncrement = 1
49
50#internal variables
51SkillsIndices = []
52SkillPointsLeft = 0
53SkillsTopIndex = 0
54SkillsTable = 0
55SkillsOldPos = 0
56SkillsClickCount = 0
57SkillsOldDirection = 0
58SkillsTextArea = 0
59SkillsKitName = 0
60SkillsAssignable = 0
61
62#WARNING: This WILL NOT show the window, only access it. To see the return, call GemRB.GetVar ("SkillPointsLeft").
63# If nothing can be assigned, it will return 0 prior to accessing any of the window methods.
64def SetupSkillsWindow (pc, skilltype, window, callback, level1=[0,0,0], level2=[1,1,1], classid=0, scroll=True):
65	global SkillsWindow, SkillsCallback, SkillsOffsetPress, SkillsOffsetButton1, SkillsOffsetName
66	global SkillsOffsetPoints, SkillsOffsetSum, SkillsIndices, SkillPointsLeft, SkillsTopIndex
67	global SkillsTable, SkillsOldPos, SkillsClickCount, SkillsOldDirection, SkillsNumButtons
68	global SkillsTextArea, SkillsKitName, SkillsAssignable, SkillsLabelIncrement
69
70	#reset some basic values
71	SkillLeftPress = SkillIncreasePress
72	SkillRightPress = SkillDecreasePress
73	SkillsWindow = window
74	SkillsCallback = callback
75	SkillsOldPos = 0
76	SkillsClickCount = 0
77	SkillsOldDirection = 0
78	SkillsAssignable = 0
79	SkillsTable = GemRB.LoadTable ("skills")
80	SkillPointsLeft = 0
81	GemRB.SetVar ("SkillPointsLeft", 0)
82	SkillsNullify ()
83
84	#make sure we're within ranges
85	if not window or not callback or len(level1)!=len(level2):
86		return
87
88	#setup the offsets
89	if skilltype == LUSKILLS_TYPE_LEVELUP and GameCheck.IsBG2():
90		SkillsOffsetPress = 120
91		SkillsOffsetButton1 = 17
92		SkillsOffsetSum = 37
93		SkillsOffsetName = 32
94		SkillsOffsetPoints = 43
95		SkillsNumButtons = 4
96		SkillsTextArea = SkillsWindow.GetControl (110)
97		ScrollBar = SkillsWindow.GetControl (109)
98	elif skilltype == LUSKILLS_TYPE_LEVELUP and GameCheck.IsPST():
99		SkillsOffsetPress = -1
100		SkillsOffsetButton1 = 16
101		SkillsOffsetSum = 6
102		SkillsOffsetName = 7
103		SkillsOffsetPoints = 8
104		SkillsNumButtons = 4
105		SkillsLabelIncrement = 2
106		#The order of the buttons is the opposite of the other games
107		SkillLeftPress = SkillDecreasePress
108		SkillRightPress = SkillIncreasePress
109		#There is actually no hint text to describe the skills, so this is a dummy
110		SkillsWindow.CreateTextArea (45, 1, 1, 1, 1, "FONTDLG")
111		SkillsTextArea =  SkillsWindow.GetControl (45)
112	elif skilltype == LUSKILLS_TYPE_LEVELUP:
113		SkillsOffsetPress = -1
114		SkillsOffsetButton1 = 17
115		SkillsOffsetSum = 37
116		SkillsOffsetName = 32
117		SkillsOffsetPoints = 43
118		SkillsNumButtons = 4
119		SkillsTextArea = SkillsWindow.GetControl (42)
120		if (scroll):
121			ScrollBar = SkillsWindow.GetControl (109)
122	elif skilltype == LUSKILLS_TYPE_DUALCLASS:
123		SkillsOffsetPress = 5
124		SkillsOffsetButton1 = 14
125		SkillsOffsetSum = 8
126		SkillsOffsetName = 0
127		SkillsOffsetPoints = 9
128		SkillsNumButtons = 4
129		SkillsTextArea = SkillsWindow.GetControl (22)
130		SkillsTextArea.SetText(17248)
131		if (scroll):
132			ScrollBar = SkillsWindow.GetControl (26)
133			SkillsWindow.SetEventProxy(ScrollBar)
134	elif skilltype == LUSKILLS_TYPE_CHARGEN:
135		SkillsOffsetPress = 21
136		SkillsOffsetButton1 = 11
137		SkillsOffsetSum = 5
138		SkillsOffsetName = 6
139		SkillsOffsetPoints = 1
140		SkillsNumButtons = 4
141		SkillsTextArea = SkillsWindow.GetControl (19)
142		SkillsTextArea.SetText(17248)
143		if (scroll):
144			ScrollBar = SkillsWindow.GetControl (26)
145			SkillsWindow.SetEventProxy(ScrollBar)
146	else:
147		return
148
149	#get our class id and name
150	IsDual = GUICommon.IsDualClassed (pc, 1)
151	IsMulti = GUICommon.IsMultiClassed (pc, 1)
152	if classid: #used when dual-classing
153		Class = classid
154	elif IsDual[0] == 3:
155		Class = CommonTables.KitList.GetValue (IsDual[2], 7)
156	elif IsDual[0]: #only care about the current class
157		Class = GUICommon.GetClassRowName(IsDual[2], "index")
158		Class = CommonTables.Classes.GetValue (Class, "ID")
159	else:
160		Class = GemRB.GetPlayerStat (pc, IE_CLASS)
161	ClassName = GUICommon.GetClassRowName(Class, "class")
162
163	#get the number of classes
164	if IsMulti[0]>1:
165		NumClasses = IsMulti[0]
166	else:
167		NumClasses = 1
168	if NumClasses > len (level2):
169		return
170
171	#figure out the kitname if we need it
172	#protect against kitted multiclasses
173	Kit = GUICommon.GetKitIndex (pc)
174	if not Kit or skilltype == LUSKILLS_TYPE_DUALCLASS or IsDual[0] in [1, 2] or IsMulti[0]>1:
175		SkillsKitName = ClassName
176	else:
177		SkillsKitName = CommonTables.KitList.GetValue (Kit, 0, GTV_STR)
178
179	# also treat most mod-introduced kits as kitless for skills.2da
180	# lookups - unless they add the required columns
181	if SkillsTable.GetValue ("OPEN_LOCKS", SkillsKitName) == -1:
182		SkillsKitName = ClassName
183
184	#figure out the correct skills table
185	SkillIndex = -1
186	for i in range (NumClasses):
187		TmpClass = Class
188		if NumClasses > 1:
189			TmpClass = IsMulti[i+1]
190		TmpClass = GUICommon.GetClassRowName (TmpClass, "class")
191		if (CommonTables.ClassSkills.GetValue (TmpClass, "THIEFSKILL", GTV_STR) != "*"):
192			SkillIndex = i
193			break
194
195	#see if we got a thief (or monk)
196	SkillsIndices = []
197	if SkillIndex >= 0:
198		#SkillsKitName should be fine as all multis are in classes.2da
199		#also allows for thief kits
200		SkillsAssignable = 1
201		for i in range(SkillsTable.GetRowCount()-2):
202			# -2/+2 to compensate for the special first_level and rate rows
203			SkillName = SkillsTable.GetRowName (i+2)
204			if SkillsTable.GetValue (SkillName, SkillsKitName) != -1:
205				SkillsIndices.append(i)
206
207		LevelDiff = []
208		for i in range (NumClasses):
209			LevelDiff.append (level2[i]-level1[i])
210		if level1[SkillIndex] == 0:
211			SkillPointsLeft = SkillsTable.GetValue ("FIRST_LEVEL", SkillsKitName, GTV_INT)
212			LevelDiff[SkillIndex] -= 1
213		SkillPointsLeft += LevelDiff[SkillIndex] * SkillsTable.GetValue("RATE", SkillsKitName, GTV_INT)
214		TotalSkillsAssignable = 0
215
216		if SkillPointsLeft < 0:
217			#really don't have an entry
218			SkillPointsLeft = 0
219		else:
220			#get the skill values
221			for i in range(SkillsTable.GetRowCount()-2):
222				# -2/+2 to compensate for the special first_level and rate rows
223				SkillName = SkillsTable.GetRowName (i+2)
224				SkillID = SkillsTable.GetValue (SkillName, "ID")
225				SkillValue = GemRB.GetPlayerStat (pc, SkillID)
226				BaseSkillValue = GemRB.GetPlayerStat (pc, SkillID, 1)
227				GemRB.SetVar("Skill "+str(i), SkillValue)
228				GemRB.SetVar("SkillBase "+str(i), SkillValue)
229				# display the modified stat to avoid confusion (account for dex, race and effect boni)
230				GemRB.SetVar("SkillDisplayMod "+str(i), SkillValue-BaseSkillValue)
231				TotalSkillsAssignable += LUSKILLS_MAX-SkillValue
232
233		#protect against having more skills than we can assign
234		if SkillPointsLeft > TotalSkillsAssignable:
235			SkillPointsLeft = TotalSkillsAssignable
236		GemRB.SetVar ("SkillPointsLeft", SkillPointsLeft)
237	else:
238		#get ranger and bard skills
239		SpecialSkillsMap = []
240		for i in range(NumClasses):
241			if IsMulti[0]>1:
242				classname = IsMulti[i+1]
243			else:
244				classname = Class
245			classname = GUICommon.GetClassRowName (classname, "class")
246			for table in "RANGERSKILL", "BARDSKILL":
247				SpecialSkillsTable = CommonTables.ClassSkills.GetValue (classname, table)
248				if SpecialSkillsTable != "*":
249					SpecialSkillsMap.append((SpecialSkillsTable, i))
250					break
251		for skills in SpecialSkillsMap:
252			SpecialSkillsTable = GemRB.LoadTable (skills[0])
253			for skill in range(SpecialSkillsTable.GetColumnCount ()):
254				skillname = SpecialSkillsTable.GetColumnName (skill)
255				value = SpecialSkillsTable.GetValue (str(level2[skills[1]]), skillname)
256				skillindex = SkillsTable.GetRowIndex (skillname) - 2
257				GemRB.SetVar ("Skill " + str(skillindex), value)
258				SkillsIndices.append(skillindex)
259
260	#we didn't find anything, so don't continue (will show as a return of 0)
261	#or don't display if we aren't leveling and have a bard/ranger
262	if not len (SkillsIndices) or (not SkillPointsLeft and skilltype != LUSKILLS_TYPE_LEVELUP):
263		SkillSumLabel = SkillsWindow.GetControl(0x10000000+SkillsOffsetSum)
264		SkillSumLabel.SetText("")
265		return
266
267	#skills scrollbar
268	SkillsTopIndex = 0
269	GemRB.SetVar ("SkillsTopIndex", SkillsTopIndex)
270	if len(SkillsIndices) > SkillsNumButtons:
271		ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, SkillScrollBarPress)
272		#decrease it with the number of controls on screen (list size) and two unrelated rows
273		ScrollBar.SetVarAssoc ("SkillsTopIndex", SkillsTable.GetRowCount()-SkillsNumButtons-2)
274	else:
275		if len(SkillsIndices) and SkillsAssignable:
276			#autoscroll to the first valid skill; luckily all three monk ones are adjacent
277			GemRB.SetVar ("SkillsTopIndex", SkillsIndices[0])
278
279	#setup all the visible buttons
280	for i in range(len(SkillsIndices)):
281		if i == SkillsNumButtons:
282			break
283		if SkillsOffsetPress != -1:
284			Button = SkillsWindow.GetControl(i+SkillsOffsetPress)
285			Button.SetVarAssoc("Skill",SkillsIndices[i])
286			Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkillJustPress)
287
288		Button = SkillsWindow.GetControl(i*2+SkillsOffsetButton1)
289		Button.SetVarAssoc("Skill",SkillsIndices[i])
290		Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkillLeftPress)
291		Button.SetActionInterval(20);
292
293		Button = SkillsWindow.GetControl(i*2+SkillsOffsetButton1+1)
294		Button.SetVarAssoc("Skill",SkillsIndices[i])
295		Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkillRightPress)
296		Button.SetActionInterval(20);
297
298	SkillsRedraw ()
299	return
300
301def SkillsRedraw (direction=0):
302	global SkillsOldDirection, SkillsClickCount, SkillsLabelIncrement
303
304	#update how many skill points are left and call the callback function
305	SkillSumLabel = SkillsWindow.GetControl(0x10000000+SkillsOffsetSum)
306	SkillSumLabel.SetText(str(SkillPointsLeft))
307
308	for i in range (SkillsNumButtons):
309		if len(SkillsIndices) <= i:
310			SkillsHide (i)
311			continue
312
313		#show the current skills name
314		Pos = SkillsIndices[SkillsTopIndex+i]
315		SkillName = SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "CAP_REF")
316		Label = SkillsWindow.GetControl (0x10000000+SkillsOffsetName+(i*SkillsLabelIncrement))
317		Label.SetText (SkillName)
318
319		#enable/disable the button if we can(not) get the skills
320		SkillName = SkillsTable.GetRowName (Pos+2)
321		Ok = SkillsTable.GetValue (SkillName, SkillsKitName) and SkillsAssignable
322		Button1 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1)
323		Button2 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1+1)
324		if not Ok:
325			Button1.SetState(IE_GUI_BUTTON_DISABLED)
326			Button2.SetState(IE_GUI_BUTTON_DISABLED)
327			Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
328			Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
329		else:
330			Button1.SetState(IE_GUI_BUTTON_ENABLED)
331			Button2.SetState(IE_GUI_BUTTON_ENABLED)
332			Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND)
333			Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND)
334
335		#show how many points are allocated to this skill
336		Label = SkillsWindow.GetControl(0x10000000+SkillsOffsetPoints+(i*SkillsLabelIncrement))
337		ActPoint = GemRB.GetVar("Skill "+str(Pos) )
338		Label.SetText(str(ActPoint))
339
340	#setup doublespeed
341	if SkillsOldDirection == direction:
342		SkillsClickCount = SkillsClickCount + 1
343		return
344
345	SkillsOldDirection = direction
346	SkillsClickCount = 0
347	return
348
349def SkillJustPress(btn, val):
350	Pos = val+SkillsTopIndex
351	SkillsTextArea.SetText (SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "DESC_REF"))
352	return
353
354def SkillDecreasePress(btn, val):
355	global SkillPointsLeft, SkillsClickCount, SkillsOldPos
356
357	Pos = val+SkillsTopIndex
358	SkillsTextArea.SetText (SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "DESC_REF"))
359	ActPoint = GemRB.GetVar("Skill "+str(Pos) )
360	BasePoint = GemRB.GetVar("SkillBase "+str(Pos) )
361	if ActPoint <= 0 or ActPoint <= BasePoint:
362		return
363	GemRB.SetVar("Skill "+str(Pos),ActPoint-1)
364	SkillPointsLeft = SkillPointsLeft + 1
365	if SkillsOldPos != Pos:
366		SkillsOldPos = Pos
367		SkillsClickCount = 0
368
369	GemRB.SetVar ("SkillPointsLeft", SkillPointsLeft)
370	SkillsRedraw(2)
371	SkillsCallback ()
372	return
373
374def SkillIncreasePress(btn, val):
375	global SkillPointsLeft, SkillsClickCount, SkillsOldPos
376
377	Pos = val+SkillsTopIndex
378	SkillsTextArea.SetText (SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "DESC_REF"))
379	if SkillPointsLeft == 0:
380		return
381	ActPoint = GemRB.GetVar("Skill "+str(Pos) )
382	if ActPoint >= LUSKILLS_MAX:
383		return
384	GemRB.SetVar("Skill "+str(Pos), ActPoint+1)
385	SkillPointsLeft = SkillPointsLeft - 1
386	if SkillsOldPos != Pos:
387		SkillsOldPos = Pos
388		SkillsClickCount = 0
389
390	GemRB.SetVar ("SkillPointsLeft", SkillPointsLeft)
391	SkillsRedraw(1)
392	SkillsCallback ()
393	return
394
395def SkillScrollBarPress():
396	global SkillsTopIndex
397
398	SkillsTopIndex = GemRB.GetVar("SkillsTopIndex")
399	SkillsRedraw ()
400	return
401
402# saves all the skills
403def SkillsSave (pc):
404	global SkillsTable
405	if not SkillsTable:
406		SkillsTable = GemRB.LoadTable ("skills")
407
408	for i in range(SkillsTable.GetRowCount() - 2):
409		SkillName = SkillsTable.GetRowName (i+2)
410		SkillID = SkillsTable.GetValue (SkillName, "ID")
411		SkillValue = GemRB.GetVar ("Skill "+str(i)) - GemRB.GetVar("SkillDisplayMod "+str(i))
412		if SkillValue > 0:
413			GemRB.SetPlayerStat (pc, SkillID, SkillValue)
414
415def SkillsNullify (pc = None):
416	global SkillsTable
417	if not SkillsTable:
418		SkillsTable = GemRB.LoadTable ("skills")
419
420	for i in range(SkillsTable.GetRowCount()-2):
421		GemRB.SetVar ("Skill "+str(i), 0)
422		GemRB.SetVar ("SkillBase "+str(i), 0)
423		if pc:
424			SkillName = SkillsTable.GetRowName (i+2)
425			SkillID = SkillsTable.GetValue (SkillName, "ID")
426			GemRB.SetPlayerStat (pc, SkillID, 0)
427
428def SkillsHide (i):
429	Label = SkillsWindow.GetControl (0x10000000+SkillsOffsetName+i)
430	Label.SetText ("")
431	Button1 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1)
432	Button1.SetState(IE_GUI_BUTTON_DISABLED)
433	Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
434	Button2 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1+1)
435	Button2.SetState(IE_GUI_BUTTON_DISABLED)
436	Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
437	Label = SkillsWindow.GetControl(0x10000000+SkillsOffsetPoints+i)
438	Label.SetText("")
439