1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003-2007 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 #include "GameScript/GameScript.h"
22 
23 #include "GameScript/GSUtils.h"
24 #include "GameScript/Matching.h"
25 
26 #include "voodooconst.h"
27 
28 #include "AmbientMgr.h"
29 #include "CharAnimations.h"
30 #include "DataFileMgr.h"
31 #include "DialogHandler.h"
32 #include "DisplayMessage.h"
33 #include "Game.h"
34 #include "GameData.h"
35 #include "GlobalTimer.h"
36 #include "IniSpawn.h"
37 #include "Item.h"
38 #include "Map.h"
39 #include "MusicMgr.h"
40 #include "SaveGameIterator.h"
41 #include "ScriptEngine.h"
42 #include "TileMap.h"
43 #include "Video.h"
44 #include "WorldMap.h"
45 #include "GUI/GameControl.h"
46 #include "GUI/EventMgr.h"
47 #include "RNG.h"
48 #include "Scriptable/Container.h"
49 #include "Scriptable/Door.h"
50 #include "Scriptable/InfoPoint.h"
51 #include "ScriptedAnimation.h"
52 
53 namespace GemRB {
54 
55 //------------------------------------------------------------
56 // Action Functions
57 //-------------------------------------------------------------
58 
SetExtendedNight(Scriptable * Sender,Action * parameters)59 void GameScript::SetExtendedNight(Scriptable* Sender, Action* parameters)
60 {
61 	Map *map=Sender->GetCurrentArea();
62 	//sets the 'can rest other' bit
63 	if (parameters->int0Parameter) {
64 		map->AreaType|=AT_EXTENDED_NIGHT;
65 	} else {
66 		map->AreaType&=~AT_EXTENDED_NIGHT;
67 	}
68 }
69 
SetAreaRestFlag(Scriptable * Sender,Action * parameters)70 void GameScript::SetAreaRestFlag(Scriptable* Sender, Action* parameters)
71 {
72 	Map *map=Sender->GetCurrentArea();
73 	//sets the 'can rest other' bit
74 	if (parameters->int0Parameter) {
75 		map->AreaType |= AT_CAN_REST_INDOORS;
76 	} else {
77 		map->AreaType &= ~AT_CAN_REST_INDOORS;
78 	}
79 }
80 
AddAreaFlag(Scriptable * Sender,Action * parameters)81 void GameScript::AddAreaFlag(Scriptable* Sender, Action* parameters)
82 {
83 	Map *map=Sender->GetCurrentArea();
84 	map->AreaFlags|=parameters->int0Parameter;
85 }
86 
RemoveAreaFlag(Scriptable * Sender,Action * parameters)87 void GameScript::RemoveAreaFlag(Scriptable* Sender, Action* parameters)
88 {
89 	Map *map=Sender->GetCurrentArea();
90 	map->AreaFlags&=~parameters->int0Parameter;
91 }
92 
SetAreaFlags(Scriptable * Sender,Action * parameters)93 void GameScript::SetAreaFlags(Scriptable* Sender, Action* parameters)
94 {
95 	Map *map=Sender->GetCurrentArea();
96 	ieDword value = map->AreaFlags;
97 	HandleBitMod( value, parameters->int0Parameter, parameters->int1Parameter);
98 	map->AreaFlags=value;
99 }
100 
AddAreaType(Scriptable * Sender,Action * parameters)101 void GameScript::AddAreaType(Scriptable* Sender, Action* parameters)
102 {
103 	Map *map=Sender->GetCurrentArea();
104 	map->AreaType|=parameters->int0Parameter;
105 }
106 
RemoveAreaType(Scriptable * Sender,Action * parameters)107 void GameScript::RemoveAreaType(Scriptable* Sender, Action* parameters)
108 {
109 	Map *map=Sender->GetCurrentArea();
110 	map->AreaType&=~parameters->int0Parameter;
111 }
112 
NoActionAtAll(Scriptable *,Action *)113 void GameScript::NoActionAtAll(Scriptable* /*Sender*/, Action* /*parameters*/)
114 {
115 	//thats all :)
116 }
117 
118 // this action stops modal actions, so...
NoAction(Scriptable * Sender,Action *)119 void GameScript::NoAction(Scriptable* Sender, Action* /*parameters*/)
120 {
121 	if (Sender->Type!=ST_ACTOR) {
122 		return;
123 	}
124 	Actor *actor = (Actor *) Sender;
125 	actor->SetModal( MS_NONE);
126 }
127 
SG(Scriptable * Sender,Action * parameters)128 void GameScript::SG(Scriptable* Sender, Action* parameters)
129 {
130 	SetVariable( Sender, parameters->string0Parameter, "GLOBAL", parameters->int0Parameter );
131 }
132 
SetGlobal(Scriptable * Sender,Action * parameters)133 void GameScript::SetGlobal(Scriptable* Sender, Action* parameters)
134 {
135 	SetVariable( Sender, parameters->string0Parameter, parameters->int0Parameter );
136 }
137 
SetGlobalRandom(Scriptable * Sender,Action * parameters)138 void GameScript::SetGlobalRandom(Scriptable* Sender, Action* parameters)
139 {
140 	int max=parameters->int1Parameter-parameters->int0Parameter+1;
141 	if (max>0) {
142 		SetVariable( Sender, parameters->string0Parameter, RandomNumValue%max+parameters->int0Parameter );
143 	} else {
144 		SetVariable( Sender, parameters->string0Parameter, 0);
145 	}
146 }
147 
StartTimer(Scriptable * Sender,Action * parameters)148 void GameScript::StartTimer(Scriptable* Sender, Action* parameters)
149 {
150 	Sender->StartTimer(parameters->int0Parameter, parameters->int1Parameter);
151 }
152 
StartRandomTimer(Scriptable * Sender,Action * parameters)153 void GameScript::StartRandomTimer(Scriptable* Sender, Action* parameters)
154 {
155 	ieDword value = core->Roll(1, parameters->int2Parameter-parameters->int1Parameter, parameters->int2Parameter-1);
156 	Sender->StartTimer(parameters->int0Parameter, value);
157 }
158 
SetGlobalTimer(Scriptable * Sender,Action * parameters)159 void GameScript::SetGlobalTimer(Scriptable* Sender, Action* parameters)
160 {
161 	ieDword mytime;
162 
163 	mytime=core->GetGame()->GameTime; //gametime (should increase it)
164 	SetVariable( Sender, parameters->string0Parameter,
165 		parameters->int0Parameter*AI_UPDATE_TIME + mytime);
166 }
167 
SetGlobalTimerRandom(Scriptable * Sender,Action * parameters)168 void GameScript::SetGlobalTimerRandom(Scriptable* Sender, Action* parameters)
169 {
170 	ieDword mytime;
171 	int random;
172 
173 	//This works both ways in the original engine
174 	if (parameters->int1Parameter>parameters->int0Parameter) {
175 		random = parameters->int1Parameter-parameters->int0Parameter+1;
176 		//random cannot be 0, its minimal value is 1
177 		random = RandomNumValue % random + parameters->int0Parameter;
178 	} else {
179 		random = parameters->int0Parameter-parameters->int1Parameter+1;
180 		random = RandomNumValue % random + parameters->int1Parameter;
181 	}
182 	mytime=core->GetGame()->GameTime; //gametime (should increase it)
183 	SetVariable( Sender, parameters->string0Parameter, random*AI_UPDATE_TIME + mytime);
184 }
185 
SetGlobalTimerOnce(Scriptable * Sender,Action * parameters)186 void GameScript::SetGlobalTimerOnce(Scriptable* Sender, Action* parameters)
187 {
188 	ieDword mytime = CheckVariable( Sender, parameters->string0Parameter );
189 	if (mytime != 0) {
190 		return;
191 	}
192 	mytime=core->GetGame()->GameTime; //gametime (should increase it)
193 	SetVariable( Sender, parameters->string0Parameter,
194 		parameters->int0Parameter*AI_UPDATE_TIME + mytime);
195 }
196 
RealSetGlobalTimer(Scriptable * Sender,Action * parameters)197 void GameScript::RealSetGlobalTimer(Scriptable* Sender, Action* parameters)
198 {
199 	ieDword mytime=core->GetGame()->RealTime;
200 
201 	SetVariable( Sender, parameters->string0Parameter,
202 		parameters->int0Parameter*AI_UPDATE_TIME + mytime);
203 }
204 
ChangeAllegiance(Scriptable * Sender,Action * parameters)205 void GameScript::ChangeAllegiance(Scriptable* Sender, Action* parameters)
206 {
207 	Scriptable *scr = Sender;
208 	if (parameters->objects[1]) {
209 		scr=GetActorFromObject( Sender, parameters->objects[1] );
210 	}
211 	if (!scr || scr->Type != ST_ACTOR) {
212 		return;
213 	}
214 	Actor* actor = ( Actor* ) scr;
215 	actor->SetBase( IE_EA, parameters->int0Parameter );
216 }
217 
ChangeGeneral(Scriptable * Sender,Action * parameters)218 void GameScript::ChangeGeneral(Scriptable* Sender, Action* parameters)
219 {
220 	Scriptable *scr = Sender;
221 	if (parameters->objects[1]) {
222 		scr=GetActorFromObject( Sender, parameters->objects[1] );
223 	}
224 	if (!scr || scr->Type != ST_ACTOR) {
225 		return;
226 	}
227 	Actor* actor = ( Actor* ) scr;
228 	actor->SetBase( IE_GENERAL, parameters->int0Parameter );
229 }
230 
ChangeRace(Scriptable * Sender,Action * parameters)231 void GameScript::ChangeRace(Scriptable* Sender, Action* parameters)
232 {
233 	Scriptable *scr = Sender;
234 	if (parameters->objects[1]) {
235 		scr=GetActorFromObject( Sender, parameters->objects[1] );
236 	}
237 	if (!scr || scr->Type != ST_ACTOR) {
238 		return;
239 	}
240 	Actor* actor = ( Actor* ) scr;
241 	actor->SetBase( IE_RACE, parameters->int0Parameter );
242 }
243 
ChangeClass(Scriptable * Sender,Action * parameters)244 void GameScript::ChangeClass(Scriptable* Sender, Action* parameters)
245 {
246 	Scriptable *scr = Sender;
247 	if (parameters->objects[1]) {
248 		scr=GetActorFromObject( Sender, parameters->objects[1] );
249 	}
250 	if (!scr || scr->Type != ST_ACTOR) {
251 		return;
252 	}
253 	Actor* actor = ( Actor* ) scr;
254 	actor->SetBase( IE_CLASS, parameters->int0Parameter );
255 }
256 
SetNamelessClass(Scriptable *,Action * parameters)257 void GameScript::SetNamelessClass(Scriptable* /*Sender*/, Action* parameters)
258 {
259 	//same as Protagonist
260 	Actor* actor = core->GetGame()->GetPC(0, false);
261 	actor->SetBase( IE_CLASS, parameters->int0Parameter );
262 }
263 
SetNamelessDisguise(Scriptable * Sender,Action * parameters)264 void GameScript::SetNamelessDisguise(Scriptable* Sender, Action* parameters)
265 {
266 	SetVariable(Sender, "APPEARANCE", "GLOBAL", parameters->int0Parameter);
267 	core->SetEventFlag(EF_UPDATEANIM);
268 }
269 
ChangeSpecifics(Scriptable * Sender,Action * parameters)270 void GameScript::ChangeSpecifics(Scriptable* Sender, Action* parameters)
271 {
272 	Scriptable *scr = Sender;
273 	if (parameters->objects[1]) {
274 		scr=GetActorFromObject( Sender, parameters->objects[1] );
275 	}
276 	if (!scr || scr->Type != ST_ACTOR) {
277 		return;
278 	}
279 	Actor* actor = ( Actor* ) scr;
280 	actor->SetBase( IE_SPECIFIC, parameters->int0Parameter );
281 }
282 
PermanentStatChange(Scriptable * Sender,Action * parameters)283 void GameScript::PermanentStatChange(Scriptable* Sender, Action* parameters)
284 {
285 	Scriptable *scr = Sender;
286 	if (parameters->objects[1]) {
287 		scr=GetActorFromObject( Sender, parameters->objects[1] );
288 	}
289 	if (!scr || scr->Type != ST_ACTOR) {
290 		return;
291 	}
292 	Actor* actor = ( Actor* ) scr;
293 	ieDword value;
294 	// int1Parameter is from delta.ids
295 	// int2Parameter is supposed to support also bones.ids, but nothing uses it like that
296 	// if we need it, take the implementation from GameScript::Damage
297 	switch (parameters->int1Parameter) {
298 		case DM_LOWER:
299 			value = actor->GetBase(parameters->int0Parameter);
300 			value-= parameters->int2Parameter;
301 			break;
302 		case DM_RAISE:
303 			value = actor->GetBase(parameters->int0Parameter);
304 			value+= parameters->int2Parameter;
305 			break;
306 		case DM_SET:
307 		default:
308 			value = parameters->int2Parameter;
309 			break;
310 	}
311 	actor->SetBase( parameters->int0Parameter, value);
312 }
313 
ChangeStat(Scriptable * Sender,Action * parameters)314 void GameScript::ChangeStat(Scriptable* Sender, Action* parameters)
315 {
316 	Scriptable *scr = Sender;
317 	if (parameters->objects[1]) {
318 		scr=GetActorFromObject( Sender, parameters->objects[1] );
319 	}
320 	if (!scr || scr->Type != ST_ACTOR) {
321 		return;
322 	}
323 	Actor* actor = ( Actor* ) scr;
324 	ieDword value = parameters->int1Parameter;
325 	if (parameters->int2Parameter==1) { // basically statmod.ids entries, but there's only two
326 		value+=actor->GetBase(parameters->int0Parameter);
327 	}
328 	actor->SetBase( parameters->int0Parameter, value);
329 }
330 
ChangeStatGlobal(Scriptable * Sender,Action * parameters)331 void GameScript::ChangeStatGlobal(Scriptable* Sender, Action* parameters)
332 {
333 	Scriptable *scr = Sender;
334 	if (parameters->objects[1]) {
335 		scr=GetActorFromObject( Sender, parameters->objects[1] );
336 	}
337 	if (!scr || scr->Type != ST_ACTOR) {
338 		return;
339 	}
340 	ieDword value = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter);
341 	Actor* actor = ( Actor* ) scr;
342 	if (parameters->int1Parameter==1) {
343 		value+=actor->GetBase(parameters->int0Parameter);
344 	}
345 	actor->SetBase( parameters->int0Parameter, value);
346 }
347 
ChangeGender(Scriptable * Sender,Action * parameters)348 void GameScript::ChangeGender(Scriptable* Sender, Action* parameters)
349 {
350 	Scriptable *scr = Sender;
351 	if (parameters->objects[1]) {
352 		scr=GetActorFromObject( Sender, parameters->objects[1] );
353 	}
354 	if (!scr || scr->Type != ST_ACTOR) {
355 		return;
356 	}
357 	Actor* actor = ( Actor* ) scr;
358 	actor->SetBase( IE_SEX, parameters->int0Parameter );
359 }
360 
ChangeAlignment(Scriptable * Sender,Action * parameters)361 void GameScript::ChangeAlignment(Scriptable* Sender, Action* parameters)
362 {
363 	Scriptable *scr = Sender;
364 	if (parameters->objects[1]) {
365 		scr=GetActorFromObject( Sender, parameters->objects[1] );
366 	}
367 	if (!scr || scr->Type != ST_ACTOR) {
368 		return;
369 	}
370 	Actor* actor = ( Actor* ) scr;
371 	actor->SetBase( IE_ALIGNMENT, parameters->int0Parameter );
372 }
373 
SetFaction(Scriptable * Sender,Action * parameters)374 void GameScript::SetFaction(Scriptable* Sender, Action* parameters)
375 {
376 	Scriptable *scr = Sender;
377 	if (parameters->objects[1]) {
378 		scr=GetActorFromObject( Sender, parameters->objects[1] );
379 	}
380 	if (!scr || scr->Type != ST_ACTOR) {
381 		return;
382 	}
383 	Actor* actor = ( Actor* ) scr;
384 	actor->SetBase( IE_FACTION, parameters->int0Parameter );
385 }
386 
SetHP(Scriptable * Sender,Action * parameters)387 void GameScript::SetHP(Scriptable* Sender, Action* parameters)
388 {
389 	Scriptable *scr = Sender;
390 	if (parameters->objects[1]) {
391 		scr=GetActorFromObject( Sender, parameters->objects[1] );
392 	}
393 	if (!scr || scr->Type != ST_ACTOR) {
394 		return;
395 	}
396 	Actor* actor = ( Actor* ) scr;
397 	actor->SetBase( IE_HITPOINTS, parameters->int0Parameter );
398 }
399 
SetHPPercent(Scriptable * Sender,Action * parameters)400 void GameScript::SetHPPercent(Scriptable* Sender, Action* parameters)
401 {
402 	Scriptable *scr = Sender;
403 	if (parameters->objects[1]) {
404 		scr=GetActorFromObject( Sender, parameters->objects[1] );
405 	}
406 	if (!scr || scr->Type != ST_ACTOR) {
407 		return;
408 	}
409 	Actor* actor = ( Actor* ) scr;
410 	// 0 - adjust to max hp, 1 - adjust to current
411 	if (parameters->int1Parameter) {
412 		actor->NewBase(IE_HITPOINTS, parameters->int0Parameter, MOD_PERCENT);
413 	} else {
414 		actor->NewBase(IE_HITPOINTS, actor->GetStat(IE_MAXHITPOINTS) * parameters->int0Parameter/100, MOD_ABSOLUTE);
415 	}
416 }
417 
AddHP(Scriptable * Sender,Action * parameters)418 void GameScript::AddHP(Scriptable* Sender, Action* parameters)
419 {
420 	Scriptable *scr = Sender;
421 	if (parameters->objects[1]) {
422 		scr=GetActorFromObject( Sender, parameters->objects[1] );
423 	}
424 	if (!scr || scr->Type != ST_ACTOR) {
425 		return;
426 	}
427 	Actor* actor = ( Actor* ) scr;
428 	actor->NewBase(IE_HITPOINTS, parameters->int0Parameter, MOD_ADDITIVE);
429 }
430 
431 //this works on an object (pst)
432 //but can also work on actor itself (gemrb)
SetTeam(Scriptable * Sender,Action * parameters)433 void GameScript::SetTeam(Scriptable* Sender, Action* parameters)
434 {
435 	Scriptable *scr = Sender;
436 	if (parameters->objects[1]) {
437 		scr=GetActorFromObject( Sender, parameters->objects[1] );
438 	}
439 	if (!scr || scr->Type != ST_ACTOR) {
440 		return;
441 	}
442 	Actor* actor = ( Actor* ) scr;
443 	actor->SetBase( IE_TEAM, parameters->int0Parameter );
444 }
445 
446 //this works on an object (gemrb)
447 //or on Myself if object isn't given (iwd2)
SetTeamBit(Scriptable * Sender,Action * parameters)448 void GameScript::SetTeamBit(Scriptable* Sender, Action* parameters)
449 {
450 	Scriptable *scr = Sender;
451 	if (parameters->objects[1]) {
452 		scr=GetActorFromObject( Sender, parameters->objects[1] );
453 	}
454 	if (!scr || scr->Type != ST_ACTOR) {
455 		return;
456 	}
457 	Actor* actor = ( Actor* ) scr;
458 	if (parameters->int1Parameter) {
459 		actor->SetBase( IE_TEAM, actor->GetStat(IE_TEAM) | parameters->int0Parameter );
460 	} else {
461 		actor->SetBase( IE_TEAM, actor->GetStat(IE_TEAM) & ~parameters->int0Parameter );
462 	}
463 }
464 
TriggerActivation(Scriptable * Sender,Action * parameters)465 void GameScript::TriggerActivation(Scriptable* Sender, Action* parameters)
466 {
467 	Scriptable* ip = Sender;
468 
469 	if (parameters->objects[1]) {
470 		ip = GetActorFromObject(Sender, parameters->objects[1]);
471 	}
472 	if (!ip || (ip->Type!=ST_TRIGGER && ip->Type!=ST_TRAVEL && ip->Type!=ST_PROXIMITY)) {
473 		Log(WARNING, "Actions", "Script error: No Trigger Named \"%s\"", parameters->objects[1]->objectName);
474 		return;
475 	}
476 	InfoPoint *trigger = (InfoPoint *) ip;
477 	if ( parameters->int0Parameter != 0 ) {
478 		trigger->Flags &= ~TRAP_DEACTIVATED;
479 		if (trigger->TrapResets()) {
480 			trigger->Trapped = 1;
481 			Sender->AddTrigger(TriggerEntry(trigger_reset, trigger->GetGlobalID()));
482 		}
483 	} else {
484 		trigger->Flags |= TRAP_DEACTIVATED;
485 	}
486 }
487 
FadeToColor(Scriptable * Sender,Action * parameters)488 void GameScript::FadeToColor(Scriptable* Sender, Action* parameters)
489 {
490 	core->timer.SetFadeToColor( parameters->pointParameter.x );
491 //	Sender->SetWait( parameters->pointParameter.x );
492 	Sender->ReleaseCurrentAction(); // todo, blocking?
493 }
494 
FadeFromColor(Scriptable * Sender,Action * parameters)495 void GameScript::FadeFromColor(Scriptable* Sender, Action* parameters)
496 {
497 	core->timer.SetFadeFromColor( parameters->pointParameter.x );
498 //	Sender->SetWait( parameters->pointParameter.x );
499 	Sender->ReleaseCurrentAction(); // todo, blocking?
500 }
501 
FadeToAndFromColor(Scriptable * Sender,Action * parameters)502 void GameScript::FadeToAndFromColor(Scriptable* Sender, Action* parameters)
503 {
504 	core->timer.SetFadeToColor( parameters->pointParameter.x );
505 	core->timer.SetFadeFromColor( parameters->pointParameter.x );
506 //	Sender->SetWait( parameters->pointParameter.x<<1 ); //multiply by 2
507 	Sender->ReleaseCurrentAction(); // todo, blocking?
508 }
509 
JumpToPoint(Scriptable * Sender,Action * parameters)510 void GameScript::JumpToPoint(Scriptable* Sender, Action* parameters)
511 {
512 	if (Sender->Type != ST_ACTOR) {
513 		return;
514 	}
515 	Actor* ab = ( Actor* ) Sender;
516 	ab->SetPosition( parameters->pointParameter, true );
517 }
518 
JumpToPointInstant(Scriptable * Sender,Action * parameters)519 void GameScript::JumpToPointInstant(Scriptable* Sender, Action* parameters)
520 {
521 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
522 	if (!tar || tar->Type != ST_ACTOR) {
523 		return;
524 	}
525 	Actor* ab = ( Actor* ) tar;
526 	ab->SetPosition( parameters->pointParameter, true );
527 }
528 
529 /** instant jump to location saved in stats */
530 /** default subject is the current actor */
JumpToSavedLocation(Scriptable * Sender,Action * parameters)531 void GameScript::JumpToSavedLocation(Scriptable* Sender, Action* parameters)
532 {
533 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
534 	if (!tar) {
535 		tar = Sender;
536 	}
537 	if (tar->Type != ST_ACTOR) {
538 		return;
539 	}
540 	Actor *actor = (Actor *) tar;
541 	Point p((short) actor->GetStat(IE_SAVEDXPOS), (short) actor->GetStat(IE_SAVEDYPOS) );
542 	actor->SetPosition(p, true );
543 	actor->SetOrientation( actor->GetStat(IE_SAVEDFACE), false );
544 }
545 
JumpToObject(Scriptable * Sender,Action * parameters)546 void GameScript::JumpToObject(Scriptable* Sender, Action* parameters)
547 {
548 	if (Sender->Type != ST_ACTOR) {
549 		return;
550 	}
551 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
552 
553 	if (!tar) {
554 		return;
555 	}
556 	const Map *map = tar->GetCurrentArea();
557 
558 	if (map) {
559 		if (parameters->string0Parameter[0]) {
560 			CreateVisualEffectCore(Sender, Sender->Pos, parameters->string0Parameter, 0);
561 		}
562 		Actor *actor = (Actor *) Sender;
563 		if (actor->Persistent() || !CreateMovementEffect(actor, map->GetScriptName(), tar->Pos, 0) ) {
564 			MoveBetweenAreasCore( actor, map->GetScriptName(), tar->Pos, -1, true);
565 		}
566 	}
567 }
568 
TeleportParty(Scriptable *,Action * parameters)569 void GameScript::TeleportParty(Scriptable* /*Sender*/, Action* parameters)
570 {
571 	const Game *game = core->GetGame();
572 	int i = game->GetPartySize(false);
573 	while (i--) {
574 		Actor *tar = game->GetPC(i, false);
575 		MoveBetweenAreasCore( tar, parameters->string0Parameter, parameters->pointParameter, -1, true);
576 	}
577 
578 	//move familiars with the party
579 	i = game->GetNPCCount();
580 	while (i--) {
581 		Actor *tar = game->GetNPC(i);
582 		if (tar->GetBase(IE_EA)==EA_FAMILIAR)
583 			MoveBetweenAreasCore( tar, parameters->string0Parameter, parameters->pointParameter, -1, true);
584 	}
585 }
586 
587 //5 is the ToB value, but it might be useful to have multiple expansions
MoveToExpansion(Scriptable * Sender,Action * parameters)588 void GameScript::MoveToExpansion(Scriptable* Sender, Action* parameters)
589 {
590 	Game *game = core->GetGame();
591 
592 	game->SetExpansion(parameters->int0Parameter);
593 	Sender->ReleaseCurrentAction();
594 }
595 
596 //add some animation effects too?
ExitPocketPlane(Scriptable *,Action *)597 void GameScript::ExitPocketPlane(Scriptable* /*Sender*/, Action* /*parameters*/)
598 {
599 	int i, cnt;
600 	Point pos;
601 	ieResRef area;
602 
603 	Game *game = core->GetGame();
604 	cnt = game->GetPartySize(false);
605 	for (i = 0; i < cnt; i++) {
606 		Actor* act = game->GetPC( i, false );
607 		if (act) {
608 			GAMLocationEntry *gle;
609 			if (game->GetPlaneLocationCount() <= (unsigned int)i) {
610 				// no location, meaning the actor joined the party after the save
611 				// reuse the last valid location
612 				gle = game->GetPlaneLocationEntry(game->GetPlaneLocationCount()-1);
613 			} else {
614 				gle = game->GetPlaneLocationEntry(i);
615 			}
616 
617 			// save player1 location for familiar movement
618 			if (!i) {
619 				pos = gle->Pos;
620 				memcpy(area, gle->AreaResRef, sizeof(area) );
621 			}
622 			MoveBetweenAreasCore(act, gle->AreaResRef, gle->Pos, -1, true);
623 		}
624 	}
625 
626 	// move familiars
627 	cnt = game->GetNPCCount();
628 	for (i = 0; i < cnt; i++) {
629 		Actor* act = game->GetNPC( i );
630 		if (act->GetBase(IE_EA)==EA_FAMILIAR) {
631 			MoveBetweenAreasCore(act, area, pos, -1, true);
632 		}
633 	}
634 	// don't clear locations!
635 }
636 
637 //moves pcs and npcs from an area to another area
MoveGlobalsTo(Scriptable *,Action * parameters)638 void GameScript::MoveGlobalsTo(Scriptable* /*Sender*/, Action* parameters)
639 {
640 	const Game *game = core->GetGame();
641 	int i = game->GetPartySize(false);
642 	while (i--) {
643 		Actor *tar = game->GetPC(i, false);
644 		//if the actor isn't in the source area, we don't care
645 		if (strnicmp(tar->Area, parameters->string0Parameter,8) ) {
646 			continue;
647 		}
648 		// no need of CreateMovementEffect, party members are always moved immediately
649 		MoveBetweenAreasCore( tar, parameters->string1Parameter,
650 			parameters->pointParameter, -1, true);
651 	}
652 	i = game->GetNPCCount();
653 	while (i--) {
654 		Actor *tar = game->GetNPC(i);
655 		//if the actor isn't in the source area, we don't care
656 		if (strnicmp(tar->Area, parameters->string0Parameter, 8) ) {
657 			continue;
658 		}
659 		//if the actor is currently in a loaded area, remove it from there
660 		Map *map = tar->GetCurrentArea();
661 		if (map) {
662 			map->RemoveActor(tar);
663 		}
664 		//update the target's area to the destination
665 		strnuprcpy(tar->Area, parameters->string1Parameter, 8);
666 		//if the destination area is currently loaded, move the actor there now
667 		if (game->FindMap(tar->Area) ) {
668 			MoveBetweenAreasCore( tar, parameters->string1Parameter, parameters->pointParameter, -1, true);
669 		}
670 	}
671 }
672 
MoveGlobal(Scriptable * Sender,Action * parameters)673 void GameScript::MoveGlobal(Scriptable* Sender, Action* parameters)
674 {
675 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
676 	if (!tar || tar->Type != ST_ACTOR) {
677 		return;
678 	}
679 
680 	Actor *actor = (Actor *) tar;
681 	//FIXME:CreateMovement UnMakes globals, probably this isn't what we want!!!
682 	//maybe there is some flag that marks real global actors and temporary ones
683 	if (actor->InParty || !CreateMovementEffect(actor, parameters->string0Parameter, parameters->pointParameter, 0) ) {
684 		MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, -1, true);
685 	}
686 }
687 
688 //we also allow moving to door, container
MoveGlobalObject(Scriptable * Sender,Action * parameters)689 void GameScript::MoveGlobalObject(Scriptable* Sender, Action* parameters)
690 {
691 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
692 	if (!tar || tar->Type != ST_ACTOR) {
693 		return;
694 	}
695 	Scriptable* to = GetActorFromObject( Sender, parameters->objects[2] );
696 	if (!to) {
697 		return;
698 	}
699 	const Map *map = to->GetCurrentArea();
700 
701 	if (map) {
702 		Actor *actor = (Actor *) tar;
703 		if (actor->InParty || !CreateMovementEffect(actor, map->GetScriptName(), to->Pos, 0)) {
704 			MoveBetweenAreasCore( (Actor *) tar, map->GetScriptName(), to->Pos, -1, true);
705 		}
706 	}
707 }
708 
MoveGlobalObjectOffScreen(Scriptable * Sender,Action * parameters)709 void GameScript::MoveGlobalObjectOffScreen(Scriptable* Sender, Action* parameters)
710 {
711 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
712 	if (!tar || tar->Type != ST_ACTOR) {
713 		return;
714 	}
715 	Scriptable* to = GetActorFromObject( Sender, parameters->objects[2] );
716 	if (!to) {
717 		return;
718 	}
719 
720 	Actor *actor = (Actor *) tar;
721 	if (actor->InParty || !CreateMovementEffect(actor, parameters->string0Parameter, to->Pos, 0) ) {
722 		MoveBetweenAreasCore( actor, parameters->string0Parameter, to->Pos, -1, false);
723 	}
724 }
725 
726 //don't use offset from Sender
CreateCreature(Scriptable * Sender,Action * parameters)727 void GameScript::CreateCreature(Scriptable* Sender, Action* parameters)
728 {
729 	CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP|CC_SCRIPTNAME );
730 }
731 
732 //another highly redundant action
CreateCreatureDoor(Scriptable * Sender,Action * parameters)733 void GameScript::CreateCreatureDoor(Scriptable* Sender, Action* parameters)
734 {
735 	//we hack this to death
736 	strcpy(parameters->string1Parameter, "SPDIMNDR");
737 	CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP | CC_PLAY_ANIM );
738 }
739 
740 //another highly redundant action
CreateCreatureObjectDoor(Scriptable * Sender,Action * parameters)741 void GameScript::CreateCreatureObjectDoor(Scriptable* Sender, Action* parameters)
742 {
743 	//we hack this to death
744 	strcpy(parameters->string1Parameter, "SPDIMNDR");
745 	CreateCreatureCore( Sender, parameters, CC_OBJECT | CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP | CC_PLAY_ANIM );
746 }
747 
748 //don't use offset from Sender
CreateCreatureImpassable(Scriptable * Sender,Action * parameters)749 void GameScript::CreateCreatureImpassable(Scriptable* Sender, Action* parameters)
750 {
751 	CreateCreatureCore( Sender, parameters, CC_CHECK_OVERLAP );
752 }
753 
CreateCreatureImpassableAllowOverlap(Scriptable * Sender,Action * parameters)754 void GameScript::CreateCreatureImpassableAllowOverlap(Scriptable* Sender, Action* parameters)
755 {
756 	CreateCreatureCore( Sender, parameters, 0 );
757 }
758 
759 //use offset from Sender
CreateCreatureAtFeet(Scriptable * Sender,Action * parameters)760 void GameScript::CreateCreatureAtFeet(Scriptable* Sender, Action* parameters)
761 {
762 	CreateCreatureCore( Sender, parameters, CC_OFFSET | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP);
763 }
764 
CreateCreatureOffScreen(Scriptable * Sender,Action * parameters)765 void GameScript::CreateCreatureOffScreen(Scriptable* Sender, Action* parameters)
766 {
767 	CreateCreatureCore( Sender, parameters, CC_OFFSCREEN | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP );
768 }
769 
770 //creates copy at actor, plays animation
CreateCreatureObjectCopy(Scriptable * Sender,Action * parameters)771 void GameScript::CreateCreatureObjectCopy(Scriptable* Sender, Action* parameters)
772 {
773 	CreateCreatureCore( Sender, parameters, CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_COPY | CC_PLAY_ANIM );
774 }
775 
776 //creates copy at absolute point
CreateCreatureCopyPoint(Scriptable * Sender,Action * parameters)777 void GameScript::CreateCreatureCopyPoint(Scriptable* Sender, Action* parameters)
778 {
779 	CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_COPY | CC_PLAY_ANIM );
780 }
781 
782 //this is the same, object + offset
783 //using this for simple createcreatureobject, (0 offsets)
784 //createcreatureobjecteffect may have animation
CreateCreatureObjectOffset(Scriptable * Sender,Action * parameters)785 void GameScript::CreateCreatureObjectOffset(Scriptable* Sender, Action* parameters)
786 {
787 	CreateCreatureCore( Sender, parameters, CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_PLAY_ANIM);
788 }
789 
CreateCreatureObjectOffScreen(Scriptable * Sender,Action * parameters)790 void GameScript::CreateCreatureObjectOffScreen(Scriptable* Sender, Action* parameters)
791 {
792 	CreateCreatureCore( Sender, parameters, CC_OFFSCREEN | CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP );
793 }
794 
795 //I think this simply removes the cursor and hides the gui without disabling scripts
796 //See Interface::SetCutSceneMode
SetCursorState(Scriptable *,Action * parameters)797 void GameScript::SetCursorState(Scriptable* /*Sender*/, Action* parameters)
798 {
799 	int active = parameters->int0Parameter;
800 
801 	Game *game = core->GetGame();
802 	game->SetControlStatus(CS_HIDEGUI, (active) ? OP_OR : OP_NAND );
803 	if (active) {
804 		core->GetWindowManager()->SetCursorFeedback(WindowManager::MOUSE_NONE);
805 	} else {
806 		core->GetWindowManager()->SetCursorFeedback(WindowManager::CursorFeedback(core->MouseFeedback));
807 	}
808 }
809 
StartCutSceneMode(Scriptable *,Action *)810 void GameScript::StartCutSceneMode(Scriptable* /*Sender*/, Action* /*parameters*/)
811 {
812 	core->SetCutSceneMode( true );
813 }
814 
EndCutSceneMode(Scriptable *,Action *)815 void GameScript::EndCutSceneMode(Scriptable* /*Sender*/, Action* /*parameters*/)
816 {
817 	core->SetCutSceneMode( false );
818 }
819 
StartCutScene(Scriptable * Sender,Action * parameters)820 void GameScript::StartCutScene(Scriptable* Sender, Action* parameters)
821 {
822 	GameScript* gs = new GameScript( parameters->string0Parameter, Sender );
823 	gs->EvaluateAllBlocks();
824 	delete( gs );
825 }
826 
827 // StartCutScene("my_nifty_cut_scene") = StartCutSceneEx("my_nifty_cut_scene",FALSE)
StartCutSceneEx(Scriptable * Sender,Action * parameters)828 void GameScript::StartCutSceneEx(Scriptable* Sender, Action* parameters)
829 {
830 	if (parameters->int0Parameter) {
831 		// TODO: ee, don't skip trigger evaluation
832 		// not needed in pst, since the only two uses just have True conditions
833 		// see ifdef in GameScript::EvaluateAllBlocks
834 	}
835 	GameScript *gs = new GameScript(parameters->string0Parameter, Sender);
836 	gs->EvaluateAllBlocks();
837 	delete gs;
838 }
839 
CutSceneID(Scriptable * Sender,Action *)840 void GameScript::CutSceneID(Scriptable *Sender, Action* /*parameters*/)
841 {
842 	// shouldn't get called
843 	Log(DEBUG, "GameScript", "CutSceneID was called by %s!", Sender->GetScriptName());
844 }
845 
846 static EffectRef fx_charm_ref = { "State:Charmed", -1 };
847 
Enemy(Scriptable * Sender,Action *)848 void GameScript::Enemy(Scriptable* Sender, Action* /*parameters*/)
849 {
850 	if (Sender->Type != ST_ACTOR) {
851 		return;
852 	}
853 	Actor* actor = ( Actor* ) Sender;
854 
855 	actor->fxqueue.RemoveAllEffects(fx_charm_ref);
856 	actor->SetBase( IE_EA, EA_ENEMY );
857 }
858 
Ally(Scriptable * Sender,Action *)859 void GameScript::Ally(Scriptable* Sender, Action* /*parameters*/)
860 {
861 	if (Sender->Type != ST_ACTOR) {
862 		return;
863 	}
864 	Actor* actor = ( Actor* ) Sender;
865 
866 	actor->fxqueue.RemoveAllEffects(fx_charm_ref);
867 	actor->SetBase( IE_EA, EA_ALLY );
868 }
869 
870 /** GemRB extension: you can replace baldur.bcs */
ChangeAIScript(Scriptable * Sender,Action * parameters)871 void GameScript::ChangeAIScript(Scriptable* Sender, Action* parameters)
872 {
873 	if (parameters->int0Parameter>=MAX_SCRIPTS) {
874 		return;
875 	}
876 	//clearing the queue, and checking script level was intentionally removed
877 	Sender->SetScript( parameters->string0Parameter, parameters->int0Parameter, false );
878 }
879 
ForceAIScript(Scriptable * Sender,Action * parameters)880 void GameScript::ForceAIScript(Scriptable* Sender, Action* parameters)
881 {
882 	if (parameters->int0Parameter>=MAX_SCRIPTS) {
883 		return;
884 	}
885 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
886 	if (!tar || tar->Type != ST_ACTOR) {
887 		return;
888 	}
889 	Actor* actor = ( Actor* ) tar;
890 	//clearing the queue, and checking script level was intentionally removed
891 	actor->SetScript( parameters->string0Parameter, parameters->int0Parameter, false );
892 }
893 
SetPlayerSound(Scriptable * Sender,Action * parameters)894 void GameScript::SetPlayerSound(Scriptable* Sender, Action* parameters)
895 {
896 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
897 	if (!tar || tar->Type != ST_ACTOR) {
898 		return;
899 	}
900 	if (((ieDword) parameters->int0Parameter)>=100) {
901 		Log(WARNING, "GameScript", "Invalid index %d in SetPlayerSound.", parameters->int0Parameter);
902 		return;
903 	}
904 	Actor* actor = ( Actor* ) tar;
905 	actor->StrRefs[parameters->int0Parameter]=parameters->int1Parameter;
906 }
907 
908 //this one works only on real actors, they got constants
VerbalConstantHead(Scriptable * Sender,Action * parameters)909 void GameScript::VerbalConstantHead(Scriptable* Sender, Action* parameters)
910 {
911 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
912 	if (!tar || tar->Type != ST_ACTOR) {
913 		return;
914 	}
915 	DisplayStringCore( tar, parameters->int0Parameter, DS_HEAD|DS_CONSOLE|DS_CONST);
916 }
917 
VerbalConstant(Scriptable * Sender,Action * parameters)918 void GameScript::VerbalConstant(Scriptable* Sender, Action* parameters)
919 {
920 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
921 	if (!tar || tar->Type != ST_ACTOR) {
922 		return;
923 	}
924 	DisplayStringCore( tar, parameters->int0Parameter, DS_CONSOLE|DS_CONST);
925 }
926 
927 //bg2 - variable
SaveLocation(Scriptable * Sender,Action * parameters)928 void GameScript::SaveLocation(Scriptable* Sender, Action* parameters)
929 {
930 	ieDword value = parameters->pointParameter.asDword();
931 	if (!parameters->string0Parameter[0]) {
932 		strcpy(parameters->string0Parameter,"LOCALSsavedlocation");
933 	}
934 	SetVariable(Sender, parameters->string0Parameter, value);
935 }
936 
937 //PST:has parameters, IWD2: no params
SetSavedLocation(Scriptable * Sender,Action * parameters)938 void GameScript::SetSavedLocation(Scriptable* Sender, Action* parameters)
939 {
940 	if (Sender->Type!=ST_ACTOR) {
941 		return;
942 	}
943 	Actor *actor = (Actor *) Sender;
944 	//iwd2
945 	if (parameters->pointParameter.isnull()) {
946 		actor->SetBase(IE_SAVEDXPOS, actor->Pos.x);
947 		actor->SetBase(IE_SAVEDYPOS, actor->Pos.y);
948 		actor->SetBase(IE_SAVEDFACE, actor->GetOrientation());
949 		return;
950 	}
951 	//pst
952 	actor->SetBase(IE_SAVEDXPOS, parameters->pointParameter.x);
953 	actor->SetBase(IE_SAVEDYPOS, parameters->pointParameter.y);
954 	actor->SetBase(IE_SAVEDFACE, parameters->int0Parameter);
955 }
956 //IWD2, sets the homepoint int0,int1,int2
SetSavedLocationPoint(Scriptable * Sender,Action * parameters)957 void GameScript::SetSavedLocationPoint(Scriptable* Sender, Action* parameters)
958 {
959 	if (Sender->Type!=ST_ACTOR) {
960 		return;
961 	}
962 	Actor *actor = (Actor *) Sender;
963 	actor->SetBase(IE_SAVEDXPOS, parameters->int0Parameter);
964 	actor->SetBase(IE_SAVEDYPOS, parameters->int1Parameter);
965 	actor->SetBase(IE_SAVEDFACE, parameters->int2Parameter);
966 }
967 
968 //IWD2, sets the homepoint P
969 // handle [-1.-1] specially, if needed; ar6200.bcs has interesting use
SetStartPos(Scriptable * Sender,Action * parameters)970 void GameScript::SetStartPos(Scriptable* Sender, Action* parameters)
971 {
972 	if (Sender->Type!=ST_ACTOR) {
973 		return;
974 	}
975 	Actor *actor = (Actor *) Sender;
976 	actor->SetBase(IE_SAVEDXPOS, parameters->pointParameter.x);
977 	actor->SetBase(IE_SAVEDYPOS, parameters->pointParameter.y);
978 	actor->SetBase(IE_SAVEDFACE, parameters->int0Parameter);
979 }
980 
SaveObjectLocation(Scriptable * Sender,Action * parameters)981 void GameScript::SaveObjectLocation(Scriptable* Sender, Action* parameters)
982 {
983 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
984 	if (!tar) {
985 		return;
986 	}
987 	ieDword value = tar->Pos.asDword();
988 	if (!parameters->string0Parameter[0]) {
989 		strcpy(parameters->string0Parameter,"LOCALSsavedlocation");
990 	}
991 	SetVariable(Sender, parameters->string0Parameter, value);
992 }
993 
994 /** you may omit the string0Parameter, in this case this will be a */
995 /** CreateCreatureAtSavedLocation */
CreateCreatureAtLocation(Scriptable * Sender,Action * parameters)996 void GameScript::CreateCreatureAtLocation(Scriptable* Sender, Action* parameters)
997 {
998 	if (!parameters->string0Parameter[0]) {
999 		strcpy(parameters->string0Parameter,"LOCALSsavedlocation");
1000 	}
1001 	ieDword value = CheckVariable(Sender, parameters->string0Parameter);
1002 	parameters->pointParameter.y = (ieWord) (value & 0xffff);
1003 	parameters->pointParameter.x = (ieWord) (value >> 16);
1004 	CreateCreatureCore(Sender, parameters, CC_CHECK_IMPASSABLE|CC_STRING1);
1005 }
1006 
WaitRandom(Scriptable * Sender,Action * parameters)1007 void GameScript::WaitRandom(Scriptable* Sender, Action* parameters)
1008 {
1009 	if (!Sender->CurrentActionState) {
1010 		int width = parameters->int1Parameter-parameters->int0Parameter;
1011 		if (width<2) {
1012 			width = parameters->int0Parameter;
1013 		} else {
1014 			width = RAND(0, width-1) + parameters->int0Parameter;
1015 		}
1016 		Sender->CurrentActionState = width * AI_UPDATE_TIME;
1017 	} else {
1018 		Sender->CurrentActionState--;
1019 	}
1020 
1021 	if (!Sender->CurrentActionState) {
1022 		Sender->ReleaseCurrentAction();
1023 		return;
1024 	}
1025 
1026 	assert(Sender->CurrentActionState >= 0);
1027 }
1028 
Wait(Scriptable * Sender,Action * parameters)1029 void GameScript::Wait(Scriptable* Sender, Action* parameters)
1030 {
1031 	if (!Sender->CurrentActionState) {
1032 		Sender->CurrentActionState = parameters->int0Parameter * AI_UPDATE_TIME;
1033 	} else {
1034 		Sender->CurrentActionState--;
1035 	}
1036 
1037 	if (!Sender->CurrentActionState) {
1038 		Sender->ReleaseCurrentAction();
1039 		return;
1040 	}
1041 
1042 	assert(Sender->CurrentActionState >= 0);
1043 }
1044 
SmallWait(Scriptable * Sender,Action * parameters)1045 void GameScript::SmallWait(Scriptable* Sender, Action* parameters)
1046 {
1047 	if (!Sender->CurrentActionState) {
1048 		Sender->CurrentActionState = parameters->int0Parameter;
1049 	} else {
1050 		Sender->CurrentActionState--;
1051 	}
1052 
1053 	if (!Sender->CurrentActionState) {
1054 		Sender->ReleaseCurrentAction();
1055 	}
1056 
1057 	assert(Sender->CurrentActionState >= 0);
1058 }
1059 
SmallWaitRandom(Scriptable * Sender,Action * parameters)1060 void GameScript::SmallWaitRandom(Scriptable* Sender, Action* parameters)
1061 {
1062 	if (!Sender->CurrentActionState) {
1063 		int random = parameters->int1Parameter - parameters->int0Parameter;
1064 		if (random<1) {
1065 			random = 1;
1066 		}
1067 		Sender->CurrentActionState = RAND(0, random-1) + parameters->int0Parameter;
1068 	} else {
1069 		Sender->CurrentActionState--;
1070 	}
1071 
1072 	if (!Sender->CurrentActionState) {
1073 		Sender->ReleaseCurrentAction();
1074 	}
1075 
1076 	assert(Sender->CurrentActionState >= 0);
1077 }
1078 
MoveViewPoint(Scriptable * Sender,Action * parameters)1079 void GameScript::MoveViewPoint(Scriptable* Sender, Action* parameters)
1080 {
1081 	// disable centering if anything enabled it before us (eg. LeaveAreaLUA as in movie02a.bcs)
1082 	GameControl *gc = core->GetGameControl();
1083 	gc->SetScreenFlags(SF_CENTERONACTOR, OP_NAND);
1084 	core->timer.SetMoveViewPort( parameters->pointParameter, parameters->int0Parameter<<1, true );
1085 	Sender->SetWait(1); // todo, blocking?
1086 	Sender->ReleaseCurrentAction(); // todo, blocking?
1087 }
1088 
MoveViewObject(Scriptable * Sender,Action * parameters)1089 void GameScript::MoveViewObject(Scriptable* Sender, Action* parameters)
1090 {
1091 	Scriptable * scr = GetActorFromObject( Sender, parameters->objects[1]);
1092 	if (!scr) {
1093 		Sender->ReleaseCurrentAction();
1094 		return;
1095 	}
1096 	core->timer.SetMoveViewPort( scr->Pos, parameters->int0Parameter<<1, true );
1097 	Sender->SetWait(1); // todo, blocking?
1098 	Sender->ReleaseCurrentAction(); // todo, blocking?
1099 }
1100 
AddWayPoint(Scriptable * Sender,Action * parameters)1101 void GameScript::AddWayPoint(Scriptable* Sender, Action* parameters)
1102 {
1103 	if (Sender->Type != ST_ACTOR) {
1104 		Sender->ReleaseCurrentAction();
1105 		return;
1106 	}
1107 	Actor* actor = ( Actor* ) Sender;
1108 	actor->AddWayPoint( parameters->pointParameter );
1109 	// this is marked as AF_BLOCKING (and indeed AddWayPoint causes moves),
1110 	// but this probably needs more thought
1111 	Sender->ReleaseCurrentAction();
1112 }
1113 
MoveToPointNoRecticle(Scriptable * Sender,Action * parameters)1114 void GameScript::MoveToPointNoRecticle(Scriptable* Sender, Action* parameters)
1115 {
1116 	if (Sender->Type != ST_ACTOR) {
1117 		Sender->ReleaseCurrentAction();
1118 		return;
1119 	}
1120 	Actor *actor = ( Actor* ) Sender;
1121 	if (!actor->InMove() || actor->Destination != parameters->pointParameter) {
1122 		actor->WalkTo( parameters->pointParameter, IF_NORETICLE, 0 );
1123 	}
1124 	if (!actor->InMove()) {
1125 		// we should probably instead keep retrying until we reach dest
1126 		actor->ClearPath();
1127 		Sender->ReleaseCurrentAction();
1128 	}
1129 }
1130 
MoveToPointNoInterrupt(Scriptable * Sender,Action * parameters)1131 void GameScript::MoveToPointNoInterrupt(Scriptable* Sender, Action* parameters)
1132 {
1133 	if (Sender->Type != ST_ACTOR) {
1134 		Sender->ReleaseCurrentAction();
1135 		return;
1136 	}
1137 	Actor* actor = ( Actor* ) Sender;
1138 	if (!actor->InMove() || actor->Destination != parameters->pointParameter) {
1139 		actor->WalkTo( parameters->pointParameter, IF_NOINT, 0 );
1140 	}
1141 	// should we always force IF_NOINT here?
1142 	if (!actor->InMove()) {
1143 		// we should probably instead keep retrying until we reach dest
1144 		actor->Interrupt();
1145 		actor->ClearPath();
1146 		Sender->ReleaseCurrentAction();
1147 	}
1148 }
1149 
RunToPointNoRecticle(Scriptable * Sender,Action * parameters)1150 void GameScript::RunToPointNoRecticle(Scriptable* Sender, Action* parameters)
1151 {
1152 	if (Sender->Type != ST_ACTOR) {
1153 		Sender->ReleaseCurrentAction();
1154 		return;
1155 	}
1156 	Actor* actor = ( Actor* ) Sender;
1157 	if (!actor->InMove() || actor->Destination != parameters->pointParameter) {
1158 		actor->SetOrientation(GetOrient(parameters->pointParameter, actor->Pos), false);
1159 		actor->WalkTo( parameters->pointParameter, IF_NORETICLE|IF_RUNNING, 0 );
1160 	}
1161 	if (!actor->InMove()) {
1162 		// we should probably instead keep retrying until we reach dest
1163 		actor->ClearPath();
1164 		Sender->ReleaseCurrentAction();
1165 	}
1166 }
1167 
RunToPoint(Scriptable * Sender,Action * parameters)1168 void GameScript::RunToPoint(Scriptable* Sender, Action* parameters)
1169 {
1170 	if (Sender->Type != ST_ACTOR) {
1171 		Sender->ReleaseCurrentAction();
1172 		return;
1173 	}
1174 	Actor* actor = ( Actor* ) Sender;
1175 	if (!actor->InMove() || actor->Destination != parameters->pointParameter) {
1176 		actor->SetOrientation(GetOrient(parameters->pointParameter, actor->Pos), false);
1177 		actor->WalkTo( parameters->pointParameter, IF_RUNNING, 0 );
1178 	}
1179 	if (!actor->InMove()) {
1180 		// we should probably instead keep retrying until we reach dest
1181 		actor->ClearPath();
1182 		Sender->ReleaseCurrentAction();
1183 	}
1184 }
1185 
1186 //movetopoint until timer is down or target reached
TimedMoveToPoint(Scriptable * Sender,Action * parameters)1187 void GameScript::TimedMoveToPoint(Scriptable* Sender, Action* parameters)
1188 {
1189 	if (Sender->Type != ST_ACTOR) {
1190 		Sender->ReleaseCurrentAction();
1191 		return;
1192 	}
1193 	if (parameters->int0Parameter<=0) {
1194 		Sender->ReleaseCurrentAction();
1195 		return;
1196 	}
1197 	Actor *actor = (Actor *) Sender;
1198 
1199 	if (!actor->InMove() || actor->Destination != parameters->pointParameter) {
1200 		actor->WalkTo(parameters->pointParameter, 0, parameters->int1Parameter);
1201 	}
1202 
1203 	//hopefully this hack will prevent lockups
1204 	if (!actor->InMove()) {
1205 		// we should probably instead keep retrying until we reach dest
1206 		actor->ClearPath();
1207 		Sender->ReleaseCurrentAction();
1208 		return;
1209 	}
1210 
1211 	//repeat movement...
1212 	if (parameters->int0Parameter>0) {
1213 		Action *newaction = ParamCopyNoOverride(parameters);
1214 		newaction->int0Parameter--;
1215 		actor->AddActionInFront(newaction);
1216 		Sender->SetWait(1);
1217 	}
1218 
1219 	Sender->ReleaseCurrentAction();
1220 }
1221 
MoveToPoint(Scriptable * Sender,Action * parameters)1222 void GameScript::MoveToPoint(Scriptable* Sender, Action* parameters)
1223 {
1224 	if (Sender->Type != ST_ACTOR) {
1225 		Sender->ReleaseCurrentAction();
1226 		return;
1227 	}
1228 	Actor* actor = ( Actor* ) Sender;
1229 
1230 	// iwd2 is the only one with special handling:
1231 	// -2 is used as HomeLocation; no other unusual values are used
1232 	if (parameters->pointParameter.x < 0) {
1233 		parameters->pointParameter = actor->HomeLocation;
1234 	}
1235 
1236 	// try the actual move, if we are not already moving there
1237 	if (!actor->InMove() || actor->Destination != parameters->pointParameter) {
1238 		actor->WalkTo( parameters->pointParameter, 0 );
1239 	}
1240 
1241 	// give up if we can't move there (no path was found)
1242 	if (!actor->InMove()) {
1243 		// we should probably instead keep retrying until we reach dest
1244 		actor->ClearPath();
1245 		Sender->ReleaseCurrentAction();
1246 	}
1247 }
1248 
1249 //bg2, jumps to saved location in variable
MoveToSavedLocation(Scriptable * Sender,Action * parameters)1250 void GameScript::MoveToSavedLocation(Scriptable* Sender, Action* parameters)
1251 {
1252 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
1253 	if (!tar) {
1254 		tar = Sender;
1255 	}
1256 	if (tar->Type != ST_ACTOR) {
1257 		Sender->ReleaseCurrentAction();
1258 		return;
1259 	}
1260 
1261 	Point p;
1262 	Actor* actor = ( Actor* ) tar;
1263 	ieDword value = CheckVariable(Sender, parameters->string0Parameter);
1264 	p.fromDword(value);
1265 	actor->SetPosition(p, true );
1266 	Sender->ReleaseCurrentAction();
1267 }
1268 /** iwd2 returntosavedlocation (with stats) */
1269 /** pst returntosavedplace */
1270 /** use Sender as default subject */
ReturnToSavedLocation(Scriptable * Sender,Action * parameters)1271 void GameScript::ReturnToSavedLocation(Scriptable* Sender, Action* parameters)
1272 {
1273 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
1274 	if (!tar) {
1275 		tar = Sender;
1276 	}
1277 	if (tar->Type != ST_ACTOR) {
1278 		Sender->ReleaseCurrentAction();
1279 		return;
1280 	}
1281 
1282 	Actor* actor = ( Actor* ) tar;
1283 	Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) );
1284 	if (p.isnull()) {
1285 		Sender->ReleaseCurrentAction();
1286 		return;
1287 	}
1288 	if (!actor->InMove() || actor->Destination != p) {
1289 		actor->WalkTo( p, 0, 0 );
1290 	}
1291 	if (!actor->InMove()) {
1292 		// we should probably instead keep retrying until we reach dest
1293 		actor->ClearPath();
1294 		Sender->ReleaseCurrentAction();
1295 	}
1296 }
1297 
1298 //PST
RunToSavedLocation(Scriptable * Sender,Action * parameters)1299 void GameScript::RunToSavedLocation(Scriptable* Sender, Action* parameters)
1300 {
1301 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
1302 	if (!tar) {
1303 		tar = Sender;
1304 	}
1305 	if (tar->Type != ST_ACTOR) {
1306 		Sender->ReleaseCurrentAction();
1307 		return;
1308 	}
1309 
1310 	Actor* actor = ( Actor* ) tar;
1311 	Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) );
1312 	if (p.isnull()) {
1313 		Sender->ReleaseCurrentAction();
1314 		return;
1315 	}
1316 	if (!actor->InMove() || actor->Destination != p) {
1317 		actor->WalkTo( p, IF_RUNNING, 0 );
1318 	}
1319 	if (!actor->InMove()) {
1320 		// we should probably instead keep retrying until we reach dest
1321 		actor->ClearPath();
1322 		Sender->ReleaseCurrentAction();
1323 	}
1324 }
1325 
1326 //iwd2
ReturnToSavedLocationDelete(Scriptable * Sender,Action * parameters)1327 void GameScript::ReturnToSavedLocationDelete(Scriptable* Sender, Action* parameters)
1328 {
1329 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
1330 	if (!tar) {
1331 		tar = Sender;
1332 	}
1333 	if (tar->Type != ST_ACTOR) {
1334 		Sender->ReleaseCurrentAction();
1335 		return;
1336 	}
1337 
1338 	Actor* actor = ( Actor* ) tar;
1339 	Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) );
1340 	actor->SetBase(IE_SAVEDXPOS,0);
1341 	actor->SetBase(IE_SAVEDYPOS,0);
1342 	if (p.isnull()) {
1343 		Sender->ReleaseCurrentAction();
1344 		return;
1345 	}
1346 	if (!actor->InMove() || actor->Destination != p) {
1347 		actor->WalkTo( p, 0, 0 );
1348 	}
1349 	//what else?
1350 	if (!actor->InMove()) {
1351 		// we should probably instead keep retrying until we reach dest
1352 		actor->ClearPath();
1353 		Sender->ReleaseCurrentAction();
1354 	}
1355 }
1356 
ReturnToStartLocation(Scriptable * Sender,Action * parameters)1357 void GameScript::ReturnToStartLocation(Scriptable* Sender, Action* parameters)
1358 {
1359 	Scriptable* tar = GetActorFromObject(Sender, parameters->objects[1], GA_NO_DEAD);
1360 	if (!tar) {
1361 		tar = Sender;
1362 	}
1363 	if (tar->Type != ST_ACTOR) {
1364 		Sender->ReleaseCurrentAction();
1365 		return;
1366 	}
1367 
1368 	Actor* actor = (Actor *) tar;
1369 	Point p = actor->HomeLocation;
1370 	if (p.isnull()) {
1371 		Sender->ReleaseCurrentAction();
1372 		return;
1373 	}
1374 	if (!actor->InMove() || actor->Destination != p) {
1375 		actor->WalkTo(p, 0, parameters->int0Parameter);
1376 	}
1377 	if (!actor->InMove()) {
1378 		// we should probably instead keep retrying until we reach dest
1379 		actor->ClearPath();
1380 		Sender->ReleaseCurrentAction();
1381 	}
1382 }
1383 
TriggerWalkTo(Scriptable * Sender,Action * parameters)1384 void GameScript::TriggerWalkTo(Scriptable* Sender, Action* parameters)
1385 {
1386 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], 0 );
1387 	if (!tar) {
1388 		Sender->ReleaseCurrentAction();
1389 		return;
1390 	}
1391 	MoveToObjectCore(Sender, parameters, 0, false);
1392 	tar->AddTrigger(TriggerEntry(trigger_walkedtotrigger, Sender->GetGlobalID()));
1393 }
1394 
MoveToObjectNoInterrupt(Scriptable * Sender,Action * parameters)1395 void GameScript::MoveToObjectNoInterrupt(Scriptable* Sender, Action* parameters)
1396 {
1397 	MoveToObjectCore(Sender, parameters, IF_NOINT, false);
1398 }
1399 
RunToObject(Scriptable * Sender,Action * parameters)1400 void GameScript::RunToObject(Scriptable* Sender, Action* parameters)
1401 {
1402 	MoveToObjectCore(Sender, parameters, IF_RUNNING, false);
1403 }
1404 
MoveToObject(Scriptable * Sender,Action * parameters)1405 void GameScript::MoveToObject(Scriptable* Sender, Action* parameters)
1406 {
1407 	MoveToObjectCore(Sender, parameters, 0, false);
1408 }
1409 
MoveToObjectUntilSee(Scriptable * Sender,Action * parameters)1410 void GameScript::MoveToObjectUntilSee(Scriptable* Sender, Action* parameters)
1411 {
1412 	MoveToObjectCore(Sender, parameters, 0, true);
1413 }
1414 
MoveToObjectFollow(Scriptable * Sender,Action * parameters)1415 void GameScript::MoveToObjectFollow(Scriptable* Sender, Action* parameters)
1416 {
1417 	if (Sender->Type != ST_ACTOR) {
1418 		Sender->ReleaseCurrentAction();
1419 		return;
1420 	}
1421 	Scriptable* target = GetStoredActorFromObject( Sender, parameters->objects[1] );
1422 	if (!target) {
1423 		Sender->ReleaseCurrentAction();
1424 		return;
1425 	}
1426 	Actor* actor = ( Actor* ) Sender;
1427 	//follow leader from a distance of 5
1428 	//could also follow the leader with a point offset
1429 	if (target->Type==ST_ACTOR) {
1430 		actor->SetLeader( (Actor *) target, 5);
1431 	}
1432 	MoveNearerTo(Sender, target, MAX_OPERATING_DISTANCE);
1433 }
1434 
StorePartyLocation(Scriptable *,Action *)1435 void GameScript::StorePartyLocation(Scriptable* /*Sender*/, Action* /*parameters*/)
1436 {
1437 	Game *game = core->GetGame();
1438 	for (int i = 0; i < game->GetPartySize(false); i++) {
1439 		Actor* act = game->GetPC( i, false );
1440 		GAMLocationEntry *gle = game->GetSavedLocationEntry(i);
1441 		if (act && gle) {
1442 			gle->Pos = act->Pos;
1443 			memcpy(gle->AreaResRef, act->Area, 9);
1444 		}
1445 	}
1446 }
1447 
RestorePartyLocation(Scriptable *,Action *)1448 void GameScript::RestorePartyLocation(Scriptable* /*Sender*/, Action* /*parameters*/)
1449 {
1450 	Game *game = core->GetGame();
1451 	for (int i = 0; i < game->GetPartySize(false); i++) {
1452 		Actor* act = game->GetPC( i, false );
1453 		if (act) {
1454 			GAMLocationEntry *gle;
1455 			if (game->GetSavedLocationCount() <= (unsigned int)i) {
1456 				// no location, meaning the actor joined the party after the save
1457 				// reuse the last valid location
1458 				gle = game->GetSavedLocationEntry(game->GetSavedLocationCount()-1);
1459 			} else {
1460 				gle = game->GetSavedLocationEntry(i);
1461 			}
1462 			MoveBetweenAreasCore(act, gle->AreaResRef, gle->Pos, -1, true);
1463 		}
1464 	}
1465 
1466 	// presumably this is correct
1467 	game->ClearSavedLocations();
1468 }
1469 
MoveToCenterOfScreen(Scriptable * Sender,Action *)1470 void GameScript::MoveToCenterOfScreen(Scriptable* Sender, Action* /*parameters*/)
1471 {
1472 	if (Sender->Type != ST_ACTOR) {
1473 		Sender->ReleaseCurrentAction();
1474 		return;
1475 	}
1476 	Region vp = core->GetGameControl()->Viewport();
1477 	Actor* actor = ( Actor* ) Sender;
1478 	Point p((short) (vp.x+vp.w/2), (short) (vp.y+vp.h/2) );
1479 	if (!actor->InMove() || actor->Destination != p) {
1480 		actor->WalkTo( p, IF_NOINT, 0 );
1481 	}
1482 	if (!actor->InMove()) {
1483 		// we should probably instead keep retrying until we reach dest
1484 		actor->Interrupt();
1485 		actor->ClearPath();
1486 		Sender->ReleaseCurrentAction();
1487 	}
1488 }
1489 
MoveToOffset(Scriptable * Sender,Action * parameters)1490 void GameScript::MoveToOffset(Scriptable* Sender, Action* parameters)
1491 {
1492 	if (Sender->Type != ST_ACTOR) {
1493 		Sender->ReleaseCurrentAction();
1494 		return;
1495 	}
1496 	Actor* actor = ( Actor* ) Sender;
1497 	Point p(Sender->Pos.x+parameters->pointParameter.x, Sender->Pos.y+parameters->pointParameter.y);
1498 	if (!actor->InMove() || actor->Destination != p) {
1499 		actor->WalkTo( p, 0, 0 );
1500 	}
1501 	if (!actor->InMove()) {
1502 		// we should probably instead keep retrying until we reach dest
1503 		actor->ClearPath();
1504 		Sender->ReleaseCurrentAction();
1505 	}
1506 }
1507 
RunAwayFrom(Scriptable * Sender,Action * parameters)1508 void GameScript::RunAwayFrom(Scriptable* Sender, Action* parameters)
1509 {
1510 	if (Sender->Type != ST_ACTOR) {
1511 		Sender->ReleaseCurrentAction();
1512 		return;
1513 	}
1514 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
1515 		Sender->ReleaseCurrentAction();
1516 		return;
1517 	}
1518 	Actor* actor = ( Actor* ) Sender;
1519 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
1520 	if (!tar) {
1521 		Sender->ReleaseCurrentAction();
1522 		return;
1523 	}
1524 	//TODO: actor could use travel areas
1525 	// we should be using int0Parameter for the timing here, not distance
1526 	if (!actor->InMove()) {
1527 		// we should make sure our existing walk is a 'run away', or fix moving/path code
1528 		actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false);
1529 		if (actor->ShouldModifyMorale()) {
1530 			actor->NewBase(IE_MORALE, 20, MOD_ABSOLUTE);
1531 		}
1532 	}
1533 
1534 	//repeat movement...
1535 	if (parameters->int0Parameter>0) {
1536 		Action *newaction = ParamCopyNoOverride(parameters);
1537 		newaction->int0Parameter--;
1538 		actor->AddActionInFront(newaction);
1539 		Sender->SetWait(1);
1540 	}
1541 
1542 	Sender->ReleaseCurrentAction();
1543 }
1544 
RunAwayFromNoLeaveArea(Scriptable * Sender,Action * parameters)1545 void GameScript::RunAwayFromNoLeaveArea(Scriptable* Sender, Action* parameters)
1546 {
1547 	if (Sender->Type != ST_ACTOR) {
1548 		Sender->ReleaseCurrentAction();
1549 		return;
1550 	}
1551 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
1552 		Sender->ReleaseCurrentAction();
1553 		return;
1554 	}
1555 	Actor* actor = ( Actor* ) Sender;
1556 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
1557 	if (!tar) {
1558 		Sender->ReleaseCurrentAction();
1559 		return;
1560 	}
1561 	// we should be using int0Parameter for the timing here, not distance
1562 	if (!actor->InMove()) {
1563 		// we should make sure our existing walk is a 'run away', or fix moving/path code
1564 		actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false);
1565 	}
1566 
1567 	//repeat movement...
1568 	if (parameters->int0Parameter>0) {
1569 		Action *newaction = ParamCopyNoOverride(parameters);
1570 		newaction->int0Parameter--;
1571 		actor->AddActionInFront(newaction);
1572 		Sender->SetWait(1);
1573 	}
1574 
1575 	Sender->ReleaseCurrentAction();
1576 }
1577 
RunAwayFromNoInterrupt(Scriptable * Sender,Action * parameters)1578 void GameScript::RunAwayFromNoInterrupt(Scriptable* Sender, Action* parameters)
1579 {
1580 	if (Sender->Type != ST_ACTOR) {
1581 		Sender->ReleaseCurrentAction();
1582 		return;
1583 	}
1584 	//i believe being dead still interrupts this action
1585 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
1586 		Sender->ReleaseCurrentAction();
1587 		return;
1588 	}
1589 	Actor* actor = ( Actor* ) Sender;
1590 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
1591 	if (!tar) {
1592 		Sender->ReleaseCurrentAction();
1593 		return;
1594 	}
1595 	actor->NoInterrupt();
1596 	//TODO: actor could use travel areas; once implemented, copy original to RunAwayFromNoInterruptNoLeaveArea and break the alias in GameScript.cpp
1597 	// we should be using int0Parameter for the timing here, not distance
1598 	if (!actor->InMove()) {
1599 		// we should make sure our existing walk is a 'run away', or fix moving/path code
1600 		actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false);
1601 	}
1602 
1603 	//repeat movement...
1604 	if (parameters->int0Parameter>0) {
1605 		Action *newaction = ParamCopyNoOverride(parameters);
1606 		newaction->int0Parameter--;
1607 		actor->AddActionInFront(newaction);
1608 		Sender->SetWait(1);
1609 	} else {
1610 		actor->Interrupt();
1611 	}
1612 
1613 	Sender->ReleaseCurrentAction();
1614 }
1615 
RunAwayFromPoint(Scriptable * Sender,Action * parameters)1616 void GameScript::RunAwayFromPoint(Scriptable* Sender, Action* parameters)
1617 {
1618 	if (Sender->Type != ST_ACTOR) {
1619 		Sender->ReleaseCurrentAction();
1620 		return;
1621 	}
1622 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
1623 		Sender->ReleaseCurrentAction();
1624 		return;
1625 	}
1626 	Actor* actor = ( Actor* ) Sender;
1627 	// we should be using int0Parameter for the timing here, not distance?
1628 	if (!actor->InMove()) {
1629 		// we should make sure our existing walk is a 'run away', or fix moving/path code
1630 		actor->RunAwayFrom( parameters->pointParameter, parameters->int0Parameter, false);
1631 	}
1632 
1633 	//repeat movement...
1634 	if (parameters->int0Parameter>0) {
1635 		Action *newaction = ParamCopyNoOverride(parameters);
1636 		newaction->int0Parameter--;
1637 		actor->AddActionInFront(newaction);
1638 		Sender->SetWait(1);
1639 	}
1640 
1641 	Sender->ReleaseCurrentAction();
1642 }
1643 
DisplayStringNoName(Scriptable * Sender,Action * parameters)1644 void GameScript::DisplayStringNoName(Scriptable* Sender, Action* parameters)
1645 {
1646 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]);
1647 	if (!target) {
1648 		target=Sender;
1649 	}
1650 	if (Sender->Type==ST_ACTOR) {
1651 		DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE|DS_NONAME);
1652 	} else {
1653 		// Virtue calls this from the global script, but maybe Pos is ok for areas
1654 		// set DS_CONSOLE only for ST_GLOBAL if it turns out areas don't care;
1655 		// could also be dependent on the subtitle setting, see DisplayStringCore
1656 		DisplayStringCore(target, parameters->int0Parameter, DS_AREA|DS_CONSOLE|DS_NONAME);
1657 	}
1658 }
1659 
DisplayStringNoNameHead(Scriptable * Sender,Action * parameters)1660 void GameScript::DisplayStringNoNameHead(Scriptable* Sender, Action* parameters)
1661 {
1662 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1663 	if (!target) {
1664 		target=Sender;
1665 	}
1666 
1667 	DisplayStringCore( target, parameters->int0Parameter, DS_HEAD|DS_CONSOLE|DS_NONAME);
1668 }
1669 
1670 //display message over current script owner
DisplayMessage(Scriptable * Sender,Action * parameters)1671 void GameScript::DisplayMessage(Scriptable* Sender, Action* parameters)
1672 {
1673 	DisplayStringCore(Sender, parameters->int0Parameter, DS_CONSOLE );
1674 }
1675 
1676 //float message over target
DisplayStringHead(Scriptable * Sender,Action * parameters)1677 void GameScript::DisplayStringHead(Scriptable* Sender, Action* parameters)
1678 {
1679 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1680 	if (!target) {
1681 		target=Sender;
1682 		Log(WARNING, "Actions", "DisplayStringHead/FloatMessage got no target, assuming Sender!");
1683 	}
1684 
1685 	DisplayStringCore(target, parameters->int0Parameter, DS_CONSOLE|DS_HEAD|DS_SPEECH );
1686 }
1687 
KillFloatMessage(Scriptable * Sender,Action * parameters)1688 void GameScript::KillFloatMessage(Scriptable* Sender, Action* parameters)
1689 {
1690 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1691 	if (!target) {
1692 		target=Sender;
1693 	}
1694 	target->DisplayOverheadText(false);
1695 }
1696 
DisplayStringHeadOwner(Scriptable *,Action * parameters)1697 void GameScript::DisplayStringHeadOwner(Scriptable* /*Sender*/, Action* parameters)
1698 {
1699 	Game *game=core->GetGame();
1700 
1701 	int i = game->GetPartySize(true);
1702 	while(i--) {
1703 		Actor *actor = game->GetPC(i, true);
1704 		if (actor->inventory.HasItem(parameters->string0Parameter, 0)) {
1705 			DisplayStringCore(actor, parameters->int0Parameter, DS_CONSOLE|DS_HEAD );
1706 		}
1707 	}
1708 }
1709 
1710 // TODO: fix these two actions — they actually take a point, not an object
FloatMessageFixed(Scriptable * Sender,Action * parameters)1711 void GameScript::FloatMessageFixed(Scriptable* Sender, Action* parameters)
1712 {
1713 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1714 	if (!target) {
1715 		target=Sender;
1716 		Log(ERROR, "GameScript", "DisplayStringHead/FloatMessage got no target, assuming Sender!");
1717 	}
1718 
1719 	DisplayStringCore(target, parameters->int0Parameter, DS_CONSOLE|DS_HEAD);
1720 }
1721 
FloatMessageFixedRnd(Scriptable * Sender,Action * parameters)1722 void GameScript::FloatMessageFixedRnd(Scriptable* Sender, Action* parameters)
1723 {
1724 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1725 	if (!target) {
1726 		target=Sender;
1727 		Log(ERROR, "GameScript", "DisplayStringHead/FloatMessage got no target, assuming Sender!");
1728 	}
1729 
1730 	SrcVector *rndstr=LoadSrc(parameters->string0Parameter);
1731 	if (!rndstr) {
1732 		Log(ERROR, "GameScript", "Cannot display resource!");
1733 		return;
1734 	}
1735 	DisplayStringCore(target, rndstr->at(RAND(0, rndstr->size()-1)), DS_CONSOLE|DS_HEAD);
1736 	FreeSrc(rndstr, parameters->string0Parameter);
1737 }
1738 
FloatMessageRnd(Scriptable * Sender,Action * parameters)1739 void GameScript::FloatMessageRnd(Scriptable* Sender, Action* parameters)
1740 {
1741 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1742 	if (!target) {
1743 		target=Sender;
1744 		Log(ERROR, "GameScript", "DisplayStringHead/FloatMessage got no target, assuming Sender!");
1745 	}
1746 
1747 	SrcVector *rndstr=LoadSrc(parameters->string0Parameter);
1748 	if (!rndstr) {
1749 		Log(ERROR, "GameScript", "Cannot display resource!");
1750 		return;
1751 	}
1752 	DisplayStringCore(target, rndstr->at(RAND(0, rndstr->size()-1)), DS_CONSOLE|DS_HEAD);
1753 	FreeSrc(rndstr, parameters->string0Parameter);
1754 }
1755 
1756 //apparently this should not display over head (for actors)
DisplayString(Scriptable * Sender,Action * parameters)1757 void GameScript::DisplayString(Scriptable* Sender, Action* parameters)
1758 {
1759 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]);
1760 	if (!target) {
1761 		target=Sender;
1762 	}
1763 	if (Sender->Type==ST_ACTOR) {
1764 		DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE);
1765 	} else {
1766 		DisplayStringCore( target, parameters->int0Parameter, DS_AREA);
1767 	}
1768 }
1769 
1770 //DisplayStringHead, but wait until done
DisplayStringWait(Scriptable * Sender,Action * parameters)1771 void GameScript::DisplayStringWait(Scriptable* Sender, Action* parameters)
1772 {
1773 	ieDword gt = core->GetGame()->GameTime;
1774 	if (Sender->CurrentActionState) {
1775 		if (gt >= (ieDword)parameters->int2Parameter) {
1776 			Sender->ReleaseCurrentAction();
1777 		}
1778 		return;
1779 	}
1780 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]);
1781 	if (!target) {
1782 		target=Sender;
1783 	}
1784 	DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE|DS_WAIT|DS_SPEECH|DS_HEAD);
1785 	Sender->CurrentActionState = 1;
1786 	// parameters->int2Parameter is unused here so hijack it to store the wait time
1787 	// and make sure we wait at least one round, so strings without audio have some time to display
1788 	unsigned long waitCounter = target->GetWait();
1789 	parameters->int2Parameter = gt + (waitCounter > 0 ? waitCounter : core->Time.round_size);
1790 }
1791 
ForceFacing(Scriptable * Sender,Action * parameters)1792 void GameScript::ForceFacing(Scriptable* Sender, Action* parameters)
1793 {
1794 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
1795 	if (!tar || tar->Type!=ST_ACTOR) {
1796 		Sender->ReleaseCurrentAction();
1797 		return;
1798 	}
1799 	Actor *actor = (Actor *) tar;
1800 	actor->SetOrientation(parameters->int0Parameter, false);
1801 }
1802 
1803 /* A -1 means random facing? */
Face(Scriptable * Sender,Action * parameters)1804 void GameScript::Face(Scriptable* Sender, Action* parameters)
1805 {
1806 	if (Sender->Type != ST_ACTOR) {
1807 		Sender->ReleaseCurrentAction();
1808 		return;
1809 	}
1810 	Actor* actor = ( Actor* ) Sender;
1811 	if (parameters->int0Parameter==-1) {
1812 		actor->SetOrientation(core->Roll(1,MAX_ORIENT,-1), false);
1813 	} else {
1814 		actor->SetOrientation(parameters->int0Parameter, false);
1815 	}
1816 	actor->SetWait( 1 );
1817 	Sender->ReleaseCurrentAction(); // todo, blocking?
1818 }
1819 
FaceObject(Scriptable * Sender,Action * parameters)1820 void GameScript::FaceObject(Scriptable* Sender, Action* parameters)
1821 {
1822 	if (Sender->Type != ST_ACTOR) {
1823 		Sender->ReleaseCurrentAction();
1824 		return;
1825 	}
1826 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1827 	if (!target) {
1828 		Sender->ReleaseCurrentAction();
1829 		return;
1830 	}
1831 	Actor* actor = ( Actor* ) Sender;
1832 	actor->SetOrientation( GetOrient( target->Pos, actor->Pos ), false);
1833 	actor->SetWait( 1 );
1834 	Sender->ReleaseCurrentAction(); // todo, blocking?
1835 }
1836 
FaceSavedLocation(Scriptable * Sender,Action * parameters)1837 void GameScript::FaceSavedLocation(Scriptable* Sender, Action* parameters)
1838 {
1839 	Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] );
1840 	if (!target || target->Type!=ST_ACTOR) {
1841 		Sender->ReleaseCurrentAction();
1842 		return;
1843 	}
1844 	Actor* actor = ( Actor* ) target;
1845 	ieDword value;
1846 	if (!parameters->string0Parameter[0]) {
1847 		strcpy(parameters->string0Parameter,"LOCALSsavedlocation");
1848 	}
1849 	value = CheckVariable(target, parameters->string0Parameter);
1850 	Point p;
1851 	p.fromDword(value);
1852 
1853 	actor->SetOrientation ( GetOrient( p, actor->Pos ), false);
1854 	actor->SetWait( 1 );
1855 	Sender->ReleaseCurrentAction(); // todo, blocking?
1856 }
1857 
1858 //pst and bg2 can play a song designated by index
1859 //actually pst has some extra params not currently implemented (never used - always the same)
1860 //switchplaylist implements fade by simply scheduling the next
1861 //music after the currently running one
1862 //FIXME: This code is similar to PlayAreaSong, consider refactoring
StartSong(Scriptable *,Action * parameters)1863 void GameScript::StartSong(Scriptable* /*Sender*/, Action* parameters)
1864 {
1865 	//the force play logic should be handled by SwitchPlayList
1866 	bool force;
1867 	char* poi = core->GetMusicPlaylist( parameters->int0Parameter );
1868 	if (!poi) return;
1869 
1870 	//if parameter is force, force the music, otherwise just schedule it for next
1871 	if (parameters->int1Parameter==1) {
1872 		force = true;
1873 	} else {
1874 		force = false;
1875 	}
1876 	int ret = core->GetMusicMgr()->SwitchPlayList( poi, force );
1877 	if (ret) {
1878 		*poi = '*';
1879 	}
1880 }
1881 
1882 //starts the current area music (songtype is in int0Parameter)
1883 //PlayAreaSong will set the CombatCounter to 150 if
1884 //it is battlemusic (the Counter will tick back to 0)
StartMusic(Scriptable * Sender,Action * parameters)1885 void GameScript::StartMusic(Scriptable* Sender, Action* parameters)
1886 {
1887 	//don't break on bad values
1888 	if (parameters->int0Parameter >= 10) return;
1889 	const Map *map = Sender->GetCurrentArea();
1890 	if (!map) return;
1891 	bool force, restart;
1892 
1893 	// values from mflags.ids
1894 	// in the originals it's only used with QUICK_FADE, otherwise we'd have a few todos here
1895 	switch (parameters->int1Parameter) {
1896 	case 1: //force switch
1897 		force = true;
1898 		restart = true;
1899 		break;
1900 	case 3: // QUICK_FADE
1901 		//force switch, but wait for previous music to end gracefully
1902 		force = false;
1903 		restart = true;
1904 		break;
1905 	case 2: // play
1906 	case 4: // SLOW_FADE
1907 	default:
1908 		force = false;
1909 		restart = false;
1910 		break;
1911 	}
1912 	map->PlayAreaSong(parameters->int0Parameter, restart, force);
1913 }
1914 
StartCombatCounter(Scriptable * Sender,Action *)1915 void GameScript::StartCombatCounter(Scriptable* Sender, Action* /*parameters*/)
1916 {
1917 	const Map *map = Sender->GetCurrentArea();
1918 	if (!map) return;
1919 	map->PlayAreaSong(3, 1, 1);
1920 }
1921 
1922 /*iwd2 can set an areasong slot*/
SetMusic(Scriptable * Sender,Action * parameters)1923 void GameScript::SetMusic(Scriptable* Sender, Action* parameters)
1924 {
1925 	//iwd2 allows setting all 10 slots, though, there is no evidence they are used
1926 	if (parameters->int0Parameter >= 10) return;
1927 	Map *map = Sender->GetCurrentArea();
1928 	if (!map) return;
1929 	map->SongHeader.SongList[parameters->int0Parameter]=parameters->int1Parameter;
1930 }
1931 
1932 //optional integer parameter (isSpeech)
PlaySound(Scriptable * Sender,Action * parameters)1933 void GameScript::PlaySound(Scriptable* Sender, Action* parameters)
1934 {
1935 	Log(MESSAGE, "Actions", "PlaySound(%s)", parameters->string0Parameter);
1936 	core->GetAudioDrv()->Play(parameters->string0Parameter, SFX_CHAN_CHAR0, Sender->Pos.x,
1937 				Sender->Pos.y, parameters->int0Parameter ? GEM_SND_SPEECH : 0);
1938 }
1939 
PlaySoundPoint(Scriptable *,Action * parameters)1940 void GameScript::PlaySoundPoint(Scriptable* /*Sender*/, Action* parameters)
1941 {
1942 	Log(MESSAGE, "Actions", "PlaySound(%s)", parameters->string0Parameter);
1943 	core->GetAudioDrv()->Play(parameters->string0Parameter, SFX_CHAN_ACTIONS,
1944 		parameters->pointParameter.x, parameters->pointParameter.y);
1945 }
1946 
PlaySoundNotRanged(Scriptable *,Action * parameters)1947 void GameScript::PlaySoundNotRanged(Scriptable* /*Sender*/, Action* parameters)
1948 {
1949 	Log(MESSAGE, "Actions", "PlaySound(%s)", parameters->string0Parameter);
1950 	core->GetAudioDrv()->Play(parameters->string0Parameter, SFX_CHAN_ACTIONS, 0, 0, GEM_SND_RELATIVE);
1951 }
1952 
Continue(Scriptable *,Action *)1953 void GameScript::Continue(Scriptable* /*Sender*/, Action* /*parameters*/)
1954 {
1955 }
1956 
1957 // creates area vvc at position of object
CreateVisualEffectObject(Scriptable * Sender,Action * parameters)1958 void GameScript::CreateVisualEffectObject(Scriptable* Sender, Action* parameters)
1959 {
1960 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
1961 	if (!tar) {
1962 		return;
1963 	}
1964 	CreateVisualEffectCore(tar, tar->Pos, parameters->string0Parameter, parameters->int0Parameter);
1965 }
1966 
1967 // creates sticky vvc on actor or normal animation on object
CreateVisualEffectObjectSticky(Scriptable * Sender,Action * parameters)1968 void GameScript::CreateVisualEffectObjectSticky(Scriptable* Sender, Action* parameters)
1969 {
1970 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
1971 	if (!tar) {
1972 		return;
1973 	}
1974 	if (tar->Type==ST_ACTOR) {
1975 		CreateVisualEffectCore((Actor *) tar, parameters->string0Parameter, parameters->int0Parameter);
1976 	} else {
1977 		CreateVisualEffectCore(tar, tar->Pos, parameters->string0Parameter, parameters->int0Parameter);
1978 	}
1979 }
1980 
1981 // creates area effect at point
CreateVisualEffect(Scriptable * Sender,Action * parameters)1982 void GameScript::CreateVisualEffect(Scriptable* Sender, Action* parameters)
1983 {
1984 	CreateVisualEffectCore(Sender, parameters->pointParameter, parameters->string0Parameter, parameters->int0Parameter);
1985 }
1986 
DestroySelf(Scriptable * Sender,Action *)1987 void GameScript::DestroySelf(Scriptable* Sender, Action* /*parameters*/)
1988 {
1989 	if (Sender->Type != ST_ACTOR) {
1990 		return;
1991 	}
1992 	Sender->ClearActions();
1993 	Actor* actor = ( Actor* ) Sender;
1994 	actor->DestroySelf();
1995 	// needeed in pst #532, but softly breaks bg2 #1179
1996 	if (actor == core->GetCutSceneRunner() && core->HasFeature(GF_PST_STATE_FLAGS)) {
1997 		core->SetCutSceneMode(false);
1998 	}
1999 }
2000 
ScreenShake(Scriptable * Sender,Action * parameters)2001 void GameScript::ScreenShake(Scriptable* Sender, Action* parameters)
2002 {
2003 	if (parameters->int1Parameter) { //IWD2 has a different profile
2004 		Point p(parameters->int1Parameter, parameters->int2Parameter);
2005 		core->timer.SetScreenShake(p, parameters->int0Parameter);
2006 	} else {
2007 		core->timer.SetScreenShake(parameters->pointParameter, parameters->int0Parameter);
2008 	}
2009 	Sender->SetWait( parameters->int0Parameter );
2010 	Sender->ReleaseCurrentAction(); // todo, blocking?
2011 }
2012 
UnhideGUI(Scriptable *,Action *)2013 void GameScript::UnhideGUI(Scriptable* /*Sender*/, Action* /*parameters*/)
2014 {
2015 	Game* game = core->GetGame();
2016 	game->SetControlStatus(CS_HIDEGUI, OP_NAND);
2017 }
2018 
HideGUI(Scriptable *,Action *)2019 void GameScript::HideGUI(Scriptable* /*Sender*/, Action* /*parameters*/)
2020 {
2021 	Game* game = core->GetGame();
2022 	game->SetControlStatus(CS_HIDEGUI, OP_OR);
2023 }
2024 
LockScroll(Scriptable *,Action *)2025 void GameScript::LockScroll(Scriptable* /*Sender*/, Action* /*parameters*/)
2026 {
2027 	GameControl* gc = core->GetGameControl();
2028 	if (gc) {
2029 		gc->SetScreenFlags(SF_CENTERONACTOR|SF_ALWAYSCENTER, OP_OR);
2030 	}
2031 }
2032 
UnlockScroll(Scriptable *,Action *)2033 void GameScript::UnlockScroll(Scriptable* /*Sender*/, Action* /*parameters*/)
2034 {
2035 	GameControl* gc = core->GetGameControl();
2036 	if (gc) {
2037 		gc->SetScreenFlags(SF_CENTERONACTOR|SF_ALWAYSCENTER, OP_NAND);
2038 	}
2039 }
2040 
2041 //no string, increase talkcount, no interrupt
Dialogue(Scriptable * Sender,Action * parameters)2042 void GameScript::Dialogue(Scriptable* Sender, Action* parameters)
2043 {
2044 	BeginDialog( Sender, parameters, BD_SOURCE | BD_TALKCOUNT | BD_CHECKDIST );
2045 }
2046 
DialogueForceInterrupt(Scriptable * Sender,Action * parameters)2047 void GameScript::DialogueForceInterrupt(Scriptable* Sender, Action* parameters)
2048 {
2049 	BeginDialog( Sender, parameters, BD_SOURCE | BD_TALKCOUNT | BD_INTERRUPT );
2050 }
2051 
2052 // not in IESDP but this one should affect ambients
SoundActivate(Scriptable *,Action * parameters)2053 void GameScript::SoundActivate(Scriptable* /*Sender*/, Action* parameters)
2054 {
2055 	AmbientMgr * ambientmgr = core->GetAudioDrv()->GetAmbientMgr();
2056 	if (parameters->int0Parameter) {
2057 		ambientmgr->activate(parameters->objects[1]->objectName);
2058 	} else {
2059 		ambientmgr->deactivate(parameters->objects[1]->objectName);
2060 	}
2061 }
2062 
2063 // according to IESDP this action is about animations
2064 //PST's SetCorpseEnabled also handles containers, but no one uses it
AmbientActivate(Scriptable * Sender,Action * parameters)2065 void GameScript::AmbientActivate(Scriptable* Sender, Action* parameters)
2066 {
2067 	AmbientActivateCore(Sender, parameters, parameters->int0Parameter);
2068 }
2069 
ChangeTileState(Scriptable * Sender,Action * parameters)2070 void GameScript::ChangeTileState(Scriptable* Sender, Action* parameters)
2071 {
2072 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
2073 	if (!tar) {
2074 		return;
2075 	}
2076 	if (tar->Type != ST_DOOR) {
2077 		return;
2078 	}
2079 	Door* door = ( Door* ) tar;
2080 	int state = parameters->int0Parameter;
2081 	if(door) {
2082 		door->ToggleTiles(state); /* default is false for playsound */
2083 	}
2084 }
2085 
StaticStart(Scriptable * Sender,Action * parameters)2086 void GameScript::StaticStart(Scriptable* Sender, Action* parameters)
2087 {
2088 	AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName);
2089 	if (!anim) {
2090 		Log(WARNING, "Actions", "Script error: No Animation Named \"%s\"",
2091 			parameters->objects[1]->objectName );
2092 		return;
2093 	}
2094 	anim->Flags &=~A_ANI_PLAYONCE;
2095 }
2096 
StaticStop(Scriptable * Sender,Action * parameters)2097 void GameScript::StaticStop(Scriptable* Sender, Action* parameters)
2098 {
2099 	AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName);
2100 	if (!anim) {
2101 		Log(WARNING, "Actions", "Script error: No Animation Named \"%s\"",
2102 			parameters->objects[1]->objectName );
2103 		return;
2104 	}
2105 	anim->Flags |= A_ANI_PLAYONCE;
2106 }
2107 
StaticPalette(Scriptable * Sender,Action * parameters)2108 void GameScript::StaticPalette(Scriptable* Sender, Action* parameters)
2109 {
2110 	AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName);
2111 	if (!anim) {
2112 		Log(WARNING, "Actions", "Script error: No Animation Named \"%s\"",
2113 			parameters->objects[1]->objectName );
2114 		return;
2115 	}
2116 	anim->SetPalette( parameters->string0Parameter );
2117 }
2118 
2119 //this is a special case of PlaySequence (with wait time, not for area anims)
PlaySequenceTimed(Scriptable * Sender,Action * parameters)2120 void GameScript::PlaySequenceTimed(Scriptable* Sender, Action* parameters)
2121 {
2122 	Scriptable* tar;
2123 	if (parameters->objects[1]) {
2124 		tar = GetActorFromObject( Sender, parameters->objects[1] );
2125 	} else {
2126 		tar=Sender;
2127 	}
2128 	if (!tar || tar->Type != ST_ACTOR) {
2129 		return;
2130 	}
2131 	Actor* actor = ( Actor* ) tar;
2132 	actor->SetStance( parameters->int0Parameter );
2133 	int delay = parameters->int1Parameter;
2134 	actor->SetWait( delay );
2135 }
2136 
2137 //waitanimation: waiting while animation of target is of a certain type
WaitAnimation(Scriptable * Sender,Action * parameters)2138 void GameScript::WaitAnimation(Scriptable* Sender, Action* parameters)
2139 {
2140 	Scriptable *tar = GetActorFromObject( Sender, parameters->objects[1] );
2141 	if (!tar) {
2142 		tar=Sender;
2143 	}
2144 	if (tar->Type != ST_ACTOR) {
2145 		return;
2146 	}
2147 	Actor* actor = ( Actor* ) tar;
2148 	// HACK HACK: avoid too long waits due to buggy AI evaluation
2149 	if (actor->GetStance() != parameters->int0Parameter || parameters->int1Parameter > (signed)core->Time.round_size) {
2150 		Sender->ReleaseCurrentAction();
2151 		return;
2152 	}
2153 	parameters->int1Parameter++;
2154 }
2155 
2156 // the spell target and attack target are different only in iwd2
SetMyTarget(Scriptable * Sender,Action * parameters)2157 void GameScript::SetMyTarget(Scriptable* Sender, Action* parameters)
2158 {
2159 	Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1]);
2160 	if (!tar) {
2161 		// we got called with Nothing to invalidate the target
2162 		Sender->MyTarget = 0;
2163 		return;
2164 	}
2165 	Sender->MyTarget = tar->GetGlobalID();
2166 }
2167 
2168 // PlaySequence without object parameter defaults to Sender
PlaySequence(Scriptable * Sender,Action * parameters)2169 void GameScript::PlaySequence(Scriptable* Sender, Action* parameters)
2170 {
2171 	PlaySequenceCore(Sender, parameters, parameters->int0Parameter);
2172 }
2173 
2174 //same as PlaySequence, but the value comes from a variable
PlaySequenceGlobal(Scriptable * Sender,Action * parameters)2175 void GameScript::PlaySequenceGlobal(Scriptable* Sender, Action* parameters)
2176 {
2177 	ieDword value = CheckVariable(Sender, parameters->string0Parameter);
2178 	PlaySequenceCore(Sender, parameters, value);
2179 }
2180 
SetDialogue(Scriptable * Sender,Action * parameters)2181 void GameScript::SetDialogue(Scriptable* Sender, Action* parameters)
2182 {
2183 	if (Sender->Type != ST_ACTOR) {
2184 		return;
2185 	}
2186 	Actor* target = ( Actor* ) Sender;
2187 	target->SetDialog( parameters->string0Parameter );
2188 }
2189 
ChangeDialogue(Scriptable * Sender,Action * parameters)2190 void GameScript::ChangeDialogue(Scriptable* Sender, Action* parameters)
2191 {
2192 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2193 	if (!tar) {
2194 		return;
2195 	}
2196 	if (tar->Type != ST_ACTOR) {
2197 		return;
2198 	}
2199 	Actor* target = ( Actor* ) tar;
2200 	target->SetDialog( parameters->string0Parameter );
2201 }
2202 
2203 //string0, no interrupt, talkcount increased
StartDialogue(Scriptable * Sender,Action * parameters)2204 void GameScript::StartDialogue(Scriptable* Sender, Action* parameters)
2205 {
2206 	BeginDialog( Sender, parameters, BD_STRING0 | BD_TALKCOUNT | BD_SETDIALOG );
2207 }
2208 
2209 //string0, no interrupt, talkcount increased, don't set default
2210 //optionally item name is used
StartDialogueOverride(Scriptable * Sender,Action * parameters)2211 void GameScript::StartDialogueOverride(Scriptable* Sender, Action* parameters)
2212 {
2213 	int flags = BD_STRING0 | BD_TALKCOUNT;
2214 
2215 	if (parameters->int2Parameter) {
2216 		flags|=BD_ITEM;
2217 	}
2218 	BeginDialog( Sender, parameters, flags );
2219 }
2220 
2221 //string0, no interrupt, talkcount increased, don't set default
2222 //optionally item name is used
StartDialogueOverrideInterrupt(Scriptable * Sender,Action * parameters)2223 void GameScript::StartDialogueOverrideInterrupt(Scriptable* Sender,
2224 	Action* parameters)
2225 {
2226 	int flags = BD_STRING0 | BD_TALKCOUNT | BD_INTERRUPT;
2227 
2228 	if (parameters->int2Parameter) {
2229 		flags|=BD_ITEM;
2230 	}
2231 	BeginDialog( Sender, parameters, flags );
2232 }
2233 
2234 //start talking to oneself
PlayerDialogue(Scriptable * Sender,Action * parameters)2235 void GameScript::PlayerDialogue(Scriptable* Sender, Action* parameters)
2236 {
2237 	BeginDialog( Sender, parameters, BD_RESERVED | BD_OWN );
2238 }
2239 
2240 //we hijack this action for the player initiated dialogue
NIDSpecial1(Scriptable * Sender,Action * parameters)2241 void GameScript::NIDSpecial1(Scriptable* Sender, Action* parameters)
2242 {
2243 	BeginDialog( Sender, parameters, BD_INTERRUPT | BD_TARGET /*| BD_NUMERIC*/ | BD_TALKCOUNT | BD_CHECKDIST );
2244 }
2245 
NIDSpecial2(Scriptable * Sender,Action *)2246 void GameScript::NIDSpecial2(Scriptable* Sender, Action* /*parameters*/)
2247 {
2248 	if (Sender->Type != ST_ACTOR) {
2249 		Sender->ReleaseCurrentAction();
2250 		return;
2251 	}
2252 	Game *game=core->GetGame();
2253 	if (!game->EveryoneStopped() ) {
2254 		//wait for a while
2255 		Sender->SetWait( 1 * AI_UPDATE_TIME );
2256 		return;
2257 	}
2258 	Actor *actor = (Actor *) Sender;
2259 	if (!game->EveryoneNearPoint(actor->GetCurrentArea(), actor->Pos, ENP_CANMOVE) ) {
2260 		//we abort the command, everyone should be here
2261 		Sender->ReleaseCurrentAction();
2262 		return;
2263 	}
2264 
2265 	//travel direction passed to guiscript
2266 	int direction = Sender->GetCurrentArea()->WhichEdge(actor->Pos);
2267 	Log(MESSAGE, "Actions", "Travel direction returned: %d", direction);
2268 	//this is notoriously flaky
2269 	//if it doesn't work for the sender try other party members, too
2270 	if (direction == -1) {
2271 		int i, best, directions[4] = { -1, -1, -1, -1 };
2272 		for (i = 0; i < game->GetPartySize(false); i++) {
2273 			actor = game->GetPC(i, false);
2274 			if (actor != Sender) {
2275 				int partydir = actor->GetCurrentArea()->WhichEdge(actor->Pos);
2276 				if (partydir != -1) {
2277 					directions[partydir]++;
2278 				}
2279 			}
2280 		}
2281 		best = 0;
2282 		for (i = 1; i < 4; ++i) {
2283 			if (directions[i] > directions[best]) {
2284 				best = i;
2285 			}
2286 		}
2287 		if (directions[best] != -1) {
2288 			direction = best;
2289 		}
2290 		Log(DEBUG, "Actions", "Travel direction determined by party: %d", direction);
2291 	}
2292 
2293 	// pst enables worldmap travel only after visiting the lower ward
2294 	bool keyAreaVisited = core->HasFeature(GF_TEAM_MOVEMENT) && CheckVariable(Sender, "AR0500_Visited", "GLOBAL") == 1;
2295 	if (direction == -1 && !keyAreaVisited) {
2296 		Sender->ReleaseCurrentAction();
2297 		return;
2298 	}
2299 	if (direction == -1 && keyAreaVisited) {
2300 		// FIXME: not ideal, pst uses the infopoint links (ip->EntranceName), so direction doesn't matter
2301 		// but we're not travelling through them (the whole point of the world map), so how to pick a good entrance?
2302 		// DestEntryPoint is all zeroes, pst just didn't use it
2303 		direction = 1;
2304 	}
2305 	core->GetDictionary()->SetAt("Travel", (ieDword) direction);
2306 	core->GetGUIScriptEngine()->RunFunction( "GUIMA", "OpenTravelWindow" );
2307 	//sorry, i have absolutely no idea when i should do this :)
2308 	Sender->ReleaseCurrentAction();
2309 }
2310 
StartDialogueInterrupt(Scriptable * Sender,Action * parameters)2311 void GameScript::StartDialogueInterrupt(Scriptable* Sender, Action* parameters)
2312 {
2313 	BeginDialog( Sender, parameters,
2314 		BD_STRING0 | BD_INTERRUPT | BD_TALKCOUNT | BD_SETDIALOG );
2315 }
2316 
2317 //No string, flags:0
StartDialogueNoSet(Scriptable * Sender,Action * parameters)2318 void GameScript::StartDialogueNoSet(Scriptable* Sender, Action* parameters)
2319 {
2320 	BeginDialog( Sender, parameters, BD_TALKCOUNT | BD_SOURCE );
2321 }
2322 
StartDialogueNoSetInterrupt(Scriptable * Sender,Action * parameters)2323 void GameScript::StartDialogueNoSetInterrupt(Scriptable* Sender,
2324 	Action* parameters)
2325 {
2326 	BeginDialog( Sender, parameters, BD_TALKCOUNT | BD_SOURCE | BD_INTERRUPT );
2327 }
2328 
2329 //no talkcount, using banter dialogs
2330 //probably banter dialogs are random, like rumours!
2331 //no, they aren't, but they increase interactcount
Interact(Scriptable * Sender,Action * parameters)2332 void GameScript::Interact(Scriptable* Sender, Action* parameters)
2333 {
2334 	BeginDialog( Sender, parameters, BD_INTERACT | BD_NOEMPTY );
2335 }
2336 
FindNearPoint(Scriptable * Sender,Point * & p1,Point * & p2)2337 static unsigned int FindNearPoint(Scriptable* Sender, Point *&p1, Point *&p2)
2338 {
2339 	unsigned int distance1 = Distance(*p1, Sender);
2340 	unsigned int distance2 = Distance(*p2, Sender);
2341 	if (distance1 <= distance2) {
2342 		return distance1;
2343 	} else {
2344 		Point *tmp = p1;
2345 		p1 = p2;
2346 		p2 = tmp;
2347 		return distance2;
2348 	}
2349 }
2350 
2351 //this is an immediate action without checking Sender
DetectSecretDoor(Scriptable * Sender,Action * parameters)2352 void GameScript::DetectSecretDoor(Scriptable* Sender, Action* parameters)
2353 {
2354 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
2355 	if (!tar) {
2356 		return;
2357 	}
2358 	if (tar->Type != ST_DOOR) {
2359 		return;
2360 	}
2361 	Door* door = ( Door* ) tar;
2362 	if (door->Flags & DOOR_SECRET) {
2363 		door->Flags |= DOOR_FOUND;
2364 	}
2365 }
2366 
2367 //this is an immediate action without checking Sender
Lock(Scriptable * Sender,Action * parameters)2368 void GameScript::Lock(Scriptable* Sender, Action* parameters)
2369 {
2370 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2371 	if (!tar) {
2372 		return;
2373 	}
2374 	switch (tar->Type) {
2375 		case ST_DOOR:
2376 			((Door *)tar)->SetDoorLocked(true, true);
2377 			break;
2378 		case ST_CONTAINER:
2379 			((Container *)tar)->SetContainerLocked(true);
2380 			break;
2381 		default:
2382 			return;
2383 	}
2384 }
2385 
Unlock(Scriptable * Sender,Action * parameters)2386 void GameScript::Unlock(Scriptable* Sender, Action* parameters)
2387 {
2388 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2389 	if (!tar) {
2390 		return;
2391 	}
2392 	switch (tar->Type) {
2393 		case ST_DOOR:
2394 			((Door *)tar)->SetDoorLocked(false, true);
2395 			break;
2396 		case ST_CONTAINER:
2397 			((Container *)tar)->SetContainerLocked(false);
2398 			break;
2399 		default:
2400 			return;
2401 	}
2402 }
2403 
SetDoorLocked(Scriptable * Sender,Action * parameters)2404 void GameScript::SetDoorLocked(Scriptable* Sender, Action* parameters)
2405 {
2406 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2407 	if (!tar) {
2408 		return;
2409 	}
2410 	if (tar->Type != ST_DOOR) {
2411 		return;
2412 	}
2413 	// two dialog states in pst (and nothing else) use "FALSE" (yes, quoted)
2414 	// they're on a critical path so we have to handle this data bug ourselves
2415 	if (parameters->int0Parameter == -1) {
2416 		parameters->int0Parameter = 0;
2417 	}
2418 	Door* door = ( Door* ) tar;
2419 	door->SetDoorLocked( parameters->int0Parameter!=0, false);
2420 }
2421 
SetDoorFlag(Scriptable * Sender,Action * parameters)2422 void GameScript::SetDoorFlag(Scriptable* Sender, Action* parameters)
2423 {
2424 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2425 	if (!tar) {
2426 		return;
2427 	}
2428 	if (tar->Type != ST_DOOR) {
2429 		return;
2430 	}
2431 	Door* door = ( Door* ) tar;
2432 	ieDword flag = parameters->int0Parameter;
2433 
2434 	//these are special flags
2435 	if (flag&DOOR_LOCKED) {
2436 		flag&=~DOOR_LOCKED;
2437 		door->SetDoorLocked(parameters->int1Parameter!=0, false);
2438 	}
2439 	if (flag&DOOR_OPEN) {
2440 		flag&=~DOOR_OPEN;
2441 		door->SetDoorOpen(parameters->int1Parameter!=0, false, 0);
2442 	}
2443 
2444 	if (parameters->int1Parameter) {
2445 		door->Flags|=flag;
2446 	} else {
2447 		door->Flags&=~flag;
2448 	}
2449 }
2450 
RemoveTraps(Scriptable * Sender,Action * parameters)2451 void GameScript::RemoveTraps(Scriptable* Sender, Action* parameters)
2452 {
2453 	//only actors may try to pick a lock
2454 	if (Sender->Type != ST_ACTOR) {
2455 		Sender->ReleaseCurrentAction();
2456 		return;
2457 	}
2458 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2459 	if (!tar) {
2460 		Sender->ReleaseCurrentAction();
2461 		return;
2462 	}
2463 	Actor * actor = (Actor *) Sender;
2464 	unsigned int distance;
2465 	Point *p, *otherp;
2466 	Door *door = NULL;
2467 	Container *container = NULL;
2468 	InfoPoint *trigger = NULL;
2469 	ScriptableType type = tar->Type;
2470 	ieDword flags;
2471 
2472 	switch (type) {
2473 	case ST_DOOR:
2474 		door = ( Door* ) tar;
2475 		if (door->IsOpen()) {
2476 			//door is already open
2477 			Sender->ReleaseCurrentAction();
2478 			return;
2479 		}
2480 		p = door->toOpen;
2481 		otherp = door->toOpen+1;
2482 		distance = FindNearPoint( Sender, p, otherp);
2483 		flags = door->Trapped && door->TrapDetected;
2484 		break;
2485 	case ST_CONTAINER:
2486 		container = (Container *) tar;
2487 		p = &container->Pos;
2488 		otherp = p;
2489 		distance = Distance(*p, Sender);
2490 		flags = container->Trapped && container->TrapDetected;
2491 		break;
2492 	case ST_PROXIMITY:
2493 		trigger = (InfoPoint *) tar;
2494 		// this point is incorrect! will cause actor to enter trap
2495 		// need to find a point using trigger->outline
2496 		p = &trigger->Pos;
2497 		otherp = p;
2498 		distance = Distance(tar, Sender);
2499 		flags = trigger->Trapped && trigger->TrapDetected && trigger->CanDetectTrap();
2500 		actor->SetDisarmingTrap(trigger->GetGlobalID());
2501 		break;
2502 	default:
2503 		Sender->ReleaseCurrentAction();
2504 		return;
2505 	}
2506 	actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false);
2507 	if (distance <= MAX_OPERATING_DISTANCE) {
2508 		if (flags) {
2509 			switch(type) {
2510 			case ST_DOOR:
2511 				door->TryDisarm(actor);
2512 				break;
2513 			case ST_CONTAINER:
2514 				container->TryDisarm(actor);
2515 				break;
2516 			case ST_PROXIMITY:
2517 				trigger->TryDisarm(actor);
2518 				break;
2519 			default:
2520 				//not gonna happen!
2521 				assert(false);
2522 			}
2523 		} else {
2524 			//no trap here
2525 			//displaymsg->DisplayString(STR_NOT_TRAPPED);
2526 		}
2527 	} else {
2528 		MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0);
2529 		return;
2530 	}
2531 	Sender->SetWait(1);
2532 	Sender->ReleaseCurrentAction();
2533 }
2534 
PickLock(Scriptable * Sender,Action * parameters)2535 void GameScript::PickLock(Scriptable* Sender, Action* parameters)
2536 {
2537 	//only actors may try to pick a lock
2538 	if (Sender->Type != ST_ACTOR) {
2539 		Sender->ReleaseCurrentAction();
2540 		return;
2541 	}
2542 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2543 	if (!tar) {
2544 		Sender->ReleaseCurrentAction();
2545 		return;
2546 	}
2547 	unsigned int distance;
2548 	Point *p, *otherp;
2549 	Door *door = NULL;
2550 	Container *container = NULL;
2551 	ScriptableType type = tar->Type;
2552 	ieDword flags;
2553 
2554 	switch (type) {
2555 	case ST_DOOR:
2556 		door = ( Door* ) tar;
2557 		if (door->IsOpen()) {
2558 			//door is already open
2559 			Sender->ReleaseCurrentAction();
2560 			return;
2561 		}
2562 		p = door->toOpen;
2563 		otherp = door->toOpen+1;
2564 		distance = FindNearPoint( Sender, p, otherp);
2565 		flags = door->Flags&DOOR_LOCKED;
2566 		break;
2567 	case ST_CONTAINER:
2568 		container = (Container *) tar;
2569 		p = &container->Pos;
2570 		otherp = p;
2571 		distance = Distance(*p, Sender);
2572 		flags = container->Flags&CONT_LOCKED;
2573 		break;
2574 	default:
2575 		Sender->ReleaseCurrentAction();
2576 		return;
2577 	}
2578 	Actor * actor = (Actor *) Sender;
2579 	actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false);
2580 	if (distance <= MAX_OPERATING_DISTANCE) {
2581 		if (flags) {
2582 			if (type==ST_DOOR) {
2583 				door->TryPickLock(actor);
2584 			} else {
2585 				container->TryPickLock(actor);
2586 			}
2587 		} else {
2588 			//notlocked
2589 			//displaymsg->DisplayString(STR_NOT_LOCKED);
2590 		}
2591 	} else {
2592 		MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0);
2593 		return;
2594 	}
2595 	Sender->SetWait(1);
2596 	Sender->ReleaseCurrentAction();
2597 }
2598 
OpenDoor(Scriptable * Sender,Action * parameters)2599 void GameScript::OpenDoor(Scriptable* Sender, Action* parameters) {
2600 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2601 	if (!tar) {
2602 		return;
2603 	}
2604 	if (tar->Type != ST_DOOR) {
2605 		return;
2606 	}
2607 	Door* door = ( Door* ) tar;
2608 	int gid = Sender->GetGlobalID();
2609 	// no idea if this is right, or whether OpenDoor/CloseDoor should allow opening
2610 	// of all doors, or some doors, or whether it should still check for non-actors
2611 	if (Sender->Type == ST_ACTOR) {
2612 		Actor *actor = (Actor *)Sender;
2613 		actor->SetModal(MS_NONE);
2614 		if (!door->TryUnlock(actor)) {
2615 			return;
2616 		}
2617 	}
2618 	//if not an actor opens, it don't play sound
2619 	door->SetDoorOpen(true, (Sender->Type == ST_ACTOR), gid, false);
2620 	Sender->ReleaseCurrentAction();
2621 }
2622 
CloseDoor(Scriptable * Sender,Action * parameters)2623 void GameScript::CloseDoor(Scriptable* Sender, Action* parameters) {
2624 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2625 	if (!tar) {
2626 		return;
2627 	}
2628 	if (tar->Type != ST_DOOR) {
2629 		return;
2630 	}
2631 	Door* door = ( Door* ) tar;
2632 	// see comments in OpenDoor above
2633 	if (Sender->Type == ST_ACTOR) {
2634 		Actor *actor = (Actor *)Sender;
2635 		if (!door->TryUnlock(actor)) {
2636 			return;
2637 		}
2638 	}
2639 	//if not an actor closes, it don't play sound
2640 	door->SetDoorOpen( false, (Sender->Type == ST_ACTOR), 0 );
2641 	Sender->ReleaseCurrentAction();
2642 }
2643 
ToggleDoor(Scriptable * Sender,Action *)2644 void GameScript::ToggleDoor(Scriptable* Sender, Action* /*parameters*/)
2645 {
2646 	if (Sender->Type != ST_ACTOR) {
2647 		Sender->ReleaseCurrentAction();
2648 		return;
2649 	}
2650 	Actor *actor = (Actor *) Sender;
2651 	actor->SetModal(MS_NONE);
2652 
2653 	Door* door = actor->GetCurrentArea()->GetDoorByGlobalID(actor->TargetDoor);
2654 	if (!door) {
2655 		Sender->ReleaseCurrentAction();
2656 		return;
2657 	}
2658 	unsigned int distance;
2659 	Point *p = door->toOpen;
2660 	Point *otherp = door->toOpen+1;
2661 	distance = FindNearPoint( Sender, p, otherp);
2662 	if (distance <= MAX_OPERATING_DISTANCE) {
2663 		actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false);
2664 		if (!door->TryUnlock(actor)) {
2665 			displaymsg->DisplayConstantString(STR_DOORLOCKED, DMC_LIGHTGREY, door);
2666 			door->AddTrigger(TriggerEntry(trigger_failedtoopen, actor->GetGlobalID()));
2667 
2668 			//playsound unsuccessful opening of door
2669 			core->PlaySound(door->IsOpen() ? DS_CLOSE_FAIL : DS_OPEN_FAIL, SFX_CHAN_ACTIONS);
2670 			Sender->ReleaseCurrentAction();
2671 			actor->TargetDoor = 0;
2672 			return; //don't open door
2673 		}
2674 
2675 		//trap scripts are triggered by SetDoorOpen
2676 		door->SetDoorOpen( !door->IsOpen(), true, actor->GetGlobalID() );
2677 	} else {
2678 		MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0);
2679 		return;
2680 	}
2681 	Sender->SetWait(1);
2682 	Sender->ReleaseCurrentAction();
2683 	actor->TargetDoor = 0;
2684 }
2685 
ContainerEnable(Scriptable * Sender,Action * parameters)2686 void GameScript::ContainerEnable(Scriptable* Sender, Action* parameters)
2687 {
2688 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2689 	if (!tar || tar->Type!=ST_CONTAINER) {
2690 		return;
2691 	}
2692 	Container *cnt = (Container *) tar;
2693 	if (parameters->int0Parameter) {
2694 		cnt->Flags&=~CONT_DISABLED;
2695 	} else {
2696 		cnt->Flags|=CONT_DISABLED;
2697 	}
2698 }
2699 
MoveBetweenAreas(Scriptable * Sender,Action * parameters)2700 void GameScript::MoveBetweenAreas(Scriptable* Sender, Action* parameters)
2701 {
2702 	if (Sender->Type != ST_ACTOR) {
2703 		return;
2704 	}
2705 	if (parameters->string1Parameter[0]) {
2706 		CreateVisualEffectCore(Sender, Sender->Pos, parameters->string1Parameter, 0);
2707 	}
2708 
2709 	Actor *actor = (Actor *) Sender;
2710 	if (actor->Persistent() || !CreateMovementEffect(actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter) ) {
2711 		MoveBetweenAreasCore(actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true);
2712 	}
2713 }
2714 
2715 //spell is depleted, casting time is calculated, interruptible
2716 //FIXME The caster must meet the level requirements as set in the spell file
Spell(Scriptable * Sender,Action * parameters)2717 void GameScript::Spell(Scriptable* Sender, Action* parameters)
2718 {
2719 	SpellCore(Sender, parameters, SC_NO_DEAD|SC_RANGE_CHECK|SC_DEPLETE|SC_AURA_CHECK);
2720 }
2721 
2722 //spell is depleted, casting time is calculated, interruptible
2723 //FIXME The caster must meet the level requirements as set in the spell file
SpellPoint(Scriptable * Sender,Action * parameters)2724 void GameScript::SpellPoint(Scriptable* Sender, Action* parameters)
2725 {
2726 	SpellPointCore(Sender, parameters, SC_RANGE_CHECK|SC_DEPLETE|SC_AURA_CHECK);
2727 }
2728 
2729 //spell is not depleted (doesn't need to be memorised or known)
2730 //casting time is calculated, interruptible
2731 //FIXME The caster must meet the level requirements as set in the spell file
SpellNoDec(Scriptable * Sender,Action * parameters)2732 void GameScript::SpellNoDec(Scriptable* Sender, Action* parameters)
2733 {
2734 	SpellCore(Sender, parameters, SC_NO_DEAD|SC_RANGE_CHECK|SC_AURA_CHECK);
2735 }
2736 
2737 //spell is not depleted (doesn't need to be memorised or known)
2738 //casting time is calculated, interruptible
2739 //FIXME The caster must meet the level requirements as set in the spell file
SpellPointNoDec(Scriptable * Sender,Action * parameters)2740 void GameScript::SpellPointNoDec(Scriptable* Sender, Action* parameters)
2741 {
2742 	SpellPointCore(Sender, parameters, SC_RANGE_CHECK|SC_AURA_CHECK);
2743 }
2744 
2745 //spell is not depleted (doesn't need to be memorised or known)
2746 //casting time is calculated, not interruptable
2747 //FIXME The caster must meet the level requirements as set in the spell file
ForceSpell(Scriptable * Sender,Action * parameters)2748 void GameScript::ForceSpell(Scriptable* Sender, Action* parameters)
2749 {
2750 	SpellCore(Sender, parameters, SC_NOINTERRUPT);
2751 }
2752 
ForceSpellRange(Scriptable * Sender,Action * parameters)2753 void GameScript::ForceSpellRange(Scriptable* Sender, Action* parameters)
2754 {
2755 	SpellCore(Sender, parameters, SC_NOINTERRUPT|SC_RANGE_CHECK);
2756 }
2757 
2758 //spell is not depleted (doesn't need to be memorised or known)
2759 //casting time is calculated, not interruptable
2760 //FIXME The caster must meet the level requirements as set in the spell file
ForceSpellPoint(Scriptable * Sender,Action * parameters)2761 void GameScript::ForceSpellPoint(Scriptable* Sender, Action* parameters)
2762 {
2763 	SpellPointCore(Sender, parameters, SC_NOINTERRUPT);
2764 }
2765 
ForceSpellPointRange(Scriptable * Sender,Action * parameters)2766 void GameScript::ForceSpellPointRange(Scriptable* Sender, Action* parameters)
2767 {
2768 	SpellPointCore(Sender, parameters, SC_NOINTERRUPT|SC_RANGE_CHECK);
2769 }
2770 
2771 //ForceSpell with zero casting time
2772 //zero casting time, no depletion, not interruptable
2773 //FIXME The caster must meet the level requirements as set in the spell file
ReallyForceSpell(Scriptable * Sender,Action * parameters)2774 void GameScript::ReallyForceSpell(Scriptable* Sender, Action* parameters)
2775 {
2776 	SpellCore(Sender, parameters, SC_NOINTERRUPT|SC_SETLEVEL|SC_INSTANT);
2777 }
2778 
2779 //ForceSpellPoint with zero casting time
2780 //zero casting time, no depletion (finish casting at point), not interruptable
2781 //no CFB
2782 //FIXME The caster must meet the level requirements as set in the spell file
ReallyForceSpellPoint(Scriptable * Sender,Action * parameters)2783 void GameScript::ReallyForceSpellPoint(Scriptable* Sender, Action* parameters)
2784 {
2785 	SpellPointCore(Sender, parameters, SC_NOINTERRUPT|SC_SETLEVEL|SC_INSTANT);
2786 }
2787 
2788 // this differs from ReallyForceSpell that this one allows dead Sender casting
2789 // zero casting time, no depletion
ReallyForceSpellDead(Scriptable * Sender,Action * parameters)2790 void GameScript::ReallyForceSpellDead(Scriptable* Sender, Action* parameters)
2791 {
2792 	// the difference from ReallyForceSpell is handled by the lack of AF_ALIVE being set
2793 	SpellCore(Sender, parameters, SC_NOINTERRUPT|SC_SETLEVEL|SC_INSTANT);
2794 }
2795 
Activate(Scriptable * Sender,Action * parameters)2796 void GameScript::Activate(Scriptable* Sender, Action* parameters)
2797 {
2798 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2799 	if (!tar) {
2800 		//it could still be an area animation, PST allows deactivating them via Activate
2801 		AmbientActivateCore(Sender, parameters, 1);
2802 		return;
2803 	}
2804 	if (tar->Type == ST_ACTOR) {
2805 		tar->Unhide();
2806 		return;
2807 	}
2808 
2809 	//PST allows activating of containers
2810 	if (tar->Type == ST_CONTAINER) {
2811 		((Container *) tar)->Flags&=~CONT_DISABLED;
2812 		return;
2813 	}
2814 
2815 	//and regions
2816 	if (tar->Type == ST_PROXIMITY || tar->Type == ST_TRAVEL || tar->Type==ST_TRIGGER) {
2817 		((InfoPoint *) tar)->Flags&=~TRAP_DEACTIVATED;
2818 		return;
2819 	}
2820 }
2821 
Deactivate(Scriptable * Sender,Action * parameters)2822 void GameScript::Deactivate(Scriptable* Sender, Action* parameters)
2823 {
2824 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2825 	if (!tar) {
2826 		//it could still be an area animation, PST allows deactivating them via Deactivate
2827 		AmbientActivateCore(Sender, parameters, 0);
2828 		return;
2829 	}
2830 	if (tar->Type == ST_ACTOR) {
2831 		tar->Hide();
2832 		return;
2833 	}
2834 	//PST allows deactivating of containers
2835 	//but IWD doesn't, ar9705 chests rely on it (if this is changed, make sure they are all still selectable!)
2836 	//FIXME: add a new game flag / differentiate more container flags
2837 	if (tar->Type == ST_CONTAINER && !core->HasFeature(GF_SPECIFIC_DMG_BONUS)) {
2838 		((Container *) tar)->Flags|=CONT_DISABLED;
2839 		return;
2840 	}
2841 
2842 	//and regions
2843 	if (tar->Type == ST_PROXIMITY || tar->Type == ST_TRAVEL || tar->Type==ST_TRIGGER) {
2844 		((InfoPoint *) tar)->Flags|=TRAP_DEACTIVATED;
2845 		return;
2846 	}
2847 }
2848 
MakeGlobal(Scriptable * Sender,Action *)2849 void GameScript::MakeGlobal(Scriptable* Sender, Action* /*parameters*/)
2850 {
2851 	if (Sender->Type != ST_ACTOR) {
2852 		return;
2853 	}
2854 	Actor* act = ( Actor* ) Sender;
2855 	core->GetGame()->AddNPC( act );
2856 }
2857 
UnMakeGlobal(Scriptable * Sender,Action *)2858 void GameScript::UnMakeGlobal(Scriptable* Sender, Action* /*parameters*/)
2859 {
2860 	if (Sender->Type != ST_ACTOR) {
2861 		return;
2862 	}
2863 	Actor* act = ( Actor* ) Sender;
2864 	int slot;
2865 	slot = core->GetGame()->InStore( act );
2866 	if (slot >= 0) {
2867 		core->GetGame()->DelNPC( slot );
2868 		act->SetPersistent(-1);
2869 	}
2870 }
2871 
2872 //this apparently doesn't check the gold, thus could be used from non actors
GivePartyGoldGlobal(Scriptable * Sender,Action * parameters)2873 void GameScript::GivePartyGoldGlobal(Scriptable* Sender, Action* parameters)
2874 {
2875 	ieDword gold = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter);
2876 	if (Sender->Type == ST_ACTOR) {
2877 		Actor* act = ( Actor* ) Sender;
2878 		ieDword mygold = act->GetStat(IE_GOLD);
2879 		if (mygold < gold) {
2880 			gold = mygold;
2881 		}
2882 		//will get saved, not adjusted
2883 		act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-gold);
2884 	}
2885 	core->GetGame()->AddGold(gold);
2886 }
2887 
CreatePartyGold(Scriptable *,Action * parameters)2888 void GameScript::CreatePartyGold(Scriptable* /*Sender*/, Action* parameters)
2889 {
2890 	core->GetGame()->AddGold(parameters->int0Parameter);
2891 }
2892 
GivePartyGold(Scriptable * Sender,Action * parameters)2893 void GameScript::GivePartyGold(Scriptable* Sender, Action* parameters)
2894 {
2895 	ieDword gold = (ieDword) parameters->int0Parameter;
2896 	if (Sender->Type == ST_ACTOR) {
2897 		Actor* act = ( Actor* ) Sender;
2898 		ieDword mygold = act->GetStat(IE_GOLD);
2899 		if (mygold < gold) {
2900 			gold = mygold;
2901 		}
2902 		//will get saved, not adjusted
2903 		act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-gold);
2904 	}
2905 	core->GetGame()->AddGold(gold);
2906 }
2907 
DestroyPartyGold(Scriptable *,Action * parameters)2908 void GameScript::DestroyPartyGold(Scriptable* /*Sender*/, Action* parameters)
2909 {
2910 	int gold = core->GetGame()->PartyGold;
2911 	if (gold>parameters->int0Parameter) {
2912 		gold=parameters->int0Parameter;
2913 	}
2914 	core->GetGame()->AddGold(-gold);
2915 }
2916 
TakePartyGold(Scriptable * Sender,Action * parameters)2917 void GameScript::TakePartyGold(Scriptable* Sender, Action* parameters)
2918 {
2919 	ieDword gold = core->GetGame()->PartyGold;
2920 	if (gold>(ieDword) parameters->int0Parameter) {
2921 		gold=(ieDword) parameters->int0Parameter;
2922 	}
2923 	core->GetGame()->AddGold((ieDword) -(int) gold);
2924 	if (Sender->Type == ST_ACTOR) {
2925 		Actor* act = ( Actor* ) Sender;
2926 		//fixes PST limlim shop, partymembers don't receive the taken gold
2927 		if (!act->InParty) {
2928 			act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)+gold);
2929 		}
2930 	}
2931 }
2932 
AddXPObject(Scriptable * Sender,Action * parameters)2933 void GameScript::AddXPObject(Scriptable* Sender, Action* parameters)
2934 {
2935 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
2936 	if (!tar) {
2937 		return;
2938 	}
2939 	if (tar->Type != ST_ACTOR) {
2940 		return;
2941 	}
2942 	Actor* actor = ( Actor* ) tar;
2943 	int xp = parameters->int0Parameter;
2944 	core->GetTokenDictionary()->SetAtCopy("EXPERIENCEAMOUNT", xp);
2945 	if (core->HasFeedback(FT_MISC)) {
2946 		if (displaymsg->HasStringReference(STR_GOTQUESTXP)) {
2947 			displaymsg->DisplayConstantStringName(STR_GOTQUESTXP, DMC_BG2XPGREEN, actor);
2948 		} else {
2949 			displaymsg->DisplayConstantStringValue(STR_GOTXP, DMC_BG2XPGREEN, (ieDword)xp);
2950 		}
2951 	}
2952 
2953 	//normally the second parameter is 0, but it may be handy to have control over that (See SX_* flags)
2954 	actor->AddExperience(xp, parameters->int1Parameter);
2955 	core->PlaySound(DS_GOTXP, SFX_CHAN_ACTIONS);
2956 }
2957 
AddXP2DA(Scriptable *,Action * parameters)2958 void GameScript::AddXP2DA(Scriptable* /*Sender*/, Action* parameters)
2959 {
2960 	AddXPCore(parameters);
2961 }
2962 
AddXPVar(Scriptable *,Action * parameters)2963 void GameScript::AddXPVar(Scriptable* /*Sender*/, Action* parameters)
2964 {
2965 	AddXPCore(parameters, true);
2966 }
2967 
AddExperienceParty(Scriptable *,Action * parameters)2968 void GameScript::AddExperienceParty(Scriptable* /*Sender*/, Action* parameters)
2969 {
2970 	core->GetGame()->ShareXP(parameters->int0Parameter, SX_DIVIDE);
2971 	core->PlaySound(DS_GOTXP, SFX_CHAN_ACTIONS);
2972 }
2973 
2974 //this needs moncrate.2da, but otherwise independent from GF_CHALLENGERATING
AddExperiencePartyCR(Scriptable *,Action * parameters)2975 void GameScript::AddExperiencePartyCR(Scriptable* /*Sender*/, Action* parameters)
2976 {
2977 	core->GetGame()->ShareXP(parameters->int0Parameter, SX_DIVIDE|SX_CR);
2978 }
2979 
AddExperiencePartyGlobal(Scriptable * Sender,Action * parameters)2980 void GameScript::AddExperiencePartyGlobal(Scriptable* Sender, Action* parameters)
2981 {
2982 	ieDword xp = CheckVariable( Sender, parameters->string0Parameter, parameters->string1Parameter );
2983 	core->GetGame()->ShareXP(xp, SX_DIVIDE);
2984 	core->PlaySound(DS_GOTXP, SFX_CHAN_ACTIONS);
2985 }
2986 
2987 // these two didn't work in the original (bg2, ee) and were unused
SetMoraleAI(Scriptable * Sender,Action * parameters)2988 void GameScript::SetMoraleAI(Scriptable* Sender, Action* parameters)
2989 {
2990 	if (Sender->Type != ST_ACTOR) {
2991 		return;
2992 	}
2993 	Actor* act = ( Actor* ) Sender;
2994 	act->SetBase(IE_MORALE, parameters->int0Parameter);
2995 }
2996 
IncMoraleAI(Scriptable * Sender,Action * parameters)2997 void GameScript::IncMoraleAI(Scriptable* Sender, Action* parameters)
2998 {
2999 	if (Sender->Type != ST_ACTOR) {
3000 		return;
3001 	}
3002 	Actor* act = ( Actor* ) Sender;
3003 	act->SetBase(IE_MORALE, parameters->int0Parameter + act->GetBase(IE_MORALE));
3004 }
3005 
3006 // these three are present in all engines
MoraleSet(Scriptable * Sender,Action * parameters)3007 void GameScript::MoraleSet(Scriptable* Sender, Action* parameters)
3008 {
3009 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3010 	if (!tar) {
3011 		return;
3012 	}
3013 	if (tar->Type != ST_ACTOR) {
3014 		return;
3015 	}
3016 	Actor* act = ( Actor* ) tar;
3017 	act->SetBase(IE_MORALE, parameters->int0Parameter);
3018 }
3019 
MoraleInc(Scriptable * Sender,Action * parameters)3020 void GameScript::MoraleInc(Scriptable* Sender, Action* parameters)
3021 {
3022 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3023 	if (!tar) {
3024 		return;
3025 	}
3026 	if (tar->Type != ST_ACTOR) {
3027 		return;
3028 	}
3029 	Actor* act = ( Actor* ) tar;
3030 	act->SetBase(IE_MORALE, act->GetBase(IE_MORALE) + parameters->int0Parameter);
3031 }
3032 
MoraleDec(Scriptable * Sender,Action * parameters)3033 void GameScript::MoraleDec(Scriptable* Sender, Action* parameters)
3034 {
3035 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3036 	if (!tar) {
3037 		return;
3038 	}
3039 	if (tar->Type != ST_ACTOR) {
3040 		return;
3041 	}
3042 	Actor* act = ( Actor* ) tar;
3043 	act->SetBase(IE_MORALE, act->GetBase(IE_MORALE) - parameters->int0Parameter);
3044 }
3045 
JoinParty(Scriptable * Sender,Action * parameters)3046 void GameScript::JoinParty(Scriptable* Sender, Action* parameters)
3047 {
3048 	if (Sender->Type != ST_ACTOR) {
3049 		return;
3050 	}
3051 	// make sure we're in the same area, otherwise Dynaheir joins when Minsc does
3052 	// but she's in another area and needs to be rescued first!
3053 	Actor* act = ( Actor* ) Sender;
3054 	Game *game = core->GetGame();
3055 	if (act->GetCurrentArea() != game->GetCurrentArea()) {
3056 		return;
3057 	}
3058 
3059 	/* calling this, so it is simpler to change */
3060 	/* i'm not sure this is required here at all */
3061 	SetBeenInPartyFlags(Sender, parameters);
3062 	act->SetBase( IE_EA, EA_PC );
3063 	if (core->HasFeature( GF_HAS_DPLAYER )) {
3064 		/* we must reset various existing scripts */
3065 		act->SetScript( "DEFAULT", AI_SCRIPT_LEVEL, true );
3066 		act->SetScript( "", SCR_RACE, true );
3067 		act->SetScript( "", SCR_GENERAL, true );
3068 		act->SetScript( "DPLAYER2", SCR_DEFAULT, false );
3069 	}
3070 	AutoTable pdtable("pdialog");
3071 	if (pdtable) {
3072 		const char* scriptname = act->GetScriptName();
3073 		ieResRef resref;
3074 		//set dialog only if we got a row
3075 		if (pdtable->GetRowIndex( scriptname ) != -1) {
3076 			if (game->Expansion==5) {
3077 				strnlwrcpy(resref, pdtable->QueryField( scriptname, "25JOIN_DIALOG_FILE"), sizeof(ieResRef)-1);
3078 			} else {
3079 				strnlwrcpy(resref, pdtable->QueryField( scriptname, "JOIN_DIALOG_FILE"), sizeof(ieResRef)-1);
3080 			}
3081 			act->SetDialog( resref );
3082 		}
3083 	}
3084 	game->JoinParty( act, JP_JOIN );
3085 }
3086 
LeaveParty(Scriptable * Sender,Action *)3087 void GameScript::LeaveParty(Scriptable* Sender, Action* /*parameters*/)
3088 {
3089 	if (Sender->Type != ST_ACTOR) {
3090 		return;
3091 	}
3092 	Actor* act = ( Actor* ) Sender;
3093 	core->GetGame()->LeaveParty( act );
3094 }
3095 
3096 //HideCreature hides only the visuals of a creature
3097 //(feet circle and avatar)
3098 //the scripts of the creature are still running
3099 //iwd2 stores this flag in the MC field (MC_HIDDEN)
HideCreature(Scriptable * Sender,Action * parameters)3100 void GameScript::HideCreature(Scriptable* Sender, Action* parameters)
3101 {
3102 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3103 	if (!tar || tar->Type != ST_ACTOR) {
3104 		return;
3105 	}
3106 	Actor* actor = ( Actor* ) tar;
3107 	actor->SetBase(IE_AVATARREMOVAL, parameters->int0Parameter);
3108 }
3109 
3110 //i have absolutely no idea why this is needed when we have HideCreature
ForceHide(Scriptable * Sender,Action * parameters)3111 void GameScript::ForceHide(Scriptable* Sender, Action* parameters)
3112 {
3113 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3114 	if (!tar) {
3115 		tar=Sender;
3116 	}
3117 	if (tar->Type != ST_ACTOR) {
3118 		return;
3119 	}
3120 	Actor* actor = ( Actor* ) tar;
3121 	actor->SetBase(IE_AVATARREMOVAL, 1);
3122 }
3123 
ForceLeaveAreaLUA(Scriptable * Sender,Action * parameters)3124 void GameScript::ForceLeaveAreaLUA(Scriptable* Sender, Action* parameters)
3125 {
3126 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3127 	if (!tar || tar->Type != ST_ACTOR) {
3128 		return;
3129 	}
3130 	Actor* actor = ( Actor* ) tar;
3131 	//the LoadMos ResRef may be empty
3132 	if (parameters->string1Parameter[0]) {
3133 		strnlwrcpy(core->GetGame()->LoadMos, parameters->string1Parameter, sizeof(ieResRef)-1);
3134 	}
3135 	if (actor->Persistent() || !CreateMovementEffect(actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter) ) {
3136 		MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true);
3137 	}
3138 }
3139 
LeaveAreaLUA(Scriptable * Sender,Action * parameters)3140 void GameScript::LeaveAreaLUA(Scriptable* Sender, Action* parameters)
3141 {
3142 	if (Sender->Type != ST_ACTOR) {
3143 		return;
3144 	}
3145 	Actor* actor = ( Actor* ) Sender;
3146 	//the LoadMos ResRef may be empty
3147 	if (parameters->string1Parameter[0]) {
3148 		strnlwrcpy(core->GetGame()->LoadMos, parameters->string1Parameter, sizeof(ieResRef)-1);
3149 	}
3150 	if (actor->Persistent() || !CreateMovementEffect(actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter) ) {
3151 		MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true);
3152 	}
3153 }
3154 
3155 //this is a blocking action, because we have to move to the Entry
LeaveAreaLUAEntry(Scriptable * Sender,Action * parameters)3156 void GameScript::LeaveAreaLUAEntry(Scriptable* Sender, Action* parameters)
3157 {
3158 	if (Sender->Type != ST_ACTOR) {
3159 		Sender->ReleaseCurrentAction();
3160 		return;
3161 	}
3162 	Game *game = core->GetGame();
3163 	if (parameters->string1Parameter[0]) {
3164 		strnlwrcpy(game->LoadMos, parameters->string1Parameter, sizeof(ieResRef)-1);
3165 	}
3166 	Point p = GetEntryPoint(parameters->string0Parameter, parameters->string1Parameter);
3167 	if (p.isempty()) {
3168 		Sender->ReleaseCurrentAction();
3169 		return;
3170 	}
3171 	parameters->pointParameter=p;
3172 	strcpy(parameters->string1Parameter, "");
3173 	LeaveAreaLUA(Sender, parameters);
3174 	Sender->ReleaseCurrentAction();
3175 }
3176 
3177 //at this time it is unclear what the LeaveAreaLUAPanic* commands are used for
3178 //since they are always followed by the non-panic version of the command in all
3179 //games that use them (bg1 + bg2) we simply make them de-facto no-ops for now
LeaveAreaLUAPanic(Scriptable * Sender,Action * parameters)3180 void GameScript::LeaveAreaLUAPanic(Scriptable* Sender, Action* parameters)
3181 {
3182 	if (Sender->Type != ST_ACTOR) {
3183 		return;
3184 	}
3185 	if (parameters->string1Parameter[0]) {
3186 		strnlwrcpy(core->GetGame()->LoadMos, parameters->string1Parameter, sizeof(ieResRef)-1);
3187 	}
3188 }
3189 
LeaveAreaLUAPanicEntry(Scriptable * Sender,Action * parameters)3190 void GameScript::LeaveAreaLUAPanicEntry(Scriptable* Sender, Action* parameters)
3191 {
3192 	LeaveAreaLUAPanic(Sender, parameters);
3193 }
3194 
SetToken(Scriptable *,Action * parameters)3195 void GameScript::SetToken(Scriptable* /*Sender*/, Action* parameters)
3196 {
3197 	//SetAt takes a newly created reference (no need of free/copy)
3198 	char * str = core->GetCString( parameters->int0Parameter);
3199 	core->GetTokenDictionary()->SetAt(parameters->string0Parameter, str);
3200 }
3201 
3202 //Assigns a numeric variable to the token
SetTokenGlobal(Scriptable * Sender,Action * parameters)3203 void GameScript::SetTokenGlobal(Scriptable* Sender, Action* parameters)
3204 {
3205 	ieDword value = CheckVariable( Sender, parameters->string0Parameter );
3206 	//using SetAtCopy because we need a copy of the value
3207 	core->GetTokenDictionary()->SetAtCopy( parameters->string1Parameter, value );
3208 }
3209 
3210 //Assigns the target object's name (not scriptname) to the token
SetTokenObject(Scriptable * Sender,Action * parameters)3211 void GameScript::SetTokenObject(Scriptable* Sender, Action* parameters)
3212 {
3213 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3214 	if (!tar || tar->Type != ST_ACTOR) {
3215 		return;
3216 	}
3217 	Actor* actor = ( Actor* ) tar;
3218 	core->GetTokenDictionary()->SetAtCopy( parameters->string0Parameter, actor->GetName(0) );
3219 }
3220 
PlayDead(Scriptable * Sender,Action * parameters)3221 void GameScript::PlayDead(Scriptable* Sender, Action* parameters)
3222 {
3223 	if (Sender->Type != ST_ACTOR) {
3224 		Sender->ReleaseCurrentAction();
3225 		return;
3226 	}
3227 	Actor* actor = ( Actor* ) Sender;
3228 
3229 	actor->CurrentActionInterruptable = false;
3230 	if (!Sender->CurrentActionTicks && parameters->int0Parameter) {
3231 		// set countdown on first run
3232 		Sender->CurrentActionState = parameters->int0Parameter;
3233 		actor->SetStance( IE_ANI_DIE );
3234 	}
3235 	if (Sender->CurrentActionState <= 0) {
3236 		actor->SetStance( IE_ANI_GET_UP );
3237 		Sender->ReleaseCurrentAction();
3238 		return;
3239 	}
3240 	actor->CurrentActionState--;
3241 }
3242 
PlayDeadInterruptable(Scriptable * Sender,Action * parameters)3243 void GameScript::PlayDeadInterruptable(Scriptable* Sender, Action* parameters)
3244 {
3245 	if (Sender->Type != ST_ACTOR) {
3246 		Sender->ReleaseCurrentAction();
3247 		return;
3248 	}
3249 	Actor* actor = ( Actor* ) Sender;
3250 
3251 	if (!Sender->CurrentActionTicks && parameters->int0Parameter) {
3252 		// set countdown on first run
3253 		Sender->CurrentActionState = parameters->int0Parameter;
3254 		actor->SetStance( IE_ANI_DIE );
3255 	}
3256 	if (Sender->CurrentActionState <= 0) {
3257 		actor->SetStance( IE_ANI_GET_UP );
3258 		Sender->ReleaseCurrentAction();
3259 		return;
3260 	}
3261 	actor->CurrentActionState--;
3262 }
3263 
3264 /* this is not correct, see #92 */
Swing(Scriptable * Sender,Action *)3265 void GameScript::Swing(Scriptable* Sender, Action* /*parameters*/)
3266 {
3267 	if (Sender->Type != ST_ACTOR) {
3268 		return;
3269 	}
3270 	Actor* actor = ( Actor* ) Sender;
3271 	actor->SetStance( IE_ANI_ATTACK );
3272 	actor->SetWait(AI_UPDATE_TIME * 2);
3273 }
3274 
3275 /* this is not correct, see #92 */
SwingOnce(Scriptable * Sender,Action *)3276 void GameScript::SwingOnce(Scriptable* Sender, Action* /*parameters*/)
3277 {
3278 	if (Sender->Type != ST_ACTOR) {
3279 		return;
3280 	}
3281 	Actor* actor = ( Actor* ) Sender;
3282 	actor->SetStance( IE_ANI_ATTACK );
3283 	actor->SetWait(AI_UPDATE_TIME);
3284 }
3285 
Recoil(Scriptable * Sender,Action *)3286 void GameScript::Recoil(Scriptable* Sender, Action* /*parameters*/)
3287 {
3288 	if (Sender->Type != ST_ACTOR) {
3289 		return;
3290 	}
3291 	Actor* actor = ( Actor* ) Sender;
3292 	actor->SetStance( IE_ANI_DAMAGE );
3293 	actor->SetWait( 1 );
3294 }
3295 
AnkhegEmerge(Scriptable * Sender,Action *)3296 void GameScript::AnkhegEmerge(Scriptable* Sender, Action* /*parameters*/)
3297 {
3298 	if (Sender->Type != ST_ACTOR) {
3299 		return;
3300 	}
3301 	Actor* actor = ( Actor* ) Sender;
3302 	if (actor->GetStance()!=IE_ANI_EMERGE) {
3303 		actor->SetStance( IE_ANI_EMERGE );
3304 		actor->SetWait( 1 );
3305 	}
3306 }
3307 
AnkhegHide(Scriptable * Sender,Action *)3308 void GameScript::AnkhegHide(Scriptable* Sender, Action* /*parameters*/)
3309 {
3310 	if (Sender->Type != ST_ACTOR) {
3311 		return;
3312 	}
3313 	Actor* actor = ( Actor* ) Sender;
3314 	if (actor->GetStance()!=IE_ANI_HIDE) {
3315 		actor->SetStance( IE_ANI_HIDE );
3316 		actor->SetWait( 1 );
3317 	}
3318 }
3319 
GlobalSetGlobal(Scriptable * Sender,Action * parameters)3320 void GameScript::GlobalSetGlobal(Scriptable* Sender, Action* parameters)
3321 {
3322 	ieDword value = CheckVariable( Sender, parameters->string0Parameter );
3323 	SetVariable( Sender, parameters->string1Parameter, value );
3324 }
3325 
3326 /* adding the second variable to the first, they must be GLOBAL */
AddGlobals(Scriptable * Sender,Action * parameters)3327 void GameScript::AddGlobals(Scriptable* Sender, Action* parameters)
3328 {
3329 	ieDword value1 = CheckVariable( Sender, parameters->string0Parameter, "GLOBAL");
3330 	ieDword value2 = CheckVariable( Sender, parameters->string1Parameter, "GLOBAL");
3331 	SetVariable( Sender, parameters->string0Parameter, "GLOBAL", value1 + value2 );
3332 }
3333 
3334 /* adding the second variable to the first, they could be area or locals */
GlobalAddGlobal(Scriptable * Sender,Action * parameters)3335 void GameScript::GlobalAddGlobal(Scriptable* Sender, Action* parameters)
3336 {
3337 	ieDword value1 = CheckVariable( Sender,
3338 		parameters->string0Parameter );
3339 	ieDword value2 = CheckVariable( Sender,
3340 		parameters->string1Parameter );
3341 	SetVariable( Sender, parameters->string0Parameter, value1 + value2 );
3342 }
3343 
3344 /* adding the number to the global, they could be area or locals */
IncrementGlobal(Scriptable * Sender,Action * parameters)3345 void GameScript::IncrementGlobal(Scriptable* Sender, Action* parameters)
3346 {
3347 	ieDword value = CheckVariable( Sender, parameters->string0Parameter );
3348 	SetVariable( Sender, parameters->string0Parameter,
3349 		value + parameters->int0Parameter );
3350 }
3351 
3352 /* adding the number to the global ONLY if the first global is zero */
IncrementGlobalOnce(Scriptable * Sender,Action * parameters)3353 void GameScript::IncrementGlobalOnce(Scriptable* Sender, Action* parameters)
3354 {
3355 	ieDword value = CheckVariable( Sender, parameters->string0Parameter );
3356 	if (value != 0) {
3357 		return;
3358 	}
3359 	//todo:the actual behaviour of this opcode may need to be verified, as this is
3360 	//just a best guess at how the two parameters are changed, and could
3361 	//well be more complex; the original usage of this function is currently
3362 	//not well understood (relates to hardcoded alignment changes)
3363 	SetVariable( Sender, parameters->string0Parameter, 1 );
3364 
3365 	value = CheckVariable( Sender, parameters->string1Parameter );
3366 	SetVariable( Sender, parameters->string1Parameter,
3367 		value + parameters->int0Parameter );
3368 }
3369 
GlobalSubGlobal(Scriptable * Sender,Action * parameters)3370 void GameScript::GlobalSubGlobal(Scriptable* Sender, Action* parameters)
3371 {
3372 	ieDword value1 = CheckVariable( Sender,
3373 		parameters->string0Parameter );
3374 	ieDword value2 = CheckVariable( Sender,
3375 		parameters->string1Parameter );
3376 	SetVariable( Sender, parameters->string0Parameter, value1 - value2 );
3377 }
3378 
GlobalAndGlobal(Scriptable * Sender,Action * parameters)3379 void GameScript::GlobalAndGlobal(Scriptable* Sender, Action* parameters)
3380 {
3381 	ieDword value1 = CheckVariable( Sender,
3382 		parameters->string0Parameter );
3383 	ieDword value2 = CheckVariable( Sender,
3384 		parameters->string1Parameter );
3385 	SetVariable( Sender, parameters->string0Parameter, value1 && value2 );
3386 }
3387 
GlobalOrGlobal(Scriptable * Sender,Action * parameters)3388 void GameScript::GlobalOrGlobal(Scriptable* Sender, Action* parameters)
3389 {
3390 	ieDword value1 = CheckVariable( Sender,
3391 		parameters->string0Parameter );
3392 	ieDword value2 = CheckVariable( Sender,
3393 		parameters->string1Parameter );
3394 	SetVariable( Sender, parameters->string0Parameter, value1 || value2 );
3395 }
3396 
GlobalBOrGlobal(Scriptable * Sender,Action * parameters)3397 void GameScript::GlobalBOrGlobal(Scriptable* Sender, Action* parameters)
3398 {
3399 	ieDword value1 = CheckVariable( Sender,
3400 		parameters->string0Parameter );
3401 	ieDword value2 = CheckVariable( Sender,
3402 		parameters->string1Parameter );
3403 	SetVariable( Sender, parameters->string0Parameter, value1 | value2 );
3404 }
3405 
GlobalBAndGlobal(Scriptable * Sender,Action * parameters)3406 void GameScript::GlobalBAndGlobal(Scriptable* Sender, Action* parameters)
3407 {
3408 	ieDword value1 = CheckVariable( Sender,
3409 		parameters->string0Parameter );
3410 	ieDword value2 = CheckVariable( Sender,
3411 		parameters->string1Parameter );
3412 	SetVariable( Sender, parameters->string0Parameter, value1 & value2 );
3413 }
3414 
GlobalXorGlobal(Scriptable * Sender,Action * parameters)3415 void GameScript::GlobalXorGlobal(Scriptable* Sender, Action* parameters)
3416 {
3417 	ieDword value1 = CheckVariable( Sender,
3418 		parameters->string0Parameter );
3419 	ieDword value2 = CheckVariable( Sender,
3420 		parameters->string1Parameter );
3421 	SetVariable( Sender, parameters->string0Parameter, value1 ^ value2 );
3422 }
3423 
GlobalBOr(Scriptable * Sender,Action * parameters)3424 void GameScript::GlobalBOr(Scriptable* Sender, Action* parameters)
3425 {
3426 	ieDword value1 = CheckVariable( Sender,
3427 		parameters->string0Parameter );
3428 	SetVariable( Sender, parameters->string0Parameter,
3429 		value1 | parameters->int0Parameter );
3430 }
3431 
GlobalBAnd(Scriptable * Sender,Action * parameters)3432 void GameScript::GlobalBAnd(Scriptable* Sender, Action* parameters)
3433 {
3434 	ieDword value1 = CheckVariable( Sender,
3435 		parameters->string0Parameter );
3436 	SetVariable( Sender, parameters->string0Parameter,
3437 		value1 & parameters->int0Parameter );
3438 }
3439 
GlobalXor(Scriptable * Sender,Action * parameters)3440 void GameScript::GlobalXor(Scriptable* Sender, Action* parameters)
3441 {
3442 	ieDword value1 = CheckVariable( Sender,
3443 		parameters->string0Parameter );
3444 	SetVariable( Sender, parameters->string0Parameter,
3445 		value1 ^ parameters->int0Parameter );
3446 }
3447 
GlobalMax(Scriptable * Sender,Action * parameters)3448 void GameScript::GlobalMax(Scriptable* Sender, Action* parameters)
3449 {
3450 	long value1 = CheckVariable( Sender, parameters->string0Parameter );
3451 	if (value1 > parameters->int0Parameter) {
3452 		SetVariable( Sender, parameters->string0Parameter, value1 );
3453 	}
3454 }
3455 
GlobalMin(Scriptable * Sender,Action * parameters)3456 void GameScript::GlobalMin(Scriptable* Sender, Action* parameters)
3457 {
3458 	long value1 = CheckVariable( Sender, parameters->string0Parameter );
3459 	if (value1 < parameters->int0Parameter) {
3460 		SetVariable( Sender, parameters->string0Parameter, value1 );
3461 	}
3462 }
3463 
BitClear(Scriptable * Sender,Action * parameters)3464 void GameScript::BitClear(Scriptable* Sender, Action* parameters)
3465 {
3466 	ieDword value1 = CheckVariable( Sender,
3467 		parameters->string0Parameter );
3468 	SetVariable( Sender, parameters->string0Parameter,
3469 		value1 & ~parameters->int0Parameter );
3470 }
3471 
GlobalShL(Scriptable * Sender,Action * parameters)3472 void GameScript::GlobalShL(Scriptable* Sender, Action* parameters)
3473 {
3474 	ieDword value1 = CheckVariable( Sender,
3475 		parameters->string0Parameter );
3476 	ieDword value2 = parameters->int0Parameter;
3477 	if (value2 > 31) {
3478 		value1 = 0;
3479 	} else {
3480 		value1 <<= value2;
3481 	}
3482 	SetVariable( Sender, parameters->string0Parameter, value1 );
3483 }
3484 
GlobalShR(Scriptable * Sender,Action * parameters)3485 void GameScript::GlobalShR(Scriptable* Sender, Action* parameters)
3486 {
3487 	ieDword value1 = CheckVariable( Sender,
3488 		parameters->string0Parameter );
3489 	ieDword value2 = parameters->int0Parameter;
3490 	if (value2 > 31) {
3491 		value1 = 0;
3492 	} else {
3493 		value1 >>= value2;
3494 	}
3495 	SetVariable( Sender, parameters->string0Parameter, value1 );
3496 }
3497 
GlobalMaxGlobal(Scriptable * Sender,Action * parameters)3498 void GameScript::GlobalMaxGlobal(Scriptable* Sender, Action* parameters)
3499 {
3500 	ieDword value1 = CheckVariable( Sender, parameters->string0Parameter );
3501 	ieDword value2 = CheckVariable( Sender, parameters->string1Parameter );
3502 	if (value1 < value2) {
3503 		SetVariable( Sender, parameters->string0Parameter, value2 );
3504 	}
3505 }
3506 
GlobalMinGlobal(Scriptable * Sender,Action * parameters)3507 void GameScript::GlobalMinGlobal(Scriptable* Sender, Action* parameters)
3508 {
3509 	ieDword value1 = CheckVariable( Sender, parameters->string0Parameter );
3510 	ieDword value2 = CheckVariable( Sender, parameters->string1Parameter );
3511 	if (value1 > value2) {
3512 		SetVariable( Sender, parameters->string0Parameter, value2 );
3513 	}
3514 }
3515 
GlobalShLGlobal(Scriptable * Sender,Action * parameters)3516 void GameScript::GlobalShLGlobal(Scriptable* Sender, Action* parameters)
3517 {
3518 	ieDword value1 = CheckVariable( Sender, parameters->string0Parameter );
3519 	ieDword value2 = CheckVariable( Sender, parameters->string1Parameter );
3520 	if (value2 > 31) {
3521 		value1 = 0;
3522 	} else {
3523 		value1 <<= value2;
3524 	}
3525 	SetVariable( Sender, parameters->string0Parameter, value1 );
3526 }
GlobalShRGlobal(Scriptable * Sender,Action * parameters)3527 void GameScript::GlobalShRGlobal(Scriptable* Sender, Action* parameters)
3528 {
3529 	ieDword value1 = CheckVariable( Sender, parameters->string0Parameter );
3530 	ieDword value2 = CheckVariable( Sender, parameters->string1Parameter );
3531 	if (value2 > 31) {
3532 		value1 = 0;
3533 	} else {
3534 		value1 >>= value2;
3535 	}
3536 	SetVariable( Sender, parameters->string0Parameter, value1 );
3537 }
3538 
ClearAllActions(Scriptable * Sender,Action *)3539 void GameScript::ClearAllActions(Scriptable* Sender, Action* /*parameters*/)
3540 {
3541 	Map *map = Sender->GetCurrentArea();
3542 	int i = map->GetActorCount(true);
3543 	while(i--) {
3544 		Actor* act = map->GetActor(i,true);
3545 		if (act && act != Sender && act->ValidTarget(GA_NO_DEAD)) {
3546 			if (!(act->GetInternalFlag() & IF_NOINT)) {
3547 				act->Stop();
3548 				act->SetModal(MS_NONE);
3549 			}
3550 		}
3551 	}
3552 }
3553 
ClearActions(Scriptable * Sender,Action * parameters)3554 void GameScript::ClearActions(Scriptable* Sender, Action* parameters)
3555 {
3556 	Scriptable* tar = Sender;
3557 	if (parameters->objects[1]) {
3558 		tar = GetActorFromObject( Sender, parameters->objects[1] );
3559 		if (!tar) {
3560 			Log(WARNING, "GameScript", "Couldn't find target for ClearActions!");
3561 			parameters->objects[1]->dump();
3562 			return;
3563 		}
3564 	}
3565 	if (!(tar->GetInternalFlag() & IF_NOINT)) {
3566 		tar->Stop();
3567 	}
3568 }
3569 
SetNumTimesTalkedTo(Scriptable * Sender,Action * parameters)3570 void GameScript::SetNumTimesTalkedTo(Scriptable* Sender, Action* parameters)
3571 {
3572 	if (Sender->Type != ST_ACTOR) {
3573 		return;
3574 	}
3575 	Actor* actor = ( Actor* ) Sender;
3576 	actor->TalkCount = parameters->int0Parameter;
3577 }
3578 
StartMovie(Scriptable * Sender,Action * parameters)3579 void GameScript::StartMovie(Scriptable* Sender, Action* parameters)
3580 {
3581 	core->PlayMovie( parameters->string0Parameter );
3582 	Sender->ReleaseCurrentAction(); // should this be blocking?
3583 }
3584 
SetLeavePartyDialogFile(Scriptable * Sender,Action *)3585 void GameScript::SetLeavePartyDialogFile(Scriptable* Sender, Action* /*parameters*/)
3586 {
3587 	if (Sender->Type != ST_ACTOR) {
3588 		return;
3589 	}
3590 	AutoTable pdtable("pdialog");
3591 	Actor* act = ( Actor* ) Sender;
3592 	const char* scriptname = act->GetScriptName();
3593 	if (pdtable->GetRowIndex( scriptname ) != -1) {
3594 		ieResRef resref;
3595 
3596 		if (core->GetGame()->Expansion==5) {
3597 			strnlwrcpy(resref, pdtable->QueryField( scriptname, "25POST_DIALOG_FILE" ), sizeof(ieResRef)-1 );
3598 		} else {
3599 			strnlwrcpy(resref, pdtable->QueryField( scriptname, "POST_DIALOG_FILE" ), sizeof(ieResRef)-1 );
3600 		}
3601 		act->SetDialog(resref);
3602 	}
3603 }
3604 
TextScreen(Scriptable *,Action * parameters)3605 void GameScript::TextScreen(Scriptable* /*Sender*/, Action* parameters)
3606 {
3607 	core->SetPause(PAUSE_ON, PF_QUIET);
3608 	// bg2 sometimes calls IncrementChapter("") right after a TextScreen("sometable"),
3609 	// so we make sure they don't cancel out
3610 	if (parameters->string0Parameter[0]) {
3611 		strnlwrcpy(core->GetGame()->TextScreen, parameters->string0Parameter, sizeof(ieResRef) - 1);
3612 	}
3613 
3614 	core->SetEventFlag(EF_TEXTSCREEN);
3615 }
3616 
IncrementChapter(Scriptable * Sender,Action * parameters)3617 void GameScript::IncrementChapter(Scriptable* Sender, Action* parameters)
3618 {
3619 	core->GetGame()->IncrementChapter();
3620 	TextScreen(Sender, parameters);
3621 }
3622 
SetCriticalPathObject(Scriptable * Sender,Action * parameters)3623 void GameScript::SetCriticalPathObject(Scriptable* Sender, Action* parameters)
3624 {
3625 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3626 	if (!tar || tar->Type != ST_ACTOR) {
3627 		return;
3628 	}
3629 	Actor* actor = ( Actor* ) tar;
3630 	if (parameters->int0Parameter) {
3631 		actor->SetMCFlag(MC_PLOT_CRITICAL, OP_OR);
3632 	} else {
3633 		actor->SetMCFlag(MC_PLOT_CRITICAL, OP_NAND);
3634 	}
3635 }
3636 
SetBeenInPartyFlags(Scriptable * Sender,Action *)3637 void GameScript::SetBeenInPartyFlags(Scriptable* Sender, Action* /*parameters*/)
3638 {
3639 	if (Sender->Type != ST_ACTOR) {
3640 		return;
3641 	}
3642 	Actor* actor = ( Actor* ) Sender;
3643 	//it is bit 15 of the multi-class flags (confirmed)
3644 	actor->SetMCFlag(MC_BEENINPARTY, OP_OR);
3645 }
3646 
3647 /*iwd2 sets the high MC bits this way*/
SetCreatureAreaFlag(Scriptable * Sender,Action * parameters)3648 void GameScript::SetCreatureAreaFlag(Scriptable* Sender, Action* parameters)
3649 {
3650 	if (Sender->Type != ST_ACTOR) {
3651 		return;
3652 	}
3653 	Actor* actor = ( Actor* ) Sender;
3654 	if (parameters->int1Parameter) {
3655 		actor->SetMCFlag(parameters->int0Parameter, OP_OR);
3656 	} else {
3657 		actor->SetMCFlag(parameters->int0Parameter, OP_NAND);
3658 	}
3659 }
3660 
3661 //this will be a global change, fixme if it should be local
SetTextColor(Scriptable *,Action * parameters)3662 void GameScript::SetTextColor(Scriptable* /*Sender*/, Action* parameters)
3663 {
3664 	Color c = Color::FromABGR(parameters->int0Parameter);
3665 	core->SetInfoTextColor(c);
3666 }
3667 
BitGlobal(Scriptable * Sender,Action * parameters)3668 void GameScript::BitGlobal(Scriptable* Sender, Action* parameters)
3669 {
3670 	ieDword value = CheckVariable(Sender, parameters->string0Parameter );
3671 	HandleBitMod( value, parameters->int0Parameter, parameters->int1Parameter);
3672 	SetVariable(Sender, parameters->string0Parameter, value);
3673 }
3674 
GlobalBitGlobal(Scriptable * Sender,Action * parameters)3675 void GameScript::GlobalBitGlobal(Scriptable* Sender, Action* parameters)
3676 {
3677 	ieDword value1 = CheckVariable(Sender, parameters->string0Parameter );
3678 	ieDword value2 = CheckVariable(Sender, parameters->string1Parameter );
3679 	HandleBitMod( value1, value2, parameters->int1Parameter);
3680 	SetVariable(Sender, parameters->string0Parameter, value1);
3681 }
3682 
SetVisualRange(Scriptable * Sender,Action * parameters)3683 void GameScript::SetVisualRange(Scriptable* Sender, Action* parameters)
3684 {
3685 	if (Sender->Type != ST_ACTOR) {
3686 		return;
3687 	}
3688 	Actor* actor = ( Actor* ) Sender;
3689 	actor->SetBase(IE_VISUALRANGE,parameters->int0Parameter);
3690 	if (actor->GetStat(IE_EA) < EA_EVILCUTOFF) {
3691 		actor->SetBase(IE_EXPLORE, 1);
3692 	}
3693 }
3694 
MakeUnselectable(Scriptable * Sender,Action * parameters)3695 void GameScript::MakeUnselectable(Scriptable* Sender, Action* parameters)
3696 {
3697 	Sender->UnselectableTimer=parameters->int0Parameter * AI_UPDATE_TIME;
3698 
3699 	//update color
3700 	if (Sender->Type != ST_ACTOR) {
3701 		return;
3702 	}
3703 	Actor* actor = ( Actor* ) Sender;
3704 	if (parameters->int0Parameter) {
3705 		// flags may be wrong
3706 		core->GetGame()->SelectActor(actor, false, SELECT_QUIET);
3707 	}
3708 
3709 	actor->SetCircleSize();
3710 }
3711 
Debug(Scriptable *,Action * parameters)3712 void GameScript::Debug(Scriptable* /*Sender*/, Action* parameters)
3713 {
3714 	core->SetDebugMode(parameters->int0Parameter);
3715 	Log(WARNING, "GameScript", "DEBUG: %s", parameters->string0Parameter);
3716 }
3717 
IncrementProficiency(Scriptable * Sender,Action * parameters)3718 void GameScript::IncrementProficiency(Scriptable* Sender, Action* parameters)
3719 {
3720 	unsigned int idx = parameters->int0Parameter;
3721 	if (idx>31) {
3722 		return;
3723 	}
3724 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3725 	if (!tar) {
3726 		return;
3727 	}
3728 	if (tar->Type != ST_ACTOR) {
3729 		return;
3730 	}
3731 	Actor* target = ( Actor* ) tar;
3732 	//start of the proficiency stats
3733 	target->SetBase(IE_PROFICIENCYBASTARDSWORD+idx,
3734 		target->GetBase(IE_PROFICIENCYBASTARDSWORD+idx)+parameters->int1Parameter);
3735 }
3736 
IncrementExtraProficiency(Scriptable * Sender,Action * parameters)3737 void GameScript::IncrementExtraProficiency(Scriptable* Sender, Action* parameters)
3738 {
3739 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3740 	if (!tar) {
3741 		return;
3742 	}
3743 	if (tar->Type != ST_ACTOR) {
3744 		return;
3745 	}
3746 	Actor* target = ( Actor* ) tar;
3747 	target->SetBase(IE_FREESLOTS, target->GetBase(IE_FREESLOTS)+parameters->int0Parameter);
3748 }
3749 
3750 //the third parameter is a GemRB extension
AddJournalEntry(Scriptable *,Action * parameters)3751 void GameScript::AddJournalEntry(Scriptable* /*Sender*/, Action* parameters)
3752 {
3753 	core->GetGame()->AddJournalEntry(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter);
3754 }
3755 
SetQuestDone(Scriptable *,Action * parameters)3756 void GameScript::SetQuestDone(Scriptable* /*Sender*/, Action* parameters)
3757 {
3758 	Game *game = core->GetGame();
3759 	game->DeleteJournalEntry(parameters->int0Parameter);
3760 	game->AddJournalEntry(parameters->int0Parameter, IE_GAM_QUEST_DONE, parameters->int2Parameter);
3761 
3762 }
3763 
RemoveJournalEntry(Scriptable *,Action * parameters)3764 void GameScript::RemoveJournalEntry(Scriptable* /*Sender*/, Action* parameters)
3765 {
3766 	core->GetGame()->DeleteJournalEntry(parameters->int0Parameter);
3767 }
3768 
SetInternal(Scriptable * Sender,Action * parameters)3769 void GameScript::SetInternal(Scriptable* Sender, Action* parameters)
3770 {
3771 	unsigned int idx = parameters->int0Parameter;
3772 	if (idx>15) {
3773 		return;
3774 	}
3775 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3776 	if (!tar) {
3777 		return;
3778 	}
3779 	if (tar->Type != ST_ACTOR) {
3780 		return;
3781 	}
3782 	Actor* target = ( Actor* ) tar;
3783 	//start of the internal stats
3784 	target->SetBase(IE_INTERNAL_0+idx, parameters->int1Parameter);
3785 }
3786 
IncInternal(Scriptable * Sender,Action * parameters)3787 void GameScript::IncInternal(Scriptable* Sender, Action* parameters)
3788 {
3789 	unsigned int idx = parameters->int0Parameter;
3790 	if (idx>15) {
3791 		return;
3792 	}
3793 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3794 	if (!tar || tar->Type != ST_ACTOR) {
3795 		return;
3796 	}
3797 	Actor* target = ( Actor* ) tar;
3798 	//start of the internal stats
3799 	target->SetBase(IE_INTERNAL_0+idx,
3800 		target->GetBase(IE_INTERNAL_0+idx)+parameters->int1Parameter);
3801 }
3802 
DestroyAllEquipment(Scriptable * Sender,Action *)3803 void GameScript::DestroyAllEquipment(Scriptable* Sender, Action* /*parameters*/)
3804 {
3805 	Inventory *inv=NULL;
3806 
3807 	switch (Sender->Type) {
3808 		case ST_ACTOR:
3809 			inv = &(((Actor *) Sender)->inventory);
3810 			break;
3811 		case ST_CONTAINER:
3812 			inv = &(((Container *) Sender)->inventory);
3813 			break;
3814 		default:;
3815 	}
3816 	if (inv) {
3817 		inv->DestroyItem("",0,(ieDword) ~0); //destroy any and all
3818 	}
3819 }
3820 
DestroyItem(Scriptable * Sender,Action * parameters)3821 void GameScript::DestroyItem(Scriptable* Sender, Action* parameters)
3822 {
3823 	Inventory *inv=NULL;
3824 
3825 	switch (Sender->Type) {
3826 		case ST_ACTOR:
3827 			inv = &(((Actor *) Sender)->inventory);
3828 			break;
3829 		case ST_CONTAINER:
3830 			inv = &(((Container *) Sender)->inventory);
3831 			break;
3832 		default:;
3833 	}
3834 	if (inv) {
3835 		inv->DestroyItem(parameters->string0Parameter,0,1); //destroy one (even indestructible?)
3836 	}
3837 }
3838 
3839 //negative destroygold creates gold
DestroyGold(Scriptable * Sender,Action * parameters)3840 void GameScript::DestroyGold(Scriptable* Sender, Action* parameters)
3841 {
3842 	if (Sender->Type!=ST_ACTOR)
3843 		return;
3844 	Actor *act=(Actor *) Sender;
3845 	int max=(int) act->GetStat(IE_GOLD);
3846 	if (parameters->int0Parameter != 0) {
3847 		if (max>parameters->int0Parameter) {
3848 			max=parameters->int0Parameter;
3849 		}
3850 	}
3851 	act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-max);
3852 }
3853 
DestroyPartyItem(Scriptable *,Action * parameters)3854 void GameScript::DestroyPartyItem(Scriptable* /*Sender*/, Action* parameters)
3855 {
3856 	Game *game = core->GetGame();
3857 	int i = game->GetPartySize(false);
3858 	ieDword count;
3859 	if (parameters->int0Parameter) {
3860 		count=0;
3861 	} else {
3862 		count=1;
3863 	}
3864 	while (i--) {
3865 		Inventory *inv = &(game->GetPC( i,false )->inventory);
3866 		int res=inv->DestroyItem(parameters->string0Parameter,0,count);
3867 		if ( (count == 1) && res) {
3868 			break;
3869 		}
3870 	}
3871 }
3872 
3873 /* this is a gemrb extension */
DestroyPartyItemNum(Scriptable *,Action * parameters)3874 void GameScript::DestroyPartyItemNum(Scriptable* /*Sender*/, Action* parameters)
3875 {
3876 	Game *game = core->GetGame();
3877 	int i = game->GetPartySize(false);
3878 	ieDword count;
3879 	count = parameters->int0Parameter;
3880 	while (i--) {
3881 		Inventory *inv = &(game->GetPC( i,false )->inventory);
3882 		count -= inv->DestroyItem(parameters->string0Parameter,0,count);
3883 		if (!count ) {
3884 			break;
3885 		}
3886 	}
3887 }
3888 
DestroyAllDestructableEquipment(Scriptable * Sender,Action *)3889 void GameScript::DestroyAllDestructableEquipment(Scriptable* Sender, Action* /*parameters*/)
3890 {
3891 	Inventory *inv=NULL;
3892 
3893 	switch (Sender->Type) {
3894 		case ST_ACTOR:
3895 			inv = &(((Actor *) Sender)->inventory);
3896 			break;
3897 		case ST_CONTAINER:
3898 			inv = &(((Container *) Sender)->inventory);
3899 			break;
3900 		default:;
3901 	}
3902 	if (inv) {
3903 		inv->DestroyItem("", IE_INV_ITEM_DESTRUCTIBLE, (ieDword) ~0);
3904 	}
3905 }
3906 
SetApparentName(Scriptable * Sender,Action * parameters)3907 void GameScript::SetApparentName(Scriptable* Sender, Action* parameters)
3908 {
3909 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3910 	if (!tar || tar->Type != ST_ACTOR) {
3911 		return;
3912 	}
3913 	Actor* target = ( Actor* ) tar;
3914 	target->SetName(parameters->int0Parameter,1);
3915 }
3916 
SetRegularName(Scriptable * Sender,Action * parameters)3917 void GameScript::SetRegularName(Scriptable* Sender, Action* parameters)
3918 {
3919 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3920 	if (!tar || tar->Type != ST_ACTOR) {
3921 		return;
3922 	}
3923 	Actor* target = ( Actor* ) tar;
3924 	target->SetName(parameters->int0Parameter,2);
3925 }
3926 
3927 /** this is a gemrb extension */
UnloadArea(Scriptable *,Action * parameters)3928 void GameScript::UnloadArea(Scriptable* /*Sender*/, Action* parameters)
3929 {
3930 	int map=core->GetGame()->FindMap(parameters->string0Parameter);
3931 	if (map>=0) {
3932 		core->GetGame()->DelMap(map, parameters->int0Parameter);
3933 	}
3934 }
3935 
3936 static EffectRef fx_death_ref = { "Death", -1 };
Kill(Scriptable * Sender,Action * parameters)3937 void GameScript::Kill(Scriptable* Sender, Action* parameters)
3938 {
3939 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3940 	if (!tar || tar->Type != ST_ACTOR) {
3941 		return;
3942 	}
3943 	Actor* target = ( Actor* ) tar;
3944 	Effect *fx = EffectQueue::CreateEffect(fx_death_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
3945 	target->fxqueue.AddEffect(fx, false);
3946 	delete fx;
3947 }
3948 
SetGabber(Scriptable * Sender,Action * parameters)3949 void GameScript::SetGabber(Scriptable* Sender, Action* parameters)
3950 {
3951 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3952 	if (!tar || tar->Type != ST_ACTOR) {
3953 		return;
3954 	}
3955 	GameControl* gc = core->GetGameControl();
3956 	if (gc->GetDialogueFlags()&DF_IN_DIALOG) {
3957 		gc->dialoghandler->SetSpeaker(tar);
3958 	} else {
3959 		Log(WARNING, "GameScript", "Can't set gabber!");
3960 	}
3961 }
3962 
ReputationSet(Scriptable *,Action * parameters)3963 void GameScript::ReputationSet(Scriptable* /*Sender*/, Action* parameters)
3964 {
3965 	core->GetGame()->SetReputation(parameters->int0Parameter*10);
3966 }
3967 
ReputationInc(Scriptable *,Action * parameters)3968 void GameScript::ReputationInc(Scriptable* /*Sender*/, Action* parameters)
3969 {
3970 	Game *game = core->GetGame();
3971 	game->SetReputation( (int) game->Reputation + parameters->int0Parameter*10);
3972 }
3973 
FullHeal(Scriptable * Sender,Action * parameters)3974 void GameScript::FullHeal(Scriptable* Sender, Action* parameters)
3975 {
3976 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
3977 	if (!tar || tar->Type != ST_ACTOR) {
3978 		return;
3979 	}
3980 	Actor *scr = (Actor *) tar;
3981 	//0 means full healing
3982 	//Heal() might contain curing of some conditions
3983 	//if FullHeal doesn't do that, replace this with a SetBase
3984 	//fullhealex might still be the curing action
3985 	scr->Heal(0);
3986 }
3987 
3988 static EffectRef fx_disable_button_ref = { "DisableButton", -1 };
RemovePaladinHood(Scriptable * Sender,Action *)3989 void GameScript::RemovePaladinHood(Scriptable* Sender, Action* /*parameters*/)
3990 {
3991 	if (Sender->Type!=ST_ACTOR) {
3992 		return;
3993 	}
3994 	Actor *act = (Actor *) Sender;
3995 	act->ApplyKit(true, act->GetClassID(ISPALADIN));
3996 	act->SetMCFlag(MC_FALLEN_PALADIN, OP_OR);
3997 	Effect *fx = EffectQueue::CreateEffect(fx_disable_button_ref, 0, ACT_TURN, FX_DURATION_INSTANT_PERMANENT);
3998 	act->fxqueue.AddEffect(fx, false);
3999 	delete fx;
4000 	fx = EffectQueue::CreateEffect(fx_disable_button_ref, 0, ACT_CAST, FX_DURATION_INSTANT_PERMANENT);
4001 	act->fxqueue.AddEffect(fx, false);
4002 	delete fx;
4003 	if (act->InParty && core->HasFeedback(FT_STATES)) displaymsg->DisplayConstantStringName(STR_PALADIN_FALL, DMC_BG2XPGREEN, act);
4004 }
4005 
RemoveRangerHood(Scriptable * Sender,Action *)4006 void GameScript::RemoveRangerHood(Scriptable* Sender, Action* /*parameters*/)
4007 {
4008 	if (Sender->Type!=ST_ACTOR) {
4009 		return;
4010 	}
4011 	Actor *act = (Actor *) Sender;
4012 	act->ApplyKit(true, act->GetClassID(ISRANGER));
4013 	act->SetMCFlag(MC_FALLEN_RANGER, OP_OR);
4014 	Effect *fx = EffectQueue::CreateEffect(fx_disable_button_ref, 0, ACT_STEALTH, FX_DURATION_INSTANT_PERMANENT);
4015 	act->fxqueue.AddEffect(fx, false);
4016 	delete fx;
4017 	fx = EffectQueue::CreateEffect(fx_disable_button_ref, 0, ACT_CAST, FX_DURATION_INSTANT_PERMANENT);
4018 	act->fxqueue.AddEffect(fx, false);
4019 	delete fx;
4020 	if (act->InParty && core->HasFeedback(FT_STATES)) displaymsg->DisplayConstantStringName(STR_RANGER_FALL, DMC_BG2XPGREEN, act);
4021 }
4022 
RegainPaladinHood(Scriptable * Sender,Action *)4023 void GameScript::RegainPaladinHood(Scriptable* Sender, Action* /*parameters*/)
4024 {
4025 	if (Sender->Type!=ST_ACTOR) {
4026 		return;
4027 	}
4028 	Actor *act = (Actor *) Sender;
4029 	act->SetMCFlag(MC_FALLEN_PALADIN, OP_NAND);
4030 	act->fxqueue.RemoveAllEffectsWithParam(fx_disable_button_ref, ACT_CAST);
4031 	act->fxqueue.RemoveAllEffectsWithParam(fx_disable_button_ref, ACT_TURN);
4032 	act->ApplyKit(false, act->GetClassID(ISPALADIN));
4033 }
4034 
RegainRangerHood(Scriptable * Sender,Action *)4035 void GameScript::RegainRangerHood(Scriptable* Sender, Action* /*parameters*/)
4036 {
4037 	if (Sender->Type!=ST_ACTOR) {
4038 		return;
4039 	}
4040 	Actor *act = (Actor *) Sender;
4041 	act->SetMCFlag(MC_FALLEN_RANGER, OP_NAND);
4042 	act->fxqueue.RemoveAllEffectsWithParam(fx_disable_button_ref, ACT_CAST);
4043 	act->fxqueue.RemoveAllEffectsWithParam(fx_disable_button_ref, ACT_STEALTH);
4044 	act->ApplyKit(false, act->GetClassID(ISRANGER));
4045 }
4046 
4047 //transfering item from Sender to target, target must be an actor
4048 //if target can't get it, it will be dropped at its feet
4049 //a container or an actor can take an item from someone
GetItem(Scriptable * Sender,Action * parameters)4050 void GameScript::GetItem(Scriptable* Sender, Action* parameters)
4051 {
4052 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4053 	if (!tar) {
4054 		return;
4055 	}
4056 	MoveItemCore(tar, Sender, parameters->string0Parameter,0,0);
4057 }
4058 
4059 //getting one single item
TakePartyItem(Scriptable * Sender,Action * parameters)4060 void GameScript::TakePartyItem(Scriptable* Sender, Action* parameters)
4061 {
4062 	Game *game=core->GetGame();
4063 	int i=game->GetPartySize(false);
4064 	while (i--) {
4065 		int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE,IE_INV_ITEM_UNSTEALABLE);
4066 		if (res!=MIC_NOITEM) return;
4067 	}
4068 }
4069 
4070 //getting x single item
TakePartyItemNum(Scriptable * Sender,Action * parameters)4071 void GameScript::TakePartyItemNum(Scriptable* Sender, Action* parameters)
4072 {
4073 	int count = parameters->int0Parameter;
4074 	Game *game=core->GetGame();
4075 	int i=game->GetPartySize(false);
4076 	while (i-- && count) {
4077 		Actor *pc = game->GetPC(i, false);
4078 		int res = MoveItemCore(pc, Sender, parameters->string0Parameter, IE_INV_ITEM_UNDROPPABLE, IE_INV_ITEM_UNSTEALABLE, 1);
4079 		if (res == MIC_GOTITEM) {
4080 			i++;
4081 			count--;
4082 		}
4083 	}
4084 }
4085 
TakePartyItemRange(Scriptable * Sender,Action * parameters)4086 void GameScript::TakePartyItemRange(Scriptable* Sender, Action* parameters)
4087 {
4088 	Game *game=core->GetGame();
4089 	int i=game->GetPartySize(false);
4090 	while (i--) {
4091 		Actor *ac = game->GetPC(i,false);
4092 		if (Distance(Sender, ac)<MAX_OPERATING_DISTANCE) {
4093 			while (MoveItemCore(ac, Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE,IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { }
4094 		}
4095 	}
4096 }
4097 
TakePartyItemAll(Scriptable * Sender,Action * parameters)4098 void GameScript::TakePartyItemAll(Scriptable* Sender, Action* parameters)
4099 {
4100 	Game *game=core->GetGame();
4101 	int i=game->GetPartySize(false);
4102 	while (i--) {
4103 		while (MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE, IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { }
4104 	}
4105 }
4106 
4107 //an actor can 'give' an item to a container or another actor
GiveItem(Scriptable * Sender,Action * parameters)4108 void GameScript::GiveItem(Scriptable *Sender, Action* parameters)
4109 {
4110 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4111 	MoveItemCore(Sender, tar, parameters->string0Parameter,0,0);
4112 }
4113 
4114 //this action creates an item in a container or a creature
4115 //if there is an object it works as GiveItemCreate
4116 //otherwise it creates the item on the Sender
CreateItem(Scriptable * Sender,Action * parameters)4117 void GameScript::CreateItem(Scriptable *Sender, Action* parameters)
4118 {
4119 	Scriptable* tar;
4120 	if (parameters->objects[1]) {
4121 		tar = GetActorFromObject( Sender, parameters->objects[1] );
4122 	} else {
4123 		tar = Sender;
4124 	}
4125 	if (!tar)
4126 		return;
4127 	Inventory *myinv;
4128 
4129 	switch(tar->Type) {
4130 		case ST_ACTOR:
4131 			myinv = &((Actor *) tar)->inventory;
4132 			break;
4133 		case ST_CONTAINER:
4134 			myinv = &((Container *) tar)->inventory;
4135 			break;
4136 		default:
4137 			return;
4138 	}
4139 
4140 	CREItem *item = new CREItem();
4141 	if (!CreateItemCore(item, parameters->string0Parameter, parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter)) {
4142 		delete item;
4143 		return;
4144 	}
4145 	if (tar->Type==ST_CONTAINER) {
4146 		myinv->AddItem(item);
4147 	} else {
4148 		Actor *act = (Actor *) tar;
4149 		if ( ASI_SUCCESS != myinv->AddSlotItem(item, SLOT_ONLYINVENTORY)) {
4150 			Map *map=tar->GetCurrentArea();
4151 			// drop it at my feet
4152 			map->AddItemToLocation(tar->Pos, item);
4153 			if (act->InParty) {
4154 				act->VerbalConstant(VB_INVENTORY_FULL);
4155 				if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, DMC_BG2XPGREEN);
4156 			}
4157 		} else {
4158 			if (act->InParty && core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_GOTITEM, DMC_BG2XPGREEN);
4159 		}
4160 	}
4161 }
4162 
CreateItemNumGlobal(Scriptable * Sender,Action * parameters)4163 void GameScript::CreateItemNumGlobal(Scriptable *Sender, Action* parameters)
4164 {
4165 	Inventory *myinv;
4166 
4167 	switch(Sender->Type) {
4168 		case ST_ACTOR:
4169 			myinv = &((Actor *) Sender)->inventory;
4170 			break;
4171 		case ST_CONTAINER:
4172 			myinv = &((Container *) Sender)->inventory;
4173 			break;
4174 		default:
4175 			return;
4176 	}
4177 	int value = CheckVariable( Sender, parameters->string0Parameter );
4178 	CREItem *item = new CREItem();
4179 	if (!CreateItemCore(item, parameters->string1Parameter, value, 0, 0)) {
4180 		delete item;
4181 		return;
4182 	}
4183 	if (Sender->Type==ST_CONTAINER) {
4184 		myinv->AddItem(item);
4185 	} else {
4186 		Actor *act = (Actor *) Sender;
4187 		if ( ASI_SUCCESS != myinv->AddSlotItem(item, SLOT_ONLYINVENTORY)) {
4188 			Map *map=Sender->GetCurrentArea();
4189 			// drop it at my feet
4190 			map->AddItemToLocation(Sender->Pos, item);
4191 			if (act->InParty) {
4192 				act->VerbalConstant(VB_INVENTORY_FULL);
4193 				if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, DMC_BG2XPGREEN);
4194 			}
4195 		} else {
4196 			if (act->InParty && core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_GOTITEM, DMC_BG2XPGREEN);
4197 		}
4198 	}
4199 }
4200 
SetItemFlags(Scriptable * Sender,Action * parameters)4201 void GameScript::SetItemFlags(Scriptable *Sender, Action* parameters)
4202 {
4203 	Scriptable* tar;
4204 	if (parameters->objects[1]) {
4205 		tar = GetActorFromObject(Sender, parameters->objects[1]);
4206 	} else {
4207 		tar = Sender;
4208 	}
4209 	if (!tar) return;
4210 
4211 	Inventory *myinv;
4212 	switch(tar->Type) {
4213 		case ST_ACTOR:
4214 			myinv = &((Actor *) tar)->inventory;
4215 			break;
4216 		case ST_CONTAINER:
4217 			myinv = &((Container *) tar)->inventory;
4218 			break;
4219 		default:
4220 			return;
4221 	}
4222 
4223 	int slot = myinv->FindItem(parameters->string0Parameter, 0);
4224 	if (slot == -1) {
4225 		Log(ERROR, "GameScript", "Item %s not found in inventory of %s", parameters->string0Parameter, tar->GetScriptName());
4226 		return;
4227 	}
4228 
4229 	int op = OP_NAND;
4230 	if (parameters->int1Parameter) op = OP_OR;
4231 	myinv->ChangeItemFlag(slot, parameters->int0Parameter, op);
4232 }
4233 
TakeItemReplace(Scriptable * Sender,Action * parameters)4234 void GameScript::TakeItemReplace(Scriptable *Sender, Action* parameters)
4235 {
4236 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4237 	if (!tar || tar->Type != ST_ACTOR) {
4238 		return;
4239 	}
4240 
4241 	Actor *scr = (Actor *) tar;
4242 	CREItem *item;
4243 	int slot = scr->inventory.RemoveItem(parameters->string1Parameter, IE_INV_ITEM_UNDROPPABLE, &item);
4244 	if (!item) {
4245 		item = new CREItem();
4246 	}
4247 	if (!CreateItemCore(item, parameters->string0Parameter, -1, 0, 0)) {
4248 		delete item;
4249 		return;
4250 	}
4251 	if (ASI_SUCCESS != scr->inventory.AddSlotItem(item,slot)) {
4252 		Map *map = scr->GetCurrentArea();
4253 		map->AddItemToLocation(Sender->Pos, item);
4254 	}
4255 }
4256 
4257 //same as equipitem, but with additional slots parameter, and object to perform action
4258 // XEquipItem("00Troll1",Myself,SLOT_RING_LEFT,TRUE)
XEquipItem(Scriptable * Sender,Action * parameters)4259 void GameScript::XEquipItem(Scriptable *Sender, Action* parameters)
4260 {
4261 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4262 
4263 	if (!tar || tar->Type!=ST_ACTOR) {
4264 		return;
4265 	}
4266 	Actor *actor = (Actor *) tar;
4267 	int slot = actor->inventory.FindItem(parameters->string0Parameter, IE_INV_ITEM_UNDROPPABLE);
4268 	if (slot<0) {
4269 		return;
4270 	}
4271 
4272 	int slot2 = parameters->int0Parameter;
4273 	bool equip = parameters->int1Parameter;
4274 
4275 	if (equip) {
4276 		if (slot != slot2) {
4277 			// swap them first, so we equip to the desired slot
4278 			CREItem *si = actor->inventory.RemoveItem(slot);
4279 			if (actor->inventory.AddSlotItem(si, slot2) != ASI_SUCCESS) {
4280 				// should never happen, since we just made room
4281 				error("Actions", "XEquip: suddenly no slots left!\n");
4282 			}
4283 		}
4284 		actor->inventory.EquipItem(slot2);
4285 	} else {
4286 		CREItem *si = actor->inventory.RemoveItem(slot);
4287 		if (si) {
4288 			if (actor->inventory.AddSlotItem(si, SLOT_ONLYINVENTORY) == ASI_FAILED) {
4289 				Map *map = Sender->GetCurrentArea();
4290 				if (map) {
4291 					//drop item at the feet of the character instead of destroying it
4292 					map->AddItemToLocation(Sender->Pos, si);
4293 				} else {
4294 					delete si;
4295 				}
4296 			}
4297 		}
4298 	}
4299 
4300 	actor->ReinitQuickSlots();
4301 }
4302 
4303 //GemRB extension: if int1Parameter is nonzero, don't destroy existing items
FillSlot(Scriptable * Sender,Action * parameters)4304 void GameScript::FillSlot(Scriptable *Sender, Action* parameters)
4305 {
4306 	if (Sender->Type!=ST_ACTOR) {
4307 		return;
4308 	}
4309 
4310 	CREItem *tmp = NULL;
4311 	Actor *actor = (Actor *) Sender;
4312 	int slot = parameters->int0Parameter;
4313 
4314 	//free up target slot
4315 	tmp = actor->inventory.RemoveItem(slot);
4316 
4317 	actor->inventory.TryEquipAll(slot);
4318 
4319 	if (tmp) {
4320 		if (actor->inventory.HasItemInSlot("",slot) ) {
4321 			slot = SLOT_ONLYINVENTORY;
4322 		}
4323 
4324 		//reequip original item
4325 		if(actor->inventory.AddSlotItem(tmp, slot)!=ASI_SUCCESS) {
4326 			delete tmp;
4327 		}
4328 	}
4329 }
4330 
4331 //iwd2 also has a flag for unequip (it might collide with original!)
EquipItem(Scriptable * Sender,Action * parameters)4332 void GameScript::EquipItem(Scriptable *Sender, Action* parameters)
4333 {
4334 	if (Sender->Type!=ST_ACTOR) {
4335 		return;
4336 	}
4337 	Actor *actor = (Actor *) Sender;
4338 	int slot = actor->inventory.FindItem(parameters->string0Parameter, IE_INV_ITEM_UNDROPPABLE);
4339 	if (slot<0) {
4340 		return;
4341 	}
4342 
4343 	int slot2;
4344 
4345 	if (parameters->int0Parameter) {
4346 		//unequip item, and move it to the inventory
4347 		slot2 = SLOT_ONLYINVENTORY;
4348 	} else {
4349 		//equip item if possible
4350 		slot2 = SLOT_AUTOEQUIP;
4351 	}
4352 	CREItem *si = actor->inventory.RemoveItem(slot);
4353 	if (si) {
4354 		if (actor->inventory.AddSlotItem(si, slot2)==ASI_FAILED) {
4355 			Map *map = Sender->GetCurrentArea();
4356 			if (map) {
4357 				//drop item at the feet of the character instead of destroying it
4358 				map->AddItemToLocation(Sender->Pos, si);
4359 			} else {
4360 				delete si;
4361 			}
4362 		}
4363 	}
4364 	actor->ReinitQuickSlots();
4365 }
4366 
DropItem(Scriptable * Sender,Action * parameters)4367 void GameScript::DropItem(Scriptable *Sender, Action* parameters)
4368 {
4369 	if (Sender->Type!=ST_ACTOR) {
4370 		Sender->ReleaseCurrentAction();
4371 		return;
4372 	}
4373 
4374 	// iwd2 has two uses with [-1.-1]
4375 	if (parameters->pointParameter.x == -1) {
4376 		parameters->pointParameter.x = Sender->Pos.x;
4377 		parameters->pointParameter.y = Sender->Pos.y;
4378 	}
4379 
4380 	if (Distance(parameters->pointParameter, Sender) > 10) {
4381 		MoveNearerTo(Sender, parameters->pointParameter, 10,0);
4382 		return;
4383 	}
4384 	Actor *scr = (Actor *) Sender;
4385 	Map *map = Sender->GetCurrentArea();
4386 
4387 	if (parameters->string0Parameter[0]) {
4388 		//dropping location isn't exactly our place, this is why i didn't use a simple DropItem
4389 		scr->inventory.DropItemAtLocation(parameters->string0Parameter,
4390 0, map, parameters->pointParameter);
4391 	} else {
4392 		//this should be converted from scripting slot to physical slot
4393 		scr->inventory.DropItemAtLocation(parameters->int0Parameter, 0, map, parameters->pointParameter);
4394 	}
4395 
4396 	Sender->ReleaseCurrentAction();
4397 }
4398 
DropInventory(Scriptable * Sender,Action *)4399 void GameScript::DropInventory(Scriptable *Sender, Action* /*parameters*/)
4400 {
4401 	if (Sender->Type!=ST_ACTOR) {
4402 		return;
4403 	}
4404 	Actor *scr = (Actor *) Sender;
4405 	scr->DropItem("",0);
4406 }
4407 
4408 //this should work on containers!
4409 //using the same code for DropInventoryEXExclude
DropInventoryEX(Scriptable * Sender,Action * parameters)4410 void GameScript::DropInventoryEX(Scriptable *Sender, Action* parameters)
4411 {
4412 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4413 	if (!tar) {
4414 		return;
4415 	}
4416 	Inventory *inv = NULL;
4417 	switch (tar->Type) {
4418 		case ST_ACTOR:
4419 			inv = &(((Actor *) tar)->inventory);
4420 			break;
4421 		case ST_CONTAINER:
4422 			inv = &(((Container *) tar)->inventory);
4423 			break;
4424 		default:;
4425 	}
4426 	if (inv) {
4427 		int x = inv->GetSlotCount();
4428 		Map *area = tar->GetCurrentArea();
4429 		while(x--) {
4430 			if (parameters->string0Parameter[0]) {
4431 				const char *resref = inv->GetSlotItem(x)->ItemResRef;
4432 				if (!strnicmp(parameters->string0Parameter, resref, 8)) {
4433 					continue;
4434 				}
4435 			}
4436 			inv->DropItemAtLocation(x, 0, area, tar->Pos);
4437 		}
4438 	}
4439 }
4440 
GivePartyAllEquipment(Scriptable * Sender,Action *)4441 void GameScript::GivePartyAllEquipment(Scriptable *Sender, Action* /*parameters*/)
4442 {
4443 	if (Sender->Type!=ST_ACTOR) {
4444 		return;
4445 	}
4446 	Game *game = core->GetGame();
4447 	// pick the first actor first
4448 	for (int i = 0; i < game->GetPartySize(false); i++) {
4449 		Actor *tar = game->GetPC(i,false);
4450 		//don't try to give self, it would be an infinite loop
4451 		if (tar==(Actor *) Sender)
4452 			continue;
4453 		while(MoveItemCore(Sender, tar, "",0,0)!=MIC_NOITEM) { }
4454 	}
4455 }
4456 
4457 //This is unsure, Plunder could be just handling ground piles and not dead actors
Plunder(Scriptable * Sender,Action * parameters)4458 void GameScript::Plunder(Scriptable *Sender, Action* parameters)
4459 {
4460 	if (Sender->Type!=ST_ACTOR) {
4461 		Sender->ReleaseCurrentAction();
4462 		return;
4463 	}
4464 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
4465 	if (!tar) {
4466 		Sender->ReleaseCurrentAction();
4467 		return;
4468 	}
4469 
4470 	//you must be joking
4471 	if (tar==Sender) {
4472 		Sender->ReleaseCurrentAction();
4473 		return;
4474 	}
4475 
4476 	if (tar->Type == ST_ACTOR) {
4477 		Actor *scr = (Actor *) tar;
4478 		//can plunder only dead actors
4479 		if (! (scr->BaseStats[IE_STATE_ID]&STATE_DEAD) ) {
4480 			Sender->ReleaseCurrentAction();
4481 			return;
4482 		}
4483 	}
4484 	if (PersonalDistance(Sender, tar)>MAX_OPERATING_DISTANCE ) {
4485 		MoveNearerTo(Sender, tar->Pos, MAX_OPERATING_DISTANCE,0);
4486 		return;
4487 	}
4488 	//move all movable item from the target to the Sender
4489 	//the rest will be dropped at the feet of Sender
4490 	while(MoveItemCore(tar, Sender, "",0,0)!=MIC_NOITEM) { }
4491 	Sender->ReleaseCurrentAction();
4492 }
4493 
MoveInventory(Scriptable * Sender,Action * parameters)4494 void GameScript::MoveInventory(Scriptable *Sender, Action* parameters)
4495 {
4496 	Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] );
4497 	if (!src || src->Type!=ST_ACTOR) {
4498 		return;
4499 	}
4500 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2] );
4501 	if (!tar || tar->Type!=ST_ACTOR) {
4502 		return;
4503 	}
4504 	//don't try to move to self, it would create infinite loop
4505 	if (src==tar)
4506 		return;
4507 	//move all movable item from the target to the Sender
4508 	//the rest will be dropped at the feet of Sender
4509 	while(MoveItemCore(src, tar, "",0,0)!=MIC_NOITEM) { }
4510 }
4511 
PickPockets(Scriptable * Sender,Action * parameters)4512 void GameScript::PickPockets(Scriptable *Sender, Action* parameters)
4513 {
4514 	if (Sender->Type!=ST_ACTOR) {
4515 		Sender->ReleaseCurrentAction();
4516 		return;
4517 	}
4518 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
4519 	if (!tar || tar->Type!=ST_ACTOR) {
4520 		Sender->ReleaseCurrentAction();
4521 		return;
4522 	}
4523 	Actor *snd = (Actor *) Sender;
4524 	Actor *scr = (Actor *) tar;
4525 	//for PP one must go REALLY close
4526 	Map *map=Sender->GetCurrentArea();
4527 	if (!map) {
4528 		Sender->ReleaseCurrentAction();
4529 		return;
4530 	}
4531 
4532 	if (PersonalDistance(Sender, tar)>10 ) {
4533 		MoveNearerTo(Sender, tar, 10);
4534 		return;
4535 	}
4536 
4537 	if (scr->GetStat(IE_EA)>EA_EVILCUTOFF) {
4538 		if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_PICKPOCKET_EVIL, DMC_WHITE);
4539 		Sender->ReleaseCurrentAction();
4540 		return;
4541 	}
4542 
4543 	int skill = snd->GetStat(IE_PICKPOCKET);
4544 	int tgt = scr->GetStat(IE_PICKPOCKET);
4545 	int check;
4546 	if (core->HasFeature(GF_3ED_RULES)) {
4547 		int skill = snd->GetSkill(IE_PICKPOCKET);
4548 		int roll = core->Roll(1, 20, 0);
4549 		int level = scr->GetXPLevel(true);
4550 		int wismod = scr->GetAbilityBonus(IE_WIS);
4551 		// ~Pick pocket check. (10 + skill w/Dex bonus) %d vs. ((d20 + target's level) + Wisdom modifier) %d + %d.~
4552 		displaymsg->DisplayRollStringName(39302, DMC_LIGHTGREY, snd, 10+skill, roll+level, wismod);
4553 		check = (10 + skill) > (roll + level + wismod);
4554 		if (skill == 0) { // a trained skill, make sure we fail
4555 			check = 1;
4556 		}
4557 	} else {
4558 		//the original engine has no random here
4559 		if (tgt != 255) {
4560 			skill -= tgt;
4561 			//if you want original behaviour: remove this
4562 			skill += snd->LuckyRoll(1, 100, 0);
4563 		} else {
4564 			skill = 0;
4565 		}
4566 		//and change this 50 to 0.
4567 		check = skill < 50;
4568 	}
4569 	if (check) {
4570 		//noticed attempt
4571 		if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_PICKPOCKET_FAIL, DMC_WHITE);
4572 		if (core->HasFeature(GF_STEAL_IS_ATTACK) ) {
4573 			scr->AttackedBy(snd);
4574 		} else {
4575 			//pickpocket failed trigger
4576 			tar->AddTrigger(TriggerEntry(trigger_pickpocketfailed, snd->GetGlobalID()));
4577 		}
4578 		Sender->ReleaseCurrentAction();
4579 		return;
4580 	}
4581 
4582 	int ret = MIC_NOITEM;
4583 	if ((RandomNumValue&3) || (scr->GetStat(IE_GOLD)<=0) ) {
4584 		int slot = scr->inventory.FindStealableItem();
4585 		if (slot != -1) {
4586 			CREItem *item = scr->inventory.RemoveItem(slot);
4587 			ret = snd->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY);
4588 			if (ret!=ASI_SUCCESS) {
4589 				map->AddItemToLocation(snd->Pos, item);
4590 				ret = MIC_FULL;
4591 			}
4592 		}
4593 	}
4594 
4595 	if (ret==MIC_NOITEM) {
4596 		int money=0;
4597 		//go for money too
4598 		if (scr->GetStat(IE_GOLD)>0) {
4599 			money = (RandomNumValue % scr->GetStat(IE_GOLD)) + 1;
4600 		}
4601 		if (!money) {
4602 			//no stuff to steal
4603 			if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_PICKPOCKET_NONE, DMC_WHITE);
4604 			Sender->ReleaseCurrentAction();
4605 			return;
4606 		}
4607 		CREItem *item = new CREItem();
4608 		if (!CreateItemCore(item, core->GoldResRef, money, 0, 0)) {
4609 			error("GameScript", "Failed to create pick-pocketed gold '%s' %dg.\n", core->GoldResRef, money);
4610 		}
4611 		scr->SetBase(IE_GOLD, scr->GetBase(IE_GOLD) - money);
4612 		if (ASI_SUCCESS != snd->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY)) {
4613 			// drop it at my feet
4614 			map->AddItemToLocation(snd->Pos, item);
4615 			ret = MIC_FULL;
4616 		}
4617 	}
4618 
4619 	if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_PICKPOCKET_DONE, DMC_WHITE);
4620 	DisplayStringCore(snd, VB_PP_SUCC, DS_CONSOLE|DS_CONST );
4621 	if (ret == MIC_FULL && snd->InParty) {
4622 		snd->VerbalConstant(VB_INVENTORY_FULL);
4623 		if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, DMC_BG2XPGREEN);
4624 	}
4625 	Sender->ReleaseCurrentAction();
4626 }
4627 
TakeItemList(Scriptable * Sender,Action * parameters)4628 void GameScript::TakeItemList(Scriptable * Sender, Action* parameters)
4629 {
4630 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4631 	if (!tar || tar->Type!=ST_ACTOR) {
4632 		return;
4633 	}
4634 	AutoTable tab(parameters->string0Parameter);
4635 	if (!tab) {
4636 		return;
4637 	}
4638 
4639 	int rows = tab->GetRowCount();
4640 	for (int i=0;i<rows;i++) {
4641 		MoveItemCore(tar, Sender, tab->QueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE);
4642 	}
4643 }
4644 
TakeItemListParty(Scriptable * Sender,Action * parameters)4645 void GameScript::TakeItemListParty(Scriptable * Sender, Action* parameters)
4646 {
4647 	AutoTable tab(parameters->string0Parameter);
4648 	if (!tab) {
4649 		return;
4650 	}
4651 	Game *game = core->GetGame();
4652 	int rows = tab->GetRowCount();
4653 	for (int i=0;i<rows;i++) {
4654 		int j = game->GetPartySize(false);
4655 		while (j--) {
4656 			Actor *tar = game->GetPC(j, false);
4657 			MoveItemCore(tar, Sender, tab->QueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE);
4658 		}
4659 	}
4660 }
4661 
TakeItemListPartyNum(Scriptable * Sender,Action * parameters)4662 void GameScript::TakeItemListPartyNum(Scriptable * Sender, Action* parameters)
4663 {
4664 	AutoTable tab(parameters->string0Parameter);
4665 	if (!tab) {
4666 		return;
4667 	}
4668 	Game *game = core->GetGame();
4669 	int rows = tab->GetRowCount();
4670 	int count = parameters->int0Parameter;
4671 	for (int i=0;i<rows;i++) {
4672 		int j = game->GetPartySize(false);
4673 		while (j--) {
4674 			Actor *tar = game->GetPC(j, false);
4675 			int res=MoveItemCore(tar, Sender, tab->QueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE);
4676 			if (res==MIC_GOTITEM) {
4677 				j++;
4678 				count--;
4679 			}
4680 			if (!count) break;
4681 		}
4682 	}
4683 	if (count == 1) {
4684 		// grant the default table item to the Sender in regular games
4685 		Action *params = new Action(true);
4686 		snprintf(params->string0Parameter, sizeof(params->string0Parameter), "%s", tab->QueryDefault());
4687 		CreateItem(Sender, params);
4688 		delete params;
4689 	}
4690 }
4691 
4692 //bg2
SetRestEncounterProbabilityDay(Scriptable * Sender,Action * parameters)4693 void GameScript::SetRestEncounterProbabilityDay(Scriptable* Sender, Action* parameters)
4694 {
4695 	Map *map=Sender->GetCurrentArea();
4696 	map->RestHeader.DayChance = (ieWord) parameters->int0Parameter;
4697 }
4698 
SetRestEncounterProbabilityNight(Scriptable * Sender,Action * parameters)4699 void GameScript::SetRestEncounterProbabilityNight(Scriptable* Sender, Action* parameters)
4700 {
4701 	Map *map=Sender->GetCurrentArea();
4702 	map->RestHeader.NightChance = (ieWord) parameters->int0Parameter;
4703 }
4704 
4705 //iwd
SetRestEncounterChance(Scriptable * Sender,Action * parameters)4706 void GameScript::SetRestEncounterChance(Scriptable * Sender, Action* parameters)
4707 {
4708 	Map *map=Sender->GetCurrentArea();
4709 	map->RestHeader.DayChance = (ieWord) parameters->int0Parameter;
4710 	map->RestHeader.NightChance = (ieWord) parameters->int1Parameter;
4711 }
4712 
4713 //easily hardcoded end sequence
EndCredits(Scriptable * Sender,Action * parameters)4714 void GameScript::EndCredits(Scriptable* Sender, Action* parameters)
4715 {
4716 	if (gamedata->Exists("25ecred", IE_2DA_CLASS_ID, true)) {
4717 		/* ToB */
4718 		ExecuteString(Sender, "TextScreen(\"25ecred\")");
4719 	} else {
4720 		core->PlayMovie("credits");
4721 		QuitGame(Sender, parameters);
4722 	}
4723 }
4724 
4725 //easily hardcoded end sequence
ExpansionEndCredits(Scriptable * Sender,Action * parameters)4726 void GameScript::ExpansionEndCredits(Scriptable *Sender, Action *parameters)
4727 {
4728 	core->PlayMovie("ecredit");
4729 	QuitGame(Sender, parameters);
4730 }
4731 
4732 //always quits game, but based on game it can play end animation, or display
4733 //death text, etc
4734 //this covers:
4735 //QuitGame (play two of 3 movies in PST, display death screen with strref; names also in movval.ids)
4736 //EndGame (display death screen with strref)
QuitGame(Scriptable * Sender,Action * parameters)4737 void GameScript::QuitGame(Scriptable* Sender, Action* parameters)
4738 {
4739 	ClearAllActions(Sender, parameters);
4740 	core->GetDictionary()->SetAt("QuitGame1", (ieDword) parameters->int0Parameter);
4741 	core->GetDictionary()->SetAt("QuitGame2", (ieDword) parameters->int1Parameter);
4742 	core->GetDictionary()->SetAt("QuitGame3", (ieDword) parameters->int2Parameter);
4743 	core->SetNextScript("QuitGame");
4744 }
4745 
4746 //BG2 demo end, shows some pictures then goes to main screen
DemoEnd(Scriptable * Sender,Action * parameters)4747 void GameScript::DemoEnd(Scriptable* Sender, Action* parameters)
4748 {
4749 	ClearAllActions(Sender, parameters);
4750 	core->GetDictionary()->SetAt("QuitGame1", (ieDword)0);
4751 	core->GetDictionary()->SetAt("QuitGame2", (ieDword)0);
4752 	core->GetDictionary()->SetAt("QuitGame3", (ieDword)-1);
4753 	core->SetNextScript("QuitGame");
4754 }
4755 
StopMoving(Scriptable * Sender,Action *)4756 void GameScript::StopMoving(Scriptable* Sender, Action* /*parameters*/)
4757 {
4758 	if (Sender->Type != ST_ACTOR) {
4759 		return;
4760 	}
4761 	Actor *actor = (Actor *) Sender;
4762 	actor->ClearPath();
4763 }
4764 
ApplyDamage(Scriptable * Sender,Action * parameters)4765 void GameScript::ApplyDamage(Scriptable* Sender, Action* parameters)
4766 {
4767 	Actor *damagee;
4768 	Actor *damager;
4769 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4770 	if (!tar || tar->Type!=ST_ACTOR) {
4771 		return;
4772 	}
4773 	damagee = (Actor *) tar;
4774 	if (Sender->Type==ST_ACTOR) {
4775 		damager=(Actor *) Sender;
4776 	} else {
4777 		damager=damagee;
4778 	}
4779 	damagee->Damage(parameters->int0Parameter, parameters->int1Parameter >> 16, damager);
4780 }
4781 
ApplyDamagePercent(Scriptable * Sender,Action * parameters)4782 void GameScript::ApplyDamagePercent(Scriptable* Sender, Action* parameters)
4783 {
4784 	Actor *damagee;
4785 	Actor *damager;
4786 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4787 	if (!tar || tar->Type!=ST_ACTOR) {
4788 		return;
4789 	}
4790 	damagee = (Actor *) tar;
4791 	if (Sender->Type==ST_ACTOR) {
4792 		damager=(Actor *) Sender;
4793 	} else {
4794 		damager=damagee;
4795 	}
4796 	//this, if the percent is calculated from the current hp
4797 	damagee->Damage((parameters->int0Parameter*damagee->Modified[IE_HITPOINTS])/100, parameters->int1Parameter >> 16, damager);
4798 	//this, if the percent is calculated from the max hp
4799 	//damagee->Damage(parameters->int0Parameter, parameters->int1Parameter >> 16, damager, MOD_PERCENT);
4800 }
4801 
Damage(Scriptable * Sender,Action * parameters)4802 void GameScript::Damage(Scriptable* Sender, Action* parameters)
4803 {
4804 	Actor *damagee;
4805 	Scriptable *damager = Sender;
4806 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4807 	if (!tar || tar->Type!=ST_ACTOR) {
4808 		return;
4809 	}
4810 
4811 	// bones.ids handling
4812 	int diceNum = (parameters->int1Parameter>>12)&15;
4813 	int diceSize = (parameters->int1Parameter>>4)&255;
4814 	int diceAdd = parameters->int1Parameter&15;
4815 	int damage = 0;
4816 	damagee = (Actor *) tar;
4817 	Actor *damager2 = NULL;
4818 	if (Sender->Type==ST_ACTOR) {
4819 		damager2 = (Actor *) Sender;
4820 	}
4821 
4822 	if (damager2 && damager2 != damagee) {
4823 		damage = damager2->LuckyRoll(diceNum, diceSize, diceAdd, LR_DAMAGELUCK, damagee);
4824 	} else {
4825 		damage = core->Roll(diceNum, diceSize, diceAdd);
4826 	}
4827 	int type=MOD_ADDITIVE;
4828 	// delta.ids
4829 	switch(parameters->int0Parameter) {
4830 	case DM_LOWER: // lower
4831 		break;
4832 	case DM_RAISE: //raise
4833 		damage=-damage;
4834 		break;
4835 	case DM_SET: //set
4836 		type=MOD_ABSOLUTE;
4837 		break;
4838 	case 4: // GemRB extension
4839 		type=MOD_PERCENT;
4840 		break;
4841 	// NOTE: forge.d has a bunch of calls missing a parameter, eg. Damage(Protagonist, 15)
4842 	// it's unclear if it worked, but let's accommodate it
4843 	default:
4844 		damage = parameters->int0Parameter;
4845 		break;
4846 	}
4847 	//damagetype seems to be always 0
4848 	damagee->Damage( damage, 0, damager, type);
4849 }
4850 
SetHomeLocation(Scriptable * Sender,Action * parameters)4851 void GameScript::SetHomeLocation(Scriptable* Sender, Action* parameters)
4852 {
4853 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
4854 	if (!tar || tar->Type!=ST_ACTOR) {
4855 		return;
4856 	}
4857 	Actor *movable = (Actor *) tar;
4858 	movable->HomeLocation = parameters->pointParameter;
4859 	//no movement should be started here, i think
4860 }
4861 
SetMasterArea(Scriptable *,Action * parameters)4862 void GameScript::SetMasterArea(Scriptable* /*Sender*/, Action* parameters)
4863 {
4864 	core->GetGame()->SetMasterArea(parameters->string0Parameter);
4865 }
4866 
Berserk(Scriptable * Sender,Action *)4867 void GameScript::Berserk(Scriptable* Sender, Action* /*parameters*/)
4868 {
4869 	if (Sender->Type!=ST_ACTOR) {
4870 		return;
4871 	}
4872 
4873 	const Map *map = Sender->GetCurrentArea();
4874 	if (!map) {
4875 		return;
4876 	}
4877 
4878 	const Actor *act = (const Actor *) Sender;
4879 	const Actor *target;
4880 
4881 	if (!act->GetStat(IE_BERSERKSTAGE2) && (core->Roll(1,100,0)<50) ) {
4882 		//anyone
4883 		target = GetNearestEnemyOf(map, act, ORIGIN_SEES_ENEMY);
4884 	} else {
4885 		target = GetNearestOf(map, act, ORIGIN_SEES_ENEMY);
4886 	}
4887 
4888 	if (!target) {
4889 		Sender->SetWait(6);
4890 	} else {
4891 		//generate attack action
4892 		Action *newaction = GenerateActionDirect("NIDSpecial3()", target);
4893 		if (newaction) {
4894 			Sender->AddActionInFront(newaction);
4895 		}
4896 	}
4897 	Sender->ReleaseCurrentAction();
4898 }
4899 
Panic(Scriptable * Sender,Action *)4900 void GameScript::Panic(Scriptable* Sender, Action* /*parameters*/)
4901 {
4902 	if (Sender->Type!=ST_ACTOR) {
4903 		return;
4904 	}
4905 	Actor *act = (Actor *) Sender;
4906 	act->Panic(NULL, PANIC_RANDOMWALK);
4907 }
4908 
4909 /* as of now: removes panic and berserk */
Calm(Scriptable * Sender,Action *)4910 void GameScript::Calm(Scriptable* Sender, Action* /*parameters*/)
4911 {
4912 	if (Sender->Type!=ST_ACTOR) {
4913 		return;
4914 	}
4915 	Actor *act = (Actor *) Sender;
4916 	act->SetBaseBit(IE_STATE_ID, STATE_BERSERK|STATE_PANIC, false);
4917 }
4918 
RevealAreaOnMap(Scriptable *,Action * parameters)4919 void GameScript::RevealAreaOnMap(Scriptable* /*Sender*/, Action* parameters)
4920 {
4921 	WorldMap *worldmap = core->GetWorldMap();
4922 	if (!worldmap) {
4923 		error("GameScript", "Can't find worldmap!\n");
4924 	}
4925 	// WMP_ENTRY_ADJACENT because otherwise revealed bg2 areas are unreachable from city gates
4926 	worldmap->SetAreaStatus(parameters->string0Parameter, WMP_ENTRY_VISIBLE|WMP_ENTRY_ADJACENT, OP_OR);
4927 	displaymsg->DisplayConstantString(STR_WORLDMAPCHANGE, DMC_BG2XPGREEN);
4928 }
4929 
HideAreaOnMap(Scriptable *,Action * parameters)4930 void GameScript::HideAreaOnMap( Scriptable* /*Sender*/, Action* parameters)
4931 {
4932 	WorldMap *worldmap = core->GetWorldMap();
4933 	if (!worldmap) {
4934 		error("GameScript", "Can't find worldmap!\n");
4935 	}
4936 	// WMP_ENTRY_ADJACENT because otherwise revealed bg2 areas are unreachable from city gates
4937 	worldmap->SetAreaStatus(parameters->string0Parameter, WMP_ENTRY_VISIBLE|WMP_ENTRY_ADJACENT, OP_NAND);
4938 }
4939 
AddWorldmapAreaFlag(Scriptable *,Action * parameters)4940 void GameScript::AddWorldmapAreaFlag(Scriptable* /*Sender*/, Action* parameters)
4941 {
4942 	WorldMap *worldmap = core->GetWorldMap();
4943 	if (!worldmap) {
4944 		error("GameScript", "Can't find worldmap!\n");
4945 	}
4946 	worldmap->SetAreaStatus(parameters->string0Parameter, parameters->int0Parameter, OP_OR);
4947 }
4948 
RemoveWorldmapAreaFlag(Scriptable *,Action * parameters)4949 void GameScript::RemoveWorldmapAreaFlag(Scriptable* /*Sender*/, Action* parameters)
4950 {
4951 	WorldMap *worldmap = core->GetWorldMap();
4952 	if (!worldmap) {
4953 		error("GameScript", "Can't find worldmap!\n");
4954 	}
4955 	worldmap->SetAreaStatus(parameters->string0Parameter, parameters->int0Parameter, OP_NAND);
4956 }
4957 
SendTrigger(Scriptable * Sender,Action * parameters)4958 void GameScript::SendTrigger(Scriptable* Sender, Action* parameters)
4959 {
4960 	Scriptable *tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
4961 	if (!tar) {
4962 		return;
4963 	}
4964 	tar->AddTrigger(TriggerEntry(trigger_trigger, parameters->int0Parameter));
4965 }
4966 
Shout(Scriptable * Sender,Action * parameters)4967 void GameScript::Shout( Scriptable* Sender, Action* parameters)
4968 {
4969 	if (Sender->Type!=ST_ACTOR) {
4970 		return;
4971 	}
4972 	//according to IESDP silenced creatures cannot use shout
4973 	// neither do dead ones or the paladin ogres turn Garren hostile
4974 	Actor *actor = (Actor *) Sender;
4975 	if (actor->GetStat(IE_STATE_ID) & (STATE_SILENCED|STATE_DEAD)) {
4976 		return;
4977 	}
4978 	Map *map=Sender->GetCurrentArea();
4979 	map->Shout(actor, parameters->int0Parameter, false);
4980 }
4981 
GlobalShout(Scriptable * Sender,Action * parameters)4982 void GameScript::GlobalShout( Scriptable* Sender, Action* parameters)
4983 {
4984 	if (Sender->Type!=ST_ACTOR) {
4985 		return;
4986 	}
4987 	//according to IESDP silenced creatures cannot use shout
4988 	Actor *actor = (Actor *) Sender;
4989 	if (actor->GetStat(IE_STATE_ID) & (STATE_SILENCED|STATE_DEAD)) {
4990 		return;
4991 	}
4992 	Map *map=Sender->GetCurrentArea();
4993 	// true means global, unlimited, shout distance
4994 	map->Shout(actor, parameters->int0Parameter, true);
4995 }
4996 
Help(Scriptable * Sender,Action *)4997 void GameScript::Help( Scriptable* Sender, Action* /*parameters*/)
4998 {
4999 	if (Sender->Type!=ST_ACTOR) {
5000 		return;
5001 	}
5002 	//TODO: add state limiting like in Shout?
5003 	Map *map=Sender->GetCurrentArea();
5004 	map->Shout((Actor *) Sender, 0, false);
5005 }
5006 
GiveOrder(Scriptable * Sender,Action * parameters)5007 void GameScript::GiveOrder(Scriptable* Sender, Action* parameters)
5008 {
5009 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5010 	if (tar) {
5011 		tar->AddTrigger(TriggerEntry(trigger_receivedorder, Sender->GetGlobalID(), parameters->int0Parameter));
5012 	}
5013 }
5014 
AddMapnote(Scriptable * Sender,Action * parameters)5015 void GameScript::AddMapnote( Scriptable* Sender, Action* parameters)
5016 {
5017 	Map *map=Sender->GetCurrentArea();
5018 	map->AddMapNote(parameters->pointParameter, parameters->int1Parameter, parameters->int0Parameter);
5019 }
5020 
RemoveMapnote(Scriptable * Sender,Action * parameters)5021 void GameScript::RemoveMapnote( Scriptable* Sender, Action* parameters)
5022 {
5023 	Map *map=Sender->GetCurrentArea();
5024 	map->RemoveMapNote(parameters->pointParameter);
5025 }
5026 
AttackOneRound(Scriptable * Sender,Action * parameters)5027 void GameScript::AttackOneRound( Scriptable* Sender, Action* parameters)
5028 {
5029 	if (Sender->Type != ST_ACTOR) {
5030 		Sender->ReleaseCurrentAction();
5031 		return;
5032 	}
5033 	//using auto target!
5034 	Scriptable* tar;
5035 	tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5036 	if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) {
5037 		Sender->ReleaseCurrentAction();
5038 		return;
5039 	}
5040 
5041 	//actor is already incapable of attack
5042 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
5043 		Sender->ReleaseCurrentAction();
5044 		return;
5045 	}
5046 
5047 	if (!Sender->CurrentActionState) {
5048 		Sender->CurrentActionState = core->Time.round_size;
5049 	}
5050 
5051 	AttackCore(Sender, tar, 0);
5052 
5053 	if (Sender->CurrentActionState <= 1) {
5054 		Sender->ReleaseCurrentAction();
5055 	} else {
5056 		Sender->CurrentActionState--;
5057 	}
5058 }
5059 
RunningAttackNoSound(Scriptable * Sender,Action * parameters)5060 void GameScript::RunningAttackNoSound( Scriptable* Sender, Action* parameters)
5061 {
5062 	if (Sender->Type != ST_ACTOR) {
5063 		Sender->ReleaseCurrentAction();
5064 		return;
5065 	}
5066 	//using auto target!
5067 	Scriptable* tar;
5068 	tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5069 	if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) {
5070 		Sender->ReleaseCurrentAction();
5071 		return;
5072 	}
5073 
5074 	//actor is already incapable of attack
5075 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
5076 		Sender->ReleaseCurrentAction();
5077 		return;
5078 	}
5079 
5080 	AttackCore(Sender, tar, AC_NO_SOUND|AC_RUNNING);
5081 }
5082 
AttackNoSound(Scriptable * Sender,Action * parameters)5083 void GameScript::AttackNoSound( Scriptable* Sender, Action* parameters)
5084 {
5085 	if (Sender->Type != ST_ACTOR) {
5086 		Sender->ReleaseCurrentAction();
5087 		return;
5088 	}
5089 	//using auto target!
5090 	Scriptable* tar;
5091 	tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5092 	if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) {
5093 		Sender->ReleaseCurrentAction();
5094 		return;
5095 	}
5096 
5097 	//actor is already incapable of attack
5098 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
5099 		Sender->ReleaseCurrentAction();
5100 		return;
5101 	}
5102 
5103 	AttackCore(Sender, tar, AC_NO_SOUND);
5104 }
5105 
RunningAttack(Scriptable * Sender,Action * parameters)5106 void GameScript::RunningAttack( Scriptable* Sender, Action* parameters)
5107 {
5108 	if (Sender->Type != ST_ACTOR) {
5109 		Sender->ReleaseCurrentAction();
5110 		return;
5111 	}
5112 	//using auto target!
5113 	Scriptable* tar;
5114 	tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5115 	if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) {
5116 		Sender->ReleaseCurrentAction();
5117 		return;
5118 	}
5119 
5120 	//actor is already incapable of attack
5121 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
5122 		Sender->ReleaseCurrentAction();
5123 		return;
5124 	}
5125 
5126 	AttackCore(Sender, tar, AC_RUNNING);
5127 }
5128 
Attack(Scriptable * Sender,Action * parameters)5129 void GameScript::Attack( Scriptable* Sender, Action* parameters)
5130 {
5131 	if (Sender->Type != ST_ACTOR) {
5132 		Sender->ReleaseCurrentAction();
5133 		return;
5134 	}
5135 	//using auto target!
5136 	Scriptable* tar;
5137 	tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5138 
5139 	if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) || tar == Sender) {
5140 		Sender->ReleaseCurrentAction();
5141 		return;
5142 	}
5143 
5144 	//actor is already incapable of attack
5145 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
5146 		Sender->ReleaseCurrentAction();
5147 		return;
5148 	}
5149 
5150 	AttackCore(Sender, tar, 0);
5151 }
5152 
ForceAttack(Scriptable * Sender,Action * parameters)5153 void GameScript::ForceAttack( Scriptable* Sender, Action* parameters)
5154 {
5155 	Scriptable* scr = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5156 	if (!scr || scr->Type != ST_ACTOR) {
5157 		return;
5158 	}
5159 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2], GA_NO_DEAD );
5160 	if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) {
5161 		return;
5162 	}
5163 	//this is a hack, we use a gui variable for our own hideous reasons?
5164 	if (tar->Type==ST_ACTOR) {
5165 		GameControl *gc = core->GetGameControl();
5166 		if (gc) {
5167 			//saving the target object ID from the gui variable
5168 			scr->AddAction( GenerateActionDirect("NIDSpecial3()", (Actor *) tar) );
5169 		}
5170 	} else {
5171 		char Tmp[80];
5172 		snprintf(Tmp, sizeof(Tmp), "BashDoor(%s)", tar->GetScriptName());
5173 		scr->AddAction ( GenerateAction(Tmp) );
5174 	}
5175 }
5176 
AttackReevaluate(Scriptable * Sender,Action * parameters)5177 void GameScript::AttackReevaluate( Scriptable* Sender, Action* parameters)
5178 {
5179 	if (Sender->Type != ST_ACTOR) {
5180 		Sender->ReleaseCurrentAction();
5181 		return;
5182 	}
5183 
5184 	if (!Sender->CurrentActionState) {
5185 		Sender->CurrentActionState = parameters->int0Parameter;
5186 		// TODO: reevaluate target (set CurrentActionTarget to 0) if we are not actively in combat
5187 	}
5188 
5189 	Scriptable* tar;
5190 	tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5191 	if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) {
5192 		Sender->ReleaseCurrentAction();
5193 		return;
5194 	}
5195 
5196 	//actor is already incapable of attack
5197 	if (Sender->GetInternalFlag()&IF_STOPATTACK) {
5198 		Sender->ReleaseCurrentAction();
5199 		return;
5200 	}
5201 
5202 	// if same target as before, don't play the war cry again, as they'd pop up too often
5203 	int flags = 0;
5204 	if (Sender->LastTargetPersistent == tar->GetGlobalID()) {
5205 		flags = AC_NO_SOUND;
5206 	}
5207 
5208 	AttackCore(Sender, tar, flags);
5209 	parameters->int2Parameter = 1;
5210 
5211 	Sender->CurrentActionState--;
5212 	if (Sender->CurrentActionState <= 0) {
5213 		Sender->CurrentActionState = 0;
5214 		Sender->ReleaseCurrentAction();
5215 	}
5216 }
5217 
Explore(Scriptable * Sender,Action *)5218 void GameScript::Explore( Scriptable* Sender, Action* /*parameters*/)
5219 {
5220 	Sender->GetCurrentArea()->FillExplored(true);
5221 }
5222 
UndoExplore(Scriptable * Sender,Action *)5223 void GameScript::UndoExplore( Scriptable* Sender, Action* /*parameters*/)
5224 {
5225 	Sender->GetCurrentArea()->FillExplored(false);
5226 }
5227 
ExploreMapChunk(Scriptable * Sender,Action * parameters)5228 void GameScript::ExploreMapChunk( Scriptable* Sender, Action* parameters)
5229 {
5230 	Map *map = Sender->GetCurrentArea();
5231 	/*
5232 	There is a mode flag in int1Parameter, but i don't know what is it,
5233 	our implementation uses it for LOS=1, or no LOS=0
5234 	ExploreMapChunk will reveal both visibility/explored map, but the
5235 	visibility will fade in the next update cycle (which is quite frequent)
5236 	*/
5237 	map->ExploreMapChunk(parameters->pointParameter, parameters->int0Parameter, parameters->int1Parameter);
5238 }
5239 
StartStore(Scriptable * Sender,Action * parameters)5240 void GameScript::StartStore( Scriptable* Sender, Action* parameters)
5241 {
5242 	if (core->GetCurrentStore() ) {
5243 		return;
5244 	}
5245 	core->SetCurrentStore( parameters->string0Parameter, Sender->GetGlobalID());
5246 	core->SetEventFlag(EF_OPENSTORE);
5247 	//sorry, i have absolutely no idea when i should do this :)
5248 	Sender->ReleaseCurrentAction();
5249 }
5250 
5251 //The integer parameter is a GemRB extension, if set to 1, the player
5252 //gains experience for learning the spell
AddSpecialAbility(Scriptable * Sender,Action * parameters)5253 void GameScript::AddSpecialAbility( Scriptable* Sender, Action* parameters)
5254 {
5255 	if (Sender->Type != ST_ACTOR) {
5256 		return;
5257 	}
5258 	Actor *actor = (Actor *) Sender;
5259 	actor->LearnSpell (parameters->string0Parameter, parameters->int0Parameter|LS_MEMO|LS_LEARN);
5260 	core->SetEventFlag(EF_ACTION);
5261 }
5262 
5263 //actually this just depletes a spell, doesn't remove it from the book
5264 //GemRB extension: the first/second int parameter can also make it removed
5265 //from the spell memorization schedule (also from the spellbook)
RemoveSpell(Scriptable * Sender,Action * parameters)5266 void GameScript::RemoveSpell( Scriptable* Sender, Action* parameters)
5267 {
5268 	ieResRef spellres;
5269 	int type;
5270 
5271 	if (Sender->Type!=ST_ACTOR) {
5272 		return;
5273 	}
5274 	if (!ResolveSpellName( spellres, parameters) ) {
5275 		return;
5276 	}
5277 	Actor *actor = (Actor *) Sender;
5278 	if (parameters->string0Parameter[0]) {
5279 		//the spell resref is in the string parameter
5280 		type = parameters->int0Parameter;
5281 	} else {
5282 		//the spell number is in the int0 parameter
5283 		type = parameters->int1Parameter;
5284 	}
5285 	if (type==2) {
5286 	//remove spell from both book and memorization
5287 		actor->spellbook.RemoveSpell(spellres);
5288 		return;
5289 	}
5290 	//type == 1 remove spell only from memorization
5291 	//type == 0 original behaviour: deplete only
5292 	actor->spellbook.UnmemorizeSpell(spellres, type);
5293 }
5294 
SetScriptName(Scriptable * Sender,Action * parameters)5295 void GameScript::SetScriptName( Scriptable* Sender, Action* parameters)
5296 {
5297 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5298 	if (!tar || tar->Type!=ST_ACTOR) {
5299 		return;
5300 	}
5301 	tar->SetScriptName(parameters->string0Parameter);
5302 }
5303 
5304 //iwd2
5305 //advance time with a constant
5306 //This is in seconds according to IESDP
AdvanceTime(Scriptable *,Action * parameters)5307 void GameScript::AdvanceTime(Scriptable* /*Sender*/, Action* parameters)
5308 {
5309 	core->GetGame()->AdvanceTime(parameters->int0Parameter*AI_UPDATE_TIME);
5310 	core->GetGame()->ResetPartyCommentTimes();
5311 }
5312 
5313 // advance at the beginning of the specified hour (minus one tick? unsure)
5314 // the parameter is HOURS (time.ids, 0 to 23)
5315 // never advance a full day or more (in fact, duplicating this action does nothing)
DayNight(Scriptable *,Action * parameters)5316 void GameScript::DayNight(Scriptable* /*Sender*/, Action* parameters)
5317 {
5318 	int delta = parameters->int0Parameter * core->Time.hour_size
5319 	          - core->GetGame()->GameTime % core->Time.day_size;
5320 	if (delta < 0) {
5321 		delta += core->Time.day_size;
5322 	}
5323 	core->GetGame()->AdvanceTime(delta, false);
5324 }
5325 
5326 // most games take no parameters: RestParty()
5327 // pst style parameters: RestParty(I:SuggestedDream*,I:HP*,I:Renting*)
5328 // - suggested dream: unused and always -1 in the original data
5329 //   (compatibility: if suggested dream is 0, then area flags determine the 'movie')
5330 // - hp: number of hps healed
5331 // - renting: crashes pst, we simply ignore it
RestParty(Scriptable * Sender,Action * parameters)5332 void GameScript::RestParty(Scriptable* Sender, Action* parameters)
5333 {
5334 	Game *game = core->GetGame();
5335 	game->RestParty(REST_NOCHECKS, parameters->int0Parameter, parameters->int1Parameter);
5336 	Sender->ReleaseCurrentAction();
5337 }
5338 
5339 //doesn't advance game time, just removes fatigue & refreshes spells of target
5340 //this is a non-blocking action
5341 static EffectRef fx_fatigue_ref = { "FatigueModifier", -1 };
5342 
Rest(Scriptable * Sender,Action *)5343 void GameScript::Rest(Scriptable* Sender, Action* /*parameters*/)
5344 {
5345 	if (Sender->Type!=ST_ACTOR) {
5346 		return;
5347 	}
5348 	Actor *actor = (Actor *) Sender;
5349 	actor->spellbook.ChargeAllSpells();
5350 	actor->fxqueue.RemoveAllEffects(fx_fatigue_ref);
5351 	actor->SetBase(IE_FATIGUE,0);
5352 }
5353 
5354 //doesn't advance game time, just removes fatigue
RestNoSpells(Scriptable * Sender,Action *)5355 void GameScript::RestNoSpells(Scriptable* Sender, Action* /*parameters*/)
5356 {
5357 	if (Sender->Type!=ST_ACTOR) {
5358 		return;
5359 	}
5360 	Actor *actor = (Actor *) Sender;
5361 	actor->fxqueue.RemoveAllEffects(fx_fatigue_ref);
5362 	actor->SetBase(IE_FATIGUE,0);
5363 }
5364 
5365 //this most likely advances time and heals whole party
RestUntilHealed(Scriptable * Sender,Action *)5366 void GameScript::RestUntilHealed(Scriptable* Sender, Action* /*parameters*/)
5367 {
5368 	core->GetGame()->RestParty(REST_NOCHECKS, 0, 0);
5369 	Sender->ReleaseCurrentAction();
5370 }
5371 
5372 //iwd2
5373 //removes all delayed/duration/semi permanent effects (like a ctrl-r)
ClearPartyEffects(Scriptable *,Action *)5374 void GameScript::ClearPartyEffects(Scriptable* /*Sender*/, Action* /*parameters*/)
5375 {
5376 	Game *game = core->GetGame();
5377 	int i = game->GetPartySize(false);
5378 	while (i--) {
5379 		Actor *tar = game->GetPC(i, false);
5380 		tar->fxqueue.RemoveExpiredEffects(0xffffffff);
5381 	}
5382 }
5383 
5384 //iwd2 removes effects from a single sprite
ClearSpriteEffects(Scriptable * Sender,Action * parameters)5385 void GameScript::ClearSpriteEffects(Scriptable* Sender, Action* parameters)
5386 {
5387 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5388 	if (!tar || tar->Type!=ST_ACTOR) {
5389 		return;
5390 	}
5391 	Actor *actor = (Actor *) tar;
5392 	actor->fxqueue.RemoveExpiredEffects(0xffffffff);
5393 }
5394 
5395 //IWD2 special, can mark only actors, hope it is enough
MarkObject(Scriptable * Sender,Action * parameters)5396 void GameScript::MarkObject(Scriptable* Sender, Action* parameters)
5397 {
5398 	if (Sender->Type != ST_ACTOR) {
5399 		return;
5400 	}
5401 	//unsure, could mark dead objects?
5402 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD );
5403 	if (!tar || tar->Type!=ST_ACTOR) {
5404 		return;
5405 	}
5406 	Actor *actor = (Actor *) Sender;
5407 	actor->LastMarked = tar->GetGlobalID();
5408 }
5409 
MarkSpellAndObject(Scriptable * Sender,Action * parameters)5410 void GameScript::MarkSpellAndObject(Scriptable* Sender, Action* parameters)
5411 {
5412 	if (Sender->Type != ST_ACTOR) {
5413 		return;
5414 	}
5415 	Actor *me = (Actor *) Sender;
5416 	if (me->LastMarkedSpell) {
5417 		return;
5418 	}
5419 
5420 	const Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1]);
5421 	if (!tar) {
5422 		// target died on us
5423 		return;
5424 	}
5425 	const Actor *actor = nullptr;
5426 	if (tar->Type == ST_ACTOR) {
5427 		actor = (const Actor *) tar;
5428 	}
5429 
5430 	int flags = parameters->int0Parameter;
5431 	if (!(flags & MSO_IGNORE_NULL) && !actor) {
5432 		return;
5433 	}
5434 	if (!(flags & MSO_IGNORE_INVALID) && actor && actor->InvalidSpellTarget() ) {
5435 		return;
5436 	}
5437 	if (!(flags & MSO_IGNORE_SEE) && actor && !CanSee(Sender, actor, true, 0) ) {
5438 		return;
5439 	}
5440 	size_t len = strlen(parameters->string0Parameter);
5441 	//
5442 	if (len&3) {
5443 		return;
5444 	}
5445 	len/=4;
5446 	size_t max = len;
5447 	size_t pos;
5448 	if (flags & MSO_RANDOM_SPELL) {
5449 		pos = core->Roll(1,len,0);
5450 	} else {
5451 		pos = 0;
5452 	}
5453 	while(len--) {
5454 		char spl[5];
5455 
5456 		memcpy(spl, parameters->string0Parameter+pos*4, 4);
5457 		spl[4]=0;
5458 		int splnum = atoi(spl);
5459 
5460 		if (!(flags & MSO_IGNORE_HAVE) && !me->spellbook.HaveSpell(splnum, 0) ) {
5461 			goto end_mso_loop;
5462 		}
5463 		int range;
5464 		if ((flags & MSO_IGNORE_RANGE) || !actor) {
5465 			range = 0;
5466 		} else {
5467 			range = Distance(me, actor);
5468 		}
5469 		if (!(flags & MSO_IGNORE_INVALID) && actor && actor->InvalidSpellTarget(splnum, me, range)) {
5470 			goto end_mso_loop;
5471 		}
5472 		//mark spell and target
5473 		me->LastMarkedSpell = splnum;
5474 		me->LastSpellTarget = tar->GetGlobalID();
5475 		break;
5476 end_mso_loop:
5477 		pos++;
5478 		if (pos==max) {
5479 			pos = 0;
5480 		}
5481 	}
5482 }
5483 
ForceMarkedSpell(Scriptable * Sender,Action * parameters)5484 void GameScript::ForceMarkedSpell(Scriptable* Sender, Action* parameters)
5485 {
5486 	if (Sender->Type != ST_ACTOR) {
5487 		return;
5488 	}
5489 	Actor *actor = (Actor *) Sender;
5490 	actor->LastMarkedSpell = parameters->int0Parameter;
5491 }
5492 
SetMarkedSpell(Scriptable * Sender,Action * parameters)5493 void GameScript::SetMarkedSpell(Scriptable* Sender, Action* parameters)
5494 {
5495 	if (Sender->Type != ST_ACTOR) {
5496 		return;
5497 	}
5498 	Actor *actor = (Actor *) Sender;
5499 	if (parameters->int0Parameter) {
5500 		if (actor->LastMarkedSpell) {
5501 			return;
5502 		}
5503 		if (!actor->spellbook.HaveSpell(parameters->int0Parameter, 0) ) {
5504 			return;
5505 		}
5506 	}
5507 
5508 	actor->LastMarkedSpell = parameters->int0Parameter;
5509 	return;
5510 }
5511 
SetDialogueRange(Scriptable * Sender,Action * parameters)5512 void GameScript::SetDialogueRange(Scriptable* Sender, Action* parameters)
5513 {
5514 	if (Sender->Type != ST_ACTOR) {
5515 		return;
5516 	}
5517 	Actor *actor = (Actor *) Sender;
5518 	actor->SetBase( IE_DIALOGRANGE, parameters->int0Parameter );
5519 }
5520 
SetGlobalTint(Scriptable *,Action * parameters)5521 void GameScript::SetGlobalTint(Scriptable* /*Sender*/, Action* parameters)
5522 {
5523 	Color c(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter, 0xff);
5524 	core->GetWindowManager()->FadeColor = c;
5525 }
5526 
SetArmourLevel(Scriptable * Sender,Action * parameters)5527 void GameScript::SetArmourLevel(Scriptable* Sender, Action* parameters)
5528 {
5529 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5530 	if (!tar || tar->Type!=ST_ACTOR) {
5531 		return;
5532 	}
5533 	Actor *actor = (Actor *) Sender;
5534 	actor->SetBase( IE_ARMOR_TYPE, parameters->int0Parameter );
5535 }
5536 
RandomWalk(Scriptable * Sender,Action *)5537 void GameScript::RandomWalk(Scriptable* Sender, Action* /*parameters*/)
5538 {
5539 	if (Sender->Type != ST_ACTOR) {
5540 		Sender->ReleaseCurrentAction();
5541 		return;
5542 	}
5543 	Actor* actor = ( Actor* ) Sender;
5544 	actor->RandomWalk( true, false );
5545 }
5546 
RandomRun(Scriptable * Sender,Action *)5547 void GameScript::RandomRun(Scriptable* Sender, Action* /*parameters*/)
5548 {
5549 	if (Sender->Type != ST_ACTOR) {
5550 		Sender->ReleaseCurrentAction();
5551 		return;
5552 	}
5553 	Actor* actor = ( Actor* ) Sender;
5554 	actor->RandomWalk( true, true );
5555 }
5556 
RandomWalkContinuous(Scriptable * Sender,Action *)5557 void GameScript::RandomWalkContinuous(Scriptable* Sender, Action* /*parameters*/)
5558 {
5559 	if (Sender->Type != ST_ACTOR) {
5560 		Sender->ReleaseCurrentAction();
5561 		return;
5562 	}
5563 	Actor* actor = ( Actor* ) Sender;
5564 	actor->RandomWalk( false, false );
5565 }
5566 
RandomFly(Scriptable * Sender,Action *)5567 void GameScript::RandomFly(Scriptable* Sender, Action* /*parameters*/)
5568 {
5569 	if (Sender->Type != ST_ACTOR) {
5570 		Sender->ReleaseCurrentAction();
5571 		return;
5572 	}
5573 	Actor* actor = ( Actor* ) Sender;
5574 	int x = RAND(0,31);
5575 	if (x<10) {
5576 		actor->SetOrientation(actor->GetOrientation()-1, false);
5577 	} else if (x>20) {
5578 		actor->SetOrientation(actor->GetOrientation()+1, false);
5579 	}
5580 	//fly in this direction for 20 steps
5581 	actor->MoveLine(20, actor->GetOrientation());
5582 }
5583 
5584 //UseContainer uses the predefined target (like Nidspecial1 dialog hack)
UseContainer(Scriptable * Sender,Action * parameters)5585 void GameScript::UseContainer(Scriptable* Sender, Action* parameters)
5586 {
5587 	if (Sender->Type != ST_ACTOR) {
5588 		Sender->ReleaseCurrentAction();
5589 		return;
5590 	}
5591 
5592 	if (core->InCutSceneMode()) {
5593 		//cannot use container in dialog or cutscene
5594 		Sender->ReleaseCurrentAction();
5595 		return;
5596 	}
5597 
5598 	Actor *actor = (Actor *)Sender;
5599 	Container *container = core->GetCurrentContainer();
5600 	if (!container) {
5601 		Log(WARNING, "GameScript", "No container selected!");
5602 		Sender->ReleaseCurrentAction();
5603 		return;
5604 	}
5605 	if (parameters->int2Parameter > 20) {
5606 		Log(WARNING, "GameScript", "Could not get close enough to container!");
5607 		Sender->ReleaseCurrentAction();
5608 		return;
5609 	}
5610 
5611 	ieDword distance = PersonalDistance(Sender, container);
5612 	ieDword needed = MAX_OPERATING_DISTANCE;
5613 	// give up the strictness after 10 retries from the same spot
5614 	if (!parameters->int2Parameter) {
5615 		parameters->int1Parameter = distance;
5616 		parameters->int2Parameter = 1;
5617 	} else {
5618 		if (parameters->int1Parameter == (signed)distance) {
5619 			parameters->int2Parameter++;
5620 		} else {
5621 			parameters->int1Parameter = distance;
5622 		}
5623 	}
5624 	if (container->Type==IE_CONTAINER_PILE && parameters->int2Parameter < 10) {
5625 		needed = 0; // less than a search square (width)
5626 	}
5627 	if (distance<=needed)
5628 	{
5629 		//check if the container is unlocked
5630 		if (!container->TryUnlock(actor)) {
5631 			//playsound can't open container
5632 			//display string, etc
5633 			if (core->HasFeedback(FT_MISC)) displaymsg->DisplayConstantString(STR_CONTLOCKED, DMC_LIGHTGREY, container);
5634 			Sender->ReleaseCurrentAction();
5635 			return;
5636 		}
5637 		actor->SetModal(MS_NONE);
5638 		if (container->Trapped) {
5639 			container->AddTrigger(TriggerEntry(trigger_opened, actor->GetGlobalID()));
5640 		} else {
5641 			container->AddTrigger(TriggerEntry(trigger_harmlessopened, actor->GetGlobalID()));
5642 		}
5643 		container->TriggerTrap(0, actor->GetGlobalID());
5644 		core->SetCurrentContainer(actor, container, true);
5645 		Sender->ReleaseCurrentAction();
5646 		return;
5647 	}
5648 	MoveNearerTo(Sender, container, needed, 1);
5649 }
5650 
5651 //call the usecontainer action in target (not used)
ForceUseContainer(Scriptable * Sender,Action * parameters)5652 void GameScript::ForceUseContainer(Scriptable* Sender, Action* parameters)
5653 {
5654 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5655 	if (!tar || tar->Type != ST_ACTOR) {
5656 		Sender->ReleaseCurrentAction(); //why blocking???
5657 		return;
5658 	}
5659 	Action *newaction = GenerateAction("UseContainer()");
5660 	tar->AddActionInFront(newaction);
5661 	Sender->ReleaseCurrentAction(); //why blocking???
5662 }
5663 
5664 //these actions directly manipulate a game variable (as the original engine)
SetMazeEasier(Scriptable * Sender,Action *)5665 void GameScript::SetMazeEasier(Scriptable* Sender, Action* /*parameters*/)
5666 {
5667 	int value = CheckVariable( Sender, "MAZEDIFFICULTY","GLOBAL");
5668 	if (value>0) {
5669 		SetVariable(Sender, "MAZEDIFFICULTY", "GLOBAL", value-1);
5670 	}
5671 }
5672 
SetMazeHarder(Scriptable * Sender,Action *)5673 void GameScript::SetMazeHarder(Scriptable* Sender, Action* /*parameters*/)
5674 {
5675 	int value = CheckVariable( Sender, "MAZEDIFFICULTY","GLOBAL");
5676 	if (value<2) {
5677 		SetVariable(Sender, "MAZEDIFFICULTY", "GLOBAL", value+1);
5678 	}
5679 }
5680 
GenerateMaze(Scriptable *,Action *)5681 void GameScript::GenerateMaze(Scriptable* /*Sender*/, Action* /*parameters*/)
5682 {
5683 	core->SetEventFlag(EF_CREATEMAZE);
5684 }
5685 
FixEngineRoom(Scriptable * Sender,Action *)5686 void GameScript::FixEngineRoom(Scriptable* Sender, Action* /*parameters*/)
5687 {
5688 	int value = CheckVariable( Sender, "EnginInMaze","GLOBAL");
5689 	if (value) {
5690 		SetVariable(Sender, "EnginInMaze", "GLOBAL", 0);
5691 		//this works only because the engine room exit depends only on the EnginInMaze variable
5692 		ScriptEngine *sE = core->GetGUIScriptEngine();
5693 		sE->RunFunction("Maze", "CustomizeArea");
5694 	}
5695 }
5696 
StartRainNow(Scriptable *,Action *)5697 void GameScript::StartRainNow(Scriptable* /*Sender*/, Action* /*parameters*/)
5698 {
5699 	core->GetGame()->StartRainOrSnow( false, WB_RAIN|WB_RARELIGHTNING);
5700 }
5701 
Weather(Scriptable *,Action * parameters)5702 void GameScript::Weather(Scriptable* /*Sender*/, Action* parameters)
5703 {
5704 	Game *game = core->GetGame();
5705 	switch(parameters->int0Parameter & WB_FOG) {
5706 		case WB_NORMAL:
5707 			game->StartRainOrSnow( false, 0);
5708 			break;
5709 		case WB_RAIN:
5710 			game->StartRainOrSnow( true, WB_RAIN|WB_RARELIGHTNING);
5711 			break;
5712 		case WB_SNOW:
5713 			game->StartRainOrSnow( true, WB_SNOW);
5714 			break;
5715 		case WB_FOG:
5716 			game->StartRainOrSnow( true, WB_FOG);
5717 			break;
5718 	}
5719 }
5720 
CopyGroundPilesTo(Scriptable * Sender,Action * parameters)5721 void GameScript::CopyGroundPilesTo(Scriptable* Sender, Action* parameters)
5722 {
5723 	Map *map = Sender->GetCurrentArea();
5724 	Map *othermap = core->GetGame()->GetMap( parameters->string0Parameter, false );
5725 	if (!othermap) {
5726 		return;
5727 	}
5728 	map->CopyGroundPiles( othermap, parameters->pointParameter );
5729 }
5730 
5731 //iwd specific
PlayBardSong(Scriptable * Sender,Action *)5732 void GameScript::PlayBardSong(Scriptable* Sender, Action* /*parameters*/)
5733 {
5734 	if (Sender->Type!=ST_ACTOR) {
5735 		return;
5736 	}
5737 	//actually this one must use int0Parameter to set a bardsong
5738 	Actor *actor = (Actor *) Sender;
5739 	actor->SetModal( MS_BATTLESONG);
5740 }
5741 
BattleSong(Scriptable * Sender,Action *)5742 void GameScript::BattleSong(Scriptable* Sender, Action* /*parameters*/)
5743 {
5744 	if (Sender->Type!=ST_ACTOR) {
5745 		return;
5746 	}
5747 	Actor *actor = (Actor *) Sender;
5748 	actor->SetModal( MS_BATTLESONG);
5749 }
5750 
FindTraps(Scriptable * Sender,Action *)5751 void GameScript::FindTraps(Scriptable* Sender, Action* /*parameters*/)
5752 {
5753 	if (Sender->Type!=ST_ACTOR) {
5754 		return;
5755 	}
5756 	Actor *actor = (Actor *) Sender;
5757 	actor->SetModal( MS_DETECTTRAPS);
5758 }
5759 
Hide(Scriptable * Sender,Action *)5760 void GameScript::Hide(Scriptable* Sender, Action* /*parameters*/)
5761 {
5762 	if (Sender->Type!=ST_ACTOR) {
5763 		return;
5764 	}
5765 	Actor *actor = (Actor *) Sender;
5766 
5767 	if (actor->TryToHide()) {
5768 		actor->SetModal(MS_STEALTH);
5769 	}
5770 	//TODO: expiry isn't instant (skill based transition?)
5771 
5772 }
5773 
5774 static EffectRef fx_set_invisible_state_ref = { "State:Invisible", -1 };
Unhide(Scriptable * Sender,Action *)5775 void GameScript::Unhide(Scriptable* Sender, Action* /*parameters*/)
5776 {
5777 	if (Sender->Type != ST_ACTOR) {
5778 		return;
5779 	}
5780 	Actor *actor = (Actor *) Sender;
5781 
5782 	if (actor->Modal.State == MS_STEALTH) {
5783 		actor->SetModal(MS_NONE);
5784 	}
5785 	actor->fxqueue.RemoveAllEffects(fx_set_invisible_state_ref);
5786 }
5787 
Turn(Scriptable * Sender,Action *)5788 void GameScript::Turn(Scriptable* Sender, Action* /*parameters*/)
5789 {
5790 	if (Sender->Type!=ST_ACTOR) {
5791 		return;
5792 	}
5793 	Actor *actor = (Actor *) Sender;
5794 
5795 	if (actor->Modified[IE_DISABLEDBUTTON] & (1<<ACT_TURN)) {
5796 		return;
5797 	}
5798 
5799 	int skill = actor->GetStat(IE_TURNUNDEADLEVEL);
5800 	if (skill < 1) return;
5801 
5802 	actor->SetModal(MS_TURNUNDEAD);
5803 
5804 }
5805 
TurnAMT(Scriptable * Sender,Action * parameters)5806 void GameScript::TurnAMT(Scriptable* Sender, Action* parameters)
5807 {
5808 	if (Sender->Type!=ST_ACTOR) {
5809 		Sender->ReleaseCurrentAction();
5810 		return;
5811 	}
5812 	Actor *actor = (Actor *) Sender;
5813 	actor->SetOrientation(actor->GetOrientation()+parameters->int0Parameter, true);
5814 	actor->SetWait( 1 );
5815 	Sender->ReleaseCurrentAction(); // todo, blocking?
5816 }
5817 
RandomTurn(Scriptable * Sender,Action * parameters)5818 void GameScript::RandomTurn(Scriptable* Sender, Action* parameters)
5819 {
5820 	if (Sender->Type!=ST_ACTOR) {
5821 		Sender->ReleaseCurrentAction();
5822 		return;
5823 	}
5824 	// it doesn't take parameters, but we used them internally for one-shot runs
5825 	if (parameters->int0Parameter > 1) parameters->int0Parameter--;
5826 	if (parameters->int0Parameter == 1) {
5827 		Sender->ReleaseCurrentAction();
5828 		return;
5829 	}
5830 	Actor *actor = (Actor *) Sender;
5831 	actor->SetOrientation(RAND(0, MAX_ORIENT-1), true);
5832 	// the original waited more if the actor was offscreen, perhaps as an optimization
5833 	int diceSides = 40;
5834 	Region vp = core->GetGameControl()->Viewport();
5835 	if (vp.PointInside(actor->Pos)) diceSides = 10;
5836 	actor->SetWait(AI_UPDATE_TIME * core->Roll(1, diceSides, 0));
5837 }
5838 
AttachTransitionToDoor(Scriptable * Sender,Action * parameters)5839 void GameScript::AttachTransitionToDoor(Scriptable* Sender, Action* parameters)
5840 {
5841 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5842 	if (!tar || tar->Type != ST_DOOR) {
5843 		return;
5844 	}
5845 	Door* door = ( Door* ) tar;
5846 	strnspccpy(door->LinkedInfo, parameters->string0Parameter, 32);
5847 }
5848 
5849 /*getting a handle of a temporary actor resource to copy its selected attributes*/
ChangeAnimation(Scriptable * Sender,Action * parameters)5850 void GameScript::ChangeAnimation(Scriptable* Sender, Action* parameters)
5851 {
5852 	if (Sender->Type!=ST_ACTOR) {
5853 		return;
5854 	}
5855 	ChangeAnimationCore((Actor *) Sender, parameters->string0Parameter,1);
5856 }
5857 
ChangeAnimationNoEffect(Scriptable * Sender,Action * parameters)5858 void GameScript::ChangeAnimationNoEffect(Scriptable* Sender, Action* parameters)
5859 {
5860 	if (Sender->Type!=ST_ACTOR) {
5861 		return;
5862 	}
5863 	ChangeAnimationCore((Actor *) Sender, parameters->string0Parameter,0);
5864 }
5865 
Polymorph(Scriptable * Sender,Action * parameters)5866 void GameScript::Polymorph(Scriptable* Sender, Action* parameters)
5867 {
5868 	if (Sender->Type!=ST_ACTOR) {
5869 		return;
5870 	}
5871 	Actor *act = (Actor *) Sender;
5872 	act->SetBase(IE_ANIMATION_ID, parameters->int0Parameter);
5873 }
5874 
PolymorphCopy(Scriptable * Sender,Action * parameters)5875 void GameScript::PolymorphCopy(Scriptable* Sender, Action* parameters)
5876 {
5877 	if (Sender->Type!=ST_ACTOR) {
5878 		return;
5879 	}
5880 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5881 	if (!tar || tar->Type!=ST_ACTOR) {
5882 		return;
5883 	}
5884 	PolymorphCopyCore((Actor *) tar, (Actor *) Sender);
5885 }
5886 
5887 /* according to IESDP this only copies the animation ID */
PolymorphCopyBase(Scriptable * Sender,Action * parameters)5888 void GameScript::PolymorphCopyBase(Scriptable* Sender, Action* parameters)
5889 {
5890 	if (Sender->Type!=ST_ACTOR) {
5891 		return;
5892 	}
5893 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
5894 	if (!tar || tar->Type!=ST_ACTOR) {
5895 		return;
5896 	}
5897 	Actor *act = (Actor *) Sender;
5898 	Actor *actor = (Actor *) tar;
5899 	act->SetBase(IE_ANIMATION_ID, actor->GetBase(IE_ANIMATION_ID) );
5900 }
5901 
ExportParty(Scriptable *,Action * parameters)5902 void GameScript::ExportParty(Scriptable* /*Sender*/, Action* parameters)
5903 {
5904 	char FileName[_MAX_PATH];
5905 
5906 	Game *game = core->GetGame();
5907 	int i = game->GetPartySize(false);
5908 	while (i--) {
5909 		Actor *actor = game->GetPC(i, false);
5910 		snprintf(FileName,_MAX_PATH,"%s%d",parameters->string0Parameter,i+1);
5911 		core->WriteCharacter(FileName, actor);
5912 	}
5913 	displaymsg->DisplayConstantString(STR_EXPORTED, DMC_BG2XPGREEN);
5914 }
5915 
SaveGame(Scriptable *,Action * parameters)5916 void GameScript::SaveGame(Scriptable* /*Sender*/, Action* parameters)
5917 {
5918 	if (core->HasFeature(GF_STRREF_SAVEGAME)) {
5919 		const char *basename = "Auto-Save";
5920 		AutoTable tab("savegame");
5921 		if (tab) {
5922 			basename = tab->QueryDefault();
5923 		}
5924 		char * str = core->GetCString( parameters->int0Parameter, IE_STR_STRREFOFF);
5925 		char FolderName[_MAX_PATH];
5926 		snprintf (FolderName, sizeof(FolderName), "%s - %s", basename, str);
5927 		free(str);
5928 
5929 		core->GetSaveGameIterator()->CreateSaveGame(core->GetSaveGameIterator()->GetSaveGame(FolderName), FolderName);
5930 	} else {
5931 		core->GetSaveGameIterator()->CreateSaveGame(parameters->int0Parameter);
5932 	}
5933 }
5934 
5935 /*EscapeAreaMove(S:Area*,I:X*,I:Y*,I:Face*)*/
EscapeArea(Scriptable * Sender,Action * parameters)5936 void GameScript::EscapeArea(Scriptable* Sender, Action* parameters)
5937 {
5938 	ScriptDebugLog(ID_ACTIONS, "EscapeArea/EscapeAreaMove");
5939 
5940 	if (Sender->Type!=ST_ACTOR) {
5941 		Sender->ReleaseCurrentAction();
5942 		return;
5943 	}
5944 	Map *map = Sender->GetCurrentArea();
5945 	if (!map) {
5946 		Sender->ReleaseCurrentAction();
5947 		return;
5948 	}
5949 
5950 	Point p = Sender->Pos;
5951 	map->TMap->AdjustNearestTravel(p);
5952 
5953 	if (parameters->string0Parameter[0]) {
5954 		Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter);
5955 		EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter );
5956 	} else {
5957 		EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY, parameters->int0Parameter );
5958 	}
5959 	//EscapeAreaCore will do its ReleaseCurrentAction
5960 	//Sender->ReleaseCurrentAction();
5961 }
5962 
EscapeAreaNoSee(Scriptable * Sender,Action * parameters)5963 void GameScript::EscapeAreaNoSee(Scriptable* Sender, Action* parameters)
5964 {
5965 	ScriptDebugLog(ID_ACTIONS, "EscapeAreaNoSee");
5966 
5967 	if (Sender->Type!=ST_ACTOR) {
5968 		Sender->ReleaseCurrentAction();
5969 		return;
5970 	}
5971 	Map *map = Sender->GetCurrentArea();
5972 	if (!map) {
5973 		Sender->ReleaseCurrentAction();
5974 		return;
5975 	}
5976 
5977 	Point p = Sender->Pos;
5978 	map->TMap->AdjustNearestTravel(p);
5979 
5980 	if (parameters->string0Parameter[0]) {
5981 		Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter);
5982 		EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter );
5983 	} else {
5984 		EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY|EA_NOSEE, parameters->int0Parameter );
5985 	}
5986 	//EscapeAreaCore will do its ReleaseCurrentAction
5987 	//Sender->ReleaseCurrentAction();
5988 }
5989 
EscapeAreaDestroy(Scriptable * Sender,Action * parameters)5990 void GameScript::EscapeAreaDestroy(Scriptable* Sender, Action* parameters)
5991 {
5992 	if (Sender->Type!=ST_ACTOR) {
5993 		Sender->ReleaseCurrentAction();
5994 		return;
5995 	}
5996 	Map *map = Sender->GetCurrentArea();
5997 	if (!map) {
5998 		Sender->ReleaseCurrentAction();
5999 		return;
6000 	}
6001 
6002 	//find nearest exit
6003 	Point p = Sender->Pos;
6004 	map->TMap->AdjustNearestTravel(p);
6005 	//EscapeAreaCore will do its ReleaseCurrentAction
6006 	EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY, parameters->int0Parameter );
6007 }
6008 
6009 /*EscapeAreaObjectMove(S:Area*,I:X*,I:Y*,I:Face*)*/
EscapeAreaObject(Scriptable * Sender,Action * parameters)6010 void GameScript::EscapeAreaObject(Scriptable* Sender, Action* parameters)
6011 {
6012 	if (Sender->Type!=ST_ACTOR) {
6013 		Sender->ReleaseCurrentAction();
6014 		return;
6015 	}
6016 	Map *map = Sender->GetCurrentArea();
6017 	if (!map) {
6018 		Sender->ReleaseCurrentAction();
6019 		return;
6020 	}
6021 
6022 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6023 	if (!tar) {
6024 		Sender->ReleaseCurrentAction();
6025 		return;
6026 	}
6027 	Point p = tar->Pos;
6028 	if (parameters->string0Parameter[0]) {
6029 		Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter);
6030 		EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter );
6031 	} else {
6032 		EscapeAreaCore( Sender, p, 0, p, EA_DESTROY, parameters->int0Parameter );
6033 	}
6034 	//EscapeAreaCore will do its ReleaseCurrentAction
6035 }
6036 
6037 //This one doesn't require the object to be seen?
6038 //We don't have that feature yet, so this is the same as EscapeAreaObject
EscapeAreaObjectNoSee(Scriptable * Sender,Action * parameters)6039 void GameScript::EscapeAreaObjectNoSee(Scriptable* Sender, Action* parameters)
6040 {
6041 	if (Sender->Type!=ST_ACTOR) {
6042 		Sender->ReleaseCurrentAction();
6043 		return;
6044 	}
6045 	Map *map = Sender->GetCurrentArea();
6046 	if (!map) {
6047 		Sender->ReleaseCurrentAction();
6048 		return;
6049 	}
6050 
6051 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6052 	if (!tar) {
6053 		Sender->ReleaseCurrentAction();
6054 		return;
6055 	}
6056 	Point p = tar->Pos;
6057 	Sender->SetWait(parameters->int0Parameter);
6058 	if (parameters->string0Parameter[0]) {
6059 		Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter);
6060 		EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter );
6061 	} else {
6062 		EscapeAreaCore( Sender, p, 0, p, EA_DESTROY|EA_NOSEE, parameters->int0Parameter );
6063 	}
6064 	//EscapeAreaCore will do its ReleaseCurrentAction
6065 }
6066 
6067 //takes first fitting item from container at feet, doesn't seem to be working in the original engines
PickUpItem(Scriptable * Sender,Action * parameters)6068 void GameScript::PickUpItem(Scriptable* Sender, Action* parameters)
6069 {
6070 	if (Sender->Type!=ST_ACTOR) {
6071 		return;
6072 	}
6073 	Actor *scr = (Actor *) Sender;
6074 	Map *map = scr->GetCurrentArea();
6075 	Container *c = map->GetPile(scr->Pos);
6076 	if (!c) { //this shouldn't happen, but lets prepare for the worst
6077 		return;
6078 	}
6079 
6080 	//the following part is coming from GUISCript.cpp with trivial changes
6081 	int Slot = c->inventory.FindItem(parameters->string0Parameter, 0);
6082 	if (Slot<0) {
6083 		return;
6084 	}
6085 	int res = core->CanMoveItem(c->inventory.GetSlotItem(Slot) );
6086 	if (!res) { //cannot move
6087 		return;
6088 	}
6089 	CREItem *item = c->RemoveItem(Slot,0);
6090 	if (!item) {
6091 		return;
6092 	}
6093 	if (res!=-1 && scr->InParty) { //it is gold and we got the party pool!
6094 		goto item_is_gold;
6095 	}
6096 	res = scr->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY);
6097 	if (res !=ASI_SUCCESS) { //putting it back
6098 		c->AddItem(item);
6099 	}
6100 	return;
6101 item_is_gold: //we take gold!
6102 	if (scr->InParty) {
6103 		core->GetGame()->PartyGold += res;
6104 		//if you want message here then use
6105 		//core->GetGame()->AddGold(res);
6106 	} else {
6107 		scr->SetBase( IE_GOLD, scr->GetBase(IE_GOLD) + res );
6108 	}
6109 	delete item;
6110 }
6111 
ChangeStoreMarkup(Scriptable *,Action * parameters)6112 void GameScript::ChangeStoreMarkup(Scriptable* /*Sender*/, Action* parameters)
6113 {
6114 	bool has_current = false;
6115 	ieResRef current;
6116 	ieDword owner;
6117 
6118 	Store *store = core->GetCurrentStore();
6119 	if (!store) {
6120 		store = core->SetCurrentStore(parameters->string0Parameter, 0);
6121 	} else {
6122 		if (strnicmp(store->Name, parameters->string0Parameter, sizeof(ieResRef)-1) ) {
6123 			//not the current store, we need some dirty hack
6124 			has_current = true;
6125 			strnlwrcpy(current, store->Name, sizeof(ieResRef)-1);
6126 			owner = store->GetOwnerID();
6127 		}
6128 	}
6129 	store->BuyMarkup = parameters->int0Parameter;
6130 	store->SellMarkup = parameters->int1Parameter;
6131 	//additional markup, is this depreciation???
6132 	store->DepreciationRate = parameters->int2Parameter;
6133 	if (has_current) {
6134 		//setting back old store (this will save our current store)
6135 		core->SetCurrentStore(current, owner);
6136 	}
6137 }
6138 
SetEncounterProbability(Scriptable *,Action * parameters)6139 void GameScript::SetEncounterProbability(Scriptable* /*Sender*/, Action* parameters)
6140 {
6141 	const WorldMap *wmap = core->GetWorldMap(parameters->string0Parameter);
6142 	if (!wmap) {
6143 		//no such starting area
6144 		return;
6145 	}
6146 	WMPAreaLink *link = wmap->GetLink(parameters->string0Parameter, parameters->string1Parameter);
6147 	if (!link) {
6148 		return;
6149 	}
6150 	link->EncounterChance = parameters->int0Parameter;
6151 }
6152 
SpawnPtActivate(Scriptable * Sender,Action * parameters)6153 void GameScript::SpawnPtActivate(Scriptable* Sender, Action* parameters)
6154 {
6155 	if (parameters->objects[1]) {
6156 		const Map *map = Sender->GetCurrentArea();
6157 		Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName);
6158 		if (spawn) {
6159 			spawn->Enabled = 1;
6160 		}
6161 	}
6162 }
6163 
SpawnPtDeactivate(Scriptable * Sender,Action * parameters)6164 void GameScript::SpawnPtDeactivate(Scriptable* Sender, Action* parameters)
6165 {
6166 	if (parameters->objects[1]) {
6167 		const Map *map = Sender->GetCurrentArea();
6168 		Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName);
6169 		if (spawn) {
6170 			spawn->Enabled = 0;
6171 		}
6172 	}
6173 }
6174 
SpawnPtSpawn(Scriptable * Sender,Action * parameters)6175 void GameScript::SpawnPtSpawn(Scriptable* Sender, Action* parameters)
6176 {
6177 	if (parameters->objects[1]) {
6178 		Map *map = Sender->GetCurrentArea();
6179 		Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName);
6180 		if (spawn) {
6181 			spawn->Enabled = 1; //??? maybe use an unconditionality flag
6182 			map->TriggerSpawn(spawn);
6183 		}
6184 	}
6185 }
6186 
ApplySpell(Scriptable * Sender,Action * parameters)6187 void GameScript::ApplySpell(Scriptable* Sender, Action* parameters)
6188 {
6189 	ieResRef spellres;
6190 
6191 	if (!ResolveSpellName( spellres, parameters) ) {
6192 		return;
6193 	}
6194 
6195 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6196 	if (!tar) {
6197 		return;
6198 	}
6199 	if (tar->Type==ST_ACTOR) {
6200 		//apply spell on target
6201 /*
6202 		Actor *owner;
6203 
6204 		if (Sender->Type==ST_ACTOR) {
6205 			owner = (Actor *) Sender;
6206 		} else {
6207 			owner = (Actor *) tar;
6208 		}
6209 */
6210 		//core->ApplySpell(spellres, (Actor *) tar, owner, parameters->int1Parameter);
6211 		core->ApplySpell(spellres, (Actor *) tar, Sender, parameters->int1Parameter);
6212 	} else {
6213 		//apply spell on point
6214 		Point d;
6215 		GetPositionFromScriptable(tar, d, false);
6216 		core->ApplySpellPoint(spellres, tar->GetCurrentArea(), d, Sender, parameters->int1Parameter);
6217 	}
6218 }
6219 
ApplySpellPoint(Scriptable * Sender,Action * parameters)6220 void GameScript::ApplySpellPoint(Scriptable* Sender, Action* parameters)
6221 {
6222 	ieResRef spellres;
6223 
6224 	if (!ResolveSpellName( spellres, parameters) ) {
6225 		return;
6226 	}
6227 
6228 	core->ApplySpellPoint(spellres, Sender->GetCurrentArea(), parameters->pointParameter, Sender, parameters->int1Parameter);
6229 }
6230 
6231 //this is a gemrb extension
6232 //sets a variable to the stat value
GetStat(Scriptable * Sender,Action * parameters)6233 void GameScript::GetStat(Scriptable* Sender, Action* parameters)
6234 {
6235 	ieDword value;
6236 
6237 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6238 	if (!tar || tar->Type!=ST_ACTOR) {
6239 		value = 0;
6240 	} else {
6241 		Actor* actor = ( Actor* ) tar;
6242 		value = actor->GetStat( parameters->int0Parameter );
6243 	}
6244 	SetVariable( Sender, parameters->string0Parameter, value );
6245 }
6246 
BreakInstants(Scriptable * Sender,Action *)6247 void GameScript::BreakInstants(Scriptable* Sender, Action* /*parameters*/)
6248 {
6249 	//don't do anything, apparently the point of this action is to
6250 	//delay the execution of further actions to the next AI cycle
6251 	//Sender->SetWait(1);
6252 	Sender->ReleaseCurrentAction(); // this doesn't really need to block
6253 }
6254 
6255 //an interesting improvement would be to pause game for a given duration
PauseGame(Scriptable * Sender,Action *)6256 void GameScript::PauseGame(Scriptable* Sender, Action* /*parameters*/)
6257 {
6258 	GameControl *gc = core->GetGameControl();
6259 	if (gc) {
6260 		gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, OP_OR);
6261 		displaymsg->DisplayConstantString(STR_SCRIPTPAUSED, DMC_RED);
6262 	}
6263 	// releasing this action allows actions to continue executing,
6264 	// so we force a wait
6265 	Sender->SetWait(1);
6266 	Sender->ReleaseCurrentAction(); // does this need to block?
6267 }
6268 
SetNoOneOnTrigger(Scriptable * Sender,Action * parameters)6269 void GameScript::SetNoOneOnTrigger(Scriptable* Sender, Action* parameters)
6270 {
6271 	Scriptable* ip;
6272 
6273 	if (!parameters->objects[1]) {
6274 		ip=Sender;
6275 	} else {
6276 		ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName);
6277 	}
6278 	if (!ip || (ip->Type!=ST_TRIGGER && ip->Type!=ST_TRAVEL && ip->Type!=ST_PROXIMITY)) {
6279 		Log(WARNING, "Actions", "Script error: No Trigger Named \"%s\"", parameters->objects[1]->objectName);
6280 		return;
6281 	}
6282 
6283 	ip->ClearTriggers();
6284 	// we also need to reset the IF_INTRAP bit for any actors that are inside or subsequent triggers will be skipped
6285 	// there are only two users of this action, so we can be a bit sloppy and skip the geometry checks
6286 	std::vector<Actor *> nearActors = Sender->GetCurrentArea()->GetAllActorsInRadius(ip->Pos, GA_NO_LOS|GA_NO_DEAD|GA_NO_UNSCHEDULED, MAX_OPERATING_DISTANCE);
6287 	std::vector<Actor *>::iterator candidate;
6288 	for (candidate = nearActors.begin(); candidate != nearActors.end(); ++candidate) {
6289 		(*candidate)->SetInTrap(false);
6290 	}
6291 }
6292 
UseDoor(Scriptable * Sender,Action * parameters)6293 void GameScript::UseDoor(Scriptable* Sender, Action* parameters)
6294 {
6295 	GameControl *gc = core->GetGameControl();
6296 	if (!gc) {
6297 		Sender->ReleaseCurrentAction();
6298 		return;
6299 	}
6300 
6301 	gc->ResetTargetMode();
6302 	OpenDoor(Sender, parameters);
6303 
6304 	Sender->ReleaseCurrentAction(); // this is blocking, OpenDoor is not
6305 }
6306 
6307 //this will force bashing the door, if bend bars check is successful,
6308 //it will unlock the door and sets the broken flag
BashDoor(Scriptable * Sender,Action * parameters)6309 void GameScript::BashDoor(Scriptable* Sender, Action* parameters)
6310 {
6311 	GameControl *gc = core->GetGameControl();
6312 	if (!gc) {
6313 		Sender->ReleaseCurrentAction();
6314 		return;
6315 	}
6316 	if (Sender->Type != ST_ACTOR) {
6317 		Sender->ReleaseCurrentAction();
6318 		return;
6319 	}
6320 
6321 	Actor * actor = (Actor *) Sender;
6322 
6323 	Scriptable *target = GetActorFromObject(Sender, parameters->objects[1]);
6324 	Door *door = NULL;
6325 	Container *container = NULL;
6326 	Point *pos;
6327 	if (!target) {
6328 		Sender->ReleaseCurrentAction();
6329 		return;
6330 	}
6331 	if (target->Type == ST_DOOR) {
6332 		door = (Door *) target;
6333 		pos = door->toOpen;
6334 		Point *otherp = door->toOpen+1;
6335 		if (Distance(*pos, Sender)>Distance(*otherp, Sender)) {
6336 			pos=otherp;
6337 		}
6338 	} else if(target->Type == ST_CONTAINER) {
6339 		container = (Container *) target;
6340 		pos = &target->Pos;
6341 	} else {
6342 		Sender->ReleaseCurrentAction();
6343 		return;
6344 	}
6345 
6346 	if (SquaredPersonalDistance(*pos, Sender) > MAX_OPERATING_DISTANCE*MAX_OPERATING_DISTANCE) {
6347 		MoveNearerTo(Sender, *pos, MAX_OPERATING_DISTANCE, 0);
6348 		return;
6349 	}
6350 
6351 	//bashing makes the actor visible
6352 	actor->CureInvisibility();
6353 	gc->SetTargetMode(TARGET_MODE_ATTACK); //for bashing doors too
6354 
6355 	// try to bash it
6356 	if (door) {
6357 		door->TryBashLock(actor);
6358 	} else if (container) {
6359 		container->TryBashLock(actor);
6360 	}
6361 
6362 	Sender->ReleaseCurrentAction();
6363 }
6364 
6365 //pst action
ActivatePortalCursor(Scriptable * Sender,Action * parameters)6366 void GameScript::ActivatePortalCursor(Scriptable* Sender, Action* parameters)
6367 {
6368 	Scriptable* ip;
6369 
6370 	if (!parameters->objects[1]) {
6371 		ip=Sender;
6372 	} else {
6373 		ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName);
6374 	}
6375 	if (!ip) {
6376 		return;
6377 	}
6378 	if (ip->Type!=ST_PROXIMITY && ip->Type!=ST_TRAVEL) {
6379 		return;
6380 	}
6381 	InfoPoint *tar = (InfoPoint *) ip;
6382 	if (parameters->int0Parameter) {
6383 		tar->Trapped|=PORTAL_CURSOR;
6384 	} else {
6385 		tar->Trapped&=~PORTAL_CURSOR;
6386 	}
6387 }
6388 
6389 //pst action
EnablePortalTravel(Scriptable * Sender,Action * parameters)6390 void GameScript::EnablePortalTravel(Scriptable* Sender, Action* parameters)
6391 {
6392 	Scriptable* ip;
6393 
6394 	if (!parameters->objects[1]) {
6395 		ip=Sender;
6396 	} else {
6397 		ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName);
6398 	}
6399 	if (!ip) {
6400 		return;
6401 	}
6402 	if (ip->Type!=ST_PROXIMITY && ip->Type!=ST_TRAVEL) {
6403 		return;
6404 	}
6405 	InfoPoint *tar = (InfoPoint *) ip;
6406 	if (parameters->int0Parameter) {
6407 		tar->Trapped|=PORTAL_TRAVEL;
6408 	} else {
6409 		tar->Trapped&=~PORTAL_TRAVEL;
6410 	}
6411 }
6412 
6413 //unhardcoded iwd action (for the forge entrance change)
ChangeDestination(Scriptable * Sender,Action * parameters)6414 void GameScript::ChangeDestination(Scriptable* Sender, Action* parameters)
6415 {
6416 	InfoPoint *ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName);
6417 	if (ip && (ip->Type==ST_TRAVEL) ) {
6418 		//alter the destination area, don't touch the entrance variable link
6419 		strnlwrcpy(ip->Destination, parameters->string0Parameter, sizeof(ieResRef)-1 );
6420 	}
6421 }
6422 
MoveCursorPoint(Scriptable *,Action *)6423 void GameScript::MoveCursorPoint(Scriptable* /*Sender*/, Action* /*parameters*/)
6424 {
6425 	// according to IESDP this does nothing...
6426 	// the other cursor actions we implement affect only GameControl
6427 	// in GemRB you wouldnt need to move the mouse before scripting a click etc, so this is probably not needed.
6428 }
6429 
6430 //false means, no talk
DialogueInterrupt(Scriptable * Sender,Action * parameters)6431 void GameScript::DialogueInterrupt(Scriptable* Sender, Action* parameters)
6432 {
6433 	if (Sender->Type!=ST_ACTOR) {
6434 		return;
6435 	}
6436 	Actor* actor = ( Actor* ) Sender;
6437 	if ( parameters->int0Parameter != 0 ) {
6438 		actor->SetMCFlag(MC_NO_TALK, OP_NAND);
6439 	} else {
6440 		actor->SetMCFlag(MC_NO_TALK, OP_OR);
6441 	}
6442 }
6443 
EquipMostDamagingMelee(Scriptable * Sender,Action *)6444 void GameScript::EquipMostDamagingMelee(Scriptable* Sender, Action* /*parameters*/)
6445 {
6446 	if (Sender->Type!=ST_ACTOR) {
6447 		return;
6448 	}
6449 	Actor* actor = ( Actor* ) Sender;
6450 	actor->inventory.EquipBestWeapon(EQUIP_MELEE);
6451 }
6452 
EquipRanged(Scriptable * Sender,Action *)6453 void GameScript::EquipRanged(Scriptable* Sender, Action* /*parameters*/)
6454 {
6455 	if (Sender->Type!=ST_ACTOR) {
6456 		return;
6457 	}
6458 	Actor* actor = ( Actor* ) Sender;
6459 	actor->inventory.EquipBestWeapon(EQUIP_RANGED);
6460 }
6461 
6462 //will equip best weapon regardless of range considerations
EquipWeapon(Scriptable * Sender,Action *)6463 void GameScript::EquipWeapon(Scriptable* Sender, Action* /*parameters*/)
6464 {
6465 	if (Sender->Type!=ST_ACTOR) {
6466 		return;
6467 	}
6468 	Actor* actor = ( Actor* ) Sender;
6469 	actor->inventory.EquipBestWeapon(EQUIP_MELEE|EQUIP_RANGED);
6470 }
6471 
SetBestWeapon(Scriptable * Sender,Action * parameters)6472 void GameScript::SetBestWeapon(Scriptable* Sender, Action* parameters)
6473 {
6474 	if (Sender->Type!=ST_ACTOR) {
6475 		return;
6476 	}
6477 
6478 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6479 	if (!tar || tar->Type!=ST_ACTOR) {
6480 		return;
6481 	}
6482 	Actor* actor = ( Actor* ) Sender;
6483 
6484 	Actor *target = (Actor *) tar;
6485 	if (PersonalDistance(actor,target)>(unsigned int) parameters->int0Parameter) {
6486 		actor->inventory.EquipBestWeapon(EQUIP_RANGED);
6487 	} else {
6488 		actor->inventory.EquipBestWeapon(EQUIP_MELEE);
6489 	}
6490 }
6491 
FakeEffectExpiryCheck(Scriptable * Sender,Action * parameters)6492 void GameScript::FakeEffectExpiryCheck(Scriptable* Sender, Action* parameters)
6493 {
6494 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6495 	if (!tar || tar->Type!=ST_ACTOR) {
6496 		return;
6497 	}
6498 	Actor *target = (Actor *) tar;
6499 	target->fxqueue.RemoveExpiredEffects(parameters->int0Parameter * AI_UPDATE_TIME);
6500 }
6501 
SetInterrupt(Scriptable * Sender,Action * parameters)6502 void GameScript::SetInterrupt(Scriptable* Sender, Action* parameters)
6503 {
6504 	if (parameters->int0Parameter) {
6505 		Sender->Interrupt();
6506 	} else {
6507 		Sender->NoInterrupt();
6508 	}
6509 }
6510 
SelectWeaponAbility(Scriptable * Sender,Action * parameters)6511 void GameScript::SelectWeaponAbility(Scriptable* Sender, Action* parameters)
6512 {
6513 	if (Sender->Type!=ST_ACTOR) {
6514 		return;
6515 	}
6516 	Actor *scr = (Actor *) Sender;
6517 	int slot = parameters->int0Parameter;
6518 	int wslot = scr->inventory.GetWeaponSlot();
6519 	//weapon
6520 	if (core->QuerySlotType(slot)&SLOT_WEAPON) {
6521 		slot-=wslot;
6522 		if (slot<0 || slot>=MAX_QUICKWEAPONSLOT) {
6523 			return;
6524 		}
6525 		scr->SetEquippedQuickSlot(slot, parameters->int1Parameter);
6526 		return;
6527 	}
6528 	//quick item
6529 	wslot = scr->inventory.GetQuickSlot();
6530 	if (core->QuerySlotType(slot)&SLOT_ITEM) {
6531 		slot-=wslot;
6532 		if (slot<0 || slot>=MAX_QUICKITEMSLOT) {
6533 			return;
6534 		}
6535 		if (scr->PCStats) {
6536 			scr->PCStats->QuickItemHeaders[slot]=(ieWord) parameters->int1Parameter;
6537 		}
6538 	}
6539 }
6540 
UseItem(Scriptable * Sender,Action * parameters)6541 void GameScript::UseItem(Scriptable* Sender, Action* parameters)
6542 {
6543 	if (Sender->Type!=ST_ACTOR) {
6544 		Sender->ReleaseCurrentAction();
6545 		return;
6546 	}
6547 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
6548 	if (!tar) {
6549 		Sender->ReleaseCurrentAction();
6550 		return;
6551 	}
6552 	Actor *act = (Actor *) Sender;
6553 	int Slot;
6554 	ieDword header, flags;
6555 	ieResRef itemres;
6556 
6557 	if (parameters->string0Parameter[0]) {
6558 		Slot = act->inventory.FindItem(parameters->string0Parameter, IE_INV_ITEM_UNDROPPABLE);
6559 		//this IS in the original game code (ability)
6560 		header = parameters->int0Parameter;
6561 		flags = parameters->int1Parameter;
6562 	} else {
6563 		Slot = parameters->int0Parameter;
6564 		//this is actually not in the original game code
6565 		header = parameters->int1Parameter;
6566 		flags = parameters->int2Parameter;
6567 	}
6568 
6569 	if (Slot == -1) {
6570 		Sender->ReleaseCurrentAction();
6571 		return;
6572 	}
6573 
6574 	if (!ResolveItemName( itemres, act, Slot) ) {
6575 		Sender->ReleaseCurrentAction();
6576 		return;
6577 	}
6578 
6579 	unsigned int dist = GetItemDistance(itemres, header);
6580 
6581 	if (PersonalDistance(Sender, tar) > dist) {
6582 		MoveNearerTo(Sender, tar, dist);
6583 		return;
6584 	}
6585 
6586 	act->UseItem(Slot, header, tar, flags);
6587 	Sender->ReleaseCurrentAction();
6588 }
6589 
UseItemPoint(Scriptable * Sender,Action * parameters)6590 void GameScript::UseItemPoint(Scriptable* Sender, Action* parameters)
6591 {
6592 	if (Sender->Type!=ST_ACTOR) {
6593 		Sender->ReleaseCurrentAction();
6594 		return;
6595 	}
6596 
6597 	Actor *act = (Actor *) Sender;
6598 	int Slot;
6599 	ieDword header;
6600 	ieResRef itemres;
6601 	ieDword flags;
6602 
6603 	if (parameters->string0Parameter[0]) {
6604 		Slot = act->inventory.FindItem(parameters->string0Parameter, 0);
6605 		//this IS in the original game code (ability)
6606 		header = parameters->int0Parameter;
6607 		flags = parameters->int1Parameter;
6608 	} else {
6609 		Slot = parameters->int0Parameter;
6610 		//this is actually not in the original game code
6611 		header = parameters->int1Parameter;
6612 		flags = parameters->int2Parameter;
6613 	}
6614 
6615 	if (Slot == -1) {
6616 		Sender->ReleaseCurrentAction();
6617 		return;
6618 	}
6619 
6620 	if (!ResolveItemName( itemres, act, Slot) ) {
6621 		Sender->ReleaseCurrentAction();
6622 		return;
6623 	}
6624 
6625 	unsigned int dist = GetItemDistance(itemres, header);
6626 
6627 	if (PersonalDistance(parameters->pointParameter, Sender) > dist) {
6628 		MoveNearerTo(Sender, parameters->pointParameter, dist, 0);
6629 		return;
6630 	}
6631 
6632 	act->UseItemPoint(Slot, header, parameters->pointParameter, flags);
6633 	Sender->ReleaseCurrentAction();
6634 }
6635 
6636 //addfeat will be able to remove feats too
6637 //(the second int parameter is a value to add to the feat)
AddFeat(Scriptable * Sender,Action * parameters)6638 void GameScript::AddFeat(Scriptable* Sender, Action* parameters)
6639 {
6640 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6641 	if (!tar || tar->Type!=ST_ACTOR) {
6642 		return;
6643 	}
6644 	Actor *actor = (Actor *)tar;
6645 	//value to add to the feat
6646 	int value = parameters->int1Parameter;
6647 	//default is increase by 1
6648 	if (!value) value = 1;
6649 	value += actor->GetFeat(parameters->int0Parameter);
6650 	//SetFeatValue will handle edges
6651 	actor->SetFeatValue(parameters->int0Parameter, value);
6652 }
6653 
MatchHP(Scriptable * Sender,Action * parameters)6654 void GameScript::MatchHP(Scriptable* Sender, Action* parameters)
6655 {
6656 	if (Sender->Type!=ST_ACTOR) {
6657 		return;
6658 	}
6659 	Actor *scr = (Actor *) Sender;
6660 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6661 	if (!tar || tar->Type!=ST_ACTOR) {
6662 		return;
6663 	}
6664 	Actor *actor = (Actor *)tar;
6665 	switch (parameters->int0Parameter) {
6666 		case 1: //sadly the hpflags are not the same as stats
6667 			actor->SetBase(IE_HITPOINTS,scr->GetBase(IE_HITPOINTS));
6668 			break;
6669 		case 0:
6670 			actor->SetBase(IE_MAXHITPOINTS, scr->GetBase(IE_MAXHITPOINTS));
6671 			break;
6672 		default: //this is gemrb extension
6673 			actor->SetBase(parameters->int0Parameter, scr->GetBase(parameters->int0Parameter));
6674 			break;
6675 	}
6676 }
6677 
ChangeColor(Scriptable * Sender,Action * parameters)6678 void GameScript::ChangeColor(Scriptable* Sender, Action* parameters)
6679 {
6680 	if (Sender->Type!=ST_ACTOR) {
6681 		return;
6682 	}
6683 	Actor *scr = (Actor *) Sender;
6684 	ieDword stat = parameters->int0Parameter;
6685 	if (stat<9 || stat>14) {
6686 		return;
6687 	}
6688 	stat += IE_COLORS - 9;
6689 	scr->SetBase(stat, (scr->GetBase(stat)&~255)|(parameters->int1Parameter&255));
6690 }
6691 
6692 //removes previous kit, adds new
AddKit(Scriptable * Sender,Action * parameters)6693 void GameScript::AddKit(Scriptable* Sender, Action* parameters)
6694 {
6695 	if (Sender->Type!=ST_ACTOR) {
6696 		return;
6697 	}
6698 	Actor *scr = (Actor *) Sender;
6699 	//remove previous kit stuff
6700 	scr->ApplyKit(true);
6701 	//this adds the current level abilities
6702 	scr->SetBase(IE_KIT, parameters->int0Parameter);
6703 	scr->ApplyKit(false);
6704 }
6705 
6706 //doesn't remove old kit
AddSuperKit(Scriptable * Sender,Action * parameters)6707 void GameScript::AddSuperKit(Scriptable* Sender, Action* parameters)
6708 {
6709 	if (Sender->Type!=ST_ACTOR) {
6710 		return;
6711 	}
6712 	Actor *scr = (Actor *) Sender;
6713 	scr->SetBase(IE_KIT, parameters->int0Parameter);
6714 	scr->ApplyKit(false);
6715 }
6716 
SetSelection(Scriptable *,Action * parameters)6717 void GameScript::SetSelection(Scriptable* /*Sender*/, Action* parameters)
6718 {
6719 	GameControl *gc = core->GetGameControl();
6720 	if (!gc) {
6721 		return;
6722 	}
6723 	gc->SelectActor(parameters->int0Parameter, parameters->int1Parameter);
6724 }
6725 
6726 //this action is weird in the original game, because it overwrites ALL
6727 //IDS stats.
6728 //in this version, if a stat is set to 0, it won't change
6729 //it will alter only the main IDS stats
6730 // (unused in the originals, but if that changes, make sure all ids files are handled; eg. see iwd2 script.2da)
ChangeAIType(Scriptable * Sender,Action * parameters)6731 void GameScript::ChangeAIType(Scriptable* Sender, Action* parameters)
6732 {
6733 	if (Sender->Type!=ST_ACTOR) {
6734 		return;
6735 	}
6736 	Object *ob = parameters->objects[1];
6737 	if (!ob) {
6738 		return;
6739 	}
6740 	Actor *scr = (Actor *) Sender;
6741 	for (int i=0;i<MAX_OBJECT_FIELDS;i++) {
6742 		int val = ob->objectFields[i];
6743 		if (!val) continue;
6744 		if (!strnicmp(ObjectIDSTableNames[i],"ea",8)) {
6745 			scr->SetBase(IE_EA, val);
6746 			continue;
6747 		}
6748 		if (!strnicmp(ObjectIDSTableNames[i],"general",8)) {
6749 			scr->SetBase(IE_GENERAL, val);
6750 			continue;
6751 		}
6752 		if (!strnicmp(ObjectIDSTableNames[i],"race",8)) {
6753 			scr->SetBase(IE_RACE, val);
6754 			continue;
6755 		}
6756 		if (!strnicmp(ObjectIDSTableNames[i],"class",8)) {
6757 			scr->SetBase(IE_CLASS, val);
6758 			continue;
6759 		}
6760 		if (!strnicmp(ObjectIDSTableNames[i],"gender",8)) {
6761 			scr->SetBase(IE_SEX, val);
6762 			continue;
6763 		}
6764 		if (!strnicmp(ObjectIDSTableNames[i],"specific",8)) {
6765 			scr->SetBase(IE_SPECIFIC, val);
6766 			continue;
6767 		}
6768 		if (!strnicmp(ObjectIDSTableNames[i],"align",8)) {
6769 			scr->SetBase(IE_ALIGNMENT, val);
6770 			continue;
6771 		}
6772 	}
6773 }
6774 
6775 //same as MoveToPoint, but not blocking
Leader(Scriptable * Sender,Action * parameters)6776 void GameScript::Leader(Scriptable* Sender, Action* parameters)
6777 {
6778 	if (Sender->Type != ST_ACTOR) {
6779 		return;
6780 	}
6781 
6782 	char Tmp[256];
6783 
6784 	snprintf(Tmp, 256, "MoveToPoint([%d.%d])", parameters->pointParameter.x, parameters->pointParameter.y);
6785 	Action *newact = GenerateAction(Tmp);
6786 	Sender->AddAction(newact);
6787 }
6788 
6789 //same as MoveToPointNoRecticle, but not blocking
Follow(Scriptable * Sender,Action * parameters)6790 void GameScript::Follow(Scriptable* Sender, Action* parameters)
6791 {
6792 	if (Sender->Type != ST_ACTOR) {
6793 		return;
6794 	}
6795 
6796 	char Tmp[256];
6797 
6798 	snprintf(Tmp, 256, "MoveToPointNoRecticle([%d.%d])", parameters->pointParameter.x, parameters->pointParameter.y);
6799 	Action *newact = GenerateAction(Tmp);
6800 	Sender->AddAction(newact);
6801 }
6802 
FollowCreature(Scriptable * Sender,Action * parameters)6803 void GameScript::FollowCreature(Scriptable* Sender, Action* parameters)
6804 {
6805 	if (Sender->Type!=ST_ACTOR) {
6806 		Sender->ReleaseCurrentAction();
6807 		return;
6808 	}
6809 
6810 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
6811 	if (!tar || tar->Type!=ST_ACTOR) {
6812 		Sender->ReleaseCurrentAction();
6813 		return;
6814 	}
6815 	Actor *scr = (Actor *)Sender;
6816 	Actor *actor = (Actor *)tar;
6817 	scr->LastFollowed = actor->GetGlobalID();
6818 	scr->FollowOffset.empty();
6819 	if (!scr->InMove() || scr->Destination != actor->Pos) {
6820 		scr->WalkTo(actor->Pos, 0, 1);
6821 	}
6822 }
6823 
RunFollow(Scriptable * Sender,Action * parameters)6824 void GameScript::RunFollow(Scriptable* Sender, Action* parameters)
6825 {
6826 	if (Sender->Type!=ST_ACTOR) {
6827 		Sender->ReleaseCurrentAction();
6828 		return;
6829 	}
6830 
6831 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
6832 	if (!tar || tar->Type!=ST_ACTOR) {
6833 		Sender->ReleaseCurrentAction();
6834 		return;
6835 	}
6836 	Actor *scr = (Actor *)Sender;
6837 	Actor *actor = (Actor *)tar;
6838 	scr->LastFollowed = actor->GetGlobalID();
6839 	scr->FollowOffset.empty();
6840 	if (!scr->InMove() || scr->Destination != actor->Pos) {
6841 		scr->WalkTo(actor->Pos, IF_RUNNING, 1);
6842 	}
6843 }
6844 
ProtectPoint(Scriptable * Sender,Action * parameters)6845 void GameScript::ProtectPoint(Scriptable* Sender, Action* parameters)
6846 {
6847 	if (Sender->Type!=ST_ACTOR) {
6848 		Sender->ReleaseCurrentAction();
6849 		return;
6850 	}
6851 	Actor *scr = (Actor *)Sender;
6852 	if (!scr->InMove() || scr->Destination != parameters->pointParameter) {
6853 		scr->WalkTo( parameters->pointParameter, 0, 1 );
6854 	}
6855 	// we should handle 'Protect' here rather than just unblocking
6856 	Sender->ReleaseCurrentAction();
6857 }
6858 
ProtectObject(Scriptable * Sender,Action * parameters)6859 void GameScript::ProtectObject(Scriptable* Sender, Action* parameters)
6860 {
6861 	if (Sender->Type!=ST_ACTOR) {
6862 		Sender->ReleaseCurrentAction();
6863 		return;
6864 	}
6865 
6866 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
6867 	if (!tar || tar->Type!=ST_ACTOR) {
6868 		Sender->ReleaseCurrentAction();
6869 		return;
6870 	}
6871 	Actor *scr = (Actor *)Sender;
6872 	Actor *actor = (Actor *)tar;
6873 	scr->LastFollowed = actor->GetGlobalID();
6874 	scr->LastProtectee = actor->GetGlobalID();
6875 	actor->LastProtector = scr->GetGlobalID();
6876 	//not exactly range
6877 	scr->FollowOffset.x = parameters->int0Parameter;
6878 	scr->FollowOffset.y = parameters->int0Parameter;
6879 	if (!scr->InMove() || scr->Destination != tar->Pos) {
6880 		scr->WalkTo( tar->Pos, 0, MAX_OPERATING_DISTANCE );
6881 	}
6882 	// we should handle 'Protect' here rather than just unblocking
6883 	Sender->ReleaseCurrentAction();
6884 }
6885 
6886 //keeps following the object in formation
FollowObjectFormation(Scriptable * Sender,Action * parameters)6887 void GameScript::FollowObjectFormation(Scriptable* Sender, Action* parameters)
6888 {
6889 	GameControl *gc = core->GetGameControl();
6890 	if (!gc) {
6891 		Sender->ReleaseCurrentAction();
6892 		return;
6893 	}
6894 	if (Sender->Type!=ST_ACTOR) {
6895 		Sender->ReleaseCurrentAction();
6896 		return;
6897 	}
6898 
6899 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
6900 	if (!tar || tar->Type!=ST_ACTOR) {
6901 		Sender->ReleaseCurrentAction();
6902 		return;
6903 	}
6904 	Actor *scr = (Actor *)Sender;
6905 	Actor *actor = (Actor *)tar;
6906 	scr->LastFollowed = actor->GetGlobalID();
6907 	ieDword formation = parameters->int0Parameter;
6908 	ieDword pos = parameters->int1Parameter;
6909 	scr->FollowOffset = gc->GetFormationOffset(formation, pos);
6910 	if (!scr->InMove() || scr->Destination != tar->Pos) {
6911 		scr->WalkTo( tar->Pos, 0, 1 );
6912 	}
6913 	Sender->ReleaseCurrentAction();
6914 }
6915 
6916 //walks to a specific offset of target (quite like movetoobject)
Formation(Scriptable * Sender,Action * parameters)6917 void GameScript::Formation(Scriptable* Sender, Action* parameters)
6918 {
6919 	GameControl *gc = core->GetGameControl();
6920 	if (!gc) {
6921 		Sender->ReleaseCurrentAction();
6922 		return;
6923 	}
6924 	if (Sender->Type!=ST_ACTOR) {
6925 		Sender->ReleaseCurrentAction();
6926 		return;
6927 	}
6928 	Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] );
6929 	if (!tar) {
6930 		Sender->ReleaseCurrentAction();
6931 		return;
6932 	}
6933 	Actor *scr = (Actor *)Sender;
6934 	ieDword formation = parameters->int0Parameter;
6935 	ieDword pos = parameters->int1Parameter;
6936 	Point FollowOffset = gc->GetFormationOffset(formation, pos);
6937 	FollowOffset.x+=tar->Pos.x;
6938 	FollowOffset.y+=tar->Pos.y;
6939 	if (!scr->InMove() || scr->Destination != FollowOffset) {
6940 		scr->WalkTo( FollowOffset, 0, 1 );
6941 	}
6942 }
6943 
TransformItem(Scriptable * Sender,Action * parameters)6944 void GameScript::TransformItem(Scriptable* Sender, Action* parameters)
6945 {
6946 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6947 	if (!tar || tar->Type!=ST_ACTOR) {
6948 		return;
6949 	}
6950 	TransformItemCore((Actor *)tar, parameters, true);
6951 }
6952 
TransformPartyItem(Scriptable *,Action * parameters)6953 void GameScript::TransformPartyItem(Scriptable* /*Sender*/, Action* parameters)
6954 {
6955 	Game *game = core->GetGame();
6956 	int i = game->GetPartySize(false);
6957 	while (i--) {
6958 		Actor *tar = game->GetPC(i, false);
6959 		TransformItemCore(tar, parameters, true);
6960 	}
6961 }
6962 
TransformItemAll(Scriptable * Sender,Action * parameters)6963 void GameScript::TransformItemAll(Scriptable* Sender, Action* parameters)
6964 {
6965 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
6966 	if (!tar || tar->Type!=ST_ACTOR) {
6967 		return;
6968 	}
6969 	TransformItemCore((Actor *)tar, parameters, false);
6970 }
6971 
TransformPartyItemAll(Scriptable *,Action * parameters)6972 void GameScript::TransformPartyItemAll(Scriptable* /*Sender*/, Action* parameters)
6973 {
6974 	Game *game = core->GetGame();
6975 	int i = game->GetPartySize(false);
6976 	while (i--) {
6977 		Actor *tar = game->GetPC(i, false);
6978 		TransformItemCore(tar, parameters, false);
6979 	}
6980 }
6981 
6982 // pst spawning
GeneratePartyMember(Scriptable *,Action * parameters)6983 void GameScript::GeneratePartyMember(Scriptable* /*Sender*/, Action* parameters)
6984 {
6985 	AutoTable pcs("bios");
6986 	if (!pcs) {
6987 		return;
6988 	}
6989 	const char* string = pcs->GetRowName(parameters->int0Parameter);
6990 	char name[32];
6991 	strnlwrcpy(name, string, 32);
6992 	Actor *actor = core->GetGame()->FindNPC(string);
6993 	if (!actor) {
6994 		return;
6995 	}
6996 	if (!actor->GetCurrentArea()) {
6997 		core->GetGame()->GetCurrentArea()->AddActor(actor, true);
6998 	}
6999 	actor->SetOrientation(parameters->int1Parameter, false);
7000 	actor->MoveTo(parameters->pointParameter);
7001 	actor->Die(NULL);
7002 	actor->SetBaseBit(IE_STATE_ID, STATE_DEAD, true);
7003 }
7004 
EnableFogDither(Scriptable *,Action *)7005 void GameScript::EnableFogDither(Scriptable* /*Sender*/, Action* /*parameters*/)
7006 {
7007 	core->GetGameControl()->DebugFlags |= DEBUG_SHOW_FOG_ALL;
7008 }
7009 
DisableFogDither(Scriptable *,Action *)7010 void GameScript::DisableFogDither(Scriptable* /*Sender*/, Action* /*parameters*/)
7011 {
7012 	core->GetGameControl()->DebugFlags &= ~DEBUG_SHOW_FOG_ALL;
7013 }
7014 
EnableSpriteDither(Scriptable *,Action *)7015 void GameScript::EnableSpriteDither(Scriptable* /*Sender*/, Action* /*parameters*/)
7016 {
7017 	core->DitherSprites = true;
7018 }
7019 
DisableSpriteDither(Scriptable *,Action *)7020 void GameScript::DisableSpriteDither(Scriptable* /*Sender*/, Action* /*parameters*/)
7021 {
7022 	core->DitherSprites = false;
7023 }
7024 
7025 //the PST crew apparently loved hardcoding stuff
7026 ieResRef RebusResRef={"DABUS1"};
7027 
FloatRebus(Scriptable * Sender,Action * parameters)7028 void GameScript::FloatRebus(Scriptable* Sender, Action* parameters)
7029 {
7030 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
7031 	if (!tar || tar->Type!=ST_ACTOR) {
7032 		return;
7033 	}
7034 	Actor *actor = (Actor *)tar;
7035 	RebusResRef[5]=(char) core->Roll(1,5,'0');
7036 	ScriptedAnimation *vvc = gamedata->GetScriptedAnimation(RebusResRef, 0);
7037 	if (vvc) {
7038 		//setting the height
7039 		vvc->ZOffset = actor->size * 20;
7040 		vvc->PlayOnce();
7041 		//maybe this needs setting up some time
7042 		vvc->SetDefaultDuration(20);
7043 		actor->AddVVCell(vvc);
7044 	}
7045 }
7046 
IncrementKillStat(Scriptable * Sender,Action * parameters)7047 void GameScript::IncrementKillStat(Scriptable* Sender, Action* parameters)
7048 {
7049 	DataFileMgr * ini = core->GetBeastsINI();
7050 	if (!ini) {
7051 		return;
7052 	}
7053 	char key[40];
7054 	sprintf(key,"%d", parameters->int0Parameter);
7055 	const char *variable = ini->GetKeyAsString( key, "killvar", NULL );
7056 	if (!variable) {
7057 		return;
7058 	}
7059 	ieDword value = CheckVariable( Sender, variable, "GLOBAL" ) + 1;
7060 	SetVariable( Sender, variable, "GLOBAL", value );
7061 }
7062 
7063 //this action uses the sceffect.ids (which should be covered by our cgtable.2da)
7064 //The original engines solved cg by hardcoding either vvcs or sparkles
7065 //so either we include sparkles as possible CG or just simulate all of these with projectiles
7066 //in any case, this action just creates an opcode (0xeb) and plays sound
7067 static EffectRef fx_iwd_casting_glow_ref = { "CastingGlow2", -1 };
7068 
SpellCastEffect(Scriptable * Sender,Action * parameters)7069 void GameScript::SpellCastEffect(Scriptable* Sender, Action* parameters)
7070 {
7071 	Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] );
7072 	if (!src || src->Type!=ST_ACTOR) {
7073 		return;
7074 	}
7075 
7076 	ieDword sparkle = parameters->int0Parameter;
7077 
7078 	int opcode = EffectQueue::ResolveEffect(fx_iwd_casting_glow_ref);
7079 	Effect *fx = core->GetEffect(opcode);
7080 	if (!fx) {
7081 		//invalid effect name didn't resolve to opcode
7082 		return;
7083 	}
7084 
7085 	core->GetAudioDrv()->Play(parameters->string0Parameter, SFX_CHAN_CASTING,
7086 				Sender->Pos.x, Sender->Pos.y, 0);
7087 
7088 	fx->ProbabilityRangeMax = 100;
7089 	fx->ProbabilityRangeMin = 0;
7090 	fx->Parameter2 = sparkle; //animation type
7091 	fx->TimingMode = FX_DURATION_INSTANT_LIMITED;
7092 	fx->Duration = parameters->int1Parameter * 15;
7093 	fx->Target = FX_TARGET_PRESET;
7094 	//int2param isn't actually used in the original engine
7095 
7096 	core->ApplyEffect(fx, (Actor *) src, src);
7097 	delete fx;
7098 }
7099 
7100 //this action plays a vvc animation over target
7101 //we simply apply the appropriate opcode on the target (see iwdopcodes)
7102 //the list of vvcs is in iwdshtab.2da (sheffect.ids)
7103 static EffectRef fx_iwd_visual_spell_hit_ref = { "IWDVisualSpellHit", -1 };
7104 
SpellHitEffectSprite(Scriptable * Sender,Action * parameters)7105 void GameScript::SpellHitEffectSprite(Scriptable* Sender, Action* parameters)
7106 {
7107 	Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] );
7108 	if (!src) {
7109 		return;
7110 	}
7111 	Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2] );
7112 	if (!tar || tar->Type!=ST_ACTOR) {
7113 		return;
7114 	}
7115 	int opcode = EffectQueue::ResolveEffect(fx_iwd_visual_spell_hit_ref);
7116 	Effect *fx = core->GetEffect(opcode);
7117 	if (!fx) {
7118 		//invalid effect name didn't resolve to opcode
7119 		return;
7120 	}
7121 
7122 	//vvc type
7123 	fx->Parameter2 = parameters->int0Parameter;
7124 	//height (not sure if this is in the opcode, but seems acceptable)
7125 	fx->Parameter1 = parameters->int1Parameter;
7126 	fx->Parameter4 = 1; // mark for special treatment
7127 	fx->ProbabilityRangeMax = 100;
7128 	fx->ProbabilityRangeMin = 0;
7129 	fx->TimingMode=FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES;
7130 	fx->PosX=tar->Pos.x;
7131 	fx->PosY=tar->Pos.y;
7132 	fx->Target = FX_TARGET_PRESET;
7133 	core->ApplyEffect(fx, (Actor *) tar, src);
7134 	delete fx;
7135 }
7136 
SpellHitEffectPoint(Scriptable * Sender,Action * parameters)7137 void GameScript::SpellHitEffectPoint(Scriptable* Sender, Action* parameters)
7138 {
7139 	Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] );
7140 	if (!src) {
7141 		return;
7142 	}
7143 
7144 	int opcode = EffectQueue::ResolveEffect(fx_iwd_visual_spell_hit_ref);
7145 	Effect *fx = core->GetEffect(opcode);
7146 	if (!fx) {
7147 		//invalid effect name didn't resolve to opcode
7148 		return;
7149 	}
7150 
7151 	//vvc type
7152 	fx->Parameter2 = parameters->int0Parameter;
7153 	//height (not sure if this is in the opcode, but seems acceptable)
7154 	fx->Parameter1 = parameters->int1Parameter;
7155 	fx->Parameter4 = 1; // mark for special treatment
7156 	fx->ProbabilityRangeMax = 100;
7157 	fx->ProbabilityRangeMin = 0;
7158 	fx->TimingMode=FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES;
7159 	// iwd2 with [-1.-1] again
7160 	if (parameters->pointParameter.x == -1) {
7161 		fx->PosX = src->Pos.x;
7162 		fx->PosY = src->Pos.y;
7163 	} else {
7164 		fx->PosX = parameters->pointParameter.x;
7165 		fx->PosY = parameters->pointParameter.y;
7166 	}
7167 	fx->Target = FX_TARGET_PRESET;
7168 	core->ApplyEffect(fx, NULL, src);
7169 	delete fx;
7170 }
7171 
7172 
ClickLButtonObject(Scriptable * Sender,Action * parameters)7173 void GameScript::ClickLButtonObject(Scriptable* Sender, Action* parameters)
7174 {
7175 	Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] );
7176 	if (!tar) {
7177 		Sender->ReleaseCurrentAction(); // this is blocking for some reason?
7178 		return;
7179 	}
7180 	Event e = EventMgr::CreateMouseBtnEvent(tar->Pos, GEM_MB_ACTION, true);
7181 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7182 }
7183 
ClickLButtonPoint(Scriptable * Sender,Action * parameters)7184 void GameScript::ClickLButtonPoint(Scriptable* Sender, Action* parameters)
7185 {
7186 	Event e = EventMgr::CreateMouseBtnEvent(parameters->pointParameter, GEM_MB_ACTION, true);
7187 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7188 }
7189 
ClickRButtonObject(Scriptable * Sender,Action * parameters)7190 void GameScript::ClickRButtonObject(Scriptable* Sender, Action* parameters)
7191 {
7192 	Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] );
7193 	if (!tar) {
7194 		Sender->ReleaseCurrentAction(); // this is blocking for some reason?
7195 		return;
7196 	}
7197 	Event e = EventMgr::CreateMouseBtnEvent(tar->Pos, GEM_MB_MENU, true);
7198 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7199 }
7200 
ClickRButtonPoint(Scriptable * Sender,Action * parameters)7201 void GameScript::ClickRButtonPoint(Scriptable* Sender, Action* parameters)
7202 {
7203 	Event e = EventMgr::CreateMouseBtnEvent(parameters->pointParameter, GEM_MB_MENU, true);
7204 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7205 }
7206 
DoubleClickLButtonObject(Scriptable * Sender,Action * parameters)7207 void GameScript::DoubleClickLButtonObject(Scriptable* Sender, Action* parameters)
7208 {
7209 	Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] );
7210 	if (!tar) {
7211 		Sender->ReleaseCurrentAction(); // this is blocking for some reason?
7212 		return;
7213 	}
7214 	Event e = EventMgr::CreateMouseBtnEvent(tar->Pos, GEM_MB_ACTION, true);
7215 	e.mouse.repeats = 2;
7216 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7217 }
7218 
DoubleClickLButtonPoint(Scriptable * Sender,Action * parameters)7219 void GameScript::DoubleClickLButtonPoint(Scriptable* Sender, Action* parameters)
7220 {
7221 	Event e = EventMgr::CreateMouseBtnEvent(parameters->pointParameter, GEM_MB_ACTION, true);
7222 	e.mouse.repeats = 2;
7223 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7224 }
7225 
DoubleClickRButtonObject(Scriptable * Sender,Action * parameters)7226 void GameScript::DoubleClickRButtonObject(Scriptable* Sender, Action* parameters)
7227 {
7228 	Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] );
7229 	if (!tar) {
7230 		Sender->ReleaseCurrentAction(); // this is blocking for some reason?
7231 		return;
7232 	}
7233 	Event e = EventMgr::CreateMouseBtnEvent(tar->Pos, GEM_MB_MENU, true);
7234 	e.mouse.repeats = 2;
7235 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7236 }
7237 
DoubleClickRButtonPoint(Scriptable * Sender,Action * parameters)7238 void GameScript::DoubleClickRButtonPoint(Scriptable* Sender, Action* parameters)
7239 {
7240 	Event e = EventMgr::CreateMouseBtnEvent(parameters->pointParameter, GEM_MB_MENU, true);
7241 	e.mouse.repeats = 2;
7242 	ClickCore(Sender, e.mouse, parameters->int0Parameter);
7243 }
7244 
7245 //Picks 5 lines from wish.2da
7246 //Gets the 5 values (column is int0parameter) from the table.
7247 //Sets the five wishpowerNN to 1, while resets the rest to 0.
7248 //TODO: investigate what happens with * values
SetupWish(Scriptable * Sender,Action * parameters)7249 void GameScript::SetupWish(Scriptable* Sender, Action* parameters)
7250 {
7251 	SetupWishCore(Sender, parameters->int0Parameter, parameters->int1Parameter);
7252 }
7253 
7254 //The same as the previous action, except that the column parameter comes from
7255 //the target object's wisdom directly (this action is not used in the original)
SetupWishObject(Scriptable * Sender,Action * parameters)7256 void GameScript::SetupWishObject(Scriptable* Sender, Action* parameters)
7257 {
7258 	Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] );
7259 	if (!tar || tar->Type!=ST_ACTOR) {
7260 		return;
7261 	}
7262 	SetupWishCore(Sender, ((Actor *)tar)->GetStat(IE_WIS), parameters->int0Parameter);
7263 }
7264 
7265 //GemRB specific action
7266 //Sets up multiple tokens randomly (one per 2da row)
7267 //the row label column sets the token names
SetToken2DA(Scriptable *,Action * parameters)7268 void GameScript::SetToken2DA(Scriptable* /*Sender*/, Action* parameters)
7269 {
7270 	int count;
7271 	int i,j;
7272 	ieVariable tokenname;
7273 
7274 	AutoTable tm(parameters->string0Parameter);
7275 	if (!tm) {
7276 		Log(ERROR, "Actions", "Cannot find %s.2da.", parameters->string0Parameter);
7277 		return;
7278 	}
7279 
7280 	count = tm->GetRowCount();
7281 	for(i=0;i<count;i++) {
7282 		//roll a random number between 0 and column #
7283 		j = core->Roll(1,tm->GetColumnCount(i),-1);
7284 		strnuprcpy(tokenname, tm->GetRowName(i), 32);
7285 		core->GetTokenDictionary()->SetAtCopy( tokenname, tm->QueryField(i, j) );
7286 	}
7287 }
7288 
7289 //this is a gemrb extension for scriptable tracks
SetTrackString(Scriptable * Sender,Action * parameters)7290 void GameScript::SetTrackString(Scriptable* Sender, Action* parameters)
7291 {
7292 	Map *map = Sender->GetCurrentArea();
7293 	if (!map) return;
7294 	map->SetTrackString(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter);
7295 }
7296 
StateOverrideFlag(Scriptable *,Action * parameters)7297 void GameScript::StateOverrideFlag(Scriptable* /*Sender*/, Action* parameters)
7298 {
7299 	core->GetGame()->StateOverrideFlag = parameters->int0Parameter;
7300 }
7301 
StateOverrideTime(Scriptable *,Action * parameters)7302 void GameScript::StateOverrideTime(Scriptable* /*Sender*/, Action* parameters)
7303 {
7304 	core->GetGame()->StateOverrideTime = parameters->int0Parameter;
7305 }
7306 
BanterBlockFlag(Scriptable *,Action * parameters)7307 void GameScript::BanterBlockFlag(Scriptable* /*Sender*/, Action* parameters)
7308 {
7309 	core->GetGame()->BanterBlockFlag = parameters->int0Parameter;
7310 }
7311 
BanterBlockTime(Scriptable *,Action * parameters)7312 void GameScript::BanterBlockTime(Scriptable* /*Sender*/, Action* parameters)
7313 {
7314 	core->GetGame()->BanterBlockTime = parameters->int0Parameter;
7315 }
7316 
SetNamelessDeath(Scriptable * Sender,Action * parameters)7317 void GameScript::SetNamelessDeath(Scriptable* Sender, Action* parameters)
7318 {
7319 	ieResRef area;
7320 
7321 	snprintf(area,8,"AR%04d", parameters->int0Parameter);
7322 	IniSpawn *sp = Sender->GetCurrentArea()->INISpawn;
7323 	if (!sp) {
7324 		return;
7325 	}
7326 	sp->SetNamelessDeath(area, parameters->pointParameter, parameters->int1Parameter);
7327 }
7328 
7329 // like GameScript::Kill, but forces chunking damage (disabling resurrection)
ChunkCreature(Scriptable * Sender,Action * parameters)7330 void GameScript::ChunkCreature(Scriptable *Sender, Action* parameters)
7331 {
7332 	Scriptable* tar = GetActorFromObject(Sender, parameters->objects[1]);
7333 	if (!tar || tar->Type != ST_ACTOR) {
7334 		return;
7335 	}
7336 
7337 	Actor *target = (Actor *) tar;
7338 	Effect *fx = EffectQueue::CreateEffect(fx_death_ref, 0, 8, FX_DURATION_INSTANT_PERMANENT);
7339 	target->fxqueue.AddEffect(fx, false);
7340 	delete fx;
7341 }
7342 
MultiPlayerSync(Scriptable * Sender,Action *)7343 void GameScript::MultiPlayerSync(Scriptable* Sender, Action* /*parameters*/)
7344 {
7345 	Sender->SetWait(1);
7346 }
7347 
DestroyAllFragileEquipment(Scriptable * Sender,Action * parameters)7348 void GameScript::DestroyAllFragileEquipment(Scriptable* Sender, Action* parameters)
7349 {
7350 	Scriptable* tar = GetActorFromObject(Sender, parameters->objects[1]);
7351 	if (!tar || tar->Type != ST_ACTOR) {
7352 		return;
7353 	}
7354 
7355 	// TODO: ensure it's using the inventory/CREItem flags, not Item — IE_ITEM_ADAMANTINE won't work as an input otherwise
7356 	Actor *actor = (Actor *) tar;
7357 	actor->inventory.DestroyItem("", parameters->int0Parameter, ~0);
7358 }
7359 
SetOriginalClass(Scriptable * Sender,Action * parameters)7360 void GameScript::SetOriginalClass(Scriptable* Sender, Action* parameters)
7361 {
7362 	Scriptable* tar = GetActorFromObject(Sender, parameters->objects[1]);
7363 	int classBit = parameters->int0Parameter & MC_WAS_ANY;
7364 	if (!tar || tar->Type != ST_ACTOR || !classBit) {
7365 		return;
7366 	}
7367 
7368 	Actor *actor = (Actor *) tar;
7369 	if (parameters->int1Parameter == OP_SET) {
7370 		// only reset the class bits
7371 		actor->SetMCFlag(MC_WAS_ANY, OP_NAND);
7372 		parameters->int1Parameter = OP_OR;
7373 	}
7374 	actor->SetMCFlag(classBit, parameters->int1Parameter);
7375 }
7376 
7377 }
7378