1705 lines
41 KiB
C
1705 lines
41 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/chraction.h"
|
|
#include "game/game_006900.h"
|
|
#include "game/bondgun.h"
|
|
#include "game/game_0b0fd0.h"
|
|
#include "game/tex.h"
|
|
#include "game/savebuffer.h"
|
|
#include "game/menu.h"
|
|
#include "game/inv.h"
|
|
#include "game/game_1531a0.h"
|
|
#include "game/file.h"
|
|
#include "game/bot.h"
|
|
#include "game/botcmd.h"
|
|
#include "game/gfxmemory.h"
|
|
#include "game/training.h"
|
|
#include "game/lang.h"
|
|
#include "game/mplayer/mplayer.h"
|
|
#include "game/options.h"
|
|
#include "bss.h"
|
|
#include "lib/vi.h"
|
|
#include "lib/main.h"
|
|
#include "lib/mtx.h"
|
|
#include "lib/ailist.h"
|
|
#include "lib/str.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
struct activemenu g_AmMenus[4];
|
|
struct fontchar *g_AmFont1;
|
|
struct font *g_AmFont2;
|
|
s32 g_AmIndex;
|
|
|
|
struct menudialogdef g_AmPickTargetMenuDialog;
|
|
|
|
/**
|
|
* This is a map of weapon numbers (as per the weapon set) to active menu slots.
|
|
* For the purpose of this array, the AM slots are:
|
|
*
|
|
* 0-2 = top left to top right
|
|
* 3,4 = left, right
|
|
* 5-7 = bottom left to bottom right
|
|
*
|
|
* The values in the array are the slot numbers where that weapon will go.
|
|
* For example, the value at index 2 is 6 which means weapon #2 from the
|
|
* weapon set will go into slot 6 which is the bottom slot.
|
|
*/
|
|
const u8 g_AmMapping[] = {
|
|
0, // unarmed
|
|
1, // weapon #1
|
|
6, // weapon #2
|
|
3, // weapon #3
|
|
4, // weapon #4
|
|
7, // weapon #5
|
|
5, // weapon #6
|
|
2, // unused
|
|
};
|
|
|
|
struct chrdata *currentPlayerGetCommandingAibot(void)
|
|
{
|
|
return g_Vars.currentplayer->commandingaibot;
|
|
}
|
|
|
|
void amOpenPickTarget(void)
|
|
{
|
|
u32 prevplayernum = g_MpPlayerNum;
|
|
|
|
if (!mpIsPaused()) {
|
|
g_AmMenus[g_AmIndex].prevallbots = g_AmMenus[g_AmIndex].allbots;
|
|
g_Vars.currentplayer->activemenumode = AMMODE_CLOSED;
|
|
g_MpPlayerNum = g_Vars.currentplayerstats->mpindex;
|
|
menuPushRootDialog(&g_AmPickTargetMenuDialog, MENUROOT_PICKTARGET);
|
|
g_MpPlayerNum = prevplayernum;
|
|
}
|
|
}
|
|
|
|
s32 amPickTargetMenuList(s32 operation, struct menuitem *item, union handlerdata *data)
|
|
{
|
|
static u32 teamcolours[] = {
|
|
0xff666600,
|
|
0xffff0000,
|
|
0x4444ff00,
|
|
0xff00ff00,
|
|
0x00ffff00,
|
|
0xff885500,
|
|
0x8800ff00,
|
|
0x88445500,
|
|
};
|
|
|
|
switch (operation) {
|
|
case MENUOP_GETOPTIONCOUNT:
|
|
if (g_AmMenus[g_AmIndex].prevallbots) {
|
|
// All bots: num bot opponents + all human players
|
|
s32 count = 0;
|
|
s32 i;
|
|
struct chrdata *playerchr = g_Vars.currentplayer->prop->chr;
|
|
|
|
for (i = PLAYERCOUNT(); i < g_MpNumChrs; i++) {
|
|
if (g_MpAllChrPtrs[i]->team != playerchr->team) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
count += PLAYERCOUNT();
|
|
data->list.value = count;
|
|
} else {
|
|
// Single bot: All except the bot itself
|
|
data->list.value = g_MpNumChrs - 1;
|
|
}
|
|
break;
|
|
case MENUOP_SET:
|
|
{
|
|
s32 numremaining;
|
|
s32 chrindex;
|
|
struct chrdata *botchr;
|
|
struct chrdata *playerchr;
|
|
s32 i;
|
|
|
|
chrindex = -1;
|
|
numremaining = data->list.value;
|
|
botchr = g_MpAllChrPtrs[g_Vars.currentplayer->aibuddynums[g_AmMenus[g_AmIndex].screenindex - 2]];
|
|
playerchr = g_Vars.currentplayer->prop->chr;
|
|
|
|
do {
|
|
chrindex++;
|
|
|
|
if (g_AmMenus[g_AmIndex].prevallbots) {
|
|
if (playerchr == g_MpAllChrPtrs[chrindex] || playerchr->team != g_MpAllChrPtrs[chrindex]->team) {
|
|
numremaining--;
|
|
}
|
|
} else {
|
|
if (botchr != g_MpAllChrPtrs[chrindex]) {
|
|
numremaining--;
|
|
}
|
|
}
|
|
} while (numremaining >= 0);
|
|
|
|
if (g_AmMenus[g_AmIndex].prevallbots) {
|
|
g_AmMenus[g_AmIndex].prevallbots = false;
|
|
|
|
for (i = 0; i < g_Vars.currentplayer->numaibuddies; i++) {
|
|
botApplyAttack(g_MpAllChrPtrs[g_Vars.currentplayer->aibuddynums[i]], g_MpAllChrPtrs[chrindex]->prop);
|
|
}
|
|
} else {
|
|
botApplyAttack(botchr, g_MpAllChrPtrs[chrindex]->prop);
|
|
}
|
|
|
|
menuPopDialog();
|
|
}
|
|
break;
|
|
case MENUOP_GETSELECTEDINDEX:
|
|
data->list.value = 0xfffff;
|
|
break;
|
|
case MENUOP_RENDER:
|
|
{
|
|
Gfx *gdl = data->type19.gdl;
|
|
struct menuitemrenderdata *renderdata = data->type19.renderdata2;
|
|
s32 x;
|
|
s32 y;
|
|
u32 colour;
|
|
s32 numremaining = (s32)data->type19.unk04;
|
|
s32 chrindex = -1;
|
|
struct chrdata *botchr = g_MpAllChrPtrs[g_Vars.currentplayer->aibuddynums[g_AmMenus[g_AmIndex].screenindex - 2]];
|
|
struct chrdata *playerchr = g_Vars.currentplayer->prop->chr;
|
|
|
|
do {
|
|
chrindex++;
|
|
|
|
if (g_AmMenus[g_AmIndex].prevallbots) {
|
|
if (playerchr == g_MpAllChrPtrs[chrindex] || playerchr->team != g_MpAllChrPtrs[chrindex]->team) {
|
|
numremaining--;
|
|
}
|
|
} else {
|
|
if (botchr != g_MpAllChrPtrs[chrindex]) {
|
|
numremaining--;
|
|
}
|
|
}
|
|
} while (numremaining >= 0);
|
|
|
|
colour = teamcolours[g_MpAllChrConfigPtrs[chrindex]->team] | (renderdata->colour & 0xff);
|
|
|
|
if (renderdata->unk10) {
|
|
u32 weight = menuGetSinOscFrac(40) * 255;
|
|
colour = colourBlend(renderdata->colour | 0xffffff00, colourBlend(colour, colour & 0xff, 0x7f), weight);
|
|
}
|
|
|
|
x = renderdata->x + 10;
|
|
y = renderdata->y + 1;
|
|
|
|
gdl = text0f153628(gdl);
|
|
gdl = textRenderProjected(gdl, &x, &y, g_MpAllChrConfigPtrs[chrindex]->name, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, viGetWidth(), viGetHeight(), 0, 0);
|
|
gdl = text0f153780(gdl);
|
|
return (s32)gdl;
|
|
}
|
|
case MENUOP_GETOPTIONHEIGHT:
|
|
data->list.value = LINEHEIGHT;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 amPickTargetMenuDialog(s32 operation, struct menudialogdef *dialogdef, union handlerdata *data)
|
|
{
|
|
switch (operation) {
|
|
case MENUOP_OPEN:
|
|
g_PlayersWithControl[g_Vars.currentplayernum] = false;
|
|
break;
|
|
case MENUOP_TICK:
|
|
g_PlayersWithControl[g_Vars.currentplayernum] = false;
|
|
break;
|
|
case MENUOP_CLOSE:
|
|
g_PlayersWithControl[g_Vars.currentplayernum] = true;
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
struct menuitem g_AmPickTargetMenuItems[] = {
|
|
{
|
|
MENUITEMTYPE_LIST,
|
|
0,
|
|
MENUITEMFLAG_LIST_CUSTOMRENDER,
|
|
0x0000005a,
|
|
0,
|
|
amPickTargetMenuList,
|
|
},
|
|
{ MENUITEMTYPE_END },
|
|
};
|
|
|
|
struct menudialogdef g_AmPickTargetMenuDialog = {
|
|
MENUDIALOGTYPE_DANGER,
|
|
L_OPTIONS_492, // "Pick Target"
|
|
g_AmPickTargetMenuItems,
|
|
amPickTargetMenuDialog,
|
|
0,
|
|
NULL,
|
|
};
|
|
|
|
void amSetAiBuddyTemperament(bool aggressive)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_Vars.numaibuddies; i++) {
|
|
if (g_Vars.aibuddies[i]) {
|
|
struct chrdata *chr = g_Vars.aibuddies[i]->chr;
|
|
|
|
if (chr) {
|
|
if (aggressive) {
|
|
chr->hidden &= ~CHRHFLAG_PASSIVE;
|
|
} else {
|
|
chr->hidden |= CHRHFLAG_PASSIVE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
void amSetAiBuddyStealth(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_Vars.numaibuddies; i++) {
|
|
if (g_Vars.aibuddies[i]) {
|
|
struct chrdata *chr = g_Vars.aibuddies[i]->chr;
|
|
|
|
if (chr && chr->prop
|
|
&& !chrIsDead(chr)
|
|
&& chr->ailist != ailistFindById(GAILIST_BUDDY_STEALTH)
|
|
&& chr->actiontype != ACT_DRUGGEDDROP
|
|
&& chr->actiontype != ACT_DRUGGEDKO
|
|
&& chr->actiontype != ACT_DRUGGEDCOMINGUP) {
|
|
chrStopFiring(chr);
|
|
chr->ailist = ailistFindById(GAILIST_BUDDY_STEALTH);
|
|
chr->aioffset = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
s32 amGetFirstBuddyIndex(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_Vars.numaibuddies; i++) {
|
|
if (g_Vars.aibuddies[i]) {
|
|
struct chrdata *chr = g_Vars.aibuddies[i]->chr;
|
|
|
|
if (chr && chr->actiontype != ACT_DIE
|
|
&& chr->actiontype != ACT_DEAD
|
|
&& chr->prop
|
|
&& chr->model) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void amApply(s32 slot)
|
|
{
|
|
s32 numinvitems;
|
|
s32 invindex;
|
|
bool pass;
|
|
s32 state;
|
|
s32 weaponnum;
|
|
s32 i;
|
|
|
|
switch (g_AmMenus[g_AmIndex].screenindex) {
|
|
case 0: // Weapon
|
|
if (slot > 4) {
|
|
slot--;
|
|
}
|
|
|
|
invindex = g_AmMenus[g_AmIndex].invindexes[slot];
|
|
numinvitems = invGetCount();
|
|
|
|
if (invindex < numinvitems) {
|
|
weaponnum = invGetWeaponNumByIndex(invindex);
|
|
pass = true;
|
|
|
|
if (weaponnum) {
|
|
state = currentPlayerGetDeviceState(weaponnum);
|
|
|
|
if (state != DEVICESTATE_UNEQUIPPED) {
|
|
pass = false;
|
|
|
|
if (state == DEVICESTATE_INACTIVE) {
|
|
currentPlayerSetDeviceActive(weaponnum, true);
|
|
} else {
|
|
currentPlayerSetDeviceActive(weaponnum, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pass) {
|
|
pass = true;
|
|
|
|
if (g_FrIsValidWeapon) {
|
|
s32 weaponnum = frGetWeaponBySlot(frGetSlot());
|
|
|
|
if (g_Vars.currentplayer->hands[HAND_RIGHT].gset.weaponnum == weaponnum) {
|
|
pass = false;
|
|
}
|
|
}
|
|
|
|
if (pass) {
|
|
invSetCurrentIndex(invindex);
|
|
|
|
if (invHasDoubleWeaponIncAllGuns(weaponnum, weaponnum)) {
|
|
if (bgunGetWeaponNum(HAND_RIGHT) != weaponnum) {
|
|
bgunEquipWeapon2(HAND_RIGHT, weaponnum);
|
|
}
|
|
|
|
if (bgunGetWeaponNum(HAND_LEFT) != weaponnum) {
|
|
bgunEquipWeapon2(HAND_LEFT, weaponnum);
|
|
}
|
|
} else {
|
|
if (bgunGetWeaponNum(HAND_RIGHT) != weaponnum) {
|
|
bgunEquipWeapon2(HAND_RIGHT, weaponnum);
|
|
}
|
|
|
|
if (bgunGetWeaponNum(HAND_LEFT) != WEAPON_NONE) {
|
|
bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 1: // Function
|
|
if (g_Vars.currentplayer->gunctrl.weaponnum >= WEAPON_UNARMED
|
|
&& g_Vars.currentplayer->gunctrl.weaponnum <= WEAPON_COMBATBOOST
|
|
&& g_PlayerConfigsArray[g_Vars.currentplayerstats->mpindex].gunfuncs[(g_Vars.currentplayer->gunctrl.weaponnum - 1) >> 3] & (1 << ((g_Vars.currentplayer->gunctrl.weaponnum - 1) & 7))) {
|
|
if (slot == 1) {
|
|
g_AmMenus[g_AmIndex].togglefunc = true;
|
|
}
|
|
} else {
|
|
if (slot != 1) {
|
|
g_AmMenus[g_AmIndex].togglefunc = true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (g_MissionConfig.iscoop) {
|
|
if (amGetFirstBuddyIndex() > -1) {
|
|
if (slot == 1) {
|
|
amSetAiBuddyTemperament(true); // aggressive
|
|
} else if (slot == 7) {
|
|
amSetAiBuddyTemperament(false); // passive
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
} else if (slot == 3) {
|
|
amSetAiBuddyStealth();
|
|
#endif
|
|
}
|
|
}
|
|
} else if (g_Vars.normmplayerisrunning) {
|
|
if (g_AmMenus[g_AmIndex].allbots) {
|
|
for (i = 0; i < g_Vars.currentplayer->numaibuddies; i++) {
|
|
botcmdApply(g_MpAllChrPtrs[g_Vars.currentplayer->aibuddynums[i]], g_AmBotCommands[slot]);
|
|
}
|
|
} else {
|
|
botcmdApply(g_MpAllChrPtrs[g_Vars.currentplayer->aibuddynums[g_AmMenus[g_AmIndex].screenindex - 2]], g_AmBotCommands[slot]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void amGetSlotDetails(s32 slot, u32 *flags, char *label)
|
|
{
|
|
u32 weaponnum;
|
|
s32 qty;
|
|
s32 secs;
|
|
s32 modulo;
|
|
struct weaponfunc *prifunc;
|
|
struct weaponfunc *secfunc;
|
|
|
|
switch (g_AmMenus[g_AmIndex].screenindex) {
|
|
case 0: // Weapon screen
|
|
if (slot == 4) {
|
|
strcpy(label, langGet(L_MISC_170)); // "Weapon"
|
|
return;
|
|
}
|
|
|
|
if (slot > 4) {
|
|
slot--;
|
|
}
|
|
|
|
if (invGetCurrentIndex() == g_AmMenus[g_AmIndex].invindexes[slot]) {
|
|
*flags |= AMSLOTFLAG_CURRENT;
|
|
}
|
|
|
|
if (g_AmMenus[g_AmIndex].invindexes[slot] >= invGetCount()) {
|
|
strcpy(label, "");
|
|
} else {
|
|
if (invGetWeaponNumByIndex(g_AmMenus[g_AmIndex].invindexes[slot]) == WEAPON_CLOAKINGDEVICE) {
|
|
// Special case: "Cloak %d"
|
|
qty = bgunGetReservedAmmoCount(AMMOTYPE_CLOAK);
|
|
secs = qty / TICKS(60);
|
|
modulo = (qty - (secs * TICKS(60))) * 100 / TICKS(60);
|
|
sprintf(label, langGet(L_OPTIONS_491), secs + (modulo > 0 ? 1 : 0)); // "cloak %d"
|
|
} else {
|
|
strcpy(label, invGetShortNameByIndex(g_AmMenus[g_AmIndex].invindexes[slot]));
|
|
}
|
|
}
|
|
|
|
weaponnum = invGetWeaponNumByIndex(g_AmMenus[g_AmIndex].invindexes[slot]);
|
|
|
|
if (currentPlayerGetDeviceState(weaponnum) == DEVICESTATE_ACTIVE) {
|
|
*flags |= AMSLOTFLAG_ACTIVE;
|
|
}
|
|
|
|
weaponnum = invGetWeaponNumByIndex(g_AmMenus[g_AmIndex].invindexes[slot]);
|
|
|
|
if (!bgunHasAmmoForWeapon(weaponnum)) {
|
|
*flags |= AMSLOTFLAG_NOAMMO;
|
|
}
|
|
break;
|
|
case 1: // Function screen
|
|
strcpy(label, "");
|
|
|
|
if (slot == 4) {
|
|
strcpy(label, langGet(L_MISC_171)); // "Function"
|
|
} else if (slot == 1 || slot == 7) {
|
|
prifunc = weaponGetFunction(&g_Vars.currentplayer->hands[HAND_RIGHT].gset, FUNC_PRIMARY);
|
|
secfunc = weaponGetFunction(&g_Vars.currentplayer->hands[HAND_RIGHT].gset, FUNC_SECONDARY);
|
|
|
|
if (slot == 1) {
|
|
if (!secfunc || !FUNCISSEC()) {
|
|
*flags |= AMSLOTFLAG_CURRENT;
|
|
}
|
|
|
|
if (prifunc) {
|
|
strcpy(label, langGet(prifunc->name));
|
|
}
|
|
} else {
|
|
if (!prifunc || FUNCISSEC()) {
|
|
*flags |= AMSLOTFLAG_CURRENT;
|
|
}
|
|
|
|
if (secfunc) {
|
|
strcpy(label, langGet(secfunc->name));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default: // Orders screen
|
|
strcpy(label, "");
|
|
|
|
if (g_MissionConfig.iscoop) {
|
|
if (slot == 4) {
|
|
strcpy(label, langGet(L_MISC_474)); // "Perfect Buddies"
|
|
} else if (slot == 1) {
|
|
strcpy(label, langGet(L_MISC_472)); // "Aggressive"
|
|
} else if (slot == 7) {
|
|
strcpy(label, langGet(L_MISC_473)); // "Passive"
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
} else if (slot == 3) {
|
|
strcpy(label, langGet(L_MISC_475)); // "Stealth"
|
|
#endif
|
|
}
|
|
} else {
|
|
if (slot == 4) {
|
|
strcpy(label, langGet(L_MISC_172)); // "Orders"
|
|
} else {
|
|
strcpy(label, botGetCommandName(g_AmBotCommands[slot]));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void amReset(void)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
|
|
// @bug? Should this be set for each player?
|
|
g_Vars.currentplayer->activemenumode = AMMODE_CLOSED;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_AmMenus); i++) {
|
|
g_AmMenus[i].togglefunc = false;
|
|
|
|
for (j = 0; j < ARRAYCOUNT(g_AmMenus[i].favourites); j++) {
|
|
g_AmMenus[i].favourites[j] = 0xff;
|
|
}
|
|
|
|
if (g_Vars.normmplayerisrunning) {
|
|
s32 index = 0;
|
|
|
|
g_AmMenus[i].favourites[g_AmMapping[index]] = WEAPON_UNARMED;
|
|
index++;
|
|
|
|
for (j = 0; j < ARRAYCOUNT(g_MpSetup.weapons); j++) {
|
|
s32 weaponnum = g_MpWeapons[g_MpSetup.weapons[j]].weaponnum;
|
|
|
|
switch (weaponnum) {
|
|
case WEAPON_NONE:
|
|
case WEAPON_MPSHIELD:
|
|
case WEAPON_DISABLED:
|
|
break;
|
|
default:
|
|
g_AmMenus[i].favourites[g_AmMapping[index]] = weaponnum;
|
|
index++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
g_AmFont1 = g_CharsHandelGothicSm;
|
|
g_AmFont2 = g_FontHandelGothicSm;
|
|
#else
|
|
if (PLAYERCOUNT() >= 2) {
|
|
g_AmFont1 = g_CharsHandelGothicXs;
|
|
g_AmFont2 = g_FontHandelGothicXs;
|
|
} else {
|
|
g_AmFont1 = g_CharsHandelGothicSm;
|
|
g_AmFont2 = g_FontHandelGothicSm;
|
|
}
|
|
#endif
|
|
|
|
g_AmIndex = 0;
|
|
}
|
|
|
|
s16 amCalculateSlotWidth(void)
|
|
{
|
|
s32 textheight;
|
|
s32 textwidth;
|
|
s32 max = 0;
|
|
s32 i;
|
|
u32 flags;
|
|
char text[32];
|
|
|
|
for (i = 0; i != 9; i++) {
|
|
amGetSlotDetails(i, &flags, text);
|
|
textMeasure(&textheight, &textwidth, text, g_AmFont1, g_AmFont2, 0);
|
|
|
|
if (textwidth > max) {
|
|
max = textwidth;
|
|
}
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (PLAYERCOUNT() > 1) {
|
|
max += 3;
|
|
} else {
|
|
max += 4;
|
|
}
|
|
#else
|
|
max += 4;
|
|
#endif
|
|
|
|
return max;
|
|
}
|
|
|
|
void amChangeScreen(s32 step)
|
|
{
|
|
s32 maxscreenindex;
|
|
|
|
g_AmMenus[g_AmIndex].screenindex += step;
|
|
|
|
if (g_Vars.normmplayerisrunning && (g_MpSetup.options & MPOPTION_TEAMSENABLED)) {
|
|
if (g_AmMenus[g_AmIndex].allbots) {
|
|
// Weapon selection, second function, and bot command menu
|
|
|
|
// @bug: This is missing a check to see if there are any bots on
|
|
// your team. In most cases this isn't a problem because the player
|
|
// opens the screen for a single bot then uses R to switch to all
|
|
// bots. When they do this without buddy bots the else part below
|
|
// runs first, limits the max screen index to 1 and all is good.
|
|
// But if you hold R as you switch to the bot command menu then this
|
|
// part runs first and sets the screen index to an invalid value,
|
|
// causing a crash.
|
|
maxscreenindex = 2;
|
|
} else {
|
|
// Weapon selection, second function and one for each AI buddy
|
|
maxscreenindex = g_Vars.currentplayer->numaibuddies + 1;
|
|
}
|
|
} else {
|
|
// Solo missions, or MP with no teams
|
|
if (g_MissionConfig.iscoop && amGetFirstBuddyIndex() >= 0) {
|
|
// Weapon selection, second function and AI buddy commands
|
|
maxscreenindex = 2;
|
|
} else {
|
|
// Weapon selection and second function
|
|
maxscreenindex = 1;
|
|
}
|
|
}
|
|
|
|
if (g_AmMenus[g_AmIndex].screenindex > maxscreenindex) {
|
|
g_AmMenus[g_AmIndex].screenindex = 0;
|
|
}
|
|
|
|
if (g_AmMenus[g_AmIndex].screenindex < 0) {
|
|
g_AmMenus[g_AmIndex].screenindex = maxscreenindex;
|
|
}
|
|
|
|
g_AmMenus[g_AmIndex].xradius = 10;
|
|
g_AmMenus[g_AmIndex].dstx = -123;
|
|
g_AmMenus[g_AmIndex].slotnum = 4;
|
|
g_AmMenus[g_AmIndex].returntimer = 0;
|
|
g_AmMenus[g_AmIndex].cornertimer = 0;
|
|
g_AmMenus[g_AmIndex].alphafrac = 0;
|
|
g_AmMenus[g_AmIndex].slotwidth = amCalculateSlotWidth();
|
|
}
|
|
|
|
void amAssignWeaponSlots(void)
|
|
{
|
|
s32 numitems = invGetCount();
|
|
u8 weaponnum;
|
|
s32 i;
|
|
s32 j;
|
|
|
|
g_AmMenus[g_AmIndex].numitems = numitems;
|
|
|
|
// Reset inventory indexes
|
|
for (i = 0; i < ARRAYCOUNT(g_AmMenus[g_AmIndex].invindexes);) {
|
|
g_AmMenus[g_AmIndex].invindexes[i] = 0xff;
|
|
i++;
|
|
}
|
|
|
|
// Assign favourites
|
|
for (i = 0; i < numitems; i++) {
|
|
weaponnum = invGetWeaponNumByIndex(i);
|
|
|
|
if ((weaponnum >= WEAPON_UNARMED && weaponnum <= WEAPON_DISGUISE41)
|
|
|| weaponnum == WEAPON_SUICIDEPILL
|
|
|| weaponnum == WEAPON_BACKUPDISK
|
|
|| weaponnum == WEAPON_SUITCASE) {
|
|
for (j = 0; j < ARRAYCOUNT(g_AmMenus[g_AmIndex].favourites); j++) {
|
|
if (g_AmMenus[g_AmIndex].favourites[j] == weaponnum) {
|
|
if (g_AmMenus[g_AmIndex].invindexes[j] == 0xff) {
|
|
g_AmMenus[g_AmIndex].invindexes[j] = i;
|
|
} else {
|
|
// empty
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there are still unused slots, fill the remaining slots in inventory
|
|
// order with unfavourited weapons.
|
|
for (i = 0; i < numitems; i++) {
|
|
bool isfavourited = false;
|
|
|
|
for (j = 0; j < ARRAYCOUNT(g_AmMenus[g_AmIndex].invindexes); j++) {
|
|
if (g_AmMenus[g_AmIndex].invindexes[j] == i) {
|
|
isfavourited = true;
|
|
}
|
|
}
|
|
|
|
if (!isfavourited) {
|
|
weaponnum = invGetWeaponNumByIndex(i);
|
|
|
|
if ((weaponnum >= WEAPON_UNARMED && weaponnum <= WEAPON_DISGUISE41)
|
|
|| weaponnum == WEAPON_SUICIDEPILL
|
|
|| weaponnum == WEAPON_SUITCASE) {
|
|
s32 useindex = -1;
|
|
s32 j;
|
|
|
|
// Try to find any mapping which is not yet used.
|
|
// While it could just iterate the invitems or weaponnums arrays
|
|
// directly, doing it using the mapping makes it allocate these
|
|
// somewhat randomly rather than in slot order.
|
|
for (j = 0; j < ARRAYCOUNT(g_AmMapping); j++) {
|
|
if (g_AmMenus[g_AmIndex].favourites[g_AmMapping[j]] == 0xff) {
|
|
useindex = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (useindex == -1) {
|
|
// This part is pointless. If this part of the code is
|
|
// reached then all the mappings were in use, and therefore
|
|
// all the slots are in use too. There's no way this can
|
|
// find any new slots.
|
|
for (j = 0; j < ARRAYCOUNT(g_AmMapping); j++) {
|
|
if (g_AmMenus[g_AmIndex].invindexes[g_AmMapping[j]] == 0xff) {
|
|
useindex = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (useindex >= 0) {
|
|
g_AmMenus[g_AmIndex].invindexes[g_AmMapping[useindex]] = i;
|
|
g_AmMenus[g_AmIndex].favourites[g_AmMapping[useindex]] = weaponnum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void amOpen(void)
|
|
{
|
|
if (g_Vars.currentplayer->gunctrl.passivemode == false) {
|
|
g_AmIndex = g_Vars.currentplayernum;
|
|
g_Vars.currentplayer->activemenumode = AMMODE_VIEW;
|
|
g_PlayersWithControl[g_Vars.currentplayernum] = false;
|
|
g_AmMenus[g_AmIndex].screenindex = 0;
|
|
g_AmMenus[g_AmIndex].selpulse = 0;
|
|
amAssignWeaponSlots();
|
|
amChangeScreen(0);
|
|
g_AmMenus[g_AmIndex].xradius = g_AmMenus[g_AmIndex].slotwidth + 5;
|
|
g_AmMenus[g_AmIndex].alphafrac = 0.3;
|
|
g_AmMenus[g_AmIndex].origscreennum = 0;
|
|
g_AmMenus[g_AmIndex].prevallbots = 0;
|
|
g_AmMenus[g_AmIndex].allbots = false;
|
|
}
|
|
}
|
|
|
|
void amClose(void)
|
|
{
|
|
if (g_AmMenus[g_AmIndex].slotnum != 4) {
|
|
amApply(g_AmMenus[g_AmIndex].slotnum);
|
|
}
|
|
|
|
g_Vars.currentplayer->activemenumode = AMMODE_CLOSED;
|
|
g_Vars.currentplayer->joybutinhibit = 0xffffffff;
|
|
g_PlayersWithControl[g_Vars.currentplayernum] = 1;
|
|
}
|
|
|
|
bool amIsCramped(void)
|
|
{
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
if (PLAYERCOUNT() >= 3 && g_AmMenus[g_AmIndex].screenindex != 1) {
|
|
return true;
|
|
}
|
|
|
|
if (IS4MB() && PLAYERCOUNT() == 2) {
|
|
return true;
|
|
}
|
|
|
|
if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL
|
|
&& PLAYERCOUNT() == 2
|
|
&& g_AmMenus[g_AmIndex].screenindex != 1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
#else
|
|
return (g_AmMenus[g_AmIndex].screenindex == 0 && PLAYERCOUNT() >= 3)
|
|
|| (IS4MB() && PLAYERCOUNT() == 2)
|
|
|| (PLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL);
|
|
#endif
|
|
}
|
|
|
|
void amCalculateSlotPosition(s16 column, s16 row, s16 *x, s16 *y)
|
|
{
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
s32 playercount = PLAYERCOUNT();
|
|
|
|
*x = g_AmMenus[g_AmIndex].xradius * (column - 1);
|
|
*y = (row - 1) * 50;
|
|
|
|
if (column != 1 && row != 1) {
|
|
*x = *x / 2;
|
|
*y = *y / 2;
|
|
}
|
|
|
|
if (amIsCramped()) {
|
|
s32 offset = 1;
|
|
|
|
if (row == 1) {
|
|
offset = 3;
|
|
}
|
|
|
|
if (column == 0) {
|
|
*x = -(g_AmMenus[g_AmIndex].slotwidth / 2) - offset;
|
|
} else if (column == 2) {
|
|
*x = g_AmMenus[g_AmIndex].slotwidth / 2 + offset;
|
|
}
|
|
} else {
|
|
if (playercount >= 2) {
|
|
if (row == 1 && !amIsCramped()) {
|
|
*x = (*x * 6) / 7;
|
|
}
|
|
} else {
|
|
if (playercount >= 3 && row == 1 && !amIsCramped()) {
|
|
*x = (*x * 6) / 14;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (playercount >= 2) {
|
|
*y = (*y * 7) / 10;
|
|
}
|
|
|
|
*x += viGetViewLeft() / g_ScaleX + viGetViewWidth() / (g_ScaleX * 2);
|
|
*y += viGetViewTop() + viGetViewHeight() / 2;
|
|
|
|
if (playercount >= 2) {
|
|
*y += 4;
|
|
}
|
|
|
|
if ((playercount == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB()))
|
|
|| playercount >= 3) {
|
|
if ((g_Vars.currentplayernum % 2) == 0) {
|
|
*x += 8;
|
|
} else {
|
|
*x -= 8;
|
|
}
|
|
}
|
|
#elif VERSION >= VERSION_NTSC_1_0
|
|
s32 playercount = PLAYERCOUNT();
|
|
|
|
*x = g_AmMenus[g_AmIndex].xradius * (column - 1);
|
|
*y = (row - 1) * 50;
|
|
|
|
if (column != 1 && row != 1) {
|
|
*x = *x / 2;
|
|
*y = *y / 2;
|
|
}
|
|
|
|
if (amIsCramped()) {
|
|
s32 offset = 1;
|
|
|
|
if (row == 1) {
|
|
offset = 3;
|
|
}
|
|
|
|
if (column == 0) {
|
|
*x = -(g_AmMenus[g_AmIndex].slotwidth / 2) - offset;
|
|
} else if (column == 2) {
|
|
*x = g_AmMenus[g_AmIndex].slotwidth / 2 + offset;
|
|
}
|
|
} else {
|
|
if (playercount >= 2) {
|
|
if (row == 1 && !amIsCramped()) {
|
|
*x = (*x * 6) / 7;
|
|
}
|
|
} else {
|
|
if (playercount >= 3 && row == 1 && !amIsCramped()) {
|
|
*x = (*x * 6) / 14;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (playercount >= 2) {
|
|
*y = (*y * 3) / 5;
|
|
} else if (playercount >= 3) {
|
|
*y = (*y * 3) / 5;
|
|
}
|
|
|
|
*x += viGetViewLeft() / g_ScaleX + viGetViewWidth() / (g_ScaleX * 2);
|
|
*y += viGetViewTop() + viGetViewHeight() / 2;
|
|
|
|
if ((playercount == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB()))
|
|
|| playercount >= 3) {
|
|
if ((g_Vars.currentplayernum % 2) == 0) {
|
|
*x += 8;
|
|
} else {
|
|
*x -= 8;
|
|
}
|
|
}
|
|
#else
|
|
s32 playercount = PLAYERCOUNT();
|
|
|
|
*x = g_AmMenus[g_AmIndex].xradius * (column - 1);
|
|
*y = (row - 1) * 50;
|
|
|
|
if (column != 1 && row != 1) {
|
|
*x = *x / 2;
|
|
*y = *y / 2;
|
|
|
|
if (1);
|
|
}
|
|
|
|
if (row == 1 && amIsCramped() && column != 1) {
|
|
*x = *x / 2;
|
|
|
|
if (*x < 0) {
|
|
*x -= 4;
|
|
} else {
|
|
*x += 4;
|
|
}
|
|
}
|
|
|
|
if (playercount >= 2) {
|
|
if (row == 1 && !amIsCramped()) {
|
|
*x = (*x * 6) / 7;
|
|
}
|
|
|
|
*y = (*y * 3) / 5;
|
|
} else if (playercount >= 3) {
|
|
if (row == 1 && !amIsCramped()) {
|
|
*x = (*x * 6) / 14;
|
|
}
|
|
|
|
*y = (*y * 3) / 5;
|
|
}
|
|
|
|
*x += viGetViewLeft() / g_ScaleX + viGetViewWidth() / (g_ScaleX * 2);
|
|
*y += viGetViewTop() + viGetViewHeight() / 2;
|
|
|
|
if ((playercount == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) || playercount >= 3) {
|
|
if ((g_Vars.currentplayernum % 2) == 0) {
|
|
*x += 8;
|
|
} else {
|
|
*x -= 8;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Gfx *amRenderText(Gfx *gdl, char *text, u32 colour, s16 left, s16 top)
|
|
{
|
|
s32 x;
|
|
s32 y;
|
|
s32 textwidth;
|
|
s32 textheight;
|
|
|
|
textMeasure(&textheight, &textwidth, text, g_AmFont1, g_AmFont2, 0);
|
|
|
|
x = left - (textwidth / 2);
|
|
y = top - 4;
|
|
gdl = textRenderProjected(gdl, &x, &y, text, g_AmFont1, g_AmFont2, colour, 320, 240, 0, 0);
|
|
|
|
return gdl;
|
|
}
|
|
|
|
Gfx *amRenderAibotInfo(Gfx *gdl, s32 buddynum)
|
|
{
|
|
s32 x;
|
|
s32 y;
|
|
s32 textwidth;
|
|
s32 textheight;
|
|
s32 weaponnum;
|
|
char *weaponname;
|
|
char *aibotname;
|
|
s32 offset = 0;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
bool wide = false;
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (PLAYERCOUNT() == 1 && optionsGetEffectiveScreenSize() != SCREENSIZE_FULL) {
|
|
wide = true;
|
|
}
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if ((PLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) || PLAYERCOUNT() >= 3)
|
|
#else
|
|
if ((PLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) || PLAYERCOUNT() >= 3)
|
|
#endif
|
|
{
|
|
if ((g_Vars.currentplayernum % 2) == 0) {
|
|
offset = 8;
|
|
} else {
|
|
offset = -8;
|
|
}
|
|
}
|
|
|
|
if (!g_AmMenus[g_AmIndex].allbots) {
|
|
buddynum = g_Vars.currentplayer->aibuddynums[buddynum];
|
|
aibotname = g_MpAllChrConfigPtrs[buddynum]->name;
|
|
|
|
if (g_MpAllChrPtrs[buddynum]->aibot) {
|
|
weaponnum = g_MpAllChrPtrs[buddynum]->aibot->weaponnum;
|
|
} else {
|
|
weaponnum = 0;
|
|
}
|
|
|
|
if (weaponnum < WEAPON_FALCON2 || weaponnum > WEAPON_HORIZONSCANNER) {
|
|
weaponname = langGet(L_MISC_173); // "No Weapon"
|
|
} else {
|
|
weaponname = bgunGetShortName(weaponnum);
|
|
}
|
|
|
|
textMeasure(&textheight, &textwidth, aibotname, g_AmFont1, g_AmFont2, 0);
|
|
|
|
x = viGetViewLeft() / g_ScaleX
|
|
+ (s32)(viGetViewWidth() / g_ScaleX * 0.5f)
|
|
- (s32)(textwidth * 0.5f)
|
|
+ offset;
|
|
|
|
if (PLAYERCOUNT() >= 2) {
|
|
y = viGetViewTop() + 5;
|
|
} else {
|
|
y = viGetViewTop() + 10;
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (wide) {
|
|
x = viGetViewLeft() / g_ScaleX + 32;
|
|
}
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
gdl = func0f1574d0jf(gdl, &x, &y, aibotname, g_AmFont1, g_AmFont2, -1,
|
|
0x000000ff, 320, 240, 0, 0);
|
|
|
|
y += (PLAYERCOUNT() >= 2) ? 0 : (s32)(textheight * 1.1f);
|
|
#else
|
|
gdl = textRender(gdl, &x, &y, aibotname, g_AmFont1, g_AmFont2, -1,
|
|
0x000000ff, 320, 240, 0, 0);
|
|
|
|
y += (PLAYERCOUNT() >= 2) ? 0 : (s32)(textheight * 1.1f);
|
|
textMeasure(&textheight, &textwidth, weaponname, g_AmFont1, g_AmFont2, 0);
|
|
|
|
x = viGetViewLeft() / g_ScaleX
|
|
+ (s32)(viGetViewWidth() / g_ScaleX * 0.5f)
|
|
- (s32)(textwidth * 0.5f)
|
|
+ offset;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (wide) {
|
|
x = viGetViewLeft() / g_ScaleX + 32;
|
|
}
|
|
#endif
|
|
|
|
gdl = textRender(gdl, &x, &y, weaponname, g_AmFont1, g_AmFont2, -1,
|
|
0x000000ff, 320, 240, 0, 0);
|
|
#endif
|
|
|
|
g_Vars.currentplayer->commandingaibot = g_MpAllChrPtrs[buddynum];
|
|
} else {
|
|
char *title = langGet(L_MISC_215); // "All Simulants"
|
|
|
|
textMeasure(&textheight, &textwidth, title, g_AmFont1, g_AmFont2, 0);
|
|
|
|
x = viGetViewLeft() / g_ScaleX
|
|
+ (s32)(viGetViewWidth() / g_ScaleX * 0.5f)
|
|
- (s32)(textwidth * 0.5f)
|
|
+ offset;
|
|
|
|
if (PLAYERCOUNT() >= 2) {
|
|
y = viGetViewTop() + 5;
|
|
} else {
|
|
y = viGetViewTop() + 10;
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (wide) {
|
|
x = viGetViewLeft() / g_ScaleX + 32;
|
|
}
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
gdl = func0f1574d0jf(gdl, &x, &y, title, g_AmFont1, g_AmFont2, -1,
|
|
0x000000ff, 320, 240, 0, 0);
|
|
#else
|
|
gdl = textRender(gdl, &x, &y, title, g_AmFont1, g_AmFont2, -1,
|
|
0x000000ff, 320, 240, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
return gdl;
|
|
}
|
|
|
|
const char var7f1b2b34[] = "Here is where the activemenu sets favourites\n";
|
|
const char var7f1b2b64[] = "slot %d = guntype %d\n";
|
|
const char var7f1b2b7c[] = "put it in %d\n";
|
|
|
|
#if VERSION < VERSION_JPN_FINAL
|
|
const char var7f1b2b8c[] = "activemenu: setting up for multiplayer\n";
|
|
const char var7f1b2bb4[] = "activemenu: setting up for single player\n";
|
|
#endif
|
|
|
|
const char var7f1b2be0[] = "Put guntype %d in slot %d\n";
|
|
const char var7f1b2bfc[] = "ActiveMenu: Two or more equipped items of guntype %d\n";
|
|
const char var7f1b2c34[] = "FAV: Added gun %d to slot %d\n";
|
|
|
|
u8 var800719a0[][3] = { {0, 1, 2}, {3, 4, 5}, {6, 7, 8} };
|
|
|
|
Gfx *amRenderSlot(Gfx *gdl, char *text, s16 x, s16 y, s32 mode, s32 flags)
|
|
{
|
|
static u32 obcol = 0xff00004f; // outer border
|
|
static u32 ibcol = VERSION >= VERSION_NTSC_1_0 ? 0x3f00008f : 0x3f00006f; // inner background
|
|
static u32 defcol = 0xff4f00ff; // text
|
|
static u32 favcol = 0xffff7fff; // unused
|
|
static u32 pickcol = 0xff4f00ff; // unused
|
|
static u32 pickcol2 = 0xff4f00ff; // unused
|
|
|
|
u32 colour;
|
|
s32 paddingtop;
|
|
s32 paddingbottom;
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
paddingtop = 7;
|
|
paddingbottom = 7;
|
|
#else
|
|
paddingtop = 6;
|
|
paddingbottom = 6;
|
|
|
|
if (PLAYERCOUNT() >= 2) {
|
|
paddingtop = 5;
|
|
paddingbottom = 3;
|
|
}
|
|
#endif
|
|
|
|
if (text == NULL || strcmp(text, "") == 0) {
|
|
return gdl;
|
|
}
|
|
|
|
mainOverrideVariable("obcol", &obcol);
|
|
mainOverrideVariable("ibcol", &ibcol);
|
|
mainOverrideVariable("defcol", &defcol);
|
|
mainOverrideVariable("favcol", &favcol);
|
|
mainOverrideVariable("pickcol", &pickcol);
|
|
mainOverrideVariable("pickcol2", &pickcol2);
|
|
|
|
// Render background colour
|
|
colour = (u32)(g_AmMenus[g_AmIndex].alphafrac * (ibcol & 0xff)) | (ibcol & 0xffffff00);
|
|
|
|
if (mode == AMSLOTMODE_FOCUSED) {
|
|
colour &= 0x000000ff;
|
|
}
|
|
|
|
if (mode == AMSLOTMODE_CURRENT || (flags & AMSLOTFLAG_CURRENT)) {
|
|
colour &= 0x000000ff;
|
|
}
|
|
|
|
if (flags & AMSLOTFLAG_NOAMMO) {
|
|
colour &= 0x000000ff;
|
|
}
|
|
|
|
if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) {
|
|
colour = 0x0000006f;
|
|
}
|
|
|
|
gdl = textSetPrimColour(gdl, colour);
|
|
|
|
gDPFillRectangleScaled(gdl++,
|
|
x - g_AmMenus[g_AmIndex].slotwidth / 2 + 1,
|
|
y - paddingtop + 1,
|
|
x + g_AmMenus[g_AmIndex].slotwidth / 2,
|
|
y + paddingbottom);
|
|
|
|
gdl = text0f153838(gdl);
|
|
|
|
// Render borders
|
|
colour = obcol;
|
|
|
|
if (flags & AMSLOTFLAG_NOAMMO) {
|
|
colour &= 0x000000ff;
|
|
}
|
|
|
|
if (mode == AMSLOTMODE_CURRENT || (flags & AMSLOTFLAG_CURRENT)) {
|
|
colour = 0xffffff8f;
|
|
}
|
|
|
|
colour = (u32)(g_AmMenus[g_AmIndex].alphafrac * (colour & 0xff)) | (colour & 0xffffff00);
|
|
|
|
if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) {
|
|
colour = 0x4f4f4f7f;
|
|
}
|
|
|
|
gdl = textSetPrimColour(gdl, colour);
|
|
|
|
// Top border
|
|
gDPFillRectangleScaled(gdl++,
|
|
x - g_AmMenus[g_AmIndex].slotwidth / 2,
|
|
y - paddingtop,
|
|
x + g_AmMenus[g_AmIndex].slotwidth / 2 + 1,
|
|
y - paddingtop + 1);
|
|
|
|
// Bottom border
|
|
gDPFillRectangleScaled(gdl++,
|
|
x - g_AmMenus[g_AmIndex].slotwidth / 2,
|
|
y + paddingbottom,
|
|
x + g_AmMenus[g_AmIndex].slotwidth / 2 + 1,
|
|
y + paddingbottom + 1);
|
|
|
|
// Left border
|
|
gDPFillRectangleScaled(gdl++,
|
|
x - g_AmMenus[g_AmIndex].slotwidth / 2,
|
|
y - paddingtop + 1,
|
|
x - g_AmMenus[g_AmIndex].slotwidth / 2 + 1,
|
|
y + paddingbottom);
|
|
|
|
// Right border
|
|
gDPFillRectangleScaled(gdl++,
|
|
x + g_AmMenus[g_AmIndex].slotwidth / 2,
|
|
y - paddingtop + 1,
|
|
x + g_AmMenus[g_AmIndex].slotwidth / 2 + 1,
|
|
y + paddingbottom);
|
|
|
|
gdl = text0f153838(gdl);
|
|
|
|
// Render text
|
|
colour = defcol;
|
|
|
|
if (mode == AMSLOTMODE_CURRENT || (flags & AMSLOTFLAG_CURRENT)) {
|
|
colour = 0xffffffff;
|
|
}
|
|
|
|
if (flags & AMSLOTFLAG_ACTIVE) {
|
|
colour = colourBlend(0xffaf8fff, colour, menuGetCosOscFrac(10) * 255.0f);
|
|
}
|
|
|
|
colour = (u32)(g_AmMenus[g_AmIndex].alphafrac * (colour & 0xff)) | (colour & 0xffffff00);
|
|
|
|
if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) {
|
|
colour = 0x4f4f4f7f;
|
|
}
|
|
|
|
gdl = amRenderText(gdl, text, colour, x, y);
|
|
|
|
return gdl;
|
|
}
|
|
|
|
Gfx *amRender(Gfx *gdl)
|
|
{
|
|
struct chrdata *chr;
|
|
u32 flags;
|
|
u32 *colours;
|
|
struct gfxvtx *vertices;
|
|
s32 mpchrnum;
|
|
s16 column;
|
|
s16 row;
|
|
u32 colour;
|
|
s16 slotx;
|
|
s16 sloty;
|
|
s16 tmp1;
|
|
s16 tmp2;
|
|
|
|
#if PAL
|
|
g_ScaleX = 1;
|
|
#else
|
|
g_ScaleX = g_ViRes == VIRES_HI ? 2 : 1;
|
|
#endif
|
|
|
|
g_AmIndex = g_Vars.currentplayernum;
|
|
g_Vars.currentplayer->commandingaibot = NULL;
|
|
|
|
if (g_Vars.currentplayer->activemenumode != AMMODE_CLOSED) {
|
|
// Draw diamond
|
|
gdl = text0f153628(gdl);
|
|
|
|
if (g_Vars.normmplayerisrunning
|
|
&& g_AmMenus[g_AmIndex].screenindex >= 2) {
|
|
mpchrnum = g_Vars.currentplayer->aibuddynums[g_AmMenus[g_AmIndex].screenindex - 2];
|
|
}
|
|
|
|
if (g_AmMenus[g_AmIndex].dstx == -123) {
|
|
amCalculateSlotPosition(
|
|
g_AmMenus[g_AmIndex].slotnum % 3,
|
|
g_AmMenus[g_AmIndex].slotnum / 3,
|
|
&g_AmMenus[g_AmIndex].selx,
|
|
&g_AmMenus[g_AmIndex].sely);
|
|
g_AmMenus[g_AmIndex].dstx = g_AmMenus[g_AmIndex].selx;
|
|
g_AmMenus[g_AmIndex].dsty = g_AmMenus[g_AmIndex].sely;
|
|
} else {
|
|
amCalculateSlotPosition(
|
|
g_AmMenus[g_AmIndex].slotnum % 3,
|
|
g_AmMenus[g_AmIndex].slotnum / 3,
|
|
&g_AmMenus[g_AmIndex].dstx,
|
|
&g_AmMenus[g_AmIndex].dsty);
|
|
}
|
|
|
|
gdl = func0f0d479c(gdl);
|
|
|
|
colours = gfxAllocateColours(2);
|
|
vertices = gfxAllocateVertices(8);
|
|
|
|
gDPPipeSync(gdl++);
|
|
gDPSetCycleType(gdl++, G_CYC_1CYCLE);
|
|
gDPSetAlphaCompare(gdl++, G_AC_NONE);
|
|
gDPSetAlphaDither(gdl++, G_AD_NOISE);
|
|
gDPSetCombineMode(gdl++, G_CC_MODULATEI, G_CC_MODULATEI);
|
|
gSPClearGeometryMode(gdl++, G_CULL_BOTH);
|
|
|
|
texSelect(&gdl, NULL, 2, 0, 2, 1, NULL);
|
|
|
|
gDPSetRenderMode(gdl++, G_RM_XLU_SURF, G_RM_XLU_SURF2);
|
|
|
|
// Top
|
|
amCalculateSlotPosition(1, 0, &slotx, &sloty);
|
|
|
|
vertices[0].x = slotx * 10;
|
|
vertices[0].y = sloty * 10;
|
|
vertices[0].z = -10;
|
|
|
|
// Right
|
|
amCalculateSlotPosition(2, 1, &slotx, &sloty);
|
|
|
|
vertices[1].x = slotx * 10;
|
|
vertices[1].y = sloty * 10;
|
|
vertices[1].z = -10;
|
|
|
|
// Bottom
|
|
amCalculateSlotPosition(1, 2, &slotx, &sloty);
|
|
|
|
vertices[2].x = slotx * 10;
|
|
vertices[2].y = sloty * 10;
|
|
vertices[2].z = -10;
|
|
|
|
// Left
|
|
amCalculateSlotPosition(0, 1, &slotx, &sloty);
|
|
|
|
vertices[3].x = slotx * 10;
|
|
vertices[3].y = sloty * 10;
|
|
vertices[3].z = -10;
|
|
|
|
vertices[4].z = -10;
|
|
vertices[5].z = -10;
|
|
vertices[6].z = -10;
|
|
vertices[7].z = -10;
|
|
|
|
tmp2 = (vertices[1].x - vertices[3].x) / 8;
|
|
tmp1 = (vertices[2].y - vertices[0].y) / 8;
|
|
|
|
vertices[4].x = vertices[0].x;
|
|
vertices[4].y = vertices[0].y + tmp1;
|
|
vertices[5].x = vertices[1].x - tmp2;
|
|
vertices[5].y = vertices[1].y;
|
|
vertices[6].x = vertices[2].x;
|
|
vertices[6].y = vertices[2].y - tmp1;
|
|
vertices[7].x = vertices[3].x + tmp2;
|
|
vertices[7].y = vertices[3].y;
|
|
|
|
vertices[0].colour = 0;
|
|
vertices[1].colour = 0;
|
|
vertices[2].colour = 0;
|
|
vertices[3].colour = 0;
|
|
vertices[4].colour = 4;
|
|
vertices[5].colour = 4;
|
|
vertices[6].colour = 4;
|
|
vertices[7].colour = 4;
|
|
|
|
colours[0] = 0x22222200;
|
|
colours[1] = 0x0000004f;
|
|
|
|
gDPSetColorArray(gdl++, osVirtualToPhysical(colours), 2);
|
|
gDPSetVerticeArray(gdl++, osVirtualToPhysical(vertices), 8);
|
|
|
|
gDPTri2(gdl++, 4, 5, 6, 6, 7, 4);
|
|
gDPTri4(gdl++, 0, 4, 7, 7, 3, 0, 0, 1, 5, 5, 4, 0);
|
|
gDPTri4(gdl++, 1, 2, 6, 6, 5, 1, 6, 2, 3, 3, 7, 6);
|
|
|
|
gdl = func0f0d49c8(gdl);
|
|
|
|
// Draw slots
|
|
for (column = 0; column < 3; column++) {
|
|
for (row = 0; row < 3; row++) {
|
|
s16 slotx;
|
|
s16 sloty;
|
|
u32 mode;
|
|
char text[32];
|
|
s32 buddynum;
|
|
|
|
mode = AMSLOTMODE_DEFAULT;
|
|
amCalculateSlotPosition(column, row, &slotx, &sloty);
|
|
flags = 0;
|
|
|
|
if (column + row * 3 == g_AmMenus[g_AmIndex].slotnum) {
|
|
mode = AMSLOTMODE_FOCUSED;
|
|
}
|
|
|
|
if (g_MissionConfig.iscoop && (buddynum = amGetFirstBuddyIndex(), buddynum >= 0)) {
|
|
if (mode == AMSLOTMODE_DEFAULT && g_AmMenus[g_AmIndex].screenindex >= 2) {
|
|
struct chrdata *chr = g_Vars.aibuddies[buddynum]->chr;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (var800719a0[row][column] == 7) {
|
|
if (chr->hidden & CHRHFLAG_PASSIVE) {
|
|
mode = AMSLOTMODE_CURRENT;
|
|
}
|
|
} else if (var800719a0[row][column] == 1) {
|
|
if ((chr->hidden & CHRHFLAG_PASSIVE) == 0) {
|
|
mode = AMSLOTMODE_CURRENT;
|
|
}
|
|
}
|
|
#else
|
|
if (chr->hidden & CHRHFLAG_PASSIVE) {
|
|
if (var800719a0[row][column] == 7) {
|
|
mode = AMSLOTMODE_CURRENT;
|
|
}
|
|
} else {
|
|
if (var800719a0[row][column] == 1) {
|
|
mode = AMSLOTMODE_CURRENT;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
if (g_Vars.normmplayerisrunning
|
|
&& mode == AMSLOTMODE_DEFAULT
|
|
&& g_AmMenus[g_AmIndex].screenindex >= 2) {
|
|
s32 slotcmd = g_AmBotCommands[var800719a0[row][column]];
|
|
s32 botcmd = g_MpAllChrPtrs[mpchrnum]->aibot->command;
|
|
|
|
if (slotcmd == botcmd) {
|
|
mode = AMSLOTMODE_CURRENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
colour = 0xffffffff;
|
|
|
|
if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) {
|
|
colour = 0x4f4f4f7f;
|
|
}
|
|
|
|
amGetSlotDetails(column + row * 3, &flags, text);
|
|
|
|
if (column == 1 && row == 1) {
|
|
if (!amIsCramped()) {
|
|
gdl = amRenderText(gdl, text, colour, slotx, sloty);
|
|
}
|
|
} else {
|
|
gdl = amRenderSlot(gdl, text, slotx, sloty, mode, flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Render AI bot name and weapon
|
|
{
|
|
struct g_vars *vars = &g_Vars;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
if (!(g_MissionConfig.iscoop && amGetFirstBuddyIndex() >= 0)
|
|
&& g_Vars.normmplayerisrunning
|
|
&& g_AmMenus[g_AmIndex].screenindex >= 2) {
|
|
gdl = amRenderAibotInfo(gdl, g_AmMenus[g_AmIndex].screenindex - 2);
|
|
}
|
|
#else
|
|
if (!(g_MissionConfig.iscoop && amGetFirstBuddyIndex() >= 0)
|
|
&& vars->normmplayerisrunning
|
|
&& g_AmMenus[g_AmIndex].screenindex >= 2) {
|
|
gdl = amRenderAibotInfo(gdl, g_AmMenus[g_AmIndex].screenindex - 2);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Note: the column and row values will never be 1 here, so this
|
|
// condition always passes. Looks like they intended to skip drawing the
|
|
// selection box on the simulants screen if the middle box was selected.
|
|
if (g_AmMenus[g_AmIndex].screenindex < 2 || column != 1 || row != 1) {
|
|
// Render selection
|
|
s32 halfwidth;
|
|
s16 above;
|
|
s16 below;
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
above = 7;
|
|
below = 7;
|
|
#else
|
|
above = 6;
|
|
below = 6;
|
|
|
|
if (PLAYERCOUNT() >= 2) {
|
|
above = 5;
|
|
below = 3;
|
|
}
|
|
#endif
|
|
|
|
colour = (sinf(g_AmMenus[g_AmIndex].selpulse) + 1) * 127;
|
|
colour = 0xff0000ff | colour << 8 | colour << 16;
|
|
|
|
if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) {
|
|
colour = 0x4f4f4f7f;
|
|
}
|
|
|
|
gdl = textSetPrimColour(gdl, colour);
|
|
|
|
halfwidth = g_AmMenus[g_AmIndex].slotwidth / 2;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (g_AmMenus[g_AmIndex].slotnum == 4) {
|
|
if (amIsCramped()) {
|
|
halfwidth = 1;
|
|
above = 2;
|
|
below = 0;
|
|
} else if (PLAYERCOUNT() >= 2) {
|
|
s32 textheight;
|
|
s32 textwidth;
|
|
char text[32];
|
|
u32 flags;
|
|
|
|
amGetSlotDetails(4, &flags, text);
|
|
textMeasure(&textheight, &textwidth, text, g_AmFont1, g_AmFont2, 0);
|
|
|
|
halfwidth = textwidth / 2 + 2;
|
|
}
|
|
}
|
|
#else
|
|
if (g_AmMenus[g_AmIndex].slotnum == 4 && amIsCramped()) {
|
|
halfwidth = 4;
|
|
}
|
|
#endif
|
|
|
|
// Top
|
|
gDPFillRectangleScaled(gdl++,
|
|
g_AmMenus[g_AmIndex].selx - halfwidth,
|
|
g_AmMenus[g_AmIndex].sely - above,
|
|
g_AmMenus[g_AmIndex].selx + halfwidth + 1,
|
|
g_AmMenus[g_AmIndex].sely - above + 1);
|
|
|
|
// Bottom
|
|
gDPFillRectangleScaled(gdl++,
|
|
g_AmMenus[g_AmIndex].selx - halfwidth,
|
|
g_AmMenus[g_AmIndex].sely + below,
|
|
g_AmMenus[g_AmIndex].selx + halfwidth + 1,
|
|
g_AmMenus[g_AmIndex].sely + below + 1);
|
|
|
|
// Left
|
|
gDPFillRectangleScaled(gdl++,
|
|
g_AmMenus[g_AmIndex].selx - halfwidth,
|
|
g_AmMenus[g_AmIndex].sely - above + 1,
|
|
g_AmMenus[g_AmIndex].selx - halfwidth + 1,
|
|
g_AmMenus[g_AmIndex].sely + below);
|
|
|
|
// Right
|
|
gDPFillRectangleScaled(gdl++,
|
|
g_AmMenus[g_AmIndex].selx + halfwidth,
|
|
g_AmMenus[g_AmIndex].sely - above + 1,
|
|
g_AmMenus[g_AmIndex].selx + halfwidth + 1,
|
|
g_AmMenus[g_AmIndex].sely + below);
|
|
|
|
gdl = text0f153838(gdl);
|
|
}
|
|
|
|
gdl = text0f153780(gdl);
|
|
}
|
|
|
|
#if VERSION != VERSION_JPN_FINAL
|
|
chr = g_Vars.currentplayer->commandingaibot;
|
|
|
|
if (chr) {
|
|
// Render health bar
|
|
f32 healthfrac = (chr->maxdamage - chr->damage) / chr->maxdamage;
|
|
f32 shieldfrac = chr->cshield * 0.125f;
|
|
bool redhealth = false;
|
|
s32 xoffset;
|
|
s32 barwidth;
|
|
s32 barheight;
|
|
s32 part1width;
|
|
s32 part1left;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
s32 part2left;
|
|
#endif
|
|
s32 y;
|
|
s32 a2;
|
|
|
|
if (healthfrac < 0.25f) {
|
|
redhealth = true;
|
|
}
|
|
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
{
|
|
struct player *tmp = g_Vars.players[2];
|
|
if (tmp);
|
|
}
|
|
#endif
|
|
|
|
barwidth = PLAYERCOUNT() >= 2 ? 48 : 64;
|
|
barheight = PLAYERCOUNT() >= 2 ? 7 : 11;
|
|
xoffset = 0;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if ((PLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) || PLAYERCOUNT() >= 3) {
|
|
xoffset = (g_Vars.currentplayernum & 1) == 0 ? 8 : -8;
|
|
}
|
|
|
|
if (PLAYERCOUNT() == 1 && optionsGetEffectiveScreenSize() != SCREENSIZE_FULL) {
|
|
part1left = viGetViewLeft() / g_ScaleX + 32;
|
|
} else {
|
|
part1left = (s32) ((viGetViewWidth() / g_ScaleX) * 0.5f)
|
|
+ (s32) (viGetViewLeft() / g_ScaleX)
|
|
- (s32) (barwidth * 0.5f)
|
|
+ xoffset;
|
|
}
|
|
#else
|
|
if ((PLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) || PLAYERCOUNT() >= 3) {
|
|
xoffset = (g_Vars.currentplayernum & 1) == 0 ? 8 : -8;
|
|
}
|
|
|
|
part1left = (s32) ((viGetViewWidth() / g_ScaleX) * 0.5f)
|
|
+ (s32) (viGetViewLeft() / g_ScaleX)
|
|
- (s32) (barwidth * 0.5f)
|
|
+ xoffset;
|
|
#endif
|
|
|
|
part1width = (s32) (barwidth * 0.25f) - 1;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (part1width);
|
|
part2left = part1left + part1width + 2;
|
|
#endif
|
|
|
|
if (healthfrac < 0) {
|
|
healthfrac = 0;
|
|
}
|
|
|
|
gDPPipeSync(gdl++);
|
|
gDPSetCycleType(gdl++, G_CYC_1CYCLE);
|
|
gDPSetRenderMode(gdl++, G_RM_XLU_SURF, G_RM_XLU_SURF2);
|
|
gDPSetCombineMode(gdl++, G_CC_PRIMITIVE, G_CC_PRIMITIVE);
|
|
|
|
y = viGetViewTop() + viGetViewHeight() - (PLAYERCOUNT() >= 2 ? 19 : 34);
|
|
|
|
// NTSC beta doesn't scale the health bar when hi-res is on,
|
|
// and it only matches if part2left is an inline expression
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
#define RECT(gdl, x1, y1, x2, y2) gDPFillRectangleScaled(gdl, x1, y1, x2, y2)
|
|
#define PART2LEFT() part2left
|
|
#else
|
|
#define RECT(gdl, x1, y1, x2, y2) gDPFillRectangle(gdl, x1, y1, x2, y2)
|
|
#define PART2LEFT() (part1left + part1width + 2)
|
|
#endif
|
|
|
|
if (redhealth) {
|
|
a2 = part1left + part1width - (s32) (part1width * (0.25f - healthfrac) * 4.0f);
|
|
|
|
gDPSetPrimColorViaWord(gdl++, 0, 0, 0xff000060);
|
|
|
|
// Part 1 red
|
|
RECT(gdl++, a2, y, part1left + part1width, y + barheight);
|
|
|
|
gDPSetPrimColorViaWord(gdl++, 0, 0, 0x00000080);
|
|
|
|
// Part 1 black
|
|
RECT(gdl++, part1left, y, a2, y + barheight);
|
|
|
|
// Part 2 black
|
|
RECT(gdl++, PART2LEFT(), y, part1left + barwidth, y + barheight);
|
|
} else {
|
|
gDPSetPrimColorViaWord(gdl++, 0, 0, 0x00c00060);
|
|
|
|
// Part 1 green
|
|
RECT(gdl++, part1left, y, part1left + part1width, y + barheight);
|
|
|
|
// Part 2 green
|
|
a2 = part1left + (s32) (barwidth * healthfrac);
|
|
|
|
RECT(gdl++, PART2LEFT(), y, a2, y + barheight);
|
|
|
|
gDPSetPrimColorViaWord(gdl++, 0, 0, 0x00000080);
|
|
|
|
// Part 2 black
|
|
RECT(gdl++, a2, y, part1left + barwidth, y + barheight);
|
|
}
|
|
|
|
// Render shield bar
|
|
y = y + barheight + 2;
|
|
barheight = barheight * 0.75f;
|
|
|
|
gDPSetPrimColorViaWord(gdl++, 0, 0, 0x00c00060);
|
|
|
|
a2 = part1left + (s32) (barwidth * shieldfrac);
|
|
|
|
RECT(gdl++, part1left, y, a2, y + barheight);
|
|
|
|
gDPSetPrimColorViaWord(gdl++, 0, 0, 0x00000080);
|
|
|
|
RECT(gdl++, a2, y, part1left + barwidth, y + barheight);
|
|
}
|
|
#endif
|
|
|
|
g_ScaleX = 1;
|
|
|
|
return gdl;
|
|
}
|