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