1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 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.
23 
24 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.
25 
26 ===========================================================================
27 */
28 
29 //===========================================================================
30 //
31 // Name:			ai_cast_script_ents.c
32 // Function:		Wolfenstein AI Character Scripting
33 // Programmer:		Ridah
34 // Tab Size:		4 (real tabs)
35 //===========================================================================
36 
37 #include "g_local.h"
38 #include "../qcommon/q_shared.h"
39 #include "../botlib/botlib.h"      //bot lib interface
40 #include "../botlib/be_aas.h"
41 #include "../botlib/be_ea.h"
42 #include "../botlib/be_ai_gen.h"
43 #include "../botlib/be_ai_goal.h"
44 #include "../botlib/be_ai_move.h"
45 #include "../botlib/botai.h"          //bot ai interface
46 
47 #include "ai_cast.h"
48 
49 /*QUAKED ai_marker (1 0.5 0) (-18 -18 -24) (18 18 48) NODROP
50 AI marker
51 
52 NODROP means dont drop it to the ground
53 
54 "targetname" : identifier for this marker
55 */
56 
57 /*
58 ============
59 SP_ai_marker
60 ============
61 */
62 extern vec3_t playerMins, playerMaxs;
SP_ai_marker(gentity_t * ent)63 void SP_ai_marker( gentity_t *ent ) {
64 	vec3_t dest;
65 	trace_t tr;
66 	vec3_t checkMins, checkMaxs;
67 
68 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
69 		G_FreeEntity( ent );
70 		return;
71 	}
72 
73 //----(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
74 	VectorCopy( playerMins, checkMins );
75 	checkMins[0] += 1;
76 	checkMins[1] += 1;
77 	VectorCopy( playerMaxs, checkMaxs );
78 	checkMaxs[0] -= 1;
79 	checkMaxs[1] -= 1;
80 //----(SA)	end
81 
82 	if ( !( ent->spawnflags & 1 ) ) {
83 		// drop to floor
84 		ent->r.currentOrigin[2] += 1.0; // fixes QErad -> engine bug?
85 		VectorSet( dest, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2] - 4096 );
86 		trap_Trace( &tr, ent->r.currentOrigin, checkMins, checkMaxs, dest, ent->s.number, MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP );
87 
88 		if ( tr.startsolid ) {
89 			G_Printf( "WARNING: ai_marker (%s) in solid at %s\n", ent->targetname, vtos( ent->r.currentOrigin ) );
90 			return;
91 		}
92 
93 		G_SetOrigin( ent, tr.endpos );
94 	}
95 }
96 
97 /*QUAKED ai_effect (0.3 0.8 0.2) (-4 -4 -4) (4 4 4)
98 AI effect entity
99 
100 "ainame" is the name of the AI character that uses this entity for effects
101 */
102 
103 /*
104 ============
105 SP_ai_effect
106 ============
107 */
ai_effect_think(gentity_t * ent)108 void ai_effect_think( gentity_t *ent ) {
109 	gentity_t *targ;
110 
111 	// find the client number that uses this entity
112 	targ = AICast_FindEntityForName( ent->aiName );
113 	if ( !targ ) {
114 		// keep waiting until they enter, if they never do, then we have no purpose, therefore no harm can be done
115 		ent->think = ai_effect_think;
116 		ent->nextthink = level.time + 200;
117 		return;
118 		//G_Error( "ai_effect with invalid aiName at %s\n", vtos(ent->s.origin) );
119 	}
120 
121 	// make sure the clients can use this association
122 	ent->s.otherEntityNum = targ->s.number;
123 
124 	ent->s.eType = ET_AI_EFFECT;
125 	G_SetOrigin( ent, ent->s.origin );
126 	trap_LinkEntity( ent );
127 	ent->r.svFlags |= SVF_BROADCAST;    // make sure all clients are aware of this entity
128 }
129 
SP_ai_effect(gentity_t * ent)130 void SP_ai_effect( gentity_t *ent ) {
131 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
132 		G_FreeEntity( ent );
133 		return;
134 	}
135 
136 	ent->think = ai_effect_think;
137 	ent->nextthink = level.time + 500;
138 }
139 
140 //===========================================================
141 
142 // the wait time has passed, so set back up for another activation
AICast_trigger_wait(gentity_t * ent)143 void AICast_trigger_wait( gentity_t *ent ) {
144 	ent->nextthink = 0;
145 }
146 
147 
148 // the trigger was just activated
149 // ent->activator should be set to the activator so it can be held through a delay
150 // so wait for the delay time before firing
AICast_trigger_trigger(gentity_t * ent,gentity_t * activator)151 void AICast_trigger_trigger( gentity_t *ent, gentity_t *activator ) {
152 	if ( ent->nextthink ) {
153 		return;     // can't retrigger until the wait is over
154 	}
155 
156 	ent->activator = AICast_FindEntityForName( ent->aiName );
157 	if ( ent->activator ) { // they might be dead
158 		// trigger the script event
159 		AICast_ScriptEvent( AICast_GetCastState( ent->activator->s.number ), "trigger", ent->target );
160 	}
161 
162 	if ( ent->wait > 0 ) {
163 		ent->think = AICast_trigger_wait;
164 		ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
165 	} else {
166 		// we can't just remove (self) here, because this is a touch function
167 		// called while looping through area links...
168 		ent->touch = 0;
169 		ent->nextthink = level.time + FRAMETIME;
170 		ent->think = G_FreeEntity;
171 	}
172 }
173 
AICast_Touch_Trigger(gentity_t * self,gentity_t * other,trace_t * trace)174 void AICast_Touch_Trigger( gentity_t *self, gentity_t *other, trace_t *trace ) {
175 	if ( !other->client || ( other->r.svFlags & SVF_CASTAI ) ) {
176 		return;
177 	}
178 	AICast_trigger_trigger( self, other );
179 }
180 
181 /*QUAKED ai_trigger (1 0.5 0) ? Startoff
182 Triggered only by the player touching it
183 "wait" : Seconds between triggerings, -1 = one time only (default).
184 "ainame" : name of AI to target (use "player" for the.. player)
185 "target" : trigger identifier for that AI script
186 */
187 extern void InitTrigger( gentity_t *self );
188 
ai_trigger_activate(gentity_t * self)189 void ai_trigger_activate( gentity_t *self ) {
190 	if ( self->r.linked ) {
191 		return;
192 	}
193 
194 	self->use = 0;
195 	self->AIScript_AlertEntity = 0;
196 
197 	self->touch = AICast_Touch_Trigger;
198 
199 	InitTrigger( self );
200 	trap_LinkEntity( self );
201 }
202 
ai_trigger_use(gentity_t * self,gentity_t * other,gentity_t * activator)203 void ai_trigger_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
204 	ai_trigger_activate( self );
205 }
206 
SP_ai_trigger(gentity_t * ent)207 void SP_ai_trigger( gentity_t *ent ) {
208 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
209 		G_FreeEntity( ent );
210 		return;
211 	}
212 
213 	G_SpawnFloat( "wait", "-1", &ent->wait );
214 
215 	if ( !ent->aiName ) {
216 		G_Error( "ai_trigger without \"ainame\"\n" );
217 	}
218 	if ( !ent->target ) {
219 		G_Error( "ai_trigger without \"target\"\n" );
220 	}
221 
222 	if ( ent->spawnflags & 1 ) { // TriggerSpawn
223 		ent->AIScript_AlertEntity = ai_trigger_activate;
224 		ent->use = ai_trigger_use;
225 		trap_UnlinkEntity( ent );
226 	} else {
227 		ai_trigger_activate( ent );
228 	}
229 }
230