jabot: add checkges from 0.9.3 version

This commit is contained in:
Denis Pauk 2025-03-13 00:54:19 +02:00
parent ec60ebd7f8
commit 04e7a303db
20 changed files with 551 additions and 557 deletions

View file

@ -103,6 +103,8 @@ Games:
* Dawn of Darkness:
* Docs: <https://www.moddb.com/mods/dawn-of-darkness1/downloads/dod-mood-scripts-gsm-tutorials-fgd-and-def-file>
* Demo: [Episode 1](https://www.moddb.com/mods/dawn-of-darkness1/downloads/dawn-of-darkness-episode-1)
* JaBot:
* SDK: <https://www.moddb.com/mods/jabotq2/downloads/jabot-q2-v09x-win32-and-linux>
* Additional maps used for check maps support:
* PSX: <https://www.moddb.com/mods/quake-ii-psx/downloads/quake-ii-psx-10>
* ReRelease N64 Jam: <https://www.moddb.com/games/quake-2/addons/quake-2-re-release-n64-sp-map-jam>
@ -176,7 +178,6 @@ Goals, fully finished goals could be checked in [here](CHANGELOG):
* [ ] Support scalled textures for models and walls in soft render and fix
lighting with remastered maps,
* [ ] Modified ReRelease game code support with removed KEX only related code.
* [ ] Add support for [JaBot](https://www.moddb.com/mods/jabotq2/downloads/jabot-q2-v09x-win32-and-linux)
Not a goal:

View file

@ -42,16 +42,14 @@ static gitem_t *blueflag;
//==========================================
void BOT_DMclass_Move(edict_t *self, usercmd_t *ucmd)
{
// int current_node_flags = 0;
int next_node_flags = 0;
int current_link_type = 0;
int i;
// current_node_flags = nodes[self->ai.current_node].flags;
next_node_flags = nodes[self->ai.next_node].flags;
if( AI_PlinkExists( self->ai.current_node, self->ai.next_node ))
next_node_flags = nodes[self->ai->next_node].flags;
if( AI_PlinkExists( self->ai->current_node, self->ai->next_node ))
{
current_link_type = AI_PlinkMoveType( self->ai.current_node, self->ai.next_node );
current_link_type = AI_PlinkMoveType( self->ai->current_node, self->ai->next_node );
//Com_Printf("%s\n", AI_LinkString( current_link_type ));
}
@ -59,8 +57,8 @@ void BOT_DMclass_Move(edict_t *self, usercmd_t *ucmd)
if( current_link_type == LINK_PLATFORM )
{
// Move to the center
self->ai.move_vector[2] = 0; // kill z movement
if(VectorLength(self->ai.move_vector) > 10)
self->ai->move_vector[2] = 0; // kill z movement
if(VectorLength(self->ai->move_vector) > 10)
ucmd->forwardmove = 200; // walk to center
AI_ChangeAngle(self);
@ -71,7 +69,7 @@ void BOT_DMclass_Move(edict_t *self, usercmd_t *ucmd)
{
// is lift down?
for(i=0;i<nav.num_ents;i++){
if( nav.ents[i].node == self->ai.next_node )
if( nav.ents[i].node == self->ai->next_node )
{
//testing line
//vec3_t tPoint;
@ -83,15 +81,16 @@ void BOT_DMclass_Move(edict_t *self, usercmd_t *ucmd)
//AITools_DrawLine( self->s.origin, tPoint );
//if not reachable, wait for it (only height matters)
if( (nav.ents[i].ent->s.origin[2] + nav.ents[i].ent->maxs[2])
> (self->s.origin[2] + self->mins[2] + AI_JUMPABLE_HEIGHT) )
if( ((nav.ents[i].ent->s.origin[2] + nav.ents[i].ent->maxs[2])
> (self->s.origin[2] + self->mins[2] + AI_JUMPABLE_HEIGHT) ) &&
nav.ents[i].ent->moveinfo.state != STATE_BOTTOM) //jabot092(2)
return; //wait for elevator
}
}
}
// Ladder movement
if( self->ai.is_ladder )
if( self->is_ladder )
{
ucmd->forwardmove = 70;
ucmd->upmove = 200;
@ -100,17 +99,17 @@ void BOT_DMclass_Move(edict_t *self, usercmd_t *ucmd)
}
// Falling off ledge
if(!self->groundentity && !self->ai.is_step && !self->ai.is_swim )
if(!self->groundentity && !self->is_step && !self->is_swim )
{
AI_ChangeAngle(self);
if (current_link_type == LINK_JUMPPAD ) {
ucmd->forwardmove = 100;
} else if( current_link_type == LINK_JUMP ) {
self->velocity[0] = self->ai.move_vector[0] * 280;
self->velocity[1] = self->ai.move_vector[1] * 280;
self->velocity[0] = self->ai->move_vector[0] * 280;
self->velocity[1] = self->ai->move_vector[1] * 280;
} else {
self->velocity[0] = self->ai.move_vector[0] * 160;
self->velocity[1] = self->ai.move_vector[1] * 160;
self->velocity[0] = self->ai->move_vector[0] * 160;
self->velocity[1] = self->ai->move_vector[1] * 160;
}
return;
}
@ -122,7 +121,7 @@ void BOT_DMclass_Move(edict_t *self, usercmd_t *ucmd)
vec3_t v1, v2;
//check floor in front, if there's none... Jump!
VectorCopy( self->s.origin, v1 );
VectorCopy( self->ai.move_vector, v2 );
VectorCopy( self->ai->move_vector, v2 );
VectorNormalize( v2 );
VectorMA( v1, 12, v2, v1 );
v1[2] += self->mins[2];
@ -148,12 +147,12 @@ void BOT_DMclass_Move(edict_t *self, usercmd_t *ucmd)
return;
// swimming
if( self->ai.is_swim )
if( self->is_swim )
{
// We need to be pointed up/down
AI_ChangeAngle(self);
if( !(gi.pointcontents(nodes[self->ai.next_node].origin) & MASK_WATER) ) // Exit water
if( !(gi.pointcontents(nodes[self->ai->next_node].origin) & MASK_WATER) ) // Exit water
ucmd->upmove = 400;
ucmd->forwardmove = 300;
@ -193,7 +192,7 @@ void BOT_DMclass_Wander(edict_t *self, usercmd_t *ucmd)
vec3_t temp;
// Do not move
if(self->ai.next_move_time > level.time)
if(self->ai->next_move_time > level.time)
return;
if (self->deadflag)
@ -208,7 +207,7 @@ void BOT_DMclass_Wander(edict_t *self, usercmd_t *ucmd)
self->velocity[0] = 0;
self->velocity[1] = 0;
self->velocity[2] = 0;
self->ai.next_move_time = level.time + 0.5;
self->ai->next_move_time = level.time + 0.5;
return;
}
}
@ -261,7 +260,7 @@ void BOT_DMclass_Wander(edict_t *self, usercmd_t *ucmd)
self->s.angles[YAW] += random() * 180 - 90;
if (!self->ai.is_step)// if there is ground continue otherwise wait for next move
if (!self->is_step)// if there is ground continue otherwise wait for next move
ucmd->forwardmove = 0; //0
else if( AI_CanMove( self, BOT_MOVE_FORWARD))
ucmd->forwardmove = 100;
@ -365,35 +364,35 @@ qboolean BOT_DMclass_FindEnemy(edict_t *self)
return true;
// Find Enemy
for(i=0;i<num_players;i++)
for(i=0;i<num_AIEnemies;i++)
{
if(players[i] == NULL || players[i] == self
|| players[i]->solid == SOLID_NOT)
if( AIEnemies[i] == NULL || AIEnemies[i] == self
|| AIEnemies[i]->solid == SOLID_NOT)
continue;
//Ignore players with 0 weight (was set at botstatus)
if(self->ai.status.playersWeights[i] == 0)
if(self->ai->status.playersWeights[i] == 0)
continue;
if(!players[i]->deadflag && visible(self, players[i]) &&
if( !AIEnemies[i]->deadflag && visible(self, AIEnemies[i]) &&
//trap_inPVS (self->s.origin, players[i]->s.origin))
gi.inPVS(self->s.origin, players[i]->s.origin))
gi.inPVS(self->s.origin, AIEnemies[i]->s.origin))
{
//(weight enemies from fusionbot) Is enemy visible, or is it too close to ignore
VectorSubtract(self->s.origin, players[i]->s.origin, dist);
VectorSubtract(self->s.origin, AIEnemies[i]->s.origin, dist);
weight = VectorLength( dist );
//modify weight based on precomputed player weights
weight *= (1.0 - self->ai.status.playersWeights[i]);
weight *= (1.0 - self->ai->status.playersWeights[i]);
if( infront( self, players[i] ) ||
if( infront( self, AIEnemies[i] ) ||
(weight < 300 ) )
{
// Check if best target, or better than current target
if (weight < bestweight)
{
bestweight = weight;
bestenemy = players[i];
bestenemy = AIEnemies[i];
}
}
}
@ -402,10 +401,10 @@ qboolean BOT_DMclass_FindEnemy(edict_t *self)
// If best enemy, set up
if(bestenemy)
{
// if (AIDevel.debugChased && bot_showcombat->value && bestenemy->ai.is_bot)
// gi.cprintf(NULL, PRINT_HIGH, "%s: selected %s as enemy.\n",
// self->ai.pers.netname,
// bestenemy->ai.pers.netname);
// if (AIDevel.debugChased && bot_showcombat->value && bestenemy->ai->is_bot)
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: selected %s as enemy.\n",
// self->ai->pers.netname,
// bestenemy->ai->pers.netname);
self->enemy = bestenemy;
return true;
@ -442,7 +441,7 @@ qboolean BOT_DMClass_ChangeWeapon (edict_t *ent, gitem_t *item)
// Change to this weapon
ent->client->newweapon = item;
ent->ai.changeweapon_timeout = level.time + 6.0;
ent->ai->changeweapon_timeout = level.time + 6.0;
return true;
}
@ -465,7 +464,7 @@ void BOT_DMclass_ChooseWeapon(edict_t *self)
if(!self->enemy)
return;
if( self->ai.changeweapon_timeout > level.time )
if( self->ai->changeweapon_timeout > level.time )
return;
// Base weapon selection on distance:
@ -572,22 +571,22 @@ void BOT_DMclass_FireWeapon (edict_t *self, usercmd_t *ucmd)
}
// modify attack angles based on accuracy (mess this up to make the bot's aim not so deadly)
target[0] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai.pers.skillLevel) *2);
target[1] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai.pers.skillLevel) *2);
target[0] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai->pers.skillLevel) *2);
target[1] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai->pers.skillLevel) *2);
// Set direction
VectorSubtract (target, self->s.origin, self->ai.move_vector);
vectoangles (self->ai.move_vector, angles);
VectorSubtract (target, self->s.origin, self->ai->move_vector);
vectoangles (self->ai->move_vector, angles);
VectorCopy(angles,self->s.angles);
// Set the attack
firedelay = random()*(MAX_BOT_SKILL*1.8);
if (firedelay > (MAX_BOT_SKILL - self->ai.pers.skillLevel) && BOT_DMclass_CheckShot(self, target))
if (firedelay > (MAX_BOT_SKILL - self->ai->pers.skillLevel) && BOT_DMclass_CheckShot(self, target))
ucmd->buttons = BUTTON_ATTACK;
//if(AIDevel.debugChased && bot_showcombat->integer)
// gi.cprintf(NULL, PRINT_HIGH, "%s: attacking %s\n",self->bot.pers.netname ,self->enemy->r.client->pers.netname);
// gi.cprintf(AIDevel.devguy, PRINT_HIGH, "%s: attacking %s\n",self->bot.pers.netname ,self->enemy->r.client->pers.netname);
}
@ -600,48 +599,48 @@ void BOT_DMclass_WeightPlayers(edict_t *self)
int i;
//clear
memset(self->ai.status.playersWeights, 0, sizeof (self->ai.status.playersWeights));
memset(self->ai->status.playersWeights, 0, sizeof (self->ai->status.playersWeights));
for( i=0; i<num_players; i++ )
for( i=0; i<num_AIEnemies; i++ )
{
if( players[i] == NULL )
if( AIEnemies[i] == NULL )
continue;
if( players[i] == self )
if( AIEnemies[i] == self )
continue;
//ignore spectators and dead players
if( players[i]->svflags & SVF_NOCLIENT || players[i]->deadflag ) {
self->ai.status.playersWeights[i] = 0.0f;
if( AIEnemies[i]->svflags & SVF_NOCLIENT || AIEnemies[i]->deadflag ) {
self->ai->status.playersWeights[i] = 0.0f;
continue;
}
if( ctf->value )
{
if( players[i]->client->resp.ctf_team != self->client->resp.ctf_team )
if( AIEnemies[i]->client->resp.ctf_team != self->client->resp.ctf_team )
{
//being at enemy team gives a small weight, but weight afterall
self->ai.status.playersWeights[i] = 0.2;
self->ai->status.playersWeights[i] = 0.2;
//enemy has redflag
if( redflag && players[i]->client->pers.inventory[ITEM_INDEX(redflag)]
if( redflag && AIEnemies[i]->client->pers.inventory[ITEM_INDEX(redflag)]
&& (self->client->resp.ctf_team == CTF_TEAM1) )
{
if( !self->client->pers.inventory[ITEM_INDEX(blueflag)] ) //don't hunt if you have the other flag, let others do
self->ai.status.playersWeights[i] = 0.9;
self->ai->status.playersWeights[i] = 0.9;
}
//enemy has blueflag
if( blueflag && players[i]->client->pers.inventory[ITEM_INDEX(blueflag)]
if( blueflag && AIEnemies[i]->client->pers.inventory[ITEM_INDEX(blueflag)]
&& (self->client->resp.ctf_team == CTF_TEAM2) )
{
if( !self->client->pers.inventory[ITEM_INDEX(redflag)] ) //don't hunt if you have the other flag, let others do
self->ai.status.playersWeights[i] = 0.9;
self->ai->status.playersWeights[i] = 0.9;
}
}
}
else //if not at ctf every player has some value
self->ai.status.playersWeights[i] = 0.3;
self->ai->status.playersWeights[i] = 0.3;
}
}
@ -703,7 +702,7 @@ void BOT_DMclass_WeightInventory(edict_t *self)
client = self->client;
//reset with persistant values
memcpy(self->ai.status.inventoryWeights, self->ai.pers.inventoryWeights, sizeof(self->ai.pers.inventoryWeights));
memcpy(self->ai->status.inventoryWeights, self->ai->pers.inventoryWeights, sizeof(self->ai->pers.inventoryWeights));
//weight ammo down if bot doesn't have the weapon for it,
@ -713,57 +712,57 @@ void BOT_DMclass_WeightInventory(edict_t *self)
//AMMO_BULLETS
if (!AI_CanPick_Ammo (self, AIWeapons[WEAP_MACHINEGUN].ammoItem) )
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_MACHINEGUN].ammoItem)] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_MACHINEGUN].ammoItem)] = 0.0;
//find out if it has a weapon for this amno
else if (!client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_CHAINGUN].weaponItem)]
&& !client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_MACHINEGUN].weaponItem)] )
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_MACHINEGUN].ammoItem)] *= LowNeedFactor;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_MACHINEGUN].ammoItem)] *= LowNeedFactor;
//AMMO_SHELLS:
//find out if it's packed up
if (!AI_CanPick_Ammo (self, AIWeapons[WEAP_SHOTGUN].ammoItem) )
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SHOTGUN].ammoItem)] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SHOTGUN].ammoItem)] = 0.0;
//find out if it has a weapon for this amno
else if (!client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_SHOTGUN].weaponItem)]
&& !client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_SUPERSHOTGUN].weaponItem)] )
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SHOTGUN].ammoItem)] *= LowNeedFactor;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SHOTGUN].ammoItem)] *= LowNeedFactor;
//AMMO_ROCKETS:
//find out if it's packed up
if (!AI_CanPick_Ammo (self, AIWeapons[WEAP_ROCKETLAUNCHER].ammoItem))
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_ROCKETLAUNCHER].ammoItem)] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_ROCKETLAUNCHER].ammoItem)] = 0.0;
//find out if it has a weapon for this amno
else if (!client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_ROCKETLAUNCHER].weaponItem)] )
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_ROCKETLAUNCHER].ammoItem)] *= LowNeedFactor;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_ROCKETLAUNCHER].ammoItem)] *= LowNeedFactor;
//AMMO_GRENADES:
//find if it's packed up
if (!AI_CanPick_Ammo (self, AIWeapons[WEAP_GRENADES].ammoItem))
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_GRENADES].ammoItem)] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_GRENADES].ammoItem)] = 0.0;
//grenades are also weapons, and are weighted down by LowNeedFactor in weapons group
//AMMO_CELLS:
//find out if it's packed up
if (!AI_CanPick_Ammo (self, AIWeapons[WEAP_HYPERBLASTER].ammoItem))
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_HYPERBLASTER].ammoItem)] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_HYPERBLASTER].ammoItem)] = 0.0;
//find out if it has a weapon for this amno
else if (!client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_HYPERBLASTER].weaponItem)]
&& !client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_BFG].weaponItem)]
&& !client->pers.inventory[ITEM_INDEX(FindItemByClassname("item_power_shield"))])
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_HYPERBLASTER].ammoItem)] *= LowNeedFactor;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_HYPERBLASTER].ammoItem)] *= LowNeedFactor;
//AMMO_SLUGS:
//find out if it's packed up
if (!AI_CanPick_Ammo (self, AIWeapons[WEAP_RAILGUN].ammoItem))
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_RAILGUN].ammoItem)] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_RAILGUN].ammoItem)] = 0.0;
//find out if it has a weapon for this amno
else if (!client->pers.inventory[ITEM_INDEX(AIWeapons[WEAP_RAILGUN].weaponItem)] )
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_RAILGUN].ammoItem)] *= LowNeedFactor;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_RAILGUN].ammoItem)] *= LowNeedFactor;
//WEAPONS
@ -772,23 +771,23 @@ void BOT_DMclass_WeightInventory(edict_t *self)
//weight weapon down if bot already has it
for (i=0; i<WEAP_TOTAL; i++) {
if ( AIWeapons[i].weaponItem && client->pers.inventory[ITEM_INDEX(AIWeapons[i].weaponItem)])
self->ai.status.inventoryWeights[ITEM_INDEX(AIWeapons[i].weaponItem)] *= LowNeedFactor;
self->ai->status.inventoryWeights[ITEM_INDEX(AIWeapons[i].weaponItem)] *= LowNeedFactor;
}
//ARMOR
//-----------------------------------------------------
//shards are ALWAYS accepted but still...
if (!AI_CanUseArmor ( FindItemByClassname("item_armor_shard"), self ))
self->ai.status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_shard"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_shard"))] = 0.0;
if (!AI_CanUseArmor ( FindItemByClassname("item_armor_jacket"), self ))
self->ai.status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_jacket"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_jacket"))] = 0.0;
if (!AI_CanUseArmor ( FindItemByClassname("item_armor_combat"), self ))
self->ai.status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_combat"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_combat"))] = 0.0;
if (!AI_CanUseArmor ( FindItemByClassname("item_armor_body"), self ))
self->ai.status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_body"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_body"))] = 0.0;
//TECH :
@ -798,10 +797,10 @@ void BOT_DMclass_WeightInventory(edict_t *self)
|| self->client->pers.inventory[ITEM_INDEX( FindItemByClassname("item_tech3"))]
|| self->client->pers.inventory[ITEM_INDEX( FindItemByClassname("item_tech4"))] )
{
self->ai.status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech1"))] = 0.0;
self->ai.status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech2"))] = 0.0;
self->ai.status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech3"))] = 0.0;
self->ai.status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech4"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech1"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech2"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech3"))] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX( FindItemByClassname("item_tech4"))] = 0.0;
}
//CTF:
@ -814,16 +813,16 @@ void BOT_DMclass_WeightInventory(edict_t *self)
//flags have weights defined inside persistant inventory. Remove weight from the unwanted one/s.
if (blueflag && blueflag != wantedFlag)
self->ai.status.inventoryWeights[ITEM_INDEX(blueflag)] = 0.0;
else if (redflag && redflag != wantedFlag)
self->ai.status.inventoryWeights[ITEM_INDEX(redflag)] = 0.0;
self->ai->status.inventoryWeights[ITEM_INDEX(blueflag)] = 0.0;
if (redflag && redflag != wantedFlag)
self->ai->status.inventoryWeights[ITEM_INDEX(redflag)] = 0.0;
}
}
//==========================================
// BOT_DMclass_UpdateStatus
// update ai.status values based on bot state,
// update ai->status values based on bot state,
// so ai can decide based on these settings
//==========================================
void BOT_DMclass_UpdateStatus( edict_t *self )
@ -838,14 +837,14 @@ void BOT_DMclass_UpdateStatus( edict_t *self )
//JALFIXMEQ2
/*
if (self->client->jumppad_time)
self->ai.status.jumpadReached = true; //jumpad time from client to botStatus
self->ai->status.jumpadReached = true; //jumpad time from client to botStatus
else
self->ai.status.jumpadReached = false;
self->ai->status.jumpadReached = false;
*/
if (self->client->ps.pmove.pm_flags & PMF_TIME_TELEPORT)
self->ai.status.TeleportReached = true;
self->ai->status.TeleportReached = true;
else
self->ai.status.TeleportReached = false;
self->ai->status.TeleportReached = false;
//set up AI status for the upcoming AI_frame
BOT_DMclass_WeightInventory( self ); //weight items
@ -860,7 +859,7 @@ void BOT_DMclass_UpdateStatus( edict_t *self )
void BOT_DMClass_BloquedTimeout( edict_t *self )
{
self->health = 0;
self->ai.bloqued_timeout = level.time + 15.0;
self->ai->bloqued_timeout = level.time + 15.0;
self->die(self, self, self, 100000, vec3_origin);
self->nextthink = level.time + FRAMETIME;
}
@ -896,24 +895,24 @@ void BOT_DMclass_RunFrame( edict_t *self )
{
BOT_DMclass_ChooseWeapon( self );
BOT_DMclass_FireWeapon( self, &ucmd );
self->ai.state = BOT_STATE_ATTACK;
self->ai.state_combat_timeout = level.time + 1.0;
self->ai->state = BOT_STATE_ATTACK;
self->ai->state_combat_timeout = level.time + 1.0;
} else if( self->ai.state == BOT_STATE_ATTACK &&
level.time > self->ai.state_combat_timeout)
} else if( self->ai->state == BOT_STATE_ATTACK &&
level.time > self->ai->state_combat_timeout)
{
//Jalfixme: change to: AI_SetUpStateMove(self);
self->ai.state = BOT_STATE_MOVE;
self->ai->state = BOT_STATE_MOVE;
}
// Execute the move, or wander
if( self->ai.state == BOT_STATE_MOVE )
if( self->ai->state == BOT_STATE_MOVE )
BOT_DMclass_Move( self, &ucmd );
else if(self->ai.state == BOT_STATE_ATTACK)
else if(self->ai->state == BOT_STATE_ATTACK)
BOT_DMclass_CombatMovement( self, &ucmd );
else if ( self->ai.state == BOT_STATE_WANDER )
else if ( self->ai->state == BOT_STATE_WANDER )
BOT_DMclass_Wander( self, &ucmd );
@ -943,61 +942,61 @@ void BOT_DMclass_InitPersistant(edict_t *self)
//copy name
if (self->client->pers.netname[0])
self->ai.pers.netname = self->client->pers.netname;
self->ai->pers.netname = self->client->pers.netname;
else
self->ai.pers.netname = "dmBot";
self->ai->pers.netname = "dmBot";
//set 'class' functions
self->ai.pers.RunFrame = BOT_DMclass_RunFrame;
self->ai.pers.UpdateStatus = BOT_DMclass_UpdateStatus;
self->ai.pers.bloquedTimeout = BOT_DMClass_BloquedTimeout;
self->ai.pers.deadFrame = BOT_DMclass_DeadFrame;
self->ai->pers.RunFrame = BOT_DMclass_RunFrame;
self->ai->pers.UpdateStatus = BOT_DMclass_UpdateStatus;
self->ai->pers.bloquedTimeout = BOT_DMClass_BloquedTimeout;
self->ai->pers.deadFrame = BOT_DMclass_DeadFrame;
//available moveTypes for this class
self->ai.pers.moveTypesMask = (LINK_MOVE|LINK_STAIRS|LINK_FALL|LINK_WATER|LINK_WATERJUMP|LINK_JUMPPAD|LINK_PLATFORM|LINK_TELEPORT|LINK_LADDER|LINK_JUMP|LINK_CROUCH);
self->ai->pers.moveTypesMask = (LINK_MOVE|LINK_STAIRS|LINK_FALL|LINK_WATER|LINK_WATERJUMP|LINK_JUMPPAD|LINK_PLATFORM|LINK_TELEPORT|LINK_LADDER|LINK_JUMP|LINK_CROUCH);
//Persistant Inventory Weights (0 = can not pick)
memset(self->ai.pers.inventoryWeights, 0, sizeof (self->ai.pers.inventoryWeights));
memset(self->ai->pers.inventoryWeights, 0, sizeof (self->ai->pers.inventoryWeights));
//weapons
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_BLASTER].weaponItem)] = 0.0;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_BLASTER].weaponItem)] = 0.0;
//self->bot.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("weapon_blaster"))] = 0.0; //it's the same thing
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SHOTGUN].weaponItem)] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SUPERSHOTGUN].weaponItem)] = 0.7;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_MACHINEGUN].weaponItem)] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_CHAINGUN].weaponItem)] = 0.7;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_GRENADES].weaponItem)] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_GRENADELAUNCHER].weaponItem)] = 0.6;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_ROCKETLAUNCHER].weaponItem)] = 0.8;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_HYPERBLASTER].weaponItem)] = 0.7;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_RAILGUN].weaponItem)] = 0.8;
self->ai.pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_BFG].weaponItem)] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SHOTGUN].weaponItem)] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_SUPERSHOTGUN].weaponItem)] = 0.7;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_MACHINEGUN].weaponItem)] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_CHAINGUN].weaponItem)] = 0.7;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_GRENADES].weaponItem)] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_GRENADELAUNCHER].weaponItem)] = 0.6;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_ROCKETLAUNCHER].weaponItem)] = 0.8;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_HYPERBLASTER].weaponItem)] = 0.7;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_RAILGUN].weaponItem)] = 0.8;
self->ai->pers.inventoryWeights[ITEM_INDEX(AIWeapons[WEAP_BFG].weaponItem)] = 0.5;
//ammo
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_shells"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_bullets"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_cells"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_rockets"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_slugs"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_grenades"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_shells"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_bullets"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_cells"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_rockets"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_slugs"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("ammo_grenades"))] = 0.5;
//armor
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_body"))] = 0.9;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_combat"))] = 0.8;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_jacket"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_shard"))] = 0.2;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_body"))] = 0.9;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_combat"))] = 0.8;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_jacket"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_armor_shard"))] = 0.2;
//techs
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech1"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech2"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech3"))] = 0.5;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech4"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech1"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech2"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech3"))] = 0.5;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_tech4"))] = 0.5;
if( ctf->value ) {
redflag = FindItemByClassname("item_flag_team1"); // store pointers to flags gitem_t, for
blueflag = FindItemByClassname("item_flag_team2");// simpler comparisons inside this archive
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_flag_team1"))] = 3.0;
self->ai.pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_flag_team2"))] = 3.0;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_flag_team1"))] = 3.0;
self->ai->pers.inventoryWeights[ITEM_INDEX(FindItemByClassname("item_flag_team2"))] = 3.0;
}
}

