char *AIFunc_LoperAttack2Start( cast_state_t *cs ) { gentity_t *ent; vec3_t vec, avec; // ent = &g_entities[cs->entityNum]; // if ( cs->enemyNum < 0 ) { return AIFunc_DefaultStart( cs ); } // face them AICast_AimAtEnemy( cs ); // if not facing them yet, wait VectorSubtract( g_entities[cs->enemyNum].client->ps.origin, cs->bs->origin, vec ); VectorNormalize( vec ); AngleVectors( cs->viewangles, avec, NULL, NULL ); if ( DotProduct( vec, avec ) < 0.9 ) { //cs->aifunc = AIFunc_LoperAttack2Start; return NULL; } // OK, start the animation BG_PlayAnimName( &ent->client->ps, "legs_extra3", ANIM_BP_LEGS, qtrue, qfalse, qtrue ); ent->client->ps.legsTimer = 500; // stay on this until landing // send us hurtling towards our enemy VectorScale( vec, LOPER_LEAP_VELOCITY_START, vec ); vec[2] = LOPER_LEAP_VELOCITY_Z; VectorCopy( vec, ent->client->ps.velocity ); VectorCopy( vec, cs->loperLeapVel ); // cs->aiFlags &= ~AIFL_LAND_ANIM_PLAYED; // play the sound // TODO // cs->aifunc = AIFunc_LoperAttack2; return "AIFunc_LoperAttack2"; }
char *AIFunc_Heinrich_MeleeStart(cast_state_t *cs) { gentity_t *ent = &g_entities[cs->entityNum]; gentity_t *enemy = &g_entities[cs->enemyNum]; int rnd; static int lastStomp; if (cs->enemyNum < 0) { return NULL; } // record weapon fire cs->weaponFireTimes[cs->weaponNum] = level.time; // face them AICast_AimAtEnemy(cs); // clear flags cs->aiFlags &= ~(AIFL_MISCFLAG1|AIFL_MISCFLAG2); // decide which attack to use if (VectorDistance(ent->r.currentOrigin, enemy->r.currentOrigin) < 60) { rnd = 0; // sword slash up close } else if (VectorDistance(ent->r.currentOrigin, enemy->r.currentOrigin) >= HEINRICH_SLASH_RANGE) { rnd = 1; // too far away, stomp } else { // pick at random rnd = rand() % 2; } switch (rnd) { case 0: { int rnd = rand() % 3; switch (rnd) { case 0: return AIFunc_Heinrich_SwordSideSlashStart(cs); case 1: return AIFunc_Heinrich_SwordKnockbackStart(cs); case 2: return AIFunc_Heinrich_SwordLungeStart(cs); } } case 1: // dont do stomp too often if (lastStomp > level.time - 12000) { // plenty of time to let debris disappear return NULL; } lastStomp = level.time; cs->aiFlags |= AIFL_SPECIAL_FUNC; // sound G_AddEvent(ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_EARTHQUAKE_START]); // play the anim BG_PlayAnimName(&ent->client->ps, "attack7", ANIM_BP_BOTH, qtrue, qfalse, qtrue); // start the func cs->aifunc = AIFunc_Heinrich_Earthquake; return "AIFunc_Heinrich_Earthquake"; } // shutup compiler return NULL; }
char *AIFunc_LoperAttack1Start( cast_state_t *cs ) { gentity_t *ent; // ent = &g_entities[cs->entityNum]; // face them AICast_AimAtEnemy( cs ); // start the animation if ( rand() % 2 ) { G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[FOLLOWSOUNDSCRIPT] ) ); BG_PlayAnimName( &ent->client->ps, "legs_extra", ANIM_BP_LEGS, qtrue, qfalse, qtrue ); } else { G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[STAYSOUNDSCRIPT] ) ); BG_PlayAnimName( &ent->client->ps, "legs_extra2", ANIM_BP_LEGS, qtrue, qfalse, qtrue ); } // cs->aifunc = AIFunc_LoperAttack1; return "AIFunc_LoperAttack1"; }
const char *AIFunc_Heinrich_SwordKnockbackStart( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; // gentity_t *enemy = &g_entities[cs->enemyNum]; cs->aiFlags |= AIFL_SPECIAL_FUNC; // sound G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDKNOCKBACK_START] ); // weapon sound G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDKNOCKBACK_WEAPON] ); // face them AICast_AimAtEnemy( cs ); // clear flags cs->aiFlags &= ~( AIFL_MISCFLAG1 | AIFL_MISCFLAG2 ); // play the anim if ( rand() % 2 ) { BG_PlayAnimName( &ent->client->ps, "attack2", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); } else { BG_PlayAnimName( &ent->client->ps, "attack3", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); } // start the func cs->aifunc = AIFunc_Heinrich_SwordKnockback; return "AIFunc_Heinrich_SwordKnockback"; }
char *AIFunc_BlackGuardAttack1Start( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; // cs->weaponFireTimes[cs->weaponNum] = level.time; // face them AICast_AimAtEnemy( cs ); // audible sound AIChar_AttackSound( cs ); // start the animation BG_PlayAnimName( &ent->client->ps, "kick", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); // clear flags cs->aiFlags &= ~( AIFL_MISCFLAG1 | AIFL_MISCFLAG2 ); // cs->aifunc = AIFunc_BlackGuardAttack1; return "AIFunc_BlackGuardAttack1"; }
char *AIFunc_LoperAttack3Start( cast_state_t *cs ) { gentity_t *ent; // ent = &g_entities[cs->entityNum]; // // face them AICast_AimAtEnemy( cs ); // play the animation BG_PlayAnimName( &ent->client->ps, "legs_extra5", ANIM_BP_LEGS, qtrue, qfalse, qtrue ); // // play the buildup sound // TODO // cs->aifunc = AIFunc_LoperAttack3; return "AIFunc_LoperAttack3"; }
const char *AIFunc_Heinrich_SwordLungeStart( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; // gentity_t *enemy = &g_entities[cs->enemyNum]; cs->aiFlags |= AIFL_SPECIAL_FUNC; // sound G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDLUNGE_START] ); // face them AICast_AimAtEnemy( cs ); // clear flags cs->aiFlags &= ~( AIFL_MISCFLAG1 | AIFL_MISCFLAG2 ); // play the anim BG_PlayAnimName( &ent->client->ps, "attack9", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); // start the func cs->aifunc = AIFunc_Heinrich_SwordLunge; return "AIFunc_Heinrich_SwordLunge"; }
char *AIFunc_ZombieFlameAttackStart( cast_state_t *cs ) { gentity_t *ent; // ent = &g_entities[cs->entityNum]; ent->s.otherEntityNum2 = cs->bs->enemy; ent->s.effect3Time = level.time; // // dont turn cs->bs->ideal_viewangles[YAW] = cs->bs->viewangles[YAW]; cs->bs->ideal_viewangles[PITCH] = -45; // look upwards // start the flame ent->s.onFireStart = level.time; ent->s.onFireEnd = level.time + ZOMBIE_FLAME_DURATION; // // set the correct animation BG_PlayAnimName( &ent->client->ps, "both_attack1", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); // cs->aifunc = AIFunc_ZombieFlameAttack; return "AIFunc_ZombieFlameAttack"; }
const char *AIFunc_Heinrich_SpawnSpiritsStart( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; gentity_t *trav, *spirits; float circleDist; // // enable all the spirit spawners trav = NULL; // TTimo: gcc: suggest () around assignment used as truth value while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) { if ( !trav->active && trav->spawnflags & 4 ) { trav->active = 1; // let them release spirits now } } // is the player outside the circle? trav = NULL; // TTimo: gcc: suggest () around assignment used as truth value while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) { if ( trav->spawnflags & 4 ) { spirits = trav; circleDist = trav->radius; trav = G_Find( NULL, FOFS( targetname ), trav->target ); if ( trav ) { if ( VectorDistance( g_entities[0].s.pos.trBase, trav->s.origin ) > circleDist ) { cs->aiFlags &= ~AIFL_MISCFLAG1; ent->count2 = 0; cs->aiFlags |= AIFL_SPECIAL_FUNC; // start the animation BG_PlayAnimName( &ent->client->ps, "attack4", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); // play the sound G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_RAISEDEAD_START] ); // start the func cs->aifunc = AIFunc_Heinrich_RaiseDead; // just do raise dead, without raising any warriors return "AIFunc_Heinrich_RaiseDead"; } } break; } } // return NULL; }
const char *AIFunc_Heinrich_RaiseDeadStart( cast_state_t *cs ) { int i, cnt, free; gentity_t *ent = &g_entities[cs->entityNum]; // gentity_t *enemy = &g_entities[cs->enemyNum]; gentity_t *trav, *spirits; float circleDist; // // count the number of active warriors cnt = 0; free = 0; for ( i = 0, trav = g_entities; i < level.maxclients; i++, trav++ ) { if ( !trav->inuse ) { continue; } if ( trav->aiCharacter != AICHAR_WARZOMBIE ) { continue; } if ( trav->aiInactive ) { free++; continue; } if ( trav->health <= 0 ) { continue; } cnt++; } // if ( cnt < HEINRICH_RAISEDEAD_COUNT && free ) { // need a new one cs->aiFlags &= ~AIFL_MISCFLAG1; ent->count2 = HEINRICH_RAISEDEAD_COUNT - cnt; lastRaise = level.time; cs->aiFlags |= AIFL_SPECIAL_FUNC; // start the animation BG_PlayAnimName( &ent->client->ps, "attack4", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); // play the sound G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_RAISEDEAD_START] ); // start the func cs->aifunc = AIFunc_Heinrich_RaiseDead; return "AIFunc_Heinrich_RaiseDead"; } // enable all the spirit spawners trav = NULL; // TTimo: gcc: suggest () around assignment used as truth value while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) { if ( !trav->active && trav->spawnflags & 4 ) { trav->active = 1; // let them release spirits now } } // is the player outside the circle? trav = NULL; // TTimo: gcc: suggest () around assignment used as truth value while ( ( trav = G_Find( trav, FOFS( classname ), "func_bats" ) ) ) { if ( trav->spawnflags & 4 ) { spirits = trav; circleDist = trav->radius; trav = G_Find( NULL, FOFS( targetname ), trav->target ); if ( trav ) { if ( VectorDistance( g_entities[0].s.pos.trBase, trav->s.origin ) > circleDist ) { cs->aiFlags &= ~AIFL_MISCFLAG1; ent->count2 = 0; cs->aiFlags |= AIFL_SPECIAL_FUNC; // start the animation BG_PlayAnimName( &ent->client->ps, "attack4", ANIM_BP_BOTH, qtrue, qfalse, qtrue ); // play the sound G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_RAISEDEAD_START] ); // start the func cs->aifunc = AIFunc_Heinrich_RaiseDead; return "AIFunc_Heinrich_RaiseDead"; } } break; } } // return NULL; }
/* =============== AIFunc_LoperAttack2() Loper's leaping long range attack =============== */ char *AIFunc_LoperAttack2( cast_state_t *cs ) { gentity_t *ent; vec3_t vec; qboolean onGround = qfalse; // ent = &g_entities[cs->entityNum]; // // are we waiting to inflict damage? if ( ( cs->enemyNum >= 0 ) && ( cs->weaponFireTimes[WP_MONSTER_ATTACK2] < level.time - 50 ) && ( cs->bs->cur_ps.groundEntityNum == ENTITYNUM_NONE ) ) { // ready to inflict damage? if ( cs->thinkFuncChangeTime < level.time - LOPER_LEAP_DELAY ) { // check for damage if ( VectorDistance( cs->bs->origin, g_entities[cs->enemyNum].client->ps.origin ) < LOPER_LEAP_RANGE ) { // draw the client-side lightning effect ent->client->ps.eFlags |= EF_MONSTER_EFFECT; // do the damage G_Damage( &g_entities[cs->enemyNum], ent, ent, vec3_origin, cs->bs->origin, LOPER_LEAP_DAMAGE, 0, MOD_LOPER_LEAP ); G_Sound( &g_entities[cs->entityNum], level.loperZapSound ); cs->weaponFireTimes[WP_MONSTER_ATTACK2] = level.time; } } } // // landed? if ( cs->bs->cur_ps.groundEntityNum != ENTITYNUM_NONE ) { onGround = qtrue; } else { // predict a landing aicast_predictmove_t move; float changeTime; AICast_PredictMovement( cs, 1, 0.2, &move, &cs->lastucmd, cs->enemyNum ); if ( move.groundEntityNum != ENTITYNUM_NONE ) { onGround = qtrue; } // // adjust velocity VectorCopy( cs->loperLeapVel, vec ); vec[2] = 0; VectorNormalize( vec ); changeTime = 2.0 * ( 0.001 * ( level.time - cs->thinkFuncChangeTime ) ); if ( changeTime > 1.0 ) { changeTime = 1.0; } VectorScale( vec, LOPER_LEAP_VELOCITY_START + changeTime * ( LOPER_LEAP_VELOCITY_END - LOPER_LEAP_VELOCITY_START ), vec ); g_entities[cs->entityNum].s.pos.trDelta[0] = vec[0]; g_entities[cs->entityNum].s.pos.trDelta[1] = vec[1]; } // if ( onGround || ( cs->aiFlags & AIFL_LAND_ANIM_PLAYED ) ) { // if we just started the attack recently, we probably haven't had a chance to get airborne yet if ( cs->thinkFuncChangeTime < level.time - LOPER_LEAP_DELAY ) { // loper is back on ground, wait for animation to play out if ( !( cs->aiFlags & AIFL_LAND_ANIM_PLAYED ) ) { BG_PlayAnimName( &ent->client->ps, "legs_extra4", ANIM_BP_LEGS, qtrue, qfalse, qtrue ); // cs->aiFlags |= AIFL_LAND_ANIM_PLAYED; // TODO:play the landing thud } // if ( ent->client->ps.legsTimer < 800 ) { // we're done ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } // keep moving slightly in our facing direction to simulate landing momentum AngleVectors( cs->viewangles, vec, NULL, NULL ); trap_EA_Move( cs->entityNum, vec, ( (float)ent->client->ps.legsTimer / (float)LOPER_LAND_DURATION ) * (float)LOPER_LEAP_LAND_MOMENTUM ); return NULL; } } ent->client->ps.legsTimer = 500; // stay on this until landing return NULL; }