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_funcs.c
32 // Function:		Wolfenstein AI Character Decision Making
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 //=================================================================================
50 //
51 // Helga, the first boss
52 //
53 //=================================================================================
54 
55 #define HELGA_SPIRIT_BUILDUP_TIME       8000    // last for this long
56 #define HELGA_SPIRIT_FADEOUT_TIME       1000
57 #define HELGA_SPIRIT_DLIGHT_RADIUS_MAX  384
58 #define HELGA_SPIRIT_FIRE_INTERVAL      1000
59 
60 extern int lastZombieSpiritAttack;
61 
AIFunc_Helga_SpiritAttack(cast_state_t * cs)62 char *AIFunc_Helga_SpiritAttack( cast_state_t *cs ) {
63 	gentity_t *ent;
64 	//
65 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
66 	ent = &g_entities[cs->entityNum];
67 	// make sure we're still playing the right anim
68 	if ( ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack1", cs->entityNum ) ) {
69 		return AIFunc_DefaultStart( cs );
70 	}
71 	//
72 	if ( cs->enemyNum < 0 ) {
73 		ent->client->ps.torsoTimer  = 0;
74 		ent->client->ps.legsTimer   = 0;
75 		return AIFunc_DefaultStart( cs );
76 	}
77 	//
78 	// if we can't see them anymore, abort immediately
79 	if ( cs->vislist[cs->enemyNum].real_visible_timestamp != cs->vislist[cs->enemyNum].real_update_timestamp ) {
80 		ent->client->ps.torsoTimer  = 0;
81 		ent->client->ps.legsTimer   = 0;
82 		return AIFunc_DefaultStart( cs );
83 	}
84 	// we are firing this weapon, so record it
85 	cs->weaponFireTimes[WP_MONSTER_ATTACK2] = level.time;
86 	//
87 	// once an attack has started, only abort once the player leaves our view, or time runs out
88 	if ( cs->thinkFuncChangeTime < level.time - HELGA_SPIRIT_BUILDUP_TIME ) {
89 		// if enough time has elapsed, finish this attack
90 		if ( level.time > cs->thinkFuncChangeTime + HELGA_SPIRIT_BUILDUP_TIME + HELGA_SPIRIT_FADEOUT_TIME ) {
91 			ent->client->ps.torsoTimer  = 0;
92 			ent->client->ps.legsTimer   = 0;
93 			return AIFunc_DefaultStart( cs );
94 		}
95 	} else {
96 
97 		// set timers
98 		ent->client->ps.torsoTimer  = 1000;
99 		ent->client->ps.legsTimer   = 1000;
100 
101 		// draw the client-side effect
102 		ent->client->ps.eFlags |= EF_MONSTER_EFFECT;
103 
104 		// inform the client of our enemies position
105 		VectorCopy( g_entities[cs->enemyNum].client->ps.origin, ent->s.origin2 );
106 		ent->s.origin2[2] += g_entities[cs->enemyNum].client->ps.viewheight;
107 	}
108 	//
109 	//
110 	return NULL;
111 }
112 
AIFunc_Helga_SpiritAttack_Start(cast_state_t * cs)113 char *AIFunc_Helga_SpiritAttack_Start( cast_state_t *cs ) {
114 	gentity_t *ent;
115 	//
116 	ent = &g_entities[cs->entityNum];
117 	ent->s.otherEntityNum2 = cs->enemyNum;
118 	ent->s.effect1Time = level.time;
119 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
120 	//
121 	// dont turn
122 	cs->ideal_viewangles[YAW] = cs->viewangles[YAW];
123 	// play an anim
124 	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_WEAPON, WP_MONSTER_ATTACK2, qtrue );
125 	BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
126 	//
127 	cs->aifunc = AIFunc_Helga_SpiritAttack;
128 	return "AIFunc_Helga_SpiritAttack";
129 }
130 
131 //=================================================================================
132 //
133 // Standing melee attacks
134 //
135 //=================================================================================
136 
137 #define NUM_HELGA_ANIMS     3
138 #define MAX_HELGA_IMPACTS   3
139 int helgaHitTimes[NUM_HELGA_ANIMS][MAX_HELGA_IMPACTS] = {   // up to three hits per attack
140 	{ANIMLENGTH( 16,20 ),-1},
141 	{ANIMLENGTH( 11,20 ),ANIMLENGTH( 19,20 ),-1},
142 	{ANIMLENGTH( 10,20 ),ANIMLENGTH( 17,20 ),ANIMLENGTH( 26,20 )},
143 };
144 int helgaHitDamage[NUM_HELGA_ANIMS] = {
145 	20,
146 	14,
147 	12
148 };
149 
150 /*
151 ================
152 AIFunc_Helga_Melee
153 ================
154 */
AIFunc_Helga_Melee(cast_state_t * cs)155 char *AIFunc_Helga_Melee( cast_state_t *cs ) {
156 	gentity_t *ent = &g_entities[cs->entityNum];
157 	gentity_t *enemy;
158 	cast_state_t *ecs;
159 	int hitDelay = -1, anim;
160 	trace_t tr;
161 	float enemyDist;
162 	aicast_predictmove_t move;
163 	vec3_t vec;
164 
165 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
166 
167 	if ( !ent->client->ps.torsoTimer || !ent->client->ps.legsTimer ) {
168 		cs->aiFlags &= ~AIFL_SPECIAL_FUNC;
169 		return AIFunc_DefaultStart( cs );
170 	}
171 
172 	if ( cs->enemyNum < 0 ) {
173 		ent->client->ps.legsTimer = 0;      // allow legs us to move
174 		ent->client->ps.torsoTimer = 0;     // allow legs us to move
175 		cs->aiFlags &= ~AIFL_SPECIAL_FUNC;
176 		return AIFunc_DefaultStart( cs );
177 	}
178 
179 	ecs = AICast_GetCastState( cs->enemyNum );
180 	enemy = &g_entities[cs->enemyNum];
181 
182 	anim = ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack3", cs->entityNum );
183 	if ( anim < 0 || anim >= NUM_HELGA_ANIMS ) {
184 		// animation interupted
185 		cs->aiFlags &= ~AIFL_SPECIAL_FUNC;
186 		return AIFunc_DefaultStart( cs );
187 		//G_Error( "AIFunc_HelgaZombieMelee: helgaBoss using invalid or unknown attack anim" );
188 	}
189 	if ( cs->animHitCount < MAX_HELGA_IMPACTS && helgaHitTimes[anim][cs->animHitCount] >= 0 ) {
190 
191 		// face them
192 		VectorCopy( cs->bs->origin, vec );
193 		vec[2] += ent->client->ps.viewheight;
194 		VectorSubtract( enemy->client->ps.origin, vec, vec );
195 		VectorNormalize( vec );
196 		vectoangles( vec, cs->ideal_viewangles );
197 		cs->ideal_viewangles[PITCH] = AngleNormalize180( cs->ideal_viewangles[PITCH] );
198 
199 		// get hitDelay
200 		if ( !cs->animHitCount ) {
201 			hitDelay = helgaHitTimes[anim][cs->animHitCount];
202 		} else {
203 			hitDelay = helgaHitTimes[anim][cs->animHitCount] - helgaHitTimes[anim][cs->animHitCount - 1];
204 		}
205 
206 		// check for inflicting damage
207 		if ( level.time - cs->weaponFireTimes[cs->weaponNum] > hitDelay ) {
208 			// do melee damage
209 			enemyDist = VectorDistance( enemy->r.currentOrigin, ent->r.currentOrigin );
210 			enemyDist -= g_entities[cs->enemyNum].r.maxs[0];
211 			enemyDist -= ent->r.maxs[0];
212 			if ( enemyDist < 10 + AICast_WeaponRange( cs, cs->weaponNum ) ) {
213 				trap_Trace( &tr, ent->r.currentOrigin, NULL, NULL, enemy->r.currentOrigin, ent->s.number, MASK_SHOT );
214 				if ( tr.entityNum == cs->enemyNum ) {
215 					G_Damage( &g_entities[tr.entityNum], ent, ent, vec3_origin, tr.endpos,
216 							  helgaHitDamage[anim], 0, MOD_GAUNTLET );
217 					G_AddEvent( enemy, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[STAYSOUNDSCRIPT] ) );
218 				}
219 			}
220 			cs->weaponFireTimes[cs->weaponNum] = level.time;
221 			cs->animHitCount++;
222 		}
223 	}
224 
225 	// if they are outside range, move forward
226 	AICast_PredictMovement( ecs, 2, 0.3, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 );
227 	VectorSubtract( move.endpos, cs->bs->origin, vec );
228 	vec[2] = 0;
229 	enemyDist = VectorLength( vec );
230 	enemyDist -= g_entities[cs->enemyNum].r.maxs[0];
231 	enemyDist -= ent->r.maxs[0];
232 	if ( enemyDist > 8 ) {    // we can get closer
233 		//if (!ent->client->ps.legsTimer) {
234 		//	cs->castScriptStatus.scriptNoMoveTime = 0;
235 		trap_EA_MoveForward( cs->entityNum );
236 		//}
237 		//ent->client->ps.legsTimer = 0;		// allow legs us to move
238 	}
239 
240 	return NULL;
241 }
242 
243 /*
244 ================
245 AIFunc_Helga_MeleeStart
246 ================
247 */
AIFunc_Helga_MeleeStart(cast_state_t * cs)248 char *AIFunc_Helga_MeleeStart( cast_state_t *cs ) {
249 	gentity_t *ent;
250 
251 	ent = &g_entities[cs->entityNum];
252 	ent->s.effect1Time = level.time;
253 	cs->ideal_viewangles[YAW] = cs->viewangles[YAW];
254 	cs->weaponFireTimes[cs->weaponNum] = level.time;
255 	cs->animHitCount = 0;
256 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
257 
258 	// face them
259 	AICast_AimAtEnemy( cs );
260 
261 	// play an anim
262 	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_WEAPON, cs->weaponNum, qtrue );
263 	BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
264 
265 	// play a sound
266 	G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[ATTACKSOUNDSCRIPT] ) );
267 
268 	cs->aifunc = AIFunc_Helga_Melee;
269 	cs->aifunc( cs );   // think once now, to prevent a delay
270 	return "AIFunc_Helga_Melee";
271 }
272 
273 
274 //===================================================================
275 
276 /*
277 ==============
278 AIFunc_FlameZombie_Portal
279 ==============
280 */
AIFunc_FlameZombie_Portal(cast_state_t * cs)281 char *AIFunc_FlameZombie_Portal( cast_state_t *cs ) {
282 	gentity_t *ent = &g_entities[cs->entityNum];
283 	//
284 	if ( cs->thinkFuncChangeTime < level.time - PORTAL_ZOMBIE_SPAWNTIME ) {
285 		// HACK, make them aware of the player
286 		AICast_UpdateVisibility( &g_entities[cs->entityNum], AICast_FindEntityForName( "player" ), qfalse, qtrue );
287 		ent->s.time2 = 0;   // turn spawning effect off
288 		return AIFunc_DefaultStart( cs );
289 	}
290 	//
291 	return NULL;
292 }
293 
294 /*
295 ==============
296 AIFunc_FlameZombie_PortalStart
297 ==============
298 */
AIFunc_FlameZombie_PortalStart(cast_state_t * cs)299 char *AIFunc_FlameZombie_PortalStart( cast_state_t *cs ) {
300 	gentity_t *ent = &g_entities[cs->entityNum];
301 	//
302 	ent->s.time2 = level.time + 200;    // hijacking this for portal spawning effect
303 	//
304 	// play a special animation
305 	ent->client->ps.torsoAnim =
306 		( ( ent->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | BOTH_EXTRA1;
307 	ent->client->ps.legsAnim =
308 		( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | BOTH_EXTRA1;
309 	ent->client->ps.torsoTimer = PORTAL_ZOMBIE_SPAWNTIME - 200;
310 	ent->client->ps.legsTimer = PORTAL_ZOMBIE_SPAWNTIME - 200;
311 	//
312 	cs->thinkFuncChangeTime = level.time;
313 	//
314 	cs->aifunc = AIFunc_FlameZombie_Portal;
315 	return "AIFunc_FlameZombie_Portal";
316 }
317 
318 
319 //=================================================================================
320 //
321 // Heinrich, the LAST boss
322 //
323 //=================================================================================
324 
325 //
326 // Special Sound Precache
327 //
328 
329 typedef enum
330 {
331 	HEINRICH_SWORDIMPACT,
332 	HEINRICH_SWORDLUNGE_START,
333 	HEINRICH_SWORDKNOCKBACK_START,
334 	HEINRICH_SWORDKNOCKBACK_WEAPON,
335 	HEINRICH_SWORDSIDESLASH_START,
336 	HEINRICH_SWORDSIDESLASH_WEAPON,
337 	HEINRICH_EARTHQUAKE_START,
338 	HEINRICH_RAISEDEAD_START,
339 	HEINRICH_TAUNT_GOODHEALTH,
340 	HEINRICH_TAUNT_LOWHEALTH,
341 
342 	MAX_HEINRICH_SOUNDS
343 } heinrichSounds_t;
344 
345 char *heinrichSounds[MAX_HEINRICH_SOUNDS] = {
346 	"heinrichSwordImpact",
347 	"heinrichSwordLungeStart",
348 	"heinrichSwordKnockbackStart",
349 	"heinrichSwordKnockbackWeapon",
350 	"heinrichSwordSideSlashStart",
351 	"heinrichSwordSideSlashWeapon",
352 	"heinrichSwordEarthquakeStart",
353 	"heinrichRaiseWarriorStart",
354 	"heinrichTauntGoodHealth",
355 	"heinrichTauntLowHealth",
356 };
357 
358 int heinrichSoundIndex[MAX_HEINRICH_SOUNDS];
359 
AICast_Heinrich_SoundPrecache(void)360 void AICast_Heinrich_SoundPrecache( void ) {
361 	int i;
362 	for ( i = 0; i < MAX_HEINRICH_SOUNDS; i++ ) {
363 		heinrichSoundIndex[i] = G_SoundIndex( heinrichSounds[i] );
364 	}
365 }
366 
AICast_Heinrich_Taunt(cast_state_t * cs)367 void AICast_Heinrich_Taunt( cast_state_t *cs ) {
368 	gentity_t *ent = &g_entities[cs->entityNum];
369 	static int lastTaunt;
370 	// sound
371 	if ( ent->health > cs->attributes[STARTING_HEALTH] * 0.25 ) {
372 		if ( lastTaunt > level.time || lastTaunt < level.time - 20000 ) {
373 			G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_TAUNT_GOODHEALTH] );
374 			lastTaunt = level.time;
375 		}
376 	} else {
377 		if ( lastTaunt > level.time || lastTaunt < level.time - 40000 ) {
378 			G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_TAUNT_LOWHEALTH] );
379 			lastTaunt = level.time;
380 		}
381 	}
382 }
383 
384 #define HEINRICH_LUNGE_DELAY    ANIMLENGTH( 15,20 )
385 #define HEINRICH_LUNGE_RANGE    170
386 #define HEINRICH_LUNGE_DAMAGE   ( 50 + rand() % 20 )
387 
AIFunc_Heinrich_SwordLunge(cast_state_t * cs)388 char *AIFunc_Heinrich_SwordLunge( cast_state_t *cs ) {
389 	gentity_t *ent = &g_entities[cs->entityNum];
390 	trace_t *tr;
391 	vec3_t left;
392 	float enemyDist;
393 	aicast_predictmove_t move;
394 	vec3_t vec;
395 	cast_state_t *ecs;
396 
397 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
398 
399 	if ( cs->enemyNum < 0 ) {
400 		if ( ent->client->ps.torsoTimer ) {
401 			return NULL;
402 		}
403 		return AIFunc_DefaultStart( cs );
404 	}
405 
406 	ecs = AICast_GetCastState( cs->enemyNum );
407 
408 
409 	if ( ent->client->ps.torsoTimer < 500 ) {
410 		if ( !ent->client->ps.legsTimer ) {
411 			trap_EA_MoveForward( cs->entityNum );
412 		}
413 		ent->client->ps.legsTimer = 0;
414 		ent->client->ps.torsoTimer = 0;
415 		cs->castScriptStatus.scriptNoMoveTime = 0;
416 		AICast_Heinrich_Taunt( cs );
417 		return AIFunc_BattleChaseStart( cs );
418 	}
419 
420 	// time for the melee?
421 	if ( cs->enemyNum >= 0 && !( cs->aiFlags & AIFL_MISCFLAG1 ) ) {
422 		// face them
423 		AICast_AimAtEnemy( cs );
424 		// keep checking for impact status
425 		tr = CheckMeleeAttack( ent, HEINRICH_LUNGE_RANGE, qfalse );
426 /*		// do we need to move?
427 		if (!(tr && (tr->entityNum == cs->enemyNum))) {
428 			ent->client->ps.legsTimer = 0;
429 			cs->castScriptStatus.scriptNoMoveTime = 0;
430 			trap_EA_MoveForward( cs->entityNum );
431 		}
432 */                                                                                                                                                                                                           // ready for damage?
433 		if ( cs->thinkFuncChangeTime < level.time - HEINRICH_LUNGE_DELAY ) {
434 			cs->aiFlags |= AIFL_MISCFLAG1;
435 			// do melee damage
436 			if ( tr && ( tr->entityNum == cs->enemyNum ) ) {
437 				G_Damage( &g_entities[tr->entityNum], ent, ent, left, tr->endpos, HEINRICH_LUNGE_DAMAGE, 0, MOD_GAUNTLET );
438 				// sound
439 				G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDIMPACT] );
440 			}
441 		}
442 	}
443 
444 	// if they are outside range, move forward
445 	AICast_PredictMovement( ecs, 2, 0.3, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 );
446 	VectorSubtract( move.endpos, cs->bs->origin, vec );
447 	vec[2] = 0;
448 	enemyDist = VectorLength( vec );
449 	enemyDist -= g_entities[cs->enemyNum].r.maxs[0];
450 	enemyDist -= ent->r.maxs[0];
451 	if ( enemyDist > 30 ) {   // we can get closer
452 		if ( ent->client->ps.legsTimer ) {
453 			cs->castScriptStatus.scriptNoMoveTime = level.time + 100;
454 			ent->client->ps.legsTimer = 0;      // allow legs to move us
455 		}
456 		if ( cs->castScriptStatus.scriptNoMoveTime < level.time ) {
457 			trap_EA_MoveForward( cs->entityNum );
458 		}
459 	}
460 
461 	return NULL;
462 }
463 
AIFunc_Heinrich_SwordLungeStart(cast_state_t * cs)464 char *AIFunc_Heinrich_SwordLungeStart( cast_state_t *cs ) {
465 	gentity_t *ent = &g_entities[cs->entityNum];
466 //	gentity_t	*enemy = &g_entities[cs->enemyNum];
467 
468 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
469 	// sound
470 	G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDLUNGE_START] );
471 	// face them
472 	AICast_AimAtEnemy( cs );
473 	// clear flags
474 	cs->aiFlags &= ~( AIFL_MISCFLAG1 | AIFL_MISCFLAG2 );
475 	// play the anim
476 	BG_PlayAnimName( &ent->client->ps, "attack9", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
477 	// start the func
478 	cs->aifunc = AIFunc_Heinrich_SwordLunge;
479 	return "AIFunc_Heinrich_SwordLunge";
480 }
481 
482 #define HEINRICH_KNOCKBACK_DELAY    ANIMLENGTH( 26,20 )
483 #define HEINRICH_KNOCKBACK_RANGE    150
484 #define HEINRICH_KNOCKBACK_DAMAGE   ( 60 + rand() % 20 )
485 
AIFunc_Heinrich_SwordKnockback(cast_state_t * cs)486 char *AIFunc_Heinrich_SwordKnockback( cast_state_t *cs ) {
487 	gentity_t *ent = &g_entities[cs->entityNum];
488 	trace_t *tr;
489 	vec3_t right, left;
490 
491 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
492 
493 	if ( cs->enemyNum < 0 ) {
494 		if ( ent->client->ps.torsoTimer ) {
495 			return NULL;
496 		}
497 		return AIFunc_DefaultStart( cs );
498 	}
499 
500 	AICast_GetCastState( cs->enemyNum );
501 
502 	if ( ent->client->ps.torsoTimer < 500 ) {
503 		if ( !ent->client->ps.legsTimer ) {
504 			trap_EA_MoveForward( cs->entityNum );
505 		}
506 		ent->client->ps.legsTimer = 0;
507 		ent->client->ps.torsoTimer = 0;
508 		cs->castScriptStatus.scriptNoMoveTime = 0;
509 		AICast_Heinrich_Taunt( cs );
510 		return AIFunc_BattleChaseStart( cs );
511 	}
512 
513 	// time for the melee?
514 	if ( cs->enemyNum >= 0 && !( cs->aiFlags & AIFL_MISCFLAG1 ) ) {
515 		// face them
516 		AICast_AimAtEnemy( cs );
517 		// keep checking for impact status
518 		tr = CheckMeleeAttack( ent, HEINRICH_KNOCKBACK_RANGE, qfalse );
519 /*		// do we need to move?
520 		if (!(tr && (tr->entityNum == cs->enemyNum))) {
521 			ent->client->ps.legsTimer = 0;
522 			cs->castScriptStatus.scriptNoMoveTime = 0;
523 			trap_EA_MoveForward( cs->entityNum );
524 		}
525 */                                                                                                                                                                                                           // ready for damage?
526 		if ( cs->thinkFuncChangeTime < level.time - HEINRICH_KNOCKBACK_DELAY ) {
527 			cs->aiFlags |= AIFL_MISCFLAG1;
528 			// do melee damage
529 			if ( tr && ( tr->entityNum == cs->enemyNum ) ) {
530 				AngleVectors( cs->viewangles, NULL, right, NULL );
531 				VectorNegate( right, left );
532 				G_Damage( &g_entities[tr->entityNum], ent, ent, left, tr->endpos, HEINRICH_KNOCKBACK_DAMAGE, 0, MOD_GAUNTLET );
533 				// sound
534 				G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDIMPACT] );
535 				// throw them in direction of impact
536 				if ( ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) == BG_AnimationIndexForString( "attack2", cs->entityNum ) ) {
537 					// right
538 					right[2] = 0.5;
539 					VectorMA( g_entities[cs->enemyNum].client->ps.velocity, 400, right, g_entities[cs->enemyNum].client->ps.velocity );
540 				} else {
541 					// left
542 					left[2] = 0.5;
543 					VectorMA( g_entities[cs->enemyNum].client->ps.velocity, 400, left, g_entities[cs->enemyNum].client->ps.velocity );
544 				}
545 			}
546 		}
547 	}
548 	return NULL;
549 }
550 
AIFunc_Heinrich_SwordKnockbackStart(cast_state_t * cs)551 char *AIFunc_Heinrich_SwordKnockbackStart( cast_state_t *cs ) {
552 	gentity_t *ent = &g_entities[cs->entityNum];
553 //	gentity_t	*enemy = &g_entities[cs->enemyNum];
554 
555 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
556 	// sound
557 	G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDKNOCKBACK_START] );
558 	// weapon sound
559 	G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDKNOCKBACK_WEAPON] );
560 	// face them
561 	AICast_AimAtEnemy( cs );
562 	// clear flags
563 	cs->aiFlags &= ~( AIFL_MISCFLAG1 | AIFL_MISCFLAG2 );
564 	// play the anim
565 	if ( rand() % 2 ) {
566 		BG_PlayAnimName( &ent->client->ps, "attack2", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
567 	} else {
568 		BG_PlayAnimName( &ent->client->ps, "attack3", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
569 	}
570 	// start the func
571 	cs->aifunc = AIFunc_Heinrich_SwordKnockback;
572 	return "AIFunc_Heinrich_SwordKnockback";
573 }
574 
575 #define HEINRICH_SLASH_DELAY    ANIMLENGTH( 17,25 )
576 #define HEINRICH_SLASH_RANGE    140
577 #define HEINRICH_SLASH_DAMAGE   ( 30 + rand() % 15 )
578 
AIFunc_Heinrich_SwordSideSlash(cast_state_t * cs)579 char *AIFunc_Heinrich_SwordSideSlash( cast_state_t *cs ) {
580 	gentity_t *ent = &g_entities[cs->entityNum];
581 	trace_t *tr;
582 	vec3_t right, left;
583 	float enemyDist;
584 	aicast_predictmove_t move;
585 	vec3_t vec;
586 	cast_state_t *ecs;
587 
588 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
589 
590 	if ( cs->enemyNum < 0 ) {
591 		if ( ent->client->ps.torsoTimer ) {
592 			return NULL;
593 		}
594 		return AIFunc_DefaultStart( cs );
595 	}
596 
597 	ecs = AICast_GetCastState( cs->enemyNum );
598 
599 	if ( ent->client->ps.torsoTimer < 500 ) {
600 		if ( !ent->client->ps.legsTimer ) {
601 			trap_EA_MoveForward( cs->entityNum );
602 		}
603 		ent->client->ps.legsTimer = 0;
604 		ent->client->ps.torsoTimer = 0;
605 		cs->castScriptStatus.scriptNoMoveTime = 0;
606 		AICast_Heinrich_Taunt( cs );
607 		return AIFunc_BattleChaseStart( cs );
608 	}
609 
610 	// time for the melee?
611 	if ( cs->enemyNum >= 0 && !( cs->aiFlags & AIFL_MISCFLAG1 ) ) {
612 		// face them
613 		AICast_AimAtEnemy( cs );
614 		// keep checking for impact status
615 		tr = CheckMeleeAttack( ent, HEINRICH_SLASH_RANGE, qfalse );
616 		// ready for damage?
617 		if ( cs->thinkFuncChangeTime < level.time - HEINRICH_SLASH_DELAY ) {
618 			cs->aiFlags |= AIFL_MISCFLAG1;
619 			// do melee damage
620 			if ( tr && ( tr->entityNum == cs->enemyNum ) ) {
621 				AngleVectors( cs->viewangles, NULL, right, NULL );
622 				VectorNegate( right, left );
623 				G_Damage( &g_entities[tr->entityNum], ent, ent, left, tr->endpos, HEINRICH_SLASH_DAMAGE, 0, MOD_GAUNTLET );
624 				// sound
625 				G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDIMPACT] );
626 				// throw them in direction of impact
627 				left[2] = 0.5;
628 				VectorMA( g_entities[cs->enemyNum].client->ps.velocity, 400, left, g_entities[cs->enemyNum].client->ps.velocity );
629 			}
630 		}
631 	}
632 
633 	// if they are outside range, move forward
634 	AICast_PredictMovement( ecs, 2, 0.3, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 );
635 	VectorSubtract( move.endpos, cs->bs->origin, vec );
636 	vec[2] = 0;
637 	enemyDist = VectorLength( vec );
638 	enemyDist -= g_entities[cs->enemyNum].r.maxs[0];
639 	enemyDist -= ent->r.maxs[0];
640 	if ( enemyDist > 30 ) {   // we can get closer
641 		if ( ent->client->ps.legsTimer ) {
642 			cs->castScriptStatus.scriptNoMoveTime = level.time + 100;
643 			ent->client->ps.legsTimer = 0;      // allow legs to move us
644 		}
645 		if ( cs->castScriptStatus.scriptNoMoveTime < level.time ) {
646 			trap_EA_MoveForward( cs->entityNum );
647 		}
648 	}
649 
650 	return NULL;
651 }
652 
AIFunc_Heinrich_SwordSideSlashStart(cast_state_t * cs)653 char *AIFunc_Heinrich_SwordSideSlashStart( cast_state_t *cs ) {
654 	gentity_t *ent = &g_entities[cs->entityNum];
655 
656 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
657 	// sound
658 	G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDSIDESLASH_START] );
659 	// weapon sound
660 	G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDSIDESLASH_WEAPON] );
661 	// face them
662 	AICast_AimAtEnemy( cs );
663 	// clear flags
664 	cs->aiFlags &= ~( AIFL_MISCFLAG1 | AIFL_MISCFLAG2 );
665 	// play the anim
666 	BG_PlayAnimName( &ent->client->ps, "attack8", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
667 	// start the func
668 	cs->aifunc = AIFunc_Heinrich_SwordSideSlash;
669 	return "AIFunc_Heinrich_SwordSideSlash";
670 }
671 
672 #define HEINRICH_STOMP_DELAY        900
673 #define HEINRICH_STOMP_RANGE        1024.0
674 #define HEINRICH_STOMP_VELOCITY_Z   420
675 #define HEINRICH_STOMP_DAMAGE       35
676 
AIFunc_Heinrich_Earthquake(cast_state_t * cs)677 char *AIFunc_Heinrich_Earthquake( cast_state_t *cs ) {
678 	gentity_t   *ent = &g_entities[cs->entityNum];
679 	gentity_t   *enemy;
680 	cast_state_t *ecs;
681 	vec3_t enemyVec;
682 	float enemyDist, scale;
683 	trace_t *tr;
684 
685 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
686 
687 	if ( cs->enemyNum < 0 ) {
688 		if ( !ent->client->ps.torsoTimer ) {
689 			return AIFunc_DefaultStart( cs );
690 		}
691 		return NULL;
692 	}
693 
694 	enemy = &g_entities[cs->enemyNum];
695 	ecs = AICast_GetCastState( cs->enemyNum );
696 
697 	VectorMA( enemy->r.currentOrigin, HEINRICH_STOMP_DELAY, enemy->client->ps.velocity, enemyVec );
698 	VectorDistance( ent->r.currentOrigin, enemyVec );
699 
700 	if ( ent->client->ps.torsoTimer < 500 ) {
701 		int rnd;
702 		aicast_predictmove_t move;
703 		vec3_t vec;
704 
705 		AICast_PredictMovement( ecs, 2, 0.5, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 );
706 		VectorSubtract( move.endpos, cs->bs->origin, vec );
707 		vec[2] = 0;
708 		enemyDist = VectorLength( vec );
709 		enemyDist -= g_entities[cs->enemyNum].r.maxs[0];
710 		enemyDist -= ent->r.maxs[0];
711 		//
712 		if ( enemyDist < 140 ) {
713 			// combo attack
714 			rnd = rand() % 3;
715 			switch ( rnd ) {
716 			case 0:
717 				return AIFunc_Heinrich_SwordSideSlashStart( cs );
718 			case 1:
719 				return AIFunc_Heinrich_SwordKnockbackStart( cs );
720 			case 2:
721 				return AIFunc_Heinrich_SwordLungeStart( cs );
722 			}
723 		} else {    // back to roaming
724 			ent->client->ps.legsTimer = 0;
725 			ent->client->ps.torsoTimer = 0;
726 			cs->castScriptStatus.scriptNoMoveTime = 0;
727 			AICast_Heinrich_Taunt( cs );
728 			return AIFunc_DefaultStart( cs );
729 		}
730 	}
731 
732 	// time for the thump?
733 	if ( !( cs->aiFlags & AIFL_MISCFLAG1 ) ) {
734 		// face them
735 		AICast_AimAtEnemy( cs );
736 		// ready for damage?
737 		if ( cs->thinkFuncChangeTime < level.time - HEINRICH_STOMP_DELAY ) {
738 			cs->aiFlags |= AIFL_MISCFLAG1;
739 			// play the stomp sound
740 			G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[ORDERSDENYSOUNDSCRIPT] ) );
741 			// check for striking the player
742 			tr = CheckMeleeAttack( ent, 70, qfalse );
743 			// do melee damage
744 			if ( tr && ( tr->entityNum == cs->enemyNum ) ) {
745 				G_Damage( &g_entities[tr->entityNum], ent, ent, vec3_origin, tr->endpos, HEINRICH_STOMP_DAMAGE, 0, MOD_GAUNTLET );
746 			}
747 			// call the debris trigger
748 			AICast_ScriptEvent( cs, "trigger", "quake" );
749 		}
750 	}
751 
752 	enemyDist = Distance( enemy->s.pos.trBase, ent->s.pos.trBase );
753 
754 	// do the earthquake effects
755 	if ( cs->thinkFuncChangeTime < level.time - HEINRICH_STOMP_DELAY ) {
756 		// throw the player into the air, if they are on the ground
757 		if ( ( enemy->s.groundEntityNum != ENTITYNUM_NONE ) && enemyDist < HEINRICH_STOMP_RANGE ) {
758 			scale = 0.5 + 0.5 * ( (float)ent->client->ps.torsoTimer / 1000.0 );
759 			if ( scale > 1.0 ) {
760 				scale = 1.0;
761 			}
762 			VectorSubtract( ent->s.pos.trBase, enemy->s.pos.trBase, enemyVec );
763 			VectorScale( enemyVec, 2.0 * ( 0.6 + 0.5 * random() ) * scale * ( 0.6 + 0.6 * ( 1.0 - ( enemyDist / HEINRICH_STOMP_RANGE ) ) ), enemyVec );
764 			enemyVec[2] = scale * HEINRICH_STOMP_VELOCITY_Z * ( 1.0 - 0.5 * ( enemyDist / HEINRICH_STOMP_RANGE ) );
765 			// bounce the player using this velocity
766 			VectorAdd( enemy->client->ps.velocity, enemyVec, enemy->client->ps.velocity );
767 		}
768 	}
769 
770 	return NULL;
771 }
772 
AIFunc_Heinrich_MeleeStart(cast_state_t * cs)773 char *AIFunc_Heinrich_MeleeStart( cast_state_t *cs ) {
774 	gentity_t *ent = &g_entities[cs->entityNum];
775 	gentity_t   *enemy = &g_entities[cs->enemyNum];
776 	int rnd;
777 	static int lastStomp;
778 
779 	if ( cs->enemyNum < 0 ) {
780 		return NULL;
781 	}
782 
783 	// record weapon fire
784 	cs->weaponFireTimes[cs->weaponNum] = level.time;
785 	// face them
786 	AICast_AimAtEnemy( cs );
787 	// clear flags
788 	cs->aiFlags &= ~( AIFL_MISCFLAG1 | AIFL_MISCFLAG2 );
789 	// decide which attack to use
790 	if ( VectorDistance( ent->r.currentOrigin, enemy->r.currentOrigin ) < 60 ) {
791 		rnd = 0;    // sword slash up close
792 	} else if ( VectorDistance( ent->r.currentOrigin, enemy->r.currentOrigin ) >= HEINRICH_SLASH_RANGE ) {
793 		rnd = 1;    // too far away, stomp
794 	} else {
795 		// pick at random
796 		rnd = rand() % 2;
797 	}
798 	//
799 	switch ( rnd ) {
800 	case 0:
801 	{
802 		int rnd = rand() % 3;
803 		switch ( rnd ) {
804 		case 0:
805 			return AIFunc_Heinrich_SwordSideSlashStart( cs );
806 		case 1:
807 			return AIFunc_Heinrich_SwordKnockbackStart( cs );
808 		case 2:
809 			return AIFunc_Heinrich_SwordLungeStart( cs );
810 		}
811 	}
812 	case 1:
813 		// dont do stomp too often
814 		if ( lastStomp > level.time - 12000 ) {   // plenty of time to let debris disappear
815 			return NULL;
816 		}
817 		lastStomp = level.time;
818 		cs->aiFlags |= AIFL_SPECIAL_FUNC;
819 		// sound
820 		G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_EARTHQUAKE_START] );
821 		// play the anim
822 		BG_PlayAnimName( &ent->client->ps, "attack7", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
823 		// start the func
824 		cs->aifunc = AIFunc_Heinrich_Earthquake;
825 		return "AIFunc_Heinrich_Earthquake";
826 	}
827 	// shutup compiler
828 	return NULL;
829 }
830 
831 #define HEINRICH_RAISEDEAD_DELAY        1200
832 #define HEINRICH_RAISEDEAD_COUNT        3
833 int lastRaise;
834 
AIFunc_Heinrich_RaiseDead(cast_state_t * cs)835 char *AIFunc_Heinrich_RaiseDead( cast_state_t *cs ) {
836 	int i;
837 	gentity_t   *ent = &g_entities[cs->entityNum];
838 	gentity_t   *enemy = &g_entities[cs->enemyNum];
839 	gentity_t *trav, *closest;
840 	float closestDist, dist;
841 	//
842 	cs->aiFlags |= AIFL_SPECIAL_FUNC;
843 	if ( cs->enemyNum < 0 ) {
844 		if ( !ent->client->ps.torsoTimer ) {
845 			return AIFunc_DefaultStart( cs );
846 		}
847 		return NULL;
848 	}
849 	//
850 	// record weapon fire
851 	cs->weaponFireTimes[cs->weaponNum] = level.time;
852 	//
853 	if ( !ent->client->ps.torsoTimer ) {
854 		return AIFunc_DefaultStart( cs );
855 	}
856 	if ( ent->count2 && lastRaise < level.time - HEINRICH_RAISEDEAD_DELAY ) {
857 		lastRaise = level.time;
858 		// summons the closest warrior
859 		closest = NULL;
860 		closestDist = 0;    // shutup the compiler
861 		for ( i = 0, trav = g_entities; i < level.maxclients; i++, trav++ ) {
862 			if ( !trav->inuse ) {
863 				continue;
864 			}
865 			if ( !trav->aiInactive ) {
866 				continue;
867 			}
868 			if ( trav->aiCharacter != AICHAR_WARZOMBIE ) {
869 				continue;
870 			}
871 			dist = VectorDistance( trav->s.pos.trBase, enemy->r.currentOrigin );
872 			if ( !closest || dist < closestDist ) {
873 				closest = trav;
874 				closestDist = dist;
875 			}
876 		}
877 		//
878 		if ( closest ) {
879 			closest->AIScript_AlertEntity( closest );
880 			// make them aware of the player
881 			AICast_UpdateVisibility( closest, enemy, qtrue, qtrue );
882 			// reduce the count
883 			ent->count2--;
884 		}
885 	}
886 	//
887 	return NULL;
888 }
889 
AIFunc_Heinrich_RaiseDeadStart(cast_state_t * cs)890 char *AIFunc_Heinrich_RaiseDeadStart( cast_state_t *cs ) {
891 	int i, cnt, free;
892 	gentity_t   *ent = &g_entities[cs->entityNum];
893 	gentity_t *trav;
894 	float circleDist;
895 	//
896 	// count the number of active warriors
897 	cnt = 0;
898 	free = 0;
899 	for ( i = 0, trav = g_entities; i < level.maxclients; i++, trav++ ) {
900 		if ( !trav->inuse ) {
901 			continue;
902 		}
903 		if ( trav->aiCharacter != AICHAR_WARZOMBIE ) {
904 			continue;
905 		}
906 		if ( trav->aiInactive ) {
907 			free++;
908 			continue;
909 		}
910 		if ( trav->health <= 0 ) {
911 			continue;
912 		}
913 		cnt++;
914 	}
915 	//
916 	if ( cnt < HEINRICH_RAISEDEAD_COUNT && free ) {   // need a new one
917 		cs->aiFlags &= ~AIFL_MISCFLAG1;
918 		ent->count2 = HEINRICH_RAISEDEAD_COUNT - cnt;
919 		lastRaise = level.time;
920 		cs->aiFlags |= AIFL_SPECIAL_FUNC;
921 		// start the animation
922 		BG_PlayAnimName( &ent->client->ps, "attack4", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
923 		// play the sound
924 		G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_RAISEDEAD_START] );
925 		// start the func
926 		cs->aifunc = AIFunc_Heinrich_RaiseDead;
927 		return "AIFunc_Heinrich_RaiseDead";
928 	}
929 	// enable all the spirit spawners
930 	trav = NULL;
931 	// TTimo: gcc: suggest () around assignment used as truth value
932 	while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) {
933 		if ( !trav->active && trav->spawnflags & 4 ) {
934 			trav->active = 1;   // let them release spirits now
935 		}
936 	}
937 	// is the player outside the circle?
938 	trav = NULL;
939 	// TTimo: gcc: suggest () around assignment used as truth value
940 	while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) {
941 		if ( trav->spawnflags & 4 ) {
942 			circleDist = trav->radius;
943 			trav = G_Find( NULL, FOFS( targetname ), trav->target );
944 			if ( trav ) {
945 				if ( VectorDistance( g_entities[0].s.pos.trBase, trav->s.origin ) > circleDist ) {
946 					cs->aiFlags &= ~AIFL_MISCFLAG1;
947 					ent->count2 = 0;
948 					cs->aiFlags |= AIFL_SPECIAL_FUNC;
949 					// start the animation
950 					BG_PlayAnimName( &ent->client->ps, "attack4", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
951 					// play the sound
952 					G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_RAISEDEAD_START] );
953 					// start the func
954 					cs->aifunc = AIFunc_Heinrich_RaiseDead;
955 					return "AIFunc_Heinrich_RaiseDead";
956 				}
957 			}
958 			break;
959 		}
960 	}
961 	//
962 	return NULL;
963 }
964 
AIFunc_Heinrich_SpawnSpiritsStart(cast_state_t * cs)965 char *AIFunc_Heinrich_SpawnSpiritsStart( cast_state_t *cs ) {
966 	gentity_t   *ent = &g_entities[cs->entityNum];
967 	gentity_t *trav;
968 	float circleDist;
969 	//
970 	// enable all the spirit spawners
971 	trav = NULL;
972 	// TTimo: gcc: suggest () around assignment used as truth value
973 	while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) {
974 		if ( !trav->active && trav->spawnflags & 4 ) {
975 			trav->active = 1;   // let them release spirits now
976 		}
977 	}
978 	// is the player outside the circle?
979 	trav = NULL;
980 	// TTimo: gcc: suggest () around assignment used as truth value
981 	while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) {
982 		if ( trav->spawnflags & 4 ) {
983 			circleDist = trav->radius;
984 			trav = G_Find( NULL, FOFS( targetname ), trav->target );
985 			if ( trav ) {
986 				if ( VectorDistance( g_entities[0].s.pos.trBase, trav->s.origin ) > circleDist ) {
987 					cs->aiFlags &= ~AIFL_MISCFLAG1;
988 					ent->count2 = 0;
989 					cs->aiFlags |= AIFL_SPECIAL_FUNC;
990 					// start the animation
991 					BG_PlayAnimName( &ent->client->ps, "attack4", ANIM_BP_BOTH, qtrue, qfalse, qtrue );
992 					// play the sound
993 					G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_RAISEDEAD_START] );
994 					// start the func
995 					cs->aifunc = AIFunc_Heinrich_RaiseDead; // just do raise dead, without raising any warriors
996 					return "AIFunc_Heinrich_RaiseDead";
997 				}
998 			}
999 			break;
1000 		}
1001 	}
1002 	//
1003 	return NULL;
1004 }
1005