void gsc_player_set_anim(int id) { char* animation; if ( ! stackGetParams("s", &animation)) { printf("scriptengine> ERROR: gsc_player_set_anim(): param \"animation\"[1] has to be an string!\n"); stackPushUndefined(); return; } #if COD_VERSION == COD2_1_0 int anim_offset = 0x080D46AC; #elif COD_VERSION == COD2_1_2 int anim_offset = 0x080D6C8C; #elif COD_VERSION == COD2_1_3 int anim_offset = 0x080D6DD0; #else #warning gsc_player_set_anim() got no working addresses for anim_offset int anim_offset = 0x0; #endif int (*BG_AnimationIndexForString)(char *src); *(int *)&BG_AnimationIndexForString = anim_offset; int animationIndex = 0; extern int custom_animation[64]; if(strcmp(animation, "none")) animationIndex = BG_AnimationIndexForString(animation); custom_animation[id] = (animationIndex); }
const char *AIFunc_Helga_SpiritAttack( cast_state_t *cs ) { bot_state_t *bs; gentity_t *ent; // cs->aiFlags |= AIFL_SPECIAL_FUNC; ent = &g_entities[cs->entityNum]; bs = cs->bs; // make sure we're still playing the right anim if ( ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack1", cs->entityNum ) ) { return AIFunc_DefaultStart( cs ); } // if ( cs->enemyNum < 0 ) { ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } // // if we can't see them anymore, abort immediately if ( cs->vislist[cs->enemyNum].real_visible_timestamp != cs->vislist[cs->enemyNum].real_update_timestamp ) { ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } // we are firing this weapon, so record it cs->weaponFireTimes[WP_MONSTER_ATTACK2] = level.time; // // once an attack has started, only abort once the player leaves our view, or time runs out if ( cs->thinkFuncChangeTime < level.time - HELGA_SPIRIT_BUILDUP_TIME ) { // if enough time has elapsed, finish this attack if ( level.time > cs->thinkFuncChangeTime + HELGA_SPIRIT_BUILDUP_TIME + HELGA_SPIRIT_FADEOUT_TIME ) { ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; return AIFunc_DefaultStart( cs ); } } else { // set timers ent->client->ps.torsoTimer = 1000; ent->client->ps.legsTimer = 1000; // draw the client-side effect ent->client->ps.eFlags |= EF_MONSTER_EFFECT; // inform the client of our enemies position VectorCopy( g_entities[cs->enemyNum].client->ps.origin, ent->s.origin2 ); ent->s.origin2[2] += g_entities[cs->enemyNum].client->ps.viewheight; } // // return NULL; }
/* ================ AIFunc_WarriorZombieMelee ================ */ char *AIFunc_WarriorZombieMelee( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; int hitDelay = -1, anim; trace_t *tr; if ( !ent->client->ps.torsoTimer ) { return AIFunc_DefaultStart( cs ); } anim = ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack1", cs->entityNum ); if ( anim < 0 || anim >= NUM_WARRIOR_ANIMS ) { // animation interupted return AIFunc_DefaultStart( cs ); //G_Error( "AIFunc_WarriorZombieMelee: warrior using invalid or unknown attack anim" ); } if ( warriorHitTimes[anim][cs->animHitCount] >= 0 ) { // face them AICast_AimAtEnemy( cs ); if ( !cs->animHitCount ) { hitDelay = warriorHitTimes[anim][cs->animHitCount]; } else { hitDelay = warriorHitTimes[anim][cs->animHitCount] - warriorHitTimes[anim][cs->animHitCount - 1]; } // check for inflicting damage if ( level.time - cs->weaponFireTimes[cs->bs->weaponnum] > hitDelay ) { // do melee damage if ( ( tr = CheckMeleeAttack( ent, 48, qfalse ) ) && ( tr->entityNum == cs->bs->enemy ) ) { G_Damage( &g_entities[tr->entityNum], ent, ent, vec3_origin, tr->endpos, warriorHitDamage[anim], 0, MOD_GAUNTLET ); } G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].staySoundScript ) ); cs->weaponFireTimes[cs->bs->weaponnum] = level.time; cs->animHitCount++; } else { // if they are outside range, move forward if ( anim != 4 && !CheckMeleeAttack( ent, 48, qfalse ) ) { //ent->client->ps.torsoTimer = 0; ent->client->ps.legsTimer = 0; // allow legs us to move //return AIFunc_DefaultStart(cs); trap_EA_MoveForward( cs->entityNum ); } } } return NULL; }
/* =============== AIFunc_LoperAttack1() Loper's close range melee attack =============== */ char *AIFunc_LoperAttack1( cast_state_t *cs ) { trace_t *tr; gentity_t *ent; int anim; // ent = &g_entities[cs->entityNum]; // // draw the client-side lightning effect //ent->client->ps.eFlags |= EF_MONSTER_EFFECT; // // have we inflicted the damage? if ( cs->weaponFireTimes[WP_MONSTER_ATTACK1] > cs->thinkFuncChangeTime ) { // has the animation finished? if ( !ent->client->ps.legsTimer ) { return AIFunc_DefaultStart( cs ); } return NULL; // just wait for anim to finish } // ready to inflict damage? anim = ( ent->client->ps.legsAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "legs_extra", cs->entityNum ); if ( cs->thinkFuncChangeTime < level.time - loperHitTimes[anim] ) { // check for damage // TTimo: gcc: suggests () around assignment used as truth value if ( ( tr = CheckMeleeAttack( &g_entities[cs->entityNum], LOPER_MELEE_RANGE, qfalse ) ) ) { G_Damage( &g_entities[tr->entityNum], ent, ent, vec3_origin, tr->endpos, LOPER_MELEE_DAMAGE, 0, MOD_LOPER_HIT ); // sound if ( anim == 0 ) { G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[ORDERSDENYSOUNDSCRIPT] ) ); } else { G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[MISC1SOUNDSCRIPT] ) ); } } cs->weaponFireTimes[WP_MONSTER_ATTACK1] = level.time; } return NULL; }
const char *AIFunc_Heinrich_SwordKnockback( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; trace_t *tr; vec3_t right, left; // float enemyDist; // aicast_predictmove_t move; // vec3_t vec; cast_state_t *ecs; cs->aiFlags |= AIFL_SPECIAL_FUNC; if ( cs->enemyNum < 0 ) { if ( ent->client->ps.torsoTimer ) { return NULL; } return AIFunc_DefaultStart( cs ); } ecs = AICast_GetCastState( cs->enemyNum ); if ( ent->client->ps.torsoTimer < 500 ) { if ( !ent->client->ps.legsTimer ) { trap_EA_MoveForward( cs->entityNum ); } ent->client->ps.legsTimer = 0; ent->client->ps.torsoTimer = 0; cs->castScriptStatus.scriptNoMoveTime = 0; AICast_Heinrich_Taunt( cs ); return AIFunc_BattleChaseStart( cs ); } // time for the melee? if ( cs->enemyNum >= 0 && !( cs->aiFlags & AIFL_MISCFLAG1 ) ) { // face them AICast_AimAtEnemy( cs ); // keep checking for impact status tr = CheckMeleeAttack( ent, HEINRICH_KNOCKBACK_RANGE, qfalse ); /* // do we need to move? if (!(tr && (tr->entityNum == cs->enemyNum))) { ent->client->ps.legsTimer = 0; cs->castScriptStatus.scriptNoMoveTime = 0; trap_EA_MoveForward( cs->entityNum ); } */ // ready for damage? if ( cs->thinkFuncChangeTime < level.time - HEINRICH_KNOCKBACK_DELAY ) { cs->aiFlags |= AIFL_MISCFLAG1; // do melee damage if ( tr && ( tr->entityNum == cs->enemyNum ) ) { AngleVectors( cs->viewangles, NULL, right, NULL ); VectorNegate( right, left ); G_Damage( &g_entities[tr->entityNum], ent, ent, left, tr->endpos, HEINRICH_KNOCKBACK_DAMAGE, 0, MOD_GAUNTLET ); // sound G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDIMPACT] ); // throw them in direction of impact if ( ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) == BG_AnimationIndexForString( "attack2", cs->entityNum ) ) { // right right[2] = 0.5; VectorMA( g_entities[cs->enemyNum].client->ps.velocity, 400, right, g_entities[cs->enemyNum].client->ps.velocity ); } else { // left left[2] = 0.5; VectorMA( g_entities[cs->enemyNum].client->ps.velocity, 400, left, g_entities[cs->enemyNum].client->ps.velocity ); } } } } /* DISABLED FOR SWORDKNOCKBACK..looks bad // if they are outside range, move forward AICast_PredictMovement( ecs, 2, 0.3, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 ); VectorSubtract( move.endpos, cs->bs->origin, vec ); vec[2] = 0; enemyDist = VectorLength( vec ); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if (enemyDist > 30) { // we can get closer if (ent->client->ps.legsTimer) { cs->castScriptStatus.scriptNoMoveTime = level.time + 100; ent->client->ps.legsTimer = 0; // allow legs to move us } if (cs->castScriptStatus.scriptNoMoveTime < level.time) { trap_EA_MoveForward(cs->entityNum); } } */ return NULL; }
/* ================ AIFunc_Helga_Melee ================ */ const char *AIFunc_Helga_Melee( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; gentity_t *enemy; cast_state_t *ecs; int hitDelay = -1, anim; trace_t tr; float enemyDist; aicast_predictmove_t move; vec3_t vec; cs->aiFlags |= AIFL_SPECIAL_FUNC; if ( !ent->client->ps.torsoTimer || !ent->client->ps.legsTimer ) { cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart( cs ); } if ( cs->enemyNum < 0 ) { ent->client->ps.legsTimer = 0; // allow legs us to move ent->client->ps.torsoTimer = 0; // allow legs us to move cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart( cs ); } ecs = AICast_GetCastState( cs->enemyNum ); enemy = &g_entities[cs->enemyNum]; anim = ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack3", cs->entityNum ); if ( anim < 0 || anim >= NUM_HELGA_ANIMS ) { // animation interupted cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart( cs ); //G_Error( "AIFunc_HelgaZombieMelee: helgaBoss using invalid or unknown attack anim" ); } if ( cs->animHitCount < MAX_HELGA_IMPACTS && helgaHitTimes[anim][cs->animHitCount] >= 0 ) { // face them VectorCopy( cs->bs->origin, vec ); vec[2] += ent->client->ps.viewheight; VectorSubtract( enemy->client->ps.origin, vec, vec ); VectorNormalize( vec ); vectoangles( vec, cs->ideal_viewangles ); cs->ideal_viewangles[PITCH] = AngleNormalize180( cs->ideal_viewangles[PITCH] ); // get hitDelay if ( !cs->animHitCount ) { hitDelay = helgaHitTimes[anim][cs->animHitCount]; } else { hitDelay = helgaHitTimes[anim][cs->animHitCount] - helgaHitTimes[anim][cs->animHitCount - 1]; } // check for inflicting damage if ( level.time - cs->weaponFireTimes[cs->weaponNum] > hitDelay ) { // do melee damage enemyDist = VectorDistance( enemy->r.currentOrigin, ent->r.currentOrigin ); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if ( enemyDist < 10 + AICast_WeaponRange( cs, cs->weaponNum ) ) { trap_Trace( &tr, ent->r.currentOrigin, NULL, NULL, enemy->r.currentOrigin, ent->s.number, MASK_SHOT ); if ( tr.entityNum == cs->enemyNum ) { G_Damage( &g_entities[tr.entityNum], ent, ent, vec3_origin, tr.endpos, helgaHitDamage[anim], 0, MOD_GAUNTLET ); G_AddEvent( enemy, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[STAYSOUNDSCRIPT] ) ); } } cs->weaponFireTimes[cs->weaponNum] = level.time; cs->animHitCount++; } } // if they are outside range, move forward AICast_PredictMovement( ecs, 2, 0.3, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 ); VectorSubtract( move.endpos, cs->bs->origin, vec ); vec[2] = 0; enemyDist = VectorLength( vec ); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if ( enemyDist > 8 ) { // we can get closer //if (!ent->client->ps.legsTimer) { // cs->castScriptStatus.scriptNoMoveTime = 0; trap_EA_MoveForward( cs->entityNum ); //} //ent->client->ps.legsTimer = 0; // allow legs us to move } return NULL; }
char *AIFunc_Heinrich_SwordKnockback(cast_state_t *cs) { gentity_t *ent = &g_entities[cs->entityNum]; trace_t *tr; vec3_t right, left; cs->aiFlags |= AIFL_SPECIAL_FUNC; if (cs->enemyNum < 0) { if (ent->client->ps.torsoTimer) { return NULL; } return AIFunc_DefaultStart(cs); } AICast_GetCastState(cs->enemyNum); if (ent->client->ps.torsoTimer < 500) { if (!ent->client->ps.legsTimer) { trap_EA_MoveForward(cs->entityNum); } ent->client->ps.legsTimer = 0; ent->client->ps.torsoTimer = 0; cs->castScriptStatus.scriptNoMoveTime = 0; AICast_Heinrich_Taunt(cs); return AIFunc_BattleChaseStart(cs); } // time for the melee? if (cs->enemyNum >= 0 && !(cs->aiFlags & AIFL_MISCFLAG1)) { // face them AICast_AimAtEnemy(cs); // keep checking for impact status tr = CheckMeleeAttack(ent, HEINRICH_KNOCKBACK_RANGE, qfalse); /* // do we need to move? if (!(tr && (tr->entityNum == cs->enemyNum))) { ent->client->ps.legsTimer = 0; cs->castScriptStatus.scriptNoMoveTime = 0; trap_EA_MoveForward(cs->entityNum); } */ // ready for damage? if (cs->thinkFuncChangeTime < level.time - HEINRICH_KNOCKBACK_DELAY) { cs->aiFlags |= AIFL_MISCFLAG1; // do melee damage if (tr && (tr->entityNum == cs->enemyNum)) { AngleVectors(cs->viewangles, NULL, right, NULL); VectorNegate(right, left); G_Damage(&g_entities[tr->entityNum], ent, ent, left, tr->endpos, HEINRICH_KNOCKBACK_DAMAGE, 0, MOD_GAUNTLET); // sound G_AddEvent(ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDIMPACT]); // throw them in direction of impact if ((ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT) == BG_AnimationIndexForString("attack2", cs->entityNum)) { // right right[2] = 0.5; VectorMA(g_entities[cs->enemyNum].client->ps.velocity, 400, right, g_entities[cs->enemyNum].client->ps.velocity); } else { // left left[2] = 0.5; VectorMA(g_entities[cs->enemyNum].client->ps.velocity, 400, left, g_entities[cs->enemyNum].client->ps.velocity); } } } } return NULL; }
/* ================ AIFunc_WarriorZombieMelee ================ */ char *AIFunc_WarriorZombieMelee( cast_state_t *cs ) { gentity_t *ent = &g_entities[cs->entityNum]; int hitDelay = -1, anim; trace_t *tr; cast_state_t *ecs = AICast_GetCastState( cs->enemyNum ); aicast_predictmove_t move; float enemyDist; if ( !ent->client->ps.torsoTimer ) { return AIFunc_DefaultStart( cs ); } // if ( cs->enemyNum < 0 ) { return NULL; } if ( ecs ) { anim = ( ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT ) - BG_AnimationIndexForString( "attack1", cs->entityNum ); if ( anim < 0 || anim >= NUM_WARRIOR_ANIMS ) { // animation interupted return AIFunc_DefaultStart( cs ); } if ( warriorHitTimes[anim][cs->animHitCount] >= 0 && cs->animHitCount < 3 ) { if ( !cs->animHitCount ) { hitDelay = warriorHitTimes[anim][cs->animHitCount]; } else { hitDelay = warriorHitTimes[anim][cs->animHitCount] - warriorHitTimes[anim][cs->animHitCount - 1]; } // check for inflicting damage if ( level.time - cs->weaponFireTimes[cs->weaponNum] > hitDelay ) { // do melee damage if ( ( tr = CheckMeleeAttack( ent, 44, qfalse ) ) && ( tr->entityNum == cs->enemyNum ) ) { G_Damage( &g_entities[tr->entityNum], ent, ent, vec3_origin, tr->endpos, warriorHitDamage[anim], 0, MOD_GAUNTLET ); G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[STAYSOUNDSCRIPT] ) ); } else { G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[FOLLOWSOUNDSCRIPT] ) ); } cs->weaponFireTimes[cs->weaponNum] = level.time; cs->animHitCount++; } } // face them AICast_AimAtEnemy( cs ); if ( anim < 3 ) { // back handed-swinging, dont allow legs to move // if they are outside range, move forward AICast_PredictMovement( ecs, 2, 0.5, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1 ); enemyDist = Distance( move.endpos, cs->bs->origin ); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if ( enemyDist > 16 ) { // we can get closer if ( ent->client->ps.legsTimer ) { ent->client->ps.legsTimer = 0; // allow legs us to move if ( cs->castScriptStatus.scriptNoMoveTime < level.time + 200 ) { // dont move until the legs are done lerping out of attack anim cs->castScriptStatus.scriptNoMoveTime = level.time + 200; } } if ( cs->castScriptStatus.scriptNoMoveTime < level.time ) { trap_EA_MoveForward( cs->entityNum ); } } } } return NULL; }