View file

@ -47,42 +47,44 @@ void M_default_Move(edict_t *self, usercmd_t *ucmd)
{
// int current_node_flags = 0;
// int next_node_flags = 0;
// int current_node_flags = 0;
// int next_node_flags = 0;
int current_link_type = 0;
// int i;
// current_node_flags = nodes[self->ai.current_node].flags;
// next_node_flags = nodes[self->ai.next_node].flags;
if( AI_PlinkExists( self->ai.current_node, self->ai.next_node ))
// current_node_flags = nodes[self->ai->current_node].flags;
// next_node_flags = nodes[self->ai->next_node].flags;
if( AI_PlinkExists( self->ai->current_node, self->ai->next_node ))
{
current_link_type = AI_PlinkMoveType( self->ai.current_node, self->ai.next_node );
current_link_type = AI_PlinkMoveType( self->ai->current_node, self->ai->next_node );
//Com_Printf("%s\n", AI_LinkString( current_link_type ));
}
// Falling off ledge
if(!self->groundentity && !self->ai.is_step && !self->ai.is_swim )
if(!self->groundentity && !self->is_step && !self->is_swim )
{
AI_ChangeAngle(self);
if (current_link_type == LINK_JUMPPAD ) {
ucmd->forwardmove = 100;
} else if( current_link_type == LINK_JUMP ) {
self->velocity[0] = self->ai.move_vector[0] * 280;
self->velocity[1] = self->ai.move_vector[1] * 280;
self->velocity[0] = self->ai->move_vector[0] * 280;
self->velocity[1] = self->ai->move_vector[1] * 280;
} else {
self->velocity[0] = self->ai.move_vector[0] * 160;
self->velocity[1] = self->ai.move_vector[1] * 160;
self->velocity[0] = self->ai->move_vector[0] * 160;
self->velocity[1] = self->ai->move_vector[1] * 160;
}
return;
}
// swimming
if( self->ai.is_swim )
if( self->is_swim )
{
// We need to be pointed up/down
AI_ChangeAngle(self);
//if( !(trap_PointContents(nodes[self->ai.next_node].origin) & MASK_WATER) ) // Exit water
if( !(gi.pointcontents(nodes[self->ai.next_node].origin) & MASK_WATER) ) // Exit water
//if( !(trap_PointContents(nodes[self->ai->next_node].origin) & MASK_WATER) ) // Exit water
if( !(gi.pointcontents(nodes[self->ai->next_node].origin) & MASK_WATER) ) // Exit water
ucmd->upmove = 400;
ucmd->forwardmove = 300;
@ -234,18 +236,18 @@ void M_default_FireWeapon (edict_t *self)
// modify attack angles based on accuracy (mess this up to make the bot's aim not so deadly)
target[0] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai.pers.skillLevel) *2);
target[1] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai.pers.skillLevel) *2);
target[0] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai->pers.skillLevel) *2);
target[1] += (random()-0.5) * ((MAX_BOT_SKILL - self->ai->pers.skillLevel) *2);
// Set direction
VectorSubtract (target, self->s.origin, self->ai.move_vector);
vectoangles (self->ai.move_vector, angles);
VectorSubtract (target, self->s.origin, self->ai->move_vector);
vectoangles (self->ai->move_vector, angles);
VectorCopy(angles,self->s.angles);
// Set the attack
firedelay = random()*(MAX_BOT_SKILL*1.8);
if (firedelay > (MAX_BOT_SKILL - self->ai.pers.skillLevel) && M_default_CheckShot(self, target))
if (firedelay > (MAX_BOT_SKILL - self->ai->pers.skillLevel) && M_default_CheckShot(self, target))
{
vec3_t start, forward, right;
AngleVectors (self->s.angles, forward, right, NULL);
@ -268,29 +270,29 @@ void M_default_WeightPlayers(edict_t *self)
int i;
//clear
memset(self->ai.status.playersWeights, 0, sizeof (self->ai.status.playersWeights));
memset(self->ai->status.playersWeights, 0, sizeof (self->ai->status.playersWeights));
for(i=0;i<num_players;i++)
for(i=0;i<num_AIEnemies;i++)
{
if (players[i] == NULL)
if( AIEnemies[i] == NULL)
continue;
if(players[i] == self)
if( AIEnemies[i] == self)
continue;
if( !strcmp(players[i]->classname, "monster") ) {
self->ai.status.playersWeights[i] = 0.0f;
if( !strcmp(AIEnemies[i]->classname, "monster") ) {
self->ai->status.playersWeights[i] = 0.0f;
continue;
}
//ignore spectators and dead players
if(players[i]->svflags & SVF_NOCLIENT || players[i]->deadflag) {
self->ai.status.playersWeights[i] = 0.0f;
if( AIEnemies[i]->svflags & SVF_NOCLIENT || AIEnemies[i]->deadflag) {
self->ai->status.playersWeights[i] = 0.0f;
continue;
}
//every player has some value
self->ai.status.playersWeights[i] = 0.3;
self->ai->status.playersWeights[i] = 0.3;
}
}
@ -302,7 +304,7 @@ void M_default_WeightPlayers(edict_t *self)
void M_default_WeightInventory(edict_t *self)
{
//reset with persistant values
memcpy(self->ai.status.inventoryWeights, self->ai.pers.inventoryWeights, sizeof(self->ai.pers.inventoryWeights));
memcpy(self->ai->status.inventoryWeights, self->ai->pers.inventoryWeights, sizeof(self->ai->pers.inventoryWeights));
}
//==========================================
@ -378,7 +380,7 @@ int M_default_GravityBoxStep( vec3_t origin, float scale, vec3_t movedir, vec3_t
int movemask = 0;
int eternalfall = 0;
float /*xzdist,*/ xzscale;
/* float ydist, yscale; */
/*float ydist, yscale; */
// float dist;
//trap_Trace( &trace, origin, mins, maxs, origin, passent, solidmask );
@ -391,9 +393,8 @@ int M_default_GravityBoxStep( vec3_t origin, float scale, vec3_t movedir, vec3_t
vectoangles( movedir, angles );
xzscale = scale;
// yscale = scale;
/*
yscale = scale;
//remaining distance in planes
if( scale < 1 )
scale = 1;
@ -593,10 +594,8 @@ qboolean M_default_movestep (edict_t *self, usercmd_t *ucmd)
}
qboolean M_walkmove (edict_t *ent, float yaw, float dist);
//void G_SetPModelFrame (edict_t *ent);
void M_WorldEffects (edict_t *ent);
void M_droptofloor (edict_t *ent);
//==========================================
// M_default_RunFrame
//
@ -612,30 +611,30 @@ void M_default_RunFrame( edict_t *self )
{
M_default_ChooseWeapon( self );
M_default_FireWeapon( self );
self->ai.state = BOT_STATE_ATTACK;
self->ai.state_combat_timeout = level.time + 1.0;
self->ai->state = BOT_STATE_ATTACK;
self->ai->state_combat_timeout = level.time + 1.0;
} else if( self->ai.state == BOT_STATE_ATTACK &&
level.time > self->ai.state_combat_timeout)
} else if( self->ai->state == BOT_STATE_ATTACK &&
level.time > self->ai->state_combat_timeout)
{
//Jalfixme: change to: AI_SetUpStateMove(self);
self->ai.state = BOT_STATE_MOVE;
self->ai->state = BOT_STATE_MOVE;
}
// Execute the move, or wander
if( self->ai.state == BOT_STATE_MOVE )
if( self->ai->state == BOT_STATE_MOVE )
M_default_Move( self, &ucmd );
else if(self->ai.state == BOT_STATE_ATTACK)
else if(self->ai->state == BOT_STATE_ATTACK)
M_default_CombatMovement( self, &ucmd );
else if ( self->ai.state == BOT_STATE_WANDER )
else if ( self->ai->state == BOT_STATE_WANDER )
M_default_Wander( self, &ucmd );
//move a step
if( M_default_movestep ( self, &ucmd ) )
self->ai.bloqued_timeout = level.time + 10.0;
self->ai->bloqued_timeout = level.time + 10.0;
M_WorldEffects (self);

View file

@ -334,8 +334,8 @@ void AI_PathMap( void )
int closest_node;
//DROP WATER JUMP NODE (not limited by delayed updates)
if ( !player.ent->ai.is_swim && player.last_node != -1
&& player.ent->ai.is_swim != player.ent->ai.was_swim)
if ( !player.ent->is_swim && player.last_node != -1
&& player.ent->is_swim != player.ent->was_swim)
{
AI_WaterJumpNode();
last_update = level.time + NODE_UPDATE_DELAY; // slow down updates a bit
@ -365,13 +365,13 @@ void AI_PathMap( void )
return;
// Not on ground, and not in the water, so bail (deeper check by using a splitmodels function)
if (!player.ent->ai.is_step )
if (!player.ent->is_step )
{
if ( !player.ent->ai.is_swim ){
if ( !player.ent->is_swim ){
player.was_falling = true;
return;
}
else if ( player.ent->ai.is_swim )
else if ( player.ent->is_swim )
player.was_falling = false;
}
@ -415,7 +415,7 @@ void AI_PathMap( void )
if( closest_node == INVALID )
{
// Add nodes in the water as needed
if( player.ent->ai.is_swim )
if( player.ent->is_swim )
closest_node = AI_AddNode( player.ent->s.origin, (NODEFLAGS_WATER|NODEFLAGS_FLOAT) );
else
closest_node = AI_AddNode( player.ent->s.origin, 0 );

View file

@ -39,7 +39,8 @@
//==========================================
void AI_EnemyAdded(edict_t *ent)
{
players[num_players++] = ent;
if( num_AIEnemies < MAX_EDICTS )
AIEnemies[num_AIEnemies++] = ent;
}
//==========================================
@ -52,27 +53,26 @@ void AI_EnemyRemoved(edict_t *ent)
int pos;
// watch for 0 players
if(num_players == 0)
if(num_AIEnemies < 1)
return;
// special case for only one player
if(num_players == 1)
if(num_AIEnemies == 1)
{
num_players = 0;
num_AIEnemies = 0;
return;
}
// Find the player
for(i=0;i<num_players;i++)
if(ent == players[i])
for(i=0;i<num_AIEnemies;i++)
if(ent == AIEnemies[i])
pos = i;
// decrement
for(i=pos;i<num_players-1;i++)
players[i] = players[i+1];
for( i=pos; i<num_AIEnemies-1; i++ )
AIEnemies[i] = AIEnemies[i+1];
num_players--;
num_AIEnemies--;
}
@ -211,25 +211,25 @@ float AI_ItemWeight(edict_t *self, edict_t *it)
//IT_WEAPON
if (it->item->flags & IT_WEAPON)
{
return self->ai.status.inventoryWeights[ITEM_INDEX(it->item)];
return self->ai->status.inventoryWeights[ITEM_INDEX(it->item)];
}
//IT_AMMO
if (it->item->flags & IT_AMMO)
{
return self->ai.status.inventoryWeights[ITEM_INDEX(it->item)];
return self->ai->status.inventoryWeights[ITEM_INDEX(it->item)];
}
//IT_ARMOR
if (it->item->flags & IT_ARMOR)
{
return self->ai.status.inventoryWeights[ITEM_INDEX(it->item)];
return self->ai->status.inventoryWeights[ITEM_INDEX(it->item)];
}
//IT_FLAG
if (it->item->flags & IT_FLAG)
{
return self->ai.status.inventoryWeights[ITEM_INDEX(it->item)];
return self->ai->status.inventoryWeights[ITEM_INDEX(it->item)];
}
//IT_HEALTH
@ -265,7 +265,7 @@ float AI_ItemWeight(edict_t *self, edict_t *it)
//IT_TECH
if (it->item->flags & IT_TECH)
{
return self->ai.status.inventoryWeights[ITEM_INDEX(it->item)];
return self->ai->status.inventoryWeights[ITEM_INDEX(it->item)];
}
//IT_STAY_COOP

View file

@ -407,7 +407,7 @@ int AI_GravityBoxStep( vec3_t origin, float scale, vec3_t destvec, vec3_t newori
droptofloor:
while(eternalfall < 20000000)
while(eternalfall < 20000)
{
if( gi.pointcontents(neworigin) & MASK_WATER ) {
@ -443,8 +443,8 @@ droptofloor:
eternalfall++;
}
gi.error ("ETERNAL FALL\n");
return 0;
//gi.error ("ETERNAL FALL\n");
return LINK_INVALID; //jabot092
}
//==========================================

View file

@ -33,8 +33,6 @@
#include "ai_nodes_local.h"
#include "ai_weapons.h"
#include "astar.h"
//bot debug_chase options
// extern cvar_t *bot_showpath;
@ -65,8 +63,8 @@ extern cvar_t *bot_debugmonster;
//acebot_items.c players table
//----------------------------------------------------------
extern int num_players;
extern edict_t *players[MAX_CLIENTS]; // pointers to all players in the game
extern int num_AIEnemies;
extern edict_t *AIEnemies[MAX_EDICTS]; // pointers to all players in the game
//Debug & creating and linking nodes
@ -112,26 +110,27 @@ qboolean AI_ItemIsReachable(edict_t *self,vec3_t goal);
// ai_movement.c
//----------------------------------------------------------
void AI_ChangeAngle (edict_t *ent);
qboolean AI_MoveToGoalEntity(edict_t *self, usercmd_t *ucmd);
qboolean AI_SpecialMove(edict_t *self, usercmd_t *ucmd);
qboolean AI_CanMove(edict_t *self, int direction);
qboolean AI_IsLadder(vec3_t origin, vec3_t v_angle, vec3_t mins, vec3_t maxs, edict_t *passent);
qboolean AI_IsStep(edict_t *ent);
qboolean AI_MoveToGoalEntity(edict_t *self, usercmd_t *ucmd);
qboolean AI_SpecialMove(edict_t *self, usercmd_t *ucmd);
qboolean AI_CanMove(edict_t *self, int direction);
qboolean AI_IsLadder(vec3_t origin, vec3_t v_angle, vec3_t mins, vec3_t maxs, edict_t *passent);
qboolean AI_IsStep (edict_t *ent);
// ai_navigation.c
//----------------------------------------------------------
int AI_FindCost(int from, int to, int movetypes);
int AI_FindClosestReachableNode( vec3_t origin, edict_t *passent, int range, int flagsmask );
void AI_SetGoal(edict_t *self, int goal_node);
qboolean AI_FollowPath(edict_t *self);
int AI_FindCost(int from, int to, int movetypes);
int AI_FindClosestReachableNode( vec3_t origin, edict_t *passent, int range, int flagsmask );
void AI_SetGoal(edict_t *self, int goal_node);
qboolean AI_FollowPath(edict_t *self);
// ai_nodes.c
//----------------------------------------------------------
qboolean AI_DropNodeOriginToFloor( vec3_t origin, edict_t *passent );
void AI_InitNavigationData(void);
int AI_FlagsForNode( vec3_t origin, edict_t *passent );
float AI_Distance( vec3_t o1, vec3_t o2 );
qboolean AI_DropNodeOriginToFloor( vec3_t origin, edict_t *passent );
void AI_InitNavigationData(void);
int AI_FlagsForNode( vec3_t origin, edict_t *passent );
float AI_Distance( vec3_t o1, vec3_t o2 );
void AITools_AddBotRoamNode(void);
@ -167,7 +166,7 @@ void AI_BotRoamFinishTimeouts(edict_t *self);
//bot_classes
//----------------------------------------------------------
void BOT_DMclass_InitPersistant(edict_t *self);
qboolean BOT_ChangeWeapon(edict_t *ent, gitem_t *item);
qboolean BOT_ChangeWeapon (edict_t *ent, gitem_t *item);
//ai_weapons.c
//----------------------------------------------------------

View file

@ -25,8 +25,8 @@
#include "../header/local.h"
#include "ai_local.h"
int num_players;
edict_t *players[MAX_CLIENTS]; // pointers to all players in the game
int num_AIEnemies;
edict_t *AIEnemies[MAX_EDICTS]; // pointers to all players in the game
ai_devel_t AIDevel;
// cvar_t *bot_showpath;
@ -69,24 +69,48 @@ void AI_NewMap(void)
AI_InitAIWeapons ();
}
//==========================================
// G_FreeAI
// removes the AI handle from memory
//==========================================
void G_FreeAI( edict_t *ent )
{
if( !ent->ai ) return;
gi.TagFree (ent->ai);
ent->ai = NULL;
}
//==========================================
// G_SpawnAI
// allocate ai_handle_t for this entity
//==========================================
void G_SpawnAI( edict_t *ent )
{
if( !ent->ai )
ent->ai = gi.TagMalloc (sizeof(ai_handle_t), TAG_LEVEL);
memset( ent->ai, 0, sizeof(ai_handle_t));
}
//==========================================
// AI_SetUpMoveWander
//==========================================
void AI_SetUpMoveWander( edict_t *ent )
{
ent->ai.state = BOT_STATE_WANDER;
ent->ai.wander_timeout = level.time + 1.0;
ent->ai.nearest_node_tries = 0;
ent->ai->state = BOT_STATE_WANDER;
ent->ai->wander_timeout = level.time + 1.0;
ent->ai->nearest_node_tries = 0;
ent->ai.next_move_time = level.time;
ent->ai.bloqued_timeout = level.time + 15.0;
ent->ai->next_move_time = level.time;
ent->ai->bloqued_timeout = level.time + 15.0;
ent->ai.goal_node = INVALID;
ent->ai.current_node = INVALID;
ent->ai.next_node = INVALID;
ent->ai->goal_node = INVALID;
ent->ai->current_node = INVALID;
ent->ai->next_node = INVALID;
}
//==========================================
// AI_ResetWeights
// Init bot weights from bot-class weights.
@ -94,8 +118,8 @@ void AI_SetUpMoveWander( edict_t *ent )
void AI_ResetWeights(edict_t *ent)
{
//restore defaults from bot persistant
memset(ent->ai.status.inventoryWeights, 0, sizeof (ent->ai.status.inventoryWeights));
memcpy(ent->ai.status.inventoryWeights, ent->ai.pers.inventoryWeights, sizeof(ent->ai.pers.inventoryWeights));
memset(ent->ai->status.inventoryWeights, 0, sizeof (ent->ai->status.inventoryWeights));
memcpy(ent->ai->status.inventoryWeights, ent->ai->pers.inventoryWeights, sizeof(ent->ai->pers.inventoryWeights));
}
@ -109,24 +133,24 @@ void AI_ResetNavigation(edict_t *ent)
ent->enemy = NULL;
ent->movetarget = NULL;
ent->ai.state_combat_timeout = 0.0;
ent->ai->state_combat_timeout = 0.0;
ent->ai.state = BOT_STATE_WANDER;
ent->ai.wander_timeout = level.time;
ent->ai.nearest_node_tries = 0;
ent->ai->state = BOT_STATE_WANDER;
ent->ai->wander_timeout = level.time;
ent->ai->nearest_node_tries = 0;
ent->ai.next_move_time = level.time;
ent->ai.bloqued_timeout = level.time + 15.0;
ent->ai->next_move_time = level.time;
ent->ai->bloqued_timeout = level.time + 15.0;
ent->ai.goal_node = INVALID;
ent->ai.current_node = INVALID;
ent->ai.next_node = INVALID;
ent->ai->goal_node = INVALID;
ent->ai->current_node = INVALID;
ent->ai->next_node = INVALID;
VectorSet( ent->ai.move_vector, 0, 0, 0 );
VectorSet( ent->ai->move_vector, 0, 0, 0 );
//reset bot_roams timeouts
for( i=0; i<nav.num_broams; i++)
ent->ai.status.broam_timeouts[i] = 0.0;
ent->ai->status.broam_timeouts[i] = 0.0;
}
@ -149,7 +173,7 @@ qboolean AI_BotRoamForLRGoal(edict_t *self, int current_node)
for( i=0; i<nav.num_broams; i++)
{
if( self->ai.status.broam_timeouts[i] > level.time)
if( self->ai->status.broam_timeouts[i] > level.time)
continue;
//limit cost finding by distance
@ -158,7 +182,7 @@ qboolean AI_BotRoamForLRGoal(edict_t *self, int current_node)
continue;
//find cost
cost = AI_FindCost(current_node, nav.broams[i].node, self->ai.pers.moveTypesMask);
cost = AI_FindCost(current_node, nav.broams[i].node, self->ai->pers.moveTypesMask);
if(cost == INVALID || cost < 3) // ignore invalid and very short hops
continue;
@ -177,11 +201,11 @@ qboolean AI_BotRoamForLRGoal(edict_t *self, int current_node)
return false;
//set up the goal
self->ai.state = BOT_STATE_MOVE;
self->ai.tries = 0; // Reset the count of how many times we tried this goal
self->ai->state = BOT_STATE_MOVE;
self->ai->tries = 0; // Reset the count of how many times we tried this goal
// if(AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: selected a bot roam of weight %f at node %d for LR goal.\n",self->ai.pers.netname, nav.broams[best_broam].weight, goal_node);
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: selected a bot roam of weight %f at node %d for LR goal.\n",self->ai->pers.netname, nav.broams[best_broam].weight, goal_node);
AI_SetGoal(self,goal_node);
@ -209,23 +233,22 @@ void AI_PickLongRangeGoal(edict_t *self)
float dist;
// look for a target
current_node = AI_FindClosestReachableNode(self->s.origin, self,((1+self->ai.nearest_node_tries)*NODE_DENSITY),NODE_ALL);
self->ai.current_node = current_node;
current_node = AI_FindClosestReachableNode(self->s.origin, self,((1+self->ai->nearest_node_tries)*NODE_DENSITY),NODE_ALL);
self->ai->current_node = current_node;
if(current_node == -1) //failed. Go wandering :(
{
// if (AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: LRGOAL: Closest node not found. Tries:%i\n", self->ai.pers.netname, self->ai.nearest_node_tries);
// Com_Printf( "%s: LRGOAL: Closest node not found. Tries:%i\n", self->ai.pers.netname, self->ai.nearest_node_tries);
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: LRGOAL: Closest node not found. Tries:%i\n", self->ai->pers.netname, self->ai->nearest_node_tries);
if( self->ai.state != BOT_STATE_WANDER )
if( self->ai->state != BOT_STATE_WANDER )
AI_SetUpMoveWander( self );
self->ai.wander_timeout = level.time + 1.0;
self->ai.nearest_node_tries++; //extend search radius with each try
self->ai->wander_timeout = level.time + 1.0;
self->ai->nearest_node_tries++; //extend search radius with each try
return;
}
self->ai.nearest_node_tries = 0;
self->ai->nearest_node_tries = 0;
// Items
@ -256,7 +279,7 @@ void AI_PickLongRangeGoal(edict_t *self)
if( nav.items[i].ent->item->flags & (IT_WEAPON|IT_FLAG) && dist > 10000 )
continue;
cost = AI_FindCost(current_node, nav.items[i].node, self->ai.pers.moveTypesMask);
cost = AI_FindCost(current_node, nav.items[i].node, self->ai->pers.moveTypesMask);
if(cost == INVALID || cost < 3) // ignore invalid and very short hops
continue;
@ -273,24 +296,24 @@ void AI_PickLongRangeGoal(edict_t *self)
// Players: This should be its own function and is for now just finds a player to set as the goal.
for(i=0;i<num_players;i++)
for( i=0; i<num_AIEnemies; i++ )
{
//ignore self & spectators
if(players[i] == self || players[i]->svflags & SVF_NOCLIENT)
if( AIEnemies[i] == self || AIEnemies[i]->svflags & SVF_NOCLIENT)
continue;
//ignore zero weighted players
if( self->ai.status.playersWeights[i] == 0.0f )
if( self->ai->status.playersWeights[i] == 0.0f )
continue;
node = AI_FindClosestReachableNode( players[i]->s.origin, players[i], NODE_DENSITY, NODE_ALL);
cost = AI_FindCost(current_node, node, self->ai.pers.moveTypesMask);
node = AI_FindClosestReachableNode( AIEnemies[i]->s.origin, AIEnemies[i], NODE_DENSITY, NODE_ALL);
cost = AI_FindCost(current_node, node, self->ai->pers.moveTypesMask);
if(cost == INVALID || cost < 4) // ignore invalid and very short hops
continue;
//precomputed player weights
weight = self->ai.status.playersWeights[i];
weight = self->ai->status.playersWeights[i];
//weight *= random(); // Allow random variations
weight /= cost; // Check against cost of getting there
@ -299,7 +322,7 @@ void AI_PickLongRangeGoal(edict_t *self)
{
best_weight = weight;
goal_node = node;
// goal_ent = players[i];
// goal_ent = AIEnemies[i];
}
}
@ -309,24 +332,21 @@ void AI_PickLongRangeGoal(edict_t *self)
//BOT_ROAMS
if (!AI_BotRoamForLRGoal(self, current_node))
{
self->ai.goal_node = INVALID;
self->ai.state = BOT_STATE_WANDER;
self->ai.wander_timeout = level.time + 1.0;
self->ai->goal_node = INVALID;
self->ai->state = BOT_STATE_WANDER;
self->ai->wander_timeout = level.time + 1.0;
// if(AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: did not find a LR goal, wandering.\n",self->ai.pers.netname);
// Com_Printf( "%s: did not find a LR goal, wandering.\n",self->ai.pers.netname);
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: did not find a LR goal, wandering.\n",self->ai->pers.netname);
}
return; // no path?
}
// OK, everything valid, let's start moving to our goal.
self->ai.state = BOT_STATE_MOVE;
self->ai.tries = 0; // Reset the count of how many times we tried this goal
self->ai->state = BOT_STATE_MOVE;
self->ai->tries = 0; // Reset the count of how many times we tried this goal
// if(goal_ent != NULL && AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: selected a %s at node %d for LR goal.\n",self->ai.pers.netname, goal_ent->classname, goal_node);
// if(goal_ent != NULL )
// Com_Printf( "%s: selected a %s at node %d for LR goal.\n",self->ai.pers.netname, goal_ent->classname, goal_node);
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: selected a %s at node %d for LR goal.\n",self->ai->pers.netname, goal_ent->classname, goal_node);
AI_SetGoal(self,goal_node);
}
@ -359,10 +379,10 @@ void AI_PickShortRangeGoal(edict_t *self)
if(strcmp(target->classname,"rocket")==0 || strcmp(target->classname,"grenade")==0)
{
//if player who shoot is a potential enemy
if (self->ai.status.playersWeights[target->owner->s.number-1])
if (self->ai->status.playersWeights[target->owner->s.number-1])
{
// if(AIDevel.debugChased && bot_showcombat->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: ROCKET ALERT!\n", self->ai.pers.netname);
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: ROCKET ALERT!\n", self->ai->pers.netname);
self->enemy = target->owner; // set who fired the rocket as enemy
return;
@ -393,7 +413,7 @@ void AI_PickShortRangeGoal(edict_t *self)
self->movetarget = best;
self->goalentity = best;
// if(AIDevel.debugChased && bot_showsrgoal->value && (self->goalentity != self->movetarget))
// gi.cprintf(NULL, PRINT_HIGH, "%s: selected a %s for SR goal.\n",self->ai.pers.netname, self->movetarget->classname);
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: selected a %s for SR goal.\n",self->ai->pers.netname, self->movetarget->classname);
}
}
@ -406,20 +426,20 @@ void AI_CategorizePosition (edict_t *ent)
{
qboolean stepping = AI_IsStep(ent);
ent->ai.was_swim = ent->ai.is_swim;
ent->ai.was_step = ent->ai.is_step;
ent->was_swim = ent->is_swim;
ent->was_step = ent->is_step;
ent->ai.is_ladder = AI_IsLadder( ent->s.origin, ent->s.angles, ent->mins, ent->maxs, ent );
ent->is_ladder = AI_IsLadder( ent->s.origin, ent->s.angles, ent->mins, ent->maxs, ent );
M_CatagorizePosition(ent);
if (ent->waterlevel > 2 || (ent->waterlevel && !stepping)) {
ent->ai.is_swim = true;
ent->ai.is_step = false;
ent->is_swim = true;
ent->is_step = false;
return;
}
ent->ai.is_swim = false;
ent->ai.is_step = stepping;
ent->is_swim = false;
ent->is_step = stepping;
}
@ -429,46 +449,49 @@ void AI_CategorizePosition (edict_t *ent)
//==========================================
void AI_Think (edict_t *self)
{
if( !self->ai ) //jabot092(2)
return;
AIDebug_SetChased(self); //jal:debug shit
AI_CategorizePosition(self);
//freeze AI when dead
if( self->deadflag ) {
self->ai.pers.deadFrame(self);
self->ai->pers.deadFrame(self);
return;
}
//if completely stuck somewhere
if(VectorLength(self->velocity) > 37)
self->ai.bloqued_timeout = level.time + 10.0;
self->ai->bloqued_timeout = level.time + 10.0;
if( self->ai.bloqued_timeout < level.time ) {
self->ai.pers.bloquedTimeout(self);
if( self->ai->bloqued_timeout < level.time ) {
self->ai->pers.bloquedTimeout(self);
return;
}
//update status information to feed up ai
self->ai.pers.UpdateStatus(self);
self->ai->pers.UpdateStatus(self);
//update position in path, set up move vector
if( self->ai.state == BOT_STATE_MOVE ) {
if( self->ai->state == BOT_STATE_MOVE ) {
if( !AI_FollowPath(self) )
{
AI_SetUpMoveWander( self );
self->ai.wander_timeout = level.time - 1; //do it now
self->ai->wander_timeout = level.time - 1; //do it now
}
}
//pick a new long range goal
if( self->ai.state == BOT_STATE_WANDER && self->ai.wander_timeout < level.time)
if( self->ai->state == BOT_STATE_WANDER && self->ai->wander_timeout < level.time)
AI_PickLongRangeGoal(self);
//Find any short range goal
AI_PickShortRangeGoal(self);
//run class based states machine
self->ai.pers.RunFrame(self);
self->ai->pers.RunFrame(self);
}

View file

@ -66,7 +66,6 @@ qboolean AI_CanMove(edict_t *self, int direction)
VectorSet(offset, 36, 0, -100);
G_ProjectSource (self->s.origin, offset, forward, right, end);
// trap_Trace(&tr, start, NULL, NULL, end, self, MASK_AISOLID);
tr = gi.trace( start, NULL, NULL, end, self, MASK_AISOLID );
if(tr.fraction == 1.0 || tr.contents & (CONTENTS_LAVA|CONTENTS_SLIME))
@ -208,25 +207,7 @@ qboolean AI_SpecialMove(edict_t *self, usercmd_t *ucmd)
if( !tr.startsolid && tr.fraction == 1.0 ) // not bloqued
return false;
if( self->ai.pers.moveTypesMask & LINK_CROUCH || self->ai.is_swim )
{
//crouch box
VectorCopy( self->s.origin, boxorigin );
VectorCopy( self->mins, boxmins );
VectorCopy( self->maxs, boxmaxs );
boxmaxs[2] = 14; //crouched size
VectorMA( boxorigin, 8, forward, boxorigin ); //move box by 8 to front
//see if bloqued
tr = gi.trace( boxorigin, boxmins, boxmaxs, boxorigin, self, MASK_AISOLID);
if( !tr.startsolid ) // can move by crouching
{
ucmd->forwardmove = 400;
ucmd->upmove = -400;
return true;
}
}
if( self->ai.pers.moveTypesMask & LINK_JUMP && self->groundentity )
if( self->ai->pers.moveTypesMask & LINK_JUMP && self->groundentity )
{
//jump box
VectorCopy( self->s.origin, boxorigin );
@ -251,6 +232,24 @@ qboolean AI_SpecialMove(edict_t *self, usercmd_t *ucmd)
}
}
if( self->ai->pers.moveTypesMask & LINK_CROUCH || self->is_swim )
{
//crouch box
VectorCopy( self->s.origin, boxorigin );
VectorCopy( self->mins, boxmins );
VectorCopy( self->maxs, boxmaxs );
boxmaxs[2] = 14; //crouched size
VectorMA( boxorigin, 8, forward, boxorigin ); //move box by 8 to front
//see if bloqued
tr = gi.trace( boxorigin, boxmins, boxmaxs, boxorigin, self, MASK_AISOLID);
if( !tr.startsolid ) // can move by crouching
{
ucmd->forwardmove = 400;
ucmd->upmove = -400;
return true;
}
}
//nothing worked, check for turning
return AI_CheckEyes(self, ucmd);
}
@ -274,12 +273,12 @@ void AI_ChangeAngle (edict_t *ent)
vec3_t ideal_angle;
// Normalize the move angle first
VectorNormalize(ent->ai.move_vector);
VectorNormalize(ent->ai->move_vector);
current_yaw = anglemod(ent->s.angles[YAW]);
current_pitch = anglemod(ent->s.angles[PITCH]);
vectoangles (ent->ai.move_vector, ideal_angle);
vectoangles (ent->ai->move_vector, ideal_angle);
ideal_yaw = anglemod(ideal_angle[YAW]);
ideal_pitch = anglemod(ideal_angle[PITCH]);
@ -360,7 +359,7 @@ qboolean AI_MoveToGoalEntity(edict_t *self, usercmd_t *ucmd)
!Q_stricmp(self->movetarget->classname,"grenade") ||
!Q_stricmp(self->movetarget->classname,"hgrenade"))
{
VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector);
VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai->move_vector);
AI_ChangeAngle(self);
// if(AIDevel.debugChased && bot_showcombat->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: Oh crap a rocket!\n",self->ai.pers.netname);
@ -375,7 +374,7 @@ qboolean AI_MoveToGoalEntity(edict_t *self, usercmd_t *ucmd)
}
// Set bot's movement direction
VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai.move_vector);
VectorSubtract (self->movetarget->s.origin, self->s.origin, self->ai->move_vector);
AI_ChangeAngle(self);
if(!AI_CanMove(self, BOT_MOVE_FORWARD) )
{

View file

@ -30,7 +30,7 @@
#include "../header/local.h"
#include "ai_local.h"
#include "astar.h"
//==========================================
@ -98,34 +98,16 @@ int AI_FindClosestReachableNode( vec3_t origin, edict_t *passent, int range, int
return node;
}
//==========================================
// AI_SetupPath
//==========================================
int AI_SetupPath( edict_t *self, int from, int to, int movetypes )
{
if( self->ai.path != NULL) {
free( self->ai.path );
self->ai.path = NULL;
}
self->ai.path = malloc( sizeof(astarpath_t) );
if( !AStar_GetPath( from, to, movetypes, self->ai.path ) )
return -1;
return self->ai.path->numNodes;
}
//==========================================
// AI_SetGoal
// set the goal
// set the goal //jabot092
//==========================================
void AI_SetGoal(edict_t *self, int goal_node)
{
int node;
self->ai.goal_node = goal_node;
self->ai->goal_node = goal_node;
node = AI_FindClosestReachableNode( self->s.origin, self, NODE_DENSITY*3, NODE_ALL );
if(node == -1) {
@ -134,28 +116,26 @@ void AI_SetGoal(edict_t *self, int goal_node)
}
//------- ASTAR -----------
if(!AI_SetupPath( self, node, goal_node, self->ai.pers.moveTypesMask ))
if( !AStar_GetPath( node, goal_node, self->ai->pers.moveTypesMask, &self->ai->path ) )
{
AI_SetUpMoveWander(self);
return;
}
self->ai.path_position = 0;
self->ai.current_node = self->ai.path->nodes[self->ai.path_position];
self->ai->current_node = self->ai->path.nodes[self->ai->path.numNodes];
//-------------------------
// if(AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: GOAL: new START NODE selected %d\n", self->ai.pers.netname, node);
self->ai.next_node = self->ai.current_node; // make sure we get to the nearest node first
self->ai.node_timeout = 0;
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: GOAL: new START NODE selected %d\n", self->ai->pers.netname, node);
self->ai->next_node = self->ai->current_node; // make sure we get to the nearest node first
self->ai->node_timeout = 0;
}
//==========================================
// AI_FollowPath
// Move closer to goal by pointing the bot to the next node
// that is closer to the goal
// that is closer to the goal //jabot092 (path-> to path.)
//==========================================
qboolean AI_FollowPath( edict_t *self )
{
@ -167,56 +147,57 @@ qboolean AI_FollowPath( edict_t *self )
if(bot_showpath->value)
{
if( AIDevel.debugChased )
AITools_DrawPath(self, self->ai.current_node, self->ai.goal_node);
AITools_DrawPath(self, self->ai->current_node, self->ai->goal_node);
}
*/
if( self->ai.goal_node == INVALID )
if( self->ai->goal_node == INVALID )
return false;
// Try again?
if(self->ai.node_timeout++ > 30)
if(self->ai->node_timeout++ > 30)
{
if(self->ai.tries++ > 3)
if(self->ai->tries++ > 3)
return false;
else
AI_SetGoal( self, self->ai.goal_node );
AI_SetGoal( self, self->ai->goal_node );
}
// Are we there yet?
VectorSubtract( self->s.origin, nodes[self->ai.next_node].origin, v );
VectorSubtract( self->s.origin, nodes[self->ai->next_node].origin, v );
dist = VectorLength(v);
//special lower plat reached check
if( dist < 64
&& nodes[self->ai.current_node].flags & NODEFLAGS_PLATFORM
&& nodes[self->ai.next_node].flags & NODEFLAGS_PLATFORM
&& nodes[self->ai->current_node].flags & NODEFLAGS_PLATFORM
&& nodes[self->ai->next_node].flags & NODEFLAGS_PLATFORM
&& self->groundentity && self->groundentity->use == Use_Plat)
dist = 16;
if( (dist < 32 && nodes[self->ai.next_node].flags != NODEFLAGS_JUMPPAD && nodes[self->ai.next_node].flags != NODEFLAGS_TELEPORTER_IN)
|| (self->ai.status.jumpadReached && nodes[self->ai.next_node].flags & NODEFLAGS_JUMPPAD)
|| (self->ai.status.TeleportReached && nodes[self->ai.next_node].flags & NODEFLAGS_TELEPORTER_IN) )
if( (dist < 32 && nodes[self->ai->next_node].flags != NODEFLAGS_JUMPPAD && nodes[self->ai->next_node].flags != NODEFLAGS_TELEPORTER_IN)
|| (self->ai->status.jumpadReached && nodes[self->ai->next_node].flags & NODEFLAGS_JUMPPAD)
|| (self->ai->status.TeleportReached && nodes[self->ai->next_node].flags & NODEFLAGS_TELEPORTER_IN) )
{
// reset timeout
self->ai.node_timeout = 0;
self->ai->node_timeout = 0;
if( self->ai.next_node == self->ai.goal_node )
if( self->ai->next_node == self->ai->goal_node )
{
// if(AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: GOAL REACHED!\n", self->ai.pers.netname);
//if(AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: GOAL REACHED!\n", self->ai->pers.netname);
//if botroam, setup a timeout for it
if( nodes[self->ai.goal_node].flags & NODEFLAGS_BOTROAM )
if( nodes[self->ai->goal_node].flags & NODEFLAGS_BOTROAM )
{
int i;
for( i=0; i<nav.num_broams; i++) { //find the broam
if( nav.broams[i].node != self->ai.goal_node )
if( nav.broams[i].node != self->ai->goal_node )
continue;
// if(AIDevel.debugChased && bot_showlrgoal->value)
// gi.cprintf(NULL, PRINT_HIGH, "%s: BotRoam Time Out set up for node %i\n", self->ai.pers.netname, nav.broams[i].node);
self->ai.status.broam_timeouts[i] = level.time + 15.0;
//if(AIDevel.debugChased && bot_showlrgoal->integer)
// gi.cprintf(AIDevel.chaseguy, PRINT_HIGH, "%s: BotRoam Time Out set up for node %i\n", self->ai->pers.netname, nav.broams[i].node);
//Com_Printf( "%s: BotRoam Time Out set up for node %i\n", self->ai->pers.netname, nav.broams[i].node);
self->ai->status.broam_timeouts[i] = level.time + 15.0;
break;
}
}
@ -226,20 +207,17 @@ qboolean AI_FollowPath( edict_t *self )
}
else
{
self->ai.current_node = self->ai.next_node;
self->ai.next_node = self->ai.path->nodes[self->ai.path_position++];
// if(AIDevel.debugChased && bot_showpath->value > 1 )
// gi.cprintf(NULL, PRINT_HIGH, "%s: CurrentNode(%i):%s NextNode(%i):%s\n", self->ai.pers.netname, self->ai.current_node, nodeTypeNames[nodes[self->ai.current_node].type], self->ai.next_node, nodeTypeNames[nodes[self->ai.next_node].type]);
self->ai->current_node = self->ai->next_node;
if( self->ai->path.numNodes )
self->ai->path.numNodes--;
self->ai->next_node = self->ai->path.nodes[self->ai->path.numNodes];
}
}
if( self->ai.current_node == -1 || self->ai.next_node == -1 )
if(self->ai->current_node == -1 || self->ai->next_node == -1)
return false;
// Set bot's movement vector
VectorSubtract( nodes[self->ai.next_node].origin, self->s.origin , self->ai.move_vector );
VectorSubtract( nodes[self->ai->next_node].origin, self->s.origin , self->ai->move_vector );
return true;
}

View file

@ -52,7 +52,7 @@ qboolean AI_DropNodeOriginToFloor( vec3_t origin, edict_t *passent )
{
trace_t trace;
//trap_Trace ( &trace, origin, tv(-15, -15, 0), tv(15, 15, 0), tv(origin[0], origin[1], world->mins[2]), NULL, MASK_NODESOLID );//jalfixme. 0?
//trap_Trace ( &trace, origin, tv(-15, -15, 0), tv(15, 15, 0), tv(origin[0], origin[1], world->mins[2]), NULL, MASK_NODESOLID );
trace = gi.trace( origin, tv(-15, -15, 0), tv(15, 15, 0), tv(origin[0], origin[1], origin[2]-2048), passent, MASK_NODESOLID );
if( trace.startsolid )
return false;
@ -109,16 +109,16 @@ void AI_JumpadGuess_ShowPoint( vec3_t origin, char *modelname )
ent->movetype = MOVETYPE_NONE;
ent->clipmask = MASK_WATER;
ent->solid = SOLID_NOT;
ent->s.type = ET_GENERIC;
ent->s.renderfx |= RF_NOSHADOW;
// ent->s.type = ET_GENERIC;
// ent->s.renderfx |= RF_NOSHADOW;
VectorClear ( ent->mins );
VectorClear ( ent->maxs );
ent->s.modelindex = trap_ModelIndex ( modelname );
ent->s.modelindex = gi.modelindex ( modelname );
ent->nextthink = level.time + 20000;
ent->think = G_FreeEdict;
ent->classname = "checkent";
trap_LinkEntity (ent);
gi.linkentity (ent);
}
#endif
@ -139,6 +139,9 @@ qboolean AI_PredictJumpadDestity( edict_t *ent, vec3_t out )
VectorClear( out );
if( !ent->target ) //jabot092
return false;
// get target entity
target = G_Find ( NULL, FOFS(targetname), ent->target );
if (!target)
@ -157,7 +160,6 @@ qboolean AI_PredictJumpadDestity( edict_t *ent, vec3_t out )
floor_target_origin[2] = pad_origin[2]; //put at pad's height
//make a guess on how player movement will affect the trajectory
//tmpfloat = Distance( pad_origin, floor_target_origin );
tmpfloat = AI_Distance( pad_origin, floor_target_origin );
htime = sqrt ((tmpfloat));
vtime = sqrt ((target->s.origin[2] - pad_origin[2]));
@ -185,7 +187,7 @@ qboolean AI_PredictJumpadDestity( edict_t *ent, vec3_t out )
#ifdef SHOW_JUMPAD_GUESS
// this is our top of the curve point, and the original target
AI_JumpadGuess_ShowPoint( target_origin, "models/powerups/health/mega_sphere.md3" );
AI_JumpadGuess_ShowPoint( target_origin, "models/objects/grenade2/tris.md2" );
AI_JumpadGuess_ShowPoint( target->s.origin, "models/powerups/health/large_cross.md3" );
#endif
@ -214,7 +216,7 @@ qboolean AI_PredictJumpadDestity( edict_t *ent, vec3_t out )
#ifdef SHOW_JUMPAD_GUESS
// destiny found
AI_JumpadGuess_ShowPoint( trace.endpos, "models/powerups/health/mega_sphere.md3" );
AI_JumpadGuess_ShowPoint( trace.endpos, "models/objects/grenade2/tris.md2" );
#endif
VectorCopy ( trace.endpos, out );
@ -270,7 +272,7 @@ int AI_AddNode_JumpPad( edict_t *ent )
//==========================================
// AI_AddNode_Door
// AI_AddNode_Door - //jabot092(2)
// Drop a node at each side of the door
// and force them to link. Only typical
// doors are covered.
@ -279,8 +281,10 @@ int AI_AddNode_Door( edict_t *ent )
{
edict_t *other;
vec3_t mins, maxs;
vec3_t forward, right, up;
vec3_t door_origin;
vec3_t crossdir;
vec3_t MOVEDIR_UP = {0, 0, 1};
vec3_t MOVEDIR_DOWN = {0, 0, -1};
if (ent->flags & FL_TEAMSLAVE)
return INVALID; //only team master will drop the nodes
@ -299,56 +303,108 @@ int AI_AddNode_Door( edict_t *ent )
door_origin[1] = (maxs[1] - mins[1]) / 2 + mins[1];
door_origin[2] = (maxs[2] - mins[2]) / 2 + mins[2];
//now find the crossing angle
AngleVectors( ent->s.angles, forward, right, up );
VectorNormalize( right );
//if it moves in y axis we don't know if it's walked to north or east, so
// we must try dropping nodes in both directions and check for solids
if (VectorCompare(MOVEDIR_UP, ent->movedir) || VectorCompare(MOVEDIR_DOWN, ent->movedir) )
{
//now find the crossing angle
AngleVectors( ent->s.angles, crossdir, NULL, NULL );
VectorNormalize( crossdir );
//add node
nodes[nav.num_nodes].flags = 0;
VectorMA( door_origin, 32, crossdir, nodes[nav.num_nodes].origin);
if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) )
{
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
#ifdef SHOW_JUMPAD_GUESS
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/objects/grenade2/tris.md2" );
#endif
nav.num_nodes++;
}
//add node 2
nodes[nav.num_nodes].flags = 0;
VectorMA( door_origin, -32, crossdir, nodes[nav.num_nodes].origin);
if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) )
{
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
#ifdef SHOW_JUMPAD_GUESS
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/objects/grenade2/tris.md2" );
#endif
//add links in both directions
AI_AddLink( nav.num_nodes, nav.num_nodes-1, LINK_MOVE );
AI_AddLink( nav.num_nodes-1, nav.num_nodes, LINK_MOVE );
nav.num_nodes++;
}
}
//find the crossing angle
AngleVectors( ent->s.angles, NULL, crossdir, NULL ); //jabot092(2)
VectorNormalize( crossdir );
//add node
nodes[nav.num_nodes].flags = 0/*NODEFLAGS_SERVERLINK*/;
VectorMA( door_origin, 32, right, nodes[nav.num_nodes].origin);
AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL );
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
nodes[nav.num_nodes].flags = 0;
VectorMA( door_origin, 32, crossdir, nodes[nav.num_nodes].origin);
if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) )
{
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
#ifdef SHOW_JUMPAD_GUESS
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/powerups/health/mega_sphere.md3" );
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/objects/grenade2/tris.md2" );
#endif
nav.num_nodes++;
nav.num_nodes++;
}
//add node 2
nodes[nav.num_nodes].flags = 0/*NODEFLAGS_SERVERLINK*/;
VectorMA( door_origin, -32, right, nodes[nav.num_nodes].origin);
AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL );
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
nodes[nav.num_nodes].flags = 0;
VectorMA( door_origin, -32, crossdir, nodes[nav.num_nodes].origin);
if( AI_DropNodeOriginToFloor( nodes[nav.num_nodes].origin, NULL ) )
{
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
#ifdef SHOW_JUMPAD_GUESS
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/powerups/health/mega_sphere.md3" );
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/objects/grenade2/tris.md2" );
#endif
//add links in both directions
AI_AddLink( nav.num_nodes, nav.num_nodes-1, LINK_MOVE );
AI_AddLink( nav.num_nodes-1, nav.num_nodes, LINK_MOVE );
//add links in both directions
AI_AddLink( nav.num_nodes, nav.num_nodes-1, LINK_MOVE );
AI_AddLink( nav.num_nodes-1, nav.num_nodes, LINK_MOVE );
nav.num_nodes++;
}
nav.num_nodes++;
return nav.num_nodes-1;
}
//==========================================
// AI_AddNode_Platform
// AI_AddNode_Platform - //jabot092(2)
// drop two nodes one at top, one at bottom
//==========================================
int AI_AddNode_Platform( edict_t *ent )
{
vec3_t v1,v2;
float plat_dist;
if (nav.num_nodes + 1 > MAX_NODES)
if (nav.num_nodes + 2 > MAX_NODES)
return INVALID;
if (ent->flags & FL_TEAMSLAVE)
return INVALID; //only team master will drop the nodes
plat_dist = ent->pos1[2] - ent->pos2[2]; //jabot092(2)
// Upper node
nodes[nav.num_nodes].flags = (NODEFLAGS_PLATFORM|NODEFLAGS_SERVERLINK|NODEFLAGS_FLOAT);
VectorCopy( ent->maxs, v1 );
VectorCopy( ent->mins, v2 );
nodes[nav.num_nodes].origin[0] = (v1[0] - v2[0]) / 2 + v2[0];
nodes[nav.num_nodes].origin[1] = (v1[1] - v2[1]) / 2 + v2[1];
nodes[nav.num_nodes].origin[2] = ent->maxs[2] + 8;
nodes[nav.num_nodes].origin[2] = ent->mins[2] + plat_dist + 16;//jabot092(2)
#ifdef SHOW_JUMPAD_GUESS
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/objects/grenade2/tris.md2" );
#endif
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
//put into ents table
@ -362,8 +418,10 @@ int AI_AddNode_Platform( edict_t *ent )
nodes[nav.num_nodes].flags = (NODEFLAGS_PLATFORM|NODEFLAGS_SERVERLINK|NODEFLAGS_FLOAT);
nodes[nav.num_nodes].origin[0] = nodes[nav.num_nodes-1].origin[0];
nodes[nav.num_nodes].origin[1] = nodes[nav.num_nodes-1].origin[1];
nodes[nav.num_nodes].origin[2] = ent->mins[2] + (AI_JUMPABLE_HEIGHT - 1);
nodes[nav.num_nodes].origin[2] = ent->mins[2] + (AI_JUMPABLE_HEIGHT - 1); //jabot092(2)
#ifdef SHOW_JUMPAD_GUESS
AI_JumpadGuess_ShowPoint( nodes[nav.num_nodes].origin, "models/objects/grenade2/tris.md2" );
#endif
nodes[nav.num_nodes].flags |= AI_FlagsForNode( nodes[nav.num_nodes].origin, NULL );
//put into ents table
@ -909,13 +967,13 @@ void AI_InitNavigationData(void)
return;
}
servernodesstart = nav.num_nodes;
for( linkscount = 0, i = 0; i< nav.num_nodes; i++ )
{
linkscount += pLinks[i].numLinks;
}
servernodesstart = nav.num_nodes;
//create nodes for map entities
AI_CreateNodesForEntities();
newlinks = AI_LinkServerNodes( servernodesstart );

