/* =========================================================================== Return to Castle Wolfenstein single player GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). RTCW SP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW SP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW SP Source Code. If not, see . In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ //=========================================================================== // // Name: ai_cast_script_ents.c // Function: Wolfenstein AI Character Scripting // Programmer: Ridah // Tab Size: 4 (real tabs) //=========================================================================== #include "../game/g_local.h" #include "../game/q_shared.h" #include "../game/botlib.h" //bot lib interface #include "../game/be_aas.h" #include "../game/be_ea.h" #include "../game/be_ai_gen.h" #include "../game/be_ai_goal.h" #include "../game/be_ai_move.h" #include "../botai/botai.h" //bot ai interface #include "ai_cast.h" /*QUAKED ai_marker (1 0.5 0) (-18 -18 -24) (18 18 48) NODROP AI marker NODROP means dont drop it to the ground "targetname" : identifier for this marker */ /* ============ SP_ai_marker ============ */ extern vec3_t playerMins, playerMaxs; void SP_ai_marker( gentity_t *ent ) { vec3_t dest; trace_t tr; vec3_t checkMins, checkMaxs; if ( g_gametype.integer != GT_SINGLE_PLAYER ) { G_FreeEntity( ent ); return; } //----(SA) move the bounding box for the check in 1 unit on each side so they can butt up against a wall and not startsolid VectorCopy( playerMins, checkMins ); checkMins[0] += 1; checkMins[1] += 1; VectorCopy( playerMaxs, checkMaxs ); checkMaxs[0] -= 1; checkMaxs[1] -= 1; //----(SA) end if ( !( ent->spawnflags & 1 ) ) { // drop to floor ent->r.currentOrigin[2] += 1.0; // fixes QErad -> engine bug? VectorSet( dest, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2] - 4096 ); trap_Trace( &tr, ent->r.currentOrigin, checkMins, checkMaxs, dest, ent->s.number, MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP ); if ( tr.startsolid ) { G_Printf( "WARNING: ai_marker (%s) in solid at %s\n", ent->targetname, vtos( ent->r.currentOrigin ) ); return; } G_SetOrigin( ent, tr.endpos ); } } /*QUAKED ai_effect (0.3 0.8 0.2) (-4 -4 -4) (4 4 4) AI effect entity "ainame" is the name of the AI character that uses this entity for effects */ /* ============ SP_ai_effect ============ */ void ai_effect_think( gentity_t *ent ) { gentity_t *targ; // find the client number that uses this entity targ = AICast_FindEntityForName( ent->aiName ); if ( !targ ) { // keep waiting until they enter, if they never do, then we have no purpose, therefore no harm can be done ent->think = ai_effect_think; ent->nextthink = level.time + 200; return; //G_Error( "ai_effect with invalid aiName at %s\n", vtos(ent->s.origin) ); } // make sure the clients can use this association ent->s.otherEntityNum = targ->s.number; ent->s.eType = ET_AI_EFFECT; G_SetOrigin( ent, ent->s.origin ); trap_LinkEntity( ent ); ent->r.svFlags |= SVF_BROADCAST; // make sure all clients are aware of this entity } void SP_ai_effect( gentity_t *ent ) { if ( g_gametype.integer != GT_SINGLE_PLAYER ) { G_FreeEntity( ent ); return; } ent->think = ai_effect_think; ent->nextthink = level.time + 500; } //=========================================================== // the wait time has passed, so set back up for another activation void AICast_trigger_wait( gentity_t *ent ) { ent->nextthink = 0; } // the trigger was just activated // ent->activator should be set to the activator so it can be held through a delay // so wait for the delay time before firing void AICast_trigger_trigger( gentity_t *ent, gentity_t *activator ) { if ( ent->nextthink ) { return; // can't retrigger until the wait is over } ent->activator = AICast_FindEntityForName( ent->aiName ); if ( ent->activator ) { // they might be dead // trigger the script event AICast_ScriptEvent( AICast_GetCastState( ent->activator->s.number ), "trigger", ent->target ); } if ( ent->wait > 0 ) { ent->think = AICast_trigger_wait; ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; } else { // we can't just remove (self) here, because this is a touch function // called while looping through area links... ent->touch = 0; ent->nextthink = level.time + FRAMETIME; ent->think = G_FreeEntity; } } void AICast_Touch_Trigger( gentity_t *self, gentity_t *other, trace_t *trace ) { if ( !other->client || ( other->r.svFlags & SVF_CASTAI ) ) { return; } AICast_trigger_trigger( self, other ); } /*QUAKED ai_trigger (1 0.5 0) ? Startoff Triggered only by the player touching it "wait" : Seconds between triggerings, -1 = one time only (default). "ainame" : name of AI to target (use "player" for the.. player) "target" : trigger identifier for that AI script */ extern void InitTrigger( gentity_t *self ); void ai_trigger_activate( gentity_t *self ) { if ( self->r.linked ) { return; } self->use = NULL; self->AIScript_AlertEntity = NULL; self->touch = AICast_Touch_Trigger; InitTrigger( self ); trap_LinkEntity( self ); } void ai_trigger_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { ai_trigger_activate( self ); } void SP_ai_trigger( gentity_t *ent ) { if ( g_gametype.integer != GT_SINGLE_PLAYER ) { G_FreeEntity( ent ); return; } G_SpawnFloat( "wait", "-1", &ent->wait ); if ( !ent->aiName ) { G_Error( "ai_trigger without \"ainame\"\n" ); } if ( !ent->target ) { G_Error( "ai_trigger without \"target\"\n" ); } if ( ent->spawnflags & 1 ) { // TriggerSpawn ent->AIScript_AlertEntity = ai_trigger_activate; ent->use = ai_trigger_use; trap_UnlinkEntity( ent ); } else { ai_trigger_activate( ent ); } }