Beispiel #1
0
/*
============
AIChar_AIScript_AlertEntity

  triggered spawning, called from AI scripting
============
*/
void AIChar_AIScript_AlertEntity( gentity_t *ent ) {
    vec3_t mins, maxs;
    int numTouch, touch[10], i;
    cast_state_t    *cs;

    if ( !ent->aiInactive ) {
        return;
    }

    cs = AICast_GetCastState( ent->s.number );

    // if the current bounding box is invalid, then wait
    VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
    VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs );
    trap_UnlinkEntity( ent );

    numTouch = trap_EntitiesInBox( mins, maxs, touch, 10 );

    // check that another client isn't inside us
    if ( numTouch ) {
        for ( i = 0; i < numTouch; i++ ) {
            // RF, note we should only check against clients since zombies need to spawn inside func_explosive (so they dont clip into view after it explodes)
            if ( g_entities[touch[i]].client && g_entities[touch[i]].r.contents == CONTENTS_BODY ) {
                //if (g_entities[touch[i]].r.contents & MASK_PLAYERSOLID)
                break;
            }
        }
        if ( i == numTouch ) {
            numTouch = 0;
        }
    }

    if ( numTouch ) {
        // invalid location
        cs->aiFlags |= AIFL_WAITINGTOSPAWN;
        return;
    }

    // RF, has to disable this so I could test some maps which have erroneously placed alertentity calls
    //ent->AIScript_AlertEntity = NULL;
    cs->aiFlags &= ~AIFL_WAITINGTOSPAWN;
    ent->aiInactive = qfalse;
    trap_LinkEntity( ent );

    // trigger a spawn script event
    AICast_ScriptEvent( AICast_GetCastState( ent->s.number ), "spawn", "" );
    // make it think so we update animations/angles
    AICast_Think( ent->s.number, (float)FRAMETIME / 1000 );
    cs->lastThink = level.time;
    AICast_UpdateInput( cs, FRAMETIME );
    trap_BotUserCommand( cs->bs->client, &( cs->lastucmd ) );
}
// the trigger was just activated
// ent->activator should be set to the activator so it can be held through a delay
// so wait for the delay time before firing
void AICast_trigger_trigger(gentity_t *ent, gentity_t *activator)
{
	if(ent->nextthink)
	{
		return;     // can't retrigger until the wait is over
	}

	ent->activator = AICast_FindEntityForName(ent->aiName);

	if(ent->activator)      // they might be dead
	{
		// trigger the script event
		AICast_ScriptEvent(AICast_GetCastState(ent->activator->s.number), "trigger", ent->target);
	}

	if(ent->wait > 0)
	{
		ent->think = AICast_trigger_wait;
		ent->nextthink = level.time + (ent->wait + ent->random * crandom()) * 1000;
	}
	else
	{
		// we can't just remove (self) here, because this is a touch function
		// called while looping through area links...
		ent->touch = 0;
		ent->nextthink = level.time + FRAMETIME;
		ent->think = G_FreeEntity;
	}
}
Beispiel #3
0
/*
==============
AICast_SetupClient
==============
*/
int AICast_SetupClient(int client)
{
	cast_state_t	*cs;
	bot_state_t		*bs;

	if (!botstates[client]) {
		botstates[client] = G_Alloc(sizeof(bot_state_t));
		memset( botstates[client], 0, sizeof(bot_state_t) );
	}
	bs = botstates[client];

	if (bs->inuse) {
		BotAI_Print(PRT_FATAL, "client %d already setup\n", client);
		return qfalse;
	}

	cs = AICast_GetCastState(client);
	cs->bs = bs;

	//allocate a goal state
	bs->gs = trap_BotAllocGoalState(client);

	bs->inuse = qtrue;
	bs->client = client;
	bs->entitynum = client;
	bs->setupcount = qtrue;
	bs->entergame_time = trap_AAS_Time();
	bs->ms = trap_BotAllocMoveState();

	return qtrue;
}
Beispiel #4
0
/*
==============
AICast_UpdateNonVisibility
==============
*/
void AICast_UpdateNonVisibility( gentity_t *srcent, gentity_t *destent, qboolean directview ) {
	cast_visibility_t   *vis;
	cast_state_t        *cs;

	cs = AICast_GetCastState( srcent->s.number );

	vis = &cs->vislist[destent->s.number];

	// update the values
	vis->lastcheck_timestamp = level.time;
	vis->notvisible_timestamp = level.time;

	if ( directview ) {
		vis->real_update_timestamp = level.time;
		vis->real_notvisible_timestamp = level.time;
	}

	// if enough time has passed, and still within chase period, drop a marker
	if ( vis->chase_marker_count < MAX_CHASE_MARKERS ) {
		if ( ( level.time - vis->visible_timestamp ) > ( vis->chase_marker_count + 1 ) * CHASE_MARKER_INTERVAL ) {
			VectorCopy( destent->client->ps.origin, vis->chase_marker[vis->chase_marker_count] );
			vis->chase_marker_count++;
		}
	}
}
Beispiel #5
0
/*
==============
AICast_ShutdownClient
==============
*/
int AICast_ShutdownClient(int client)
{
	cast_state_t	*cs;
	bot_state_t *bs;

	if (!(bs = botstates[client])) {
		return BLERR_NOERROR;
	}
	if (!bs->inuse) {
		BotAI_Print(PRT_ERROR, "client %d already shutdown\n", client);
		return BLERR_AICLIENTALREADYSHUTDOWN;
	}

	cs = AICast_GetCastState( client );
	//
	memset( cs, 0, sizeof(cast_state_t) );
	numcast--;

	// now do the other bot stuff

#ifdef DEBUG
//	botai_import.DebugLineDelete(bs->debugline);
#endif //DEBUG

	trap_BotFreeMoveState(bs->ms);
	//free the goal state
	trap_BotFreeGoalState(bs->gs);
	//
	//clear the bot state
	memset(bs, 0, sizeof(bot_state_t));
	//set the inuse flag to qfalse
	bs->inuse = qfalse;
	//everything went ok
	return BLERR_NOERROR;
}
Beispiel #6
0
/*
==============
AICast_QueryThink
==============
*/
void AICast_QueryThink( cast_state_t *cs ) {
	gentity_t *ent;
	qboolean visible;
	cast_state_t *ocs;
	vec3_t vec;

	ent = &g_entities[cs->entityNum];
	ocs = AICast_GetCastState( cs->bs->enemy );

	// never crouch while in this state (by choice anyway)
	cs->bs->attackcrouch_time = 0;

	// look at where we last (thought we) saw them
	VectorSubtract( cs->vislist[cs->bs->enemy].visible_pos, cs->bs->origin, vec );
	VectorNormalize( vec );
	vectoangles( vec, cs->bs->ideal_viewangles );

	// are they visible now?
	visible = AICast_VisibleFromPos( cs->bs->origin, cs->entityNum, g_entities[cs->bs->enemy].r.currentOrigin, cs->bs->enemy, qfalse );

	// make sure we dont process the sighting of this enemy by going into query mode again, without them being visible again after we leave here
	cs->vislist[cs->bs->enemy].flags &= ~AIVIS_PROCESS_SIGHTING;

	// look towards where we last saw them
	AICast_AimAtEnemy( cs );

	// if visible and alert time has expired, go POSTAL
	if ( ( cs->queryAlertSightTime < 0 ) || ( ( cs->queryAlertSightTime < level.time ) && visible ) ) {
		if ( !cs->queryAlertSightTime ) {
			// set the "short reaction" condition
			BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse );
		}
		AICast_StateChange( cs, AISTATE_COMBAT );
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse );
		AIFunc_BattleStart( cs );
		return;
	}

	// if they've fired since the start of the query mode, go POSTAL
	if ( ocs->lastWeaponFired > cs->queryStartTime ) {
		// set the "short reaction" condition
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse );
		AICast_StateChange( cs, AISTATE_COMBAT );
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse );
		AIFunc_BattleStart( cs );
		return;
	}

	// if not visible, then kill the Lock On timer
	if ( ( cs->queryAlertSightTime > 0 ) && !visible ) {
		cs->queryAlertSightTime = 0;
	}

	// if the query has expired, go back to relaxed
	if ( !ent->client->ps.legsTimer ) {
		AICast_StateChange( cs, AISTATE_RELAXED );
	}
}
Beispiel #7
0
/*
===============
AICast_Activate
===============
*/
void AICast_Activate( int activatorNum, int entNum ) {
	cast_state_t *cs;

	cs = AICast_GetCastState( entNum );
	if (cs->activate) {
		cs->activate( entNum, activatorNum );
	}

	AICast_Printf( AICAST_PRT_DEBUG, "activated entity # %i\n", entNum );
}
Beispiel #8
0
/*
===============
AICast_AgePlayTime
===============
*/
void AICast_AgePlayTime( int entnum ) {
	cast_state_t *cs = AICast_GetCastState(entnum);
	//
	if ((level.time - cs->lastLoadTime) > 100) {
		if ((level.time - cs->lastLoadTime) < 1000) {
			cs->totalPlayTime += level.time - cs->lastLoadTime;
		}
		//
		cs->lastLoadTime = level.time;
	}
}
Beispiel #9
0
/*
================
AICast_NoFlameDamage
================
*/
qboolean AICast_NoFlameDamage( int entNum ) {
	cast_state_t *cs;

	if (entNum >= MAX_CLIENTS)
		return qfalse;

	// DHM - Nerve :: Not in multiplayer
	if ( g_gametype.integer != GT_SINGLE_PLAYER )
		return qfalse;

	cs = AICast_GetCastState( entNum );
	return ((cs->aiFlags & AIFL_NO_FLAME_DAMAGE) != 0);
}
Beispiel #10
0
/*
============
AICast_Sight
============
*/
void AICast_Sight( gentity_t *ent, gentity_t *other, int lastSight ) {
	cast_state_t    *cs, *ocs;

	cs = AICast_GetCastState( ent->s.number );
	ocs = AICast_GetCastState( other->s.number );

	//
	// call the sightfunc for this cast, so we can play associated sounds, or do any character-specific things
	//
	if ( cs->sightfunc ) {
		// factor in the reaction time
		if ( AICast_EntityVisible( cs, other->s.number, qfalse ) ) {
			cs->sightfunc( ent, other, lastSight );
		}
	}

	if ( other->aiName && other->health <= 0 ) {

		// they died since we last saw them
		if ( ocs->deathTime > lastSight ) {
			if ( !AICast_SameTeam( cs, other->s.number ) ) {
				AICast_ScriptEvent( cs, "enemysightcorpse", other->aiName );
			} else if ( !( cs->castScriptStatus.scriptFlags & SFL_FRIENDLYSIGHTCORPSE_TRIGGERED ) ) {
				cs->castScriptStatus.scriptFlags |= SFL_FRIENDLYSIGHTCORPSE_TRIGGERED;
				AICast_ScriptEvent( cs, "friendlysightcorpse", "" );
			}
		}

		// if this is the first time, call the sight script event
	} else if ( !lastSight && other->aiName ) {
		if ( !AICast_SameTeam( cs, other->s.number ) ) {
			// disabled.. triggered when entering combat mode
			//AICast_ScriptEvent( cs, "enemysight", other->aiName );
		} else {
			AICast_ScriptEvent( cs, "sight", other->aiName );
		}
	}
}
Beispiel #11
0
/*
============
AICast_Pain
============
*/
void AICast_Pain( gentity_t *targ, gentity_t *attacker, int damage, vec3_t point ) {
	cast_state_t    *cs;

	cs = AICast_GetCastState( targ->s.number );

	// print debugging message
	if ( aicast_debug.integer == 2 && attacker->s.number == 0 ) {
		G_Printf( "hit %s %i\n", targ->aiName, targ->health );
	}

	// if we are below alert mode, then go there immediately
	if ( cs->aiState < AISTATE_ALERT ) {
		AICast_StateChange( cs, AISTATE_ALERT );
	}

	if ( cs->aiFlags & AIFL_NOPAIN ) {
		return;
	}

	// process the event (turn to face the attacking direction? go into hide/retreat state?)
	// need to weigh up the situation, but foremost, an inactive AI cast should always react in some way to being hurt
	cs->lastPain = level.time;

	// record the sighting (FIXME: silent weapons shouldn't do this, but the AI should react in some way)
	if ( attacker->client ) {
		AICast_UpdateVisibility( targ, attacker, qtrue, qtrue );
	}

	// if either of us are neutral, then we are now enemies
	if ( targ->aiTeam == AITEAM_NEUTRAL || attacker->aiTeam == AITEAM_NEUTRAL ) {
		cs->vislist[attacker->s.number].flags |= AIVIS_ENEMY;
	}

	AICast_ScriptEvent( cs, "pain", va( "%d %d", targ->health, targ->health + damage ) );

	if ( cs->aiFlags & AIFL_DENYACTION ) {
		// dont play any sounds
		return;
	}

	//
	// call the painfunc for this cast, so we can play associated sounds, or do any character-specific things
	//
	if ( cs->painfunc ) {
		cs->painfunc( targ, attacker, damage, point );
	}
}
Beispiel #12
0
/*QUAKED target_script_trigger (1 .7 .2) (-8 -8 -8) (8 8 8)
must have an aiName
must have a target

when used it will fire its targets 
*/
void target_script_trigger_use (gentity_t *ent, gentity_t *other, gentity_t *activator )
{
	gentity_t	*player;

	if (ent->aiName) {
		player = AICast_FindEntityForName("player");
		if (player)
			AICast_ScriptEvent( AICast_GetCastState(player->s.number), "trigger", ent->target );
	}

	// DHM - Nerve :: In multiplayer, we use the brush scripting only
	if ( g_gametype.integer >= GT_WOLF && ent->scriptName ) {
		G_Script_ScriptEvent( ent, "trigger", ent->target );
	}

	G_UseTargets ( ent, other);	

}
Beispiel #13
0
/*
===============
AICast_AgePlayTime
===============
*/
void AICast_AgePlayTime( int entnum ) {
    cast_state_t *cs = AICast_GetCastState( entnum );
    //
    if ( saveGamePending ) {
        return;
    }
//	if (reloading)
    if ( g_reloading.integer ) {
        return;
    }
    //
    if ( ( level.time - cs->lastLoadTime ) > 1000 ) {
        if ( /*(level.time - cs->lastLoadTime) < 2000 &&*/ ( level.time - cs->lastLoadTime ) > 0 ) {
            cs->totalPlayTime += level.time - cs->lastLoadTime;
            trap_Cvar_Set( "g_totalPlayTime", va( "%i", cs->totalPlayTime ) );
        }
        //
        cs->lastLoadTime = level.time;
    }
}
Beispiel #14
0
/*
================
AICast_SetFlameDamage
================
*/
void AICast_SetFlameDamage( int entNum, qboolean status ) {
    cast_state_t *cs;

    if ( entNum >= MAX_CLIENTS ) {
        return;
    }

    // DHM - Nerve :: Not in multiplayer
    if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
        return;
    }

    cs = AICast_GetCastState( entNum );

    if ( status ) {
        cs->aiFlags |= AIFL_NO_FLAME_DAMAGE;
    } else {
        cs->aiFlags &= ~AIFL_NO_FLAME_DAMAGE;
    }
}
Beispiel #15
0
/*
=================
G_ScriptAction_Trigger

  syntax: trigger <aiName/scriptName> <trigger>

  Calls the specified trigger for the given ai character or script entity
=================
*/
qboolean G_ScriptAction_Trigger( gentity_t *ent, const char* params ) {
	gentity_t *trent;
    const char* pString;
    char* token;
    char name[MAX_QPATH];
    char trigger[MAX_QPATH];
	int oldId;

	// get the cast name
	pString = params;
	token = COM_ParseExt( &pString, qfalse );
	Q_strncpyz( name, token, sizeof( name ) );
	if ( !name[0] ) {
		G_Error( "G_Scripting: trigger must have a name and an identifier\n" );
	}

	token = COM_ParseExt( &pString, qfalse );
	Q_strncpyz( trigger, token, sizeof( trigger ) );
	if ( !trigger[0] ) {
		G_Error( "G_Scripting: trigger must have a name and an identifier\n" );
	}

	trent = AICast_FindEntityForName( name );
	if ( trent ) { // we are triggering an AI
				  //oldId = trent->scriptStatus.scriptId;
		AICast_ScriptEvent( AICast_GetCastState( trent->s.number ), "trigger", trigger );
		return qtrue;
	}

	// look for an entity
	trent = G_Find( &g_entities[MAX_CLIENTS], FOFS( scriptName ), name );
	if ( trent ) {
		oldId = trent->scriptStatus.scriptId;
		G_Script_ScriptEvent( trent, "trigger", trigger );
		// if the script changed, return false so we don't muck with it's variables
		return ( ( trent != ent ) || ( oldId == trent->scriptStatus.scriptId ) );
	}

	G_Error( "G_Scripting: trigger has unknown name: %s\n", name );
	return qfalse;  // shutup the compiler
}
Beispiel #16
0
/*
============
AICast_CreateCharacter

  returns 0 if unable to create the character
============
*/
gentity_t *AICast_CreateCharacter( gentity_t *ent, float *attributes, cast_weapon_info_t *weaponInfo, char *castname, char *model, char *head, char *sex, char *color, char *handicap )
{
	gentity_t		*newent;
	gclient_t		*client;
	cast_state_t	*cs;
	char			**ppStr;
	int j;

	if (g_gametype.integer != GT_SINGLE_PLAYER)
	{	// no cast AI in multiplayer
		return NULL;
	}
	// are bots enabled?
	if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
		G_Printf( S_COLOR_RED "ERROR: Unable to spawn %s, 'bot_enable' is not set\n", ent->classname );
		return NULL;
	}
	//
	// make sure we have a free slot for them
	//
	if (level.numPlayingClients+1 > aicast_maxclients)
	{
		G_Error( "Exceeded sv_maxclients (%d), unable to create %s\n", aicast_maxclients, ent->classname );
		return NULL;
	}
	//
	// add it to the list (only do this if everything else passed)
	//

	newent = AICast_AddCastToGame( ent, castname, model, head, sex, color, handicap );

	if (!newent) {
		return NULL;
	}
	client = newent->client;
	//
	// setup the character..
	//
	cs = AICast_GetCastState( newent->s.number );
	//
	// setup the attributes
	memcpy( cs->attributes, attributes, sizeof(cs->attributes) );
	ppStr = &ent->aiAttributes;
	AICast_CheckLevelAttributes( cs, ent, ppStr );
	//
	AICast_SetAASIndex( cs );
	// make sure they face the right direction
	VectorCopy( ent->s.angles, cs->bs->ideal_viewangles );
	// factor in the delta_angles
	for (j = 0; j < 3; j++) {
		cs->bs->viewangles[j] = AngleMod(newent->s.angles[j] - SHORT2ANGLE(newent->client->ps.delta_angles[j]));
	}
	VectorCopy( ent->s.angles, newent->s.angles );
	VectorCopy( ent->s.origin, cs->startOrigin );
	//
	cs->lastEnemy = -1;
	cs->bs->enemy = -1;
	cs->leaderNum = -1;
	cs->castScriptStatus.scriptGotoEnt = -1;
	cs->aiCharacter = ent->aiCharacter;
	//
	newent->aiName = ent->aiName;
	newent->aiTeam = ent->aiTeam;
	newent->targetname = ent->targetname;
	//
	newent->AIScript_AlertEntity = ent->AIScript_AlertEntity;
	newent->aiInactive = ent->aiInactive;
	newent->aiCharacter = cs->aiCharacter;
	//
	// parse the AI script for this character (if applicable)
	cs->aiFlags |= AIFL_CORPSESIGHTING;		// this is on by default for all characters, disabled if they have a "friendlysightcorpse" script event
	AICast_ScriptParse( cs );
	//
	// setup bounding boxes
	//VectorCopy( mins, client->ps.mins );
	//VectorCopy( maxs, client->ps.maxs );
	AIChar_SetBBox( newent, cs );
	client->ps.friction = cs->attributes[RUNNING_SPEED]/300.0;
	//
	// clear weapons/ammo
	client->ps.weapon = 0;
	memcpy( client->ps.weapons, weaponInfo->startingWeapons, sizeof(weaponInfo->startingWeapons) );
	memcpy( client->ps.ammo, weaponInfo->startingAmmo, sizeof(client->ps.ammo) );
	//
	// starting health
	if (ent->health) {
		newent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = ent->health;
	} else {
		newent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = cs->attributes[STARTING_HEALTH];
	}
	//
	cs->weaponInfo = weaponInfo;
	//
	cs->lastThink = level.time;
	//
	newent->pain = AICast_Pain;
	newent->die = AICast_Die;
	//
	//update the attack inventory values
	AICast_UpdateBattleInventory(cs, cs->bs->enemy);