View file

@ -34,7 +34,6 @@
//
//=============================================================
#define MAX_NODES 2048 //jalToDo: needs dynamic alloc (big terrain maps)
#define NODE_DENSITY 128 // Density setting for nodes
#define INVALID -1
#define NODES_MAX_PLINKS 16

View file

@ -30,8 +30,8 @@
//
//==========================================
static int alist[MAX_NODES]; //list contains all studied nodes, Open and Closed together
static int alist_numNodes;
static int alist[MAX_NODES]; //list contains all studied nodes, Open and Closed together
static int alist_numNodes;
enum {
NOLIST,
@ -41,18 +41,17 @@ enum {
typedef struct
{
int parent;
int parent;
int G;
int H;
int list;
int list;
} astarnode_t;
astarnode_t astarnodes[MAX_NODES];
static int Apath[MAX_NODES];
static int Apath_numNodes;
struct astarpath_s *Apath;
//==========================================
//
//
@ -61,8 +60,7 @@ static int originNode;
static int goalNode;
static int currentNode;
int ValidLinksMask;
static int ValidLinksMask;
#define DEFAULT_MOVETYPES_MASK ( \
LINK_MOVE | \
LINK_STAIRS | \
@ -71,30 +69,13 @@ int ValidLinksMask;
LINK_WATERJUMP | \
LINK_JUMPPAD | \
LINK_PLATFORM | \
LINK_TELEPORT);
LINK_TELEPORT)
//==========================================
//
//
//
//==========================================
int AStar_nodeIsInPath(int node)
{
int i;
if( !Apath_numNodes )
return 0;
for (i=0; i<Apath_numNodes; i++)
{
if(node == Apath[i])
return 1;
}
return 0;
}
int AStar_nodeIsInClosed( int node )
{
if( astarnodes[node].list == CLOSEDLIST )
@ -117,38 +98,33 @@ static void AStar_InitLists (void)
for ( i=0; i<MAX_NODES; i++ )
{
Apath[i] = 0;
astarnodes[i].G = 0;
astarnodes[i].H = 0;
astarnodes[i].parent = 0;
astarnodes[i].list = NOLIST;
}
Apath_numNodes = 0;
if( Apath )
Apath->numNodes = 0;
alist_numNodes = 0;
for( i=0; i<MAX_NODES; i++ )
alist[i] = -1;
memset( alist, -1, sizeof(alist));//jabot092
}
static int AStar_PLinkDistance(int n1, int n2)
static int
AStar_PLinkDistance(int n1, int n2)
{
int i;
int found = 0;
int dist;
int i;
for ( i=0; i<pLinks[n1].numLinks; i++)
{
if( pLinks[n1].nodes[i] == n2 ) {
found = 1;
dist = (int)pLinks[n1].dist[i];
if (pLinks[n1].nodes[i] == n2)
{
return (int)pLinks[n1].dist[i];
}
}
if(!found)
return -1;
return dist;
return -1;
}
static int Astar_HDist_ManhatanGuess( int node )
@ -165,11 +141,7 @@ static int Astar_HDist_ManhatanGuess( int node )
for (i=0 ; i<3 ; i++)
{
DistVec[i] = nodes[goalNode].origin[i] - nodes[node].origin[i];
if( DistVec[i] < 0.0f )
{
DistVec[i] = -DistVec[i]; //use only positive values. We don't care about direction.
}
DistVec[i] = fabs(nodes[goalNode].origin[i] - nodes[node].origin[i]);
}
HDist = (int)(DistVec[0] + DistVec[1] + DistVec[2]);
@ -187,7 +159,8 @@ static void AStar_PutInClosed( int node )
astarnodes[node].list = CLOSEDLIST;
}
static void AStar_PutAdjacentsInOpen(int node)
static void
AStar_PutAdjacentsInOpen(int node)
{
int i;
@ -232,9 +205,7 @@ static void AStar_PutAdjacentsInOpen(int node)
astarnodes[addnode].G = astarnodes[node].G + plinkDist;
}
}
else
{ //just put it in
} else { //just put it in
int plinkDist;
@ -243,9 +214,7 @@ static void AStar_PutAdjacentsInOpen(int node)
{
plinkDist = AStar_PLinkDistance( addnode, node );
if( plinkDist == -1)
{
plinkDist = 999;//jalFIXME
}
if (bot_debugmonster->value)
{
@ -302,21 +271,19 @@ static void AStar_ListsToPath ( void )
{
int count = 0;
int cur = goalNode;
int *pnode;
Apath->numNodes = 0;
pnode = Apath->nodes;
while ( cur != originNode )
{
*pnode = cur;
pnode++;
cur = astarnodes[cur].parent;
count++;
}
cur = goalNode;
while ( count >= 0 )
{
Apath[count] = cur;
Apath_numNodes++;
count--;
cur = astarnodes[cur].parent;
}
Apath->numNodes = count-1;
}
static int AStar_FillLists ( void )
@ -333,7 +300,7 @@ static int AStar_FillLists ( void )
return (currentNode != -1); //if -1 path is bloqued
}
int AStar_ResolvePath ( int n1, int n2, int movetypes )
static int AStar_ResolvePath ( int n1, int n2, int movetypes )
{
ValidLinksMask = movetypes;
if ( !ValidLinksMask )
@ -357,30 +324,20 @@ int AStar_ResolvePath ( int n1, int n2, int movetypes )
AStar_ListsToPath();
if (bot_debugmonster->value)
{
Com_Printf("RESULT:\n Origin:%i\n Goal:%i\n numNodes:%i\n FirstInPath:%i\n LastInPath:%i\n",
originNode, goalNode, Apath_numNodes, Apath[0], Apath[Apath_numNodes-1]);
}
return 1;
}
int AStar_GetPath( int origin, int goal, int movetypes, struct astarpath_s *path )
int
AStar_GetPath(int origin, int goal, int movetypes, struct astarpath_s *path)
{
int i;
Apath = path;
if( !AStar_ResolvePath ( origin, goal, movetypes ) )
{
return 0;
}
path->numNodes = Apath_numNodes;
path->originNode = origin;
path->goalNode = goal;
for(i=0; i<path->numNodes; i++)
{
path->nodes[i] = Apath[i];
}
return 1;
}

View file

@ -28,20 +28,6 @@
* in NO WAY supported by Steve Yeager.
*/
//==========================================
//
//
//==========================================
typedef struct astarpath_s
{
int numNodes;
int nodes[MAX_NODES];
int originNode;
int goalNode;
} astarpath_t;
// A* PROPS
//===========================================
int AStar_nodeIsInClosed( int node );

View file

@ -93,7 +93,7 @@ void safe_cprintf (edict_t *ent, int printlevel, char *fmt, ...)
char bigbuffer[0x10000];
va_list argptr;
if (ent && (!ent->inuse || ent->ai.is_bot))
if (ent && (!ent->inuse || ent->ai))
return;
va_start (argptr,fmt);
@ -112,7 +112,7 @@ void safe_centerprintf (edict_t *ent, char *fmt, ...)
char bigbuffer[0x10000];
va_list argptr;
if (!ent->inuse || ent->ai.is_bot)
if (!ent->inuse || ent->ai)
return;
va_start (argptr,fmt);
@ -143,7 +143,7 @@ void safe_bprintf (int printlevel, char *fmt, ...)
for (i=0 ; i<maxclients->value ; i++)
{
cl_ent = g_edicts + 1 + i;
if (!cl_ent->inuse || cl_ent->ai.is_bot)
if (!cl_ent->inuse || cl_ent->ai)
continue;
gi.cprintf(cl_ent, printlevel, bigbuffer);

View file

@ -64,37 +64,30 @@ void BOT_Respawn (edict_t *self)
///////////////////////////////////////////////////////////////////////
// Find a free client spot
// Find a free client spot - //jabot092(2)
///////////////////////////////////////////////////////////////////////
edict_t *BOT_FindFreeClient (void)
{
edict_t *bot;
edict_t *ent;
int i;
int max_count=0;
// This is for the naming of the bots
for (i = maxclients->value; i > 0; i--)
bot = NULL;
for( i = 0, ent = g_edicts + 1; i < game.maxclients; i++, ent++ )
{
bot = g_edicts + i + 1;
if( !ent->inuse && bot == NULL )
bot = ent;
if(bot->count > max_count)
max_count = bot->count;
//count bots for bot names
if( ent->count > max_count )
max_count = ent->count;
}
// Check for free spot
for (i = maxclients->value; i > 0; i--)
{
bot = g_edicts + i + 1;
if (!bot->inuse)
break;
}
if (bot == NULL || (max_count + 2) >= game.maxclients ) //always leave room for 1 player
return NULL;
bot->count = max_count + 1; // Will become bot name...
if (bot->inuse)
bot = NULL;
return bot;
}
@ -173,8 +166,6 @@ void BOT_SetName(edict_t *bot, char *name, char *skin, char *team)
Info_SetValueForKey (userinfo, "hand", "2"); // bot is center handed for now!
ClientConnect (bot, userinfo);
// ACESP_SaveBots(); // make sure to save the bots
}
//==========================================
@ -330,13 +321,12 @@ void BOT_SpawnBot (char *team, char *name, char *skin, char *userinfo)
if (!bot)
{
// safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n");
safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n");
return;
}
//init the bot
bot->inuse = true;
bot->ai.is_bot = true;
bot->yaw_speed = 100;
// To allow bots to respawn
@ -346,17 +336,19 @@ void BOT_SpawnBot (char *team, char *name, char *skin, char *userinfo)
ClientConnect (bot, userinfo);
G_InitEdict (bot);
G_SpawnAI(bot); //jabot092(2)
bot->ai->is_bot = true;
InitClientResp (bot->client);
PutClientInServer(bot);
BOT_StartAsSpectator (bot);
//skill
bot->ai.pers.skillLevel = (int)(random()*MAX_BOT_SKILL);
if (bot->ai.pers.skillLevel > MAX_BOT_SKILL) //fix if off-limits
bot->ai.pers.skillLevel = MAX_BOT_SKILL;
else if (bot->ai.pers.skillLevel < 0)
bot->ai.pers.skillLevel = 0;
bot->ai->pers.skillLevel = (int)(random()*MAX_BOT_SKILL);
if (bot->ai->pers.skillLevel > MAX_BOT_SKILL) //fix if off-limits
bot->ai->pers.skillLevel = MAX_BOT_SKILL;
else if (bot->ai->pers.skillLevel < 0)
bot->ai->pers.skillLevel = 0;
BOT_DMclass_InitPersistant(bot);
AI_ResetWeights(bot);
@ -387,23 +379,19 @@ void BOT_RemoveBot(char *name)
for(i=0;i<maxclients->value;i++)
{
bot = g_edicts + i + 1;
if(bot->inuse)
if( !bot->inuse || !bot->ai ) //jabot092(2)
continue;
if( bot->ai->is_bot && (!strcmp(bot->client->pers.netname,name) || !strcmp(name,"all")))
{
if(bot->ai.is_bot && (strcmp(bot->client->pers.netname,name)==0 || strcmp(name,"all")==0))
{
bot->health = 0;
player_die (bot, bot, bot, 100000, vec3_origin);
// don't even bother waiting for death frames
bot->deadflag = DEAD_DEAD;
bot->inuse = false;
AI_EnemyRemoved (bot);
// safe_bprintf (PRINT_MEDIUM, "%s removed\n", bot->client->pers.netname);
}
bot->health = 0;
player_die (bot, bot, bot, 100000, vec3_origin);
// don't even bother waiting for death frames
bot->deadflag = DEAD_DEAD;
bot->inuse = false;
AI_EnemyRemoved (bot);
G_FreeAI( bot ); //jabot092(2)
//safe_bprintf (PRINT_MEDIUM, "%s removed\n", bot->client->pers.netname);
}
}
// if(!freed)
// safe_bprintf (PRINT_MEDIUM, "%s not found\n", name);
// ACESP_SaveBots(); // Save them again
}

View file

@ -522,10 +522,9 @@ G_RunFrame(void)
{
ClientBeginServerFrame(ent);
//JABot[start]
if ( ent->ai.is_bot )
G_RunEntity (ent);
if (!ent->ai)
//[end]
continue;
continue;
}
G_RunEntity(ent);

View file

@ -31,6 +31,15 @@
// declaration of botedict for the game
//----------------------------------------------------------
#define MAX_BOT_ROAMS 128
#define MAX_NODES 2048 //jalToDo: needs dynamic alloc (big terrain maps)
typedef struct astarpath_s
{
int numNodes;
int nodes[MAX_NODES];
int originNode;
int goalNode;
} astarpath_t;
typedef struct
{
@ -70,12 +79,6 @@ typedef struct
int state; // Bot State (WANDER, MOVE, etc)
float state_combat_timeout;
qboolean is_swim;
qboolean is_step;
qboolean is_ladder;
qboolean was_swim;
qboolean was_step;
// movement
vec3_t move_vector;
float next_move_time;
@ -91,8 +94,7 @@ typedef struct
int tries;
struct astarpath_s *path;
int path_position;
struct astarpath_s path; //jabot092
int nearest_node_tries; //for increasing radius of search with each try
@ -105,6 +107,8 @@ qboolean BOT_ServerCommand(void);
// ai_main.c
void AI_Init(void);
void AI_NewMap(void);
void G_FreeAI( edict_t *ent );
void G_SpawnAI( edict_t *ent );
// ai_items.c
void AI_EnemyAdded(edict_t *ent);

View file

@ -1517,7 +1517,12 @@ struct edict_s
int chasedist1;
int chasedist2;
ai_handle_t ai; //JABot
ai_handle_t *ai; //jabot092(2)
qboolean is_swim; //AI_CategorizePosition
qboolean is_step;
qboolean is_ladder;
qboolean was_swim;
qboolean was_step;
};
#define SPHERE_DEFENDER 0x0001

View file

@ -2052,7 +2052,7 @@ respawn(edict_t *self)
if (deathmatch->value || coop->value)
{
//JABot[start]
if (self->ai.is_bot){
if (self->ai && self->ai->is_bot){
BOT_Respawn (self);
return;
}
@ -2395,7 +2395,7 @@ PutClientInServer(edict_t *ent)
VectorCopy(ent->s.angles, client->v_angle);
//JABot[start]
if( ent->ai.is_bot == true )
if( ent->ai && ent->ai->is_bot )
return;
//JABot[end]