213 lines
5.8 KiB
C
213 lines
5.8 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/activemenu.h"
|
|
#include "game/chraction.h"
|
|
#include "game/chr.h"
|
|
#include "game/ceil.h"
|
|
#include "game/game_0b0fd0.h"
|
|
#include "game/playermgr.h"
|
|
#include "game/mplayer/setup.h"
|
|
#include "game/bot.h"
|
|
#include "game/botcmd.h"
|
|
#include "game/botact.h"
|
|
#include "game/botinv.h"
|
|
#include "game/mplayer/mplayer.h"
|
|
#include "game/propobj.h"
|
|
#include "bss.h"
|
|
#include "lib/rng.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
/**
|
|
* This table defines the ideal distance ranges for attacking.
|
|
*
|
|
* The first value is the minimum attack distance.
|
|
* The second value is the maximum attack distance.
|
|
* The third value doesn't appear to have any purpose.
|
|
*/
|
|
f32 g_BotDistConfigs[][3] = {
|
|
{ 0, 120, 10000 }, // BOTDISTCFG_CLOSE
|
|
{ 300, 450, 4500 }, // BOTDISTCFG_PISTOL
|
|
{ 300, 600, 4500 }, // BOTDISTCFG_DEFAULT
|
|
{ 600, 1200, 4500 }, // BOTDISTCFG_SHOOTEXPLOSIVE
|
|
{ 150, 250, 4500 }, // BOTDISTCFG_KAZE
|
|
{ 1000, 2000, 3000 }, // BOTDISTCFG_FARSIGHT
|
|
{ 0, 250, 10000 }, // BOTDISTCFG_FOLLOW
|
|
{ 450, 700, 4500 }, // BOTDISTCFG_THROWEXPLOSIVE
|
|
};
|
|
|
|
void botcmdTickDistMode(struct chrdata *chr)
|
|
{
|
|
s32 confignum;
|
|
f32 *limits;
|
|
f32 distance;
|
|
u8 newmode;
|
|
struct aibot *aibot = chr->aibot;
|
|
s32 prevmode = aibot->distmode;
|
|
struct prop *targetprop = NULL;
|
|
bool insight = false;
|
|
f32 minattackdistance;
|
|
f32 maxattackdistance;
|
|
f32 limit3;
|
|
u32 stack;
|
|
|
|
if (aibot->config->type == BOTTYPE_KAZE) {
|
|
confignum = BOTDISTCFG_KAZE;
|
|
} else {
|
|
confignum = botinvGetDistConfig(aibot->weaponnum, aibot->gunfunc);
|
|
}
|
|
|
|
if (chr->myaction == MA_AIBOTFOLLOW && aibot->followingplayernum >= 0) {
|
|
limits = g_BotDistConfigs[BOTDISTCFG_FOLLOW];
|
|
targetprop = g_MpAllChrPtrs[aibot->followingplayernum]->prop;
|
|
insight = aibot->chrsinsight[aibot->followingplayernum];
|
|
|
|
if (chr->target != -1 && (confignum == BOTDISTCFG_CLOSE || confignum == BOTDISTCFG_KAZE)) {
|
|
struct prop *target = chrGetTargetProp(chr);
|
|
f32 xdiff = targetprop->pos.x - target->pos.x;
|
|
f32 ydiff = targetprop->pos.y - target->pos.y;
|
|
f32 zdiff = targetprop->pos.z - target->pos.z;
|
|
|
|
if (xdiff * xdiff + ydiff * ydiff + zdiff * zdiff < 500 * 500) {
|
|
limits = g_BotDistConfigs[confignum];
|
|
targetprop = target;
|
|
insight = aibot->targetinsight;
|
|
}
|
|
}
|
|
} else {
|
|
limits = g_BotDistConfigs[confignum];
|
|
|
|
if (chr->myaction == MA_AIBOTATTACK && aibot->attackingplayernum >= 0) {
|
|
targetprop = g_MpAllChrPtrs[aibot->attackingplayernum]->prop;
|
|
insight = aibot->chrsinsight[aibot->attackingplayernum];
|
|
} else if (chr->target != -1) {
|
|
targetprop = chrGetTargetProp(chr);
|
|
insight = aibot->targetinsight;
|
|
}
|
|
}
|
|
|
|
if (targetprop == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (!botHasGround(targetprop->chr)) {
|
|
return;
|
|
}
|
|
|
|
distance = chrGetDistanceToCoord(chr, &targetprop->pos);
|
|
minattackdistance = limits[0];
|
|
maxattackdistance = limits[1];
|
|
limit3 = limits[2];
|
|
|
|
if (chr->aibot->config->difficulty == BOTDIFF_MEAT) {
|
|
minattackdistance *= 0.35f;
|
|
} else if (chr->aibot->config->difficulty == BOTDIFF_EASY) {
|
|
minattackdistance *= 0.5f;
|
|
}
|
|
|
|
if (aibot->distmode == BOTDISTMODE_BACKUP) {
|
|
minattackdistance += 25.0f;
|
|
} else if (aibot->distmode == BOTDISTMODE_ADVANCE || aibot->distmode == BOTDISTMODE_GOTO) {
|
|
maxattackdistance -= 25.0f;
|
|
}
|
|
|
|
if (distance < minattackdistance) {
|
|
// Bot is too close to target - move back a bit
|
|
newmode = BOTDISTMODE_BACKUP;
|
|
} else if (distance >= minattackdistance && distance < maxattackdistance) {
|
|
// Bot is a good distance from target for attacking
|
|
newmode = BOTDISTMODE_OK;
|
|
} else if (distance >= maxattackdistance && distance < limit3) {
|
|
// Bot needs to come closer to target
|
|
newmode = BOTDISTMODE_ADVANCE;
|
|
} else {
|
|
// Bot needs to come a lot closer to target
|
|
newmode = BOTDISTMODE_GOTO;
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (newmode != BOTDISTMODE_BACKUP || !insight || aibot->unk050 != targetprop) {
|
|
aibot->unk050 = NULL;
|
|
aibot->unk09d = 0;
|
|
}
|
|
|
|
if (newmode == BOTDISTMODE_OK) {
|
|
if (!insight) {
|
|
newmode = BOTDISTMODE_ADVANCE;
|
|
}
|
|
} else if (newmode == BOTDISTMODE_BACKUP) {
|
|
if (!insight) {
|
|
newmode = BOTDISTMODE_ADVANCE;
|
|
aibot->unk050 = targetprop;
|
|
aibot->unk09d = TICKS(20) + (random() % TICKS(120));
|
|
} else if (aibot->unk050) {
|
|
if (aibot->unk09d > g_Vars.lvupdate60) {
|
|
aibot->unk09d -= g_Vars.lvupdate60;
|
|
newmode = BOTDISTMODE_OK;
|
|
} else {
|
|
aibot->unk050 = NULL;
|
|
aibot->unk09d = 0;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
if (newmode == BOTDISTMODE_OK && !insight) {
|
|
newmode = BOTDISTMODE_ADVANCE;
|
|
}
|
|
#endif
|
|
|
|
aibot->distmode = newmode;
|
|
|
|
if (aibot->distmodettl60 >= 0) {
|
|
aibot->distmodettl60 -= g_Vars.lvupdate60;
|
|
}
|
|
|
|
if (newmode != prevmode
|
|
|| (newmode != BOTDISTMODE_OK && (chr->actiontype == ACT_STAND || aibot->distmodettl60 <= 0))) {
|
|
switch (newmode) {
|
|
case BOTDISTMODE_BACKUP:
|
|
chrRunFromPos(chr, GOPOSFLAG_RUN, 10000, &targetprop->pos);
|
|
break;
|
|
case BOTDISTMODE_OK:
|
|
chrTryStop(chr);
|
|
break;
|
|
case BOTDISTMODE_ADVANCE:
|
|
chrGoToProp(chr, targetprop, GOPOSFLAG_RUN);
|
|
break;
|
|
case BOTDISTMODE_GOTO:
|
|
chrGoToProp(chr, targetprop, GOPOSFLAG_RUN);
|
|
break;
|
|
}
|
|
|
|
aibot->distmodettl60 = TICKS(60);
|
|
}
|
|
}
|
|
|
|
void botcmdApply(struct chrdata *chr, u32 command)
|
|
{
|
|
f32 value;
|
|
|
|
switch (command) {
|
|
case AIBOTCMD_ATTACK:
|
|
amOpenPickTarget();
|
|
break;
|
|
case AIBOTCMD_FOLLOW:
|
|
botApplyFollow(chr, g_Vars.currentplayer->prop);
|
|
break;
|
|
case AIBOTCMD_PROTECT:
|
|
botApplyProtect(chr, g_Vars.currentplayer->prop);
|
|
break;
|
|
case AIBOTCMD_DEFEND:
|
|
value = chrGetInverseTheta(g_Vars.currentplayer->prop->chr);
|
|
botApplyDefend(chr, &g_Vars.currentplayer->prop->pos, g_Vars.currentplayer->prop->rooms, value);
|
|
break;
|
|
case AIBOTCMD_HOLD:
|
|
value = chrGetInverseTheta(g_Vars.currentplayer->prop->chr);
|
|
botApplyHold(chr, &g_Vars.currentplayer->prop->pos, g_Vars.currentplayer->prop->rooms, value);
|
|
break;
|
|
default:
|
|
botApplyScenarioCommand(chr, command);
|
|
break;
|
|
}
|
|
}
|