//----(SA)	make sure all clips are loaded so we don't hear everyone loading up
//			(we don't want to do this inside AICast_UpdateBattleInventory(), only on spawn or giveweapon)
	for (j=0; j<MAX_WEAPONS; j++) {
		Fill_Clip (&client->ps, j);
	}
//----(SA)	end

	// select a weapon
	AICast_ChooseWeapon( cs, qfalse );

	//
	// set the default function, overwrite if necessary
	cs->aiFlags |= AIFL_JUST_SPAWNED;
	AIFunc_DefaultStart( cs );
	//
	numcast++;
	//
	return newent;
}
Beispiel #17
0
/*
==============
AICast_PlayTime
==============
*/
int AICast_PlayTime( int entnum ) {
    cast_state_t *cs = AICast_GetCastState( entnum );
    return ( cs->totalPlayTime );
}
Beispiel #18
0
const char *AIFunc_Heinrich_SwordSideSlash( 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_SLASH_RANGE, qfalse );
		// ready for damage?
		if ( cs->thinkFuncChangeTime < level.time - HEINRICH_SLASH_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_SLASH_DAMAGE, 0, MOD_GAUNTLET );
				// sound
				G_AddEvent( ent, EV_GENERAL_SOUND, heinrichSoundIndex[HEINRICH_SWORDIMPACT] );
				// throw them in direction of impact
				left[2] = 0.5;
				VectorMA( g_entities[cs->enemyNum].client->ps.velocity, 400, left, g_entities[cs->enemyNum].client->ps.velocity );
			}
		}
	}

	// 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;
}
Beispiel #19
0
/*
================
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;
}
Beispiel #20
0
void AICast_StartFrame( int time ) {
	int i, elapsed, count, clCount;
	cast_state_t    *cs;
	int castcount;
	static int lasttime;
	static vmCvar_t aicast_disable;
	gentity_t *ent;

	if ( trap_Cvar_VariableIntegerValue( "savegame_loading" ) ) {
		return;
	}

	if ( saveGamePending ) {
		return;
	}

	// if waiting at intermission, don't think
	if ( strlen( g_missionStats.string ) > 1 ) {
		return;
	}

	if ( !aicast_disable.handle ) {
		trap_Cvar_Register( &aicast_disable, "aicast_disable", "0", CVAR_CHEAT );
	} else
	{
		trap_Cvar_Update( &aicast_disable );
		if ( aicast_disable.integer ) {
			return;
		}
	}

	trap_Cvar_Update( &aicast_debug );
	trap_Cvar_Update( &aicast_debugname );
	trap_Cvar_Update( &aicast_scripts );

	// no need to think during the intermission
	if ( level.intermissiontime ) {
		return;
	}
	//
	// make sure the AAS gets updated
	trap_BotLibStartFrame( (float) time / 1000 );
	//
	//
	elapsed = time - lasttime;
	if ( elapsed == 0 ) {
		return;         // no time has elapsed
	}

//G_Printf( "AI startframe: %i\n", time );

	if ( elapsed < 0 ) {
		elapsed = 0;
		lasttime = time;
	}
	// don't let the framerate drop below 10
	if ( elapsed > 100 ) {
		elapsed = 100;
	}
	//AICast_SightUpdate( (int)((float)SIGHT_PER_SEC * ((float)elapsed / 1000)) );
	//
	count = 0;
	castcount = 0;
	clCount = 0;
	ent = g_entities;
	//
	//update the AI characters
	// TTimo gcc: left-hand operand of comma expression has no effect
	// initial line was: for (i = 0; i < aicast_maxclients, clCount < level.numPlayingClients; i++, ent++)
	for ( i = 0; ( i < aicast_maxclients ) && ( clCount < level.numPlayingClients ) ; i++, ent++ )
	{
		if ( ent->client ) {
			clCount++;
		}
		//
		cs = AICast_GetCastState( i );
		// is this a cast AI?
		if ( cs->bs ) {
			if ( ent->inuse ) {
				if ( ent->aiInactive == qfalse ) {
					//
					elapsed = time - cs->lastThink;
					//
					// if they're moving/firing think every frame
					if ( ( elapsed >= 50 ) &&
						 (   (   (   ( !VectorCompare( ent->client->ps.velocity, vec3_origin ) ) ||
									 ( ent->client->buttons ) ||
									 ( elapsed >= aicast_thinktime ) ) &&
								 ( count <= aicast_maxthink ) ) ||
							 ( elapsed >= aicast_thinktime * 2 ) ) ) {
						// make it think now
						AICast_Think( i, (float)elapsed / 1000 );
						cs->lastThink = time;
						//
						count++;
					}
					// check for any debug info updates
					AICast_DebugFrame( cs );
				} else if ( cs->aiFlags & AIFL_WAITINGTOSPAWN ) {
					// check f the space is clear yet
					ent->AIScript_AlertEntity( ent );
				}
			} else {
				trap_UnlinkEntity( ent );
			}
			//
			// see if we've checked all cast AI's
			if ( ++castcount >= numcast ) {
				break;
			}
		}
	}
	//
	lasttime = time;
}
Beispiel #21
0
/*
============
AICast_StartServerFrame

  Do movements, sighting, etc
============
*/
void AICast_StartServerFrame( int time ) {
	int i, elapsed, count, clCount;
	cast_state_t    *cs;
	int castcount;
	static int lasttime;
	static vmCvar_t aicast_disable;
	gentity_t *ent;
	cast_state_t *pcs;
//	int		oldLegsTimer;

	if ( trap_Cvar_VariableIntegerValue( "savegame_loading" ) ) {
		return;
	}

	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
		return;
	}

	if ( saveGamePending ) {
		return;
	}

	// if waiting at intermission, don't think
	if ( strlen( g_missionStats.string ) > 1 ) {
		return;
	}

	if ( !aicast_disable.handle ) {
		trap_Cvar_Register( &aicast_disable, "aicast_disable", "0", CVAR_CHEAT );
	} else
	{
		trap_Cvar_Update( &aicast_disable );
		if ( aicast_disable.integer ) {
			return;
		}
	}

	trap_Cvar_Update( &aicast_debug );

	// no need to think during the intermission
	if ( level.intermissiontime ) {
		return;
	}
	//
	// make sure the AAS gets updated
	trap_BotLibStartFrame( (float) time / 1000 );
	//
	//
	elapsed = time - lasttime;
	if ( elapsed == 0 ) {
		return;         // no time has elapsed
	}

	pcs = AICast_GetCastState( 0 );

