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
21# GUICommonWindows.py - functions to open common
22# windows in lower part of the screen
23###################################################
24
25import GemRB
26from GUIDefines import *
27from ie_stats import *
28from ie_modal import *
29from ie_action import *
30from ie_slots import SLOT_QUIVER
31from ie_restype import RES_2DA
32from ie_sounds import CHAN_HITS
33from GameCheck import MAX_PARTY_SIZE
34from Clock import UpdateClock
35import GameCheck
36import GUICommon
37import CommonTables
38import CommonWindow
39import LUCommon
40import InventoryCommon
41if not GameCheck.IsPST():
42  import Spellbook  ##not used in pst - YET
43
44FRAME_PC_SELECTED = 0
45FRAME_PC_TARGET   = 1
46
47CurrentWindow = None
48ActionBarControlOffset = 0
49ScreenHeight = GemRB.GetSystemVariable (SV_HEIGHT)
50
51if GameCheck.IsIWD2():
52	MageSpellsKey = 'Spellbook'
53	CharacterStatsKey = 'Character_Record'
54elif GameCheck.IsPST():
55	MageSpellsKey = 'Mage_Spells'
56	CharacterStatsKey = 'Character_Stats'
57else:
58	MageSpellsKey = 'Wizard_Spells'
59	CharacterStatsKey = 'Character_Record'
60
61#The following tables deal with the different control indexes and string refs of each game
62#so that actual interface code can be game neutral
63#the dictionary keys match entries in keymap.2da
64AITip = {"Deactivate" : 15918, "Enable" : 15917}
65if GameCheck.IsPST(): #Torment
66	import GUIClasses
67	TimeWindow = None
68	PortWindow = None
69	MenuWindow = None
70	MainWindow = None
71	DiscWindow = None
72	AITip = {	"Deactivate" : 41631,	"Enable" : 41646 }
73	OptionTip = { #dictionary to the stringrefs in each games dialog.tlk
74	'Inventory' : 41601,'Map': 41625, MageSpellsKey : 41624, 'Priest_Spells': 4709, CharacterStatsKey : 4707,'Journal': 41623,
75	'Options' : 41626,'Rest': 41628,'Follow': 41647,'Expand': 41660,'Toggle_AI' : 1,'Return_To_Game' : 1,'Party' : 1
76	}
77	OptionControl = { #dictionary to the control indexes in the window (.CHU)
78	'Inventory' : 1, 'Map' : 2, MageSpellsKey : 3, 'Priest_Spells': 7, CharacterStatsKey : 5, 'Journal': 6,
79	'Options' : 8, 'Rest': 9, 'Follow': 0, 'Expand': 10, 'Toggle_AI': 4,
80	'Return_To_Game': 0, 'Party' : 8 , 'Time': 9 #not in pst
81	}
82elif GameCheck.IsIWD2(): #Icewind Dale 2
83	OptionTip = {
84	'Inventory' : 16307, 'Map': 16310, MageSpellsKey : 16309, CharacterStatsKey : 16306, 'Journal': 16308,
85	'Options' : 16311, 'Rest': 11942, 'Follow': 41647, 'Expand': 41660, 'Toggle_AI' : 1,'Return_To_Game' : 16313,  'Party' : 16312,
86	'SelectAll': 10485
87	}
88	OptionControl = {
89	'Inventory' : 5, 'Map' : 7, CharacterStatsKey : 8, 'Journal': 6,
90	'Options' : 9, 'Rest': 12, 'Follow': 0, 'Expand': 10, 'Toggle_AI': 14,
91	'Return_To_Game': 0, 'Party' : 13,  'Time': 10, #not in pst
92	MageSpellsKey: 4, 'SelectAll': 11
93	}
94else: # Baldurs Gate, Icewind Dale
95	OptionTip = {
96	'Inventory' : 16307, 'Map': 16310, MageSpellsKey : 16309, 'Priest_Spells': 14930, CharacterStatsKey : 16306, 'Journal': 16308,
97	'Options' : 16311, 'Rest': 11942, 'Follow': 41647,  'Expand': 41660, 'Toggle_AI' : 1, 'Return_To_Game' : 16313, 'Party' : 16312
98	}
99	OptionControl = {
100	'Inventory' : 3, 'Map' : 1, MageSpellsKey : 5, 'Priest_Spells': 6, CharacterStatsKey : 4, 'Journal': 2,
101	'Options' : 7, 'Rest': 9, 'Follow': 0, 'Expand': 10, 'Toggle_AI': 6,
102	'Return_To_Game': 0, 'Party' : 8, 'Time': 9 #not in pst
103	}
104
105# Generic option button init. Pass it the options window. Index is a key to the dicts,
106# IsPage means whether the game should mark the button selected
107def InitOptionButton(Window, Index, HotKey=True):
108	Button = Window.GetControl (OptionControl[Index])
109	if not Button:
110		print("InitOptionButton cannot find the button: " + Index)
111		return
112
113	Button.SetTooltip (OptionTip[Index])
114	if HotKey:
115		Button.SetHotKey (Index, True)
116
117	# this variable isnt used anywhere (tho it might be useful to use it for knowing what window is open)
118	# however, we still need to set one because we do depend on the button having a value
119	Button.SetVarAssoc ("OPT_BTN", OptionControl[Index])
120
121	return Button
122
123##these defaults don't seem to break the games other than pst
124def SetupMenuWindowControls (Window, Gears=None, CloseWindowCallback=None):
125	"""Binds all of the basic controls and windows to the options pane."""
126
127	global ActionBarControlOffset
128
129	bg1 = GameCheck.IsBG1()
130	bg2 = GameCheck.IsBG2()
131	iwd1 = GameCheck.IsIWD1()
132	how = GameCheck.HasHOW()
133	iwd2 = GameCheck.IsIWD2()
134	pst = GameCheck.IsPST()
135	#store these instead of doing 50 calls...
136
137	EscButton = Window.CreateButton (99, 0, 0, 0, 0);
138	EscButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CloseTopWindow)
139	EscButton.MakeEscape()
140
141	if iwd2: # IWD2 has one spellbook to rule them all
142		ActionBarControlOffset = 6 #portrait and action window were merged
143
144		InitOptionButton(Window, MageSpellsKey)
145
146		# AI
147		Button = InitOptionButton(Window, 'Toggle_AI')
148		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AIPress)
149		AIPress (0) #this initialises the state and tooltip
150
151		# Select All
152		Button = InitOptionButton(Window, 'SelectAll', False)
153		Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectAllOnPress)
154	elif pst: #pst has these three controls here instead of portrait pane
155		# (Un)Lock view on character
156		Button = InitOptionButton(Window, 'Follow', False)  # or 41648 Unlock ...
157		Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, OnLockViewPress)
158		# AI
159		Button = InitOptionButton(Window, 'Toggle_AI')
160		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AIPress)
161		AIPress(0) #this initialises the state and tooltip
162
163		# Message popup FIXME disable on non game screen...
164		Button = InitOptionButton(Window, 'Expand', False)# or 41661 Close ...
165
166	else: ## pst lacks this control here. it is on the clock. iwd2 seems to skip it
167		# Return to Game
168		Button = InitOptionButton(Window,'Return_To_Game')
169
170		if bg1:
171			# enabled BAM isn't present in .chu, defining it here
172			Button.SetSprites ("GUILSOP", 0,16,17,28,16)
173		if iwd1:
174			# disabled/selected frame isn't present in .chu, defining it here
175			Button.SetSprites ("GUILSOP", 0,16,17,16,16)
176
177	# Party managment / character arbitration. Distinct form reform party window.
178	if not pst:
179		Button = Window.GetControl (OptionControl['Party'])
180		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None) #TODO: OpenPartyWindow
181		if bg1 or bg2:
182			Button.SetState (IE_GUI_BUTTON_DISABLED)
183			Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
184		else:
185			Button.SetTooltip (OptionTip['Party'])
186
187	# Map
188	Button = InitOptionButton(Window, 'Map')
189	if bg1:
190		Button.SetSprites ("GUILSOP", 0,0,1,20,0)
191	if iwd1:
192		Button.SetSprites ("GUILSOP", 0,0,1,20,20)
193
194	# Journal
195	Button = InitOptionButton(Window, 'Journal')
196	if bg1:
197		Button.SetSprites ("GUILSOP", 0,4,5,22,4)
198	if iwd1:
199		Button.SetSprites ("GUILSOP", 0,4,5,22,22)
200
201	# Inventory
202	Button = InitOptionButton(Window, 'Inventory')
203	if bg1:
204		Button.SetSprites ("GUILSOP", 0,2,3,21,2)
205	if iwd1:
206		Button.SetSprites ("GUILSOP", 0,2,3,21,21)
207
208	# Records
209	Button = InitOptionButton(Window, CharacterStatsKey)
210	if bg1:
211		Button.SetSprites ("GUILSOP", 0,6,7,23,6)
212	if iwd1:
213		Button.SetSprites ("GUILSOP", 0,6,7,23,23)
214
215	if not iwd2: # All Other Games Have Fancy Distinct Spell Pages
216		# Mage
217		Button = InitOptionButton(Window, MageSpellsKey)
218		if bg1:
219			Button.SetSprites ("GUILSOP", 0,8,9,24,8)
220		if iwd1:
221			Button.SetSprites ("GUILSOP", 0,8,9,24,24)
222
223		# Priest
224		Button = InitOptionButton(Window, 'Priest_Spells')
225		if bg1:
226			Button.SetSprites ("GUILSOP", 0,10,11,25,10)
227		if iwd1:
228			Button.SetSprites ("GUILSOP", 0,10,11,25,25)
229
230	# Options
231	Button = InitOptionButton(Window, 'Options')
232	if bg1:
233		Button.SetSprites ("GUILSOP", 0,12,13,26,12)
234	if iwd1:
235		Button.SetSprites ("GUILSOP", 0,12,13,26,26)
236
237
238	# pause button
239	if Gears:
240		# Pendulum, gears, sun/moon dial (time)
241		# FIXME: display all animations: CPEN, CGEAR, CDIAL
242		if how: # how doesn't have this in the right place
243			pos = Window.GetFrame()["h"] - 71
244			Window.CreateButton (OptionControl['Time'], 0, pos, 64, 71)
245
246		Button = Window.GetControl (OptionControl['Time'])
247		if bg2:
248			Label = Button.CreateLabel (0x10000009, "NORMAL", "", IE_FONT_SINGLE_LINE)
249			Label.SetAnimation ("CPEN")
250
251		Button.SetAnimation ("CGEAR")
252		Button.SetState (IE_GUI_BUTTON_ENABLED)
253		Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_NORMAL, OP_SET)
254		Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
255		if iwd2:
256			Button.SetState (IE_GUI_BUTTON_LOCKED) #no button depression, timer is an inset stone planet
257			rb = OptionControl['Rest']
258		else:
259			rb = 11
260		UpdateClock ()
261	else:
262		rb = OptionControl['Rest']
263		if iwd2:
264			UpdateClock ()
265
266	# BG1 doesn't have a rest button on the main window, so this creates one
267	# from what would be the multiplayer arbitration control
268	if bg1:
269		Button = Window.GetControl (8)
270		Button.SetSprites("GUIRSBUT", 0,0,1,0,0)
271		Button.SetStatus (IE_GUI_BUTTON_ENABLED)
272		Button.SetSize(55,37)
273		Button.SetPos(4,359)
274		Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_SET)
275		rb = 8
276
277	# Rest
278	Button = Window.GetControl (rb)
279	if Button:
280		Button.SetTooltip (OptionTip['Rest'])
281		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RestPress)
282
283	return
284
285def OnLockViewPress ():
286	OptionsWindow = GemRB.GetView("OPTWIN")
287	Button = OptionsWindow.GetControl (0)
288	GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR | SF_ALWAYSCENTER, OP_XOR)
289
290	# no way to get the screen flags
291	if OnLockViewPress.counter % 2:
292		# unlock
293		Button.SetTooltip (41648)
294		Button.SetState(IE_GUI_BUTTON_SELECTED)#dont ask
295	else:
296		# lock
297		Button.SetTooltip (41647)
298		Button.SetState(IE_GUI_BUTTON_NORMAL)
299	OnLockViewPress.counter += 1
300
301	return
302
303OnLockViewPress.counter = 1
304
305def PortraitPress ():
306	"""Toggles the portraits pane """
307	PP = GemRB.GetGUIFlags () & GS_PORTRAITPANE
308	if PP:
309		GemRB.GameSetScreenFlags (GS_PORTRAITPANE, OP_NAND)
310	else:
311		GemRB.GameSetScreenFlags (GS_PORTRAITPANE, OP_OR)
312	return
313
314def AIPress (toggle=1):
315	"""Toggles the party AI or refreshes the button state if toggle = 0"""
316
317	if GameCheck.IsPST() or GameCheck.IsIWD2():
318		OptionsWindow = GemRB.GetView("OPTWIN")
319		Button = OptionsWindow.GetControl (OptionControl['Toggle_AI'])
320	else:
321		PortraitWindow = GemRB.GetView("PORTWIN")
322		Button = PortraitWindow.GetControl (OptionControl['Toggle_AI'])
323
324	if toggle:
325		GemRB.GameSetScreenFlags (GS_PARTYAI, OP_XOR)
326
327	AI = GemRB.GetGUIFlags () & GS_PARTYAI
328	if AI:
329		GemRB.SetVar ("AI", 0)
330		Button.SetTooltip (AITip['Deactivate'])
331		Button.SetState(IE_GUI_BUTTON_SELECTED)
332	else:
333		GemRB.SetVar ("AI", GS_PARTYAI)
334		Button.SetTooltip (AITip['Enable'])
335		Button.SetState (IE_GUI_BUTTON_ENABLED)
336
337	if GameCheck.IsPST ():
338		GemRB.SetGlobal ("partyScriptsActive", "GLOBALS", AI)
339
340	#force redrawing, in case a hotkey triggered this function
341	Button.SetVarAssoc ("AI", GS_PARTYAI)
342	return
343
344## The following four functions are for the action bar
345## they are currently unused in pst
346def EmptyControls ():
347	if GameCheck.IsPST():
348		return
349	Selected = GemRB.GetSelectedSize()
350	if Selected==1:
351		pc = GemRB.GameGetFirstSelectedActor ()
352		#init spell list
353		GemRB.SpellCast (pc, -1, 0)
354
355	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
356	if not CurrentWindow:
357		return # current case in our game demo (on right-click)
358	for i in range (12):
359		Button = CurrentWindow.GetControl (i+ActionBarControlOffset)
360		Button.SetVisible (False)
361	return
362
363def SelectFormationPreset ():
364	"""Choose the default formation."""
365	GemRB.GameSetFormation (GemRB.GetVar ("Value"), GemRB.GetVar ("Formation") )
366	GroupControls ()
367	return
368
369def SetupFormation ():
370	"""Opens the formation selection section."""
371	for i in range (12):
372		Button = CurrentWindow.GetControl (i+ActionBarControlOffset)
373		Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
374		Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
375		Button.SetBAM ("FORM%x"%i,0,0,-1)
376		Button.SetVarAssoc ("Value", i)
377		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectFormationPreset)
378		Button.SetState (IE_GUI_BUTTON_UNPRESSED)
379	return
380
381def GroupControls ():
382	"""Sections that control group actions."""
383	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
384	Button = CurrentWindow.GetControl (ActionBarControlOffset)
385	if GameCheck.IsBG2():
386		Button.SetActionIcon (globals(), 7, 1) #talk icon
387	else:
388		Button.SetActionIcon (globals(), 14, 1)#guard icon
389	Button = CurrentWindow.GetControl (1+ActionBarControlOffset)
390	Button.SetActionIcon (globals(), 15, 2)
391	Button = CurrentWindow.GetControl (2+ActionBarControlOffset)
392	Button.SetActionIcon (globals(), 21, 3)
393	Button = CurrentWindow.GetControl (3+ActionBarControlOffset)
394	Button.SetActionIcon (globals(), -1, 4)
395	Button = CurrentWindow.GetControl (4+ActionBarControlOffset)
396	Button.SetActionIcon (globals(), -1, 5)
397	Button = CurrentWindow.GetControl (5+ActionBarControlOffset)
398	Button.SetActionIcon (globals(), -1, 6)
399	Button = CurrentWindow.GetControl (6+ActionBarControlOffset)
400	Button.SetActionIcon (globals(), -1, 7)
401	GemRB.SetVar ("Formation", GemRB.GameGetFormation ())
402	for i in range (5):
403		Button = CurrentWindow.GetControl (7+ActionBarControlOffset+i)
404		Button.SetState (IE_GUI_BUTTON_ENABLED)
405		idx = GemRB.GameGetFormation (i)
406		Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
407		# kill the previous sprites or they show through
408		Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
409		Button.SetBAM ("FORM%x"%idx,0,0,-1)
410		Button.SetVarAssoc ("Formation", i)
411		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectFormation)
412		Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, SetupFormation)
413		Button.SetTooltip (4935)
414		# 0x90 = F1 key
415		Button.SetHotKey (chr(7+i+0x90), 0, True)
416	return
417
418def OpenActionsWindowControls (Window):
419	# 1280 and higher don't have this control
420	if not Window.GetControl (62):
421		UpdateActionsWindow ()
422		return
423	# Gears (time) when options pane is down
424	Button = Window.GetControl (62)
425	Label = Button.CreateLabel (0x1000003e, "NORMAL", "", IE_FONT_SINGLE_LINE)
426
427	# FIXME: display all animations
428	Label.SetAnimation ("CPEN")
429	Button.SetAnimation ("CGEAR")
430	Button.SetState (IE_GUI_BUTTON_ENABLED)
431	Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_NORMAL, OP_SET)
432	Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
433	UpdateActionsWindow ()
434	return
435
436##not used in pst - not sure any items have abilities, but it is worth making a note to find out
437def SelectItemAbility():
438	pc = GemRB.GameGetFirstSelectedActor ()
439	slot = GemRB.GetVar ("Slot")
440	ability = GemRB.GetVar ("Ability")
441	GemRB.SetupQuickSlot (pc, 0, slot, ability)
442	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
443	return
444
445#in pst only nordom has bolts and they show on the same floatmenu as quickweapons, so needs work here
446def SelectQuiverSlot():
447	pc = GemRB.GameGetFirstSelectedActor ()
448	slot = GemRB.GetVar ("Slot")
449	slot_item = GemRB.GetSlotItem (pc, slot)
450	# HACK: implement SetEquippedAmmunition instead?
451	if not GemRB.IsDraggingItem ():
452		item = GemRB.GetItem (slot_item["ItemResRef"])
453		GemRB.DragItem (pc, slot, item["ItemIcon"]) #, 0, 0)
454		GemRB.DropDraggedItem (pc, slot)
455	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
456	return
457
458#this doubles up as an ammo selector (not yet used in pst)
459def SetupItemAbilities(pc, slot, only):
460	slot_item = GemRB.GetSlotItem(pc, slot)
461	if not slot_item:
462		# CHIV: Could configure empty quickslots from the game screen ala spells heres
463		return
464
465	item = GemRB.GetItem (slot_item["ItemResRef"])
466	Tips = item["Tooltips"]
467	Locations = item["Locations"]
468
469	# clear buttons here
470	EmptyControls()
471
472	# check A: whether ranged weapon and B: whether to bother at all
473	ammotype = 0
474	if item["Type"] == CommonTables.ItemType.GetRowIndex ("BOW"):
475		ammotype = CommonTables.ItemType.GetRowIndex ("ARROW")
476	elif item["Type"] == CommonTables.ItemType.GetRowIndex ("XBOW"):
477		ammotype = CommonTables.ItemType.GetRowIndex ("BOLT")
478	elif item["Type"] == CommonTables.ItemType.GetRowIndex ("SLING"):
479		ammotype = CommonTables.ItemType.GetRowIndex ("BULLET")
480
481	ammoSlotCount = 0
482	if ammotype:
483		ammoslots = GemRB.GetSlots(pc, SLOT_QUIVER, 1)
484		currentammo = GemRB.GetEquippedAmmunition (pc)
485		currentbutton = None
486		for i in range (12):
487			Button = CurrentWindow.GetControl (i+ActionBarControlOffset)
488			if i < len(ammoslots):
489				ammoslot = GemRB.GetSlotItem (pc, ammoslots[i])
490				st = GemRB.GetSlotType (ammoslots[i])
491				ammoitem = GemRB.GetItem (ammoslot['ItemResRef']) # needed to show the ammo count
492				Tips = ammoitem["Tooltips"]
493				# if this item is valid ammo and was really found in a quiver slot
494				if ammoitem['Type'] == ammotype and st["Type"] == SLOT_QUIVER:
495					ammoSlotCount += 1
496					Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_ALIGN_BOTTOM|IE_GUI_BUTTON_ALIGN_RIGHT, OP_SET)
497					Button.SetSprites ("GUIBTBUT", 0, 0,1,3,5)
498					Button.SetItemIcon (ammoslot['ItemResRef'])
499					Button.SetText (str(ammoslot["Usages0"]))
500					Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectQuiverSlot)
501					Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, SelectQuiverSlot)
502					Button.SetVarAssoc ("Slot", ammoslots[i])
503					if Tips[0] != -1:
504						Button.SetTooltip (Tips[0])
505					if currentammo == ammoslots[i]:
506						currentbutton = Button
507
508		if currentbutton:
509			currentbutton.SetState (IE_GUI_BUTTON_SELECTED)
510
511	# skip when there is only one choice
512	if ammoSlotCount == 1:
513		ammoSlotCount = 0
514
515	# reset back to the main action bar if there are no extra headers or quivers
516	reset = not ammoSlotCount
517
518	# check for item abilities and skip irrelevant headers
519	# for quick weapons only show weapon headers
520	# for quick items only show the opposite
521	# for scrolls only show the first (second header is for learning)
522	# So depending on the context Staff of Magi will have none or 2
523	if item["Type"] == CommonTables.ItemType.GetRowIndex ("SCROLL"):
524		Tips = ()
525
526	# skip launchers - handled above
527	# gesen bow (bg2:bow19) has just a projectile header (not bow) for its special attack
528	# TODO: we should append it to the list of ammo as a usability improvement
529	if only == UAW_QWEAPONS and ammoSlotCount > 1:
530		Tips = ()
531
532	if len(Tips) > 0:
533		reset = False
534		rmax = min(len(Tips), 12-ammoSlotCount)
535
536		# for mixed items, only show headers if there is more than one appropriate one
537		weaps = sum([i == ITEM_LOC_WEAPON for i in Locations])
538		if only == UAW_QWEAPONS and weaps == 1 and ammoSlotCount <= 1:
539			rmax = 0
540			reset = True
541		abils = sum([i == ITEM_LOC_EQUIPMENT for i in Locations])
542		if only == UAW_QITEMS and abils == 1:
543			rmax = 0
544			reset = True
545
546		for i in range (rmax):
547			if only == UAW_QITEMS:
548				if Locations[i] != ITEM_LOC_EQUIPMENT:
549					continue
550			elif only == UAW_QWEAPONS:
551				if Locations[i] != ITEM_LOC_WEAPON:
552					continue
553			Button = CurrentWindow.GetControl (i+ActionBarControlOffset+ammoSlotCount)
554			Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
555			Button.SetSprites ("GUIBTBUT", 0, 0,1,2,5)
556			Button.SetItemIcon (slot_item['ItemResRef'], i+6)
557			Button.SetText ("")
558			Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectItemAbility)
559			Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, SelectItemAbility)
560			Button.SetVarAssoc ("Ability", i)
561			Button.SetState (IE_GUI_BUTTON_ENABLED)
562			if Tips[i] != -1:
563				Button.SetTooltip ("F%d - %s" %(i, GemRB.GetString (Tips[i])))
564
565	if reset:
566		GemRB.SetVar ("ActionLevel", UAW_STANDARD)
567		UpdateActionsWindow ()
568	return
569
570# iwd2 spell book/class selection
571def SetupBookSelection ():
572	pc = GemRB.GameGetFirstSelectedActor ()
573
574	# get all the books that still have non-depleted memorisations
575	# we need this list, so we can avoid holes in the action bar
576	usableBooks = []
577	for i in range (IE_IWD2_SPELL_SONG):
578		bookClass = i
579		if i == IE_IWD2_SPELL_INNATE: # shape stat comes later (8 vs 10)
580			bookClass = IE_IWD2_SPELL_SHAPE
581
582		enabled = False
583		if i <= (IE_IWD2_SPELL_DOMAIN + 1): # booktypes up to and including domain + shapes
584			spellCount = len(Spellbook.GetUsableMemorizedSpells (pc, bookClass))
585			enabled = spellCount > 0
586		if enabled:
587			usableBooks.append(i)
588
589	# if we have only one or only cleric+domain, skip to the spells
590	bookCount = len(usableBooks)
591	if bookCount == 1 or (bookCount == 2 and IE_IWD2_SPELL_CLERIC in usableBooks):
592		GemRB.SetVar ("ActionLevel", UAW_SPELLS_DIRECT)
593		UpdateActionsWindow ()
594		return
595
596	for i in range (12):
597		Button = CurrentWindow.GetControl (i+ActionBarControlOffset)
598		if i >= len(usableBooks):
599			Button.SetActionIcon (globals(), -1)
600			continue
601		Button.SetActionIcon (globals(), 40+usableBooks[i])
602	return
603
604#you can change this for custom skills, this is the original engine
605skillbar=(ACT_STEALTH, ACT_SEARCH, ACT_THIEVING, ACT_WILDERNESS, ACT_TAMING, 100, 100, 100, 100, 100, 100, 100)
606def SetupSkillSelection ():
607	pc = GemRB.GameGetFirstSelectedActor ()
608	CurrentWindow.SetupControls( globals(), pc, ActionBarControlOffset, skillbar)
609	return
610
611def UpdateActionsWindow ():
612	"""Redraws the actions section of the window."""
613	global CurrentWindow
614	global level, TopIndex
615
616	PortraitWindow = GemRB.GetView("PORTWIN")
617	ActionsWindow = GemRB.GetView("ACTWIN")
618
619	if not GameCheck.IsIWD2():
620		CurrentWindow = ActionsWindow
621		ActionBarControlOffset = 0
622		UpdateClock ()
623	else:
624		CurrentWindow = PortraitWindow
625		ActionBarControlOffset = 6 # set it here too, since we get called before menu setup
626
627	if GameCheck.IsPST():
628		return
629
630	if CurrentWindow == None:
631		return
632
633	Selected = GemRB.GetSelectedSize()
634
635	#setting up the disabled button overlay (using the second border slot)
636	for i in range (12):
637		Button = CurrentWindow.GetControl (i+ActionBarControlOffset)
638		if GameCheck.IsBG1():
639			color = {'r' : 0, 'g' : 254, 'b' :0, 'a' : 255}
640			Button.SetBorder (0, color, 0, 0, Button.GetInsetFrame(6,6,4,4))
641
642		color = {'r' : 50, 'g' : 30, 'b' :10, 'a' : 120}
643		Button.SetBorder (1, color, 0, 1)
644		Button.SetFont ("NUMBER")
645		Button.SetText ("")
646		Button.SetTooltip("")
647
648	if Selected == 0:
649		EmptyControls ()
650		return
651	if Selected > 1:
652		GroupControls ()
653		return
654
655	#we are sure there is only one actor selected
656	pc = GemRB.GameGetFirstSelectedActor ()
657
658	level = GemRB.GetVar ("ActionLevel")
659	TopIndex = GemRB.GetVar ("TopIndex")
660	if level == UAW_STANDARD:
661		#this is based on class
662		CurrentWindow.SetupControls (globals(), pc, ActionBarControlOffset)
663	elif level == UAW_EQUIPMENT:
664		CurrentWindow.SetupEquipmentIcons(globals(), pc, TopIndex, ActionBarControlOffset)
665	elif level == UAW_SPELLS or level == UAW_SPELLS_DIRECT: #spells
666
667		if GameCheck.IsIWD2():
668			if level == UAW_SPELLS:
669				# set up book selection if appropriate
670				SetupBookSelection ()
671				return
672			# otherwise just throw everything in a single list
673			# everything but innates, songs and shapes
674			spelltype = (1<<IE_IWD2_SPELL_INNATE) - 1
675		else:
676			spelltype = (1<<IE_SPELL_TYPE_PRIEST) + (1<<IE_SPELL_TYPE_WIZARD)
677		GemRB.SetVar ("Type", spelltype)
678		Spellbook.SetupSpellIcons(CurrentWindow, spelltype, TopIndex, ActionBarControlOffset)
679	elif level == UAW_INNATES: #innates
680		if GameCheck.IsIWD2():
681			spelltype = (1<<IE_IWD2_SPELL_INNATE) + (1<<IE_IWD2_SPELL_SHAPE)
682		else:
683			spelltype = 1<<IE_SPELL_TYPE_INNATE
684		GemRB.SetVar ("Type", spelltype)
685		Spellbook.SetupSpellIcons(CurrentWindow, spelltype, TopIndex, ActionBarControlOffset)
686	elif level == UAW_QWEAPONS or level == UAW_QITEMS: #quick weapon or quick item ability selection
687		SetupItemAbilities(pc, GemRB.GetVar("Slot"), level)
688	elif level == UAW_ALLMAGE: #all known mage spells
689		GemRB.SetVar ("Type", -1)
690		Spellbook.SetupSpellIcons(CurrentWindow, -1, TopIndex, ActionBarControlOffset)
691	elif level == UAW_SKILLS: # iwd2 skills
692		SetupSkillSelection()
693	elif level == UAW_QSPELLS: # quickspells, but with innates too
694		if GameCheck.IsIWD2():
695			spelltype = (1<<IE_IWD2_SPELL_INNATE) - 1
696			spelltype += (1<<IE_IWD2_SPELL_INNATE) + (1<<IE_IWD2_SPELL_SHAPE)
697		else:
698			spelltype = (1<<IE_SPELL_TYPE_PRIEST) + (1<<IE_SPELL_TYPE_WIZARD) + (1<<IE_SPELL_TYPE_INNATE)
699		GemRB.SetVar ("Type", spelltype)
700		Spellbook.SetupSpellIcons(CurrentWindow, spelltype, TopIndex, ActionBarControlOffset)
701	elif level == UAW_QSHAPES: # shapes selection
702		spelltype = 1<<IE_IWD2_SPELL_SHAPE
703		GemRB.SetVar ("Type", spelltype)
704		Spellbook.SetupSpellIcons(CurrentWindow, spelltype, TopIndex, ActionBarControlOffset)
705	elif level == UAW_QSONGS: # songs selection
706		spelltype = 1<<IE_IWD2_SPELL_SONG
707		GemRB.SetVar ("Type", spelltype)
708		Spellbook.SetupSpellIcons(CurrentWindow, spelltype, TopIndex, ActionBarControlOffset)
709	elif level == UAW_BOOK: # spellbook selection
710		spelltype = GemRB.GetVar ("Type")
711		Spellbook.SetupSpellIcons(CurrentWindow, spelltype, TopIndex, ActionBarControlOffset)
712	elif level == UAW_2DASPELLS: # spells from a 2da (fx_select_spell)
713		if GameCheck.IsIWD2():
714			# everything but innates, songs and shapes
715			spelltype = (1<<IE_IWD2_SPELL_INNATE) - 1
716		else:
717			spelltype = (1<<IE_SPELL_TYPE_PRIEST) + (1<<IE_SPELL_TYPE_WIZARD)
718		GemRB.SetVar ("Type", spelltype)
719		Spellbook.SetupSpellIcons (CurrentWindow, spelltype, TopIndex, ActionBarControlOffset)
720	else:
721		print("Invalid action level:", level)
722		GemRB.SetVar ("ActionLevel", UAW_STANDARD)
723	return
724
725def ActionQWeaponPressed (which):
726	"""Selects the given quickslot weapon if possible."""
727
728	pc = GemRB.GameGetFirstSelectedActor ()
729	qs = GemRB.GetEquippedQuickSlot (pc, 1)
730
731	#38 is the magic slot
732	if ((qs==which) or (qs==38)) and GemRB.GameControlGetTargetMode() != TARGET_MODE_ATTACK:
733		GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK, GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
734	else:
735		GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
736		GemRB.SetEquippedQuickSlot (pc, which, -1)
737
738	CurrentWindow.SetupControls (globals(), pc, ActionBarControlOffset)
739	UpdateActionsWindow ()
740	return
741
742# TODO: implement full weapon set switching instead
743def ActionQWeaponRightPressed (action):
744	"""Selects the used ability of the quick weapon."""
745	GemRB.SetVar ("Slot", action)
746	GemRB.SetVar ("ActionLevel", UAW_QWEAPONS)
747	UpdateActionsWindow ()
748	return
749
750###############################################
751# quick icons for spells, innates, songs, shapes
752
753def ActionQSpellPressed (which):
754	pc = GemRB.GameGetFirstSelectedActor ()
755
756	GemRB.SpellCast (pc, -2, which)
757	UpdateActionsWindow ()
758	return
759
760def ActionQSpellRightPressed (which):
761	GemRB.SetVar ("QSpell", which)
762	GemRB.SetVar ("TopIndex", 0)
763	GemRB.SetVar ("ActionLevel", UAW_QSPELLS)
764	UpdateActionsWindow ()
765	return
766
767suf = ["", "Right"]
768# this function is used to generate various action bar actions
769# that should be available in 9 slots (iwd2)
770def GenerateButtonActions(num, name, g, right=0, offset=0):
771	dec = "def Action" + name + str(num+1) + suf[right] + "Pressed():\n"
772	dec += "\tAction" + name + suf[right] + "Pressed(" + str(num+offset) + ")"
773	exec(dec, g) # pass on the same global dict, so we remain in the top scope
774
775for i in range(9):
776	GenerateButtonActions(i, "QSpec", globals(), 0)
777	GenerateButtonActions(i, "QSpec", globals(), 1)
778	GenerateButtonActions(i, "QSpell", globals(), 0)
779	GenerateButtonActions(i, "QSpell", globals(), 1)
780	GenerateButtonActions(i, "QShape", globals(), 0)
781	GenerateButtonActions(i, "QShape", globals(), 1)
782	GenerateButtonActions(i, "QSong", globals(), 0)
783	GenerateButtonActions(i, "QSong", globals(), 1)
784for i in range(4):
785	GenerateButtonActions(i, "QWeapon", globals(), 0)
786	GenerateButtonActions(i, "QWeapon", globals(), 1, 10)
787
788def ActionQSpecPressed (which):
789	ActionQSpellPressed (which)
790
791def ActionQSpecRightPressed (which):
792	GemRB.SetVar ("QSpell", which)
793	GemRB.SetVar ("TopIndex", 0)
794	GemRB.SetVar ("ActionLevel", UAW_INNATES)
795	UpdateActionsWindow ()
796
797def ActionQShapePressed (which):
798	ActionQSpellPressed (which)
799
800def ActionQShapeRightPressed (which):
801	GemRB.SetVar ("QSpell", which)
802	GemRB.SetVar ("TopIndex", 0)
803	GemRB.SetVar ("ActionLevel", UAW_QSHAPES)
804	UpdateActionsWindow ()
805
806def ActionQSongPressed (which):
807	SelectBardSong (which) # TODO: verify parameter once we have actionbar customisation
808	ActionBardSongPressed ()
809
810def ActionQSongRightPressed (which):
811	GemRB.SetVar ("QSpell", which)
812	GemRB.SetVar ("TopIndex", 0)
813	GemRB.SetVar ("ActionLevel", UAW_QSONGS)
814	UpdateActionsWindow ()
815
816# can't pass the globals dictionary from another module
817def SetActionIconWorkaround(Button, action, function):
818	Button.SetActionIcon (globals(), action, function)
819
820#no check needed because the button wouldn't be drawn if illegal
821def ActionLeftPressed ():
822	"""Scrolls the actions window left.
823
824	Used primarily for spell selection."""
825
826	TopIndex = GemRB.GetVar ("TopIndex")
827	if TopIndex>10:
828		TopIndex -= 10
829	else:
830		TopIndex = 0
831	GemRB.SetVar ("TopIndex", TopIndex)
832	UpdateActionsWindow ()
833	return
834
835#no check needed because the button wouldn't be drawn if illegal
836def ActionRightPressed ():
837	"""Scrolls the action window right.
838
839	Used primarily for spell selection."""
840
841	pc = GemRB.GameGetFirstSelectedActor ()
842	TopIndex = GemRB.GetVar ("TopIndex")
843	Type = GemRB.GetVar ("Type")
844	print("Type:", Type)
845	#Type is a bitfield if there is no level given
846	#This is to make sure cleric/mages get all spells listed
847	if GemRB.GetVar ("ActionLevel") == UAW_ALLMAGE:
848		if Type == 3:
849			Max = len(Spellbook.GetKnownSpells (pc, IE_SPELL_TYPE_PRIEST) + Spellbook.GetKnownSpells (pc, IE_SPELL_TYPE_WIZARD))
850		else:
851			Max = GemRB.GetKnownSpellsCount (pc, Type, -1) # this can handle only one type at a time
852	else:
853		Max = GemRB.GetMemorizedSpellsCount(pc, Type, -1, 1)
854	print("Max:", Max)
855	TopIndex += 10
856	if TopIndex > Max - 10:
857		if Max>10:
858			if TopIndex > Max:
859				TopIndex = Max - 10
860		else:
861			TopIndex = 0
862	GemRB.SetVar ("TopIndex", TopIndex)
863	UpdateActionsWindow ()
864	return
865
866def ActionMeleePressed ():
867	""" switches to the most damaging melee weapon"""
868	#get the party Index
869	pc = GemRB.GameGetFirstSelectedPC ()
870	GemRB.ExecuteString("EquipMostDamagingMelee()", pc)
871	return
872
873def ActionRangePressed ():
874	""" switches to the most damaging ranged weapon"""
875	#get the party Index
876	pc = GemRB.GameGetFirstSelectedPC ()
877	GemRB.ExecuteString("EquipRanged()", pc)
878	return
879
880def ActionShapeChangePressed ():
881	GemRB.SetVar ("ActionLevel", UAW_QSHAPES)
882	GemRB.SetVar ("TopIndex", 0)
883	UpdateActionsWindow ()
884	return
885
886def SelectBardSong (which):
887	pc = GemRB.GameGetFirstSelectedActor ()
888	songs = Spellbook.GetKnownSpells (pc, IE_IWD2_SPELL_SONG)
889	# "which" is a mashup of the spell index with it's type
890	idx = which % ((1<<IE_IWD2_SPELL_SHAPE)*100)
891	qsong = songs[idx]['SpellResRef']
892	# the effect needs to be set each tick, so we use FX_DURATION_INSTANT_PERMANENT==1 timing mode
893	# GemRB.SetModalState can also set the spell, but it wouldn't persist
894	GemRB.ApplyEffect (pc, 'ChangeBardSong', 0, idx, qsong, "", "", "", 1)
895
896def ActionBardSongRightPressed ():
897	"""Selects a bardsong."""
898	GemRB.SetVar ("ActionLevel", UAW_QSONGS)
899	GemRB.SetVar ("TopIndex", 0)
900	UpdateActionsWindow ()
901	return
902
903def ActionBardSongPressed ():
904	"""Toggles the battle song."""
905
906	#get the global ID
907	pc = GemRB.GameGetFirstSelectedActor ()
908	GemRB.SetModalState (pc, MS_BATTLESONG)
909	GemRB.PlaySound ("act_01")
910	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
911	UpdateActionsWindow ()
912	return
913
914def ActionSearchPressed ():
915	"""Toggles detect traps."""
916
917	#get the global ID
918	pc = GemRB.GameGetFirstSelectedActor ()
919	GemRB.SetModalState (pc, MS_DETECTTRAPS)
920	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
921	UpdateActionsWindow ()
922	return
923
924def ActionStealthPressed ():
925	"""Toggles stealth."""
926	pc = GemRB.GameGetFirstSelectedActor ()
927	GemRB.SetModalState (pc, MS_STEALTH)
928	GemRB.PlaySound ("act_07", CHAN_HITS)
929	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
930	UpdateActionsWindow ()
931	return
932
933def ActionTurnPressed ():
934	"""Toggles turn undead."""
935	pc = GemRB.GameGetFirstSelectedActor ()
936	GemRB.SetModalState (pc, MS_TURNUNDEAD)
937	GemRB.PlaySound ("act_06")
938	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
939	UpdateActionsWindow ()
940	return
941
942def ActionTamingPressed ():
943	pc = GemRB.GameGetFirstSelectedActor ()
944	GemRB.SpellCast (pc, -3, 0, "spin108")
945	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
946	UpdateActionsWindow ()
947	return
948
949def ActionWildernessPressed ():
950	pc = GemRB.GameGetFirstSelectedActor ()
951	GemRB.ApplyEffect (pc, "Reveal:Tracks", 0, 0)
952	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
953	UpdateActionsWindow ()
954	return
955
956def ActionUseItemPressed ():
957	GemRB.SetVar ("TopIndex", 0)
958	GemRB.SetVar ("ActionLevel", UAW_EQUIPMENT)
959	UpdateActionsWindow ()
960	return
961
962def ActionCastPressed ():
963	"""Opens the spell choice scrollbar."""
964	GemRB.SetVar ("QSpell", -1)
965	GemRB.SetVar ("TopIndex", 0)
966	GemRB.SetVar ("ActionLevel", UAW_SPELLS)
967	UpdateActionsWindow ()
968	return
969
970def ActionQItemPressed (action):
971	"""Uses the given quick item."""
972	pc = GemRB.GameGetFirstSelectedActor ()
973	#quick slot
974	GemRB.UseItem (pc, -2, action, -1)
975	return
976
977def ActionQItem1Pressed ():
978	ActionQItemPressed (ACT_QSLOT1)
979	return
980
981def ActionQItem2Pressed ():
982	ActionQItemPressed (ACT_QSLOT2)
983	return
984
985def ActionQItem3Pressed ():
986	ActionQItemPressed (ACT_QSLOT3)
987	return
988
989def ActionQItem4Pressed ():
990	ActionQItemPressed (ACT_QSLOT4)
991	return
992
993def ActionQItem5Pressed ():
994	ActionQItemPressed (ACT_QSLOT5)
995	return
996
997def ActionQItemRightPressed (action):
998	"""Selects the used ability of the quick item."""
999	GemRB.SetVar ("Slot", action)
1000	GemRB.SetVar ("ActionLevel", UAW_QITEMS)
1001	UpdateActionsWindow ()
1002	return
1003
1004def ActionQItem1RightPressed ():
1005	ActionQItemRightPressed (19)
1006
1007def ActionQItem2RightPressed ():
1008	ActionQItemRightPressed (20)
1009
1010def ActionQItem3RightPressed ():
1011	ActionQItemRightPressed (21)
1012
1013def ActionQItem4RightPressed ():
1014	ActionQItemRightPressed (22)
1015
1016def ActionQItem5RightPressed ():
1017	ActionQItemRightPressed (23)
1018
1019def ActionInnatePressed ():
1020	"""Opens the innate spell scrollbar."""
1021	GemRB.SetVar ("QSpell", -1)
1022	GemRB.SetVar ("TopIndex", 0)
1023	GemRB.SetVar ("ActionLevel", UAW_INNATES)
1024	UpdateActionsWindow ()
1025	return
1026
1027def ActionSkillsPressed ():
1028	GemRB.SetVar ("TopIndex", 0)
1029	GemRB.SetVar ("ActionLevel", UAW_SKILLS)
1030	UpdateActionsWindow ()
1031	return
1032
1033def TypeSpellPressed (spelltype):
1034	GemRB.SetVar ("Type", 1 << spelltype)
1035	GemRB.SetVar ("ActionLevel", UAW_BOOK)
1036	UpdateActionsWindow ()
1037	return
1038
1039def ActionBardSpellPressed ():
1040	TypeSpellPressed(IE_IWD2_SPELL_BARD)
1041	return
1042
1043def ActionClericSpellPressed ():
1044	TypeSpellPressed(IE_IWD2_SPELL_CLERIC)
1045	return
1046
1047def ActionDruidSpellPressed ():
1048	TypeSpellPressed(IE_IWD2_SPELL_DRUID)
1049	return
1050
1051def ActionPaladinSpellPressed ():
1052	TypeSpellPressed(IE_IWD2_SPELL_PALADIN)
1053	return
1054
1055def ActionRangerSpellPressed ():
1056	TypeSpellPressed(IE_IWD2_SPELL_RANGER)
1057	return
1058
1059def ActionSorcererSpellPressed ():
1060	TypeSpellPressed(IE_IWD2_SPELL_SORCERER)
1061	return
1062
1063def ActionWizardSpellPressed ():
1064	TypeSpellPressed(IE_IWD2_SPELL_WIZARD)
1065	return
1066
1067def ActionDomainSpellPressed ():
1068	TypeSpellPressed(IE_IWD2_SPELL_DOMAIN)
1069	return
1070
1071def ActionWildShapesPressed ():
1072	TypeSpellPressed(IE_IWD2_SPELL_SHAPE)
1073	return
1074
1075def SpellShiftPressed ():
1076	Spell = GemRB.GetVar ("Spell") # spellindex from spellbook jumbled with booktype
1077	Type =  Spell // 1000
1078	SpellIndex = Spell % 1000
1079
1080	# try spontaneous casting
1081	pc = GemRB.GameGetFirstSelectedActor ()
1082	ClassRowName = GUICommon.GetClassRowName (pc)
1083	SponCastTableName = CommonTables.ClassSkills.GetValue (ClassRowName, "SPONCAST")
1084	if SponCastTableName != "*":
1085		SponCastTable = GemRB.LoadTable (SponCastTableName, 1)
1086		if not SponCastTable:
1087			print("SpellShiftPressed: skipping, non-existent spontaneous casting table used! ResRef:", SponCastTableName)
1088			SpellPressed ()
1089			return
1090
1091		# determine the column number (spell variety) depending on alignment
1092		CureOrHarm = GemRB.GetPlayerStat (pc, IE_ALIGNMENT)
1093		if CureOrHarm % 16 == 3: # evil
1094			CureOrHarm = 1
1095		else:
1096			CureOrHarm = 0
1097
1098		# get the unshifted booktype
1099		BaseType = 0
1100		tmp = Type
1101		while tmp > 1:
1102			tmp = tmp>>1
1103			BaseType += 1
1104
1105		# figure out the spell's details
1106		# TODO: find a simpler way
1107		Spell = None
1108		MemorisedSpells = Spellbook.GetSpellinfoSpells (pc, BaseType)
1109		for spell in MemorisedSpells:
1110			if spell['SpellIndex']%(255000) == SpellIndex: # 255 is the engine value of Type
1111				Spell = spell
1112				break
1113
1114		# rownames==level; col1: good+neutral; col2: evil resref
1115		Level = Spell['SpellLevel']
1116		ReplacementSpell = SponCastTable.GetValue (Level-1, CureOrHarm).upper()
1117		if ReplacementSpell != Spell['SpellResRef'].upper():
1118			SpellIndex = GemRB.PrepareSpontaneousCast (pc, Spell['SpellResRef'], Spell['BookType'], Level, ReplacementSpell)
1119			GemRB.SetVar ("Spell", SpellIndex+1000*Type)
1120			if GameCheck.IsIWD2():
1121				GemRB.DisplayString (39742, ColorWhite, pc) # Spontaneous Casting
1122
1123	# proceed as if nothing happened
1124	SpellPressed ()
1125
1126# This is the endpoint for spellcasting, finally calling SpellCast. This always happens at least
1127# twice though, the second time to reset the action bar (more if wild magic or subspell selection is involved).
1128# Spell and Type (spellbook type) are set during the spell bar construction/use, which is in turn
1129# affected by ActionLevel (see UpdateActionsWindow of this module).
1130# Keep in mind, that the core resets Type and/or ActionLevel in the case of subspells (fx_select_spell).
1131def SpellPressed ():
1132	"""Prepares a spell to be cast."""
1133
1134	pc = GemRB.GameGetFirstSelectedActor ()
1135
1136	Spell = GemRB.GetVar ("Spell")
1137	Type = GemRB.GetVar ("Type")
1138
1139	if Type == 1<<IE_IWD2_SPELL_SONG:
1140		SelectBardSong (Spell)
1141		ActionBardSongPressed()
1142		return
1143
1144	GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
1145	if Type != -1:
1146		Type = Spell // 1000
1147	Spell = Spell % 1000
1148	slot = GemRB.GetVar ("QSpell")
1149	if slot>=0:
1150		#setup quickspell slot
1151		#if spell has no target, return
1152		#otherwise continue with casting
1153		Target = GemRB.SetupQuickSpell (pc, slot, Spell, Type)
1154		#sabotage the immediate casting of self targeting spells
1155		if Target == 5 or Target == 7:
1156			Type = -1
1157			GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
1158
1159	if Type==-1:
1160		GemRB.SetVar ("ActionLevel", UAW_STANDARD)
1161		GemRB.SetVar("Type", 0)
1162	GemRB.SpellCast (pc, Type, Spell)
1163	if GemRB.GetVar ("Type")!=-1:
1164		GemRB.SetVar ("ActionLevel", UAW_STANDARD)
1165		#init spell list
1166		GemRB.SpellCast (pc, -1, 0)
1167	GemRB.SetVar ("TopIndex", 0)
1168	UpdateActionsWindow ()
1169	return
1170
1171def EquipmentPressed ():
1172	pc = GemRB.GameGetFirstSelectedActor ()
1173
1174	GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
1175	Item = GemRB.GetVar ("Equipment")
1176	#equipment index
1177	GemRB.UseItem (pc, -1, Item, -1)
1178	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
1179	UpdateActionsWindow ()
1180	return
1181
1182######################
1183
1184#End of features that need adding to pst
1185
1186######################
1187
1188# NOTE: the following two features are only used in pst
1189# which=INVENTORY|STATS|FMENU
1190def GetActorPortrait (actor, which):
1191	#return GemRB.GetPlayerPortrait( actor, which)
1192
1193	# only the lowest byte is meaningful here (OneByteAnimID)
1194	anim_id = GemRB.GetPlayerStat (actor, IE_ANIMATION_ID) & 255
1195	row = "0x%02X" %anim_id
1196
1197	return CommonTables.Pdolls.GetValue (row, which)
1198
1199
1200def UpdateAnimation ():
1201	if not GemRB.HasResource ("ANIMS", RES_2DA, 1):
1202		# FIXME: make a simpler version for non-pst too
1203		# this is a callback from the core on EF_UPDATEANIM!
1204		return
1205
1206	pc = GemRB.GameGetSelectedPCSingle ()
1207
1208	disguise = GemRB.GetGameVar ("APPEARANCE")
1209	if disguise == 2: #dustman
1210		animid = "DR"
1211	elif disguise == 1: #zombie
1212		animid = "ZO"
1213	else:
1214		slot = GemRB.GetEquippedQuickSlot (pc)
1215		item = GemRB.GetSlotItem (pc, slot )
1216		animid = ""
1217		if item:
1218			item = GemRB.GetItem(item["ItemResRef"])
1219			if item:
1220				animid = item["AnimationType"]
1221
1222	BioTable = GemRB.LoadTable ("BIOS")
1223	Specific = GemRB.GetPlayerStat (pc, IE_SPECIFIC)
1224	AvatarName = BioTable.GetRowName (Specific)
1225	AnimTable = GemRB.LoadTable ("ANIMS")
1226	if animid=="":
1227		animid="*"
1228	value = AnimTable.GetValue (animid, AvatarName)
1229	if value<0:
1230		return
1231	GemRB.SetPlayerStat (pc, IE_ANIMATION_ID, value)
1232	return
1233
1234# NOTE: the following 4 functions are only used in iwd2
1235def GetActorRaceTitle (actor):
1236	import IDLUCommon
1237	RaceIndex = IDLUCommon.GetRace (actor)
1238	RaceTitle = CommonTables.Races.GetValue (RaceIndex, 2)
1239	return RaceTitle
1240
1241# NOTE: this function is called with the primary classes
1242def GetKitIndex (actor, ClassIndex):
1243	Kit = GemRB.GetPlayerStat (actor, IE_KIT)
1244
1245	KitIndex = -1
1246	ClassName = CommonTables.Classes.GetRowName (ClassIndex)
1247	ClassID = CommonTables.Classes.GetValue (ClassName, "ID")
1248	# skip the primary classes
1249	# start at the first original kit - in iwd2 both classes and kits are in the same table
1250	KitOffset = CommonTables.Classes.FindValue ("CLASS", 7)
1251	for ci in range (KitOffset, CommonTables.Classes.GetRowCount ()):
1252		RowName = CommonTables.Classes.GetRowName (ci)
1253		BaseClass = CommonTables.Classes.GetValue (RowName, "CLASS")
1254		if BaseClass == ClassID and Kit & CommonTables.Classes.GetValue (RowName, "ID"):
1255			#FIXME: this will return the last kit only, check if proper multikit return values are needed
1256			KitIndex = ci
1257
1258	if KitIndex == -1:
1259		return 0
1260
1261	return KitIndex
1262
1263def GetActorClassTitle (actor, ClassIndex):
1264	ClassTitle = GemRB.GetPlayerStat (actor, IE_TITLE1)
1265	if ClassTitle:
1266		return ClassTitle
1267
1268	KitIndex = GetKitIndex (actor, ClassIndex)
1269	if KitIndex == 0:
1270		ClassName = CommonTables.Classes.GetRowName (ClassIndex)
1271	else:
1272		ClassName = CommonTables.Classes.GetRowName (KitIndex)
1273	ClassTitle = CommonTables.Classes.GetValue (ClassName, "NAME_REF")
1274
1275	if ClassTitle == "*":
1276		return 0
1277	return ClassTitle
1278
1279# overriding the one in GUICommon, since we use a different table and animations
1280def GetActorPaperDoll (actor):
1281	level = GemRB.GetPlayerStat (actor, IE_ARMOR_TYPE)
1282	return GemRB.GetAvatarsValue (actor, level)
1283
1284
1285SelectionChangeHandler = None
1286SelectionChangeMultiHandler = None ##relates to floatmenu
1287
1288def SetSelectionChangeHandler (handler):
1289	"""Updates the selection handler."""
1290
1291	global SelectionChangeHandler
1292
1293	# Switching from walking to non-walking environment:
1294	# set the first selected PC in walking env as a selected
1295	# in nonwalking env
1296	#if (not SelectionChangeHandler) and handler:
1297	if (not SelectionChangeHandler) and handler:
1298		sel = GemRB.GameGetFirstSelectedPC ()
1299		if sel:
1300			GemRB.GameSelectPCSingle (sel)
1301
1302	SelectionChangeHandler = handler
1303
1304	# redraw selection on change main selection | single selection
1305	# SelectionChanged ()
1306	return
1307
1308def SetSelectionChangeMultiHandler (handler):
1309	global SelectionChangeMultiHandler
1310	SelectionChangeMultiHandler = handler
1311	#SelectionChanged ()
1312
1313def CloseTopWindow ():
1314	window = GemRB.GetView("WIN_TOP")
1315	# we cannot close the current WIN_TOP unless it has focus
1316	if window and window.HasFocus == True:
1317		window.Close()
1318		UpdateClock()
1319
1320def TopWindowClosed(window):
1321	optwin = GemRB.GetView("OPTWIN")
1322	btnid = GemRB.GetVar("OPTBTN")
1323	button = optwin.GetControl(btnid) if optwin else None
1324	if button:
1325		button.SetState(IE_GUI_BUTTON_UNPRESSED)
1326	rtgbtn = optwin.GetControl(0) if optwin else None # return to game button
1327	if rtgbtn: # not in PST or IWD2
1328		rtgbtn.SetState(IE_GUI_BUTTON_SELECTED)
1329
1330	print("pause state " + str(CreateTopWinLoader.PauseState))
1331	if CreateTopWinLoader.PauseState is not None:
1332		GemRB.GamePause (CreateTopWinLoader.PauseState, 3)
1333
1334	GameWin = GemRB.GetView("GAMEWIN")
1335	GameWin.SetDisabled(False)
1336
1337	if not CommonWindow.IsGameGUIHidden() and GemRB.GetView ("ACTWIN") and not (GameCheck.IsIWD2() or GameCheck.IsPST()):
1338		GemRB.GetView ("ACTWIN").SetVisible (True)
1339		GemRB.GetView ("MSGWIN").SetVisible(True)
1340
1341	# TODO: this should be moved to GUIINV and that window should have a custom close handler
1342	# that does this stuff then calls this manually
1343
1344	GemRB.LeaveContainer()
1345	if GemRB.IsDraggingItem () == 1:
1346		pc = GemRB.GameGetSelectedPCSingle ()
1347		#store the item in the inventory before window is closed
1348		GemRB.DropDraggedItem (pc, -3)
1349		#dropping on ground if cannot store in inventory
1350		if GemRB.IsDraggingItem () == 1:
1351			GemRB.DropDraggedItem (pc, -2)
1352
1353	# for worldmap purposes
1354	GemRB.SetVar ("Travel", -1)
1355
1356	#don't go back to multi selection mode when going to the store screen
1357	if not GemRB.GetVar ("Inventory"):
1358		SetSelectionChangeHandler (None)
1359
1360	SelectionChanged()
1361
1362if GameCheck.IsIWD2():
1363	DefaultWinPos = WINDOW_TOP|WINDOW_HCENTER
1364else:
1365	DefaultWinPos = WINDOW_CENTER
1366
1367def CreateTopWinLoader(id, pack, loader, initer = None, selectionHandler = None, pos = DefaultWinPos, pause = False):
1368	def ret (btn = None, val = None):
1369		topwin = GemRB.GetView("WIN_TOP")
1370		if topwin and topwin.HasFocus == False:
1371			return None # we cannot close the current WIN_TOP unless it has focus
1372
1373		window = loader(id, pack, pos)
1374
1375		if window:
1376			# set this before calling initer so initer can change it if it wants
1377			window.SetFlags(WF_ALPHA_CHANNEL, OP_NAND)
1378			if initer:
1379				initer(window)
1380
1381			if selectionHandler:
1382				selectionHandler(window)
1383				window.SetAction(selectionHandler, ACTION_WINDOW_FOCUS_GAINED)
1384
1385			SetTopWindow (window, selectionHandler)
1386			window.SetAction(lambda: TopWindowClosed(window), ACTION_WINDOW_CLOSED)
1387			if GameCheck.IsPST ():
1388				import FloatMenuWindow
1389				if FloatMenuWindow.FloatMenuWindow:
1390					FloatMenuWindow.FloatMenuWindow.Close ()
1391
1392			if pause:
1393				CreateTopWinLoader.PauseState = GemRB.GamePause(3, 1)
1394				GemRB.GamePause (1, 3)
1395			else:
1396				CreateTopWinLoader.PauseState = None
1397
1398			optwin = GemRB.GetView("OPTWIN")
1399			if optwin:
1400				rtgbtn = optwin.GetControl(0) # return to game button
1401			if optwin and rtgbtn: # not in PST or IWD2
1402				rtgbtn.SetState(IE_GUI_BUTTON_UNPRESSED)
1403			if btn:
1404				btn.SetState(IE_GUI_BUTTON_SELECTED)
1405				GemRB.SetVar("OPTBTN", val) # cant use btn.ID because it is "too large to convert to C long"
1406
1407			GameWin = GemRB.GetView("GAMEWIN")
1408			GameWin.SetDisabled(True)
1409
1410			if not CommonWindow.IsGameGUIHidden() and GemRB.GetView ("ACTWIN") and not (GameCheck.IsIWD2() or GameCheck.IsPST()):
1411				# hide some windows to declutter higher resolutions and avoid unwanted interaction
1412				GemRB.GetView ("ACTWIN").SetVisible(False)
1413				GemRB.GetView ("MSGWIN").SetVisible(False)
1414
1415		return window
1416
1417	return ret
1418
1419def SetTopWindow (window, selectionHandler = None):
1420	topwin = GemRB.GetView("WIN_TOP")
1421	if topwin == window:
1422		return
1423
1424	pc = GemRB.GameGetSelectedPCSingle()
1425
1426	if topwin:
1427		topwin.Close() # invalidates topwin so must use a different variable
1428		preserveSelection = True
1429	else:
1430		preserveSelection = False
1431
1432	if window:
1433		window.AddAlias("WIN_TOP")
1434		window.Focus()
1435
1436		UpdateClock()
1437
1438		if selectionHandler:
1439			selectionHandler = lambda win=window, fn=selectionHandler: fn(win)
1440
1441		SetSelectionChangeHandler (selectionHandler)
1442		if preserveSelection:
1443			# we are going to another topwin so preserve the selection
1444			GemRB.GameSelectPCSingle(pc)
1445	else:
1446		SetSelectionChangeHandler (None)
1447
1448def OpenWindowOnce(id, pack, pos=WINDOW_CENTER):
1449	window = GemRB.GetView(pack, id)
1450	if window:
1451		window.Focus()
1452		return None
1453	else:
1454		return GemRB.LoadWindow(id, pack, pos)
1455
1456def ToggleWindow(id, pack, pos=WINDOW_CENTER):
1457	# do nothing while in a store
1458	if GemRB.GetView ("WIN_STORE"):
1459		return None
1460
1461	window = GemRB.GetView(pack, id)
1462	if window:
1463		window.Close()
1464		UpdateClock ()
1465		return None
1466	else:
1467		return GemRB.LoadWindow(id, pack, pos)
1468
1469# returns buttons and a numerical index
1470# does nothing new in pst, iwd2 due to layout
1471# in the rest, it will enable extra button generation for higher resolutions
1472# Mode determines arrangment direction, horizontal being for party reform and potentially save/load
1473def GetPortraitButtonPairs (Window, ExtraSlots=0, Mode="vertical"):
1474	pairs = {}
1475
1476	if not Window:
1477		return pairs
1478
1479	oldSlotCount = 6 + ExtraSlots
1480
1481	for i in range(min(oldSlotCount, MAX_PARTY_SIZE + ExtraSlots)): # the default chu/game limit or less
1482		pairs[i] = Window.GetControl (i)
1483
1484	# nothing left to do
1485	PartySize = GemRB.GetPartySize ()
1486	if PartySize <= oldSlotCount:
1487		return pairs
1488
1489	if GameCheck.IsIWD2() or GameCheck.IsPST():
1490		# set Mode = "horizontal" once we can create enough space
1491		GemRB.Log(LOG_ERROR, "GetPortraitButtonPairs", "Parties larger than 6 are currently not supported in IWD2 and PST! Using 6 ...")
1492		return pairs
1493
1494	# GUIWORLD doesn't have a separate portraits window, so we need to skip
1495	# all this magic when reforming an overflowing party
1496	if GemRB.GetPartySize () > MAX_PARTY_SIZE:
1497		return pairs
1498
1499	# generate new buttons by copying from existing ones
1500	firstButton = pairs[0]
1501	firstRect = firstButton.GetFrame ()
1502	buttonHeight = firstRect["h"]
1503	buttonWidth = firstRect["w"]
1504	xOffset = firstRect["x"]
1505	yOffset = firstRect["y"]
1506	windowRect = Window.GetFrame()
1507	windowHeight = windowRect["h"]
1508	windowWidth = windowRect["w"]
1509	limit = limitStep = 0
1510	scale = 0
1511	portraitGap = 0
1512	if Mode ==  "horizontal":
1513		xOffset += 3*buttonWidth  # width of other controls in party reform; we'll draw on the other side (atleast in guiw8, guiw10 has no need for this)
1514		maxWidth = windowWidth - xOffset
1515		limit = maxWidth
1516		limitStep = buttonWidth
1517	else:
1518		# reduce it by existing slots + 0 slots in framed views (eg. inventory) and
1519		# 1 in main game control (for spacing and any other controls below (ai/select all in bg2))
1520		maxHeight = windowHeight - buttonHeight*6 - buttonHeight // 2
1521		if windowHeight != ScreenHeight:
1522			maxHeight += buttonHeight // 2
1523		limit = maxHeight
1524		# for framed views, limited to 6, we downscale the buttons to fit, clipping their portraits
1525		if maxHeight < buttonHeight:
1526			unused = 20 # remaining unused space below the portraits
1527			scale = 1
1528			portraitGap = buttonHeight
1529			buttonHeight = (buttonHeight * 6 + unused) // PartySize
1530			portraitGap = portraitGap - buttonHeight - 2 # 2 for a quasi border
1531			limit = windowHeight - buttonHeight*6 + unused
1532		limitStep = buttonHeight
1533
1534	for i in range(len(pairs), PartySize):
1535		if limitStep > limit:
1536			raise SystemExit("Not enough window space for so many party members (portraits), bailing out! %d vs width/height of %d/%d" %(limit, buttonWidth, buttonHeight))
1537		nextID = 1000 + i
1538		control = Window.GetControl (nextID)
1539		if control:
1540			pairs[i] = control
1541			continue
1542		if Mode ==  "horizontal":
1543			Window.CreateButton (nextID, xOffset+i*buttonWidth, yOffset, buttonWidth, buttonHeight)
1544		else:
1545			# vertical
1546			Window.CreateButton (nextID, xOffset, i*buttonHeight+yOffset+i*2*scale, buttonWidth, buttonHeight)
1547
1548		button = Window.GetControl (nextID)
1549		button.SetSprites ("GUIRSPOR", 0, 0, 1, 0, 0)
1550
1551		pairs[i] = button
1552		limit -= limitStep
1553
1554	# move the buttons back up, to combine the freed space
1555	if scale:
1556		for i in range(oldSlotCount):
1557			button = pairs[i]
1558			button.SetSize (buttonWidth, buttonHeight)
1559			if i == 0:
1560				continue # don't move the first portrait
1561			rect = button.GetRect ()
1562			x = rect["X"]
1563			y = rect["Y"]
1564			button.SetPos (x, y-portraitGap*i)
1565
1566	return pairs
1567
1568def OpenInventoryWindowClick (btn, pcID):
1569	import GUIINV
1570	GemRB.GameSelectPC (pcID, True, SELECT_REPLACE)
1571	GUIINV.OpenInventoryWindow ()
1572	return
1573
1574DragButton = None
1575def ButtonDragSourceHandler(btn):
1576	global DragButton
1577	DragButton = btn;
1578
1579def ButtonDragDestHandler(btn, pcID):
1580	global DragButton
1581
1582	# So far this is only used for portrait buttons
1583	if DragButton: # DragButton will be none for item drags instantiated by clicking (not sragging) an inventory item
1584		if DragButton.VarName == "portrait" and btn.VarName == "portrait":
1585			GemRB.GameSwapPCs (DragButton.Value, pcID)
1586
1587	else: # TODO: something like this: DragButton.VarName.startswith("slot")
1588		if btn.VarName == "portrait":
1589			InventoryCommon.OnDropItemToPC(pcID)
1590		# TODO: handle inventory/ground slots
1591
1592	DragButton = None
1593
1594def OpenPortraitWindow (needcontrols=0, pos=WINDOW_RIGHT|WINDOW_VCENTER):
1595	#take care, this window is different in how/iwd
1596	if GameCheck.HasHOW() and needcontrols:
1597		PortraitWindow = Window = GemRB.LoadWindow (26, GUICommon.GetWindowPack(), pos)
1598	else:
1599		PortraitWindow = Window = GemRB.LoadWindow (1, GUICommon.GetWindowPack(), pos)
1600
1601	PortraitWindow.AddAlias("PORTWIN")
1602	PortraitWindow.SetFlags(WF_BORDERLESS|IE_GUI_VIEW_IGNORE_EVENTS, OP_OR)
1603
1604	if needcontrols and not GameCheck.IsPST(): #not in pst
1605		# 1280 and higher don't have this control
1606		Button = Window.GetControl (8)
1607		if Button:
1608			if GameCheck.IsIWD():
1609				# Rest (iwd)
1610				Button.SetTooltip (11942)
1611				Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RestPress)
1612			else:
1613				Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MinimizePortraits)
1614		else:
1615			if GameCheck.HasHOW():
1616				# Rest (how)
1617				pos = Window.GetFrame()["h"] - 37
1618				Button = Window.CreateButton (8, 6, pos, 55, 37)
1619				Button.SetSprites ("GUIRSBUT", 0,0,1,0,0)
1620				Button.SetTooltip (11942)
1621				Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RestPress)
1622
1623				pos = pos - 37
1624				Window.CreateButton (6, 6, pos, 27, 36)
1625
1626		# AI
1627		Button = Window.GetControl (6)
1628		#fixing a gui bug, and while we are at it, hacking it to be easier
1629		Button.SetSprites ("GUIBTACT", 0, 46, 47, 48, 49)
1630		InitOptionButton(Window, 'Toggle_AI', AIPress)
1631		AIPress(0) #this initialises the state and tooltip
1632
1633		#Select All
1634		if GameCheck.HasHOW():
1635			Button = Window.CreateButton (7, 33, pos, 27, 36)
1636			Button.SetSprites ("GUIBTACT", 0, 50, 51, 50, 51)
1637		else:
1638			Button = Window.GetControl (7)
1639		Button.SetTooltip (10485)
1640		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectAllOnPress)
1641	else:
1642		# Rest
1643		if not GameCheck.IsIWD2():
1644			Button = Window.GetControl (6)
1645			if Button:
1646				Button.SetTooltip (11942)
1647				Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RestPress)
1648
1649	PortraitButtons = GetPortraitButtonPairs (Window)
1650	for i, Button in PortraitButtons.items():
1651		pcID = i + 1
1652
1653		Button.SetVarAssoc("portrait", pcID)
1654
1655		if not GameCheck.IsPST():
1656			fontref = "STATES2"
1657			if GameCheck.IsIWD1() or GameCheck.IsIWD2():
1658				fontref = "STATES"
1659
1660			Button.SetFont (fontref)
1661			# label for status flags (dialog, store, level up)
1662			align = IE_FONT_ALIGN_TOP | IE_FONT_ALIGN_CENTER | IE_FONT_SINGLE_LINE
1663			label = Button.CreateLabel(200 + i, fontref, "", align) #level up icon is on the right
1664			label.SetFrame(Button.GetInsetFrame(4))
1665
1666		if needcontrols or GameCheck.IsIWD2():
1667			Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenInventoryWindowClick)
1668		else:
1669			Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, PortraitButtonOnPress)
1670
1671		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonOnPress)
1672		Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, PortraitButtonOnShiftPress)
1673		Button.SetAction(ButtonDragSourceHandler, IE_ACT_DRAG_DROP_SRC)
1674		Button.SetAction(ButtonDragDestHandler, IE_ACT_DRAG_DROP_DST)
1675
1676		if GameCheck.IsIWD1() or GameCheck.IsIWD2():
1677			# overlay a label, so we can display the hp with the correct font. Regular button label
1678			#   is used by effect icons
1679			Button.CreateLabel(100+i, "NUMFONT", "", IE_FONT_ALIGN_TOP|IE_FONT_ALIGN_LEFT|IE_FONT_SINGLE_LINE)
1680
1681		# unlike other buttons, this one lacks extra frames for a selection effect
1682		# so we create it and shift it to cover the grooves of the image
1683		# except iwd2's second frame already has it incorporated (but we miscolor it)
1684		yellow = {'r' : 255, 'g' : 255, 'b' : 0, 'a' : 255}
1685		green = {'r' : 0, 'g' : 255, 'b' : 0, 'a' : 255}
1686		if GameCheck.IsIWD2():
1687			Button.SetBorder (FRAME_PC_SELECTED, green)
1688			Button.SetBorder (FRAME_PC_TARGET, yellow, 0, 0, Button.GetInsetFrame(2,2,3,3))
1689		elif GameCheck.IsPST():
1690			Button.SetBorder (FRAME_PC_SELECTED, green, 0, 0, Button.GetInsetFrame(1,1,2,2))
1691			Button.SetBorder (FRAME_PC_TARGET, yellow, 0, 0, Button.GetInsetFrame(3,3,4,4))
1692			Button.SetBAM ("PPPANN", 0, 0, -1) # NOTE: just a dummy, won't be visible
1693			ButtonHP = Window.GetControl (6 + i)
1694			ButtonHP.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonHPOnPress)
1695			ButtonHP.SetVarAssoc ("pid", i + 1) # storing so the callback knows which button it's operating on
1696		else:
1697			Button.SetBorder (FRAME_PC_SELECTED, green, 0, 0, Button.GetInsetFrame(4,3,4,3))
1698			Button.SetBorder (FRAME_PC_TARGET, yellow, 0, 0, Button.GetInsetFrame(2,2,3,3))
1699
1700	UpdatePortraitWindow ()
1701	SelectionChanged ()
1702	return Window
1703
1704def UpdatePortraitWindow ():
1705	"""Updates all of the portraits."""
1706
1707	Window = GemRB.GetView("PORTWIN")
1708	Window.Focus(None)
1709
1710	pc = GemRB.GameGetSelectedPCSingle ()
1711	Inventory = GemRB.GetVar ("Inventory")
1712	GSFlags = GemRB.GetGUIFlags()
1713	indialog = GSFlags & GS_DIALOG
1714
1715	PortraitButtons = GetPortraitButtonPairs (Window)
1716	for i, Button in PortraitButtons.items():
1717		pcID = i + 1
1718		if indialog:
1719			Button.SetHotKey(None)
1720		if (pcID <= GemRB.GetPartySize()):
1721			Button.SetAction(lambda btn, val, pc=pcID: GemRB.GameControlLocateActor(pc), IE_ACT_MOUSE_ENTER);
1722			Button.SetAction(lambda: GemRB.GameControlLocateActor(-1), IE_ACT_MOUSE_LEAVE);
1723			if (i < 6 and not indialog):
1724				Button.SetHotKey(chr(ord('1') + i), 0, True)
1725
1726		if GameCheck.IsPST():
1727			UpdateAnimatedPortrait(Window, i)
1728			continue
1729
1730		Portrait = GemRB.GetPlayerPortrait (pcID, 1)
1731		pic = Portrait["Sprite"]
1732		Hide = False
1733		if Inventory and pc != pcID:
1734			Hide = True
1735
1736		if pic and GemRB.GetPlayerStat(pcID, IE_STATE_ID) & STATE_DEAD:
1737			import GUISTORE
1738			# dead pcs are hidden in all stores but temples
1739			if GUISTORE.StoreWindow and not GUISTORE.StoreHealWindow:
1740				Hide = True
1741
1742		if Hide or (not pic and not Portrait["ResRef"]):
1743			Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
1744			Button.SetState (IE_GUI_BUTTON_DISABLED)
1745			Button.SetText ("")
1746			Button.SetTooltip ("")
1747			continue
1748
1749		portraitFlags = IE_GUI_BUTTON_PORTRAIT | IE_GUI_BUTTON_HORIZONTAL | IE_GUI_BUTTON_ALIGN_LEFT | IE_GUI_BUTTON_ALIGN_BOTTOM
1750
1751		if GameCheck.IsIWD2():
1752			Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenInventoryWindowClick)
1753			Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonOnPress)
1754
1755		Button.SetFlags (portraitFlags, OP_SET)
1756
1757		Button.SetState (IE_GUI_BUTTON_LOCKED)
1758		Button.SetPicture (pic, "NOPORTSM")
1759		ratio_str, color = GUICommon.SetupDamageInfo (pcID, Button, Window)
1760
1761		# character - 1 == bam cycle
1762		talk = store = flag = blank = ' '
1763		if GameCheck.IsBG2():
1764			# as far as I can tell only BG2 has icons for talk or store
1765			flag = blank = chr(238)
1766			talk = 154 # dialog icon
1767			store = 155 # shopping icon
1768
1769			if pc == pcID and GemRB.GetStore() != None:
1770				flag = chr(store)
1771
1772			# talk icon
1773			if GemRB.GameGetSelectedPCSingle(1) == pcID:
1774				flag = chr(talk)
1775
1776		if LUCommon.CanLevelUp (pcID):
1777			flag = flag + blank + chr(255)
1778		elif GameCheck.IsIWD1() or GameCheck.IsIWD2():
1779			HPLabel = Window.GetControl (100+i)
1780			HPLabel.SetText (ratio_str)
1781			HPLabel.SetTextColor (color)
1782
1783		#add effects on the portrait
1784		effects = GemRB.GetPlayerStates (pcID)
1785
1786		numCols = 4 if GameCheck.IsIWD2() else 3
1787		numEffects = len(effects)
1788
1789		states = b""
1790		# calculate the partial row
1791		idx = numEffects % numCols
1792		states = effects[0:idx]
1793		# now do any rows that are full
1794		for x in range(idx, numEffects):
1795			if (x - idx) % numCols == 0:
1796				states = states + b"\n"
1797			states = states + bytes(effects[x])
1798
1799		# FIXME: hack, check shouldn't be needed
1800		FlagLabel = Window.GetControl (200 + i)
1801		if FlagLabel:
1802			if flag != blank:
1803				FlagLabel.SetText (flag.ljust(3, blank))
1804			else:
1805				FlagLabel.SetText ("")
1806		Button.SetText(states)
1807	return
1808
1809def UpdateAnimatedPortrait (Window,i):
1810	"""Selects the correct portrait cycle depending on character state"""
1811	# note: there are actually two portraits per chr, eg PPPANN (static), WMPANN (animated)
1812	Button = Window.GetControl (i)
1813	ButtonHP = Window.GetControl (6 + i)
1814	pic = GemRB.GetPlayerPortrait (i+1, 0)["ResRef"]
1815	if not pic:
1816		Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
1817		Button.SetAnimation ("")
1818		ButtonHP.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
1819		ButtonHP.SetText ("")
1820		ButtonHP.SetBAM ("", 0, 0)
1821		return
1822
1823	state = GemRB.GetPlayerStat (i+1, IE_STATE_ID)
1824	hp = GemRB.GetPlayerStat (i+1, IE_HITPOINTS)
1825	hp_max = GemRB.GetPlayerStat (i+1, IE_MAXHITPOINTS)
1826	if state & STATE_DEAD:
1827			cycle = 9
1828	elif state & STATE_HELPLESS:
1829			cycle = 8
1830	elif state & STATE_PETRIFIED:
1831			cycle = 7
1832	elif state & STATE_PANIC:
1833			cycle = 6
1834	elif state & STATE_POISONED:
1835			cycle = 2
1836	elif hp<hp_max/2:
1837		cycle = 4
1838	else:
1839		cycle = 0
1840
1841	Button.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_ANIMATED, OP_SET)
1842	if cycle<6:
1843		Button.SetFlags (IE_GUI_BUTTON_PLAYRANDOM, OP_OR)
1844
1845	Button.SetAnimation (pic, cycle)
1846	ButtonHP.SetFlags(IE_GUI_BUTTON_PICTURE, OP_SET)
1847
1848	if hp_max < 1 or hp == "?":
1849		ratio = 0.0
1850	else:
1851		ratio = hp / float(hp_max)
1852		if ratio > 1.0: ratio = 1.0
1853
1854	r = int (255 * (1.0 - ratio))
1855	g = int (255 * ratio)
1856
1857	ButtonHP.SetText ("%s / %d" %(hp, hp_max))
1858	ButtonHP.SetTextColor ({'r' : r, 'g' : g, 'b' : 0})
1859	ButtonHP.SetBAM ('FILLBAR', 0, 0, -1)
1860	ButtonHP.SetPictureClipping (ratio)
1861
1862	if GemRB.GetVar('Health Bar Settings') & (1 << i):
1863		op = OP_OR
1864	else:
1865		op = OP_NAND
1866	ButtonHP.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_TEXT, op)
1867
1868	return
1869
1870def PortraitButtonOnPress (btn, pcID):
1871	"""Selects the portrait individually."""
1872
1873	if GemRB.GameControlGetTargetMode() != TARGET_MODE_NONE:
1874		GemRB.ActOnPC (pcID)
1875		return
1876
1877	if (not SelectionChangeHandler):
1878		if GemRB.GameIsPCSelected (pcID):
1879			GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR, OP_OR)
1880		GemRB.GameSelectPC (pcID, True, SELECT_REPLACE)
1881	else:
1882		GemRB.GameSelectPCSingle (pcID)
1883		SelectionChanged ()
1884	return
1885
1886def PortraitButtonOnShiftPress (btn, pcID):
1887	"""Handles selecting multiple portaits with shift."""
1888
1889	if (not SelectionChangeHandler):
1890		sel = GemRB.GameIsPCSelected (pcID)
1891		sel = not sel
1892		GemRB.GameSelectPC (pcID, sel)
1893	else:
1894		GemRB.GameSelectPCSingle (pcID)
1895		SelectionChanged ()
1896	return
1897
1898def PortraitButtonHPOnPress (btn, pcID): ##pst hitpoint display
1899	hbs = GemRB.GetVar('Health Bar Settings')
1900	if hbs & (1 << (pcID - 1)):
1901		op = OP_NAND
1902	else:
1903		op = OP_OR
1904
1905	btn.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_TEXT, op)
1906	GemRB.SetVar('Health Bar Settings', hbs ^ (1 << (pcID - 1)))
1907	return
1908
1909def SelectionChanged ():
1910	"""Ran by the Game class when a PC selection is changed."""
1911
1912	PortraitWindow = GemRB.GetView("PORTWIN")
1913	# FIXME: hack. If defined, display single selection
1914	GemRB.SetVar ("ActionLevel", UAW_STANDARD)
1915	if (not SelectionChangeHandler):
1916		UpdateActionsWindow ()
1917		PortraitButtons = GetPortraitButtonPairs (PortraitWindow)
1918		for i, Button in PortraitButtons.items():
1919			Button.EnableBorder (FRAME_PC_SELECTED, GemRB.GameIsPCSelected (i + 1))
1920		if SelectionChangeMultiHandler:
1921			SelectionChangeMultiHandler ()
1922	else:
1923		sel = GemRB.GameGetSelectedPCSingle ()
1924
1925		#update mage school
1926		GemRB.SetVar ("MAGESCHOOL", 0)
1927		Kit = GUICommon.GetKitIndex (sel)
1928		if Kit and CommonTables.KitList.GetValue (Kit, 7) == 1:
1929			MageTable = GemRB.LoadTable ("magesch")
1930			GemRB.SetVar ("MAGESCHOOL", MageTable.FindValue (3, CommonTables.KitList.GetValue (Kit, 6) ) )
1931
1932		PortraitButtons = GetPortraitButtonPairs (PortraitWindow)
1933		for i, Button in PortraitButtons.items():
1934			Button.EnableBorder (FRAME_PC_SELECTED, i + 1 == sel)
1935
1936	CommonWindow.CloseContainerWindow()
1937	if SelectionChangeHandler:
1938		SelectionChangeHandler ()
1939	return
1940
1941def ActionStopPressed ():
1942	for i in GemRB.GetSelectedActors():
1943		GemRB.ClearActions (i)
1944	return
1945
1946def ActionTalkPressed ():
1947	GemRB.GameControlSetTargetMode (TARGET_MODE_TALK,GA_NO_DEAD|GA_NO_ENEMY|GA_NO_HIDDEN)
1948
1949def ActionAttackPressed ():
1950	GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK,GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
1951
1952def ActionDefendPressed ():
1953	GemRB.GameControlSetTargetMode (TARGET_MODE_DEFEND,GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
1954
1955def ActionThievingPressed ():
1956	GemRB.GameControlSetTargetMode (TARGET_MODE_PICK, GA_NO_DEAD|GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
1957
1958def MinimizePortraits(): #bg2
1959	GemRB.GameSetScreenFlags(GS_PORTRAITPANE, OP_OR)
1960
1961def SetItemButton (Window, Button, Slot, PressHandler, RightPressHandler): #relates to pst containers
1962	if Slot != None:
1963		Item = GemRB.GetItem (Slot['ItemResRef'])
1964		identified = Slot['Flags'] & IE_INV_ITEM_IDENTIFIED
1965		#Button.SetVarAssoc ("LeftIndex", LeftTopIndex+i)
1966		#Button.SetSprites ('IVSLOT', 0,  0, 0, 0, 0)
1967		Button.SetItemIcon (Slot['ItemResRef'],0)
1968
1969		if Item['MaxStackAmount'] > 1:
1970			Button.SetText (str (Slot['Usages0']))
1971		else:
1972			Button.SetText ('')
1973
1974
1975		if not identified or Item['ItemNameIdentified'] == -1:
1976			Button.SetTooltip (Item['ItemName'])
1977		else:
1978			Button.SetTooltip (Item['ItemNameIdentified'])
1979
1980		#Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
1981		#Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
1982
1983		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PressHandler)
1984		Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, RightPressHandler)
1985		#Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, ShiftPressHandler)
1986		#Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, DragDropHandler)
1987
1988	else:
1989		#Button.SetVarAssoc ("LeftIndex", -1)
1990		Button.SetItemIcon ('')
1991		Button.SetTooltip (4273)  # Ground Item
1992		Button.SetText ('')
1993		Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
1994
1995		Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
1996		Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
1997		#Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
1998		#Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, None)
1999
2000def OpenWaitForDiscWindow ():
2001	global DiscWindow
2002
2003	if DiscWindow:
2004		DiscWindow.Unload ()
2005		DiscWindow = None
2006		return
2007
2008	DiscWindow = GemRB.LoadWindow (0, "GUIID")
2009	label = DiscWindow.GetControl (0)
2010
2011	disc_num = GemRB.GetVar ("WaitForDisc")
2012	disc_path = 'XX:'
2013
2014	text = GemRB.GetString (31483) + " " + str (disc_num) + " " + GemRB.GetString (31569) + " " + disc_path + "\n" + GemRB.GetString (49152)
2015	label.SetText (text)
2016	# 31483 - Please place PS:T disc number
2017	# 31568 - Please place the PS:T DVD
2018	# 31569 - in drive
2019	# 31570 - Wrong disc in drive
2020	# 31571 - There is no disc in drive
2021	# 31578 - No disc could be found in drive. Please place Disc 1 in drive.
2022	# 49152 - To quit the game, press Alt-F4
2023
2024
2025def CheckLevelUp(pc):
2026	GemRB.SetVar ("CheckLevelUp"+str(pc), LUCommon.CanLevelUp (pc))
2027
2028def ToggleAlwaysRun():
2029	GemRB.GameControlToggleAlwaysRun()
2030
2031def RestPress ():
2032	CloseTopWindow ()
2033	# only rest if the dream scripts haven't already
2034	# bg2 completely offloaded resting to them - if there's a talk, it has to call Rest(Party) itself
2035	if not GemRB.RunRestScripts ():
2036		# ensure the scripts run before the actual rest
2037		GemRB.SetTimedEvent (RealRestPress, 2)
2038
2039def RealRestPress ():
2040	# only bg2 has area-based rest movies
2041	# outside movies start at 2, 1 is for inns
2042	# 15 means run all checks to see if resting is possible
2043	info = GemRB.RestParty(15, 0 if GameCheck.IsBG2() else 2, 1)
2044	if info["Error"]:
2045		if GameCheck.IsPST ():
2046			# open error window
2047			Window = GemRB.LoadWindow (25, GUICommon.GetWindowPack (), WINDOW_BOTTOM|WINDOW_HCENTER)
2048			Label = Window.GetControl (0xfffffff) # -1 in the CHU
2049			Label.SetText (info["ErrorMsg"])
2050			Button = Window.GetControl (1)
2051			Button.SetText (1403)
2052			Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, lambda: Window.Close ())
2053			Window.ShowModal (MODAL_SHADOW_GRAY)
2054		else:
2055			GemRB.DisplayString (info["ErrorMsg"], ColorRed)
2056
2057	return
2058
2059# special pst death screen for the finale
2060def OpenPSTDeathWindow ():
2061	if not GameCheck.IsPST ():
2062		return
2063
2064	def ShowCredits():
2065		GemRB.ExecuteString ("EndCredits()")
2066		# will also exit to the main menu
2067
2068	# reuse the main error window
2069	GemRB.LoadWindowPack (GUICommon.GetWindowPack())
2070	Window = GemRB.LoadWindow (25)
2071	Label = Window.GetControl (0xfffffff) # -1 in the CHU
2072	Label.SetText (48155)
2073	Button = Window.GetControl (1)
2074	Button.SetText (1403)
2075	Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ShowCredits)
2076	Window.ShowModal (MODAL_SHADOW_GRAY)
2077