mirror of
https://gitlab.com/ryandwyer/perfect-dark
synced 2026-08-21 05:58:42 -04:00
4034 lines
104 KiB
C
4034 lines
104 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/chraction.h"
|
|
#include "game/dlights.h"
|
|
#include "game/prop.h"
|
|
#include "game/propsnd.h"
|
|
#include "game/objectives.h"
|
|
#include "game/atan2f.h"
|
|
#include "game/bondgun.h"
|
|
#include "game/game_0b0fd0.h"
|
|
#include "game/player.h"
|
|
#include "game/hudmsg.h"
|
|
#include "game/menu.h"
|
|
#include "game/filemgr.h"
|
|
#include "game/inv.h"
|
|
#include "game/explosions.h"
|
|
#include "game/game_1531a0.h"
|
|
#include "game/bg.h"
|
|
#include "game/trainingmenus.h"
|
|
#include "game/training.h"
|
|
#include "game/gamefile.h"
|
|
#include "game/lang.h"
|
|
#include "game/pad.h"
|
|
#include "game/padhalllv.h"
|
|
#include "game/propobj.h"
|
|
#include "game/shards.h"
|
|
#include "game/wallhit.h"
|
|
#include "bss.h"
|
|
#include "lib/vi.h"
|
|
#include "lib/dma.h"
|
|
#include "lib/main.h"
|
|
#include "lib/snd.h"
|
|
#include "lib/memp.h"
|
|
#include "lib/rng.h"
|
|
#include "lib/mtx.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
#define FRSCRIPTINDEX_WEAPONS 0x00
|
|
#define FRSCRIPTINDEX_TARGETS 0x22
|
|
#define FRSCRIPTINDEX_HELP 0x71
|
|
|
|
extern u8 *_firingrangeSegmentRomStart;
|
|
extern u8 *_firingrangeSegmentRomEnd;
|
|
|
|
struct frdata g_FrData;
|
|
struct trainingdata g_DtData;
|
|
struct trainingdata g_HtData;
|
|
|
|
u16 *g_FrScriptOffsets = NULL;
|
|
u8 g_FrIsValidWeapon = false;
|
|
u8 g_FrDataLoaded = false;
|
|
u8 g_FrNumSounds = 0;
|
|
u8 *g_FrRomData = NULL;
|
|
|
|
u16 g_FrPads[] = {
|
|
0x00d6, 0x00d7, 0x00d9, 0x00d8, 0x00da, 0x00db, 0x00dc, 0x00dd,
|
|
0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5,
|
|
0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00f4, 0x00f3,
|
|
0x00f2, 0x00f1, 0x00f0, 0x00ef, 0x00ee, 0x00ed, 0x00ec,
|
|
};
|
|
|
|
bool ciIsTourDone(void)
|
|
{
|
|
return gamefileHasFlag(GAMEFILEFLAG_CI_TOUR_DONE);
|
|
}
|
|
|
|
u8 ciGetFiringRangeScore(s32 weaponindex)
|
|
{
|
|
// Data at firingrangescores is a u8 array where each score uses 2 bits
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
if (weaponindex == frGetWeaponIndexByWeapon(WEAPON_COMBATKNIFE)) {
|
|
// The knife doesn't exist in the JPN version.
|
|
// Treat it as completed so unlockables still work.
|
|
return 3;
|
|
}
|
|
#endif
|
|
|
|
return (g_GameFile.firingrangescores[weaponindex >> 2] >> (weaponindex % 4) * 2) & 3;
|
|
}
|
|
|
|
void frSaveScoreIfBest(s32 weaponindex, s32 difficulty)
|
|
{
|
|
if (ciGetFiringRangeScore(weaponindex) < difficulty) {
|
|
u32 byteindex = weaponindex >> 2;
|
|
u32 shiftamount = (weaponindex % 4) * 2;
|
|
u32 value = g_GameFile.firingrangescores[byteindex];
|
|
u32 mask = (1 << shiftamount) + (1 << (shiftamount + 1));
|
|
|
|
value &= 255 - mask;
|
|
value += (difficulty << shiftamount) & mask;
|
|
|
|
g_GameFile.firingrangescores[byteindex] = value;
|
|
}
|
|
}
|
|
|
|
s32 func0f19ca78(u32 weaponnum)
|
|
{
|
|
s32 slot = -1;
|
|
s32 i;
|
|
|
|
for (i = 0; i <= WEAPON_HORIZONSCANNER; i++) {
|
|
switch (i) {
|
|
case WEAPON_FALCON2:
|
|
case WEAPON_FALCON2_SCOPE:
|
|
case WEAPON_FALCON2_SILENCER:
|
|
case WEAPON_MAGSEC4:
|
|
case WEAPON_MAULER:
|
|
case WEAPON_PHOENIX:
|
|
case WEAPON_DY357MAGNUM:
|
|
case WEAPON_DY357LX:
|
|
case WEAPON_CMP150:
|
|
case WEAPON_CYCLONE:
|
|
case WEAPON_CALLISTO:
|
|
case WEAPON_RCP120:
|
|
case WEAPON_LAPTOPGUN:
|
|
case WEAPON_DRAGON:
|
|
case WEAPON_K7AVENGER:
|
|
case WEAPON_AR34:
|
|
case WEAPON_SUPERDRAGON:
|
|
case WEAPON_SHOTGUN:
|
|
case WEAPON_SNIPERRIFLE:
|
|
case WEAPON_FARSIGHT:
|
|
case WEAPON_CROSSBOW:
|
|
case WEAPON_TRANQUILIZER:
|
|
case WEAPON_REAPER:
|
|
case WEAPON_DEVASTATOR:
|
|
case WEAPON_ROCKETLAUNCHER:
|
|
case WEAPON_SLAYER:
|
|
case WEAPON_COMBATKNIFE:
|
|
case WEAPON_LASER:
|
|
case WEAPON_GRENADE:
|
|
case WEAPON_TIMEDMINE:
|
|
case WEAPON_PROXIMITYMINE:
|
|
case WEAPON_REMOTEMINE:
|
|
slot++;
|
|
}
|
|
|
|
if (i == weaponnum) {
|
|
return slot;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
u8 frIsWeaponFound(s32 weaponnum)
|
|
{
|
|
u32 byteindex;
|
|
|
|
if (weaponnum <= WEAPON_UNARMED) {
|
|
return true;
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (weaponnum < (s32)sizeof(g_GameFile.weaponsfound) * 8) {
|
|
byteindex = weaponnum >> 3;
|
|
return g_GameFile.weaponsfound[byteindex] & (1 << (weaponnum % 8));
|
|
}
|
|
|
|
return false;
|
|
#else
|
|
byteindex = weaponnum >> 3;
|
|
return g_GameFile.weaponsfound[byteindex] & (1 << (weaponnum % 8));
|
|
#endif
|
|
}
|
|
|
|
void frSetWeaponFound(s32 weaponnum)
|
|
{
|
|
if (weaponnum < (s32)sizeof(g_GameFile.weaponsfound) * 8) {
|
|
u32 byteindex = weaponnum >> 3;
|
|
u32 value = g_GameFile.weaponsfound[byteindex];
|
|
|
|
value |= (1 << (weaponnum % 8));
|
|
|
|
g_GameFile.weaponsfound[byteindex] = value;
|
|
}
|
|
}
|
|
|
|
s32 ciIsStageComplete(s32 stageindex)
|
|
{
|
|
return g_GameFile.besttimes[stageindex][0]
|
|
|| g_GameFile.besttimes[stageindex][1]
|
|
|| g_GameFile.besttimes[stageindex][2];
|
|
}
|
|
|
|
bool func0f19cbcc(s32 weapon)
|
|
{
|
|
if (weapon <= 0 || weapon == WEAPON_PSYCHOSISGUN) {
|
|
return false;
|
|
}
|
|
|
|
if (weapon == WEAPON_XRAYSCANNER && ciIsStageComplete(SOLOSTAGEINDEX_INFILTRATION)) {
|
|
return true;
|
|
}
|
|
|
|
if (weapon == WEAPON_CLOAKINGDEVICE && ciIsStageComplete(SOLOSTAGEINDEX_CHICAGO)) {
|
|
return true;
|
|
}
|
|
|
|
return frIsWeaponFound(weapon);
|
|
}
|
|
|
|
bool frIsWeaponAvailable(s32 weapon)
|
|
{
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
if (weapon == WEAPON_COMBATKNIFE) {
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (weapon < WEAPON_FALCON2 || weapon > WEAPON_REMOTEMINE
|
|
|| weapon == WEAPON_PSYCHOSISGUN
|
|
|| weapon == WEAPON_COMBATBOOST
|
|
|| weapon == WEAPON_NBOMB) {
|
|
return false;
|
|
}
|
|
|
|
if (weapon == WEAPON_FALCON2 || weapon == WEAPON_CMP150) {
|
|
return true;
|
|
}
|
|
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
if (debugIsAllTrainingEnabled() && weapon <= WEAPON_XRAYSCANNER) {
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return frIsWeaponFound(weapon);
|
|
}
|
|
|
|
u32 frGetWeaponIndexByWeapon(u32 weaponnum)
|
|
{
|
|
switch (weaponnum) {
|
|
case WEAPON_FALCON2: return 0;
|
|
case WEAPON_FALCON2_SCOPE: return 1;
|
|
case WEAPON_FALCON2_SILENCER: return 2;
|
|
case WEAPON_MAGSEC4: return 3;
|
|
case WEAPON_MAULER: return 4;
|
|
case WEAPON_PHOENIX: return 5;
|
|
case WEAPON_DY357MAGNUM: return 6;
|
|
case WEAPON_DY357LX: return 7;
|
|
case WEAPON_CMP150: return 8;
|
|
case WEAPON_CYCLONE: return 9;
|
|
case WEAPON_CALLISTO: return 10;
|
|
case WEAPON_RCP120: return 11;
|
|
case WEAPON_LAPTOPGUN: return 12;
|
|
case WEAPON_DRAGON: return 13;
|
|
case WEAPON_K7AVENGER: return 14;
|
|
case WEAPON_AR34: return 15;
|
|
case WEAPON_SUPERDRAGON: return 16;
|
|
case WEAPON_SHOTGUN: return 17;
|
|
case WEAPON_SNIPERRIFLE: return 18;
|
|
case WEAPON_FARSIGHT: return 19;
|
|
case WEAPON_CROSSBOW: return 20;
|
|
case WEAPON_TRANQUILIZER: return 21;
|
|
case WEAPON_REAPER: return 22;
|
|
case WEAPON_DEVASTATOR: return 23;
|
|
case WEAPON_ROCKETLAUNCHER: return 24;
|
|
case WEAPON_SLAYER: return 25;
|
|
case WEAPON_COMBATKNIFE: return 26;
|
|
case WEAPON_LASER: return 27;
|
|
case WEAPON_GRENADE: return 28;
|
|
case WEAPON_TIMEDMINE: return 29;
|
|
case WEAPON_PROXIMITYMINE: return 30;
|
|
case WEAPON_REMOTEMINE: return 31;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 frGetWeaponScriptIndex(u32 weaponnum)
|
|
{
|
|
switch (weaponnum) {
|
|
case WEAPON_FALCON2: return 1;
|
|
case WEAPON_FALCON2_SCOPE: return 2;
|
|
case WEAPON_FALCON2_SILENCER: return 3;
|
|
case WEAPON_MAGSEC4: return 4;
|
|
case WEAPON_MAULER: return 5;
|
|
case WEAPON_PHOENIX: return 6;
|
|
case WEAPON_DY357MAGNUM: return 7;
|
|
case WEAPON_DY357LX: return 8;
|
|
case WEAPON_CMP150: return 9;
|
|
case WEAPON_CYCLONE: return 10;
|
|
case WEAPON_CALLISTO: return 11;
|
|
case WEAPON_RCP120: return 12;
|
|
case WEAPON_LAPTOPGUN: return 13;
|
|
case WEAPON_DRAGON: return 14;
|
|
case WEAPON_K7AVENGER: return 15;
|
|
case WEAPON_AR34: return 16;
|
|
case WEAPON_SUPERDRAGON: return 17;
|
|
case WEAPON_SHOTGUN: return 18;
|
|
case WEAPON_SNIPERRIFLE: return 19;
|
|
case WEAPON_FARSIGHT: return 20;
|
|
case WEAPON_CROSSBOW: return 21;
|
|
case WEAPON_TRANQUILIZER: return 22;
|
|
case WEAPON_REAPER: return 23;
|
|
case WEAPON_DEVASTATOR: return 24;
|
|
case WEAPON_ROCKETLAUNCHER: return 25;
|
|
case WEAPON_SLAYER: return 26;
|
|
case WEAPON_COMBATKNIFE: return 27;
|
|
case WEAPON_LASER: return 28;
|
|
case WEAPON_GRENADE: return 29;
|
|
case WEAPON_TIMEDMINE: return 31;
|
|
case WEAPON_PROXIMITYMINE: return 32;
|
|
case WEAPON_REMOTEMINE: return 33;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 frIsClassicWeaponUnlocked(u32 weapon)
|
|
{
|
|
switch (weapon) {
|
|
case WEAPON_PP9I:
|
|
return ciGetFiringRangeScore(0) == 3
|
|
&& ciGetFiringRangeScore(1) == 3
|
|
&& ciGetFiringRangeScore(2) == 3;
|
|
case WEAPON_CC13:
|
|
return ciGetFiringRangeScore(3) == 3
|
|
&& ciGetFiringRangeScore(4) == 3
|
|
&& ciGetFiringRangeScore(5) == 3
|
|
&& ciGetFiringRangeScore(6) == 3
|
|
&& ciGetFiringRangeScore(7) == 3;
|
|
case WEAPON_KL01313:
|
|
return ciGetFiringRangeScore(8) == 3
|
|
&& ciGetFiringRangeScore(9) == 3
|
|
&& ciGetFiringRangeScore(10) == 3
|
|
&& ciGetFiringRangeScore(11) == 3;
|
|
case WEAPON_KF7SPECIAL:
|
|
return ciGetFiringRangeScore(12) == 3
|
|
&& ciGetFiringRangeScore(13) == 3
|
|
&& ciGetFiringRangeScore(14) == 3
|
|
&& ciGetFiringRangeScore(15) == 3
|
|
&& ciGetFiringRangeScore(16) == 3;
|
|
case WEAPON_ZZT:
|
|
return ciGetFiringRangeScore(17) == 3
|
|
&& ciGetFiringRangeScore(18) == 3
|
|
&& ciGetFiringRangeScore(24) == 3
|
|
&& ciGetFiringRangeScore(25) == 3;
|
|
case WEAPON_DMC:
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
return ciGetFiringRangeScore(29) == 3
|
|
&& ciGetFiringRangeScore(30) == 3
|
|
&& ciGetFiringRangeScore(31) == 3;
|
|
#else
|
|
return ciGetFiringRangeScore(29) == 3
|
|
&& ciGetFiringRangeScore(30) == 3
|
|
&& ciGetFiringRangeScore(32) == 3
|
|
&& ciGetFiringRangeScore(33) == 3
|
|
&& ciGetFiringRangeScore(34) == 3;
|
|
#endif
|
|
case WEAPON_AR53:
|
|
return ciGetFiringRangeScore(19) == 3
|
|
&& ciGetFiringRangeScore(20) == 3
|
|
&& ciGetFiringRangeScore(26) == 3
|
|
&& ciGetFiringRangeScore(28) == 3;
|
|
case WEAPON_RCP45:
|
|
return ciGetFiringRangeScore(21) == 3
|
|
&& ciGetFiringRangeScore(22) == 3
|
|
&& ciGetFiringRangeScore(23) == 3;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
s32 frGetSlot(void)
|
|
{
|
|
return g_FrData.slot;
|
|
}
|
|
|
|
void frSetSlot(s32 slot)
|
|
{
|
|
g_FrData.slot = slot;
|
|
}
|
|
|
|
u32 frGetWeaponBySlot(s32 slot)
|
|
{
|
|
s32 index = -1;
|
|
s32 weapon;
|
|
|
|
for (weapon = WEAPON_NONE; weapon <= WEAPON_HORIZONSCANNER; weapon++) {
|
|
if (frIsWeaponAvailable(weapon)) {
|
|
index++;
|
|
}
|
|
|
|
if (slot == index) {
|
|
return weapon;
|
|
}
|
|
}
|
|
|
|
return WEAPON_UNARMED;
|
|
}
|
|
|
|
s32 frGetNumWeaponsAvailable(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = WEAPON_UNARMED; i <= WEAPON_HORIZONSCANNER; i++) {
|
|
if (frIsWeaponAvailable(i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void frInitLighting(void)
|
|
{
|
|
if (g_FrData.donelighting == false) {
|
|
s32 roomnum;
|
|
|
|
for (roomnum = 7; roomnum < 10; roomnum++) {
|
|
roomSetLighting(roomnum, LIGHTOP_3, 50, 100, TICKS(32));
|
|
}
|
|
|
|
roomSetLighting(ROOM_DISH_FIRINGRANGE, LIGHTOP_3, 25, 100, TICKS(32));
|
|
|
|
g_FrData.donelighting = true;
|
|
|
|
sndStart(var80095200, SFX_FR_LIGHTSON, NULL, -1, -1, -1, -1, -1);
|
|
}
|
|
|
|
chrSetStageFlag(NULL, STAGEFLAG_CI_IN_TRAINING);
|
|
}
|
|
|
|
void frRestoreLighting(void)
|
|
{
|
|
if (g_FrData.donelighting == true) {
|
|
s32 roomnum;
|
|
|
|
for (roomnum = 7; roomnum < 10; roomnum++) {
|
|
roomSetLighting(roomnum, LIGHTOP_3, 100, 50, TICKS(8));
|
|
}
|
|
|
|
roomSetLighting(ROOM_DISH_FIRINGRANGE, LIGHTOP_3, 100, 25, TICKS(8));
|
|
|
|
g_FrData.donelighting = false;
|
|
|
|
sndStart(var80095200, SFX_FR_LIGHTSOFF, NULL, -1, -1, -1, -1, -1);
|
|
}
|
|
}
|
|
|
|
void frReset(void)
|
|
{
|
|
s32 i;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
g_FrScriptOffsets = NULL;
|
|
#endif
|
|
|
|
g_FrDataLoaded = false;
|
|
g_FrIsValidWeapon = false;
|
|
g_FrRomData = NULL;
|
|
|
|
g_FrData.helpscriptindex = 0;
|
|
g_FrData.helpscriptoffset = 0;
|
|
g_FrData.helpscriptenabled = false;
|
|
g_FrData.helpscriptsleep = 0;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
g_FrData.menucountdown = 0;
|
|
g_FrData.maxactivetargets = 0;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
g_FrData.targets[i].prop = NULL;
|
|
g_FrData.targets[i].inuse = false;
|
|
}
|
|
|
|
g_FrNumSounds = 0;
|
|
#endif
|
|
}
|
|
|
|
void *frLoadRomData(u32 len)
|
|
{
|
|
g_FrRomData = mempAlloc(ALIGN16(len), MEMPOOL_STAGE);
|
|
|
|
if (g_FrRomData) {
|
|
return dmaExecWithAutoAlign(g_FrRomData, (u32)&_firingrangeSegmentRomStart, len);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void frSetDifficulty(s32 difficulty)
|
|
{
|
|
if (difficulty < FRDIFFICULTY_BRONZE) {
|
|
difficulty = FRDIFFICULTY_BRONZE;
|
|
}
|
|
|
|
if (difficulty > FRDIFFICULTY_GOLD) {
|
|
difficulty = FRDIFFICULTY_GOLD;
|
|
}
|
|
|
|
g_FrData.difficulty = difficulty;
|
|
}
|
|
|
|
u32 frGetDifficulty(void)
|
|
{
|
|
return g_FrData.difficulty;
|
|
}
|
|
|
|
void frInitDefaults(void)
|
|
{
|
|
s32 i;
|
|
struct pad pad;
|
|
|
|
g_FrNumSounds = 0;
|
|
|
|
padUnpack(g_FrPads[0], PADFIELD_POS, &pad);
|
|
|
|
g_FrData.maxactivetargets = 0;
|
|
g_FrData.goalscore = 0;
|
|
g_FrData.timelimit = 200;
|
|
g_FrData.ammolimit = 255;
|
|
g_FrData.sdgrenadelimit = 255;
|
|
g_FrData.goalaccuracy = 0;
|
|
g_FrData.goaltargets = 255;
|
|
g_FrData.speed = 1;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
g_FrData.targets[i].dstpos.x = pad.pos.x;
|
|
g_FrData.targets[i].dstpos.y = pad.pos.y;
|
|
g_FrData.targets[i].dstpos.z = pad.pos.z;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
g_FrData.targets[i].dstpos.z += 6.0f * i;
|
|
#endif
|
|
|
|
g_FrData.targets[i].inuse = false;
|
|
g_FrData.targets[i].rotateoncloak = false;
|
|
g_FrData.targets[i].destroyed = false;
|
|
g_FrData.targets[i].damage = 0;
|
|
g_FrData.targets[i].scriptoffset = 0;
|
|
|
|
g_FrData.targets[i].travelspeed = 0;
|
|
g_FrData.targets[i].scriptsleep = SECSTOTIME60(255);
|
|
g_FrData.targets[i].timeuntilrotate = 0;
|
|
|
|
g_FrData.targets[i].rotating = false;
|
|
g_FrData.targets[i].rotatespeed = 0;
|
|
g_FrData.targets[i].angle = 0;
|
|
g_FrData.targets[i].rotatetoangle = 0;
|
|
g_FrData.targets[i].silent = 0;
|
|
g_FrData.targets[i].donestopsound = false;
|
|
g_FrData.targets[i].travelling = false;
|
|
g_FrData.targets[i].invincibletimer = 0;
|
|
g_FrData.targets[i].frpadnum = -1;
|
|
|
|
wallhitsFreeByProp(g_FrData.targets[i].prop, 0);
|
|
wallhitsFreeByProp(g_FrData.targets[i].prop, 1);
|
|
}
|
|
|
|
g_FrData.timetaken = TICKS(-240);
|
|
g_FrData.score = 0;
|
|
g_FrData.numtargets = 0;
|
|
g_FrData.targetsdestroyed = 0;
|
|
g_FrData.menucountdown = 0;
|
|
g_FrData.padindexoffset = 0;
|
|
g_FrData.feedbackzone = 0;
|
|
g_FrData.feedbackttl = 0;
|
|
g_FrData.failreason = 0;
|
|
g_FrData.numshots = 0;
|
|
g_FrData.numhitsring3 = 0;
|
|
g_FrData.numhitsring2 = 0;
|
|
g_FrData.numhitsring1 = 0;
|
|
g_FrData.numhitsbullseye = 0;
|
|
g_FrData.helpscriptindex = 0;
|
|
g_FrData.helpscriptoffset = 0;
|
|
g_FrData.helpscriptenabled = false;
|
|
g_FrData.helpscriptsleep = 0;
|
|
g_FrData.proxyendtimer = 0;
|
|
g_FrData.donealarm = false;
|
|
g_FrData.ammohasgrace = true;
|
|
g_FrData.ammoextra = -1;
|
|
}
|
|
|
|
struct frdata *frGetData(void)
|
|
{
|
|
return &g_FrData;
|
|
}
|
|
|
|
u32 frResolveFrPad(u32 arg0)
|
|
{
|
|
switch (arg0) {
|
|
case 31: return random() % 9 + 4; // 4 - 12
|
|
case 32: return random() % 9 + 13; // 13 - 21
|
|
case 33: return random() % 9 + 22; // 22 - 30
|
|
case 34: return random() % 27 + 4; // 4 - 30
|
|
}
|
|
|
|
return g_FrData.padindexoffset + arg0;
|
|
}
|
|
|
|
bool frIsDifficulty(u32 flags)
|
|
{
|
|
if (g_FrData.difficulty == FRDIFFICULTY_BRONZE) {
|
|
if ((flags & FRTARGETFLAG_BRONZE) == 0) {
|
|
return false;
|
|
}
|
|
} else if (g_FrData.difficulty == FRDIFFICULTY_SILVER) {
|
|
if ((flags & FRTARGETFLAG_SILVER) == 0) {
|
|
return false;
|
|
}
|
|
} else if (g_FrData.difficulty == FRDIFFICULTY_GOLD) {
|
|
if ((flags & FRTARGETFLAG_GOLD) == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void frExecuteWeaponScript(s32 scriptindex)
|
|
{
|
|
s32 offset = 0;
|
|
|
|
if (scriptindex >= FRSCRIPTINDEX_WEAPONS && scriptindex < FRSCRIPTINDEX_TARGETS) {
|
|
u8 *script = &g_FrRomData[g_FrScriptOffsets[scriptindex]];
|
|
u8 mult = 1;
|
|
u32 stack[5];
|
|
s32 start;
|
|
s32 capacity;
|
|
s32 index;
|
|
u8 *subscript;
|
|
s32 i;
|
|
s32 val;
|
|
|
|
while (script[offset] != FRCMD_END) {
|
|
switch (script[offset]) {
|
|
case FRCMD_SETPADINDEXOFFSET:
|
|
g_FrData.padindexoffset = script[offset + 1];
|
|
offset += 2;
|
|
break;
|
|
case FRCMD_ADDTARGET:
|
|
if (!frIsDifficulty(script[offset + 4])) {
|
|
offset += 5;
|
|
break;
|
|
}
|
|
if (g_FrData.numtargets < ARRAYCOUNT(g_FrData.targets)) {
|
|
g_FrData.targets[g_FrData.numtargets].frpadindex = frResolveFrPad(script[offset + 1]);
|
|
g_FrData.targets[g_FrData.numtargets].scriptindex = script[offset + 2];
|
|
g_FrData.targets[g_FrData.numtargets].maxdamage = script[offset + 3];
|
|
g_FrData.targets[g_FrData.numtargets].inuse = true;
|
|
g_FrData.targets[g_FrData.numtargets].flags = script[offset + 4];
|
|
|
|
if (g_FrData.targets[g_FrData.numtargets].flags & FRTARGETFLAG_ROTATEONCLOAK) {
|
|
g_FrData.targets[g_FrData.numtargets].rotateoncloak = true;
|
|
}
|
|
|
|
if (g_FrData.targets[g_FrData.numtargets].flags & FRTARGETFLAG_ONEHITEXPLODE) {
|
|
g_FrData.targets[g_FrData.numtargets].maxdamage = 1;
|
|
}
|
|
|
|
g_FrData.numtargets++;
|
|
}
|
|
offset += 5;
|
|
break;
|
|
case FRCMD_SETMAXACTIVETARGETS:
|
|
g_FrData.maxactivetargets = script[g_FrData.difficulty + offset + 1];
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETSCOREMULTIPLIER:
|
|
if (script[g_FrData.difficulty + offset + 1] > 0) {
|
|
mult = script[g_FrData.difficulty + offset + 1];
|
|
} else {
|
|
mult = 1;
|
|
}
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETGOALSCORE:
|
|
g_FrData.goalscore = script[g_FrData.difficulty + offset + 1] * mult;
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETTIMELIMIT:
|
|
g_FrData.timelimit = script[g_FrData.difficulty + offset + 1];
|
|
if (g_FrData.timelimit == 255) {
|
|
g_FrData.timelimit = 120;
|
|
}
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETAMMOLIMIT:
|
|
capacity = bgunGetCapacityByAmmotype(bgunGetAmmoTypeForWeapon(frGetWeaponBySlot(g_FrData.slot), 0));
|
|
g_FrData.ammolimit = script[g_FrData.difficulty + offset + 1];
|
|
|
|
if (g_FrData.ammolimit != 255) {
|
|
if (g_FrData.ammolimit > capacity) {
|
|
g_FrData.ammoextra = g_FrData.ammolimit - capacity;
|
|
} else {
|
|
g_FrData.ammoextra = 0;
|
|
}
|
|
}
|
|
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETGRENADELIMIT:
|
|
capacity = bgunGetCapacityByAmmotype(AMMOTYPE_DEVASTATOR);
|
|
g_FrData.sdgrenadelimit = script[g_FrData.difficulty + offset + 1];
|
|
|
|
if (g_FrData.sdgrenadelimit != 255) {
|
|
if (g_FrData.sdgrenadelimit > capacity) {
|
|
g_FrData.sdgrenadeextra = g_FrData.sdgrenadelimit - capacity;
|
|
} else {
|
|
g_FrData.sdgrenadeextra = 0;
|
|
}
|
|
}
|
|
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETEXTRASPEED:
|
|
g_FrData.speed = script[g_FrData.difficulty + offset + 1] * 0.1f + 1.0f;
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETGOALACCURACY:
|
|
g_FrData.goalaccuracy = script[g_FrData.difficulty + offset + 1];
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETGOALTARGETS:
|
|
g_FrData.goaltargets = script[g_FrData.difficulty + offset + 1];
|
|
offset += 4;
|
|
break;
|
|
case FRCMD_SETHELPSCRIPT:
|
|
g_FrData.helpscriptindex = script[offset + 1];
|
|
g_FrData.helpscriptenabled = true;
|
|
index = FRSCRIPTINDEX_HELP + g_FrData.helpscriptindex;
|
|
if (&g_FrRomData[g_FrScriptOffsets[index]]);
|
|
subscript = &g_FrRomData[g_FrScriptOffsets[index]];
|
|
offset += 2;
|
|
|
|
if (g_FrData.difficulty == FRDIFFICULTY_BRONZE) {
|
|
start = FRCMD_IFBRONZE;
|
|
} else if (g_FrData.difficulty == FRDIFFICULTY_SILVER) {
|
|
start = FRCMD_IFSILVER;
|
|
} else if (g_FrData.difficulty == FRDIFFICULTY_GOLD) {
|
|
start = FRCMD_IFGOLD;
|
|
}
|
|
|
|
g_FrData.helpscriptoffset = 0;
|
|
i = 0;
|
|
|
|
while (1) {
|
|
g_FrData.helpscriptoffset++;
|
|
|
|
if (subscript[i] == start) {
|
|
i++;
|
|
|
|
val = subscript[i];
|
|
|
|
if (val >= FRCMD_IFBRONZE) {
|
|
g_FrData.helpscriptoffset++;
|
|
val = subscript[i + 1];
|
|
}
|
|
|
|
if (val >= FRCMD_IFBRONZE) {
|
|
g_FrData.helpscriptoffset++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void frSetTargetProps(void)
|
|
{
|
|
s32 i;
|
|
u32 targets[] = {
|
|
0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x11,
|
|
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
|
};
|
|
|
|
for (i = 0; i < ARRAYCOUNT(targets); i++) {
|
|
struct defaultobj *obj = objFindByTagId(targets[i]);
|
|
|
|
if (obj) {
|
|
g_FrData.targets[i].prop = obj->prop;
|
|
obj->flags2 |= OBJFLAG2_INVISIBLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 g_FrWeaponNum = WEAPON_UNARMED;
|
|
|
|
bool frTargetIsAtScriptStart(s32 targetnum)
|
|
{
|
|
return g_FrData.targets[targetnum].scriptoffset == 0;
|
|
}
|
|
|
|
/**
|
|
* 0 => "FIRING\n Press Z Button to fire gun.\n"
|
|
* 1 => "AUTO RELOAD\n Release Z Button when out of ammo.\n"
|
|
* 2 => "MANUAL RELOAD\n Press B Button to reload early if magazine not full.\n"
|
|
* 3 => "Aiming: Hold down R Button to enter Aim mode.\n"
|
|
* 4 => "Use Control Stick to move aiming sight.\n"
|
|
* 5 => "AUTO FIRE\n Hold Z Button to repeatedly fire automatically.\n"
|
|
* 6 => "ALTER AIM\n Press Up C Button or Down C Button to move sight up/down.\n"
|
|
* 7 => "ZOOM\n Hold R Button to enter Zoom mode.\n"
|
|
* 8 => "FAST FIRE\n Press Z Button quickly to fire faster.\n"
|
|
*/
|
|
char *frGetInstructionalText(u32 index)
|
|
{
|
|
u16 textid = (u16)(g_FrRomData[index * 2] << 8) | g_FrRomData[index * 2 + 1];
|
|
|
|
return langGet(textid);
|
|
}
|
|
|
|
void frExecuteHelpScript(void)
|
|
{
|
|
if (!g_FrData.helpscriptenabled || g_Vars.lvupdate240 == 0) {
|
|
return;
|
|
}
|
|
|
|
if (g_FrData.helpscriptsleep == 0) {
|
|
s32 index = FRSCRIPTINDEX_HELP + g_FrData.helpscriptindex;
|
|
u8 *script = &g_FrRomData[g_FrScriptOffsets[index]];
|
|
u32 offset = g_FrData.helpscriptoffset;
|
|
|
|
switch (script[offset]) {
|
|
case FRCMD_END:
|
|
case FRCMD_IFBRONZE:
|
|
case FRCMD_IFSILVER:
|
|
case FRCMD_IFGOLD:
|
|
g_FrData.helpscriptenabled = false;
|
|
break;
|
|
case FRCMD_HUDMSG:
|
|
hudmsgCreate(frGetInstructionalText(script[offset + 1]), HUDMSGTYPE_TRAINING);
|
|
g_FrData.helpscriptoffset += 2;
|
|
break;
|
|
case FRCMD_HELPWAITSECONDS:
|
|
#if PAL
|
|
g_FrData.helpscriptsleep = script[offset + 1] * 50;
|
|
#else
|
|
g_FrData.helpscriptsleep = SECSTOTIME60(script[offset + 1]);
|
|
#endif
|
|
g_FrData.helpscriptoffset += 2;
|
|
break;
|
|
case FRCMD_WAITUNTILSHOOT:
|
|
if (g_FrData.numshots) {
|
|
g_FrData.helpscriptoffset++;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
g_FrData.helpscriptsleep -= g_Vars.lvupdate240_60;
|
|
|
|
if (g_FrData.helpscriptsleep <= 0) {
|
|
g_FrData.helpscriptsleep = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool frExecuteTargetScript(s32 targetnum)
|
|
{
|
|
if (g_FrData.targets[targetnum].inuse) {
|
|
s32 index = FRSCRIPTINDEX_TARGETS + g_FrData.targets[targetnum].scriptindex;
|
|
u8 *script = &g_FrRomData[g_FrScriptOffsets[index]];
|
|
s32 offset = g_FrData.targets[targetnum].scriptoffset;
|
|
struct pad pad;
|
|
s32 frpadnum;
|
|
|
|
switch (script[offset]) {
|
|
case FRCMD_END:
|
|
g_FrData.targets[targetnum].scriptenabled = true;
|
|
g_FrData.targets[targetnum].scriptsleep = 255 * 60;
|
|
return true;
|
|
case FRCMD_GOTOPAD:
|
|
frpadnum = frResolveFrPad(script[offset + 1]);
|
|
|
|
if (frpadnum == g_FrData.targets[targetnum].frpadnum) {
|
|
g_FrData.targets[targetnum].scriptoffset += 4;
|
|
return false;
|
|
}
|
|
|
|
g_FrData.targets[targetnum].frpadnum = frpadnum;
|
|
|
|
padUnpack(g_FrPads[frpadnum], PADFIELD_POS, &pad);
|
|
|
|
g_FrData.targets[targetnum].dstpos.x = pad.pos.x;
|
|
g_FrData.targets[targetnum].dstpos.y = pad.pos.y;
|
|
g_FrData.targets[targetnum].dstpos.z = pad.pos.z;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
g_FrData.targets[targetnum].dstpos.z += 6.0f * targetnum;
|
|
#endif
|
|
|
|
if (script[offset + 2] == 0xff) {
|
|
g_FrData.targets[targetnum].travelspeed = -1;
|
|
g_FrData.targets[targetnum].travelling = true;
|
|
} else {
|
|
if (g_FrNumSounds < 3) {
|
|
g_FrNumSounds++;
|
|
propsnd0f0939f8(NULL, g_FrData.targets[targetnum].prop, SFX_FR_CONVEYER, -1,
|
|
-1, 0, 0, 0, 0, -1, 0, -1, -1, -1, -1);
|
|
}
|
|
|
|
g_FrData.targets[targetnum].travelspeed = script[offset + 2] / 60.0f * g_FrData.speed;
|
|
g_FrData.targets[targetnum].travelling = true;
|
|
}
|
|
|
|
g_FrData.targets[targetnum].scriptsleep = script[offset + 3] * TICKS(60);
|
|
g_FrData.targets[targetnum].donestopsound = false;
|
|
g_FrData.targets[targetnum].scriptoffset += 4;
|
|
return true;
|
|
case FRCMD_RESTART:
|
|
g_FrData.targets[targetnum].scriptoffset = 0;
|
|
return true;
|
|
case FRCMD_WAITSECONDS:
|
|
g_FrData.targets[targetnum].scriptenabled = true;
|
|
g_FrData.targets[targetnum].scriptsleep = script[offset + 1] * TICKS(60);
|
|
g_FrData.targets[targetnum].scriptoffset += 2;
|
|
return true;
|
|
case FRCMD_ROTATE:
|
|
if (g_FrData.targets[targetnum].rotateoncloak == false) {
|
|
f32 angles[4];
|
|
angles[0] = DEG2RAD(-90);
|
|
angles[1] = DEG2RAD(-180);
|
|
angles[2] = DEG2RAD(90);
|
|
angles[3] = DEG2RAD(180);
|
|
|
|
g_FrData.targets[targetnum].rotatetoangle = g_FrData.targets[targetnum].angle + angles[script[offset + 1]];
|
|
g_FrData.targets[targetnum].rotatespeed = angles[script[offset + 1]] / (script[offset + 2] * 15);
|
|
g_FrData.targets[targetnum].rotating = true;
|
|
g_FrData.targets[targetnum].scriptenabled = false;
|
|
}
|
|
|
|
if (1);
|
|
g_FrData.targets[targetnum].scriptoffset += 3;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void frHideAllTargets(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
struct prop *prop = g_FrData.targets[i].prop;
|
|
struct defaultobj *target = prop->obj;
|
|
|
|
target->flags2 |= OBJFLAG2_INVISIBLE;
|
|
|
|
func0f0926bc(prop, 1, 0xffff);
|
|
}
|
|
}
|
|
|
|
void frInitTargets(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
struct prop *prop;
|
|
struct defaultobj *obj;
|
|
struct pad pad;
|
|
struct coord pos;
|
|
Mtxf sp144;
|
|
f32 sp108[3][3];
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
prop = g_FrData.targets[i].prop;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (prop)
|
|
#endif
|
|
{
|
|
obj = prop->obj;
|
|
|
|
objFree(obj, false, true);
|
|
|
|
obj->damage = 0;
|
|
prop->timetoregen = 0;
|
|
|
|
if (g_FrData.targets[i].inuse) {
|
|
g_FrData.targets[i].scriptenabled = false;
|
|
g_FrData.targets[i].destroyed = false;
|
|
|
|
if (count < g_FrData.maxactivetargets) {
|
|
obj->flags2 &= ~OBJFLAG2_INVISIBLE;
|
|
g_FrData.targets[i].active = true;
|
|
} else {
|
|
obj->flags2 |= OBJFLAG2_INVISIBLE;
|
|
g_FrData.targets[i].active = false;
|
|
}
|
|
|
|
padUnpack(g_FrPads[g_FrData.targets[i].frpadindex], PADFIELD_POS, &pad);
|
|
|
|
pos.f[0] = pad.pos.f[0];
|
|
pos.f[1] = pad.pos.f[1];
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
pos.f[2] = pad.pos.f[2] + 6.0f * i;
|
|
#else
|
|
pos.f[2] = pad.pos.f[2];
|
|
#endif
|
|
|
|
frExecuteTargetScript(i);
|
|
|
|
if (g_FrData.targets[i].travelspeed == -1) {
|
|
pos.x = g_FrData.targets[i].dstpos.x;
|
|
pos.y = g_FrData.targets[i].dstpos.y;
|
|
pos.z = g_FrData.targets[i].dstpos.z;
|
|
}
|
|
|
|
count++;
|
|
} else {
|
|
obj->flags2 |= OBJFLAG2_INVISIBLE;
|
|
}
|
|
|
|
if (obj->flags2 & OBJFLAG2_INVISIBLE) {
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
padUnpack(g_FrPads[g_FrData.targets[i].frpadindex], PADFIELD_POS, &pad);
|
|
|
|
pos.x = 0.0f;
|
|
pos.y = 5000.0f;
|
|
pos.z = 0.0f;
|
|
#else
|
|
pos.x = 0.0f;
|
|
pos.y = 5000.0f;
|
|
pos.z = 6.0f * i;
|
|
#endif
|
|
}
|
|
|
|
if (g_FrData.targets[i].flags & FRTARGETFLAG_SPAWNFACINGAWAY) {
|
|
mtx4LoadYRotation(0.0f, &sp144);
|
|
g_FrData.targets[i].angle = M_PI;
|
|
} else {
|
|
mtx4LoadYRotation(M_PI, &sp144);
|
|
}
|
|
|
|
mtx00015f04(obj->model->scale, &sp144);
|
|
mtx4ToMtx3(&sp144, sp108);
|
|
mtx3Copy(sp108, obj->realrot);
|
|
|
|
prop->pos.x = pos.x;
|
|
prop->pos.y = pos.y;
|
|
prop->pos.z = pos.z;
|
|
|
|
func0f069c70(obj, true, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void frCloseAndLockDoor(void)
|
|
{
|
|
struct defaultobj *obj = objFindByTagId(0x91);
|
|
|
|
if (obj && obj->prop && obj->prop->type == PROPTYPE_DOOR) {
|
|
struct doorobj *door = (struct doorobj *)obj;
|
|
door->keyflags |= 0x40;
|
|
doorsRequestMode(door, DOORMODE_CLOSING);
|
|
}
|
|
}
|
|
|
|
void frUnlockDoor(void)
|
|
{
|
|
struct defaultobj *obj = objFindByTagId(0x91);
|
|
|
|
if (obj && obj->prop && obj->prop->type == PROPTYPE_DOOR) {
|
|
struct doorobj *door = (struct doorobj *)obj;
|
|
door->keyflags &= ~0x40;
|
|
}
|
|
}
|
|
|
|
void frLoadData(void)
|
|
{
|
|
if (!g_FrDataLoaded) {
|
|
s32 len = (s32)&_firingrangeSegmentRomEnd - (s32)&_firingrangeSegmentRomStart;
|
|
s32 index = 0;
|
|
u32 i;
|
|
u32 numscripts = 1;
|
|
s32 size;
|
|
|
|
if (index);
|
|
|
|
g_FrDataLoaded = true;
|
|
|
|
frLoadRomData(len);
|
|
|
|
for (i = 0x12; i < len; i++) {
|
|
if (g_FrRomData[i] == FRCMD_START) {
|
|
numscripts++;
|
|
}
|
|
}
|
|
|
|
size = numscripts * sizeof(*g_FrScriptOffsets);
|
|
g_FrScriptOffsets = mempAlloc(ALIGN16(size), MEMPOOL_STAGE);
|
|
|
|
if (numscripts < 0);
|
|
|
|
if (g_FrScriptOffsets) {
|
|
for (i = 0x12; i < len; i++) {
|
|
if (g_FrRomData[i] == FRCMD_START) {
|
|
g_FrScriptOffsets[index] = i + 1;
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
frSetTargetProps();
|
|
|
|
g_FrData.slot = 0;
|
|
g_FrData.difficulty = FRDIFFICULTY_BRONZE;
|
|
g_FrData.donelighting = false;
|
|
}
|
|
}
|
|
|
|
u32 frInitAmmo(s32 weaponnum)
|
|
{
|
|
u32 scriptindex;
|
|
u32 ammotype = bgunGetAmmoTypeForWeapon(weaponnum, 0);
|
|
u32 capacity = bgunGetCapacityByAmmotype(ammotype);
|
|
|
|
frInitDefaults();
|
|
scriptindex = frGetWeaponScriptIndex(weaponnum);
|
|
frExecuteWeaponScript(scriptindex);
|
|
|
|
if (g_FrData.ammolimit == 255) {
|
|
bgunSetAmmoQuantity(ammotype, capacity);
|
|
} else {
|
|
bgunSetAmmoQuantity(ammotype, g_FrData.ammolimit);
|
|
}
|
|
|
|
if (weaponnum == WEAPON_SUPERDRAGON) {
|
|
if (g_FrData.sdgrenadelimit == 255) {
|
|
bgunSetAmmoQuantity(AMMOTYPE_DEVASTATOR, capacity);
|
|
} else {
|
|
bgunSetAmmoQuantity(AMMOTYPE_DEVASTATOR, g_FrData.sdgrenadelimit);
|
|
}
|
|
}
|
|
|
|
return scriptindex;
|
|
}
|
|
|
|
void frBeginSession(s32 weapon)
|
|
{
|
|
s32 i;
|
|
struct defaultobj *obj = objFindByTagId(0x7f); // computer
|
|
|
|
if (obj) {
|
|
obj->flags |= OBJFLAG_CANNOT_ACTIVATE;
|
|
}
|
|
|
|
frCloseAndLockDoor();
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
if (g_Vars.currentplayer->gunctrl.ammotypes[i] >= 0) {
|
|
g_Vars.currentplayer->hands[0].loadedammo[i] = 0;
|
|
g_Vars.currentplayer->hands[1].loadedammo[i] = 0;
|
|
}
|
|
}
|
|
|
|
g_FrIsValidWeapon = frInitAmmo(weapon) == 0 ? false : true;
|
|
frInitTargets();
|
|
bgunSetPassiveMode(false);
|
|
}
|
|
|
|
char *frGetWeaponDescription(void)
|
|
{
|
|
u32 weapon = frGetWeaponBySlot(g_FrData.slot);
|
|
|
|
switch (weapon) {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
case WEAPON_FALCON2: return langGet(L_DISH_283);
|
|
case WEAPON_FALCON2_SCOPE: return langGet(L_DISH_284);
|
|
case WEAPON_FALCON2_SILENCER: return langGet(L_DISH_285);
|
|
case WEAPON_MAGSEC4: return langGet(L_DISH_286);
|
|
case WEAPON_MAULER: return langGet(L_DISH_287);
|
|
case WEAPON_PHOENIX: return langGet(L_DISH_288);
|
|
case WEAPON_DY357MAGNUM: return langGet(L_DISH_289);
|
|
case WEAPON_DY357LX: return langGet(L_DISH_290);
|
|
case WEAPON_CMP150: return langGet(L_DISH_291);
|
|
case WEAPON_CYCLONE: return langGet(L_DISH_292);
|
|
case WEAPON_CALLISTO: return langGet(L_DISH_293);
|
|
case WEAPON_RCP120: return langGet(L_DISH_294);
|
|
case WEAPON_LAPTOPGUN: return langGet(L_DISH_295);
|
|
case WEAPON_DRAGON: return langGet(L_DISH_296);
|
|
case WEAPON_K7AVENGER: return langGet(L_DISH_297);
|
|
case WEAPON_AR34: return langGet(L_DISH_298);
|
|
case WEAPON_SUPERDRAGON: return langGet(L_DISH_299);
|
|
case WEAPON_SHOTGUN: return langGet(L_DISH_300);
|
|
case WEAPON_SNIPERRIFLE: return langGet(L_DISH_301);
|
|
case WEAPON_FARSIGHT: return langGet(L_DISH_302);
|
|
case WEAPON_CROSSBOW: return langGet(L_DISH_303);
|
|
case WEAPON_TRANQUILIZER: return langGet(L_DISH_304);
|
|
case WEAPON_REAPER: return langGet(L_DISH_305);
|
|
case WEAPON_DEVASTATOR: return langGet(L_DISH_306);
|
|
case WEAPON_ROCKETLAUNCHER: return langGet(L_DISH_307);
|
|
case WEAPON_SLAYER: return langGet(L_DISH_308);
|
|
case WEAPON_COMBATKNIFE: return langGet(L_DISH_309);
|
|
case WEAPON_LASER: return langGet(L_DISH_310);
|
|
case WEAPON_GRENADE: return langGet(L_DISH_311);
|
|
case WEAPON_NBOMB: return langGet(L_DISH_312);
|
|
case WEAPON_TIMEDMINE: return langGet(L_DISH_313);
|
|
case WEAPON_PROXIMITYMINE: return langGet(L_DISH_314);
|
|
case WEAPON_REMOTEMINE: return langGet(L_DISH_315);
|
|
#else
|
|
case WEAPON_FALCON2: return langGet(L_MISC_377);
|
|
case WEAPON_FALCON2_SCOPE: return langGet(L_MISC_378);
|
|
case WEAPON_FALCON2_SILENCER: return langGet(L_MISC_379);
|
|
case WEAPON_MAGSEC4: return langGet(L_MISC_380);
|
|
case WEAPON_MAULER: return langGet(L_MISC_381);
|
|
case WEAPON_PHOENIX: return langGet(L_MISC_382);
|
|
case WEAPON_DY357MAGNUM: return langGet(L_MISC_383);
|
|
case WEAPON_DY357LX: return langGet(L_MISC_384);
|
|
case WEAPON_CMP150: return langGet(L_MISC_385);
|
|
case WEAPON_CYCLONE: return langGet(L_MISC_386);
|
|
case WEAPON_CALLISTO: return langGet(L_MISC_387);
|
|
case WEAPON_RCP120: return langGet(L_MISC_388);
|
|
case WEAPON_LAPTOPGUN: return langGet(L_MISC_389);
|
|
case WEAPON_DRAGON: return langGet(L_MISC_390);
|
|
case WEAPON_K7AVENGER: return langGet(L_MISC_391);
|
|
case WEAPON_AR34: return langGet(L_MISC_392);
|
|
case WEAPON_SUPERDRAGON: return langGet(L_MISC_393);
|
|
case WEAPON_SHOTGUN: return langGet(L_MISC_394);
|
|
case WEAPON_SNIPERRIFLE: return langGet(L_MISC_395);
|
|
case WEAPON_FARSIGHT: return langGet(L_MISC_396);
|
|
case WEAPON_CROSSBOW: return langGet(L_MISC_397);
|
|
case WEAPON_TRANQUILIZER: return langGet(L_MISC_398);
|
|
case WEAPON_REAPER: return langGet(L_MISC_399);
|
|
case WEAPON_DEVASTATOR: return langGet(L_MISC_400);
|
|
case WEAPON_ROCKETLAUNCHER: return langGet(L_MISC_401);
|
|
case WEAPON_SLAYER: return langGet(L_MISC_402);
|
|
case WEAPON_COMBATKNIFE: return langGet(L_MISC_403);
|
|
case WEAPON_LASER: return langGet(L_MISC_404);
|
|
case WEAPON_GRENADE: return langGet(L_MISC_405);
|
|
case WEAPON_NBOMB: return langGet(L_MISC_406);
|
|
case WEAPON_TIMEDMINE: return langGet(L_MISC_407);
|
|
case WEAPON_PROXIMITYMINE: return langGet(L_MISC_408);
|
|
case WEAPON_REMOTEMINE: return langGet(L_MISC_409);
|
|
#endif
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void frEndSession(bool hidetargets)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
s16 propnums[256];
|
|
s16 *propnumptr;
|
|
s16 rooms[20];
|
|
u32 stack1;
|
|
s16 rooms2[10];
|
|
u32 stack2;
|
|
|
|
if (g_FrDataLoaded) {
|
|
struct defaultobj *terminal = objFindByTagId(0x7f);
|
|
|
|
if (terminal) {
|
|
terminal->flags &= ~OBJFLAG_CANNOT_ACTIVATE;
|
|
}
|
|
|
|
frUnlockDoor();
|
|
|
|
if (g_Vars.currentplayer->visionmode == VISIONMODE_SLAYERROCKET) {
|
|
g_Vars.currentplayer->visionmode = VISIONMODE_NORMAL;
|
|
}
|
|
|
|
bgunSetPassiveMode(true);
|
|
|
|
g_FrIsValidWeapon = 0;
|
|
|
|
frRestoreLighting();
|
|
|
|
if (hidetargets) {
|
|
frHideAllTargets();
|
|
}
|
|
|
|
if (g_ThrownLaptops[0].base.prop) {
|
|
objFreePermanently(&g_ThrownLaptops[0].base, true);
|
|
}
|
|
|
|
roomsCopy(g_Vars.currentplayer->prop->rooms, rooms);
|
|
|
|
for (i = 0; g_Vars.currentplayer->prop->rooms[i] != -1; i++) {
|
|
roomGetNeighbours(g_Vars.currentplayer->prop->rooms[i], rooms2, 10);
|
|
roomsAppend(rooms2, rooms, 20);
|
|
}
|
|
|
|
// Remove projectiles and throwables
|
|
roomGetProps(rooms, propnums, 256);
|
|
|
|
propnumptr = propnums;
|
|
|
|
while (*propnumptr >= 0) {
|
|
struct prop *prop = &g_Vars.props[*propnumptr];
|
|
|
|
if (prop) {
|
|
struct defaultobj *obj = prop->obj;
|
|
|
|
if (prop->type == PROPTYPE_WEAPON) {
|
|
if (obj->type == OBJTYPE_AUTOGUN) {
|
|
objFreePermanently(obj, true);
|
|
}
|
|
|
|
if (obj->type == OBJTYPE_WEAPON) {
|
|
struct weaponobj *weapon = (struct weaponobj *)obj;
|
|
|
|
if (weapon->weaponnum == WEAPON_NBOMB
|
|
|| weapon->weaponnum == WEAPON_BOLT
|
|
|| weapon->weaponnum == WEAPON_COMBATKNIFE
|
|
|| weapon->weaponnum == WEAPON_HOMINGROCKET
|
|
|| weapon->weaponnum == WEAPON_GRENADE
|
|
|| weapon->weaponnum == WEAPON_GRENADEROUND
|
|
|| weapon->weaponnum == WEAPON_PROXIMITYMINE
|
|
|| weapon->weaponnum == WEAPON_REMOTEMINE
|
|
|| weapon->weaponnum == WEAPON_ROCKET
|
|
|| weapon->weaponnum == WEAPON_TIMEDMINE
|
|
|| weapon->weaponnum == WEAPON_SKROCKET
|
|
|| (weapon->weaponnum == WEAPON_DRAGON && weapon->gunfunc == FUNC_SECONDARY)
|
|
|| (weapon->weaponnum == WEAPON_LAPTOPGUN && weapon->gunfunc == FUNC_SECONDARY)) {
|
|
objFreePermanently(obj, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
propnumptr++;
|
|
}
|
|
|
|
// Remove explosions
|
|
for (i = 0; i < g_MaxExplosions; i++) {
|
|
g_Explosions[i].age = 256;
|
|
|
|
for (j = 0; j < 40; j++) {
|
|
g_Explosions[i].parts[j].frame = 0;
|
|
}
|
|
}
|
|
|
|
// Remove smoke
|
|
for (i = 0; i < g_MaxSmokes; i++) {
|
|
g_Smokes[i].age = 256;
|
|
|
|
for (j = 0; j < 10; j++) {
|
|
g_Smokes[i].parts[j].size = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
playerDisplayHealth();
|
|
|
|
g_Vars.currentplayer->bondhealth = 1;
|
|
}
|
|
|
|
bool frWasTooInaccurate(void)
|
|
{
|
|
f32 sum = (g_FrData.numhitsring3 +
|
|
+ g_FrData.numhitsbullseye
|
|
+ g_FrData.numhitsring1
|
|
+ g_FrData.numhitsring2) * 100.0f;
|
|
|
|
if (g_FrData.numshots) {
|
|
f32 accuracy = sum / g_FrData.numshots;
|
|
|
|
if (accuracy < g_FrData.goalaccuracy) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void frSetFailReason(s32 failreason)
|
|
{
|
|
frEndSession(false);
|
|
|
|
g_FrData.failreason = frWasTooInaccurate() ? FRFAILREASON_INACCURATE : failreason;
|
|
g_FrData.menutype = FRMENUTYPE_FAILED;
|
|
g_FrData.menucountdown = TICKS(60);
|
|
}
|
|
|
|
void frSetCompleted(void)
|
|
{
|
|
frEndSession(false);
|
|
|
|
if (frWasTooInaccurate()) {
|
|
g_FrData.failreason = FRFAILREASON_INACCURATE;
|
|
g_FrData.menutype = FRMENUTYPE_FAILED;
|
|
} else {
|
|
u32 frweaponindex = frGetWeaponIndexByWeapon(frGetWeaponBySlot(g_FrData.slot));
|
|
frSaveScoreIfBest(frweaponindex, g_FrData.difficulty + 1);
|
|
g_FrData.menutype = FRMENUTYPE_COMPLETED;
|
|
}
|
|
|
|
g_FrData.menucountdown = TICKS(60);
|
|
}
|
|
|
|
bool frIsTargetOneHitExplodable(struct prop *prop)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse
|
|
&& g_FrData.targets[i].destroyed == false
|
|
&& g_FrData.targets[i].active
|
|
&& prop == g_FrData.targets[i].prop) {
|
|
if (g_FrData.targets[i].flags & FRTARGETFLAG_ONEHITEXPLODE) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
f32 frGetTargetAngleToPos(struct coord *targetpos, f32 targetangle, struct coord *pos)
|
|
{
|
|
f32 xdiff = targetpos->x - pos->x;
|
|
f32 zdiff = targetpos->z - pos->z;
|
|
f32 directangle = atan2f(xdiff, zdiff);
|
|
f32 relativeangle = directangle - targetangle;
|
|
|
|
if (directangle < targetangle) {
|
|
relativeangle += M_BADTAU;
|
|
}
|
|
|
|
return relativeangle;
|
|
}
|
|
|
|
bool frIsTargetFacingPos(struct prop *prop, struct coord *pos)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (prop == g_FrData.targets[i].prop) {
|
|
f32 angle;
|
|
|
|
if (g_FrData.targets[i].destroyed) {
|
|
return false;
|
|
}
|
|
|
|
angle = frGetTargetAngleToPos(&prop->pos, g_FrData.targets[i].angle, pos);
|
|
|
|
//if (angle > DEG2RAD(90) && angle < DEG2RAD(270)) {
|
|
if (angle > 1.5707963705063f && angle < 4.7116389274597f) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
struct prop *frChooseAutogunTarget(struct coord *autogunpos)
|
|
{
|
|
f32 closestdist = 0x20000000;
|
|
s32 facingtargets[ARRAYCOUNT(g_FrData.targets)];
|
|
s32 len = 0;
|
|
struct prop *closesttarget = NULL;
|
|
s32 i;
|
|
|
|
// Make list of targets which are facing the laptop gun
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse
|
|
&& g_FrData.targets[i].destroyed == false
|
|
&& g_FrData.targets[i].active) {
|
|
f32 angle = frGetTargetAngleToPos(&g_FrData.targets[i].prop->pos, g_FrData.targets[i].angle, autogunpos);
|
|
|
|
if (!(angle > 1.5707963705063f && angle < 4.7116389274597f)) {
|
|
facingtargets[len++] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine which of the facing targets is closest
|
|
for (i = 0; i < len; i++) {
|
|
struct prop *prop = g_FrData.targets[facingtargets[i]].prop;
|
|
f32 xdiff = prop->pos.f[0] - autogunpos->f[0];
|
|
f32 ydiff = prop->pos.f[1] - autogunpos->f[1];
|
|
f32 zdiff = prop->pos.f[2] - autogunpos->f[2];
|
|
f32 dist = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff;
|
|
|
|
if (dist < closestdist) {
|
|
closestdist = dist;
|
|
closesttarget = prop;
|
|
}
|
|
}
|
|
|
|
if (facingtargets);
|
|
|
|
return closesttarget;
|
|
}
|
|
|
|
bool frIsAmmoWasted(void)
|
|
{
|
|
s32 weaponnum = frGetWeaponBySlot(g_FrData.slot);
|
|
s32 i;
|
|
s32 priammotype = bgunGetAmmoTypeForWeapon(weaponnum, 0);
|
|
s32 secammotype = bgunGetAmmoTypeForWeapon(weaponnum, 1);
|
|
struct hand *hand0 = &g_Vars.currentplayer->hands[0];
|
|
struct hand *hand1 = &g_Vars.currentplayer->hands[1];
|
|
s32 ammoloaded[2];
|
|
s32 ammototal[2];
|
|
s16 *propnumptr;
|
|
s16 propnums[258];
|
|
s16 rooms20[22];
|
|
s16 rooms10[12];
|
|
u32 stack[4];
|
|
s32 ammotype;
|
|
struct hand *hand;
|
|
struct prop *prop;
|
|
struct prop *child;
|
|
|
|
// Laser has unlimited ammo
|
|
if (weaponnum == WEAPON_LASER) {
|
|
return false;
|
|
}
|
|
|
|
// Check if player has ammo
|
|
ammoloaded[0] = hand0->loadedammo[0] + hand1->loadedammo[0];
|
|
ammoloaded[1] = hand0->loadedammo[1] + hand1->loadedammo[1];
|
|
ammototal[0] = bgunGetReservedAmmoCount(priammotype) + ammoloaded[0];
|
|
ammototal[1] = bgunGetReservedAmmoCount(secammotype) + ammoloaded[1];
|
|
|
|
if (ammototal[0] <= 0 && ammototal[1] <= 0) {
|
|
// Don't do any further checks if this is the first frame where we've
|
|
// gotten this far. I'm guessing this fixes a frame perfect bug, however
|
|
// testing with this check removed doesn't cause any unusual behaviour.
|
|
if (g_FrData.ammohasgrace) {
|
|
g_FrData.ammohasgrace = false;
|
|
return false;
|
|
}
|
|
|
|
// Check if there are any explosions
|
|
for (i = 0; i != 6; i++) {
|
|
if (g_Explosions[i].prop) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check projectiles
|
|
if (weaponnum == WEAPON_ROCKETLAUNCHER
|
|
|| weaponnum == WEAPON_SLAYER
|
|
|| weaponnum == WEAPON_DEVASTATOR
|
|
|| weaponnum == WEAPON_SUPERDRAGON
|
|
|| weaponnum == WEAPON_COMBATKNIFE
|
|
|| weaponnum == WEAPON_CROSSBOW
|
|
|| weaponnum == WEAPON_GRENADE
|
|
|| weaponnum == WEAPON_NBOMB
|
|
|| weaponnum == WEAPON_TIMEDMINE
|
|
|| weaponnum == WEAPON_PROXIMITYMINE
|
|
|| weaponnum == WEAPON_REMOTEMINE) {
|
|
roomsCopy(g_Vars.currentplayer->prop->rooms, rooms20);
|
|
|
|
for (i = 0; g_Vars.currentplayer->prop->rooms[i] != -1; i++) {
|
|
roomGetNeighbours(g_Vars.currentplayer->prop->rooms[i], rooms10, 10);
|
|
roomsAppend(rooms10, rooms20, 20);
|
|
}
|
|
|
|
roomGetProps(rooms20, propnums, 256);
|
|
propnumptr = propnums;
|
|
|
|
while (*propnumptr >= 0) {
|
|
prop = &g_Vars.props[*propnumptr];
|
|
child = prop->child;
|
|
|
|
if ((child && child->type == PROPTYPE_WEAPON && child->weapon->weaponnum == WEAPON_TIMEDMINE)
|
|
|| (child && child->type == PROPTYPE_WEAPON && child->weapon->weaponnum == WEAPON_REMOTEMINE)
|
|
|| (child && child->type == PROPTYPE_WEAPON && child->weapon->weaponnum == WEAPON_PROXIMITYMINE)
|
|
|| (child && child->type == PROPTYPE_WEAPON && child->weapon->weaponnum == WEAPON_GRENADEROUND)) {
|
|
return false;
|
|
}
|
|
|
|
if (prop->type == PROPTYPE_WEAPON) {
|
|
if (prop->weapon->weaponnum == WEAPON_ROCKET
|
|
|| prop->weapon->weaponnum == WEAPON_HOMINGROCKET
|
|
|| prop->weapon->weaponnum == WEAPON_GRENADE
|
|
|| prop->weapon->weaponnum == WEAPON_GRENADEROUND) {
|
|
return false;
|
|
}
|
|
|
|
if (prop->weapon->weaponnum == WEAPON_BOLT
|
|
|| prop->weapon->weaponnum == WEAPON_COMBATKNIFE) {
|
|
if (prop->obj->hidden & OBJHFLAG_AIRBORNE) {
|
|
return false;
|
|
}
|
|
} else if (prop->weapon->weaponnum == WEAPON_TIMEDMINE
|
|
|| prop->weapon->weaponnum == WEAPON_REMOTEMINE) {
|
|
return false;
|
|
} else if (prop->weapon->weaponnum == WEAPON_PROXIMITYMINE) {
|
|
if (g_FrData.proxyendtimer == -255) {
|
|
return false;
|
|
}
|
|
|
|
if (g_FrData.proxyendtimer == 0) {
|
|
// Initial state - set the timer to 5 seconds if player is now out of mines
|
|
ammotype = bgunGetAmmoTypeForWeapon(weaponnum, 0);
|
|
hand = &g_Vars.currentplayer->hands[HAND_RIGHT];
|
|
|
|
if (bgunGetReservedAmmoCount(ammotype) + hand->loadedammo[0] == 0) {
|
|
g_FrData.proxyendtimer = TICKS(300);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
g_FrData.proxyendtimer -= g_Vars.lvupdate240_60;
|
|
|
|
if (g_FrData.proxyendtimer <= 0) {
|
|
// Timer has just hit zero - remove all proxy items
|
|
for (i = 0; i < ARRAYCOUNT(g_Proxies); i++) {
|
|
if (g_Proxies[i]) {
|
|
g_Proxies[i]->timer240 = 0;
|
|
}
|
|
}
|
|
|
|
g_FrData.proxyendtimer = -255;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
propnumptr++;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void frTick(void)
|
|
{
|
|
s32 ammotype;
|
|
s32 capacity;
|
|
s32 weaponnum;
|
|
struct coord diff;
|
|
struct coord newpos;
|
|
u8 weaponnum2;
|
|
struct prop *prop;
|
|
struct defaultobj *obj;
|
|
struct defaultobj *obj2;
|
|
s32 invincible;
|
|
s32 i;
|
|
s32 j;
|
|
f32 dist;
|
|
u32 stack;
|
|
struct inventory_ammo *ammo;
|
|
u8 exploding;
|
|
bool oldside;
|
|
struct modelrodata_bbox *bbox;
|
|
s32 tmp;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
f32 mult;
|
|
#endif
|
|
bool newside;
|
|
struct chrdata *chr;
|
|
bool cloaked;
|
|
f32 toangle;
|
|
f32 speed;
|
|
Mtxf spbc;
|
|
f32 sp98[3][3];
|
|
|
|
if (g_FrIsValidWeapon
|
|
&& g_Vars.currentplayer->gunctrl.unk1583_04 == 0
|
|
&& invHasSingleWeaponIncAllGuns(frGetWeaponBySlot(g_FrData.slot))) {
|
|
bgunEquipWeapon(frGetWeaponBySlot(g_FrData.slot));
|
|
}
|
|
|
|
// NTSC beta does the room code then menu code,
|
|
// while everything else does the menu code then room code
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
// End the session if the player slipped through the door before it closed
|
|
if (g_Vars.currentplayer->prop->rooms[0] != ROOM_DISH_FIRINGRANGE) {
|
|
if (g_FrIsValidWeapon) {
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse
|
|
&& g_FrData.targets[i].destroyed == false
|
|
&& g_FrData.targets[i].silent == false
|
|
&& g_FrData.targets[i].travelling) {
|
|
g_FrData.targets[i].silent = true;
|
|
func0f0926bc(g_FrData.targets[i].prop, 1, 0xffff);
|
|
}
|
|
}
|
|
|
|
g_Vars.currentplayer->training = false;
|
|
frEndSession(true);
|
|
g_FrData.menucountdown = 0; // This assignment is in NTSC beta only
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_IN_TRAINING);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Handle the menu countdown
|
|
if (g_FrData.menucountdown != 0) {
|
|
g_FrData.menucountdown -= g_Vars.lvupdate240_60;
|
|
|
|
// Prevent showing the menu until gun is put away
|
|
if (g_FrData.menucountdown <= 0) {
|
|
if ((g_FrData.menutype == FRMENUTYPE_FAILED || g_FrData.menutype == FRMENUTYPE_COMPLETED)
|
|
&& g_Vars.currentplayer->hands[HAND_RIGHT].gset.weaponnum != WEAPON_UNARMED) {
|
|
g_FrData.menucountdown = 1;
|
|
}
|
|
}
|
|
|
|
if (g_FrData.menucountdown <= 0) {
|
|
g_FrData.menucountdown = 0;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].prop) {
|
|
func0f0926bc(g_FrData.targets[i].prop, 1, 0xffff);
|
|
}
|
|
}
|
|
|
|
switch (g_FrData.menutype) {
|
|
case FRMENUTYPE_WEAPONLIST:
|
|
func0f0f85e0(ciGetFrWeaponListMenuDialog(), MENUROOT_TRAINING);
|
|
break;
|
|
case FRMENUTYPE_DETAILS:
|
|
func0f0f85e0(&g_FrTrainingInfoPreGameMenuDialog, MENUROOT_TRAINING);
|
|
break;
|
|
case FRMENUTYPE_FAILED:
|
|
sndStart(var80095200, SFX_TRAINING_FAIL, NULL, -1, -1, -1, -1, -1);
|
|
func0f0f85e0(&g_FrFailedMenuDialog, MENUROOT_TRAINING);
|
|
break;
|
|
case FRMENUTYPE_COMPLETED:
|
|
sndStart(var80095200, SFX_TRAINING_COMPLETE, NULL, -1, -1, -1, -1, -1);
|
|
func0f0f85e0(&g_FrCompletedMenuDialog, MENUROOT_TRAINING);
|
|
filemgrSaveOrLoad(&g_GameFileGuid, FILEOP_SAVE_GAME_000, 0);
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
// End the session if the player slipped through the door before it closed
|
|
if (g_Vars.currentplayer->prop->rooms[0] != ROOM_DISH_FIRINGRANGE) {
|
|
if (g_FrIsValidWeapon) {
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse
|
|
&& g_FrData.targets[i].destroyed == false
|
|
&& g_FrData.targets[i].silent == false
|
|
&& g_FrData.targets[i].travelling) {
|
|
g_FrData.targets[i].silent = true;
|
|
func0f0926bc(g_FrData.targets[i].prop, 1, 0xffff);
|
|
}
|
|
}
|
|
|
|
g_Vars.currentplayer->training = false;
|
|
frEndSession(true);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_IN_TRAINING);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (!g_FrIsValidWeapon) {
|
|
return;
|
|
}
|
|
|
|
if (g_Vars.currentplayer->isdead) {
|
|
frEndSession(false);
|
|
}
|
|
|
|
// If paused, stop any target sounds
|
|
if (g_Vars.lvupdate240 == 0) {
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse
|
|
&& g_FrData.targets[i].destroyed == false
|
|
&& g_FrData.targets[i].silent == false
|
|
&& g_FrData.targets[i].travelling) {
|
|
g_FrData.targets[i].silent = true;
|
|
func0f0926bc(g_FrData.targets[i].prop, 1, 0xffff);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
g_Vars.currentplayer->training = true;
|
|
frExecuteHelpScript();
|
|
|
|
// Top up the player's ammo if the config defined more ammo than the
|
|
// weapon allows, or if it defined unlimited ammo
|
|
if (g_FrData.numshotssincetopup != 0) {
|
|
weaponnum = frGetWeaponBySlot(g_FrData.slot);
|
|
ammotype = bgunGetAmmoTypeForWeapon(weaponnum, 0);
|
|
capacity = bgunGetCapacityByAmmotype(ammotype);
|
|
ammo = weaponGetAmmoByFunction(weaponnum, 0);
|
|
capacity -= (ammo ? ammo->clipsize : 0);
|
|
|
|
if (g_FrData.ammoextra > 0) {
|
|
tmp = bgunGetReservedAmmoCount(ammotype);
|
|
g_FrData.ammoextra -= g_FrData.numshotssincetopup;
|
|
|
|
if (g_FrData.ammoextra < 0) {
|
|
g_FrData.ammoextra = 0;
|
|
}
|
|
|
|
capacity = tmp + g_FrData.numshotssincetopup;
|
|
bgunSetAmmoQuantity(ammotype, capacity);
|
|
} else if (g_FrData.ammoextra == -1) {
|
|
bgunSetAmmoQuantity(ammotype, capacity);
|
|
}
|
|
|
|
if (weaponnum == WEAPON_SUPERDRAGON) {
|
|
capacity = bgunGetCapacityByAmmotype(AMMOTYPE_DEVASTATOR);
|
|
|
|
if (g_FrData.sdgrenadeextra > 0) {
|
|
tmp = bgunGetReservedAmmoCount(AMMOTYPE_DEVASTATOR);
|
|
g_FrData.sdgrenadeextra -= g_FrData.numshotssincetopup;
|
|
|
|
if (g_FrData.sdgrenadeextra < 0) {
|
|
g_FrData.sdgrenadeextra = 0;
|
|
}
|
|
|
|
capacity = tmp + g_FrData.numshotssincetopup;
|
|
bgunSetAmmoQuantity(AMMOTYPE_DEVASTATOR, capacity);
|
|
} else if (g_FrData.sdgrenadeextra == -1) {
|
|
bgunSetAmmoQuantity(AMMOTYPE_DEVASTATOR, capacity);
|
|
}
|
|
}
|
|
|
|
g_FrData.numshotssincetopup = 0;
|
|
}
|
|
|
|
g_FrData.timetaken += g_Vars.lvupdate240_60;
|
|
|
|
// Handle prestart
|
|
if (g_FrData.timetaken < 0) {
|
|
if (g_FrData.numshots == 0) {
|
|
if (g_FrData.donealarm == false && g_FrData.timetaken > TICKS(-180)) {
|
|
g_FrData.donealarm = true;
|
|
sndStart(var80095200, SFX_FR_ALARM, NULL, -1, -1, -1, -1, -1);
|
|
}
|
|
|
|
if (!g_FrData.donelighting && g_FrData.timetaken > TICKS(-225)) {
|
|
frInitLighting();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Fired a shot during prestart
|
|
if (!g_FrData.donelighting) {
|
|
frInitLighting();
|
|
}
|
|
|
|
g_FrData.timetaken = 0;
|
|
g_FrData.donealarm = true;
|
|
}
|
|
|
|
// Iterate each target and handle their health active/inactive state
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse && g_FrData.targets[i].destroyed == false && g_FrData.targets[i].active) {
|
|
invincible = false;
|
|
exploding = false;
|
|
weaponnum2 = frGetWeaponBySlot(g_FrData.slot);
|
|
prop = g_FrData.targets[i].prop;
|
|
obj = prop->obj;
|
|
|
|
switch (weaponnum2) {
|
|
case WEAPON_GRENADE:
|
|
case WEAPON_PROXIMITYMINE:
|
|
coordTriggerProxies(&prop->pos, true);
|
|
break;
|
|
}
|
|
|
|
if (g_FrData.targets[i].travelling && g_FrData.targets[i].silent && g_FrData.targets[i].travelspeed != -1) {
|
|
g_FrData.targets[i].silent = false;
|
|
propsnd0f0939f8(NULL, g_FrData.targets[i].prop, SFX_FR_CONVEYER, -1,
|
|
-1, 0, 0, 0, 0, -1, 0, -1, -1, -1, -1);
|
|
}
|
|
|
|
if (g_FrData.targets[i].angle > 2.2915925979614f && g_FrData.targets[i].angle < 3.9915928840637f) {
|
|
obj->damage = 0;
|
|
}
|
|
|
|
if ((g_FrData.targets[i].flags & FRTARGETFLAG_TMPINVINCIBLE)
|
|
&& g_FrData.targets[i].invincibletimer < TICKS(300)) {
|
|
invincible = true;
|
|
g_FrData.targets[i].invincibletimer += g_Vars.lvupdate240_60;
|
|
}
|
|
|
|
if (obj->damage > 0) {
|
|
if (invincible || g_FrData.targets[i].angle == M_PI) {
|
|
obj->damage = 0;
|
|
} else if (g_FrData.targets[i].flags & FRTARGETFLAG_ONEHITEXPLODE
|
|
|| obj->damage >= obj->maxdamage
|
|
|| frGetWeaponBySlot(g_FrData.slot) == WEAPON_PHOENIX) {
|
|
g_FrData.numhitsbullseye++;
|
|
g_FrData.score += 10;
|
|
exploding = true;
|
|
g_FrData.feedbackttl = TICKS(60);
|
|
g_FrData.feedbackzone = FRZONE_EXPLODE;
|
|
}
|
|
}
|
|
|
|
// Handle target being destroyed
|
|
if (exploding || (g_FrData.targets[i].maxdamage != 255
|
|
&& g_FrData.targets[i].damage >= g_FrData.targets[i].maxdamage)) {
|
|
bbox = objFindBboxRodata(obj);
|
|
|
|
if (g_FrNumSounds && g_FrData.targets[i].travelling) {
|
|
g_FrNumSounds--;
|
|
func0f0926bc(prop, 1, 0xffff);
|
|
}
|
|
|
|
if (g_FrNumSounds);
|
|
|
|
shardsCreate(&prop->pos, &obj->realrot[0][0], &obj->realrot[1][0], &obj->realrot[2][0],
|
|
bbox->xmin, bbox->xmax, bbox->ymin, bbox->ymax, 2, prop);
|
|
|
|
g_FrData.targetsdestroyed++;
|
|
|
|
if (g_FrData.targets[i].flags & FRTARGETFLAG_ONEHITEXPLODE) {
|
|
explosionCreateSimple(g_FrData.targets[i].prop, &g_FrData.targets[i].prop->pos,
|
|
g_FrData.targets[i].prop->rooms, EXPLOSIONTYPE_FRTARGET, 1);
|
|
}
|
|
|
|
g_FrData.targets[i].travelling = false;
|
|
g_FrData.targets[i].active = false;
|
|
g_FrData.targets[i].destroyed = true;
|
|
|
|
obj->flags2 |= OBJFLAG2_INVISIBLE;
|
|
|
|
prop->pos.x = 0;
|
|
prop->pos.y = -5000;
|
|
prop->pos.z = 0;
|
|
|
|
func0f069c70(obj, true, false);
|
|
|
|
// Activate another target
|
|
for (j = 0; j < ARRAYCOUNT(g_FrData.targets); j++) {
|
|
if (g_FrData.targets[j].destroyed == false
|
|
&& g_FrData.targets[j].inuse
|
|
&& g_FrData.targets[j].active == false) {
|
|
obj2 = g_FrData.targets[j].prop->obj;
|
|
g_FrData.targets[j].active = true;
|
|
obj2->flags2 &= ~OBJFLAG2_INVISIBLE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if the session should end
|
|
if (g_FrData.goaltargets == 255) {
|
|
if (g_FrData.goalscore && g_FrData.score >= g_FrData.goalscore) {
|
|
frSetCompleted();
|
|
return;
|
|
}
|
|
} else {
|
|
if (g_FrData.targetsdestroyed >= g_FrData.goaltargets
|
|
&& (g_FrData.goalscore == 0 || g_FrData.score >= g_FrData.goalscore)) {
|
|
frSetCompleted();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (g_FrData.targetsdestroyed >= g_FrData.numtargets) {
|
|
frSetFailReason(FRFAILREASON_SCOREUNATTAINABLE);
|
|
return;
|
|
}
|
|
|
|
if (frIsAmmoWasted()) {
|
|
frSetFailReason(FRFAILREASON_OUTOFAMMO);
|
|
return;
|
|
}
|
|
|
|
if (g_FrData.timelimit != 255 && g_FrData.timetaken >= g_FrData.timelimit * TICKS(60)) {
|
|
frSetFailReason(FRFAILREASON_TIMEOVER);
|
|
return;
|
|
}
|
|
|
|
// Tick each target
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse
|
|
&& g_FrData.targets[i].destroyed == false
|
|
&& g_FrData.targets[i].active) {
|
|
prop = g_FrData.targets[i].prop;
|
|
obj = prop->obj;
|
|
|
|
if (g_FrData.targets[i].travelling) {
|
|
if (g_FrData.targets[i].travelspeed == -1) {
|
|
g_FrData.targets[i].donestopsound = true;
|
|
g_FrData.targets[i].travelling = false;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
mult = 1;
|
|
#endif
|
|
dist = -2;
|
|
} else {
|
|
diff.x = g_FrData.targets[i].dstpos.x - prop->pos.x;
|
|
diff.y = g_FrData.targets[i].dstpos.y - prop->pos.y;
|
|
diff.z = g_FrData.targets[i].dstpos.z - prop->pos.z;
|
|
|
|
dist = sqrtf(diff.f[0] * diff.f[0] + diff.f[1] * diff.f[1] + diff.f[2] * diff.f[2]);
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
mult = 1;
|
|
#endif
|
|
|
|
if (dist != 0) {
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
mult = g_FrData.targets[i].travelspeed * g_Vars.lvupdate240freal;
|
|
#else
|
|
mult = (g_FrData.targets[i].travelspeed * g_Vars.lvupdate240) * 0.25f;
|
|
#endif
|
|
diff.x *= 1.0f / dist;
|
|
diff.y *= 1.0f / dist;
|
|
diff.z *= 1.0f / dist;
|
|
newpos.x = diff.x * mult + prop->pos.x;
|
|
newpos.y = diff.y * mult + prop->pos.y;
|
|
newpos.z = diff.z * mult + prop->pos.z;
|
|
#else
|
|
diff.x *= 1.0f / dist;
|
|
diff.y *= 1.0f / dist;
|
|
diff.z *= 1.0f / dist;
|
|
newpos.x = diff.x * g_FrData.targets[i].travelspeed * g_Vars.lvupdate240 * 0.25f + prop->pos.x;
|
|
newpos.y = diff.y * g_FrData.targets[i].travelspeed * g_Vars.lvupdate240 * 0.25f + prop->pos.y;
|
|
newpos.z = diff.z * g_FrData.targets[i].travelspeed * g_Vars.lvupdate240 * 0.25f + prop->pos.z;
|
|
#endif
|
|
} else {
|
|
dist = -2;
|
|
}
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (mult >= dist)
|
|
#else
|
|
if (dist < g_FrData.targets[i].travelspeed)
|
|
#endif
|
|
{
|
|
// Target is stopping
|
|
newpos.x = g_FrData.targets[i].dstpos.x;
|
|
newpos.y = g_FrData.targets[i].dstpos.y;
|
|
newpos.z = g_FrData.targets[i].dstpos.z;
|
|
|
|
g_FrData.targets[i].scriptenabled = true;
|
|
g_FrData.targets[i].travelling = false;
|
|
|
|
if (g_FrData.targets[i].donestopsound == false) {
|
|
g_FrData.targets[i].donestopsound = true;
|
|
|
|
if (g_FrNumSounds) {
|
|
g_FrNumSounds--;
|
|
}
|
|
|
|
func0f0926bc(prop, 1, 0xffff);
|
|
propsnd0f0939f8(NULL, prop, SFX_FR_CONVEYER_STOP, -1,
|
|
-1, 1024, 0, 0, 0, -1, 0, -1, -1, -1, -1);
|
|
|
|
if (g_FrNumSounds);
|
|
}
|
|
}
|
|
|
|
prop->pos.x = newpos.x;
|
|
prop->pos.y = newpos.y;
|
|
prop->pos.z = newpos.z;
|
|
|
|
func0f069c70(obj, true, false);
|
|
}
|
|
|
|
if (g_FrData.targets[i].rotateoncloak && g_FrData.targets[i].rotating == false) {
|
|
if (g_FrData.targets[i].timeuntilrotate == 0) {
|
|
chr = g_Vars.currentplayer->prop->chr;
|
|
cloaked = chr->hidden & CHRHFLAG_CLOAKED;
|
|
|
|
if (cloaked) {
|
|
if (g_FrData.targets[i].angle == M_PI) {
|
|
g_FrData.targets[i].timeuntilrotate = TICKS(60);
|
|
g_FrData.targets[i].rotatetoangle = 0;
|
|
g_FrData.targets[i].rotatespeed = -M_PI / 90;
|
|
}
|
|
} else {
|
|
if (g_FrData.targets[i].angle == 0) {
|
|
g_FrData.targets[i].timeuntilrotate = TICKS(60);
|
|
g_FrData.targets[i].rotatetoangle = M_PI;
|
|
g_FrData.targets[i].rotatespeed = M_PI / 90;
|
|
}
|
|
}
|
|
} else {
|
|
g_FrData.targets[i].timeuntilrotate -= g_Vars.lvupdate240_60;
|
|
|
|
if (g_FrData.targets[i].timeuntilrotate <= 0) {
|
|
g_FrData.targets[i].timeuntilrotate = 0;
|
|
g_FrData.targets[i].rotating = true;
|
|
}
|
|
}
|
|
} else if (g_FrData.targets[i].rotating) {
|
|
toangle = g_FrData.targets[i].rotatetoangle;
|
|
speed = g_FrData.targets[i].rotatespeed;
|
|
|
|
if (toangle);
|
|
|
|
oldside = 0;
|
|
|
|
if (g_FrData.targets[i].angle < toangle) {
|
|
oldside = 1;
|
|
}
|
|
|
|
oldside = (u8)oldside;
|
|
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
g_FrData.targets[i].angle += speed * g_Vars.lvupdate240freal;
|
|
#else
|
|
g_FrData.targets[i].angle += speed * g_Vars.lvupdate240 * 0.25f;
|
|
#endif
|
|
|
|
newside = 0;
|
|
|
|
toangle = g_FrData.targets[i].rotatetoangle;
|
|
|
|
if (g_FrData.targets[i].angle < toangle) {
|
|
newside = 1;
|
|
}
|
|
|
|
newside = (u8)newside;
|
|
|
|
if (newside != oldside || g_FrData.targets[i].angle == toangle) {
|
|
// Reached desired angle
|
|
g_FrData.targets[i].angle = g_FrData.targets[i].rotatetoangle;
|
|
g_FrData.targets[i].rotating = false;
|
|
g_FrData.targets[i].scriptenabled = true;
|
|
g_FrData.targets[i].scriptsleep = 0;
|
|
|
|
while (g_FrData.targets[i].angle > M_BADTAU) {
|
|
g_FrData.targets[i].angle -= M_BADTAU;
|
|
}
|
|
|
|
while (g_FrData.targets[i].angle < 0) {
|
|
g_FrData.targets[i].angle += M_BADTAU;
|
|
}
|
|
}
|
|
|
|
mtx4LoadYRotation(g_FrData.targets[i].angle + M_PI, &spbc);
|
|
mtx00015f04(obj->model->scale, &spbc);
|
|
mtx4ToMtx3(&spbc, sp98);
|
|
mtx3Copy(sp98, obj->realrot);
|
|
}
|
|
|
|
if (g_FrData.targets[i].scriptenabled && g_FrData.targets[i].scriptsleep != SECSTOTIME60(255)) {
|
|
g_FrData.targets[i].scriptsleep -= g_Vars.lvupdate240_60;
|
|
|
|
if (g_FrData.targets[i].scriptsleep <= 0) {
|
|
g_FrData.targets[i].scriptenabled = false;
|
|
|
|
while (!frExecuteTargetScript(i));
|
|
|
|
if (frTargetIsAtScriptStart(i)) {
|
|
while (!frExecuteTargetScript(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void func0f1a0924(struct prop *prop)
|
|
{
|
|
struct defaultobj *obj = prop->obj;
|
|
s32 i;
|
|
|
|
if (obj->modelnum == MODEL_TARGET) {
|
|
f32 sp68;
|
|
f32 sp64;
|
|
f32 sp60;
|
|
f32 sp56;
|
|
|
|
sp64 = -1;
|
|
sp68 = -1;
|
|
sp56 = -2;
|
|
sp60 = -2;
|
|
|
|
modelGetScreenCoords(obj->model, &sp56, &sp64, &sp60, &sp68);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (g_Vars.currentplayer->trackedprops[i].prop == prop) {
|
|
return;
|
|
}
|
|
|
|
if (g_Vars.currentplayer->trackedprops[i].prop == NULL) {
|
|
g_Vars.currentplayer->trackedprops[i].prop = prop;
|
|
|
|
g_Vars.currentplayer->trackedprops[i].x1 = sp64 - 2;
|
|
g_Vars.currentplayer->trackedprops[i].x2 = sp56 + 2;
|
|
g_Vars.currentplayer->trackedprops[i].y1 = sp68 - 2;
|
|
g_Vars.currentplayer->trackedprops[i].y2 = sp60 + 2;
|
|
g_Vars.currentplayer->targetset[i] = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool frChooseFarsightTarget(void)
|
|
{
|
|
struct prop *bestprop = NULL;
|
|
f32 bestvalue = 1;
|
|
f32 bestdist = -1;
|
|
bool found = false;
|
|
s32 i;
|
|
|
|
if (bgunGetWeaponNum(HAND_RIGHT) == WEAPON_FARSIGHT) {
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
if (g_FrData.targets[i].inuse
|
|
&& g_FrData.targets[i].destroyed == false
|
|
&& g_FrData.targets[i].active
|
|
&& g_FrData.targets[i].flags & FRTARGETFLAG_FARSIGHTAUTOTARGETABLE) {
|
|
struct prop *prop = g_FrData.targets[i].prop;
|
|
f32 xdiff = g_Vars.currentplayer->bond2.unk10.x - prop->pos.x;
|
|
f32 ydiff = g_Vars.currentplayer->bond2.unk10.y - prop->pos.y;
|
|
f32 zdiff = g_Vars.currentplayer->bond2.unk10.z - prop->pos.z;
|
|
f32 dist = sqrtf(xdiff * xdiff + ydiff * ydiff + zdiff * zdiff);
|
|
|
|
if (dist > 0) {
|
|
f32 value = (xdiff * g_Vars.currentplayer->bond2.unk1c.f[0]
|
|
+ ydiff * g_Vars.currentplayer->bond2.unk1c.f[1]
|
|
+ zdiff * g_Vars.currentplayer->bond2.unk1c.f[2]) / dist;
|
|
|
|
if (value);
|
|
|
|
if (value < 0 && value < bestvalue) {
|
|
bestvalue = value;
|
|
bestprop = prop;
|
|
bestdist = dist;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
g_Vars.currentplayer->autoeraserdist = bestdist;
|
|
g_Vars.currentplayer->autoerasertarget = bestprop;
|
|
|
|
return found;
|
|
}
|
|
|
|
s32 frIsInTraining(void)
|
|
{
|
|
if (g_FrData.menucountdown > 0 &&
|
|
(g_FrData.menutype == FRMENUTYPE_FAILED || g_FrData.menutype == FRMENUTYPE_COMPLETED)) {
|
|
return true;
|
|
}
|
|
|
|
return g_Vars.currentplayer->prop->rooms[0] == ROOM_DISH_FIRINGRANGE
|
|
&& g_FrIsValidWeapon
|
|
&& mainGetStageNum() == STAGE_CITRAINING;
|
|
}
|
|
|
|
void frCalculateHit(struct defaultobj *obj, struct coord *hitpos, f32 maulercharge)
|
|
{
|
|
s32 i;
|
|
|
|
if (g_FrIsValidWeapon == false) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_FrData.targets); i++) {
|
|
struct prop *prop = g_FrData.targets[i].prop;
|
|
|
|
if (obj == prop->obj) {
|
|
f32 xdiff = hitpos->x - prop->pos.x;
|
|
f32 ydiff = hitpos->y - prop->pos.y;
|
|
f32 zdiff = hitpos->z - prop->pos.z;
|
|
|
|
f32 dist = sqrtf(xdiff * xdiff + ydiff * ydiff + zdiff * zdiff);
|
|
|
|
if (g_FrData.targets[i].flags & FRTARGETFLAG_ONEHITEXPLODE) {
|
|
g_FrData.targets[i].damage = g_FrData.targets[i].maxdamage;
|
|
} else if (frGetWeaponBySlot(g_FrData.slot) == WEAPON_MAULER) {
|
|
g_FrData.targets[i].damage += (f32)((s32)(maulercharge * 0.1f) + 1);
|
|
} else if ((g_FrData.targets[i].flags & FRTARGETFLAG_TMPINVINCIBLE) == 0
|
|
|| g_FrData.targets[i].invincibletimer >= TICKS(300)) {
|
|
g_FrData.targets[i].damage++;
|
|
}
|
|
|
|
if (dist < 18) {
|
|
g_FrData.feedbackzone = FRZONE_BULLSEYE;
|
|
g_FrData.numhitsbullseye++;
|
|
} else if (dist < 37) {
|
|
g_FrData.feedbackzone = FRZONE_RING1;
|
|
g_FrData.numhitsring1++;
|
|
} else if (dist < 56) {
|
|
g_FrData.feedbackzone = FRZONE_RING2;
|
|
g_FrData.numhitsring2++;
|
|
} else {
|
|
g_FrData.feedbackzone = FRZONE_RING3;
|
|
g_FrData.numhitsring3++;
|
|
}
|
|
|
|
g_FrData.feedbackttl = TICKS(60);
|
|
g_FrData.score += g_FrData.feedbackzone;
|
|
}
|
|
}
|
|
}
|
|
|
|
void frIncrementNumShots(void)
|
|
{
|
|
g_FrData.numshots++;
|
|
g_FrData.numshotssincetopup++;
|
|
}
|
|
|
|
bool ciIsChrBioUnlocked(u32 bodynum)
|
|
{
|
|
switch (bodynum) {
|
|
case BODY_DARK_COMBAT:
|
|
case BODY_CARRINGTON:
|
|
return true;
|
|
case BODY_CASSANDRA:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_DEFECTION);
|
|
case BODY_DRCAROLL:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_INVESTIGATION);
|
|
case BODY_MRBLONDE:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_EXTRACTION);
|
|
case BODY_TRENT:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_G5BUILDING);
|
|
case BODY_JONATHAN:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_INFILTRATION);
|
|
case BODY_THEKING:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_RESCUE);
|
|
case BODY_PRESIDENT:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_AIRFORCEONE);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
u8 g_ChrBioSlot = 0;
|
|
|
|
struct chrbio *ciGetChrBioByBodynum(u32 bodynum)
|
|
{
|
|
struct chrbio bios[] = {
|
|
// name, race, age, profile
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
/*0*/ { L_DISH_125, L_DISH_126, L_DISH_127, L_DISH_128 }, // Joanna Dark
|
|
/*1*/ { L_DISH_129, L_DISH_130, L_DISH_131, L_DISH_132 }, // Jonathan
|
|
/*2*/ { L_DISH_133, L_DISH_134, L_DISH_135, L_DISH_136 }, // Daniel Carrington
|
|
/*3*/ { L_DISH_137, L_DISH_138, L_DISH_139, L_DISH_140 }, // Cassandra De Vries
|
|
/*4*/ { L_DISH_141, L_DISH_142, L_DISH_143, L_DISH_144 }, // Trent Easton
|
|
/*5*/ { L_DISH_145, L_DISH_146, L_DISH_147, L_DISH_148 }, // Dr. Caroll
|
|
/*6*/ { L_DISH_149, L_DISH_150, L_DISH_151, L_DISH_152 }, // Elvis
|
|
/*7*/ { L_DISH_153, L_DISH_154, L_DISH_155, L_DISH_156 }, // Mr. Blonde
|
|
/*8*/ { L_DISH_157, L_DISH_158, L_DISH_159, L_DISH_160 }, // Mr. Blonde (repeat)
|
|
/*9*/ { L_DISH_161, L_DISH_162, L_DISH_163, L_DISH_164 }, // The U.S. President
|
|
#else
|
|
/*0*/ { L_MISC_219, L_MISC_220, L_MISC_221, L_MISC_222 }, // Joanna Dark
|
|
/*1*/ { L_MISC_223, L_MISC_224, L_MISC_225, L_MISC_226 }, // Jonathan
|
|
/*2*/ { L_MISC_227, L_MISC_228, L_MISC_229, L_MISC_230 }, // Daniel Carrington
|
|
/*3*/ { L_MISC_231, L_MISC_232, L_MISC_233, L_MISC_234 }, // Cassandra De Vries
|
|
/*4*/ { L_MISC_235, L_MISC_236, L_MISC_237, L_MISC_238 }, // Trent Easton
|
|
/*5*/ { L_MISC_239, L_MISC_240, L_MISC_241, L_MISC_242 }, // Dr. Caroll
|
|
/*6*/ { L_MISC_243, L_MISC_244, L_MISC_245, L_MISC_246 }, // Elvis
|
|
/*7*/ { L_MISC_247, L_MISC_248, L_MISC_249, L_MISC_250 }, // Mr. Blonde
|
|
/*8*/ { L_MISC_251, L_MISC_252, L_MISC_253, L_MISC_254 }, // Mr. Blonde (repeat)
|
|
/*9*/ { L_MISC_255, L_MISC_256, L_MISC_257, L_MISC_258 }, // The U.S. President
|
|
#endif
|
|
};
|
|
|
|
switch (bodynum) {
|
|
case BODY_DARK_COMBAT:
|
|
return &bios[0];
|
|
case BODY_JONATHAN:
|
|
return &bios[1];
|
|
case BODY_CARRINGTON:
|
|
return &bios[2];
|
|
case BODY_CASSANDRA:
|
|
return &bios[3];
|
|
case BODY_TRENT:
|
|
return &bios[4];
|
|
case BODY_DRCAROLL:
|
|
return &bios[5];
|
|
case BODY_THEKING:
|
|
return &bios[6];
|
|
case BODY_MRBLONDE:
|
|
if (ciIsStageComplete(SOLOSTAGEINDEX_CRASHSITE)) {
|
|
return &bios[8];
|
|
}
|
|
return &bios[7];
|
|
case BODY_PRESIDENT:
|
|
return &bios[9];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *ciGetChrBioDescription(void)
|
|
{
|
|
struct chrbio *bio = ciGetChrBioByBodynum(ciGetChrBioBodynumBySlot(g_ChrBioSlot));
|
|
return langGet(bio->description);
|
|
}
|
|
|
|
s32 ciGetNumUnlockedChrBios(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 bodynum;
|
|
|
|
for (bodynum = 0; bodynum < NUM_BODIES; bodynum++) {
|
|
if (ciIsChrBioUnlocked(bodynum)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 ciGetChrBioBodynumBySlot(s32 slot)
|
|
{
|
|
s32 index = -1;
|
|
s32 bodynum;
|
|
|
|
for (bodynum = 0; bodynum < NUM_BODIES; bodynum++) {
|
|
if (ciIsChrBioUnlocked(bodynum)) {
|
|
index++;
|
|
}
|
|
|
|
if (index == slot) {
|
|
return bodynum;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct miscbio *ciGetMiscBio(s32 index)
|
|
{
|
|
struct miscbio bios[] = {
|
|
// name, description
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
{ L_DISH_165, L_DISH_166 },
|
|
{ L_DISH_167, L_DISH_168 },
|
|
{ L_DISH_169, L_DISH_170 },
|
|
{ L_DISH_171, L_DISH_172 },
|
|
#else
|
|
{ L_MISC_259, L_MISC_260 },
|
|
{ L_MISC_261, L_MISC_262 },
|
|
{ L_MISC_263, L_MISC_264 },
|
|
{ L_MISC_265, L_MISC_266 },
|
|
#endif
|
|
};
|
|
|
|
switch (index) {
|
|
case MISCBIO_MAIANS: return &bios[0];
|
|
case MISCBIO_SKEDAR: return &bios[1];
|
|
case MISCBIO_BACKGROUND: return &bios[2];
|
|
case MISCBIO_STORY: return &bios[3];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool ciIsMiscBioUnlocked(s32 index)
|
|
{
|
|
switch (index) {
|
|
case MISCBIO_MAIANS:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_RESCUE);
|
|
case MISCBIO_SKEDAR:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_ATTACKSHIP);
|
|
case MISCBIO_BACKGROUND:
|
|
case MISCBIO_STORY:
|
|
return ciIsStageComplete(SOLOSTAGEINDEX_MBR);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
s32 ciGetNumUnlockedMiscBios(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (ciIsMiscBioUnlocked(i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 ciGetMiscBioIndexBySlot(s32 slot)
|
|
{
|
|
s32 index = -1;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (ciIsMiscBioUnlocked(i)) {
|
|
index++;
|
|
}
|
|
|
|
if (index == slot) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *ciGetMiscBioDescription(void)
|
|
{
|
|
s32 index = ciGetMiscBioIndexBySlot(g_ChrBioSlot - ciGetNumUnlockedChrBios());
|
|
struct miscbio *bio = ciGetMiscBio(index);
|
|
|
|
return langGet(bio->description);
|
|
}
|
|
|
|
bool ciIsHangarBioAVehicle(s32 index)
|
|
{
|
|
return index >= HANGARBIO_JUMPSHIP;
|
|
}
|
|
|
|
u8 g_HangarBioSlot = 0;
|
|
|
|
struct hangarbio *ciGetHangarBio(s32 index)
|
|
{
|
|
struct hangarbio bios[] = {
|
|
// name, description
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
{ L_DISH_196, L_DISH_219 }, // Carrington Institute
|
|
{ L_DISH_197, L_DISH_220 }, // Lucerne Tower
|
|
{ L_DISH_198, L_DISH_221 }, // Laboratory Basement
|
|
{ L_DISH_199, L_DISH_222 }, // Carrington Villa
|
|
{ L_DISH_200, L_DISH_223 }, // Chicago
|
|
{ L_DISH_201, L_DISH_224 }, // G5 Building
|
|
{ L_DISH_202, L_DISH_225 }, // Area 51
|
|
{ L_DISH_203, L_DISH_226 }, // Alaskan Air Base
|
|
{ L_DISH_204, L_DISH_227 }, // Air Force One
|
|
{ L_DISH_205, L_DISH_228 }, // Crash Site
|
|
{ L_DISH_206, L_DISH_229 }, // Pelagic II
|
|
{ L_DISH_207, L_DISH_230 }, // Cetan Ship
|
|
{ L_DISH_208, L_DISH_231 }, // Skedar Assault Ship
|
|
{ L_DISH_209, L_DISH_232 }, // Skedar Homeworld
|
|
{ L_DISH_210, L_DISH_233 }, // Jumpship
|
|
{ L_DISH_211, L_DISH_234 }, // HoverCrate
|
|
{ L_DISH_212, L_DISH_235 }, // HoverBike
|
|
{ L_DISH_213, L_DISH_236 }, // Cleaning Hovbot
|
|
{ L_DISH_214, L_DISH_237 }, // Hovercopter
|
|
{ L_DISH_215, L_DISH_238 }, // G5 Robot
|
|
{ L_DISH_216, L_DISH_239 }, // A51 Interceptor
|
|
{ L_DISH_217, L_DISH_240 }, // Maian Vessel
|
|
{ L_DISH_218, L_DISH_241 }, // Skedar Shuttle
|
|
#else
|
|
{ L_MISC_290, L_MISC_313 }, // Carrington Institute
|
|
{ L_MISC_291, L_MISC_314 }, // Lucerne Tower
|
|
{ L_MISC_292, L_MISC_315 }, // Laboratory Basement
|
|
{ L_MISC_293, L_MISC_316 }, // Carrington Villa
|
|
{ L_MISC_294, L_MISC_317 }, // Chicago
|
|
{ L_MISC_295, L_MISC_318 }, // G5 Building
|
|
{ L_MISC_296, L_MISC_319 }, // Area 51
|
|
{ L_MISC_297, L_MISC_320 }, // Alaskan Air Base
|
|
{ L_MISC_298, L_MISC_321 }, // Air Force One
|
|
{ L_MISC_299, L_MISC_322 }, // Crash Site
|
|
{ L_MISC_300, L_MISC_323 }, // Pelagic II
|
|
{ L_MISC_301, L_MISC_324 }, // Cetan Ship
|
|
{ L_MISC_302, L_MISC_325 }, // Skedar Assault Ship
|
|
{ L_MISC_303, L_MISC_326 }, // Skedar Homeworld
|
|
{ L_MISC_304, L_MISC_327 }, // Jumpship
|
|
{ L_MISC_305, L_MISC_328 }, // HoverCrate
|
|
{ L_MISC_306, L_MISC_329 }, // HoverBike
|
|
{ L_MISC_307, L_MISC_330 }, // Cleaning Hovbot
|
|
{ L_MISC_308, L_MISC_331 }, // Hovercopter
|
|
{ L_MISC_309, L_MISC_332 }, // G5 Robot
|
|
{ L_MISC_310, L_MISC_333 }, // A51 Interceptor
|
|
{ L_MISC_311, L_MISC_334 }, // Maian Vessel
|
|
{ L_MISC_312, L_MISC_335 }, // Skedar Shuttle
|
|
#endif
|
|
};
|
|
|
|
switch (index) {
|
|
case HANGARBIO_INSTITUTE: return &bios[0];
|
|
case HANGARBIO_DDTOWER: return &bios[1];
|
|
case HANGARBIO_LABBASEMENT: return &bios[2];
|
|
case HANGARBIO_VILLA: return &bios[3];
|
|
case HANGARBIO_CHICAGO: return &bios[4];
|
|
case HANGARBIO_G5: return &bios[5];
|
|
case HANGARBIO_AREA51: return &bios[6];
|
|
case HANGARBIO_AIRBASE: return &bios[7];
|
|
case HANGARBIO_AIRFORCEONE: return &bios[8];
|
|
case HANGARBIO_CRASHSITE: return &bios[9];
|
|
case HANGARBIO_PELAGIC: return &bios[10];
|
|
case HANGARBIO_DEEPSEA: return &bios[11];
|
|
case HANGARBIO_ATTACKSHIP: return &bios[12];
|
|
case HANGARBIO_SKEDARRUINS: return &bios[13];
|
|
case HANGARBIO_JUMPSHIP: return &bios[14];
|
|
case HANGARBIO_HOVERCRATE: return &bios[15];
|
|
case HANGARBIO_HOVERBIKE: return &bios[16];
|
|
case HANGARBIO_HOVERBOT: return &bios[17];
|
|
case HANGARBIO_HOVERCOPTER: return &bios[18];
|
|
case HANGARBIO_G5ROBOT: return &bios[19];
|
|
case HANGARBIO_A51INTERCEPTOR: return &bios[20];
|
|
case HANGARBIO_MAIANVESSEL: return &bios[21];
|
|
case HANGARBIO_SKEDARSHUTTLE: return &bios[22];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
u8 g_DtSlot = 0;
|
|
u8 var80088adc = 0;
|
|
|
|
bool ciIsHangarBioUnlocked(u32 bioindex)
|
|
{
|
|
u32 stage;
|
|
|
|
switch (bioindex) {
|
|
case HANGARBIO_INSTITUTE:
|
|
case HANGARBIO_HOVERCRATE:
|
|
return true;
|
|
case HANGARBIO_DDTOWER:
|
|
stage = SOLOSTAGEINDEX_DEFECTION;
|
|
break;
|
|
case HANGARBIO_LABBASEMENT:
|
|
case HANGARBIO_HOVERBOT:
|
|
stage = SOLOSTAGEINDEX_INVESTIGATION;
|
|
break;
|
|
case HANGARBIO_HOVERCOPTER:
|
|
stage = SOLOSTAGEINDEX_EXTRACTION;
|
|
break;
|
|
case HANGARBIO_VILLA:
|
|
case HANGARBIO_JUMPSHIP:
|
|
stage = SOLOSTAGEINDEX_VILLA;
|
|
break;
|
|
case HANGARBIO_CHICAGO:
|
|
stage = SOLOSTAGEINDEX_CHICAGO;
|
|
break;
|
|
case HANGARBIO_G5:
|
|
case HANGARBIO_G5ROBOT:
|
|
stage = SOLOSTAGEINDEX_G5BUILDING;
|
|
break;
|
|
case HANGARBIO_AREA51:
|
|
case HANGARBIO_HOVERBIKE:
|
|
case HANGARBIO_A51INTERCEPTOR:
|
|
stage = SOLOSTAGEINDEX_INFILTRATION;
|
|
break;
|
|
case HANGARBIO_AIRBASE:
|
|
stage = SOLOSTAGEINDEX_AIRBASE;
|
|
break;
|
|
case HANGARBIO_AIRFORCEONE:
|
|
stage = SOLOSTAGEINDEX_AIRFORCEONE;
|
|
break;
|
|
case HANGARBIO_CRASHSITE:
|
|
case HANGARBIO_MAIANVESSEL:
|
|
stage = SOLOSTAGEINDEX_CRASHSITE;
|
|
break;
|
|
case HANGARBIO_PELAGIC:
|
|
stage = SOLOSTAGEINDEX_PELAGIC;
|
|
break;
|
|
case HANGARBIO_DEEPSEA:
|
|
stage = SOLOSTAGEINDEX_DEEPSEA;
|
|
break;
|
|
case HANGARBIO_ATTACKSHIP:
|
|
case HANGARBIO_SKEDARSHUTTLE:
|
|
stage = SOLOSTAGEINDEX_DEFENSE;
|
|
break;
|
|
case HANGARBIO_SKEDARRUINS:
|
|
stage = SOLOSTAGEINDEX_ATTACKSHIP;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return ciIsStageComplete(stage);
|
|
}
|
|
|
|
s32 ciGetNumUnlockedLocationBios(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 23; i++) {
|
|
if (ciIsHangarBioAVehicle(i)) {
|
|
return count;
|
|
}
|
|
|
|
if (ciIsHangarBioUnlocked(i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 ciGetNumUnlockedHangarBios(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 23; i++) {
|
|
if (ciIsHangarBioUnlocked(i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 ciGetHangarBioIndexBySlot(s32 slot)
|
|
{
|
|
s32 index = -1;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 23; i++) {
|
|
if (ciIsHangarBioUnlocked(i)) {
|
|
index++;
|
|
}
|
|
|
|
if (index == slot) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *ciGetHangarBioDescription(void)
|
|
{
|
|
struct hangarbio *bio = ciGetHangarBio(ciGetHangarBioIndexBySlot(g_HangarBioSlot));
|
|
return langGet(bio->description);
|
|
}
|
|
|
|
struct trainingdata *dtGetData(void)
|
|
{
|
|
return &g_DtData;
|
|
}
|
|
|
|
void dtRestorePlayer(void)
|
|
{
|
|
bgunSetPassiveMode(true);
|
|
|
|
if (g_DtData.obj) {
|
|
objFreePermanently(g_DtData.obj, true);
|
|
}
|
|
|
|
g_DtData.obj = NULL;
|
|
|
|
if (dtGetWeaponByDeviceIndex(dtGetIndexBySlot(g_DtSlot)) == WEAPON_ECMMINE) {
|
|
bgunSetAmmoQuantity(AMMOTYPE_ECM_MINE, 0);
|
|
}
|
|
|
|
if (g_Vars.currentplayer->eyespy) {
|
|
struct chrdata *chr = g_Vars.currentplayer->eyespy->prop->chr;
|
|
g_Vars.currentplayer->eyespy->deployed = false;
|
|
g_Vars.currentplayer->eyespy->held = true;
|
|
g_Vars.currentplayer->eyespy->active = false;
|
|
|
|
chr->chrflags |= CHRCFLAG_HIDDEN;
|
|
|
|
func0f0926bc(g_Vars.currentplayer->eyespy->prop, 1, 0xffff);
|
|
|
|
g_Vars.currentplayer->devicesactive &= ~DEVICE_EYESPY;
|
|
}
|
|
}
|
|
|
|
void dtPushEndscreen(void)
|
|
{
|
|
if (g_DtData.completed) {
|
|
func0f0f85e0(&g_DtCompletedMenuDialog, MENUROOT_TRAINING);
|
|
} else if (g_DtData.failed) {
|
|
func0f0f85e0(&g_DtFailedMenuDialog, MENUROOT_TRAINING);
|
|
}
|
|
|
|
g_DtData.timeleft = 0;
|
|
g_DtData.completed = false;
|
|
g_DtData.failed = false;
|
|
g_DtData.finished = false;
|
|
g_DtData.holographedpc = false;
|
|
}
|
|
|
|
void dtTick(void)
|
|
{
|
|
if (var80088adc) {
|
|
if (g_DtData.intraining) {
|
|
g_DtData.timetaken += g_Vars.lvupdate240_60;
|
|
|
|
if (g_Vars.currentplayer->isdead) {
|
|
dtEnd();
|
|
}
|
|
|
|
if (chrHasStageFlag(NULL, STAGEFLAG_CI_TRIGGER_DEVICE_FAILURE)) {
|
|
dtEnd();
|
|
g_DtData.failed = true;
|
|
g_DtData.timeleft = 1;
|
|
g_DtData.finished = true;
|
|
} else if (chrHasStageFlag(NULL, STAGEFLAG_CI_TRIGGER_DEVICE_SUCCESS)) {
|
|
dtEnd();
|
|
g_DtData.completed = true;
|
|
g_DtData.timeleft = 1;
|
|
g_DtData.finished = true;
|
|
}
|
|
} else if (g_DtData.finished) {
|
|
if (g_DtData.timeleft <= 0) {
|
|
dtPushEndscreen();
|
|
} else {
|
|
g_DtData.timeleft -= g_Vars.lvupdate240_60;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void func0f1a1ac0(void)
|
|
{
|
|
if (var80088adc == false) {
|
|
var80088adc = true;
|
|
g_DtData.intraining = false;
|
|
g_DtData.failed = false;
|
|
g_DtData.completed = false;
|
|
g_DtData.finished = false;
|
|
g_DtData.timeleft = 0;
|
|
g_DtData.holographedpc = false;
|
|
g_DtData.timetaken = 0;
|
|
g_DtData.obj = NULL;
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_DEVICE_ABORTING);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_DEVICE_SUCCESS);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_DEVICE_FAILURE);
|
|
}
|
|
}
|
|
|
|
void dtBegin(void)
|
|
{
|
|
g_DtData.intraining = true;
|
|
g_DtData.timetaken = 0;
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_DEVICE_ABORTING);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_DEVICE_SUCCESS);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_DEVICE_FAILURE);
|
|
chrSetStageFlag(NULL, ciGetStageFlagByDeviceIndex(dtGetIndexBySlot(g_DtSlot)));
|
|
g_Vars.currentplayer->training = true;
|
|
bgunSetPassiveMode(false);
|
|
chrSetStageFlag(NULL, STAGEFLAG_CI_IN_TRAINING);
|
|
}
|
|
|
|
void dtEnd(void)
|
|
{
|
|
g_DtData.intraining = false;
|
|
dtRestorePlayer();
|
|
bgunSetAmmoQuantity(AMMOTYPE_CLOAK, 0);
|
|
chrSetStageFlag(NULL, STAGEFLAG_CI_DEVICE_ABORTING);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_DEVICE_FAILURE);
|
|
chrUnsetStageFlag(NULL, ciGetStageFlagByDeviceIndex(dtGetIndexBySlot(g_DtSlot)));
|
|
g_Vars.currentplayer->training = false;
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_IN_TRAINING);
|
|
playerDisplayHealth();
|
|
g_Vars.currentplayer->bondhealth = 1;
|
|
}
|
|
|
|
bool dtIsAvailable(s32 deviceindex)
|
|
{
|
|
u8 flags[] = {
|
|
GAMEFILEFLAG_CI_UPLINK_DONE,
|
|
GAMEFILEFLAG_CI_ECMMINE_DONE,
|
|
GAMEFILEFLAG_CI_CAMSPY_DONE,
|
|
GAMEFILEFLAG_CI_NIGHTVISION_DONE,
|
|
GAMEFILEFLAG_CI_DOORDECODER_DONE,
|
|
GAMEFILEFLAG_CI_RTRACKER_DONE,
|
|
GAMEFILEFLAG_CI_IR_DONE,
|
|
GAMEFILEFLAG_CI_XRAY_DONE,
|
|
GAMEFILEFLAG_CI_DISGUISE_DONE,
|
|
GAMEFILEFLAG_CI_CLOAK_DONE,
|
|
};
|
|
|
|
deviceindex--;
|
|
|
|
if (deviceindex >= 10) {
|
|
return true;
|
|
}
|
|
|
|
if (deviceindex < 0 || gamefileHasFlag(flags[deviceindex])) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
s32 dtGetNumAvailable(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
if (dtIsAvailable(i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 dtGetIndexBySlot(s32 wantindex)
|
|
{
|
|
s32 index = -1;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
if (dtIsAvailable(i)) {
|
|
index++;
|
|
}
|
|
|
|
if (index == wantindex) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 dtGetWeaponByDeviceIndex(s32 deviceindex)
|
|
{
|
|
u32 weapons[] = {
|
|
WEAPON_DATAUPLINK,
|
|
WEAPON_ECMMINE,
|
|
WEAPON_EYESPY,
|
|
WEAPON_NIGHTVISION,
|
|
WEAPON_DOORDECODER,
|
|
WEAPON_RTRACKER,
|
|
WEAPON_IRSCANNER,
|
|
WEAPON_XRAYSCANNER,
|
|
WEAPON_DISGUISE41,
|
|
WEAPON_CLOAKINGDEVICE,
|
|
};
|
|
|
|
return weapons[deviceindex];
|
|
}
|
|
|
|
u32 ciGetStageFlagByDeviceIndex(u32 deviceindex)
|
|
{
|
|
u32 flags[] = {
|
|
STAGEFLAG_CI_TRIGGER_UPLINK,
|
|
STAGEFLAG_CI_TRIGGER_ECMMINE,
|
|
STAGEFLAG_CI_TRIGGER_CAMSPY,
|
|
STAGEFLAG_CI_TRIGGER_NIGHTVISION,
|
|
STAGEFLAG_CI_TRIGGER_DOORDECODER,
|
|
STAGEFLAG_CI_TRIGGER_RTRACKER,
|
|
STAGEFLAG_CI_TRIGGER_IR,
|
|
STAGEFLAG_CI_TRIGGER_XRAY,
|
|
STAGEFLAG_CI_TRIGGER_DISGUISE,
|
|
STAGEFLAG_CI_TRIGGER_CLOAK,
|
|
};
|
|
|
|
return flags[deviceindex];
|
|
}
|
|
|
|
char *dtGetDescription(void)
|
|
{
|
|
u32 texts[] = {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
/*0*/ L_DISH_186, // Data uplink
|
|
/*1*/ L_DISH_185, // ECM mine
|
|
/*2*/ L_DISH_177, // CamSpy
|
|
/*3*/ L_DISH_178, // Night vision
|
|
/*4*/ L_DISH_179, // Door decoder
|
|
/*5*/ L_DISH_183, // R-tracker
|
|
/*6*/ L_DISH_182, // IR scanner
|
|
/*7*/ L_DISH_180, // X-ray scanner
|
|
/*8*/ L_DISH_181, // Disguise
|
|
/*9*/ L_DISH_184, // Cloak
|
|
#else
|
|
/*0*/ L_MISC_280, // Data uplink
|
|
/*1*/ L_MISC_279, // ECM mine
|
|
/*2*/ L_MISC_271, // CamSpy
|
|
/*3*/ L_MISC_272, // Night vision
|
|
/*4*/ L_MISC_273, // Door decoder
|
|
/*5*/ L_MISC_277, // R-tracker
|
|
/*6*/ L_MISC_276, // IR scanner
|
|
/*7*/ L_MISC_274, // X-ray scanner
|
|
/*8*/ L_MISC_275, // Disguise
|
|
/*9*/ L_MISC_278, // Cloak
|
|
#endif
|
|
};
|
|
|
|
return langGet(texts[dtGetIndexBySlot(g_DtSlot)]);
|
|
}
|
|
|
|
char *dtGetTip1(void)
|
|
{
|
|
u32 texts[] = {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
/*0*/ L_DISH_263,
|
|
/*1*/ L_DISH_264,
|
|
/*2*/ L_DISH_265,
|
|
/*3*/ L_DISH_266,
|
|
/*4*/ L_DISH_267,
|
|
/*5*/ L_DISH_268,
|
|
/*6*/ L_DISH_269,
|
|
/*7*/ L_DISH_270,
|
|
/*8*/ L_DISH_271,
|
|
/*9*/ L_DISH_272,
|
|
#else
|
|
/*0*/ L_MISC_357,
|
|
/*1*/ L_MISC_358,
|
|
/*2*/ L_MISC_359,
|
|
/*3*/ L_MISC_360,
|
|
/*4*/ L_MISC_361,
|
|
/*5*/ L_MISC_362,
|
|
/*6*/ L_MISC_363,
|
|
/*7*/ L_MISC_364,
|
|
/*8*/ L_MISC_365,
|
|
/*9*/ L_MISC_366,
|
|
#endif
|
|
};
|
|
|
|
return langGet(texts[dtGetIndexBySlot(g_DtSlot)]);
|
|
}
|
|
|
|
char *dtGetTip2(void)
|
|
{
|
|
u32 texts[] = {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
/*0*/ L_DISH_273,
|
|
/*1*/ L_DISH_274,
|
|
/*2*/ L_DISH_275,
|
|
/*3*/ L_DISH_276,
|
|
/*4*/ L_DISH_277,
|
|
/*5*/ L_DISH_278,
|
|
/*6*/ L_DISH_279,
|
|
/*7*/ L_DISH_280,
|
|
/*8*/ L_DISH_281,
|
|
/*9*/ L_DISH_282,
|
|
#else
|
|
/*0*/ L_MISC_367,
|
|
/*1*/ L_MISC_368,
|
|
/*2*/ L_MISC_369,
|
|
/*3*/ L_MISC_370,
|
|
/*4*/ L_MISC_371,
|
|
/*5*/ L_MISC_372,
|
|
/*6*/ L_MISC_373,
|
|
/*7*/ L_MISC_374,
|
|
/*8*/ L_MISC_375,
|
|
/*9*/ L_MISC_376,
|
|
#endif
|
|
};
|
|
|
|
return langGet(texts[dtGetIndexBySlot(g_DtSlot)]);
|
|
}
|
|
|
|
struct trainingdata *getHoloTrainingData(void)
|
|
{
|
|
return &g_HtData;
|
|
}
|
|
|
|
void htPushEndscreen(void)
|
|
{
|
|
if (g_HtData.completed) {
|
|
func0f0f85e0(&g_HtCompletedMenuDialog, MENUROOT_TRAINING);
|
|
} else if (g_HtData.failed) {
|
|
func0f0f85e0(&g_HtFailedMenuDialog, MENUROOT_TRAINING);
|
|
}
|
|
|
|
g_HtData.timeleft = 0;
|
|
g_HtData.completed = false;
|
|
g_HtData.failed = false;
|
|
g_HtData.finished = false;
|
|
}
|
|
|
|
u8 var80088bb4 = 0;
|
|
u8 var80088bb8 = 0;
|
|
|
|
void htTick(void)
|
|
{
|
|
if (var80088bb8) {
|
|
if (g_HtData.intraining) {
|
|
g_HtData.timetaken += g_Vars.lvupdate240_60;
|
|
|
|
if (g_Vars.currentplayer->isdead) {
|
|
htEnd();
|
|
}
|
|
|
|
if (chrHasStageFlag(NULL, STAGEFLAG_CI_TRIGGER_HOLO_FAILURE)) {
|
|
htEnd();
|
|
g_HtData.failed = true;
|
|
g_HtData.timeleft = 1;
|
|
g_HtData.finished = true;
|
|
} else if (chrHasStageFlag(NULL, STAGEFLAG_CI_TRIGGER_HOLO_SUCCESS)) {
|
|
htEnd();
|
|
g_HtData.completed = true;
|
|
g_HtData.timeleft = 1;
|
|
g_HtData.finished = true;
|
|
}
|
|
} else if (g_HtData.finished) {
|
|
if (g_HtData.timeleft <= 0) {
|
|
htPushEndscreen();
|
|
} else {
|
|
g_HtData.timeleft -= g_Vars.lvupdate240_60;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void func0f1a2198(void)
|
|
{
|
|
if (var80088bb8 == false) {
|
|
var80088bb8 = true;
|
|
g_HtData.intraining = false;
|
|
g_HtData.failed = false;
|
|
g_HtData.completed = false;
|
|
g_HtData.finished = false;
|
|
g_HtData.timeleft = 0;
|
|
g_HtData.timetaken = 0;
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_HOLO_ABORTING);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_HOLO_SUCCESS);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_HOLO_FAILURE);
|
|
}
|
|
}
|
|
|
|
void htBegin(void)
|
|
{
|
|
struct waypoint *waypoints = g_StageSetup.waypoints;
|
|
|
|
g_HtData.intraining = true;
|
|
g_HtData.timetaken = 0;
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_HOLO_ABORTING);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_HOLO_SUCCESS);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_HOLO_FAILURE);
|
|
chrSetStageFlag(NULL, func0f1a25c0(htGetIndexBySlot(var80088bb4)));
|
|
|
|
// Disable segment leading out of the door
|
|
waypointDisableSegment(&waypoints[0x20], &waypoints[0x31]);
|
|
|
|
g_Vars.currentplayer->training = true;
|
|
bgunSetPassiveMode(false);
|
|
chrSetStageFlag(NULL, STAGEFLAG_CI_IN_TRAINING);
|
|
}
|
|
|
|
void htEnd(void)
|
|
{
|
|
struct prop *prop;
|
|
s16 *propnum;
|
|
s16 propnums[256];
|
|
s16 rooms[5] = { 0x0016, 0x0017, 0x0018, 0x0019, -1 };
|
|
struct waypoint *waypoints = g_StageSetup.waypoints;
|
|
|
|
g_HtData.intraining = false;
|
|
chrSetStageFlag(NULL, STAGEFLAG_CI_HOLO_ABORTING);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_TRIGGER_HOLO_FAILURE);
|
|
chrUnsetStageFlag(NULL, func0f1a25c0(htGetIndexBySlot(var80088bb4)));
|
|
|
|
// Enable segment leading out of the door
|
|
waypointEnableSegment(&waypoints[0x20], &waypoints[0x31]);
|
|
|
|
g_Vars.currentplayer->training = false;
|
|
roomGetProps(rooms, propnums, 256);
|
|
propnum = &propnums[0];
|
|
|
|
// Remove dropped weapons
|
|
while (*propnum >= 0) {
|
|
prop = &g_Vars.props[*propnum];
|
|
|
|
if (prop && prop->type == PROPTYPE_WEAPON) {
|
|
struct defaultobj *obj = prop->obj;
|
|
|
|
if (obj->type == OBJTYPE_WEAPON) {
|
|
objFreePermanently(obj, true);
|
|
}
|
|
}
|
|
|
|
propnum++;
|
|
}
|
|
|
|
bgunSetPassiveMode(true);
|
|
chrUnsetStageFlag(NULL, STAGEFLAG_CI_IN_TRAINING);
|
|
playerDisplayHealth();
|
|
g_Vars.currentplayer->bondhealth = 1;
|
|
}
|
|
|
|
bool htIsUnlocked(u32 value)
|
|
{
|
|
switch (value) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
s32 htGetNumUnlocked(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
if (htIsUnlocked(i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 htGetIndexBySlot(s32 slot)
|
|
{
|
|
s32 index = -1;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
if (htIsUnlocked(i)) {
|
|
index++;
|
|
}
|
|
|
|
if (index == slot) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *htGetName(s32 index)
|
|
{
|
|
u32 texts[] = {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
L_DISH_316, // "Holo 1 - Looking Around"
|
|
L_DISH_317, // "Holo 2 - Movement 1"
|
|
L_DISH_318, // "Holo 3 - Movement 2"
|
|
L_DISH_319, // "Holo 4 - Unarmed Combat 1"
|
|
L_DISH_320, // "Holo 5 - Unarmed Combat 2"
|
|
L_DISH_321, // "Holo 6 - Live Combat 1"
|
|
L_DISH_322, // "Holo 7 - Live Combat 2"
|
|
#else
|
|
L_MISC_410, // "Holo 1 - Looking Around"
|
|
L_MISC_411, // "Holo 2 - Movement 1"
|
|
L_MISC_412, // "Holo 3 - Movement 2"
|
|
L_MISC_413, // "Holo 4 - Unarmed Combat 1"
|
|
L_MISC_414, // "Holo 5 - Unarmed Combat 2"
|
|
L_MISC_415, // "Holo 6 - Live Combat 1"
|
|
L_MISC_416, // "Holo 7 - Live Combat 2"
|
|
#endif
|
|
};
|
|
|
|
return langGet(texts[index]);
|
|
}
|
|
|
|
u32 func0f1a25c0(s32 index)
|
|
{
|
|
u32 flags[] = {
|
|
STAGEFLAG_CI_IN_HOLO1,
|
|
STAGEFLAG_CI_IN_HOLO2,
|
|
STAGEFLAG_CI_IN_HOLO3,
|
|
STAGEFLAG_CI_IN_HOLO4,
|
|
STAGEFLAG_CI_IN_HOLO5,
|
|
STAGEFLAG_CI_IN_HOLO6,
|
|
STAGEFLAG_CI_IN_HOLO7,
|
|
STAGEFLAG_CI_GENERAL_PURPOSE,
|
|
};
|
|
|
|
return flags[index];
|
|
}
|
|
|
|
char *htGetDescription(void)
|
|
{
|
|
u32 texts[] = {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
L_DISH_242,
|
|
L_DISH_243,
|
|
L_DISH_244,
|
|
L_DISH_245,
|
|
L_DISH_246,
|
|
L_DISH_247,
|
|
L_DISH_248,
|
|
#else
|
|
L_MISC_336,
|
|
L_MISC_337,
|
|
L_MISC_338,
|
|
L_MISC_339,
|
|
L_MISC_340,
|
|
L_MISC_341,
|
|
L_MISC_342,
|
|
#endif
|
|
};
|
|
|
|
return langGet(texts[htGetIndexBySlot(var80088bb4)]);
|
|
}
|
|
|
|
char *htGetTip1(void)
|
|
{
|
|
u32 texts[] = {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
L_DISH_249, // "For greater precision..."
|
|
L_DISH_250, // "Think about where you want to go..."
|
|
L_DISH_251, // "Ducking enables you to..."
|
|
L_DISH_252, // "Attacking opponents from behind..."
|
|
L_DISH_253, // "Only stay close long enough..."
|
|
L_DISH_254, // "Don't hang around and wait..."
|
|
L_DISH_255, // "Go for the armed opponents..."
|
|
#else
|
|
L_MISC_343, // "For greater precision..."
|
|
L_MISC_344, // "Think about where you want to go..."
|
|
L_MISC_345, // "Ducking enables you to..."
|
|
L_MISC_346, // "Attacking opponents from behind..."
|
|
L_MISC_347, // "Only stay close long enough..."
|
|
L_MISC_348, // "Don't hang around and wait..."
|
|
L_MISC_349, // "Go for the armed opponents..."
|
|
#endif
|
|
};
|
|
|
|
return langGet(texts[htGetIndexBySlot(var80088bb4)]);
|
|
}
|
|
|
|
char *htGetTip2(void)
|
|
{
|
|
u32 texts[] = {
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
L_DISH_256, // "For greater precision..."
|
|
L_DISH_257, // "Sidestepping and strafing..."
|
|
L_DISH_258, // "Ducking enables you to..."
|
|
L_DISH_259, // "Attacking opponents from behind..."
|
|
L_DISH_260, // "Only stay close long enough..."
|
|
L_DISH_261, // "Don't hang around and wait..."
|
|
L_DISH_262, // "Go for the armed opponents..."
|
|
#else
|
|
L_MISC_350, // "For greater precision..."
|
|
L_MISC_351, // "Sidestepping and strafing..."
|
|
L_MISC_352, // "Ducking enables you to..."
|
|
L_MISC_353, // "Attacking opponents from behind..."
|
|
L_MISC_354, // "Only stay close long enough..."
|
|
L_MISC_355, // "Don't hang around and wait..."
|
|
L_MISC_356, // "Go for the armed opponents..."
|
|
#endif
|
|
};
|
|
|
|
return langGet(texts[htGetIndexBySlot(var80088bb4)]);
|
|
}
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
void frGetGoalTargetsText(char *buffer, char *buffer2)
|
|
{
|
|
sprintf(buffer, "%s", langGet(L_MISC_417));
|
|
sprintf(buffer2, "%d\n", g_FrData.goaltargets);
|
|
}
|
|
#else
|
|
void frGetGoalTargetsText(char *buffer)
|
|
{
|
|
// "GOAL TARGETS:"
|
|
sprintf(buffer, "%s %d\n", langGet(L_MISC_417), g_FrData.goaltargets);
|
|
}
|
|
#endif
|
|
|
|
void frGetTargetsDestroyedValue(char *buffer)
|
|
{
|
|
sprintf(buffer, "%02d\n", g_FrData.targetsdestroyed);
|
|
}
|
|
|
|
void frGetScoreValue(char *buffer)
|
|
{
|
|
sprintf(buffer, "%03d\n", g_FrData.score);
|
|
}
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
void frGetGoalScoreText(char *buffer1, char *buffer2)
|
|
{
|
|
if (g_FrData.goalscore) {
|
|
sprintf(buffer1, "%s", langGet(L_MISC_418));
|
|
sprintf(buffer2, "%d\n", g_FrData.goalscore);
|
|
} else {
|
|
sprintf(buffer1, "");
|
|
sprintf(buffer2, "");
|
|
}
|
|
}
|
|
#else
|
|
void frGetGoalScoreText(char *buffer)
|
|
{
|
|
if (g_FrData.goalscore) {
|
|
// "GOAL SCORE:"
|
|
sprintf(buffer, "%s %d\n", langGet(L_MISC_418), g_FrData.goalscore);
|
|
} else {
|
|
sprintf(buffer, "");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
f32 frGetAccuracy(char *buffer)
|
|
{
|
|
f32 sum = (g_FrData.numhitsring3
|
|
+ g_FrData.numhitsbullseye
|
|
+ g_FrData.numhitsring1
|
|
+ g_FrData.numhitsring2) * 100.0f;
|
|
f32 accuracy = 100.0f;
|
|
|
|
if (g_FrData.numshots) {
|
|
accuracy = sum / (f32)g_FrData.numshots;
|
|
}
|
|
|
|
if (accuracy > 100.0f) {
|
|
accuracy = 100.0f;
|
|
}
|
|
|
|
sprintf(buffer, "%s%s%.2f%%\n", "", "", accuracy);
|
|
|
|
return accuracy;
|
|
}
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
bool frGetMinAccuracy(char *buffer1, f32 accuracy, char *buffer2)
|
|
{
|
|
sprintf(buffer1, "%s", langGet(L_MISC_419));
|
|
sprintf(buffer2, "%d%%\n", g_FrData.goalaccuracy);
|
|
|
|
return accuracy < g_FrData.goalaccuracy;
|
|
}
|
|
#else
|
|
bool frGetMinAccuracy(char *buffer, f32 accuracy)
|
|
{
|
|
// "MIN ACCURACY:"
|
|
sprintf(buffer, "%s %d%%\n", langGet(L_MISC_419), g_FrData.goalaccuracy);
|
|
|
|
return accuracy < g_FrData.goalaccuracy;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Formats either the time taken or time limit into buffer, and returns true if
|
|
* the time should induce a failure.
|
|
*
|
|
* The time limit will be used if it exists and the time take exceeds it,
|
|
* otherwise time taken will be used.
|
|
*
|
|
* Negative time taken (such as when the player aborts before the challenge
|
|
* starts) is wrapped to positive and will induce a failure.
|
|
*/
|
|
bool frFormatTime(char *buffer)
|
|
{
|
|
s32 mins = 0;
|
|
s32 mult = 1;
|
|
f32 secs = g_FrData.timetaken / TICKS(60.0f);
|
|
u8 failed = false;
|
|
|
|
if (g_FrData.timelimit != 255 && secs >= g_FrData.timelimit) {
|
|
failed = true;
|
|
secs = g_FrData.timelimit;
|
|
} else if (g_FrData.timetaken < 0) {
|
|
failed = true;
|
|
}
|
|
|
|
if (secs < 0) {
|
|
mult = -1;
|
|
secs = -secs;
|
|
}
|
|
|
|
if (secs >= 60) {
|
|
while (secs >= 60) {
|
|
secs -= 60;
|
|
mins++;
|
|
}
|
|
}
|
|
|
|
sprintf(buffer, "%02d:%02d\n", mult * mins, (s32)secs);
|
|
|
|
return failed;
|
|
}
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
bool frGetHudMiddleSubtext(char *buffer1, char *buffer2)
|
|
{
|
|
s32 secs;
|
|
s32 mins;
|
|
|
|
sprintf(buffer2, "");
|
|
|
|
if (g_FrData.timetaken < TICKS(-180)) {
|
|
sprintf(buffer1, "%s", langGet(L_MISC_420)); // "FIRE TO START"
|
|
return false;
|
|
}
|
|
|
|
if (g_FrData.timetaken < 0) {
|
|
sprintf(buffer1, "%s", langGet(L_MISC_421)); // "GET READY!"
|
|
return true;
|
|
}
|
|
|
|
if (g_FrData.timelimit == 255) {
|
|
return false;
|
|
}
|
|
|
|
secs = g_FrData.timelimit;
|
|
mins = 0;
|
|
|
|
if (secs >= 60) {
|
|
while (secs >= 60) {
|
|
secs -= 60;
|
|
mins++;
|
|
}
|
|
}
|
|
|
|
sprintf(buffer1, "%s", langGet(L_MISC_422)); // "LIMIT:"
|
|
sprintf(buffer2, "%02d:%02d\n", mins, secs);
|
|
return true;
|
|
}
|
|
#else
|
|
bool frGetHudMiddleSubtext(char *buffer)
|
|
{
|
|
s32 secs;
|
|
s32 mins;
|
|
|
|
if (g_FrData.timetaken < TICKS(-180)) {
|
|
sprintf(buffer, "%s", langGet(L_MISC_420)); // "FIRE TO START"
|
|
return false;
|
|
}
|
|
|
|
if (g_FrData.timetaken < 0) {
|
|
sprintf(buffer, "%s", langGet(L_MISC_421)); // "GET READY!"
|
|
return true;
|
|
}
|
|
|
|
if (g_FrData.timelimit == 255) {
|
|
return false;
|
|
}
|
|
|
|
secs = g_FrData.timelimit;
|
|
mins = 0;
|
|
|
|
if (secs >= 60) {
|
|
while (secs >= 60) {
|
|
secs -= 60;
|
|
mins++;
|
|
}
|
|
}
|
|
|
|
sprintf(buffer, "%s %02d:%02d\n", langGet(L_MISC_422), mins, secs); // "LIMIT:"
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
bool frGetFeedback(char *scorebuffer, char *zonebuffer, char *extrabuffer)
|
|
{
|
|
u32 texts[] = {
|
|
L_MISC_423, // "ZONE 3"
|
|
L_MISC_424, // "ZONE 2"
|
|
L_MISC_425, // "ZONE 1"
|
|
L_MISC_426, // "BULL'S-EYE"
|
|
L_MISC_427, // "EXPLODED"
|
|
};
|
|
|
|
sprintf(extrabuffer, "");
|
|
|
|
if (g_FrData.feedbackzone) {
|
|
g_FrData.feedbackttl -= g_Vars.lvupdate240_60;
|
|
|
|
if (g_FrData.feedbackttl <= 0) {
|
|
g_FrData.feedbackzone = 0;
|
|
g_FrData.feedbackttl = 0;
|
|
return false;
|
|
}
|
|
|
|
if (g_FrData.feedbackzone == FRZONE_EXPLODE) {
|
|
sprintf(scorebuffer, "010\n");
|
|
} else {
|
|
sprintf(scorebuffer, "%03d\n", g_FrData.feedbackzone);
|
|
}
|
|
|
|
switch (g_FrData.feedbackzone) {
|
|
case FRZONE_RING3:
|
|
sprintf(zonebuffer, "%s", langGet(texts[0]));
|
|
return true;
|
|
case FRZONE_RING2:
|
|
sprintf(zonebuffer, "%s", langGet(texts[1]));
|
|
return true;
|
|
case FRZONE_RING1:
|
|
sprintf(zonebuffer, "%s", langGet(texts[2]));
|
|
return true;
|
|
case FRZONE_BULLSEYE:
|
|
sprintf(zonebuffer, "%s", langGet(texts[3]));
|
|
return true;
|
|
case FRZONE_EXPLODE:
|
|
sprintf(zonebuffer, "%s", langGet(texts[4]));
|
|
return true;
|
|
}
|
|
|
|
sprintf(zonebuffer, "\n");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#else
|
|
bool frGetFeedback(char *scorebuffer, char *zonebuffer)
|
|
{
|
|
u32 texts[] = {
|
|
L_MISC_423, // "ZONE 3"
|
|
L_MISC_424, // "ZONE 2"
|
|
L_MISC_425, // "ZONE 1"
|
|
L_MISC_426, // "BULL'S-EYE"
|
|
L_MISC_427, // "EXPLODED"
|
|
};
|
|
|
|
if (g_FrData.feedbackzone) {
|
|
g_FrData.feedbackttl -= g_Vars.lvupdate240_60;
|
|
|
|
if (g_FrData.feedbackttl <= 0) {
|
|
g_FrData.feedbackzone = 0;
|
|
g_FrData.feedbackttl = 0;
|
|
return false;
|
|
}
|
|
|
|
if (g_FrData.feedbackzone == FRZONE_EXPLODE) {
|
|
sprintf(scorebuffer, "010\n");
|
|
} else {
|
|
sprintf(scorebuffer, "%03d\n", g_FrData.feedbackzone);
|
|
}
|
|
|
|
switch (g_FrData.feedbackzone) {
|
|
case FRZONE_RING3:
|
|
sprintf(zonebuffer, "%s", langGet(texts[0]));
|
|
return true;
|
|
case FRZONE_RING2:
|
|
sprintf(zonebuffer, "%s", langGet(texts[1]));
|
|
return true;
|
|
case FRZONE_RING1:
|
|
sprintf(zonebuffer, "%s", langGet(texts[2]));
|
|
return true;
|
|
case FRZONE_BULLSEYE:
|
|
sprintf(zonebuffer, "%s", langGet(texts[3]));
|
|
return true;
|
|
case FRZONE_EXPLODE:
|
|
sprintf(zonebuffer, "%s", langGet(texts[4]));
|
|
return true;
|
|
}
|
|
|
|
sprintf(zonebuffer, "\n");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
GLOBAL_ASM(
|
|
glabel frRenderHudElement
|
|
/* f1a3788: 27bdff88 */ addiu $sp,$sp,-120
|
|
/* f1a378c: afa60080 */ sw $a2,0x80($sp)
|
|
/* f1a3790: 3c0e8008 */ lui $t6,0x8008
|
|
/* f1a3794: 8dce0150 */ lw $t6,0x150($t6)
|
|
/* f1a3798: afb00034 */ sw $s0,0x34($sp)
|
|
/* f1a379c: afa70084 */ sw $a3,0x84($sp)
|
|
/* f1a37a0: 00e03025 */ move $a2,$a3
|
|
/* f1a37a4: 00808025 */ move $s0,$a0
|
|
/* f1a37a8: afbf003c */ sw $ra,0x3c($sp)
|
|
/* f1a37ac: afa5007c */ sw $a1,0x7c($sp)
|
|
/* f1a37b0: 3c078008 */ lui $a3,0x8008
|
|
/* f1a37b4: afb10038 */ sw $s1,0x38($sp)
|
|
/* f1a37b8: 8ce70154 */ lw $a3,0x154($a3)
|
|
/* f1a37bc: 27a50070 */ addiu $a1,$sp,0x70
|
|
/* f1a37c0: 27a40074 */ addiu $a0,$sp,0x74
|
|
/* f1a37c4: afa00014 */ sw $zero,0x14($sp)
|
|
/* f1a37c8: 0fc55d49 */ jal textMeasure
|
|
/* f1a37cc: afae0010 */ sw $t6,0x10($sp)
|
|
/* f1a37d0: 8fa80070 */ lw $t0,0x70($sp)
|
|
/* f1a37d4: 8faf007c */ lw $t7,0x7c($sp)
|
|
/* f1a37d8: 8fa30080 */ lw $v1,0x80($sp)
|
|
/* f1a37dc: 0008c043 */ sra $t8,$t0,0x1
|
|
/* f1a37e0: 27aa0074 */ addiu $t2,$sp,0x74
|
|
/* f1a37e4: 01f8c823 */ subu $t9,$t7,$t8
|
|
/* f1a37e8: afb9006c */ sw $t9,0x6c($sp)
|
|
/* f1a37ec: afaa0010 */ sw $t2,0x10($sp)
|
|
/* f1a37f0: 02002025 */ move $a0,$s0
|
|
/* f1a37f4: 27a5006c */ addiu $a1,$sp,0x6c
|
|
/* f1a37f8: 27a60068 */ addiu $a2,$sp,0x68
|
|
/* f1a37fc: 27a70070 */ addiu $a3,$sp,0x70
|
|
/* f1a3800: 0fc54bed */ jal text0f153858
|
|
/* f1a3804: afa30068 */ sw $v1,0x68($sp)
|
|
/* f1a3808: 0c002eeb */ jal viGetWidth
|
|
/* f1a380c: 00408025 */ move $s0,$v0
|
|
/* f1a3810: 00028c00 */ sll $s1,$v0,0x10
|
|
/* f1a3814: 00115c03 */ sra $t3,$s1,0x10
|
|
/* f1a3818: 0c002eef */ jal viGetHeight
|
|
/* f1a381c: 01608825 */ move $s1,$t3
|
|
/* f1a3820: 93a30097 */ lbu $v1,0x97($sp)
|
|
/* f1a3824: 8fae0090 */ lw $t6,0x90($sp)
|
|
/* f1a3828: 3c0c8008 */ lui $t4,0x8008
|
|
/* f1a382c: 3c0d8008 */ lui $t5,0x8008
|
|
/* f1a3830: 2401ff00 */ li $at,-256
|
|
/* f1a3834: 8dad0150 */ lw $t5,0x150($t5)
|
|
/* f1a3838: 8d8c0154 */ lw $t4,0x154($t4)
|
|
/* f1a383c: 01c17824 */ and $t7,$t6,$at
|
|
/* f1a3840: 01e34825 */ or $t1,$t7,$v1
|
|
/* f1a3844: 00034043 */ sra $t0,$v1,0x1
|
|
/* f1a3848: afa8001c */ sw $t0,0x1c($sp)
|
|
/* f1a384c: afa80048 */ sw $t0,0x48($sp)
|
|
/* f1a3850: afa90018 */ sw $t1,0x18($sp)
|
|
/* f1a3854: afa90044 */ sw $t1,0x44($sp)
|
|
/* f1a3858: 02002025 */ move $a0,$s0
|
|
/* f1a385c: 27a5006c */ addiu $a1,$sp,0x6c
|
|
/* f1a3860: 27a60068 */ addiu $a2,$sp,0x68
|
|
/* f1a3864: 8fa70084 */ lw $a3,0x84($sp)
|
|
/* f1a3868: afb10020 */ sw $s1,0x20($sp)
|
|
/* f1a386c: afa20024 */ sw $v0,0x24($sp)
|
|
/* f1a3870: afa00028 */ sw $zero,0x28($sp)
|
|
/* f1a3874: afa0002c */ sw $zero,0x2c($sp)
|
|
/* f1a3878: afad0014 */ sw $t5,0x14($sp)
|
|
/* f1a387c: 0fc55d34 */ jal func0f1574d0jf
|
|
/* f1a3880: afac0010 */ sw $t4,0x10($sp)
|
|
/* f1a3884: 8fa60088 */ lw $a2,0x88($sp)
|
|
/* f1a3888: 00408025 */ move $s0,$v0
|
|
/* f1a388c: 27a4005c */ addiu $a0,$sp,0x5c
|
|
/* f1a3890: 10c00060 */ beqz $a2,.JF0f1a3a14
|
|
/* f1a3894: 27a50058 */ addiu $a1,$sp,0x58
|
|
/* f1a3898: 3c188008 */ lui $t8,0x8008
|
|
/* f1a389c: 8f180148 */ lw $t8,0x148($t8)
|
|
/* f1a38a0: 3c078008 */ lui $a3,0x8008
|
|
/* f1a38a4: 8ce7014c */ lw $a3,0x14c($a3)
|
|
/* f1a38a8: afa00014 */ sw $zero,0x14($sp)
|
|
/* f1a38ac: 0fc55d49 */ jal textMeasure
|
|
/* f1a38b0: afb80010 */ sw $t8,0x10($sp)
|
|
/* f1a38b4: 3c198008 */ lui $t9,0x8008
|
|
/* f1a38b8: 8f390148 */ lw $t9,0x148($t9)
|
|
/* f1a38bc: 3c078008 */ lui $a3,0x8008
|
|
/* f1a38c0: 8ce7014c */ lw $a3,0x14c($a3)
|
|
/* f1a38c4: 27a40054 */ addiu $a0,$sp,0x54
|
|
/* f1a38c8: 27a50050 */ addiu $a1,$sp,0x50
|
|
/* f1a38cc: 8fa6008c */ lw $a2,0x8c($sp)
|
|
/* f1a38d0: afa00014 */ sw $zero,0x14($sp)
|
|
/* f1a38d4: 0fc55d49 */ jal textMeasure
|
|
/* f1a38d8: afb90010 */ sw $t9,0x10($sp)
|
|
/* f1a38dc: 8fab0058 */ lw $t3,0x58($sp)
|
|
/* f1a38e0: 8fac0050 */ lw $t4,0x50($sp)
|
|
/* f1a38e4: 8fad007c */ lw $t5,0x7c($sp)
|
|
/* f1a38e8: 8fa20080 */ lw $v0,0x80($sp)
|
|
/* f1a38ec: 016c4021 */ addu $t0,$t3,$t4
|
|
/* f1a38f0: 8faa005c */ lw $t2,0x5c($sp)
|
|
/* f1a38f4: 00087043 */ sra $t6,$t0,0x1
|
|
/* f1a38f8: 27b80074 */ addiu $t8,$sp,0x74
|
|
/* f1a38fc: 01ae7823 */ subu $t7,$t5,$t6
|
|
/* f1a3900: 24420011 */ addiu $v0,$v0,0x11
|
|
/* f1a3904: afaf006c */ sw $t7,0x6c($sp)
|
|
/* f1a3908: afa20040 */ sw $v0,0x40($sp)
|
|
/* f1a390c: afa20068 */ sw $v0,0x68($sp)
|
|
/* f1a3910: afb80010 */ sw $t8,0x10($sp)
|
|
/* f1a3914: afa80070 */ sw $t0,0x70($sp)
|
|
/* f1a3918: 02002025 */ move $a0,$s0
|
|
/* f1a391c: 27a5006c */ addiu $a1,$sp,0x6c
|
|
/* f1a3920: 27a60068 */ addiu $a2,$sp,0x68
|
|
/* f1a3924: 27a70070 */ addiu $a3,$sp,0x70
|
|
/* f1a3928: 0fc54bed */ jal text0f153858
|
|
/* f1a392c: afaa0074 */ sw $t2,0x74($sp)
|
|
/* f1a3930: 0c002eeb */ jal viGetWidth
|
|
/* f1a3934: 00408025 */ move $s0,$v0
|
|
/* f1a3938: 00028c00 */ sll $s1,$v0,0x10
|
|
/* f1a393c: 0011cc03 */ sra $t9,$s1,0x10
|
|
/* f1a3940: 0c002eef */ jal viGetHeight
|
|
/* f1a3944: 03208825 */ move $s1,$t9
|
|
/* f1a3948: 3c0a8008 */ lui $t2,0x8008
|
|
/* f1a394c: 3c0b8008 */ lui $t3,0x8008
|
|
/* f1a3950: 8d6b0148 */ lw $t3,0x148($t3)
|
|
/* f1a3954: 8d4a014c */ lw $t2,0x14c($t2)
|
|
/* f1a3958: 8fac0044 */ lw $t4,0x44($sp)
|
|
/* f1a395c: 8fad0048 */ lw $t5,0x48($sp)
|
|
/* f1a3960: 02002025 */ move $a0,$s0
|
|
/* f1a3964: 27a5006c */ addiu $a1,$sp,0x6c
|
|
/* f1a3968: 27a60068 */ addiu $a2,$sp,0x68
|
|
/* f1a396c: 8fa70088 */ lw $a3,0x88($sp)
|
|
/* f1a3970: afb10020 */ sw $s1,0x20($sp)
|
|
/* f1a3974: afa20024 */ sw $v0,0x24($sp)
|
|
/* f1a3978: afa00028 */ sw $zero,0x28($sp)
|
|
/* f1a397c: afa0002c */ sw $zero,0x2c($sp)
|
|
/* f1a3980: afab0014 */ sw $t3,0x14($sp)
|
|
/* f1a3984: afaa0010 */ sw $t2,0x10($sp)
|
|
/* f1a3988: afac0018 */ sw $t4,0x18($sp)
|
|
/* f1a398c: 0fc55d34 */ jal func0f1574d0jf
|
|
/* f1a3990: afad001c */ sw $t5,0x1c($sp)
|
|
/* f1a3994: 8fa30040 */ lw $v1,0x40($sp)
|
|
/* f1a3998: 8fae006c */ lw $t6,0x6c($sp)
|
|
/* f1a399c: 00408025 */ move $s0,$v0
|
|
/* f1a39a0: 24630001 */ addiu $v1,$v1,0x1
|
|
/* f1a39a4: 25cffffc */ addiu $t7,$t6,-4
|
|
/* f1a39a8: afaf006c */ sw $t7,0x6c($sp)
|
|
/* f1a39ac: 0c002eeb */ jal viGetWidth
|
|
/* f1a39b0: afa30068 */ sw $v1,0x68($sp)
|
|
/* f1a39b4: 00028c00 */ sll $s1,$v0,0x10
|
|
/* f1a39b8: 0011c403 */ sra $t8,$s1,0x10
|
|
/* f1a39bc: 0c002eef */ jal viGetHeight
|
|
/* f1a39c0: 03008825 */ move $s1,$t8
|
|
/* f1a39c4: 3c198008 */ lui $t9,0x8008
|
|
/* f1a39c8: 3c0a8008 */ lui $t2,0x8008
|
|
/* f1a39cc: 8d4a0148 */ lw $t2,0x148($t2)
|
|
/* f1a39d0: 8f39014c */ lw $t9,0x14c($t9)
|
|
/* f1a39d4: 8fab0044 */ lw $t3,0x44($sp)
|
|
/* f1a39d8: 8fac0048 */ lw $t4,0x48($sp)
|
|
/* f1a39dc: 02002025 */ move $a0,$s0
|
|
/* f1a39e0: 27a5006c */ addiu $a1,$sp,0x6c
|
|
/* f1a39e4: 27a60068 */ addiu $a2,$sp,0x68
|
|
/* f1a39e8: 8fa7008c */ lw $a3,0x8c($sp)
|
|
/* f1a39ec: afb10020 */ sw $s1,0x20($sp)
|
|
/* f1a39f0: afa20024 */ sw $v0,0x24($sp)
|
|
/* f1a39f4: afa00028 */ sw $zero,0x28($sp)
|
|
/* f1a39f8: afa0002c */ sw $zero,0x2c($sp)
|
|
/* f1a39fc: afaa0014 */ sw $t2,0x14($sp)
|
|
/* f1a3a00: afb90010 */ sw $t9,0x10($sp)
|
|
/* f1a3a04: afab0018 */ sw $t3,0x18($sp)
|
|
/* f1a3a08: 0fc55d34 */ jal func0f1574d0jf
|
|
/* f1a3a0c: afac001c */ sw $t4,0x1c($sp)
|
|
/* f1a3a10: 00408025 */ move $s0,$v0
|
|
.JF0f1a3a14:
|
|
/* f1a3a14: 8fbf003c */ lw $ra,0x3c($sp)
|
|
/* f1a3a18: 02001025 */ move $v0,$s0
|
|
/* f1a3a1c: 8fb00034 */ lw $s0,0x34($sp)
|
|
/* f1a3a20: 8fb10038 */ lw $s1,0x38($sp)
|
|
/* f1a3a24: 03e00008 */ jr $ra
|
|
/* f1a3a28: 27bd0078 */ addiu $sp,$sp,0x78
|
|
);
|
|
|
|
//Gfx *frRenderHudElement(Gfx *gdl, s32 x, s32 y, char *string1, char *string2, char *string3, u32 colour, u8 alpha)
|
|
//{
|
|
// s32 textheight; // 74
|
|
// s32 textwidth; // 70
|
|
// s32 x2; // 6c
|
|
// s32 y2; // 68
|
|
//
|
|
// u32 halfalpha = alpha >> 1;
|
|
// u32 fullcolour = (colour & 0xffffff00) | alpha;
|
|
//
|
|
// textMeasure(&textheight, &textwidth, string1, g_CharsHandelGothicMd, g_FontHandelGothicMd, 0);
|
|
//
|
|
// x2 = x - (textwidth >> 1);
|
|
// y2 = y;
|
|
// gdl = text0f153858(gdl, &x2, &y2, &textwidth, &textheight);
|
|
//
|
|
// gdl = textRender(gdl, &x2, &y2, string1,
|
|
// g_CharsHandelGothicMd, g_FontHandelGothicMd, fullcolour, halfalpha, viGetWidth(), viGetHeight(), 0, 0);
|
|
//
|
|
// if (string2) {
|
|
// s32 textheight2; // 5c
|
|
// s32 textwidth2; // 58
|
|
// s32 textheight3; // 54
|
|
// s32 textwidth3; // 50
|
|
//
|
|
// textMeasure(&textheight2, &textwidth2, string2, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
// textMeasure(&textheight3, &textwidth3, string3, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
//
|
|
// textwidth = textwidth2 + textwidth3;
|
|
// textheight = textheight3;
|
|
// x2 = x - (textwidth >> 1);
|
|
// y2 = y + 17;
|
|
//
|
|
// gdl = text0f153858(gdl, &x2, &y2, &textwidth, &textheight);
|
|
//
|
|
// gdl = func0f1574d0jf(gdl, &x2, &y2, string2,
|
|
// g_CharsHandelGothicXs, g_FontHandelGothicXs, fullcolour, halfalpha, viGetWidth(), viGetHeight(), 0, 0);
|
|
//
|
|
// x2 -= 4;
|
|
// y2 = y + 18;
|
|
//
|
|
// gdl = func0f1574d0jf(gdl, &x2, &y2, string3,
|
|
// g_CharsHandelGothicXs, g_FontHandelGothicXs, fullcolour, halfalpha, viGetWidth(), viGetHeight(), 0, 0);
|
|
// }
|
|
//
|
|
// return gdl;
|
|
//}
|
|
#else
|
|
Gfx *frRenderHudElement(Gfx *gdl, s32 x, s32 y, char *string1, char *string2, u32 colour, u8 alpha)
|
|
{
|
|
s32 textheight;
|
|
s32 textwidth;
|
|
s32 x2;
|
|
s32 y2;
|
|
|
|
u32 halfalpha = alpha >> 1;
|
|
u32 fullcolour = (colour & 0xffffff00) | alpha;
|
|
|
|
textMeasure(&textheight, &textwidth, string1, g_CharsHandelGothicMd, g_FontHandelGothicMd, 0);
|
|
|
|
x2 = x - (textwidth >> 1);
|
|
y2 = y;
|
|
gdl = text0f153858(gdl, &x2, &y2, &textwidth, &textheight);
|
|
|
|
gdl = textRender(gdl, &x2, &y2, string1,
|
|
g_CharsHandelGothicMd, g_FontHandelGothicMd, fullcolour, halfalpha, viGetWidth(), viGetHeight(), 0, 0);
|
|
|
|
if (string2) {
|
|
textMeasure(&textheight, &textwidth, string2, g_CharsHandelGothicXs, g_FontHandelGothicXs, 0);
|
|
|
|
x2 = x - (textwidth >> 1);
|
|
y2 = y + 17;
|
|
gdl = text0f153858(gdl, &x2, &y2, &textwidth, &textheight);
|
|
|
|
gdl = textRender(gdl, &x2, &y2, string2,
|
|
g_CharsHandelGothicXs, g_FontHandelGothicXs, fullcolour, halfalpha, viGetWidth(), viGetHeight(), 0, 0);
|
|
}
|
|
|
|
return gdl;
|
|
}
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
Gfx *frRenderHud(Gfx *gdl)
|
|
{
|
|
char string1[128];
|
|
char string2[128];
|
|
char string3[128];
|
|
bool red;
|
|
bool exists;
|
|
s32 alpha = 0xa0;
|
|
f32 mult;
|
|
|
|
if (viGetViewWidth() > 400) {
|
|
mult = 1.7f;
|
|
} else {
|
|
mult = 1;
|
|
}
|
|
|
|
if (!g_FrIsValidWeapon && g_FrData.menucountdown <= 0) {
|
|
return gdl;
|
|
}
|
|
|
|
if (g_FrData.menucountdown != 0) {
|
|
alpha = (f32)(g_FrData.menucountdown * 160) / TICKS(60.0f);
|
|
}
|
|
|
|
gdl = text0f153628(gdl);
|
|
|
|
// Time
|
|
red = frFormatTime(string1);
|
|
exists = frGetHudMiddleSubtext(string2, string3);
|
|
|
|
gdl = frRenderHudElement(gdl, viGetViewWidth() >> 1, viGetViewTop() + 12,
|
|
string1,
|
|
exists ? string2 : NULL,
|
|
exists ? string3 : NULL,
|
|
red ? 0xff4444ff : 0x00ff00a0,
|
|
alpha);
|
|
|
|
// Score
|
|
frGetScoreValue(string1);
|
|
frGetGoalScoreText(string2, string3);
|
|
gdl = frRenderHudElement(gdl, viGetViewLeft() + 65.0f * mult, viGetViewTop() + 12,
|
|
string1, string2, string3, 0x00ff00a0, alpha);
|
|
|
|
// Feedback
|
|
if (frGetFeedback(string1, string2, string3)) {
|
|
gdl = frRenderHudElement(gdl,viGetViewLeft() + 65.0f * mult, viGetViewTop() + 48,
|
|
string1, string2, string3, 0x00ff00a0, alpha);
|
|
}
|
|
|
|
if (g_FrData.goalaccuracy > 0) {
|
|
red = frGetMinAccuracy(string2, frGetAccuracy(string1), string3);
|
|
|
|
gdl = frRenderHudElement(gdl, viGetViewLeft() + viGetViewWidth() - 70.0f * mult, viGetViewTop() + 12,
|
|
string1, string2, string3,
|
|
red ? 0xff4444ff : 0x00ff00a0,
|
|
alpha);
|
|
} else if (g_FrData.goaltargets != 255) {
|
|
frGetTargetsDestroyedValue(string1);
|
|
frGetGoalTargetsText(string2, string3);
|
|
|
|
if (mult == 2) {
|
|
mult = 2.4;
|
|
}
|
|
|
|
gdl = frRenderHudElement(gdl, viGetViewLeft() + viGetViewWidth() - 70.0f * mult, viGetViewTop() + 12,
|
|
string1, string2, string3, 0x00ff00a0, alpha);
|
|
}
|
|
|
|
return text0f153780(gdl);
|
|
}
|
|
#else
|
|
Gfx *frRenderHud(Gfx *gdl)
|
|
{
|
|
char string1[128];
|
|
char string2[128];
|
|
bool red;
|
|
bool exists;
|
|
s32 alpha = 0xa0;
|
|
f32 mult;
|
|
|
|
if (viGetViewWidth() > (VERSION >= VERSION_PAL_FINAL ? 330 : 400)) {
|
|
mult = VERSION >= VERSION_PAL_FINAL ? 1.5f : 2;
|
|
} else {
|
|
mult = 1;
|
|
}
|
|
|
|
if (!g_FrIsValidWeapon && g_FrData.menucountdown <= 0) {
|
|
return gdl;
|
|
}
|
|
|
|
if (g_FrData.menucountdown != 0) {
|
|
alpha = (f32)(g_FrData.menucountdown * 160) / TICKS(60.0f);
|
|
}
|
|
|
|
gdl = text0f153628(gdl);
|
|
|
|
// Time
|
|
red = frFormatTime(string1);
|
|
exists = frGetHudMiddleSubtext(string2);
|
|
|
|
gdl = frRenderHudElement(gdl, viGetViewWidth() >> 1, viGetViewTop() + 12,
|
|
string1, exists ? string2 : NULL,
|
|
red ? 0xff0000a0 : 0x00ff00a0,
|
|
alpha);
|
|
|
|
// Score
|
|
frGetScoreValue(string1);
|
|
frGetGoalScoreText(string2);
|
|
gdl = frRenderHudElement(gdl, viGetViewLeft() + 65.0f * mult, viGetViewTop() + 12,
|
|
string1, string2, 0x00ff00a0, alpha);
|
|
|
|
// Feedback
|
|
if (frGetFeedback(string1, string2)) {
|
|
gdl = frRenderHudElement(gdl,viGetViewLeft() + 65.0f * mult, viGetViewTop() + 40,
|
|
string1, string2, 0x00ff00a0, alpha);
|
|
}
|
|
|
|
if (g_FrData.goalaccuracy > 0) {
|
|
red = frGetMinAccuracy(string2, frGetAccuracy(string1));
|
|
|
|
gdl = frRenderHudElement(gdl, viGetViewLeft() + viGetViewWidth() - 70.0f * mult, viGetViewTop() + 12,
|
|
string1, string2,
|
|
red ? 0xff0000a0 : 0x00ff00a0,
|
|
alpha);
|
|
} else if (g_FrData.goaltargets != 255) {
|
|
frGetTargetsDestroyedValue(string1);
|
|
frGetGoalTargetsText(string2);
|
|
|
|
if (mult == 2) {
|
|
mult = 2.4;
|
|
}
|
|
|
|
gdl = frRenderHudElement(gdl, viGetViewLeft() + viGetViewWidth() - 70.0f * mult, viGetViewTop() + 12,
|
|
string1, string2, 0x00ff00a0, alpha);
|
|
}
|
|
|
|
return text0f153780(gdl);
|
|
}
|
|
#endif
|