//G_Printf( "AI startserverframe: %i\n", time );

	if ( elapsed < 0 ) {
		elapsed = 0;
		lasttime = time;
	}
	// don't let the framerate drop below 10
	if ( elapsed > 100 ) {
		elapsed = 100;
	}
	//
	// process player's current script if it exists
	AICast_ScriptRun( AICast_GetCastState( 0 ), qfalse );
	//
	AICast_SightUpdate( (int)( (float)SIGHT_PER_SEC * ( (float)elapsed / 1000 ) ) );
	//
	count = 0;
	castcount = 0;
	clCount = 0;
	ent = g_entities;
	//
	//update the AI characters
	// TTimo gcc: left-hand operand of comma expression has no effect
	// initial line: for (i = 0; i < aicast_maxclients, clCount < level.numPlayingClients; i++, ent++)
	for ( i = 0; ( i < aicast_maxclients ) && ( clCount < level.numPlayingClients ) ; i++, ent++ )
	{
		if ( ent->client ) {
			clCount++;
		}
		//
		cs = AICast_GetCastState( i );
		// is this a cast AI?
		if ( cs->bs ) {
			if ( ent->aiInactive == qfalse && ent->inuse ) {
				//
				elapsed = level.time - cs->lastMoveThink;
				//
				// optimization, if they're not in the player's PVS, and they aren't trying to move, then don't bother thinking
				if (    ( ( ent->health > 0 ) && ( elapsed > 300 ) )
						||  ( g_entities[0].client && g_entities[0].client->cameraPortal )
						||  ( cs->vislist[0].visible_timestamp == cs->vislist[0].lastcheck_timestamp )
						||  ( pcs->vislist[cs->entityNum].visible_timestamp == pcs->vislist[cs->entityNum].lastcheck_timestamp )
						||  ( VectorLength( ent->client->ps.velocity ) > 0 )
						||  ( cs->bs->lastucmd.forwardmove || cs->bs->lastucmd.rightmove || cs->bs->lastucmd.upmove > 0 || cs->bs->lastucmd.buttons || cs->bs->lastucmd.wbuttons )
						||  ( trap_InPVS( cs->bs->origin, g_entities[0].s.pos.trBase ) ) ) { // do pvs check last, since it's the most expensive to call
//					oldLegsTimer = ent->client->ps.legsTimer;
					//
					// send it's movement commands
					//
					serverTime = time;
					AICast_UpdateInput( cs, elapsed );
					trap_BotUserCommand( cs->bs->client, &( cs->bs->lastucmd ) );
					cs->lastMoveThink = level.time;
					//
					// check for anim changes that may require us to stay still
					//
/*					if (oldLegsTimer != ent->client->ps.legsTimer) {
						// dont move until they are finished
						if (cs->castScriptStatus.scriptNoMoveTime < level.time + ent->client->ps.legsTimer) {
							cs->castScriptStatus.scriptNoMoveTime = level.time + ent->client->ps.legsTimer;
						}
					}
*/              }
			} else {
				trap_UnlinkEntity( ent );
			}
			//
			// see if we've checked all cast AI's
			if ( ++castcount >= numcast ) {
				break;
			}
		}
	}
	//
	lasttime = time;
}
Beispiel #22
0
/*
============
AICast_Think

  entry point for all cast AI
============
*/
void AICast_Think( int client, float thinktime ) {
	gentity_t       *ent;
	cast_state_t    *cs;
	int i;
	int animIndex;
	animation_t     *anim;

//	if (saveGamePending || (strlen( g_missionStats.string ) > 2 )) {
//		return;
//	}

	//
	// get the cast ready for processing
	//
	cs = AICast_GetCastState( client );
	ent = &g_entities[client];
	//
	// make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data)
	trap_AAS_SetCurrentWorld( cs->aasWorldIndex );
	//
	// make sure we have a valid navigation system
	//
	if ( !trap_AAS_Initialized() ) {
		return;
	}
	//
	trap_EA_ResetInput( client, NULL );
	cs->aiFlags &= ~AIFL_VIEWLOCKED;
	//cs->bs->weaponnum = ent->client->ps.weapon;
	//
	// turn off flags that are set each frame if needed
	ent->client->ps.eFlags &= ~( EF_NOSWINGANGLES | EF_MONSTER_EFFECT | EF_MONSTER_EFFECT2 | EF_MONSTER_EFFECT3 );
	// conditional flags
	if ( ent->aiCharacter == AICHAR_ZOMBIE ) {
		if ( COM_BitCheck( ent->client->ps.weapons, WP_MONSTER_ATTACK1 ) ) {
			cs->aiFlags |= AIFL_NO_FLAME_DAMAGE;
			SET_FLAMING_ZOMBIE( ent->s, 1 );
		} else {
			SET_FLAMING_ZOMBIE( ent->s, 0 );
		}
	}
	//
	// if we're dead, do special stuff only
	if ( ent->health <= 0 || cs->revivingTime || cs->rebirthTime ) {
		//
		if ( cs->revivingTime && cs->revivingTime < level.time ) {
			// start us thinking again
			ent->client->ps.pm_type = PM_NORMAL;
			cs->revivingTime = 0;
		}
		//
		if ( cs->rebirthTime && cs->rebirthTime < level.time ) {
			vec3_t mins, maxs;
			int touch[10], numTouch;
			float oldmaxZ;

			oldmaxZ = ent->r.maxs[2];

			// make sure the area is clear
			AIChar_SetBBox( ent, cs );

			VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
			VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs );
			trap_UnlinkEntity( ent );

			numTouch = trap_EntitiesInBox( mins, maxs, touch, 10 );

			if ( numTouch ) {
				for ( i = 0; i < numTouch; i++ ) {
					//if (!g_entities[touch[i]].client || g_entities[touch[i]].r.contents == CONTENTS_BODY)
					if ( g_entities[touch[i]].r.contents & MASK_PLAYERSOLID ) {
						break;
					}
				}
				if ( i == numTouch ) {
					numTouch = 0;
				}
			}

			if ( numTouch == 0 ) {    // ok to spawn

				// give them health when they start reviving, so we won't gib after
				// just a couple shots while reviving
				ent->health =
					ent->client->ps.stats[STAT_HEALTH] =
						ent->client->ps.stats[STAT_MAX_HEALTH] =
							( ( cs->attributes[STARTING_HEALTH] - 50 ) > 30 ? ( cs->attributes[STARTING_HEALTH] - 50 ) : 30 );

				ent->r.contents = CONTENTS_BODY;
				ent->clipmask = MASK_PLAYERSOLID;
				ent->takedamage = qtrue;
				ent->waterlevel = 0;
				ent->watertype = 0;
				ent->flags = 0;
				ent->die = AICast_Die;
				ent->client->ps.eFlags &= ~EF_DEAD;
				ent->s.eFlags &= ~EF_DEAD;

				cs->rebirthTime = 0;
				cs->deathTime = 0;

				// play the revive animation
				cs->revivingTime = level.time + BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_REVIVE, qfalse, qtrue );;
			} else {
				// can't spawn yet, so set bbox back, and wait
				ent->r.maxs[2] = oldmaxZ;
				ent->client->ps.maxs[2] = ent->r.maxs[2];
			}
			trap_LinkEntity( ent );
		}
		// ZOMBIE should set effect flag if really dead
		if ( cs->aiCharacter == AICHAR_ZOMBIE && !ent->r.contents ) {
			ent->client->ps.eFlags |= EF_MONSTER_EFFECT2;
		}
		//
		if ( ent->health > GIB_HEALTH && cs->deathTime && cs->deathTime < ( level.time - 3000 ) ) {
/*
			// been dead for long enough, set our animation to the end frame
			switch ( ent->s.legsAnim & ~ANIM_TOGGLEBIT ) {
			case BOTH_DEATH1:
			case BOTH_DEAD1:
				anim = BOTH_DEAD1;
				break;
			case BOTH_DEATH2:
			case BOTH_DEAD2:
				anim = BOTH_DEAD2;
				break;
			case BOTH_DEATH3:
			case BOTH_DEAD3:
				anim = BOTH_DEAD3;
				break;
			default:
				G_Error( "%s has unknown death animation\n", ent->classname);
			}
			ent->client->ps.torsoAnim = ( ( ent->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
			ent->client->ps.legsAnim = ( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
*/
			cs->deathTime = 0;
			ent->r.svFlags &= ~SVF_BROADCAST;
		}
		//
		// no more thinking required
		return;
	}
	//
	// set some anim conditions
	if ( cs->secondDeadTime ) {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qtrue, qfalse );
	} else {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qfalse, qfalse );
	}
	// set health value
	if ( ent->health <= 0.25 * cs->attributes[STARTING_HEALTH] ) {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 3, qfalse );
	} else if ( ent->health <= 0.5 * cs->attributes[STARTING_HEALTH] ) {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 2, qfalse );
	} else {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 1, qfalse );
	}
	//
	cs->speedScale = 1.0;           // reset each frame, set if required
	cs->actionFlags = 0;            // FIXME: move this to a Cast AI movement init function!
	//retrieve the current client state
	BotAI_GetClientState( client, &( cs->bs->cur_ps ) );
	//
	// setup movement speeds for the given state
	// walking
	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALK );
	if ( animIndex >= 0 ) {
		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
		cs->attributes[WALKING_SPEED] = anim->moveSpeed;
	}
	// crouching
	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALKCR );
	if ( animIndex >= 0 ) {
		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
		cs->attributes[CROUCHING_SPEED] = anim->moveSpeed;
	}
	// running
	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_RUN );
	if ( animIndex >= 0 ) {
		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
		cs->attributes[RUNNING_SPEED] = anim->moveSpeed;
	}
	// update crouch speed scale
	ent->client->ps.crouchSpeedScale = cs->attributes[CROUCHING_SPEED] / cs->attributes[RUNNING_SPEED];
	//
	// only enable headlook if we want to this frame
	ent->client->ps.eFlags &= ~EF_HEADLOOK;
	if ( cs->bs->enemy >= 0 ) {
		ent->client->ps.eFlags &= ~EF_STAND_IDLE2;  // never use alt idle if fighting
	}
	//
	// check for dead leader
	if ( cs->leaderNum >= 0 && g_entities[cs->leaderNum].health <= 0 ) {
		cs->leaderNum = -1;
	}
	//
