diff --git a/README.md b/README.md index 723fb230..7782386f 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ Games: * Dawn of Darkness: * Docs: * Demo: [Episode 1](https://www.moddb.com/mods/dawn-of-darkness1/downloads/dawn-of-darkness-episode-1) +* JaBot: + * SDK: * Additional maps used for check maps support: * PSX: * ReRelease N64 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: diff --git a/src/game/bot/ai_class_dmbot.c b/src/game/bot/ai_class_dmbot.c index 573a6697..6c437d77 100644 --- a/src/game/bot/ai_class_dmbot.c +++ b/src/game/bot/ai_class_dmbot.c @@ -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;iai.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;isolid == 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; isvflags & 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; ipers.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; } } diff --git a/src/game/bot/ai_class_monster_default.c b/src/game/bot/ai_class_monster_default.c index 9ccd1408..e840ef71 100644 --- a/src/game/bot/ai_class_monster_default.c +++ b/src/game/bot/ai_class_monster_default.c @@ -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;iclassname, "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); diff --git a/src/game/bot/ai_dropnodes.c b/src/game/bot/ai_dropnodes.c index 68b6301a..9d14286a 100644 --- a/src/game/bot/ai_dropnodes.c +++ b/src/game/bot/ai_dropnodes.c @@ -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 ); diff --git a/src/game/bot/ai_items.c b/src/game/bot/ai_items.c index 5d0cf5cb..9253b4c6 100644 --- a/src/game/bot/ai_items.c +++ b/src/game/bot/ai_items.c @@ -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;iitem->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 diff --git a/src/game/bot/ai_links.c b/src/game/bot/ai_links.c index e0ab05d4..2c25b916 100644 --- a/src/game/bot/ai_links.c +++ b/src/game/bot/ai_links.c @@ -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 } //========================================== diff --git a/src/game/bot/ai_local.h b/src/game/bot/ai_local.h index a94d6ec6..e2b0a267 100644 --- a/src/game/bot/ai_local.h +++ b/src/game/bot/ai_local.h @@ -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 //---------------------------------------------------------- diff --git a/src/game/bot/ai_main.c b/src/game/bot/ai_main.c index fc18aab1..5120e81e 100644 --- a/src/game/bot/ai_main.c +++ b/src/game/bot/ai_main.c @@ -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; iai.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; iai.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;isvflags & 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); } diff --git a/src/game/bot/ai_movement.c b/src/game/bot/ai_movement.c index d570ed6e..c372ba02 100644 --- a/src/game/bot/ai_movement.c +++ b/src/game/bot/ai_movement.c @@ -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) ) { diff --git a/src/game/bot/ai_navigation.c b/src/game/bot/ai_navigation.c index c74068f3..520f1698 100644 --- a/src/game/bot/ai_navigation.c +++ b/src/game/bot/ai_navigation.c @@ -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; iai.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; } diff --git a/src/game/bot/ai_nodes.c b/src/game/bot/ai_nodes.c index d70f85ac..98a58047 100644 --- a/src/game/bot/ai_nodes.c +++ b/src/game/bot/ai_nodes.c @@ -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 ); diff --git a/src/game/bot/ai_nodes_shared.h b/src/game/bot/ai_nodes_shared.h index 341ba586..28620140 100644 --- a/src/game/bot/ai_nodes_shared.h +++ b/src/game/bot/ai_nodes_shared.h @@ -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 diff --git a/src/game/bot/astar.c b/src/game/bot/astar.c index a709165b..c8c7e9ff 100644 --- a/src/game/bot/astar.c +++ b/src/game/bot/astar.c @@ -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; inumNodes = 0; alist_numNodes = 0; - for( i=0; ivalue) { @@ -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; inumNodes; i++) - { - path->nodes[i] = Apath[i]; - } - return 1; } diff --git a/src/game/bot/astar.h b/src/game/bot/astar.h index 747e754e..ec45bbd5 100644 --- a/src/game/bot/astar.h +++ b/src/game/bot/astar.h @@ -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 ); diff --git a/src/game/bot/bot_common.c b/src/game/bot/bot_common.c index daced4c8..56a48627 100644 --- a/src/game/bot/bot_common.c +++ b/src/game/bot/bot_common.c @@ -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 ; ivalue ; 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); diff --git a/src/game/bot/bot_spawn.c b/src/game/bot/bot_spawn.c index 70de08b0..99237e6f 100644 --- a/src/game/bot/bot_spawn.c +++ b/src/game/bot/bot_spawn.c @@ -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;ivalue;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 } diff --git a/src/game/g_main.c b/src/game/g_main.c index aaafac04..41e94644 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -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); diff --git a/src/game/header/ai.h b/src/game/header/ai.h index c6373921..bc1cbfe9 100644 --- a/src/game/header/ai.h +++ b/src/game/header/ai.h @@ -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); diff --git a/src/game/header/local.h b/src/game/header/local.h index 703f9a49..c472d24e 100644 --- a/src/game/header/local.h +++ b/src/game/header/local.h @@ -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 diff --git a/src/game/player/client.c b/src/game/player/client.c index 9517efc6..6f76b218 100644 --- a/src/game/player/client.c +++ b/src/game/player/client.c @@ -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]