Example #1
0
bool
Script::_EvaluateTrigger(trigger_node* trig)
{
	Actor* actor = dynamic_cast<Actor*>(fTarget.Target());
	if (actor != NULL && actor->SkipConditions())
		return false;

#if DEBUG_SCRIPTS
	printf("SCRIPT: %s%s (%d 0x%x) ? ",
			trig->flags != 0 ? "!" : "",
			IDTable::TriggerAt(trig->id).c_str(),
			trig->id, trig->id);
	trig->Print();
#endif

	Core* core = Core::Get();
	bool returnValue = false;
	try {
		switch (trig->id) {
			case 0x0002:
			{
				/* 0x0002 AttackedBy(O:Object*,I:Style*AStyles)
				 * Returns true only if the active CRE was attacked in the style
				 * specified (not necessarily hit) or had an offensive spell cast
				 * on it by the specified object in the last script round.
				 * The style parameter is non functional - this trigger is triggered
				 * by any attack style.
				 */
				object_node* node = FindObjectNode(trig);
				if (node == NULL)
					break;
				node->Print();
				ScriptResults* results = fTarget.Target()->LastScriptRoundResults();
				if (results != NULL) {
					Object* object = Object::GetMatchingObjectFromList(
							results->Attackers(), node);

					returnValue = object != NULL;
				}
				break;
			}
			case 0x400A:
			{
				//ALIGNMENT(O:OBJECT*,I:ALIGNMENT*Align) (16395 0x400A)
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = object->IsAlignment(trig->parameter1);

				break;
			}
			case 0x400B:
			{
				//ALLEGIANCE(O:OBJECT*,I:ALLEGIENCE*EA) (16395 0x400b)
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = object->IsEnemyAlly(trig->parameter1);

				break;
			}
			case 0x400C:
			{
				/*0x400C Class(O:Object*,I:Class*Class)*/
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = object->IsClass(trig->parameter1);
				break;
			}
			case 0x400E:
			{
				/* GENERAL(O:OBJECT*,I:GENERAL*GENERAL) (16398 0x400e)*/
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = object->IsGeneral(trig->parameter1);
				break;
			}
			case 0x400F:
			{
				/*0x400F Global(S:Name*,S:Area*,I:Value*)
				Returns true only if the variable with name 1st parameter
				of type 2nd parameter has value 3rd parameter.*/
				std::string variableScope;
				variableScope.append(trig->string1, 6);
				std::string variableName;
				variableName.append(&trig->string1[6]);

				int32 variableValue = 0;
				if (variableScope.compare("LOCALS") == 0) {
					variableValue = fTarget.Target()->GetVariable(variableName.c_str());
				} else {
					// TODO: Check for AREA variables, currently we
					// treat AREA variables as global variables
					variableValue = Core::Get()->GetVariable(variableName.c_str());
				}
				returnValue = variableValue == trig->parameter1;
				break;
			}
			case 0x0020:
			{
				// HitBy
				// Returns true on first Script launch, IOW initial area load
				if (!Processed()) {
					returnValue = true;
					break;
				}
				//object_node* object = FindObjectNode(trig);
				//returnValue = Object::CheckIfNodeInList(object,
				//		fTarget->LastScriptRoundResults()->Hitters());
				break;
			}
			case 0x0022:
			{
				/* TimerExpired(I:ID*) */
				std::ostringstream stringStream;
				stringStream << fTarget.Target()->Name() << " " << trig->parameter1;
				printf("TimerExpired %s\n", stringStream.str().c_str());
				GameTimer* timer = GameTimer::Get(stringStream.str().c_str());
				if (timer != NULL && timer->Expired()) {
					returnValue = true;
				}
				break;
			}
			case 0x002F:
			{
				/* 0x002F Heard(O:Object*,I:ID*SHOUTIDS)
				Returns true only if the active CRE was within 30 feet
				of the specified object and the specified object shouted
				the specified number (which does not have to be in
				SHOUTIDS.ids) in the last script round.
				NB. If the object is specified as a death variable,
				the trigger will only return true if the corresponding
				object shouting also has an Enemy-Ally flag of NEUTRAL. */
				Object* object = FindObject(trig);
				if (object != NULL && core->Distance(fTarget.Target(), object) <= 30
						&& object->LastScriptRoundResults()->Shouted()
						== trig->parameter1) {
					returnValue = true;
				}
				break;
			}


			case 0x4017:
			{
				// Race()
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = object->IsRace(trig->parameter1);

				break;
			}

			case 0x4018:
			{
				/* 0x4018 Range(O:Object*,I:Range*)
				Returns true only if the specified object
				is within distance given (in feet) of the active CRE. */
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = core->Distance(object, fTarget.Target()) <= trig->parameter1;
				break;
			}

			case 0x401C:
			{
				/* See(O:Object*)
				 * Returns true only if the active CRE can see
				 * the specified object which must not be hidden or invisible.
				 */
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = core->See(fTarget.Target(), object);
				break;
			}
			case 0x401E:
			{
				/* Time(I:Time*Time)
				 * Returns true only if the period of day matches the period
				 * in the 2nd parameter (taken from Time.ids).
				 * Hours are offset by 30 minutes, e.g. Time(1) is true
				 * between 00:30 and 01:29.
				 */
				break;
			}

			case 0x4023:
				/* 0x4023 True() */
				returnValue = true;
				break;
			case 0x4027:
			{
				//DELAY(I:DELAY*) (16423 0x4027)
				// TODO: Implement
				returnValue = true;
				break;
			}
			case 0x402b:
			{
				/* ACTIONLISTEMPTY() (16427 0x402b)*/
				returnValue = fTarget.Target()->IsActionListEmpty();
				break;
			}
			case 0x4034:
			{
				/*0x4034 GlobalGT(S:Name*,S:Area*,I:Value*)
				See Global(S:Name*,S:Area*,I:Value*) except the variable
				must be greater than the value specified to be true.
				*/
				returnValue = core->GetVariable(trig->string1) > trig->parameter1;
				break;
			}
			case 0x4035:
			{	/*
				0x4035 GlobalLT(S:Name*,S:Area*,I:Value*)
				As above except for less than. */
				returnValue = core->GetVariable(trig->string1) < trig->parameter1;
				break;
			}
			case 0x0036:
			{
				/*0x0036 OnCreation()
				Returns true if the script is processed for the first time this session,
				e.g. when a creature is created (for CRE scripts) or when the player
				enters an area (for ARE scripts).*/
				returnValue = !Processed();
				break;
			}
			case 0x4037:
			{
				/* StateCheck(O:Object*,I:State*State)
				 * Returns true only if the specified object
				 * is in the state specified.
				 */
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = object->IsState(trig->parameter1);
				break;
			}
			case 0x4039:
			{
				/* NUMBEROFTIMESTALKEDTO(I:NUM*) (16441 0x4039) */
				if (actor->NumTimesTalkedTo() == (uint32)trig->parameter1)
					returnValue = true;
				break;
			}
			case 0x4040:
			{
				/* GlobalTimerExpired(S:Name*,S:Area*) (16448 0x4040) */
				// TODO: Merge Name and Area before passing them to the
				// Timer::Get() method (also below)
				std::string timerName;
				timerName.append(trig->string2).append(trig->string1);
				GameTimer* timer = GameTimer::Get(timerName.c_str());
				returnValue = timer != NULL && timer->Expired();
				break;
			}
			case 0x4041:
			{
				/* GlobalTimerNotExpired(S:Name*,S:Area*) */
				std::string timerName;
				timerName.append(trig->string2).append(trig->string1);
				GameTimer* timer = GameTimer::Get(timerName.c_str());
				returnValue = timer == NULL || !timer->Expired();
				break;
			}
			case 0x4043:
			{
				// InParty
				const Actor* actor = dynamic_cast<const Actor*>(FindObject(trig));
				if (actor != NULL)
					returnValue = Game::Get()->Party()->HasActor(actor);

				break;
			}
			case 0x4051:
			{
				/*
				 * 0x4051 Dead(S:Name*)
				 * Returns only true if the creature with the specified script name
				 * has its death variable set to 1. Not every form of death sets this,
				 * but most do. So it's an almost complete test for death.
				 * The creature must have existed for this to be true.
				 * Note that SPRITE_IS_DEAD variables are not set if the creature is
				 * killed by a neutral creature.
				 */
				/*Script* actorScript = fScripts[trig->string1];
				if (actorScript != NULL) {
					// TODO: More NULL checking
					const char* deathVariable = actorScript->Target()->CRE()->DeathVariable();
					returnValue = fVariables[deathVariable] == 1;
				}*/
				break;
			}
			case 0x52:
			{
				/* OPENED(O:OBJECT*) (82 0x52) */
				object_node* objectNode = FindObjectNode(trig);
				if (objectNode == NULL)
					break;
				// TODO: We assume this is a door, but also
				// containers can be opened/closed
				Door* door = dynamic_cast<Door*>(fTarget.Target());
				if (door == NULL)
					break;
				if (!door->Opened())
					break;

				Object* object = core->GetObject(
						door->CurrentScriptRoundResults()->fOpenedBy.c_str());
				if (object != NULL)
					returnValue = object->MatchNode(objectNode);

				break;
			}
			case 0x4063:
			{
				/*INWEAPONRANGE(O:OBJECT*) (16483 0x4063) */
				int range = 40;
				// TODO: Check weapon range
				Object* object = FindObject(trig);
				if (object != NULL)
					returnValue = core->Distance(object, fTarget.Target()) <= range;

				break;
			}

#if 0
			case 0x4068:
			{
				/* TimeGT(I:Time*Time)
				 * Returns true only if the current time is greater than
				 * that specified. Hours are offset by 30 minutes,
				 * e.g. TimeGT(1) is true between 01:30 and 02:29.
				 */
				break;
			}

			case 0x4069:
			{
				//TIMELT(I:TIME*TIME) (16489 0x4069)
				break;
			}
#endif
			case 0x0070:
			{
				/* 0x0070 Clicked(O:Object*)
				 *	Only for trigger regions.
				 *	Returns true if the specified object
				 *	clicked on the trigger region running this script.
				 */
				object_node* objectNode = FindObjectNode(trig);
				returnValue = fTarget.Target()->LastScriptRoundResults()->Clicker()
						->MatchNode(objectNode);
				//objectNode->Print();
				fTarget.Target()->LastScriptRoundResults()->Clicker()->Print();

				// TODO: When to set this, other than now ?
				if (returnValue)
					fLastTrigger = fTarget;
				break;
			}

			case 0x4074:
			{
				/*
				 * 0x4074 Detect(O:Object*)
				 * Returns true if the the specified object
				 * is detected by the active CRE in any way
				 * (hearing or sight). Neither Move Silently
				 * and Hide in Shadows prevent creatures
				 * being detected via Detect().
				 * Detect ignores Protection from Creature
				 * type effects for static objects.
				 */
				Object* object = core->GetObject(fTarget.Target(), FindObjectNode(trig));
				if (object != NULL)
					returnValue = core->See(fTarget.Target(), object);
					// || core->Hear(fTarget, object);
				break;
			}

			case 0x4076:
			{
				/*
				 * 0x4076 OpenState(O:Object*,I:Open*BOOLEAN)
				 * NT Returns true only if the open state of the specified door
				 * matches the state specified in the 2nd parameter.
				 */
				object_node* doorObj = FindObjectNode(trig);
				Door *door = dynamic_cast<Door*>(
								core->GetObject(doorObj->name));
				if (door != NULL) {
					bool openState = trig->parameter1 == 1;
					returnValue = door->Opened() == openState;
				}
				break;
			}
			case 0x407D:
			{
				/* ISOVERME(O:Object*)
				 * Only for trigger regions. Returns true only if the specified
				 * object is over the trigger running the script
				 */
				object_node* object = FindObjectNode(trig);
				if (object != NULL) {
					Object* objectOverRegion = core->GetObject(
							dynamic_cast<Region*>(fTarget.Target()));
					if (objectOverRegion != NULL)
						returnValue = objectOverRegion->MatchNode(object);
				}

				break;
			}
			case 0x407E:
			{
				/* 0x407E AreaCheck(S:ResRef*)
				 * Returns true only if the active CRE
				 * is in the area specified.
				 */
				// TODO: We only check the active area
				returnValue = !strcmp(RoomContainer::Get()->Name(), trig->string1);
				break;
			}
			case 0x4086:
			{
				// AREATYPE(I:NUMBER*AREATYPE) (16518 0x4086)

				// TODO: We only check the active area
				const uint16 areaType = RoomContainer::Get()->AREA()->Type();
				returnValue = areaType & trig->parameter1;
				break;
			}
			case 0x4089:
			{
				/*0x4089 OR(I:OrCount*)*/
				fOrTriggers = trig->parameter1;
				returnValue = true;
				break;
			}
			case 0x401b:
			{
				/* REPUTATIONLT(O:OBJECT*,I:REPUTATION*) (16411 0x401b) */
				Actor* actor = dynamic_cast<Actor*>(FindObject(trig));
				if (actor != NULL) {
					returnValue = actor->CRE()->Reputation() < trig->parameter1;
				}
				break;
			}

			case 0x4c:
			{
				// Entered(O:Object)
				object_node* node = FindObjectNode(trig);
				Region* region = dynamic_cast<Region*>(fTarget.Target());
				//std::vector<std::string>::const_iterator i;
				Object* object = Object::GetMatchingObjectFromList(
										region->
										LastScriptRoundResults()->
										EnteredActors(), node);
				if (object != NULL) {
					fLastTrigger = object;
					returnValue = true;
				}
				break;
			}
			default:
			{
				printf("SCRIPT: UNIMPLEMENTED TRIGGER!!!\n");
				printf("SCRIPT: %s (%d 0x%x)\n", IDTable::TriggerAt(trig->id).c_str(),
								trig->id, trig->id);
				trig->Print();
				break;
			}
		}
	} catch (...) {
		printf("SCRIPT: EvaluateTrigger() caught exception");
	}
	if (trig->flags != 0)
		returnValue = !returnValue;
#if DEBUG_SCRIPTS
	printf("SCRIPT: (%s)\n", returnValue ? "TRUE" : "FALSE");
#endif
	return returnValue;
}