#if 0
	// HACK for village2, if they are stuck, find a good position (there is a friendly guy placed inside a table)
	{
		trace_t tr;
		vec3_t org;
		trap_Trace( &tr, cs->bs->cur_ps.origin, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, cs->bs->cur_ps.origin, cs->entityNum, CONTENTS_SOLID );
		while ( tr.startsolid ) {
			VectorCopy( cs->bs->cur_ps.origin, org );
			org[0] += 96 * crandom();
			org[1] += 96 * crandom();
			org[2] += 16 * crandom();
			trap_Trace( &tr, org, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, org, cs->entityNum, CONTENTS_SOLID );
			G_SetOrigin( &g_entities[cs->entityNum], org );
			VectorCopy( org, g_entities[cs->entityNum].client->ps.origin );
		}
	}
#endif
	//add the delta angles to the cast's current view angles
	for ( i = 0; i < 3; i++ ) {
		cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] + SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) );
	}
	//
	//increase the local time of the cast
	cs->bs->ltime += thinktime;
	//
	cs->bs->thinktime = thinktime;
	//origin of the cast
	VectorCopy( cs->bs->cur_ps.origin, cs->bs->origin );
	//eye coordinates of the cast
	VectorCopy( cs->bs->cur_ps.origin, cs->bs->eye );
	cs->bs->eye[2] += cs->bs->cur_ps.viewheight;
	//get the area the cast is in
	cs->bs->areanum = BotPointAreaNum( cs->bs->origin );
	// clear flags each frame
	cs->bs->flags = 0;
	//
	// check enemy health
	if ( cs->bs->enemy >= 0 && g_entities[cs->bs->enemy].health <= 0 ) {
		cs->bs->enemy = -1;
	}
	//
	// if the previous movetype was temporary, set it back
	if ( cs->movestateType == MSTYPE_TEMPORARY ) {
		cs->movestate = MS_DEFAULT;
		cs->movestateType = MSTYPE_NONE;
	}
	// crouching?
	if (    ( cs->bs->attackcrouch_time > trap_AAS_Time() ) &&
			( ( cs->lastAttackCrouch > level.time - 500 ) || ( cs->thinkFuncChangeTime < level.time - 1000 ) ) ) {
		// if we are not moving, and we are firing, always stand, unless we are allowed to crouch + fire
		if ( VectorLength( cs->bs->cur_ps.velocity ) || ( cs->lastWeaponFired < level.time - 2000 ) || ( cs->aiFlags & AIFL_ATTACK_CROUCH ) ) {
			cs->lastAttackCrouch = level.time;
			trap_EA_Crouch( cs->bs->client );
		}
	}
	//
	//if (cs->bs->enemy >= 0) {
	//update the attack inventory values
	AICast_UpdateBattleInventory( cs, cs->bs->enemy );
	//}
	//
	// if we don't have ammo for the current weapon, get rid of it
	if ( !( COM_BitCheck( cs->bs->cur_ps.weapons, cs->bs->weaponnum ) ) || !AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum ) ) {
		// select a weapon
		AICast_ChooseWeapon( cs, qfalse );
		// if still no ammo, select a blank weapon
		//if (!AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum )) {
		//	cs->bs->weaponnum = WP_NONE;
		//}
	}
	//
	// in query mode, we do special handling (pause scripting, check for transition to alert/combat, etc)
	if ( cs->aiState == AISTATE_QUERY ) {
		AICast_QueryThink( cs );
	} else if ( cs->pauseTime < level.time )     {
		// do the thinking
		AICast_ProcessAIFunctions( cs, thinktime );
		//
		// make sure the correct weapon is selected
		trap_EA_SelectWeapon( cs->bs->client, cs->bs->weaponnum );
		//
		// process current script if it exists
		cs->castScriptStatusCurrent = cs->castScriptStatus;
		AICast_ScriptRun( cs, qfalse );
	}
	//
	// set special movestate if necessary
	if ( cs->movestateType != MSTYPE_NONE ) {
		switch ( cs->movestate ) {
		case MS_WALK:
			cs->actionFlags |= CASTACTION_WALK;
			break;
		case MS_CROUCH:
			trap_EA_Crouch( cs->entityNum );
			break;
		default:
			break; // TTimo gcc: MS_DEFAULT MS_RUN not handled in switch
		}
	}
	//
	//subtract the delta angles
	for ( i = 0; i < 3; i++ ) {
		cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] - SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) );
	}
}
Beispiel #23
0
const char *AIFunc_Heinrich_Earthquake( cast_state_t *cs ) {
	gentity_t   *ent = &g_entities[cs->entityNum];
	gentity_t   *enemy;
	cast_state_t *ecs;
	vec3_t enemyVec;
	float enemyDist, scale;
	trace_t *tr;

	cs->aiFlags |= AIFL_SPECIAL_FUNC;

	if ( cs->enemyNum < 0 ) {
		if ( !ent->client->ps.torsoTimer ) {
			return AIFunc_DefaultStart( cs );
		}
		return NULL;
	}

	enemy = &g_entities[cs->enemyNum];
	ecs = AICast_GetCastState( cs->enemyNum );

	VectorMA( enemy->r.currentOrigin, HEINRICH_STOMP_DELAY, enemy->client->ps.velocity, enemyVec );
	enemyDist = VectorDistance( ent->r.currentOrigin, enemyVec );

	if ( ent->client->ps.torsoTimer < 500 ) {
		int rnd;
		aicast_predictmove_t move;
		vec3_t vec;

		AICast_PredictMovement( ecs, 2, 0.5, &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 < 140 ) {
			// combo attack
			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 );
			}
		} else {    // back to roaming
			ent->client->ps.legsTimer = 0;
			ent->client->ps.torsoTimer = 0;
			cs->castScriptStatus.scriptNoMoveTime = 0;
			AICast_Heinrich_Taunt( cs );
			return AIFunc_DefaultStart( cs );
		}
	}

	// time for the thump?
	if ( !( cs->aiFlags & AIFL_MISCFLAG1 ) ) {
		// face them
		AICast_AimAtEnemy( cs );
		// ready for damage?
		if ( cs->thinkFuncChangeTime < level.time - HEINRICH_STOMP_DELAY ) {
			cs->aiFlags |= AIFL_MISCFLAG1;
			// play the stomp sound
			G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[ent->aiCharacter].soundScripts[ORDERSDENYSOUNDSCRIPT] ) );
			// check for striking the player
			tr = CheckMeleeAttack( ent, 70, qfalse );
			// do melee damage
			if ( tr && ( tr->entityNum == cs->enemyNum ) ) {
				G_Damage( &g_entities[tr->entityNum], ent, ent, vec3_origin, tr->endpos, HEINRICH_STOMP_DAMAGE, 0, MOD_GAUNTLET );
			}
			// call the debris trigger
			AICast_ScriptEvent( cs, "trigger", "quake" );
		}
	}

	enemyDist = Distance( enemy->s.pos.trBase, ent->s.pos.trBase );

	// do the earthquake effects
	if ( cs->thinkFuncChangeTime < level.time - HEINRICH_STOMP_DELAY ) {
		// throw the player into the air, if they are on the ground
		if ( ( enemy->s.groundEntityNum != ENTITYNUM_NONE ) && enemyDist < HEINRICH_STOMP_RANGE ) {
			scale = 0.5 + 0.5 * ( (float)ent->client->ps.torsoTimer / 1000.0 );
			if ( scale > 1.0 ) {
				scale = 1.0;
			}
			VectorSubtract( ent->s.pos.trBase, enemy->s.pos.trBase, enemyVec );
			VectorScale( enemyVec, 2.0 * ( 0.6 + 0.5 * random() ) * scale * ( 0.6 + 0.6 * ( 1.0 - ( enemyDist / HEINRICH_STOMP_RANGE ) ) ), enemyVec );
			enemyVec[2] = scale * HEINRICH_STOMP_VELOCITY_Z * ( 1.0 - 0.5 * ( enemyDist / HEINRICH_STOMP_RANGE ) );
			// bounce the player using this velocity
			VectorAdd( enemy->client->ps.velocity, enemyVec, enemy->client->ps.velocity );
		}
	}

	return NULL;
}
Beispiel #24
0
/*
================
AICast_EvaluatePmove

  Avoidance after the event (leaders instruct AI's to get out the way, AI's instruct other non-moving AI's to get out the way)
================
*/
void AICast_EvaluatePmove( int clientnum, pmove_t *pm ) {
	cast_state_t    *cs, *ocs;
	int i, ent;
	bot_goal_t ogoal;

	//vec3_t pos, dir;
	cs = AICast_GetCastState( clientnum );
	// make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data)
	trap_AAS_SetCurrentWorld( cs->aasWorldIndex );

	// NOTE: this is only enabled for real clients, so their followers get out of their way
	//if (cs->bs)
	//	return;

	// look through the touchent's to see if we've bumped into something we should avoid, or react to
	for ( i = 0; i < pm->numtouch; i++ )
	{
		// mark the time, so they can deal with the obstruction in their own think functions
		cs->blockedTime = level.time;

		if ( pm->touchents[i] == pm->ps->groundEntityNum ) {
			continue;
		}

		// if they are an AI Cast, inform them of our disposition, and hope that they are reasonable
		// enough to assist us in our desire to move beyond our current position
		if ( pm->touchents[i] < aicast_maxclients ) {
			if ( !AICast_EntityVisible( cs, pm->touchents[i], qtrue ) ) {
				continue;
			}

			// if we are inspecting the body, abort if we touch anything
			if ( cs->bs && cs->bs->enemy >= 0 && g_entities[cs->bs->enemy].health <= 0 ) {
				cs->bs->enemy = -1;
			}

			// anything we touch, should see us
			AICast_UpdateVisibility( &g_entities[pm->touchents[i]], &g_entities[cs->entityNum], qfalse, qtrue );

			ocs = AICast_GetCastState( pm->touchents[i] );
			if (    ( ocs->bs ) &&
					( !( ocs->aiFlags & AIFL_NOAVOID ) ) &&
					( ( ocs->leaderNum == cs->entityNum ) || ( VectorLength( ocs->bs->velocity ) < 5 ) ) &&
					( ocs->obstructingTime < ( level.time + 100 ) ) ) {
				// if they are moving away from us already, let them go
				if ( VectorLength( ocs->bs->cur_ps.velocity ) > 10 ) {
					vec3_t v1, v2;

					VectorSubtract( ocs->bs->origin, g_entities[clientnum].client->ps.velocity, v2 );
					VectorNormalize( v2 );
					VectorNormalize2( ocs->bs->cur_ps.velocity, v1 );

					if ( DotProduct( v1, v2 ) > 0.0 ) {
						continue;
					}
				}
				if ( ocs->leaderNum >= 0 ) {
					VectorCopy( g_entities[ocs->leaderNum].r.currentOrigin, ogoal.origin );
					ogoal.areanum = BotPointAreaNum( ogoal.origin );
					ogoal.entitynum = ocs->leaderNum;
					if ( ocs->bs && AICast_GetAvoid( ocs, &ogoal, ocs->obstructingPos, qfalse, cs->entityNum ) ) { // give them time to move somewhere else
						ocs->obstructingTime = level.time + 1000;
					}
				} else {
					if ( ocs->bs && AICast_GetAvoid( ocs, NULL, ocs->obstructingPos, qfalse, cs->entityNum ) ) { // give them time to move somewhere else
						ocs->obstructingTime = level.time + 1000;
					}
				}
			}
		} else if ( cs->bs ) {
			// if we are blocked by a brush entity, see if we can activate it
			ent = pm->touchents[i];
			if ( g_entities[ent].s.modelindex > 0 && g_entities[ent].s.eType == ET_MOVER ) {
				//find the bsp entity which should be activated in order to remove
				//the blocking entity

				if ( !g_entities[ent].isProp
					 && Q_stricmp( g_entities[ent].classname, "func_static" )
					 && Q_stricmp( g_entities[ent].classname, "func_button" )
					 && Q_stricmp( g_entities[ent].classname, "func_tram" ) ) {
					G_Activate( &g_entities[ent], &g_entities[cs->entityNum] );
				}

			}
		}
	}
}
Beispiel #25
0
/*
============
AICast_Blocked
============
*/
void AICast_Blocked( cast_state_t *cs, bot_moveresult_t *moveresult, int activate, bot_goal_t *goal ) {
	vec3_t pos, dir;
	aicast_predictmove_t move;
	usercmd_t ucmd;
	bot_input_t bi;
	cast_state_t *ocs;
	int i, blockEnt = -1;
	bot_goal_t ogoal;

	if ( cs->blockedAvoidTime < level.time ) {
		if ( cs->blockedAvoidTime < level.time - 300 ) {
			if ( VectorCompare( cs->bs->cur_ps.velocity, vec3_origin ) && !cs->bs->lastucmd.forwardmove && !cs->bs->lastucmd.rightmove ) {
				// not moving, don't bother checking
				cs->blockedAvoidTime = level.time - 1;
				return;
			}
			// are we going to hit someone soon?
			trap_EA_GetInput( cs->entityNum, (float) level.time / 1000, &bi );
			AICast_InputToUserCommand( cs, &bi, &ucmd, cs->bs->cur_ps.delta_angles );
			AICast_PredictMovement( cs, 1, 0.6, &move, &ucmd, ( goal && goal->entitynum > -1 ) ? goal->entitynum : cs->entityNum );

			// blocked if we hit a client (or non-stationary mover) other than our enemy or goal
			if ( move.stopevent != PREDICTSTOP_HITCLIENT ) {
				// not blocked
				cs->blockedAvoidTime = level.time - 1;
				return;
			}

			// if we stopped passed our goal, ignore it
			if ( goal ) {
				if ( VectorDistance( cs->bs->origin, goal->origin ) < VectorDistance( cs->bs->origin, move.endpos ) ) {
					vec3_t v1, v2;
					VectorSubtract( goal->origin, cs->bs->origin, v1 );
					VectorSubtract( goal->origin, move.endpos, v2 );
					VectorNormalize( v1 );
					VectorNormalize( v2 );
					if ( DotProduct( v1, v2 ) < 0 ) {
						// we went passed the goal, so assume we can reach it
						cs->blockedAvoidTime = level.time - 1;
						return;
					}
				}
			}

			// try and get them to move, in case we can't get around them
			blockEnt = -1;
			for ( i = 0; i < move.numtouch; i++ ) {
				if ( move.touchents[i] >= MAX_CLIENTS ) {
					if ( !Q_stricmp( g_entities[move.touchents[i]].classname, "script_mover" ) ) {
						// avoid script_mover's
						blockEnt = move.touchents[i];
					}
					// if we are close to the impact point, then avoid this entity
					else if ( VectorDistance( cs->bs->origin, move.endpos ) < 10 ) {
						//G_Printf("AI (%s) avoiding %s\n", g_entities[cs->entityNum].aiName, g_entities[move.touchents[i]].classname );
						blockEnt = move.touchents[i];
					}
					continue;
				}
				//
				ocs = AICast_GetCastState( move.touchents[i] );
				if ( !ocs->bs ) {
					blockEnt = move.touchents[i];
				}
				// reject this blocker if we are following or going to them
				else if ( cs->followEntity != ocs->entityNum ) {
					// if they are moving away from us already, let them go
					if ( VectorLength( ocs->bs->cur_ps.velocity ) > 10 ) {
						vec3_t v1, v2;

						VectorSubtract( ocs->bs->origin, cs->bs->origin, v2 );
						VectorNormalize( v2 );
						VectorNormalize2( ocs->bs->cur_ps.velocity, v1 );

						if ( DotProduct( v1, v2 ) > 0.0 ) {
							continue;
						}
					}
					//
					// if they recently were asked to avoid us, then they're probably not listening
					if ( ocs->obstructingTime > level.time - 500 ) {
						blockEnt = move.touchents[i];
					}
					//
					// if they are not avoiding, ignore
					if ( !( ocs->aiFlags & AIFL_NOAVOID ) ) {
						continue;
					}
					//
					// they should avoid us
					if ( ocs->leaderNum >= 0 ) {
						ogoal.entitynum = ocs->leaderNum;
						VectorCopy( g_entities[ocs->leaderNum].r.currentOrigin, ogoal.origin );
						if ( AICast_GetAvoid( ocs, &ogoal, ocs->obstructingPos, qfalse, cs->entityNum ) ) {
							// give them time to move somewhere else
							ocs->obstructingTime = level.time + 1000;
						} else {
							// make sure they don't call GetAvoid() for another few frames to let others avoid also
							ocs->obstructingTime = level.time - 1;
							blockEnt = move.touchents[i];
						}
					} else {
						if ( AICast_GetAvoid( ocs, NULL, ocs->obstructingPos, qfalse, cs->entityNum ) ) {
							// give them time to move somewhere else
							ocs->obstructingTime = level.time + 1000;
						} else {
							// make sure they don't call GetAvoid() for another few frames to let others avoid also
							ocs->obstructingTime = level.time - 1;
							blockEnt = move.touchents[i];
						}
					}
				}
			}

		} else {
			return;
		}

		if ( blockEnt < 0 ) {
			// nothing found to be worth avoding
			cs->blockedAvoidTime = level.time - 1;
			return;
		}

		// something is blocking our path
		if ( g_entities[blockEnt].aiName && g_entities[blockEnt].client ) {
			int oldId = cs->castScriptStatus.scriptId;
			AICast_ScriptEvent( cs, "blocked", g_entities[blockEnt].aiName );
			if ( oldId != cs->castScriptStatus.scriptId ) {
				// the script has changed, so assume the scripting is handling the avoidance
				return;
			}
		}

		// avoid geometry and props, but assume clients will get out the way
		if ( /*blockEnt > MAX_CLIENTS &&*/ AICast_GetAvoid( cs, goal, pos, qfalse, blockEnt ) ) {
			VectorSubtract( pos, cs->bs->cur_ps.origin, dir );
			VectorNormalize( dir );
			cs->blockedAvoidYaw = vectoyaw( dir );
			if ( blockEnt >= MAX_CLIENTS ) {
				cs->blockedAvoidTime = level.time + 100 + rand() % 200;
			} else {
				cs->blockedAvoidTime = level.time + 300 + rand() % 400;
			}
		} else {
			cs->blockedAvoidTime = level.time - 1;    // don't look again for another few frames
			return;
		}
	}

	VectorClear( pos );
	pos[YAW] = cs->blockedAvoidYaw;
	AngleVectors( pos, dir, NULL, NULL );

	if ( moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE ) {
		trap_EA_Jump( cs->bs->entitynum );
	}

	trap_EA_Move( cs->bs->entitynum, dir, 200 ); //400);

	vectoangles( dir, cs->bs->ideal_viewangles );
	cs->bs->ideal_viewangles[2] *= 0.5;
}
Beispiel #26
0
/*
==================
AICast_CheckLoadGame

  at the start of a level, the game is either saved, or loaded

  we must wait for all AI to spawn themselves, and a real client to connect
==================
*/
void AICast_CheckLoadGame( void ) {
    char loading[4];
    gentity_t *ent = NULL; // TTimo: VC6 'may be used without having been init'
    qboolean ready;
    cast_state_t *pcs;

    // have we already done the save or load?
    if ( !saveGamePending ) {
        return;
    }

    // tell the cgame NOT to render the scene while we are waiting for things to settle
    trap_Cvar_Set( "cg_norender", "1" );

    trap_Cvar_VariableStringBuffer( "savegame_loading", loading, sizeof( loading ) );

//	reloading = qtrue;
    trap_Cvar_Set( "g_reloading", "1" );

    if ( strlen( loading ) > 0 && atoi( loading ) != 0 ) {
        // screen should be black if we are at this stage
        trap_SetConfigstring( CS_SCREENFADE, va( "1 %i 1", level.time - 10 ) );

//		if (!reloading && atoi(loading) == 2) {
        if ( !( g_reloading.integer ) && atoi( loading ) == 2 ) {
            // (SA) hmm, this seems redundant when it sets it above...
//			reloading = qtrue;	// this gets reset at the Map_Restart() since the server unloads the game dll
            trap_Cvar_Set( "g_reloading", "1" );
        }

        ready = qtrue;
        if ( numSpawningCast != numcast ) {
            ready = qfalse;
        } else if ( !( ent = AICast_FindEntityForName( "player" ) ) ) {
            ready = qfalse;
        } else if ( !ent->client || ent->client->pers.connected != CON_CONNECTED ) {
            ready = qfalse;
        }

        if ( ready ) {
            trap_Cvar_Set( "savegame_loading", "0" ); // in-case it aborts
            saveGamePending = qfalse;
            G_LoadGame( NULL );     // always load the "current" savegame

            // RF, spawn a thinker that will enable rendering after the client has had time to process the entities and setup the display
            //trap_Cvar_Set( "cg_norender", "0" );
            ent = G_Spawn();
            ent->nextthink = level.time + 200;
            ent->think = AICast_EnableRenderingThink;

            // wait for the clients to return from faded screen
            //trap_SetConfigstring( CS_SCREENFADE, va("0 %i 1500", level.time + 500) );
            trap_SetConfigstring( CS_SCREENFADE, va( "0 %i 750", level.time + 500 ) );
            level.reloadPauseTime = level.time + 1100;

            // make sure sound fades up
            trap_SendServerCommand( -1, va( "snd_fade 1 %d", 2000 ) );  //----(SA)	added

            AICast_CastScriptThink();
        }
    } else {

        ready = qtrue;
        if ( numSpawningCast != numcast ) {
            ready = qfalse;
        } else if ( !( ent = AICast_FindEntityForName( "player" ) ) ) {
            ready = qfalse;
        } else if ( !ent->client || ent->client->pers.connected != CON_CONNECTED ) {
            ready = qfalse;
        }

        // not loading a game, we must be in a new level, so look for some persistant data to read in, then save the game
        if ( ready ) {
            G_LoadPersistant();     // make sure we save the game after we have brought across the items

            trap_Cvar_Set( "g_totalPlayTime", "0" );  // reset play time
            trap_Cvar_Set( "g_attempts", "0" );
            pcs = AICast_GetCastState( ent->s.number );
            pcs->totalPlayTime = 0;
            pcs->lastLoadTime = 0;
            pcs->attempts = 0;

            // RF, disabled, since the pregame menu turns this off after the button is pressed, this isn't
            // required here
            // RF, spawn a thinker that will enable rendering after the client has had time to process the entities and setup the display
            //trap_Cvar_Set( "cg_norender", "0" );
            //ent = G_Spawn();
            //ent->nextthink = level.time + 200;
            //ent->think = AICast_EnableRenderingThink;

            saveGamePending = qfalse;

            // wait for the clients to return from faded screen
//			trap_SetConfigstring( CS_SCREENFADE, va("0 %i 1500", level.time + 500) );
//			trap_SetConfigstring( CS_SCREENFADE, va("0 %i 750", level.time + 500) );
            // (SA) send a command that will be interpreted for both the screenfade and any other effects (music cues, pregame menu, etc)

// briefing menu will handle transition, just set a cvar for it to check for drawing the 'continue' button
            trap_SendServerCommand( -1, "rockandroll\n" );

            level.reloadPauseTime = level.time + 1100;

            AICast_CastScriptThink();
        }
    }
}
Beispiel #27
0
/*
===============
AICast_AdjustIdealYawForMover
===============
*/
void AICast_AdjustIdealYawForMover( int entnum, float yaw ) {
	cast_state_t *cs = AICast_GetCastState(entnum);
	//
	cs->bs->ideal_viewangles[YAW] += yaw;
}
Beispiel #28
0
/*
==============
AICast_NumAttempts
==============
*/
int AICast_NumAttempts( int entnum ) {
    cast_state_t *cs = AICast_GetCastState( entnum );
    return ( cs->attempts );
}
Beispiel #29
0
/*
===============
AICast_NoReload
===============
*/
int AICast_NoReload( int entnum ) {
	cast_state_t *cs = AICast_GetCastState(entnum);
	//
	return ((cs->aiFlags & AIFL_NO_RELOAD) != 0);
}
Beispiel #30
0
void AICast_RegisterPain( int entnum ) {
    cast_state_t *cs = AICast_GetCastState( entnum );
    if ( cs ) {
        cs->lastPain = level.time;
    }
}