mirror of
https://github.com/perfect-dark-pc-port/perfect_dark.git
synced 2026-10-24 08:12:41 +02:00
Add the "Skedar Leader" track to the Combat Simulator Soundtrack selection menu as "Skedar Warrior" as we do not currently have the ability to use the official track name
4004 lines
116 KiB
C
4004 lines
116 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/camdraw.h"
|
|
#include "game/title.h"
|
|
#include "game/pdmode.h"
|
|
#include "game/bondgun.h"
|
|
#include "game/game_0b0fd0.h"
|
|
#include "game/player.h"
|
|
#include "game/savebuffer.h"
|
|
#include "game/menu.h"
|
|
#include "game/playermgr.h"
|
|
#include "game/game_1531a0.h"
|
|
#include "game/lv.h"
|
|
#include "game/music.h"
|
|
#include "game/mplayer/setup.h"
|
|
#include "game/mplayer/scenarios.h"
|
|
#include "game/mpstats.h"
|
|
#include "game/challenge.h"
|
|
#include "game/lang.h"
|
|
#include "game/mplayer/mplayer.h"
|
|
#include "game/pak.h"
|
|
#include "bss.h"
|
|
#include "lib/args.h"
|
|
#include "lib/vi.h"
|
|
#include "lib/main.h"
|
|
#include "lib/rng.h"
|
|
#include "lib/str.h"
|
|
#include "lib/lib_317f0.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
// bss
|
|
struct chrdata *g_MpAllChrPtrs[MAX_MPCHRS];
|
|
struct mpchrconfig *g_MpAllChrConfigPtrs[MAX_MPCHRS];
|
|
s32 g_MpNumChrs;
|
|
u32 var800ac534;
|
|
struct mpbotconfig g_BotConfigsArray[MAX_BOTS];
|
|
u8 g_MpSimulantDifficultiesPerNumPlayers[MAX_BOTS][MAX_PLAYERS];
|
|
struct mpplayerconfig g_PlayerConfigsArray[MAX_MPPLAYERCONFIGS];
|
|
u8 g_AmBotCommands[9];
|
|
struct mpsetup g_MpSetup;
|
|
struct bossfile g_BossFile;
|
|
u32 var800acc1c;
|
|
struct mplockinfo g_MpLockInfo;
|
|
struct modeldef *var800acc28[18];
|
|
|
|
// Forward declaractions
|
|
struct mpweaponset g_MpWeaponSets[12];
|
|
s32 g_MpWeaponSetNum;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
const char var7f1b8a00[] = "||||||||||||| Starting game... players %d\n";
|
|
#endif
|
|
|
|
s32 var80087260 = 0x00000000;
|
|
bool g_MpEnableMusicSwitching = false;
|
|
|
|
struct mpweapon g_MpWeapons[NUM_MPWEAPONS] = {
|
|
/*0x00*/ { WEAPON_NONE, 0, 0, 0, 0, 1, 0, MODEL_CHRTT33, 256 },
|
|
/*0x01*/ { WEAPON_FALCON2, AMMOTYPE_PISTOL, 80, 0, 0, 1, 0, MODEL_CHRFALCON2, 256 },
|
|
/*0x02*/ { WEAPON_FALCON2_SILENCER, AMMOTYPE_PISTOL, 80, 0, 0, 1, MPFEATURE_WEAPON_FALCON2SILENCED, MODEL_CHRFALCON2SIL, 256 },
|
|
/*0x03*/ { WEAPON_FALCON2_SCOPE, AMMOTYPE_PISTOL, 80, 0, 0, 1, MPFEATURE_WEAPON_FALCON2SCOPE, MODEL_CHRFALCON2SCOPE, 256 },
|
|
/*0x04*/ { WEAPON_MAGSEC4, AMMOTYPE_PISTOL, 80, 0, 0, 1, 0, MODEL_CHRLEEGUN1, 256 },
|
|
/*0x05*/ { WEAPON_MAULER, AMMOTYPE_PISTOL, 92, 0, 0, 1, MPFEATURE_WEAPON_MAULER, MODEL_CHRMAULER, 256 },
|
|
/*0x06*/ { WEAPON_PHOENIX, AMMOTYPE_PISTOL, 64, 0, 0, 1, MPFEATURE_WEAPON_PHOENIX, MODEL_CHRMAIANPISTOL, 256 },
|
|
/*0x07*/ { WEAPON_DY357MAGNUM, AMMOTYPE_MAGNUM, 50, 0, 0, 1, 0, MODEL_CHRDY357, 256 },
|
|
/*0x08*/ { WEAPON_DY357LX, AMMOTYPE_MAGNUM, 50, 0, 0, 1, MPFEATURE_WEAPON_DY357LX, MODEL_CHRDY357TRENT, 256 },
|
|
/*0x09*/ { WEAPON_CMP150, AMMOTYPE_SMG, 100, 0, 0, 1, 0, MODEL_CHRCMP150, 256 },
|
|
/*0x0a*/ { WEAPON_CYCLONE, AMMOTYPE_SMG, 150, 0, 0, 1, 0, MODEL_CHRCYCLONE, 256 },
|
|
/*0x0b*/ { WEAPON_CALLISTO, AMMOTYPE_SMG, 150, 0, 0, 1, MPFEATURE_WEAPON_CALLISTO, MODEL_CHRMAIANSMG, 256 },
|
|
/*0x0c*/ { WEAPON_RCP120, AMMOTYPE_SMG, 150, 0, 0, 1, MPFEATURE_WEAPON_RCP120, MODEL_CHRRCP120, 256 },
|
|
/*0x0d*/ { WEAPON_LAPTOPGUN, AMMOTYPE_SMG, 150, 0, 0, 1, MPFEATURE_WEAPON_LAPTOPGUN, MODEL_CHRPCGUN, 256 },
|
|
/*0x0e*/ { WEAPON_DRAGON, AMMOTYPE_RIFLE, 150, 0, 0, 1, 0, MODEL_CHRDRAGON, 256 },
|
|
/*0x0f*/ { WEAPON_K7AVENGER, AMMOTYPE_RIFLE, 150, 0, 0, 1, MPFEATURE_WEAPON_K7AVENGER, MODEL_CHRAVENGER, 256 },
|
|
/*0x10*/ { WEAPON_AR34, AMMOTYPE_RIFLE, 100, 0, 0, 1, 0, MODEL_CHRAR34, 256 },
|
|
/*0x11*/ { WEAPON_SUPERDRAGON, AMMOTYPE_RIFLE, 150, AMMOTYPE_DEVASTATOR, 16, 1, MPFEATURE_WEAPON_SUPERDRAGON, MODEL_CHRSUPERDRAGON, 256 },
|
|
/*0x12*/ { WEAPON_SHOTGUN, AMMOTYPE_SHOTGUN, 16, 0, 0, 1, MPFEATURE_WEAPON_SHOTGUN, MODEL_CHRSHOTGUN, 256 },
|
|
/*0x13*/ { WEAPON_REAPER, AMMOTYPE_REAPER, 200, 0, 0, 1, MPFEATURE_WEAPON_REAPER, MODEL_CHRSKMINIGUN, 256 },
|
|
/*0x14*/ { WEAPON_SNIPERRIFLE, AMMOTYPE_RIFLE, 50, 0, 0, 1, 0, MODEL_CHRSNIPERRIFLE, 256 },
|
|
/*0x15*/ { WEAPON_FARSIGHT, AMMOTYPE_FARSIGHT, 10, 0, 0, 1, MPFEATURE_WEAPON_FARSIGHT, MODEL_CHRZ2020, 256 },
|
|
/*0x16*/ { WEAPON_DEVASTATOR, AMMOTYPE_DEVASTATOR, 16, 0, 0, 1, MPFEATURE_WEAPON_DEVASTATOR, MODEL_CHRDEVASTATOR, 256 },
|
|
/*0x17*/ { WEAPON_ROCKETLAUNCHER, AMMOTYPE_ROCKET, 3, 0, 0, 1, 0, MODEL_CHRDYROCKET, 256 },
|
|
/*0x18*/ { WEAPON_SLAYER, AMMOTYPE_ROCKET, 3, 0, 0, 1, MPFEATURE_WEAPON_SLAYER, MODEL_CHRSKROCKET, 256 },
|
|
#if !((VERSION == VERSION_JPN_FINAL) && defined(PLATFORM_N64))
|
|
/*0x19*/ { WEAPON_COMBATKNIFE, AMMOTYPE_KNIFE, 5, 0, 0, 1, 0, MODEL_CHRKNIFE, 256 },
|
|
#endif
|
|
/*0x1a*/ { WEAPON_CROSSBOW, AMMOTYPE_CROSSBOW, 10, 0, 0, 1, MPFEATURE_WEAPON_CROSSBOW, MODEL_CHRCROSSBOW, 256 },
|
|
/*0x1b*/ { WEAPON_TRANQUILIZER, AMMOTYPE_SEDATIVE, 50, 0, 0, 1, MPFEATURE_WEAPON_TRANQUILIZER, MODEL_CHRDRUGGUN, 256 },
|
|
/*0x1c*/ { WEAPON_GRENADE, AMMOTYPE_GRENADE, 5, 0, 0, 0, 0, MODEL_CHRGRENADE, 256 },
|
|
/*0x1d*/ { WEAPON_NBOMB, AMMOTYPE_NBOMB, 3, 0, 0, 0, MPFEATURE_WEAPON_NBOMB, MODEL_CHRNBOMB, 256 },
|
|
/*0x1e*/ { WEAPON_TIMEDMINE, AMMOTYPE_TIMED_MINE, 5, 0, 0, 0, 0, MODEL_CHRTIMEDMINE, 384 },
|
|
/*0x1f*/ { WEAPON_PROXIMITYMINE, AMMOTYPE_PROXY_MINE, 5, 0, 0, 0, MPFEATURE_WEAPON_PROXIMITYMINE, MODEL_CHRPROXIMITYMINE, 384 },
|
|
/*0x20*/ { WEAPON_REMOTEMINE, AMMOTYPE_REMOTE_MINE, 5, 0, 0, 0, MPFEATURE_WEAPON_REMOTEMINE, MODEL_CHRREMOTEMINE, 384 },
|
|
/*0x21*/ { WEAPON_LASER, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_LASER, MODEL_CHRLASER, 512 },
|
|
/*0x22*/ { WEAPON_XRAYSCANNER, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_XRAYSCANNER, MODEL_CHRNIGHTSIGHT, 256 },
|
|
/*0x23*/ { WEAPON_CLOAKINGDEVICE, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_CLOAKINGDEVICE, MODEL_CHRCLOAKER, 256 },
|
|
/*0x24*/ { WEAPON_COMBATBOOST, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_COMBATBOOST, MODEL_CHRSPEEDPILL, 256 },
|
|
#ifndef PLATFORM_N64
|
|
/*0x25*/ { WEAPON_PP9I, AMMOTYPE_PISTOL, 80, 0, 0, 1, 0, MODEL_CHRWPPK, 256 },
|
|
/*0x26*/ { WEAPON_CC13, AMMOTYPE_PISTOL, 80, 0, 0, 1, 0, MODEL_CHRTT33, 256 },
|
|
/*0x27*/ { WEAPON_KL01313, AMMOTYPE_SMG, 100, 0, 0, 1, 0, MODEL_CHRSKORPION, 256 },
|
|
/*0x28*/ { WEAPON_KF7SPECIAL, AMMOTYPE_RIFLE, 100, 0, 0, 1, 0, MODEL_CHRKALASH, 256 },
|
|
/*0x29*/ { WEAPON_ZZT, AMMOTYPE_SMG, 100, 0, 0, 1, 0, MODEL_CHRUZI, 256 },
|
|
/*0x2a*/ { WEAPON_DMC, AMMOTYPE_SMG, 100, 0, 0, 1, 0, MODEL_CHRMP5K, 256 },
|
|
/*0x2b*/ { WEAPON_AR53, AMMOTYPE_RIFLE, 150, 0, 0, 1, 0, MODEL_CHRM16, 256 },
|
|
/*0x2c*/ { WEAPON_RCP45, AMMOTYPE_SMG, 150, 0, 0, 1, 0, MODEL_CHRFNP90, 256 },
|
|
#endif
|
|
/*0x2d*/ { WEAPON_MPSHIELD, 0, 0, 0, 0, 1, MPFEATURE_WEAPON_SHIELD, MODEL_CHRSHIELD, 256 },
|
|
/*0x2e*/ { WEAPON_DISABLED }, // 0x25 on N64
|
|
};
|
|
|
|
#ifndef PLATFORM_N64
|
|
|
|
#define PLAYER_EXT_CFG_DEFAULT { \
|
|
.fovy = 60.f, \
|
|
.fovzoommult = 1.f, \
|
|
.fovzoom = true, \
|
|
.mouseaimmode = MOUSEAIM_CLASSIC, \
|
|
.mouseaimspeedx = 0.7f, \
|
|
.mouseaimspeedy = 0.7f, \
|
|
.radialmenuspeed = 4.f, \
|
|
.crosshairsway = 1.f, \
|
|
.crouchmode = CROUCHMODE_TOGGLE_ANALOG, \
|
|
.extcontrols = true, \
|
|
}
|
|
|
|
struct extplayerconfig g_PlayerExtCfg[MAX_PLAYERS] = {
|
|
PLAYER_EXT_CFG_DEFAULT,
|
|
PLAYER_EXT_CFG_DEFAULT,
|
|
PLAYER_EXT_CFG_DEFAULT,
|
|
PLAYER_EXT_CFG_DEFAULT,
|
|
};
|
|
|
|
#endif
|
|
|
|
/**
|
|
* Converts the given value into a float on a curved scale from 0.1 to 10.
|
|
*
|
|
* value 0 will return 0.1
|
|
* value 127 will return 1
|
|
* value 255 will return 10
|
|
*/
|
|
f32 mpHandicapToDamageScale(u8 value)
|
|
{
|
|
f32 tmp;
|
|
|
|
if (value < 127) {
|
|
return (value / 127.0f) * (value / 127.0f) * 0.9f + 0.1f;
|
|
}
|
|
|
|
if (value == 127) {
|
|
return 1;
|
|
}
|
|
|
|
tmp = (value - 128) / 127.0f + 1;
|
|
|
|
return tmp * tmp * 3 - 2;
|
|
}
|
|
|
|
void func0f187838(struct mpchrconfig *mpchr)
|
|
{
|
|
s32 i = 0;
|
|
|
|
while (i < ARRAYCOUNT(mpchr->killcounts)) {
|
|
mpchr->killcounts[i++] = 0;
|
|
}
|
|
|
|
mpchr->numdeaths = 0;
|
|
mpchr->numpoints = 0;
|
|
mpchr->unk40 = 0;
|
|
}
|
|
|
|
void mpStartMatch(void)
|
|
{
|
|
s32 i;
|
|
s32 numplayers = 0;
|
|
s32 stagenum;
|
|
|
|
mpConfigureQuickTeamSimulants();
|
|
|
|
if (!challengeIsFeatureUnlocked(MPFEATURE_ONEHITKILLS)) {
|
|
g_MpSetup.options &= ~MPOPTION_ONEHITKILLS;
|
|
}
|
|
|
|
if (!challengeIsFeatureUnlocked(MPFEATURE_SLOWMOTION)) {
|
|
g_MpSetup.options &= ~(MPOPTION_SLOWMOTION_ON | MPOPTION_SLOWMOTION_SMART);
|
|
}
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
numplayers++;
|
|
}
|
|
}
|
|
|
|
stagenum = g_MpSetup.stagenum;
|
|
|
|
if (g_MpSetup.stagenum == STAGE_MP_RANDOM) {
|
|
stagenum = mpChooseRandomStage();
|
|
}
|
|
|
|
titleSetNextStage(stagenum);
|
|
mainChangeToStage(stagenum);
|
|
setNumPlayers(numplayers);
|
|
titleSetNextMode(TITLEMODE_SKIP);
|
|
|
|
g_Vars.perfectbuddynum = 1;
|
|
}
|
|
|
|
void mpReset(void)
|
|
{
|
|
s32 i;
|
|
s32 mpindex = 0;
|
|
|
|
g_MpNumChrs = 0;
|
|
g_Vars.mplayerisrunning = true;
|
|
|
|
if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) {
|
|
g_Vars.normmplayerisrunning = false;
|
|
} else {
|
|
g_Vars.normmplayerisrunning = true;
|
|
}
|
|
|
|
g_Vars.perfectbuddynum = 0;
|
|
|
|
if (mpHasSimulants()) {
|
|
g_Vars.lvmpbotlevel = true;
|
|
}
|
|
|
|
if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) {
|
|
struct mpplayerconfig tmp;
|
|
|
|
tmp = g_PlayerConfigsArray[MAX_PLAYERS];
|
|
g_PlayerConfigsArray[MAX_PLAYERS] = g_PlayerConfigsArray[0];
|
|
g_PlayerConfigsArray[0] = tmp;
|
|
|
|
tmp = g_PlayerConfigsArray[MAX_PLAYERS + 1];
|
|
g_PlayerConfigsArray[MAX_PLAYERS + 1] = g_PlayerConfigsArray[1];
|
|
g_PlayerConfigsArray[1] = tmp;
|
|
|
|
// Player index 0
|
|
g_Vars.playerstats[0].mpindex = 0;
|
|
|
|
g_PlayerConfigsArray[0].contpad1 = 0;
|
|
g_PlayerConfigsArray[0].contpad2 = 2;
|
|
|
|
if ((g_Vars.coopplayernum >= 0 && g_Vars.coopradaron)
|
|
|| (g_Vars.antiplayernum >= 0 && g_Vars.antiradaron)) {
|
|
g_PlayerConfigsArray[0].base.displayoptions |= MPDISPLAYOPTION_RADAR;
|
|
} else {
|
|
g_PlayerConfigsArray[0].base.displayoptions &= ~MPDISPLAYOPTION_RADAR;
|
|
}
|
|
|
|
// Player index 1
|
|
g_Vars.playerstats[1].mpindexu32 = 1;
|
|
|
|
g_PlayerConfigsArray[1].contpad1 = 1;
|
|
g_PlayerConfigsArray[1].contpad2 = 3;
|
|
|
|
if ((g_Vars.coopplayernum >= 0 && g_Vars.coopradaron)
|
|
|| (g_Vars.antiplayernum >= 0 && g_Vars.antiradaron)) {
|
|
g_PlayerConfigsArray[1].base.displayoptions |= MPDISPLAYOPTION_RADAR;
|
|
} else {
|
|
g_PlayerConfigsArray[1].base.displayoptions &= ~MPDISPLAYOPTION_RADAR;
|
|
}
|
|
|
|
g_MpNumChrs = 2;
|
|
} else {
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
g_Vars.playerstats[mpindex].mpindex = i;
|
|
|
|
g_PlayerConfigsArray[i].contpad1 = i;
|
|
g_PlayerConfigsArray[i].contpad2 = 0;
|
|
|
|
mpCalculatePlayerTitle(&g_PlayerConfigsArray[i]);
|
|
|
|
g_PlayerConfigsArray[i].newtitle = g_PlayerConfigsArray[i].title;
|
|
g_MpNumChrs++;
|
|
mpindex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
struct mpchrconfig *mpchr = MPCHR(i);
|
|
|
|
func0f187838(mpchr);
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
g_MpAllChrPtrs[i] = NULL;
|
|
#endif
|
|
}
|
|
|
|
g_MpSetup.paused = false;
|
|
|
|
g_MpEnableMusicSwitching = false;
|
|
|
|
if (g_Vars.normmplayerisrunning) {
|
|
if (mpGetUsingMultipleTunes()) {
|
|
s32 i = 0;
|
|
s32 count = 0;
|
|
s32 numtracks = mpGetNumUnlockedTracks();
|
|
|
|
for (i; i < numtracks && count < 2; i++) {
|
|
if (mpIsMultiTrackSlotEnabled(i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (count >= 2) {
|
|
g_MpEnableMusicSwitching = true;
|
|
}
|
|
}
|
|
|
|
g_MpLockInfo.unk04 = -1;
|
|
}
|
|
|
|
// Assign aibot commands to active menu slots
|
|
for (i = 0; i < ARRAYCOUNT(g_AmBotCommands); i++) {
|
|
g_AmBotCommands[i] = AIBOTCMD_NORMAL;
|
|
}
|
|
|
|
g_AmBotCommands[1] = AIBOTCMD_ATTACK;
|
|
g_AmBotCommands[7] = AIBOTCMD_NORMAL;
|
|
g_AmBotCommands[0] = AIBOTCMD_FOLLOW;
|
|
g_AmBotCommands[2] = AIBOTCMD_PROTECT;
|
|
g_AmBotCommands[3] = AIBOTCMD_DEFEND;
|
|
g_AmBotCommands[5] = AIBOTCMD_HOLD;
|
|
|
|
switch (g_MpSetup.scenario) {
|
|
case MPSCENARIO_CAPTURETHECASE:
|
|
g_AmBotCommands[3] = AIBOTCMD_GETCASE;
|
|
g_AmBotCommands[5] = AIBOTCMD_SAVECASE;
|
|
break;
|
|
case MPSCENARIO_KINGOFTHEHILL:
|
|
g_AmBotCommands[6] = AIBOTCMD_DEFHILL;
|
|
g_AmBotCommands[8] = AIBOTCMD_HOLDHILL;
|
|
break;
|
|
case MPSCENARIO_HACKERCENTRAL:
|
|
g_AmBotCommands[6] = AIBOTCMD_DOWNLOAD;
|
|
g_AmBotCommands[8] = AIBOTCMD_DOWNLOAD;
|
|
break;
|
|
case MPSCENARIO_POPACAP:
|
|
g_AmBotCommands[6] = AIBOTCMD_POPCAP;
|
|
g_AmBotCommands[8] = AIBOTCMD_POPCAP;
|
|
break;
|
|
case MPSCENARIO_HOLDTHEBRIEFCASE:
|
|
g_AmBotCommands[6] = AIBOTCMD_GETCASE2;
|
|
g_AmBotCommands[8] = AIBOTCMD_GETCASE2;
|
|
break;
|
|
default:
|
|
g_AmBotCommands[0] = AIBOTCMD_FOLLOW;
|
|
g_AmBotCommands[1] = AIBOTCMD_ATTACK;
|
|
g_AmBotCommands[2] = AIBOTCMD_PROTECT;
|
|
g_AmBotCommands[3] = AIBOTCMD_DEFEND;
|
|
g_AmBotCommands[5] = AIBOTCMD_HOLD;
|
|
g_AmBotCommands[7] = AIBOTCMD_NORMAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void mpCalculateTeamIsOnlyAi(void)
|
|
{
|
|
s32 playercount = PLAYERCOUNT();
|
|
s32 i;
|
|
s32 j;
|
|
|
|
// Iterate simulants, which go after players in the g_MpAllChrPtrs array
|
|
for (i = playercount; i < g_MpNumChrs; i++) {
|
|
if (!g_MpAllChrPtrs[i]) {
|
|
continue;
|
|
}
|
|
|
|
g_MpAllChrPtrs[i]->aibot->teamisonlyai = true;
|
|
|
|
if (g_MpSetup.options & MPOPTION_TEAMSENABLED) {
|
|
// Iterate human players
|
|
for (j = 0; j < playercount; j++) {
|
|
if (g_MpAllChrPtrs[i]->team == g_MpAllChrPtrs[j]->team) {
|
|
g_MpAllChrPtrs[i]->aibot->teamisonlyai = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void func0f187fbc(s32 playernum)
|
|
{
|
|
g_PlayerConfigsArray[playernum].base.unk18 = 80;
|
|
g_PlayerConfigsArray[playernum].base.unk1a = 80;
|
|
g_PlayerConfigsArray[playernum].base.unk1c = 75;
|
|
}
|
|
|
|
void func0f187fec(void)
|
|
{
|
|
g_MpSetup.timelimit = 9;
|
|
g_MpSetup.scorelimit = 9;
|
|
g_MpSetup.teamscorelimit = 19;
|
|
}
|
|
|
|
void mpPlayerSetDefaults(s32 playernum, bool autonames)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
|
|
func0f187fbc(playernum);
|
|
|
|
g_PlayerConfigsArray[playernum].controlmode = CONTROLMODE_11;
|
|
|
|
#ifndef PLATFORM_N64
|
|
if (g_PlayerExtCfg[playernum % MAX_PLAYERS].extcontrols) {
|
|
g_PlayerConfigsArray[playernum].controlmode = CONTROLMODE_PC;
|
|
}
|
|
#endif
|
|
|
|
g_PlayerConfigsArray[playernum].options =
|
|
#ifdef PLATFORM_N64
|
|
OPTION_LOOKAHEAD
|
|
#else
|
|
OPTION_FORWARDPITCH
|
|
#endif
|
|
| OPTION_SIGHTONSCREEN
|
|
| OPTION_AUTOAIM
|
|
| OPTION_AMMOONSCREEN
|
|
| OPTION_SHOWGUNFUNCTION
|
|
| OPTION_HEADROLL
|
|
| OPTION_0100
|
|
| OPTION_ALWAYSSHOWTARGET
|
|
| OPTION_SHOWZOOMRANGE;
|
|
|
|
g_PlayerConfigsArray[playernum].handicap = 128;
|
|
|
|
switch (playernum) {
|
|
case 0:
|
|
default:
|
|
g_PlayerConfigsArray[playernum].base.mpbodynum = MPBODY_DARK_COMBAT;
|
|
break;
|
|
case 1:
|
|
g_PlayerConfigsArray[playernum].base.mpbodynum = MPBODY_CASSANDRA;
|
|
break;
|
|
case 2:
|
|
g_PlayerConfigsArray[playernum].base.mpbodynum = MPBODY_CARRINGTON;
|
|
break;
|
|
case 3:
|
|
g_PlayerConfigsArray[playernum].base.mpbodynum = MPBODY_CILABTECH;
|
|
break;
|
|
}
|
|
|
|
g_PlayerConfigsArray[playernum].base.mpheadnum = mpGetMpheadnumByMpbodynum(g_PlayerConfigsArray[playernum].base.mpbodynum);
|
|
g_PlayerConfigsArray[playernum].base.displayoptions = MPDISPLAYOPTION_RADAR | MPDISPLAYOPTION_HIGHLIGHTTEAMS;
|
|
g_PlayerConfigsArray[playernum].fileguid.fileid = 0;
|
|
g_PlayerConfigsArray[playernum].fileguid.deviceserial = 0;
|
|
|
|
if (autonames) {
|
|
// "Player 1" etc
|
|
sprintf(g_PlayerConfigsArray[playernum].base.name, "%s %d\n", langGet(L_MISC_437), playernum + 1);
|
|
} else {
|
|
g_PlayerConfigsArray[playernum].base.name[0] = '\0';
|
|
}
|
|
|
|
g_PlayerConfigsArray[playernum].kills = 0;
|
|
g_PlayerConfigsArray[playernum].deaths = 0;
|
|
g_PlayerConfigsArray[playernum].gamesplayed = 0;
|
|
g_PlayerConfigsArray[playernum].gameswon = 0;
|
|
g_PlayerConfigsArray[playernum].gameslost = 0;
|
|
g_PlayerConfigsArray[playernum].time = 0;
|
|
g_PlayerConfigsArray[playernum].distance = 0;
|
|
g_PlayerConfigsArray[playernum].accuracy = 1000;
|
|
g_PlayerConfigsArray[playernum].damagedealt = 0;
|
|
g_PlayerConfigsArray[playernum].painreceived = 0;
|
|
g_PlayerConfigsArray[playernum].headshots = 0;
|
|
g_PlayerConfigsArray[playernum].ammoused = 0;
|
|
g_PlayerConfigsArray[playernum].accuracymedals = 0;
|
|
g_PlayerConfigsArray[playernum].headshotmedals = 0;
|
|
g_PlayerConfigsArray[playernum].killmastermedals = 0;
|
|
g_PlayerConfigsArray[playernum].survivormedals = 0;
|
|
g_PlayerConfigsArray[playernum].title = MPPLAYERTITLE_BEGINNER;
|
|
|
|
if (playernum < MAX_PLAYERS) {
|
|
for (i = 0; i < ARRAYCOUNT(g_MpChallenges); i++) {
|
|
for (j = 1; j <= MAX_PLAYERS; j++) {
|
|
challengeSetCompletedByPlayerWithNumPlayers(playernum, i, j, false);
|
|
}
|
|
}
|
|
|
|
challengeDetermineUnlockedFeatures();
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_PlayerConfigsArray); i++) {
|
|
g_PlayerConfigsArray[playernum].gunfuncs[i] = 0;
|
|
}
|
|
}
|
|
|
|
void func0f1881d4(s32 index)
|
|
{
|
|
g_BotConfigsArray[index].base.name[0] = '\0';
|
|
g_BotConfigsArray[index].base.mpheadnum = MPHEAD_DARK_COMBAT;
|
|
g_BotConfigsArray[index].base.mpbodynum = MPBODY_DARK_COMBAT;
|
|
g_BotConfigsArray[index].type = BOTTYPE_GENERAL;
|
|
g_BotConfigsArray[index].difficulty = BOTDIFF_DISABLED;
|
|
}
|
|
|
|
void mpInit(void)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
|
|
g_MpSetup.scenario = MPSCENARIO_COMBAT;
|
|
g_MpSetup.stagenum = STAGE_MP_SKEDAR;
|
|
g_MpSetup.options = MPOPTION_DISPLAYTEAM
|
|
| MPOPTION_KILLSSCORE
|
|
| MPOPTION_HTB_HIGHLIGHTBRIEFCASE
|
|
| MPOPTION_HTB_SHOWONRADAR
|
|
| MPOPTION_CTC_SHOWONRADAR
|
|
| MPOPTION_KOH_HILLONRADAR
|
|
| MPOPTION_KOH_MOBILEHILL
|
|
| MPOPTION_00010000
|
|
| MPOPTION_HTM_HIGHLIGHTTERMINAL
|
|
| MPOPTION_HTM_SHOWONRADAR
|
|
| MPOPTION_PAC_HIGHLIGHTTARGET
|
|
| MPOPTION_PAC_SHOWONRADAR;
|
|
|
|
g_Vars.mphilltime = 10;
|
|
|
|
func0f187fec();
|
|
|
|
g_MpSetup.fileguid.fileid = 0;
|
|
g_MpSetup.fileguid.deviceserial = 0;
|
|
|
|
strcpy(g_MpSetup.name, "");
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_PlayerConfigsArray); i++) {
|
|
mpPlayerSetDefaults(i, false);
|
|
}
|
|
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
func0f1881d4(i);
|
|
}
|
|
|
|
if (argFindByPrefix(1, "-mpwpnset")) {
|
|
char *value = argFindByPrefix(1, "-mpwpnset");
|
|
mpSetWeaponSet(*value - '0');
|
|
} else {
|
|
mpSetWeaponSet(0);
|
|
}
|
|
|
|
g_Vars.mplayerisrunning = false;
|
|
g_Vars.normmplayerisrunning = false;
|
|
g_Vars.lvmpbotlevel = 0;
|
|
|
|
g_MpLockInfo.lockedplayernum = 0;
|
|
g_MpLockInfo.lastwinner = -1;
|
|
g_MpLockInfo.lastloser = -1;
|
|
g_MpLockInfo.unk03 = -1;
|
|
g_MpLockInfo.unk04 = -1;
|
|
|
|
challengeForceUnlockBotFeatures();
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_PlayerConfigsArray); i++) {
|
|
for (j = 0; j < ARRAYCOUNT(g_PlayerConfigsArray[i].gunfuncs); j++) {
|
|
g_PlayerConfigsArray[i].gunfuncs[j] = 0;
|
|
}
|
|
}
|
|
|
|
g_MpSetup.chrslots = 0;
|
|
}
|
|
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
void mpGetTeamsWithDefaultName(u8 *mask)
|
|
{
|
|
s32 i;
|
|
|
|
*mask = 0;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_BossFile.teamnames); i++) {
|
|
if (strcmp(g_BossFile.teamnames[i], langGet(L_OPTIONS_008 + i)) == 0) {
|
|
*mask |= 1 << i;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_PAL_BETA
|
|
void mpSetTeamNamesToDefault(u8 mask)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_BossFile.teamnames); i++) {
|
|
if (mask & (1 << i)) {
|
|
strcpy(g_BossFile.teamnames[i], langGet(L_OPTIONS_008 + i));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void mpSetDefaultNamesIfEmpty(void)
|
|
{
|
|
s32 i;
|
|
|
|
// Setup file name
|
|
if (g_MpSetup.name[0] == '\0') {
|
|
strcpy(g_MpSetup.name, langGet(L_MISC_438)); // empty string
|
|
}
|
|
|
|
// Team names
|
|
for (i = 0; i < ARRAYCOUNT(g_BossFile.teamnames); i++) {
|
|
if (g_BossFile.teamnames[i][0] == '\0') {
|
|
strcpy(g_BossFile.teamnames[i], langGet(L_OPTIONS_008 + i)); // "Red", "Yellow" etc
|
|
}
|
|
}
|
|
|
|
// Player names
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
if (g_PlayerConfigsArray[i].base.name[0] == '\0') {
|
|
sprintf(g_PlayerConfigsArray[i].base.name, "%s %d\n", langGet(L_MISC_437), i + 1); // "Player 1" etc
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 mpCalculateTeamScoreLimit(void)
|
|
{
|
|
s32 limit = g_MpSetup.teamscorelimit;
|
|
s32 i;
|
|
|
|
if (g_BossFile.locktype == MPLOCKTYPE_CHALLENGE
|
|
&& g_MpSetup.teamscorelimit != 400
|
|
&& (g_MpSetup.scenario == MPSCENARIO_COMBAT || g_MpSetup.scenario == MPSCENARIO_KINGOFTHEHILL)) {
|
|
s32 numchrs = 0;
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
numchrs++;
|
|
}
|
|
}
|
|
|
|
switch (numchrs) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
limit = limit * 2 + 1;
|
|
break;
|
|
case 3:
|
|
limit = (limit * 5 + 5) / 2 - 1;
|
|
break;
|
|
case 4:
|
|
limit = limit * 3 + 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return limit;
|
|
}
|
|
|
|
void mpApplyLimits(void)
|
|
{
|
|
if (g_MpSetup.timelimit >= 60) {
|
|
lvSetMpTimeLimit60(0);
|
|
} else {
|
|
lvSetMpTimeLimit60(SECSTOTIME60((g_MpSetup.timelimit + 1) * 60));
|
|
}
|
|
|
|
if (g_MpSetup.scorelimit >= 100) {
|
|
lvSetMpScoreLimit(0);
|
|
} else {
|
|
lvSetMpScoreLimit(g_MpSetup.scorelimit + 1);
|
|
}
|
|
|
|
if (g_MpSetup.teamscorelimit >= 400) {
|
|
lvSetMpTeamScoreLimit(0);
|
|
} else {
|
|
lvSetMpTeamScoreLimit(mpCalculateTeamScoreLimit() + 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a pointer to an array of 12 ranking structs, populate those rankings
|
|
* with the winner first, loser last. Return the number of rows (players).
|
|
*
|
|
* The function also populates the placement an rankablescore values in the
|
|
* mpchr structs and calculates the last winner and loser for the lock settings.
|
|
*/
|
|
s32 mpGetPlayerRankings(struct ranking *rankings)
|
|
{
|
|
s32 i;
|
|
s32 scores[MAX_MPCHRS];
|
|
u32 rankablescores[MAX_MPCHRS];
|
|
struct mpchrconfig *mpchrs[MAX_MPCHRS];
|
|
s32 chrnums[MAX_MPCHRS];
|
|
s32 count = 0;
|
|
s32 numteams;
|
|
struct ranking teamrankings[MAX_MPCHRS];
|
|
s32 winner;
|
|
s32 loser;
|
|
s32 score;
|
|
s32 deaths;
|
|
struct mpchrconfig *mpchr;
|
|
s32 dstindex;
|
|
bool found;
|
|
u32 rankablescore;
|
|
s32 j;
|
|
|
|
if (g_MpSetup.options & MPOPTION_TEAMSENABLED) {
|
|
numteams = mpGetTeamRankings(teamrankings);
|
|
}
|
|
|
|
// Populate 4 arrays with player info, sorted by highest score descending
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
mpchr = MPCHR(i);
|
|
|
|
scenarioCalculatePlayerScore(mpchr, i, &score, &deaths);
|
|
|
|
rankablescore = (score + 0x8000) << 16 | (0xffff - deaths);
|
|
dstindex = 0;
|
|
found = false;
|
|
|
|
// Find where this player should be placed in the sorted arrays
|
|
for (j = 0; j < count && !found; j++) {
|
|
if (rankablescore > rankablescores[j]) {
|
|
dstindex = j;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
dstindex = count;
|
|
}
|
|
|
|
// Shuffle everything forward after dstindex to create a gap
|
|
for (j = count; j > dstindex; j--) {
|
|
rankablescores[j] = rankablescores[j - 1];
|
|
scores[j] = scores[j - 1];
|
|
mpchrs[j] = mpchrs[j - 1];
|
|
chrnums[j] = chrnums[j - 1];
|
|
}
|
|
|
|
count++;
|
|
|
|
// Write the new figures
|
|
rankablescores[dstindex] = (score + 0x8000) << 16 | (0xffff - deaths);
|
|
scores[dstindex] = score;
|
|
mpchrs[dstindex] = mpchr;
|
|
chrnums[dstindex] = i;
|
|
}
|
|
}
|
|
|
|
// Populate the rankings array, copy some values into the mpchr structs
|
|
// and calculate the winner and loser
|
|
winner = -1;
|
|
loser = -1;
|
|
|
|
for (j = 0; j < count; j++) {
|
|
rankings[j].mpchr = mpchrs[j];
|
|
rankings[j].teamnum = chrnums[j];
|
|
rankings[j].positionindex = j;
|
|
rankings[j].unk0c = 0;
|
|
rankings[j].score = scores[j];
|
|
|
|
// For a team game, the mpchr's placement has to be the team's placement
|
|
// and not the placement of the individual player.
|
|
if (g_MpSetup.options & MPOPTION_TEAMSENABLED) {
|
|
s32 placement = numteams - 1;
|
|
s32 i;
|
|
|
|
for (i = 0; i < numteams; i++) {
|
|
if (teamrankings[i].teamnum == mpchrs[j]->team) {
|
|
placement = i;
|
|
}
|
|
}
|
|
|
|
mpchrs[j]->placement = placement;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
mpchrs[j]->rankablescore = 255 - placement;
|
|
#endif
|
|
} else {
|
|
mpchrs[j]->placement = j;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
mpchrs[j]->rankablescore = rankablescores[j];
|
|
#endif
|
|
}
|
|
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
mpchrs[j]->rankablescore = rankablescores[j];
|
|
#endif
|
|
|
|
if (chrnums[j] < 4) {
|
|
loser = chrnums[j];
|
|
|
|
if (winner == -1) {
|
|
winner = chrnums[j];
|
|
if (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
g_MpLockInfo.lastwinner = winner;
|
|
g_MpLockInfo.lastloser = loser;
|
|
|
|
if (g_BossFile.locktype == MPLOCKTYPE_RANDOM) {
|
|
g_MpLockInfo.lockedplayernum = mpChooseRandomLockPlayer();
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Iterate players on the given team and add their scores.
|
|
*
|
|
* There are two score values:
|
|
* - The team score, which is written to the result pointer and eventually
|
|
* displayed on screen
|
|
* - The rankable score, which is the returned value. The rankable score is
|
|
* similar to the team score but uses the number of deaths as a tie breaker.
|
|
*/
|
|
s32 mpCalculateTeamScore(s32 teamnum, s32 *result)
|
|
{
|
|
struct mpchrconfig *mpchr;
|
|
s32 teamscore = 0;
|
|
s32 teamdeaths = 0;
|
|
bool teamexists = false;
|
|
s32 i;
|
|
u32 stack;
|
|
s32 score;
|
|
s32 deaths;
|
|
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
mpchr = MPCHR(i);
|
|
|
|
if (mpchr->team == teamnum) {
|
|
scenarioCalculatePlayerScore(mpchr, i, &score, &deaths);
|
|
teamexists = true;
|
|
teamscore += score;
|
|
teamdeaths += deaths;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (teamexists) {
|
|
*result = teamscore;
|
|
return (teamscore + 0x8000) << 16 | (0xffff - teamdeaths);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Given a pointer to an array of 8 ranking structs, populate those rankings
|
|
* with the winning team first, losing team last. Return the number of rows
|
|
* (teams).
|
|
*/
|
|
s32 mpGetTeamRankings(struct ranking *rankings)
|
|
{
|
|
s32 i;
|
|
s32 count;
|
|
s32 apparentscores[MAX_TEAMS];
|
|
u32 rankablescores[MAX_TEAMS];
|
|
u32 bestrankablescore;
|
|
s32 thisteamnum;
|
|
|
|
count = 0;
|
|
|
|
// Calculate scores for each team
|
|
for (i = 0; i < MAX_TEAMS; i++) {
|
|
apparentscores[i] = -8000;
|
|
rankablescores[i] = mpCalculateTeamScore(i, &apparentscores[i]);
|
|
}
|
|
|
|
// Iterate each slot and determine which team belongs in it.
|
|
// As each team is found, set their apparent score to -8000 so they're not
|
|
// found again.
|
|
do {
|
|
bestrankablescore = 0;
|
|
thisteamnum = -8000;
|
|
|
|
// Check which team has the best remaining score
|
|
for (i = 0; i < MAX_TEAMS; i++) {
|
|
if (apparentscores[7 - i] > -8000 && rankablescores[7 - i] >= bestrankablescore) {
|
|
thisteamnum = 7 - i;
|
|
bestrankablescore = rankablescores[thisteamnum];
|
|
}
|
|
}
|
|
|
|
if (thisteamnum > -8000) {
|
|
// Append to the rankings array
|
|
rankings[count].mpchr = NULL;
|
|
rankings[count].teamnum = thisteamnum;
|
|
rankings[count].positionindex = count + 1;
|
|
rankings[count].unk0c = 0;
|
|
rankings[count].score = apparentscores[thisteamnum];
|
|
|
|
apparentscores[thisteamnum] = -8000;
|
|
|
|
count++;
|
|
}
|
|
} while (thisteamnum != -8000);
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 func0f188bcc(void)
|
|
{
|
|
return NUM_MPWEAPONS;
|
|
}
|
|
|
|
s32 mpGetNumWeaponOptions(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpWeapons); i++) {
|
|
if (challengeIsFeatureUnlocked(g_MpWeapons[i].unlockfeature)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
char *mpGetWeaponLabel(s32 weaponnum)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpWeapons); i++) {
|
|
if (challengeIsFeatureUnlocked(g_MpWeapons[i].unlockfeature)) {
|
|
if (weaponnum == 0) {
|
|
if (g_MpWeapons[i].weaponnum == WEAPON_NONE) {
|
|
return langGet(L_MPWEAPONS_058); // "Nothing"
|
|
}
|
|
|
|
if (g_MpWeapons[i].weaponnum == WEAPON_MPSHIELD) {
|
|
return langGet(L_MPWEAPONS_059); // "Shield"
|
|
}
|
|
|
|
if (g_MpWeapons[i].weaponnum == WEAPON_DISABLED) {
|
|
return langGet(L_MPWEAPONS_060); // "Disabled"
|
|
}
|
|
|
|
return bgunGetName(g_MpWeapons[i].weaponnum);
|
|
}
|
|
|
|
weaponnum--;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
const char var7f1b8a5c[] = "Gun index %d -> slot %d = gun %d\n\n";
|
|
const char var7f1b8a80[] = "HOLDER: selecting weapon set %d\n";
|
|
#endif
|
|
|
|
void mpSetWeaponSlot(s32 slot, s32 mpweaponnum)
|
|
{
|
|
s32 optionindex = mpweaponnum;
|
|
s32 i;
|
|
|
|
for (i = 0; i <= mpweaponnum; i++) {
|
|
if (challengeIsFeatureUnlocked(g_MpWeapons[i].unlockfeature) == 0) {
|
|
mpweaponnum++;
|
|
}
|
|
|
|
optionindex = mpweaponnum;
|
|
}
|
|
|
|
g_MpSetup.weapons[slot] = optionindex;
|
|
}
|
|
|
|
s32 mpGetWeaponSlot(s32 slot)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_MpSetup.weapons[slot]; i++) {
|
|
if (challengeIsFeatureUnlocked(g_MpWeapons[i].unlockfeature)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
struct mpweapon *mpGetMpWeaponByLocation(s32 locationindex)
|
|
{
|
|
s32 v0 = locationindex + 1;
|
|
s32 slot = 0;
|
|
s32 a2 = v0;
|
|
u8 mpweaponnum;
|
|
|
|
while (v0 > 0) {
|
|
mpweaponnum = g_MpSetup.weapons[slot];
|
|
|
|
if (g_MpWeapons[mpweaponnum].weaponnum != WEAPON_DISABLED) {
|
|
v0--;
|
|
}
|
|
|
|
if (v0 > 0) {
|
|
slot++;
|
|
|
|
if (slot >= ARRAYCOUNT(g_MpSetup.weapons)) {
|
|
slot = 0;
|
|
|
|
if (a2 == v0) {
|
|
mpweaponnum = 0;
|
|
v0 = 0;
|
|
}
|
|
|
|
a2 = v0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return &g_MpWeapons[mpweaponnum];
|
|
}
|
|
|
|
s32 mpCountWeaponSetThing(s32 weaponsetindex)
|
|
{
|
|
s32 i;
|
|
s32 count = 0;
|
|
|
|
if (weaponsetindex >= ARRAYCOUNT(g_MpWeaponSets)) {
|
|
count = weaponsetindex - ARRAYCOUNT(g_MpWeaponSets);
|
|
weaponsetindex = ARRAYCOUNT(g_MpWeaponSets);
|
|
}
|
|
|
|
for (i = 0; i < weaponsetindex; i++) {
|
|
if ((challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[0])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[1])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[2])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[3])) || g_MpWeaponSets[i].unk0c != WEAPON_DISABLED) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
s32 func0f188f9c(s32 arg0)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpWeaponSets); i++) {
|
|
// @bug? Shouldn't the disabled check be == WEAPON_DISABLED?
|
|
if ((challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[0])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[1])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[2])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[3]))
|
|
|| g_MpWeaponSets[i].unk0c != WEAPON_DISABLED) {
|
|
if (arg0 == 0) {
|
|
break;
|
|
}
|
|
|
|
arg0--;
|
|
}
|
|
}
|
|
|
|
return i + arg0;
|
|
}
|
|
|
|
s32 func0f189058(bool full)
|
|
{
|
|
return mpCountWeaponSetThing(full ? ARRAYCOUNT(g_MpWeaponSets) + 3 : ARRAYCOUNT(g_MpWeaponSets));
|
|
}
|
|
|
|
s32 func0f189088(void)
|
|
{
|
|
return mpCountWeaponSetThing(ARRAYCOUNT(g_MpWeaponSets) + 2);
|
|
}
|
|
|
|
char *mpGetWeaponSetName(s32 index)
|
|
{
|
|
index = func0f188f9c(index);
|
|
|
|
if (index < 0 || index >= ARRAYCOUNT(g_MpWeaponSets) + 2) {
|
|
return langGet(L_MPWEAPONS_041); // "Custom"
|
|
}
|
|
|
|
if (index == ARRAYCOUNT(g_MpWeaponSets) + 1) {
|
|
return langGet(L_MPWEAPONS_042); // "Random"
|
|
}
|
|
|
|
if (index == ARRAYCOUNT(g_MpWeaponSets)) {
|
|
return langGet(L_MPWEAPONS_043); // "Random Five"
|
|
}
|
|
|
|
return langGet(g_MpWeaponSets[index].name);
|
|
}
|
|
|
|
void func0f18913c(void)
|
|
{
|
|
s32 i;
|
|
bool done = false;
|
|
u8 *ptr;
|
|
s32 j;
|
|
|
|
for (i = 0; !done && i < ARRAYCOUNT(g_MpWeaponSets); i++) {
|
|
if (challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[0])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[1])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[2])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[i].requirefeatures[3])) {
|
|
ptr = &g_MpWeaponSets[i].slots[0];
|
|
} else if (g_MpWeaponSets[i].unk0c != WEAPON_DISABLED) {
|
|
ptr = &g_MpWeaponSets[i].unk0c;
|
|
} else {
|
|
ptr = NULL;
|
|
}
|
|
|
|
if (ptr != NULL) {
|
|
bool ok = true;
|
|
|
|
for (j = 0; j < ARRAYCOUNT(g_MpWeaponSets[j].slots); j++) {
|
|
s32 weaponnum = ptr[j];
|
|
|
|
if (weaponnum == WEAPON_MPSHIELD) {
|
|
if (!challengeIsFeatureUnlocked(MPFEATURE_WEAPON_SHIELD)) {
|
|
weaponnum = 0;
|
|
}
|
|
}
|
|
|
|
if (weaponnum != g_MpWeapons[g_MpSetup.weapons[j]].weaponnum) {
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
done = true;
|
|
g_MpWeaponSetNum = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!done) {
|
|
g_MpWeaponSetNum = WEAPONSET_CUSTOM;
|
|
}
|
|
}
|
|
|
|
void mpApplyWeaponSet(void)
|
|
{
|
|
s32 i;
|
|
u8 *ptr;
|
|
|
|
if (g_MpWeaponSetNum >= 0 && g_MpWeaponSetNum < ARRAYCOUNT(g_MpWeaponSets)) {
|
|
if (challengeIsFeatureUnlocked(g_MpWeaponSets[g_MpWeaponSetNum].requirefeatures[0])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[g_MpWeaponSetNum].requirefeatures[1])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[g_MpWeaponSetNum].requirefeatures[2])
|
|
&& challengeIsFeatureUnlocked(g_MpWeaponSets[g_MpWeaponSetNum].requirefeatures[3])) {
|
|
ptr = &g_MpWeaponSets[g_MpWeaponSetNum].slots[0];
|
|
} else if (g_MpWeaponSets[g_MpWeaponSetNum].unk0c != WEAPON_DISABLED) {
|
|
ptr = &g_MpWeaponSets[g_MpWeaponSetNum].unk0c;
|
|
} else {
|
|
ptr = NULL;
|
|
}
|
|
|
|
if (ptr != NULL) {
|
|
for (i = 0; i < ARRAYCOUNT(g_MpSetup.weapons); i++) {
|
|
u32 j;
|
|
bool done = false;
|
|
s32 mpweaponnum = MPWEAPON_NONE;
|
|
s32 weaponnum = ptr[i];
|
|
|
|
if (weaponnum == WEAPON_MPSHIELD && !challengeIsFeatureUnlocked(MPFEATURE_WEAPON_SHIELD)) {
|
|
weaponnum = 0;
|
|
}
|
|
|
|
for (j = 0; !done; j++) {
|
|
if (j > MPWEAPON_DISABLED) {
|
|
done = true;
|
|
} else if (weaponnum == g_MpWeapons[j].weaponnum) {
|
|
mpweaponnum = j;
|
|
done = true;
|
|
}
|
|
}
|
|
|
|
g_MpSetup.weapons[i] = mpweaponnum;
|
|
}
|
|
}
|
|
} else if (g_MpWeaponSetNum == WEAPONSET_RANDOM) {
|
|
s32 numoptions = mpGetNumWeaponOptions();
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpSetup.weapons); i++) {
|
|
mpSetWeaponSlot(i, random() % numoptions);
|
|
}
|
|
} else if (g_MpWeaponSetNum == WEAPONSET_RANDOMFIVE) {
|
|
s32 numoptions = mpGetNumWeaponOptions() - 2;
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
mpSetWeaponSlot(i, random() % numoptions + 1);
|
|
}
|
|
|
|
mpSetWeaponSlot(i, mpGetNumWeaponOptions() - 1);
|
|
}
|
|
}
|
|
|
|
void mpSetWeaponSet(s32 weaponsetnum)
|
|
{
|
|
g_MpWeaponSetNum = func0f188f9c(weaponsetnum);
|
|
mpApplyWeaponSet();
|
|
}
|
|
|
|
void func0f1895e8(void)
|
|
{
|
|
if (g_MpWeaponSetNum < ARRAYCOUNT(g_MpWeaponSets)) {
|
|
mpApplyWeaponSet();
|
|
}
|
|
}
|
|
|
|
s32 mpGetWeaponSet(void)
|
|
{
|
|
return mpCountWeaponSetThing(g_MpWeaponSetNum);
|
|
}
|
|
|
|
bool mpIsPaused(void)
|
|
{
|
|
if (PLAYERCOUNT() == 1
|
|
&& g_Vars.mplayerisrunning
|
|
&& g_Menus[g_Vars.currentplayerstats->mpindex].curdialog) {
|
|
return true;
|
|
}
|
|
|
|
if (g_MpSetup.paused == PAUSEMODE_UNPAUSED) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void mpSetPaused(u8 mode)
|
|
{
|
|
g_MpSetup.paused = mode;
|
|
}
|
|
|
|
/**
|
|
* Render "Paused" in the middle of the viewport if paused,
|
|
* or "Press START" if player has finished their death animation.
|
|
*
|
|
* @bug: When calling textRender(), viGetWidth() is used twice but it should be
|
|
* viGetViewWidth() and viGetViewHeight(). These arguments to textRender() set
|
|
* the crop box for the text, but the text doesn't extend past the box anyway
|
|
* so it has no effect.
|
|
*/
|
|
Gfx *mpRenderModalText(Gfx *gdl)
|
|
{
|
|
s32 textwidth;
|
|
s32 textheight;
|
|
s32 x;
|
|
s32 y;
|
|
char text[50];
|
|
s32 stack1;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
g_ScaleX = g_ViRes == VIRES_HI ? 2 : 1;
|
|
#endif
|
|
|
|
if (g_MpSetup.paused == MPPAUSEMODE_PAUSED) {
|
|
s32 red = (s32) ((1.0f - g_20SecIntervalFrac) * 20.0f * 255.0f) % 255;
|
|
s32 stack2;
|
|
|
|
gdl = text0f153628(gdl);
|
|
|
|
strcpy(text, langGet(L_MPWEAPONS_040)); // "Paused"
|
|
|
|
x = viGetViewLeft() + viGetViewWidth() / 2;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
x = x / g_ScaleX;
|
|
#endif
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (g_Menus[g_Vars.currentplayerstats->mpindex].curdialog)
|
|
#else
|
|
if (g_Menus[g_Vars.currentplayernum].curdialog)
|
|
#endif
|
|
{
|
|
y = viGetViewTop() + 10;
|
|
} else {
|
|
y = viGetViewTop() + viGetViewHeight() / 2;
|
|
}
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
textMeasure(&textheight, &textwidth, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
|
|
#elif PAL
|
|
// Use smaller fonts
|
|
textMeasure(&textheight, &textwidth, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
|
|
#else
|
|
textMeasure(&textheight, &textwidth, text, g_CharsHandelGothicMd, g_FontHandelGothicMd, 0);
|
|
#endif
|
|
x -= textwidth / 2;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
gdl = func0f1574d0jf(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, (red << 24) | 0x00ff00ff, 0x000000ff, viGetWidth(), viGetWidth(), 0, 0);
|
|
#elif PAL
|
|
gdl = textRender(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, (red << 24) | 0x00ff00ff, 0x000000ff, viGetWidth(), viGetWidth(), 0, 0);
|
|
#else
|
|
gdl = textRender(gdl, &x, &y, text, g_CharsHandelGothicMd, g_FontHandelGothicMd, (red << 24) | 0x00ff00ff, 0x000000ff, viGetWidth(), viGetWidth(), 0, 0);
|
|
#endif
|
|
|
|
gdl = text0f153780(gdl);
|
|
} else if (!g_MainIsEndscreen
|
|
&& g_MpSetup.paused == MPPAUSEMODE_UNPAUSED
|
|
&& g_Vars.currentplayer->isdead
|
|
&& g_Vars.currentplayer->redbloodfinished
|
|
&& g_Vars.currentplayer->deathanimfinished
|
|
&& !(g_Vars.coopplayernum >= 0 && ((g_Vars.bond->isdead && g_Vars.coop->isdead) || !g_Vars.currentplayer->coopcanrestart || g_InCutscene))
|
|
&& !(g_Vars.antiplayernum >= 0 && ((g_Vars.currentplayer != g_Vars.anti || g_InCutscene)))
|
|
&& g_NumReasonsToEndMpMatch == 0) {
|
|
// Render "Press START" text
|
|
gdl = text0f153628(gdl);
|
|
|
|
strcpy(text, langGet(L_MPWEAPONS_039));
|
|
|
|
x = viGetViewLeft() + viGetViewWidth() / 2;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
x = x / g_ScaleX;
|
|
#endif
|
|
|
|
y = viGetViewTop() + viGetViewHeight() / 2;
|
|
|
|
textMeasure(&textheight, &textwidth, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
|
|
x -= textwidth / 2;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
gdl = func0f1574d0jf(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0xff0000ff, 0x000000ff, viGetWidth(), viGetWidth(), 0, 0);
|
|
#else
|
|
gdl = textRender(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0xff0000ff, 0x000000ff, viGetWidth(), viGetWidth(), 0, 0);
|
|
#endif
|
|
|
|
if (g_Vars.currentplayer->deadtimer > 0) {
|
|
// Render countdown timer
|
|
s32 countdowny = viGetViewTop() + viGetViewHeight() / 2 + textheight + 2;
|
|
s32 countdownx = viGetViewLeft() + viGetViewWidth() / 2;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
countdownx = countdownx / g_ScaleX;
|
|
#endif
|
|
sprintf(text, "%d\n", (g_Vars.currentplayer->deadtimer + TICKS(60) - 1) / TICKS(60));
|
|
|
|
textMeasure(&textheight, &textwidth, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0);
|
|
x = countdownx - textwidth / 2;
|
|
y = countdowny;
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
gdl = func0f1574d0jf(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0xff0000ff, 0x000000ff, viGetWidth(), viGetWidth(), 0, 0);
|
|
#else
|
|
gdl = textRender(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, 0xff0000ff, 0x000000ff, viGetWidth(), viGetWidth(), 0, 0);
|
|
#endif
|
|
}
|
|
|
|
gdl = text0f153780(gdl);
|
|
|
|
g_Menus[g_Vars.currentplayerstats->mpindex].openinhibit = 10;
|
|
}
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
g_ScaleX = 1;
|
|
#endif
|
|
|
|
return gdl;
|
|
}
|
|
|
|
s32 mpFindMaxInt(s32 numplayers, s32 val0, s32 val1, s32 val2, s32 val3)
|
|
{
|
|
s32 bestvalue = val0;
|
|
s32 bestplayer = 0;
|
|
|
|
if (numplayers >= 2) {
|
|
if (val1 > bestvalue || (val1 == bestvalue && (random() % 2))) {
|
|
bestplayer = 1;
|
|
bestvalue = val1;
|
|
} else {
|
|
bestvalue = val0;
|
|
bestplayer = 0;
|
|
}
|
|
|
|
if (numplayers >= 3) {
|
|
if (val2 > bestvalue || (val2 == bestvalue && (random() % 2))) {
|
|
bestplayer = 2;
|
|
bestvalue = val2;
|
|
}
|
|
|
|
if (numplayers >= 4) {
|
|
if (val3 > bestvalue || (val3 == bestvalue && (random() % 2))) {
|
|
bestplayer = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestplayer;
|
|
}
|
|
|
|
s32 mpFindMinInt(s32 numplayers, s32 val0, s32 val1, s32 val2, s32 val3)
|
|
{
|
|
s32 bestvalue = val0;
|
|
s32 bestplayer = 0;
|
|
|
|
if (numplayers >= 2) {
|
|
if (val1 < bestvalue || (val1 == bestvalue && (random() % 2))) {
|
|
bestplayer = 1;
|
|
bestvalue = val1;
|
|
} else {
|
|
bestvalue = val0;
|
|
bestplayer = 0;
|
|
}
|
|
|
|
if (numplayers >= 3) {
|
|
if (val2 < bestvalue || (val2 == bestvalue && (random() % 2))) {
|
|
bestplayer = 2;
|
|
bestvalue = val2;
|
|
}
|
|
|
|
if (numplayers >= 4) {
|
|
if (val3 < bestvalue || (val3 == bestvalue && (random() % 2))) {
|
|
bestplayer = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestplayer;
|
|
}
|
|
|
|
s32 mpFindMaxFloat(s32 numplayers, f32 val0, f32 val1, f32 val2, f32 val3)
|
|
{
|
|
// @bug: bestvalue should be an f32. Any value saved into here will be
|
|
// rounded down which may cause the function to return an incorrect result.
|
|
s32 bestvalue;
|
|
s32 bestplayer = 0;
|
|
|
|
if (numplayers >= 2) {
|
|
if (val1 > val0 || (val1 == val0 && (random() % 2))) {
|
|
bestplayer = 1;
|
|
bestvalue = val1;
|
|
} else {
|
|
bestvalue = val0;
|
|
bestplayer = 0;
|
|
}
|
|
|
|
if (numplayers >= 3) {
|
|
if (val2 > bestvalue || (val2 == bestvalue && (random() % 2))) {
|
|
bestplayer = 2;
|
|
bestvalue = val2;
|
|
}
|
|
|
|
if (numplayers >= 4) {
|
|
if (val3 > bestvalue || (val3 == bestvalue && (random() % 2))) {
|
|
bestplayer = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestplayer;
|
|
}
|
|
|
|
s32 mpFindMinFloat(s32 numplayers, f32 val0, f32 val1, f32 val2, f32 val3)
|
|
{
|
|
// @bug: bestvalue should be an f32. Any value saved into here will be
|
|
// rounded down which may cause the function to return an incorrect result.
|
|
s32 bestvalue;
|
|
s32 bestplayer = 0;
|
|
|
|
if (numplayers >= 2) {
|
|
if (val0);
|
|
|
|
if (val1 < val0 || (val1 == val0 && (random() % 2))) {
|
|
bestplayer = 1;
|
|
bestvalue = val1;
|
|
} else {
|
|
bestplayer = 0;
|
|
bestvalue = val0;
|
|
}
|
|
|
|
if (numplayers >= 3) {
|
|
if (val2 < bestvalue || (val2 == bestvalue && (random() % 2))) {
|
|
bestplayer = 2;
|
|
bestvalue = val2;
|
|
}
|
|
|
|
if (numplayers >= 4) {
|
|
if (val3 < bestvalue || (val3 == bestvalue && (random() % 2))) {
|
|
bestplayer = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestplayer;
|
|
}
|
|
|
|
struct mpweaponset g_MpWeaponSets[12] = {
|
|
{ /*0x00*/ L_MPWEAPONS_055, { WEAPON_FALCON2, WEAPON_MAGSEC4, WEAPON_PHOENIX, WEAPON_MAULER, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_PHOENIX, MPFEATURE_WEAPON_MAULER, 0, 0 }, WEAPON_FALCON2, WEAPON_MAGSEC4, WEAPON_FALCON2, WEAPON_DY357MAGNUM, WEAPON_MPSHIELD, WEAPON_DISABLED }, // Pistols
|
|
{ /*0x01*/ L_MPWEAPONS_054, { WEAPON_FALCON2, WEAPON_CMP150, WEAPON_LAPTOPGUN, WEAPON_AR34, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_LAPTOPGUN, 0, 0, 0 }, WEAPON_FALCON2, WEAPON_CMP150, WEAPON_DRAGON, WEAPON_AR34, WEAPON_MPSHIELD, WEAPON_DISABLED }, // Automatics
|
|
{ /*0x02*/ L_MPWEAPONS_053, { WEAPON_MAGSEC4, WEAPON_DY357MAGNUM, WEAPON_SHOTGUN, WEAPON_RCP120, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_SHOTGUN, MPFEATURE_WEAPON_RCP120, 0, 0 }, WEAPON_MAGSEC4, WEAPON_DY357MAGNUM, WEAPON_DRAGON, WEAPON_AR34, WEAPON_MPSHIELD, WEAPON_DISABLED }, // Power
|
|
{ /*0x03*/ L_MPWEAPONS_052, { WEAPON_PHOENIX, WEAPON_CYCLONE, WEAPON_CALLISTO, WEAPON_FARSIGHT, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_FARSIGHT, MPFEATURE_WEAPON_CALLISTO, 0, 0 }, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED }, // FarSight
|
|
{ /*0x04*/ L_MPWEAPONS_051, { WEAPON_FALCON2, WEAPON_CMP150, WEAPON_DRAGON, WEAPON_TRANQUILIZER, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_TRANQUILIZER, 0, 0, 0 }, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED }, // Tranquilizer
|
|
{ /*0x05*/ L_MPWEAPONS_050, { WEAPON_MAULER, WEAPON_K7AVENGER, WEAPON_REAPER, WEAPON_SUPERDRAGON, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_SUPERDRAGON, MPFEATURE_WEAPON_MAULER, MPFEATURE_WEAPON_K7AVENGER, MPFEATURE_WEAPON_REAPER }, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED }, // Heavy
|
|
{ /*0x06*/ L_MPWEAPONS_049, { WEAPON_FALCON2_SILENCER, WEAPON_GRENADE, WEAPON_CMP150, WEAPON_DY357LX, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_FALCON2SILENCED, MPFEATURE_WEAPON_DY357LX, 0, 0 }, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED }, // Golden Magnum
|
|
{ /*0x07*/ L_MPWEAPONS_048, { WEAPON_DEVASTATOR, WEAPON_DEVASTATOR, WEAPON_SUPERDRAGON, WEAPON_SUPERDRAGON, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_SUPERDRAGON, MPFEATURE_WEAPON_DEVASTATOR, 0, 0 }, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED }, // Explosive
|
|
{ /*0x08*/ L_MPWEAPONS_047, { WEAPON_MAGSEC4, WEAPON_CMP150, WEAPON_AR34, WEAPON_DEVASTATOR, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_DEVASTATOR, 0, 0, 0 }, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED }, // Grenade Launcher
|
|
{ /*0x09*/ L_MPWEAPONS_046, { WEAPON_MAULER, WEAPON_CYCLONE, WEAPON_DRAGON, WEAPON_ROCKETLAUNCHER, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_MAULER, 0, 0, 0 }, WEAPON_FALCON2, WEAPON_CYCLONE, WEAPON_DRAGON, WEAPON_ROCKETLAUNCHER, WEAPON_MPSHIELD, WEAPON_DISABLED }, // Rocket Launcher
|
|
{ /*0x0a*/ L_MPWEAPONS_045, { WEAPON_MAGSEC4, WEAPON_LAPTOPGUN, WEAPON_K7AVENGER, WEAPON_PROXIMITYMINE, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_LAPTOPGUN, MPFEATURE_WEAPON_K7AVENGER, MPFEATURE_WEAPON_PROXIMITYMINE, 0 }, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED, WEAPON_DISABLED }, // Proximity Mine
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
{ /*0x0b*/ L_MPWEAPONS_044, { WEAPON_TIMEDMINE, WEAPON_CROSSBOW, WEAPON_TIMEDMINE, WEAPON_CROSSBOW, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_CROSSBOW, 0, 0, 0 }, WEAPON_TIMEDMINE, WEAPON_TIMEDMINE, WEAPON_TIMEDMINE, WEAPON_TIMEDMINE, WEAPON_MPSHIELD, WEAPON_DISABLED }, // Close Combat
|
|
#else
|
|
{ /*0x0b*/ L_MPWEAPONS_044, { WEAPON_COMBATKNIFE, WEAPON_COMBATKNIFE, WEAPON_TIMEDMINE, WEAPON_CROSSBOW, WEAPON_MPSHIELD, WEAPON_DISABLED }, { MPFEATURE_WEAPON_CROSSBOW, 0, 0, 0 }, WEAPON_COMBATKNIFE, WEAPON_COMBATKNIFE, WEAPON_TIMEDMINE, WEAPON_TIMEDMINE, WEAPON_MPSHIELD, WEAPON_DISABLED }, // Close Combat
|
|
#endif
|
|
};
|
|
|
|
s32 g_MpWeaponSetNum = 0x00000000;
|
|
|
|
u16 g_AwardNames[] = {
|
|
L_MPMENU_000, // "Most Suicidal"
|
|
L_MPMENU_001, // "Who Needs Ammo?"
|
|
L_MPMENU_002, // "Least Shielded"
|
|
L_MPMENU_003, // "Best Protected"
|
|
L_MPMENU_004, // "Marksmanship"
|
|
L_MPMENU_005, // "Most Professional"
|
|
L_MPMENU_006, // "Most Deadly"
|
|
L_MPMENU_007, // "Most Harmless"
|
|
L_MPMENU_008, // "Most Cowardly"
|
|
L_MPMENU_009, // "Most Frantic"
|
|
L_MPMENU_010, // "Most Honorable"
|
|
L_MPMENU_011, // "Most Dishonourable"
|
|
L_MPMENU_012, // "Shortest Life"
|
|
L_MPMENU_013, // "Longest Life"
|
|
L_MPMENU_014, // "Double Kill"
|
|
L_MPMENU_015, // "Triple Kill"
|
|
L_MPMENU_016, // "Quad Kill"
|
|
};
|
|
|
|
void mpCalculatePlayerTitle(struct mpplayerconfig *mpplayer)
|
|
{
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
const u32 tiers[] = { 2, 4, 8, 16, 28, 60, 100, 150, 210, 300 };
|
|
#else
|
|
const u32 tiers[] = { 2, 4, 8, 16, 28, 48, 78, 138, 198, 300 };
|
|
#endif
|
|
s32 tallies[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
s32 sum;
|
|
s32 max;
|
|
s32 i;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
#define MULT(val) (val * 3)
|
|
#else
|
|
#define MULT(val) (val)
|
|
#endif
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->kills >= tiers[i] * MULT(20)) {
|
|
tallies[0]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->gameswon >= tiers[i] * MULT(1)) {
|
|
tallies[1]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->accuracymedals >= tiers[i] * MULT(1)) {
|
|
tallies[2]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->headshotmedals >= tiers[i] * MULT(1)) {
|
|
tallies[3]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->killmastermedals >= tiers[i] * MULT(1)) {
|
|
tallies[4]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->time >= tiers[i] * MULT(1200)) {
|
|
tallies[5]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->distance >= tiers[i] * MULT(100)) {
|
|
tallies[6]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->damagedealt >= tiers[i] * MULT(1)) {
|
|
tallies[7]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->ammoused >= tiers[i] * MULT(500)) {
|
|
tallies[8]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tiers); i++) {
|
|
if (mpplayer->survivormedals >= tiers[i] * MULT(1)) {
|
|
tallies[9]++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
sum = 0;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(tallies); i++) {
|
|
sum = sum + tallies[i];
|
|
}
|
|
|
|
if (sum > 100) {
|
|
sum = 100;
|
|
}
|
|
|
|
mpplayer->title = sum / 5;
|
|
|
|
max = MPPLAYERTITLE_PERFECT;
|
|
|
|
if (mpplayer->title > max) {
|
|
mpplayer->title = max;
|
|
}
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
const char var7f1b8ad0[] = "%s%sAccuracy Peak! real value: %f (*100)\n";
|
|
const char var7f1b8afc[] = "";
|
|
const char var7f1b8b00[] = "";
|
|
const char var7f1b8b04[] = "%splayer %d Accuracy :%f\n";
|
|
const char var7f1b8b20[] = "";
|
|
const char var7f1b8b24[] = "%splayer %d dist:%f -> %f = %d\n";
|
|
const char var7f1b8b44[] = "";
|
|
const char var7f1b8b48[] = "Player %d TitleCalc ============\n";
|
|
#endif
|
|
|
|
struct mphead g_MpBeauHeads[] = {
|
|
// head, require feature
|
|
{ HEAD_BEAU2, 0 },
|
|
{ HEAD_BEAU3, 0 },
|
|
{ HEAD_BEAU4, 0 },
|
|
{ HEAD_BEAU5, 0 },
|
|
{ HEAD_BEAU6, 0 },
|
|
};
|
|
|
|
struct mphead g_MpHeads[] = {
|
|
// head, require feature
|
|
{ /*0x00*/ HEAD_DARK_COMBAT, 0 },
|
|
{ /*0x01*/ HEAD_DARK_FROCK, MPFEATURE_CHR_CI },
|
|
{ /*0x02*/ HEAD_DARKAQUA, MPFEATURE_CHR_PELAGIC },
|
|
{ /*0x03*/ HEAD_DARK_SNOW, MPFEATURE_CHR_DARKSNOW },
|
|
{ /*0x04*/ HEAD_ELVIS, MPFEATURE_CHR_ELVIS },
|
|
{ /*0x05*/ HEAD_ELVIS_GOGS, MPFEATURE_CHR_ELVIS },
|
|
{ /*0x06*/ HEAD_CARRINGTON, 0 },
|
|
{ /*0x07*/ HEAD_MRBLONDE, MPFEATURE_CHR_MRBLONDE },
|
|
{ /*0x08*/ HEAD_CASSANDRA, 0 },
|
|
{ /*0x09*/ HEAD_TRENT, MPFEATURE_CHR_TRENT },
|
|
{ /*0x0a*/ HEAD_JONATHAN, MPFEATURE_CHR_INFILTRATION },
|
|
{ /*0x0b*/ HEAD_VD, 0 },
|
|
{ /*0x0c*/ HEAD_PRESIDENT, MPFEATURE_CHR_CI },
|
|
{ /*0x0d*/ HEAD_DDSHOCK, 0 },
|
|
{ /*0x0e*/ HEAD_BIOTECH, MPFEATURE_CHR_BIOTECH },
|
|
{ /*0x0f*/ HEAD_DDSNIPER, MPFEATURE_CHR_VILLACHRS },
|
|
{ /*0x10*/ HEAD_A51FACEPLATE, MPFEATURE_CHR_INFILTRATION },
|
|
{ /*0x11*/ HEAD_SECRETARY, 0 },
|
|
{ /*0x12*/ HEAD_FEM_GUARD, MPFEATURE_CHR_FEMGUARD },
|
|
{ /*0x13*/ HEAD_FEM_GUARD2, MPFEATURE_CHR_FEMGUARD },
|
|
{ /*0x14*/ HEAD_MAIAN_S, MPFEATURE_CHR_ELVIS },
|
|
{ /*0x15*/ HEAD_JON, 0 },
|
|
{ /*0x16*/ HEAD_BEAU1, 0 },
|
|
{ /*0x17*/ HEAD_ROSS, 0 },
|
|
{ /*0x18*/ HEAD_MARK2, 0 },
|
|
{ /*0x19*/ HEAD_CHRIST, 0 },
|
|
{ /*0x1a*/ HEAD_RUSS, 0 },
|
|
{ /*0x1b*/ HEAD_DARLING, 0 },
|
|
{ /*0x1c*/ HEAD_BRIAN, 0 },
|
|
{ /*0x1d*/ HEAD_JAMIE, 0 },
|
|
{ /*0x1e*/ HEAD_DUNCAN2, 0 },
|
|
{ /*0x1f*/ HEAD_KEITH, 0 },
|
|
{ /*0x20*/ HEAD_STEVEM, 0 },
|
|
{ /*0x21*/ HEAD_GRANT, 0 },
|
|
{ /*0x22*/ HEAD_PENNY, 0 },
|
|
{ /*0x23*/ HEAD_DAVEC, 0 },
|
|
{ /*0x24*/ HEAD_JONES, 0 },
|
|
{ /*0x25*/ HEAD_GRAHAM, 0 },
|
|
{ /*0x26*/ HEAD_ROBERT, 0 },
|
|
{ /*0x27*/ HEAD_NEIL2, 0 },
|
|
{ /*0x28*/ HEAD_SHAUN, 0 },
|
|
{ /*0x29*/ HEAD_ROBIN, 0 },
|
|
{ /*0x2a*/ HEAD_COOK, 0 },
|
|
{ /*0x2b*/ HEAD_PRYCE, 0 },
|
|
{ /*0x2c*/ HEAD_SILKE, 0 },
|
|
{ /*0x2d*/ HEAD_SMITH, 0 },
|
|
{ /*0x2e*/ HEAD_GARETH, 0 },
|
|
{ /*0x2f*/ HEAD_MURCHIE, 0 },
|
|
{ /*0x30*/ HEAD_WONG, 0 },
|
|
{ /*0x31*/ HEAD_CARTER, 0 },
|
|
{ /*0x32*/ HEAD_TINTIN, 0 },
|
|
{ /*0x33*/ HEAD_MUNTON, 0 },
|
|
{ /*0x34*/ HEAD_STAMPER, 0 },
|
|
{ /*0x35*/ HEAD_PHELPS, 0 },
|
|
{ /*0x36*/ HEAD_ALEX, 0 },
|
|
{ /*0x37*/ HEAD_JULIANNE, 0 },
|
|
{ /*0x38*/ HEAD_LAURA, 0 },
|
|
{ /*0x39*/ HEAD_EDMCG, 0 },
|
|
{ /*0x3a*/ HEAD_ANKA, 0 },
|
|
{ /*0x3b*/ HEAD_LESLIE_S, 0 },
|
|
{ /*0x3c*/ HEAD_MATT_C, 0 },
|
|
{ /*0x3d*/ HEAD_PEER_S, 0 },
|
|
{ /*0x3e*/ HEAD_EILEEN_T, 0 },
|
|
{ /*0x3f*/ HEAD_ANDY_R, 0 },
|
|
{ /*0x40*/ HEAD_BEN_R, 0 },
|
|
{ /*0x41*/ HEAD_STEVE_K, 0 },
|
|
{ /*0x42*/ HEAD_SANCHEZ, 0 },
|
|
{ /*0x43*/ HEAD_TIM, 0 },
|
|
{ /*0x44*/ HEAD_KEN, 0 },
|
|
{ /*0x45*/ HEAD_EILEEN_H, 0 },
|
|
{ /*0x46*/ HEAD_SCOTT_H, 0 },
|
|
{ /*0x47*/ HEAD_JOEL, 0 },
|
|
{ /*0x48*/ HEAD_GRIFFEY, 0 },
|
|
#if VERSION != VERSION_JPN_FINAL
|
|
{ /*0x49*/ HEAD_MOTO, 0 },
|
|
#endif
|
|
{ /*0x4a*/ HEAD_WINNER, 0 },
|
|
};
|
|
|
|
u32 g_BotHeads[] = {
|
|
MPHEAD_JON,
|
|
MPHEAD_BEAU1,
|
|
MPHEAD_ROSS,
|
|
MPHEAD_MARK2,
|
|
MPHEAD_CHRIST,
|
|
MPHEAD_RUSS,
|
|
MPHEAD_DARLING,
|
|
MPHEAD_BRIAN,
|
|
MPHEAD_JAMIE,
|
|
MPHEAD_DUNCAN2,
|
|
MPHEAD_KEITH,
|
|
MPHEAD_STEVEM,
|
|
MPHEAD_GRANT,
|
|
MPHEAD_PENNY,
|
|
MPHEAD_DAVEC,
|
|
MPHEAD_JONES,
|
|
MPHEAD_GRAHAM,
|
|
MPHEAD_ROBERT,
|
|
MPHEAD_NEIL2,
|
|
MPHEAD_SHAUN,
|
|
MPHEAD_ROBIN,
|
|
MPHEAD_COOK,
|
|
MPHEAD_PRYCE,
|
|
MPHEAD_SILKE,
|
|
MPHEAD_SMITH,
|
|
MPHEAD_GARETH,
|
|
MPHEAD_MURCHIE,
|
|
MPHEAD_WONG,
|
|
MPHEAD_CARTER,
|
|
MPHEAD_TINTIN,
|
|
MPHEAD_MUNTON,
|
|
MPHEAD_STAMPER,
|
|
MPHEAD_PHELPS,
|
|
MPHEAD_ALEX,
|
|
MPHEAD_JULIANNE,
|
|
MPHEAD_LAURA,
|
|
MPHEAD_EDMCG,
|
|
MPHEAD_ANKA,
|
|
MPHEAD_LESLIE_S,
|
|
MPHEAD_MATT_C,
|
|
MPHEAD_PEER_S,
|
|
MPHEAD_EILEEN_T,
|
|
MPHEAD_ANDY_R,
|
|
MPHEAD_BEN_R,
|
|
MPHEAD_STEVE_K,
|
|
MPHEAD_SANCHEZ,
|
|
MPHEAD_TIM,
|
|
MPHEAD_KEN,
|
|
MPHEAD_EILEEN_H,
|
|
MPHEAD_SCOTT_H,
|
|
MPHEAD_JOEL,
|
|
MPHEAD_GRIFFEY,
|
|
#if VERSION != VERSION_JPN_FINAL
|
|
MPHEAD_MOTO,
|
|
#endif
|
|
};
|
|
|
|
struct botprofile g_BotProfiles[] = {
|
|
// type, difficulty, name, body, require feature
|
|
{ BOTTYPE_GENERAL, BOTDIFF_MEAT, L_MISC_088, MPBODY_DD_GUARD, 0 },
|
|
{ BOTTYPE_GENERAL, BOTDIFF_EASY, L_MISC_089, MPBODY_DD_SECGUARD, 0 },
|
|
{ BOTTYPE_GENERAL, BOTDIFF_NORMAL, L_MISC_090, MPBODY_DD_SHOCK_INF, 0 },
|
|
{ BOTTYPE_GENERAL, BOTDIFF_HARD, L_MISC_091, MPBODY_DDSHOCK, MPFEATURE_BOTDIFF_HARD },
|
|
{ BOTTYPE_GENERAL, BOTDIFF_PERFECT, L_MISC_092, MPBODY_STRIPES, MPFEATURE_BOTDIFF_PERFECT },
|
|
{ BOTTYPE_GENERAL, BOTDIFF_DARK, L_MISC_093, MPBODY_MOORE, MPFEATURE_BOTDIFF_DARK },
|
|
{ BOTTYPE_PEACE, BOTDIFF_NORMAL, L_MISC_094, MPBODY_DD_LABTECH, 0 },
|
|
{ BOTTYPE_SHIELD, BOTDIFF_NORMAL, L_MISC_095, MPBODY_G5_SWAT_GUARD, 0 },
|
|
{ BOTTYPE_ROCKET, BOTDIFF_NORMAL, L_MISC_096, MPBODY_G5_GUARD, 0 },
|
|
{ BOTTYPE_KAZE, BOTDIFF_NORMAL, L_MISC_097, MPBODY_PRES_SECURITY, 0 },
|
|
{ BOTTYPE_FIST, BOTDIFF_NORMAL, L_MISC_098, MPBODY_PELAGIC_GUARD, 0 },
|
|
{ BOTTYPE_PREY, BOTDIFF_NORMAL, L_MISC_099, MPBODY_DDSHOCK, 0 },
|
|
{ BOTTYPE_COWARD, BOTDIFF_NORMAL, L_MISC_100, MPBODY_PRESIDENT, 0 },
|
|
{ BOTTYPE_JUDGE, BOTDIFF_NORMAL, L_MISC_101, MPBODY_STEWARD, 0 },
|
|
{ BOTTYPE_FEUD, BOTDIFF_NORMAL, L_MISC_102, MPBODY_NSA_LACKEY, 0 },
|
|
{ BOTTYPE_SPEED, BOTDIFF_NORMAL, L_MISC_103, MPBODY_MRBLONDE, 0 },
|
|
{ BOTTYPE_TURTLE, BOTDIFF_NORMAL, L_MISC_104, MPBODY_CARRINGTON, 0 },
|
|
{ BOTTYPE_VENGE, BOTDIFF_NORMAL, L_MISC_105, MPBODY_ALASKAN_GUARD, 0 },
|
|
};
|
|
|
|
struct mpbody g_MpBodies[] = {
|
|
// global body ID, name, head, require feature
|
|
/*0x00*/ { BODY_DARK_COMBAT, L_OPTIONS_016, HEAD_DARK_COMBAT, 0 },
|
|
/*0x01*/ { BODY_DARK_TRENCH, L_OPTIONS_017, HEAD_DARK_COMBAT, MPFEATURE_CHR_JOTRENCH },
|
|
/*0x02*/ { BODY_DARK_FROCK, L_OPTIONS_018, HEAD_DARK_FROCK, MPFEATURE_CHR_CI },
|
|
/*0x03*/ { BODY_DARK_RIPPED, L_OPTIONS_019, HEAD_DARK_FROCK, MPFEATURE_CHR_CI },
|
|
/*0x04*/ { BODY_DARK_AF1, L_OPTIONS_020, HEAD_DARK_COMBAT, MPFEATURE_CHR_AF1 },
|
|
/*0x05*/ { BODY_DARK_LEATHER, L_MPWEAPONS_156, HEAD_DARK_COMBAT, MPFEATURE_CHR_G5 },
|
|
/*0x06*/ { BODY_DARK_NEGOTIATOR, L_MPWEAPONS_157, HEAD_DARK_COMBAT, MPFEATURE_CHR_VILLACHRS },
|
|
/*0x07*/ { BODY_DARKWET, L_OPTIONS_021, HEAD_DARKAQUA, MPFEATURE_CHR_PELAGIC },
|
|
/*0x08*/ { BODY_DARKAQUALUNG, L_OPTIONS_022, HEAD_DARKAQUA, MPFEATURE_CHR_PELAGIC },
|
|
/*0x09*/ { BODY_DARKSNOW, L_OPTIONS_023, HEAD_DARK_SNOW, MPFEATURE_CHR_DARKSNOW },
|
|
/*0x0a*/ { BODY_DARKLAB, L_OPTIONS_024, HEAD_DARK_COMBAT, MPFEATURE_CHR_INFILTRATION },
|
|
/*0x0b*/ { BODY_THEKING, L_OPTIONS_025, HEAD_ELVIS, MPFEATURE_CHR_ELVIS },
|
|
/*0x0c*/ { BODY_ELVIS1, L_OPTIONS_026, HEAD_ELVIS, MPFEATURE_CHR_ELVIS },
|
|
/*0x0d*/ { BODY_ELVISWAISTCOAT, L_MPWEAPONS_158, HEAD_ELVIS, MPFEATURE_CHR_ELVIS },
|
|
/*0x0e*/ { BODY_CARRINGTON, L_OPTIONS_027, HEAD_CARRINGTON, 0 },
|
|
/*0x0f*/ { BODY_CARREVENINGSUIT, L_OPTIONS_028, HEAD_CARRINGTON, MPFEATURE_CHR_CI },
|
|
/*0x10*/ { BODY_MRBLONDE, L_OPTIONS_029, HEAD_MRBLONDE, MPFEATURE_CHR_MRBLONDE },
|
|
/*0x11*/ { BODY_CASSANDRA, L_OPTIONS_030, HEAD_CASSANDRA, 0 },
|
|
/*0x12*/ { BODY_TRENT, L_OPTIONS_031, HEAD_TRENT, MPFEATURE_CHR_TRENT },
|
|
/*0x13*/ { BODY_JONATHAN, L_OPTIONS_032, HEAD_JONATHAN, MPFEATURE_CHR_JONATHAN },
|
|
/*0x14*/ { BODY_CILABTECH, L_OPTIONS_033, 1000, 0 },
|
|
/*0x15*/ { BODY_CIFEMTECH, L_OPTIONS_034, 1000, 0 },
|
|
/*0x16*/ { BODY_CISOLDIER, L_OPTIONS_035, 1000, 0 },
|
|
/*0x17*/ { BODY_DDSHOCK, L_OPTIONS_036, HEAD_DDSHOCK, 0 },
|
|
/*0x18*/ { BODY_FEM_GUARD, L_OPTIONS_037, 1000, MPFEATURE_CHR_FEMGUARD },
|
|
/*0x19*/ { BODY_DD_SECGUARD, L_OPTIONS_038, 1000, 0 },
|
|
/*0x1a*/ { BODY_DD_GUARD, L_OPTIONS_039, 1000, 0 },
|
|
/*0x1b*/ { BODY_DD_SHOCK_INF, L_OPTIONS_040, 1000, 0 },
|
|
/*0x1c*/ { BODY_SECRETARY, L_OPTIONS_041, 1000, 0 },
|
|
/*0x1d*/ { BODY_OFFICEWORKER, L_OPTIONS_042, 1000, MPFEATURE_CHR_OFFICEWORKER },
|
|
/*0x1e*/ { BODY_OFFICEWORKER2, L_OPTIONS_043, 1000, MPFEATURE_CHR_OFFICEWORKER },
|
|
/*0x1f*/ { BODY_NEGOTIATOR, L_OPTIONS_044, 1000, MPFEATURE_CHR_VILLACHRS },
|
|
/*0x20*/ { BODY_DDSNIPER, L_OPTIONS_045, HEAD_DDSNIPER, MPFEATURE_CHR_VILLACHRS },
|
|
/*0x21*/ { BODY_G5_GUARD, L_OPTIONS_046, 1000, MPFEATURE_CHR_G5 },
|
|
/*0x22*/ { BODY_G5_SWAT_GUARD, L_OPTIONS_047, 1000, MPFEATURE_CHR_G5 },
|
|
/*0x23*/ { BODY_CIAGUY, L_OPTIONS_048, 1000, MPFEATURE_CHR_CIAFBI },
|
|
/*0x24*/ { BODY_FBIGUY, L_OPTIONS_049, 1000, MPFEATURE_CHR_CIAFBI },
|
|
/*0x25*/ { BODY_AREA51GUARD, L_OPTIONS_050, 1000, MPFEATURE_CHR_INFILTRATION },
|
|
/*0x26*/ { BODY_A51TROOPER, L_OPTIONS_051, 1000, MPFEATURE_CHR_INFILTRATION },
|
|
/*0x27*/ { BODY_A51AIRMAN, L_OPTIONS_052, 1000, MPFEATURE_CHR_INFILTRATION },
|
|
/*0x28*/ { BODY_OVERALL, L_OPTIONS_053, 1000, MPFEATURE_CHR_INFILTRATION },
|
|
/*0x29*/ { BODY_STRIPES, L_OPTIONS_054, 1000, MPFEATURE_CHR_STRIPES },
|
|
/*0x2a*/ { BODY_LABTECH, L_OPTIONS_055, 1000, MPFEATURE_CHR_LABTECH },
|
|
/*0x2b*/ { BODY_FEMLABTECH, L_OPTIONS_056, 1000, MPFEATURE_CHR_LABTECH },
|
|
/*0x2c*/ { BODY_DD_LABTECH, L_OPTIONS_057, 1000, MPFEATURE_CHR_LABTECH },
|
|
/*0x2d*/ { BODY_BIOTECH, L_OPTIONS_058, HEAD_BIOTECH, MPFEATURE_CHR_BIOTECH },
|
|
/*0x2e*/ { BODY_ALASKAN_GUARD, L_OPTIONS_059, 1000, MPFEATURE_CHR_ALASKANGUARD },
|
|
/*0x2f*/ { BODY_PILOTAF1, L_OPTIONS_060, 1000, MPFEATURE_CHR_AF1 },
|
|
/*0x30*/ { BODY_STEWARD, L_OPTIONS_061, 1000, MPFEATURE_CHR_AF1 },
|
|
/*0x31*/ { BODY_STEWARDESS, L_OPTIONS_062, 1000, MPFEATURE_CHR_AF1 },
|
|
/*0x32*/ { BODY_STEWARDESS_COAT, L_OPTIONS_063, 1000, MPFEATURE_CHR_AF1 },
|
|
/*0x33*/ { BODY_PRESIDENT, L_OPTIONS_064, HEAD_PRESIDENT, MPFEATURE_CHR_CI },
|
|
/*0x34*/ { BODY_NSA_LACKEY, L_OPTIONS_065, 1000, MPFEATURE_CHR_NSALACKEY },
|
|
/*0x35*/ { BODY_PRES_SECURITY, L_OPTIONS_066, 1000, MPFEATURE_CHR_PRESSECURITY },
|
|
/*0x36*/ { BODY_PRESIDENT_CLONE2, L_OPTIONS_067, HEAD_PRESIDENT, MPFEATURE_CHR_PRESCLONE },
|
|
/*0x37*/ { BODY_PELAGIC_GUARD, L_OPTIONS_068, 1000, MPFEATURE_CHR_PELAGIC },
|
|
/*0x38*/ { BODY_MAIAN_SOLDIER, L_OPTIONS_069, HEAD_MAIAN_S, MPFEATURE_CHR_ELVIS },
|
|
/*0x39*/ { BODY_CONNERY, L_OPTIONS_070, 1000, MPFEATURE_8BOTS },
|
|
/*0x3a*/ { BODY_MOORE, L_OPTIONS_070, 1000, MPFEATURE_8BOTS },
|
|
/*0x3b*/ { BODY_DALTON, L_OPTIONS_070, 1000, MPFEATURE_8BOTS },
|
|
/*0x3c*/ { BODY_DJBOND, L_OPTIONS_070, 1000, MPFEATURE_8BOTS },
|
|
};
|
|
|
|
u32 g_MpMaleHeads[] = {
|
|
HEAD_JON,
|
|
HEAD_BEAU1,
|
|
HEAD_ROSS,
|
|
HEAD_MARK2,
|
|
HEAD_CHRIST,
|
|
HEAD_RUSS,
|
|
HEAD_DARLING,
|
|
HEAD_BRIAN,
|
|
HEAD_JAMIE,
|
|
HEAD_DUNCAN2,
|
|
HEAD_KEITH,
|
|
HEAD_STEVEM,
|
|
HEAD_GRANT,
|
|
HEAD_PENNY,
|
|
HEAD_DAVEC,
|
|
HEAD_JONES,
|
|
HEAD_GRAHAM,
|
|
HEAD_NEIL2,
|
|
HEAD_SHAUN,
|
|
HEAD_ROBIN,
|
|
HEAD_COOK,
|
|
HEAD_PRYCE,
|
|
HEAD_SILKE,
|
|
HEAD_SMITH,
|
|
HEAD_GARETH,
|
|
HEAD_MURCHIE,
|
|
HEAD_WONG,
|
|
HEAD_CARTER,
|
|
HEAD_TINTIN,
|
|
HEAD_MUNTON,
|
|
HEAD_STAMPER,
|
|
HEAD_PHELPS,
|
|
HEAD_EDMCG,
|
|
HEAD_MATT_C,
|
|
HEAD_PEER_S,
|
|
HEAD_ANDY_R,
|
|
HEAD_BEN_R,
|
|
HEAD_STEVE_K,
|
|
HEAD_SANCHEZ,
|
|
HEAD_TIM,
|
|
HEAD_KEN,
|
|
HEAD_SCOTT_H,
|
|
HEAD_JOEL,
|
|
#if VERSION != VERSION_JPN_FINAL
|
|
HEAD_MOTO,
|
|
#endif
|
|
};
|
|
|
|
u32 g_MpFemaleHeads[] = {
|
|
HEAD_ALEX,
|
|
HEAD_JULIANNE,
|
|
HEAD_LAURA,
|
|
HEAD_ANKA,
|
|
HEAD_LESLIE_S,
|
|
HEAD_EILEEN_T,
|
|
HEAD_EILEEN_H,
|
|
};
|
|
|
|
/**
|
|
* Calculate player awards, medals, and update character statistics.
|
|
*/
|
|
void mpCalculateAwards(void)
|
|
{
|
|
s32 playercount;
|
|
s32 i;
|
|
s32 j;
|
|
s32 prevplayernum;
|
|
s32 duration60;
|
|
struct awardmetrics metrics[MAX_PLAYERS];
|
|
|
|
// @bug: playerrankings should have 12 elements. Because it's too small,
|
|
// overflow occurs in mpGetPlayerRankings. The overflow writes into the
|
|
// metrics array (above) which is yet to be initialised, so this bug has
|
|
// no effect on IDO.
|
|
#ifdef AVOID_UB
|
|
struct ranking playerrankings[MAX_MPCHRS];
|
|
#else
|
|
struct ranking playerrankings[1];
|
|
#endif
|
|
|
|
s32 numchrs;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
s32 numteams;
|
|
struct ranking teamrankings[MAX_MPCHRS];
|
|
u32 stack[4];
|
|
#else
|
|
u32 stack[3];
|
|
#endif
|
|
|
|
playercount = PLAYERCOUNT();
|
|
|
|
duration60 = playerGetMissionTime();
|
|
|
|
func00033dd8();
|
|
|
|
numchrs = mpGetPlayerRankings(playerrankings);
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
numteams = (g_MpSetup.options & MPOPTION_TEAMSENABLED) ? mpGetTeamRankings(teamrankings) : 0;
|
|
#endif
|
|
|
|
prevplayernum = g_Vars.currentplayernum;
|
|
|
|
// Iterate all human players and update their character stats.
|
|
// At the same time, populate the metrics array
|
|
// which is a temporary array for award calculation.
|
|
for (i = 0; i < playercount; i++) {
|
|
struct mpchrconfig *mpchr = mpGetChrConfigBySlotNum(i);
|
|
struct mpplayerconfig *mpplayer = (struct mpplayerconfig *)mpchr;
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
s32 chrnum = mpGetChrIndexBySlotNum(i);
|
|
#else
|
|
s32 chrnum = i;
|
|
#endif
|
|
s32 sum;
|
|
|
|
setCurrentPlayerNum(i);
|
|
|
|
g_Vars.currentplayer->award1 = NULL;
|
|
g_Vars.currentplayer->award2 = NULL;
|
|
|
|
metrics[i].numshots = mpstatsGetPlayerShotCountByRegion(SHOTREGION_TOTAL);
|
|
metrics[i].numheadshots = mpstatsGetPlayerShotCountByRegion(SHOTREGION_HEAD);
|
|
metrics[i].numkills = 0;
|
|
metrics[i].numdeaths = 0;
|
|
metrics[i].numsuicides = 0;
|
|
|
|
for (j = 0; j < MAX_MPCHRS; j++) {
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (chrnum == j)
|
|
#else
|
|
if (i == j)
|
|
#endif
|
|
{
|
|
metrics[i].numsuicides += mpchr->killcounts[j];
|
|
} else {
|
|
metrics[i].numkills += mpchr->killcounts[j];
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < MAX_MPCHRS; j++) {
|
|
struct mpchrconfig *othermpchr = MPCHR(j);
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
metrics[i].numdeaths += othermpchr->killcounts[chrnum];
|
|
#else
|
|
metrics[i].numdeaths += othermpchr->killcounts[i];
|
|
#endif
|
|
}
|
|
|
|
metrics[i].ksratio = metrics[i].numkills * 100.0f / (metrics[i].numshots + 1.0f);
|
|
metrics[i].kdratio = metrics[i].numkills * 100.0f / (metrics[i].numdeaths + 1.0f);
|
|
metrics[i].backshotcount = g_Vars.playerstats[i].backshotcount;
|
|
metrics[i].drawplayercount = g_Vars.playerstats[i].drawplayercount;
|
|
metrics[i].avgkmperhour = g_Vars.playerstats[i].distance / 100000.0f / ((duration60 + 1) / (3600.0f * 60.0f));
|
|
metrics[i].armourcount = g_Vars.playerstats[i].armourcount;
|
|
metrics[i].awards = 0;
|
|
metrics[i].longestlife = g_Vars.playerstats[i].longestlife;
|
|
metrics[i].shortestlife = g_Vars.playerstats[i].shortestlife;
|
|
|
|
sum = mpstatsGetPlayerShotCountByRegion(SHOTREGION_HEAD)
|
|
+ mpstatsGetPlayerShotCountByRegion(SHOTREGION_BODY)
|
|
+ mpstatsGetPlayerShotCountByRegion(SHOTREGION_LIMB)
|
|
+ mpstatsGetPlayerShotCountByRegion(SHOTREGION_GUN)
|
|
+ mpstatsGetPlayerShotCountByRegion(SHOTREGION_HAT)
|
|
+ mpstatsGetPlayerShotCountByRegion(SHOTREGION_OBJECT);
|
|
|
|
if (metrics[i].numshots > 0) {
|
|
metrics[i].accuracyfrac = sum / (f32)metrics[i].numshots;
|
|
} else {
|
|
metrics[i].accuracyfrac = 0.0f;
|
|
}
|
|
|
|
if (metrics[i].accuracyfrac > 1.0f) {
|
|
metrics[i].accuracyfrac = 1.0f;
|
|
}
|
|
|
|
mpplayer->medals = 0;
|
|
|
|
if (!g_CheatsActiveBank0 && !g_CheatsActiveBank1) {
|
|
mpplayer->kills += metrics[i].numkills;
|
|
mpplayer->deaths += metrics[i].numdeaths;
|
|
mpplayer->gamesplayed++;
|
|
mpplayer->time += duration60 / 60;
|
|
mpplayer->distance += (u32)(g_Vars.playerstats[i].distance / 10000.0f);
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (metrics[i].numshots > 0) {
|
|
if (mpplayer->gamesplayed < 2) {
|
|
mpplayer->accuracy = metrics[i].accuracyfrac * 1000.0f;
|
|
} else {
|
|
mpplayer->accuracy = ((metrics[i].accuracyfrac * 0.3f) + (mpplayer->accuracy / 1000.0f * 0.7f)) * 1000.0f;
|
|
}
|
|
}
|
|
#else
|
|
mpplayer->accuracy = ((metrics[i].accuracyfrac * 0.3f) + (mpplayer->accuracy / 1000.0f * 0.7f)) * 1000.0f;
|
|
#endif
|
|
|
|
mpplayer->damagedealt += (u32)(g_Vars.playerstats[i].damtransmitted / 0.1f);
|
|
mpplayer->painreceived += (u32)(g_Vars.playerstats[i].damreceived / 0.1f);
|
|
mpplayer->headshots += metrics[i].numheadshots;
|
|
mpplayer->ammoused += metrics[i].numshots;
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if ((numchrs >= 2 && (g_MpSetup.options & MPOPTION_TEAMSENABLED) == 0) || numteams >= 2)
|
|
#else
|
|
if (numchrs >= 2)
|
|
#endif
|
|
{
|
|
if (mpplayer->base.placement == 0) {
|
|
bool lost = false;
|
|
|
|
for (j = 0; j < MAX_MPCHRS; j++) {
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (g_MpSetup.chrslots & (1 << j))
|
|
#endif
|
|
{
|
|
struct mpchrconfig *othermpchr = MPCHR(j);
|
|
|
|
if (othermpchr->rankablescore == mpplayer->base.rankablescore
|
|
&& othermpchr != &mpplayer->base
|
|
&& !((g_MpSetup.options & MPOPTION_TEAMSENABLED) && othermpchr->team == mpplayer->base.team)) {
|
|
lost = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!lost) {
|
|
mpplayer->gameswon++;
|
|
}
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (((g_MpSetup.options & MPOPTION_TEAMSENABLED) == 0 && numchrs == mpplayer->base.placement + 1)
|
|
|| ((g_MpSetup.options & MPOPTION_TEAMSENABLED) && numteams == mpplayer->base.placement + 1))
|
|
#else
|
|
if (mpplayer->base.placement == numchrs - 1)
|
|
#endif
|
|
{
|
|
bool won = false;
|
|
|
|
for (j = 0; j < MAX_MPCHRS; j++) {
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (g_MpSetup.chrslots & (1 << j))
|
|
#endif
|
|
{
|
|
struct mpchrconfig *othermpchr = MPCHR(j);
|
|
|
|
if (othermpchr->rankablescore == mpplayer->base.rankablescore
|
|
&& &mpplayer->base != othermpchr
|
|
&& !((g_MpSetup.options & MPOPTION_TEAMSENABLED) && othermpchr->team == mpplayer->base.team)) {
|
|
won = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!won) {
|
|
mpplayer->gameslost++;
|
|
}
|
|
}
|
|
}
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
else {
|
|
// empty
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
setCurrentPlayerNum(prevplayernum);
|
|
|
|
// Choose which players are eligible for which awards
|
|
i = mpFindMaxInt(playercount, metrics[0].numsuicides, metrics[1].numsuicides, metrics[2].numsuicides, metrics[3].numsuicides);
|
|
|
|
if (metrics[i].numsuicides > 0) {
|
|
metrics[i].awards |= AWARD_MOSTSUICIDAL;
|
|
}
|
|
|
|
i = mpFindMinInt(playercount, metrics[0].numshots, metrics[1].numshots, metrics[2].numshots, metrics[3].numshots);
|
|
|
|
if (metrics[i].numshots < 100) {
|
|
metrics[i].awards |= AWARD_WHONEEDSAMMO;
|
|
}
|
|
|
|
i = mpFindMinFloat(playercount, metrics[0].armourcount, metrics[1].armourcount, metrics[2].armourcount, metrics[3].armourcount);
|
|
|
|
if (metrics[i].armourcount <= 2.0f) {
|
|
metrics[i].awards |= AWARD_LEASTSHIELDED;
|
|
}
|
|
|
|
i = mpFindMaxFloat(playercount, metrics[0].armourcount, metrics[1].armourcount, metrics[2].armourcount, metrics[3].armourcount);
|
|
|
|
if (metrics[i].armourcount > 6.0f) {
|
|
metrics[i].awards |= AWARD_BESTPROTECTED;
|
|
}
|
|
|
|
i = mpFindMaxInt(playercount, metrics[0].numheadshots, metrics[1].numheadshots, metrics[2].numheadshots, metrics[3].numheadshots);
|
|
|
|
if (metrics[i].numheadshots > 0) {
|
|
metrics[i].awards |= AWARD_MARKSMANSHIP;
|
|
}
|
|
|
|
i = mpFindMaxFloat(playercount, metrics[0].ksratio, metrics[1].ksratio, metrics[2].ksratio, metrics[3].ksratio);
|
|
|
|
if (metrics[i].ksratio > 0.0f) {
|
|
metrics[i].awards |= AWARD_MOSTPROFESSIONAL;
|
|
}
|
|
|
|
i = mpFindMaxFloat(playercount, metrics[0].kdratio, metrics[1].kdratio, metrics[2].kdratio, metrics[3].kdratio);
|
|
|
|
if (metrics[i].kdratio > 0.0f) {
|
|
metrics[i].awards |= AWARD_MOSTDEADLY;
|
|
}
|
|
|
|
i = mpFindMinFloat(playercount, metrics[0].kdratio, metrics[1].kdratio, metrics[2].kdratio, metrics[3].kdratio);
|
|
|
|
if (PLAYERCOUNT() >= 2) {
|
|
metrics[i].awards |= AWARD_MOSTHARMLESS;
|
|
}
|
|
|
|
i = mpFindMinInt(playercount, metrics[0].drawplayercount, metrics[1].drawplayercount, metrics[2].drawplayercount, metrics[3].drawplayercount);
|
|
|
|
if (PLAYERCOUNT() >= 2) {
|
|
metrics[i].awards |= AWARD_MOSTCOWARDLY;
|
|
}
|
|
|
|
i = mpFindMaxFloat(playercount, metrics[0].avgkmperhour, metrics[1].avgkmperhour, metrics[2].avgkmperhour, metrics[3].avgkmperhour);
|
|
|
|
if (metrics[i].avgkmperhour > 10.0f) {
|
|
metrics[i].awards |= AWARD_MOSTFRANTIC;
|
|
}
|
|
|
|
i = mpFindMinInt(playercount, metrics[0].backshotcount, metrics[1].backshotcount, metrics[2].backshotcount, metrics[3].backshotcount);
|
|
metrics[i].awards |= AWARD_MOSTHONORABLE;
|
|
|
|
i = mpFindMaxInt(playercount, metrics[0].backshotcount, metrics[1].backshotcount, metrics[2].backshotcount, metrics[3].backshotcount);
|
|
|
|
if (metrics[i].backshotcount > 0 && (metrics[i].awards & AWARD_MOSTHONORABLE) == 0) {
|
|
metrics[i].awards |= AWARD_MOSTDISHONORABLE;
|
|
}
|
|
|
|
i = mpFindMaxInt(playercount, metrics[0].longestlife, metrics[1].longestlife, metrics[2].longestlife, metrics[3].longestlife);
|
|
|
|
if (metrics[i].longestlife > 0) {
|
|
metrics[i].awards |= AWARD_LONGESTLIFE;
|
|
}
|
|
|
|
i = mpFindMinInt(playercount, metrics[0].shortestlife, metrics[1].shortestlife, metrics[2].shortestlife, metrics[3].shortestlife);
|
|
|
|
if (metrics[i].shortestlife > 0 && metrics[i].numdeaths > 0) {
|
|
metrics[i].awards |= AWARD_SHORTESTLIFE;
|
|
}
|
|
|
|
for (i = 0; i < playercount; i++) {
|
|
if (g_Vars.playerstats[i].maxsimulkills == 4) {
|
|
metrics[i].awards |= AWARD_QUADKILL;
|
|
} else if (g_Vars.playerstats[i].maxsimulkills == 3) {
|
|
metrics[i].awards |= AWARD_TRIPLEKILL;
|
|
} else if (g_Vars.playerstats[i].maxsimulkills == 2) {
|
|
metrics[i].awards |= AWARD_DOUBLEKILL;
|
|
}
|
|
}
|
|
|
|
// These loops would have printed each player's awards
|
|
for (i = 0; i < playercount; i++) {
|
|
for (j = 0; j < 17; j++) {
|
|
if (metrics[i].awards & (1 << j));
|
|
}
|
|
}
|
|
|
|
// For each player, choose which two awards to actually give them.
|
|
// Note that the first award checked is quad kill, but after that the awards
|
|
// are checked randomly. So if a player has quad kill they'll definitely see
|
|
// it on the endscreen, but this is not the case for triple kill or any
|
|
// other awards.
|
|
for (i = 0; i < playercount; i++) {
|
|
s32 numdone = 0;
|
|
s32 awardindex = 16;
|
|
|
|
if (playercount == 1) {
|
|
// In a single player game, only allow the following awards
|
|
metrics[i].awards &= 0
|
|
| AWARD_MARKSMANSHIP
|
|
| AWARD_MOSTPROFESSIONAL
|
|
| AWARD_MOSTDEADLY
|
|
| AWARD_MOSTFRANTIC
|
|
| AWARD_MOSTHONORABLE
|
|
| AWARD_DOUBLEKILL
|
|
| AWARD_TRIPLEKILL
|
|
| AWARD_QUADKILL;
|
|
}
|
|
|
|
while (numdone == 0) {
|
|
if (metrics[i].awards & (1 << awardindex)) {
|
|
metrics[i].awards &= ~(1 << awardindex);
|
|
g_Vars.players[i]->award1 = langGet(g_AwardNames[awardindex]);
|
|
numdone = 1;
|
|
}
|
|
|
|
if (metrics[i].awards == 0) {
|
|
numdone = 1;
|
|
}
|
|
|
|
awardindex = random() % 17;
|
|
}
|
|
|
|
while (numdone < 2) {
|
|
awardindex = random() % 17;
|
|
|
|
if (metrics[i].awards & (1 << awardindex)) {
|
|
metrics[i].awards &= ~(1 << awardindex);
|
|
g_Vars.players[i]->award2 = langGet(g_AwardNames[awardindex]);
|
|
numdone = 2;
|
|
}
|
|
|
|
if (metrics[i].awards == 0) {
|
|
numdone = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate KillMaster and Survivor medals
|
|
if (numchrs >= 2) {
|
|
s32 mostkillsvalue = 0;
|
|
s32 mostkillsplayer = -1;
|
|
s32 leastdeathsvalue = 0xffffff;
|
|
s32 leastdeathsplayer = -1;
|
|
s32 k;
|
|
|
|
for (k = 0; k < MAX_MPCHRS; k++) {
|
|
if (g_MpSetup.chrslots & (1 << k)) {
|
|
s32 totalkills = 0;
|
|
struct mpchrconfig *mpchr = MPCHR(k);
|
|
|
|
for (j = 0; j < MAX_MPCHRS; j++) {
|
|
// @bug: i should be k. The value of i was incremented after
|
|
// the last iteration of its loop above so it'll be between
|
|
// 1 and 4 inclusively depending on the number of players.
|
|
// The bot nums start from 4 regardless of how many players
|
|
// there are. So in a game with 4 humans, the kills on the
|
|
// first bot will not be considered for the killmaster medal
|
|
// which means the medal could go to a player who got fewer
|
|
// total kills. Additionally, suicides are counted as kills
|
|
// while the intention here was to omit them.
|
|
if (i != j) {
|
|
totalkills += mpchr->killcounts[j];
|
|
}
|
|
}
|
|
|
|
if (totalkills == mostkillsvalue) {
|
|
mostkillsplayer = -1;
|
|
}
|
|
|
|
if (totalkills > mostkillsvalue) {
|
|
mostkillsplayer = k;
|
|
mostkillsvalue = totalkills;
|
|
}
|
|
|
|
if (mpchr->numdeaths == leastdeathsvalue) {
|
|
leastdeathsplayer = -1;
|
|
}
|
|
|
|
if (mpchr->numdeaths < leastdeathsvalue) {
|
|
leastdeathsplayer = k;
|
|
leastdeathsvalue = mpchr->numdeaths;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!g_CheatsActiveBank0 && !g_CheatsActiveBank1) {
|
|
if (mostkillsplayer < 4 && mostkillsplayer >= 0) {
|
|
struct mpplayerconfig *mpplayer = (struct mpplayerconfig *)MPCHR(mostkillsplayer);
|
|
mpplayer->medals |= MEDAL_KILLMASTER;
|
|
mpplayer->killmastermedals++;
|
|
}
|
|
|
|
if (leastdeathsplayer < 4 && leastdeathsplayer >= 0) {
|
|
struct mpplayerconfig *mpplayer = (struct mpplayerconfig *)MPCHR(leastdeathsplayer);
|
|
mpplayer->medals |= MEDAL_SURVIVOR;
|
|
mpplayer->survivormedals++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate Headshot and Accuracy medals
|
|
if (!g_CheatsActiveBank0 && !g_CheatsActiveBank1 && playercount >= 2) {
|
|
s32 mostheadshotvalue = 0;
|
|
f32 mostaccuratevalue = 0.5f;
|
|
s32 mostheadshotplayer = -1;
|
|
s32 mostaccurateplayer = -1;
|
|
|
|
for (i = 0; i < playercount; i++) {
|
|
if (mostheadshotvalue == metrics[i].numheadshots) {
|
|
mostheadshotplayer = -1;
|
|
}
|
|
|
|
if (metrics[i].numheadshots > mostheadshotvalue) {
|
|
mostheadshotplayer = i;
|
|
mostheadshotvalue = metrics[i].numheadshots;
|
|
}
|
|
|
|
if (metrics[i].accuracyfrac > mostaccuratevalue) {
|
|
mostaccurateplayer = i;
|
|
mostaccuratevalue = metrics[i].accuracyfrac;
|
|
}
|
|
}
|
|
|
|
if (mostheadshotplayer >= 0) {
|
|
struct mpplayerconfig *mpplayer = (struct mpplayerconfig *)mpGetChrConfigBySlotNum(mostheadshotplayer);
|
|
mpplayer->medals |= MEDAL_HEADSHOT;
|
|
mpplayer->headshotmedals++;
|
|
}
|
|
|
|
if (mostaccurateplayer >= 0) {
|
|
struct mpplayerconfig *mpplayer = (struct mpplayerconfig *)mpGetChrConfigBySlotNum(mostaccurateplayer);
|
|
mpplayer->medals |= MEDAL_ACCURACY;
|
|
mpplayer->accuracymedals++;
|
|
}
|
|
}
|
|
|
|
// Recalculate title for all players
|
|
for (i = 0; i < playercount; i++) {
|
|
struct mpplayerconfig *mpchr = (struct mpplayerconfig *)mpGetChrConfigBySlotNum(i);
|
|
mpCalculatePlayerTitle(mpchr);
|
|
}
|
|
}
|
|
|
|
void mpEndMatch(void)
|
|
{
|
|
s32 stack;
|
|
s32 playercount = PLAYERCOUNT();
|
|
s32 prevplayernum;
|
|
s32 i;
|
|
|
|
musicStartMenu();
|
|
mpSetPaused(MPPAUSEMODE_GAMEOVER);
|
|
|
|
prevplayernum = g_Vars.currentplayernum;
|
|
|
|
for (i = 0; i < playercount; i++) {
|
|
setCurrentPlayerNum(i);
|
|
|
|
g_Vars.currentplayer->award1 = NULL;
|
|
g_Vars.currentplayer->award2 = NULL;
|
|
|
|
if (g_Vars.currentplayer->visionmode == VISIONMODE_SLAYERROCKET) {
|
|
g_Vars.currentplayer->visionmode = VISIONMODE_NORMAL;
|
|
}
|
|
}
|
|
|
|
setCurrentPlayerNum(prevplayernum);
|
|
mpCalculateAwards();
|
|
|
|
if (g_BossFile.locktype == MPLOCKTYPE_CHALLENGE) {
|
|
challengeConsiderMarkingComplete();
|
|
}
|
|
|
|
func0f0f820c(NULL, -6);
|
|
}
|
|
|
|
s32 mpGetNumHeads2(void)
|
|
{
|
|
return ARRAYCOUNT(g_MpHeads);
|
|
}
|
|
|
|
s32 mpGetNumHeads(void)
|
|
{
|
|
return ARRAYCOUNT(g_MpHeads);
|
|
}
|
|
|
|
s32 mpGetHeadId(u8 headnum)
|
|
{
|
|
return g_MpHeads[headnum].headnum;
|
|
}
|
|
|
|
s32 mpGetHeadRequiredFeature(u8 headnum)
|
|
{
|
|
return g_MpHeads[headnum].requirefeature;
|
|
}
|
|
|
|
s32 mpGetBeauHeadId(u8 headnum)
|
|
{
|
|
return g_MpBeauHeads[headnum].headnum;
|
|
}
|
|
|
|
s32 mpGetNumBeauHeads(void)
|
|
{
|
|
return ARRAYCOUNT(g_MpBeauHeads);
|
|
}
|
|
|
|
u32 mpGetNumBodies(void)
|
|
{
|
|
return ARRAYCOUNT(g_MpBodies);
|
|
}
|
|
|
|
s32 mpGetBodyId(u8 bodynum)
|
|
{
|
|
/**
|
|
* @bug: bodynum 61 (0x3d) would cause an array overflow.
|
|
* ARRAYCOUNT(g_MpBodies) is 61.
|
|
*/
|
|
if (bodynum > ARRAYCOUNT(g_MpBodies)) {
|
|
if (bodynum == ARRAYCOUNT(g_MpBodies) + 1) {
|
|
return BODY_DRCAROLL;
|
|
}
|
|
|
|
return BODY_DARK_COMBAT;
|
|
}
|
|
|
|
return g_MpBodies[bodynum].bodynum;
|
|
}
|
|
|
|
s32 mpGetMpbodynumByBodynum(u16 bodynum)
|
|
{
|
|
s32 i;
|
|
|
|
if (bodynum == BODY_DRCAROLL) {
|
|
return ARRAYCOUNT(g_MpBodies) + 1;
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpBodies); i++) {
|
|
if (g_MpBodies[i].bodynum == bodynum) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return g_MpBodies[0].bodynum;
|
|
}
|
|
|
|
char *mpGetBodyName(u8 mpbodynum)
|
|
{
|
|
// @bug: This should be >=
|
|
if (mpbodynum > ARRAYCOUNT(g_MpBodies)) {
|
|
mpbodynum = 0;
|
|
}
|
|
|
|
return langGet(g_MpBodies[mpbodynum].name);
|
|
}
|
|
|
|
u8 mpGetBodyRequiredFeature(u8 mpbodynum)
|
|
{
|
|
// @bug: This should be >=
|
|
if (mpbodynum > ARRAYCOUNT(g_MpBodies)) {
|
|
mpbodynum = 0;
|
|
}
|
|
|
|
return g_MpBodies[mpbodynum].requirefeature;
|
|
}
|
|
|
|
s32 mpGetMpheadnumByMpbodynum(s32 mpbodynum)
|
|
{
|
|
s32 headnum;
|
|
s32 index = 0;
|
|
s32 i;
|
|
|
|
if (mpbodynum >= HEAD_VD) {
|
|
mpbodynum = 0;
|
|
}
|
|
|
|
headnum = g_MpBodies[mpbodynum].headnum;
|
|
|
|
if (headnum == 1000) {
|
|
if (g_HeadsAndBodies[g_MpBodies[mpbodynum].bodynum].ismale) {
|
|
headnum = g_MpMaleHeads[random() % ARRAYCOUNT(g_MpMaleHeads)];
|
|
} else {
|
|
headnum = g_MpFemaleHeads[random() % ARRAYCOUNT(g_MpFemaleHeads)];
|
|
}
|
|
}
|
|
|
|
for (i = 0; i != ARRAYCOUNT(g_MpHeads); i++) {
|
|
if (g_MpHeads[i].headnum == headnum) {
|
|
index = i;
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void mpFindUnusedHeadAndBody(u8 *mpheadnum, u8 *mpbodynum)
|
|
{
|
|
struct mpchrconfig *mpchr;
|
|
bool available;
|
|
u8 trympheadnum;
|
|
u8 trympbodynum;
|
|
s32 i;
|
|
|
|
do {
|
|
available = true;
|
|
trympheadnum = random() % ARRAYCOUNT(g_MpHeads);
|
|
trympbodynum = random() % ARRAYCOUNT(g_MpBodies);
|
|
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
mpchr = MPCHR(i);
|
|
|
|
if (mpchr->mpheadnum == trympheadnum) {
|
|
available = false;
|
|
}
|
|
|
|
if (mpchr->mpbodynum == trympbodynum) {
|
|
available = false;
|
|
}
|
|
}
|
|
}
|
|
} while (!available);
|
|
|
|
if (1);
|
|
|
|
*mpheadnum = trympheadnum;
|
|
*mpbodynum = trympbodynum;
|
|
}
|
|
|
|
s32 mpChooseRandomLockPlayer(void)
|
|
{
|
|
s32 start = random() % 4;
|
|
s32 i;
|
|
|
|
for (i = (start + 1) % 4;; i = (i + 1) % 4) {
|
|
if ((g_MpSetup.chrslots & (1 << i)) || i == start) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
bool mpSetLock(s32 locktype, s32 playernum)
|
|
{
|
|
g_BossFile.locktype = locktype;
|
|
|
|
if (g_BossFile.locktype == MPLOCKTYPE_RANDOM) {
|
|
g_MpLockInfo.lockedplayernum = mpChooseRandomLockPlayer();
|
|
} else {
|
|
g_MpLockInfo.lockedplayernum = playernum;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
s32 mpGetLockType(void)
|
|
{
|
|
return g_BossFile.locktype;
|
|
}
|
|
|
|
u32 mpGetLockPlayerNum(void)
|
|
{
|
|
return g_MpLockInfo.lockedplayernum;
|
|
}
|
|
|
|
bool mpIsPlayerLockedOut(s32 playernum)
|
|
{
|
|
if (g_BossFile.locktype == MPLOCKTYPE_NONE) {
|
|
return false;
|
|
}
|
|
|
|
if (g_MpLockInfo.lockedplayernum == playernum) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void mpCalculateLockIfLastWinnerOrLoser(void)
|
|
{
|
|
if (g_BossFile.locktype == MPLOCKTYPE_LASTWINNER && g_MpLockInfo.lastwinner >= 0) {
|
|
g_MpLockInfo.lockedplayernum = g_MpLockInfo.lastwinner;
|
|
}
|
|
|
|
if (g_BossFile.locktype == MPLOCKTYPE_LASTLOSER && g_MpLockInfo.lastloser >= 0) {
|
|
g_MpLockInfo.lockedplayernum = g_MpLockInfo.lastloser;
|
|
}
|
|
|
|
if (g_MpLockInfo.lockedplayernum >= 0
|
|
&& g_BossFile.locktype != MPLOCKTYPE_CHALLENGE
|
|
&& (g_MpSetup.chrslots & (1 << g_MpLockInfo.lockedplayernum)) == 0) {
|
|
g_MpLockInfo.lastwinner = g_MpLockInfo.lastloser = -1;
|
|
g_MpLockInfo.lockedplayernum = mpChooseRandomLockPlayer();
|
|
}
|
|
}
|
|
|
|
struct mptrack g_MpTracks[] = {
|
|
// Audio ID, duration, name, unlock after stage
|
|
/*0x00*/ { MUSIC_DARK_COMBAT, 160, L_MISC_124, -1 }, // "Dark Combat"
|
|
/*0x01*/ { MUSIC_SKEDAR_MYSTERY, 170, L_MISC_125, -1 }, // "Skedar Mystery"
|
|
/*0x02*/ { MUSIC_CI_OPERATIVE, 170, L_MISC_126, -1 }, // "CI Operative"
|
|
/*0x03*/ { MUSIC_DATADYNE_ACTION, 180, L_MISC_127, -1 }, // "dataDyne Action"
|
|
/*0x04*/ { MUSIC_MAIAN_TEARS, 200, L_MISC_128, -1 }, // "Maian Tears"
|
|
/*0x05*/ { MUSIC_ALIEN_CONFLICT, 197, L_MISC_129, -1 }, // "Alien Conflict"
|
|
/*0x06*/ { MUSIC_CI, 120, L_MISC_130, -1 }, // "Carrington Institute"
|
|
/*0x07*/ { MUSIC_DEFECTION, 120, L_MISC_131, SOLOSTAGEINDEX_DEFECTION }, // "dD Central"
|
|
/*0x08*/ { MUSIC_DEFECTION_X, 120, L_MISC_132, SOLOSTAGEINDEX_DEFECTION }, // "dD Central X"
|
|
/*0x09*/ { MUSIC_INVESTIGATION, 120, L_MISC_133, SOLOSTAGEINDEX_INVESTIGATION }, // "dD Research"
|
|
/*0x0a*/ { MUSIC_INVESTIGATION_X, 120, L_MISC_134, SOLOSTAGEINDEX_INVESTIGATION }, // "dD Research X"
|
|
/*0x0b*/ { MUSIC_EXTRACTION, 120, L_MISC_135, SOLOSTAGEINDEX_EXTRACTION }, // "dD Extraction"
|
|
/*0x0c*/ { MUSIC_EXTRACTION_X, 120, L_MISC_136, SOLOSTAGEINDEX_EXTRACTION }, // "dD Extraction X"
|
|
/*0x0d*/ { MUSIC_VILLA, 120, L_MISC_137, SOLOSTAGEINDEX_VILLA }, // "Carrington Villa"
|
|
/*0x0e*/ { MUSIC_VILLA_X, 120, L_MISC_138, SOLOSTAGEINDEX_VILLA }, // "Carrington Villa X"
|
|
/*0x0f*/ { MUSIC_CHICAGO, 120, L_MISC_139, SOLOSTAGEINDEX_CHICAGO }, // "Chicago"
|
|
/*0x10*/ { MUSIC_CHICAGO_X, 120, L_MISC_140, SOLOSTAGEINDEX_CHICAGO }, // "Chicago X"
|
|
/*0x11*/ { MUSIC_G5, 120, L_MISC_141, SOLOSTAGEINDEX_G5BUILDING }, // "G5 Building"
|
|
/*0x12*/ { MUSIC_G5_X, 120, L_MISC_142, SOLOSTAGEINDEX_G5BUILDING }, // "G5 Building X"
|
|
/*0x13*/ { MUSIC_INFILTRATION, 120, L_MISC_143, SOLOSTAGEINDEX_INFILTRATION }, // "A51 Infiltration"
|
|
/*0x14*/ { MUSIC_INFILTRATION_X, 120, L_MISC_144, SOLOSTAGEINDEX_INFILTRATION }, // "A51 Infiltration X"
|
|
/*0x15*/ { MUSIC_RESCUE, 120, L_MISC_145, SOLOSTAGEINDEX_RESCUE }, // "A51 Rescue"
|
|
/*0x16*/ { MUSIC_RESCUE_X, 120, L_MISC_146, SOLOSTAGEINDEX_RESCUE }, // "A51 Rescue X"
|
|
/*0x17*/ { MUSIC_ESCAPE, 120, L_MISC_147, SOLOSTAGEINDEX_ESCAPE }, // "A51 Escape"
|
|
/*0x18*/ { MUSIC_ESCAPE_X, 120, L_MISC_148, SOLOSTAGEINDEX_ESCAPE }, // "A51 Escape X"
|
|
/*0x19*/ { MUSIC_AIRBASE, 120, L_MISC_149, SOLOSTAGEINDEX_AIRBASE }, // "Air Base"
|
|
/*0x1a*/ { MUSIC_AIRBASE_X, 120, L_MISC_150, SOLOSTAGEINDEX_AIRBASE }, // "Air Base X"
|
|
/*0x1b*/ { MUSIC_AIRFORCEONE, 120, L_MISC_151, SOLOSTAGEINDEX_AIRFORCEONE }, // "Air Force One"
|
|
/*0x1c*/ { MUSIC_AIRFORCEONE_X, 120, L_MISC_152, SOLOSTAGEINDEX_AIRFORCEONE }, // "Air Force One X"
|
|
/*0x1d*/ { MUSIC_CRASHSITE, 120, L_MISC_153, SOLOSTAGEINDEX_CRASHSITE }, // "Crash Site"
|
|
/*0x1e*/ { MUSIC_CRASHSITE_X, 120, L_MISC_154, SOLOSTAGEINDEX_CRASHSITE }, // "Crash Site X"
|
|
/*0x1f*/ { MUSIC_PELAGIC, 120, L_MISC_155, SOLOSTAGEINDEX_PELAGIC }, // "Pelagic II"
|
|
/*0x20*/ { MUSIC_PELAGIC_X, 120, L_MISC_156, SOLOSTAGEINDEX_PELAGIC }, // "Pelagic II X"
|
|
/*0x21*/ { MUSIC_DEEPSEA, 120, L_MISC_157, SOLOSTAGEINDEX_DEEPSEA }, // "Deep Sea"
|
|
/*0x22*/ { MUSIC_DEEPSEA_X, 120, L_MISC_158, SOLOSTAGEINDEX_DEEPSEA }, // "Deep Sea X"
|
|
/*0x23*/ { MUSIC_DEFENSE, 120, L_MISC_159, SOLOSTAGEINDEX_DEFENSE }, // "Institute Defense"
|
|
/*0x24*/ { MUSIC_DEFENSE_X, 120, L_MISC_160, SOLOSTAGEINDEX_DEFENSE }, // "Institute Defense X"
|
|
/*0x25*/ { MUSIC_ATTACKSHIP, 120, L_MISC_161, SOLOSTAGEINDEX_ATTACKSHIP }, // "Attack Ship"
|
|
/*0x26*/ { MUSIC_ATTACKSHIP_X, 120, L_MISC_162, SOLOSTAGEINDEX_ATTACKSHIP }, // "Attack Ship X"
|
|
/*0x27*/ { MUSIC_SKEDARRUINS, 120, L_MISC_163, SOLOSTAGEINDEX_SKEDARRUINS }, // "Skedar Ruins"
|
|
/*0x28*/ { MUSIC_SKEDARRUINS_X, 120, L_MISC_164, SOLOSTAGEINDEX_SKEDARRUINS }, // "Skedar Ruins X"
|
|
/*0x29*/ { MUSIC_CREDITS, 120, L_MISC_165, SOLOSTAGEINDEX_SKEDARRUINS }, // "End Credits"
|
|
/*0x30*/ { MUSIC_SKEDARRUINS_KING,120, L_MISC_261, SOLOSTAGEINDEX_SKEDARRUINS }, // "Skedar Warrior" (Skedar Leader)
|
|
};
|
|
|
|
bool mpIsTrackUnlocked(s32 tracknum)
|
|
{
|
|
s16 stageindex = g_MpTracks[tracknum].unlockstage;
|
|
bool unlocked = false;
|
|
u32 i;
|
|
|
|
if (stageindex < 0 || stageindex > SOLOSTAGEINDEX_SKEDARRUINS) {
|
|
unlocked = true;
|
|
} else {
|
|
for (i = 0; i != 3; i++) {
|
|
if (g_GameFile.besttimes[stageindex][i]) {
|
|
unlocked = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return unlocked;
|
|
}
|
|
|
|
s32 mpGetTrackSlotIndex(s32 tracknum)
|
|
{
|
|
s32 i;
|
|
s32 slotindex = 0;
|
|
|
|
for (i = 0; i < tracknum; i++) {
|
|
if (mpIsTrackUnlocked(i)) {
|
|
slotindex++;
|
|
}
|
|
}
|
|
|
|
return slotindex;
|
|
}
|
|
|
|
s32 mpGetTrackNumAtSlotIndex(s32 slotindex)
|
|
{
|
|
s32 i;
|
|
s32 numunlocked = 0;
|
|
|
|
for (i = 0; i != ARRAYCOUNT(g_MpTracks); i++) {
|
|
if (mpIsTrackUnlocked(i)) {
|
|
if (numunlocked == slotindex) {
|
|
break;
|
|
}
|
|
|
|
numunlocked++;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
s32 mpGetNumUnlockedTracks(void)
|
|
{
|
|
return mpGetTrackSlotIndex(ARRAYCOUNT(g_MpTracks));
|
|
}
|
|
|
|
s32 mpGetTrackMusicNum(s32 slotindex)
|
|
{
|
|
s32 tracknum = mpGetTrackNumAtSlotIndex(slotindex);
|
|
|
|
return g_MpTracks[tracknum].musicnum;
|
|
}
|
|
|
|
char *mpGetTrackName(s32 slotindex)
|
|
{
|
|
s32 tracknum = mpGetTrackNumAtSlotIndex(slotindex);
|
|
|
|
return langGet(g_MpTracks[tracknum].name);
|
|
}
|
|
|
|
void mpSetUsingMultipleTunes(bool enable)
|
|
{
|
|
g_BossFile.usingmultipletunes = enable;
|
|
}
|
|
|
|
bool mpGetUsingMultipleTunes(void)
|
|
{
|
|
return g_BossFile.usingmultipletunes;
|
|
}
|
|
|
|
bool mpIsMultiTrackSlotEnabled(s32 slot)
|
|
{
|
|
s32 tracknum = mpGetTrackNumAtSlotIndex(slot);
|
|
u8 index = tracknum >> 3;
|
|
u8 value = 1 << (tracknum & 7);
|
|
|
|
if ((g_BossFile.multipletracknums[index] & value) == 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void mpSetMultiTrackSlotEnabled(s32 slot, bool enable)
|
|
{
|
|
s32 tracknum = mpGetTrackNumAtSlotIndex(slot);
|
|
u8 value = 1 << (tracknum & 7);
|
|
u8 index = tracknum >> 3;
|
|
|
|
if (enable) {
|
|
g_BossFile.multipletracknums[index] |= value;
|
|
} else {
|
|
g_BossFile.multipletracknums[index] &= ~value;
|
|
}
|
|
}
|
|
|
|
void mpSetTrackSlotEnabled(s32 slot)
|
|
{
|
|
if (mpGetUsingMultipleTunes()) {
|
|
mpSetMultiTrackSlotEnabled(slot, 1 - mpIsMultiTrackSlotEnabled(slot));
|
|
} else {
|
|
g_BossFile.tracknum = mpGetTrackNumAtSlotIndex(slot);
|
|
}
|
|
}
|
|
|
|
void mpEnableAllMultiTracks(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i != ARRAYCOUNT(g_BossFile.multipletracknums); i++) {
|
|
g_BossFile.multipletracknums[i] = 0xff;
|
|
}
|
|
}
|
|
|
|
void mpDisableAllMultiTracks(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i != ARRAYCOUNT(g_BossFile.multipletracknums); i++) {
|
|
g_BossFile.multipletracknums[i] = 0;
|
|
}
|
|
}
|
|
|
|
void mpRandomiseMultiTracks(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i != ARRAYCOUNT(g_BossFile.multipletracknums); i++) {
|
|
g_BossFile.multipletracknums[i] = random();
|
|
}
|
|
}
|
|
|
|
void mpSetTrackToRandom(void)
|
|
{
|
|
g_BossFile.tracknum = -1;
|
|
}
|
|
|
|
s32 mpGetCurrentTrackSlotNum(void)
|
|
{
|
|
if (g_BossFile.tracknum < 0) {
|
|
return g_BossFile.tracknum;
|
|
}
|
|
|
|
return mpGetTrackSlotIndex(g_BossFile.tracknum);
|
|
}
|
|
|
|
s32 mpChooseTrack(void)
|
|
{
|
|
s32 i;
|
|
s32 tracknum;
|
|
|
|
if (mpGetUsingMultipleTunes()) {
|
|
s32 numunlocked = mpGetNumUnlockedTracks();
|
|
s32 numselected = 0;
|
|
|
|
for (i = 0; i < numunlocked; i++) {
|
|
if (mpIsMultiTrackSlotEnabled(i)) {
|
|
numselected++;
|
|
}
|
|
}
|
|
|
|
if (numselected == 0) {
|
|
do {
|
|
tracknum = mpGetTrackNumAtSlotIndex(random() % numunlocked);
|
|
} while (tracknum == g_MpLockInfo.unk04);
|
|
|
|
g_MpLockInfo.unk04 = tracknum;
|
|
g_MusicLife60 = g_MpTracks[tracknum].duration * TICKS(60);
|
|
|
|
return g_MpTracks[tracknum].musicnum;
|
|
}
|
|
|
|
do {
|
|
s32 selectionindex = random() % numselected;
|
|
s32 selectioncount = 0;
|
|
tracknum = -1;
|
|
|
|
for (i = 0; i < numunlocked; i++) {
|
|
if (mpIsMultiTrackSlotEnabled(i)) {
|
|
if (selectionindex == selectioncount) {
|
|
tracknum = i;
|
|
}
|
|
|
|
selectioncount++;
|
|
}
|
|
}
|
|
|
|
if (tracknum == -1) {
|
|
g_MusicLife60 = g_MpTracks[0].duration * TICKS(60);
|
|
return g_MpTracks[0].musicnum;
|
|
}
|
|
|
|
tracknum = mpGetTrackNumAtSlotIndex(tracknum);
|
|
} while (numselected > 1 && tracknum == g_MpLockInfo.unk04);
|
|
|
|
g_MpLockInfo.unk04 = tracknum;
|
|
g_MusicLife60 = g_MpTracks[tracknum].duration * TICKS(60);
|
|
|
|
return g_MpTracks[tracknum].musicnum;
|
|
}
|
|
|
|
tracknum = mpGetCurrentTrackSlotNum();
|
|
|
|
if (tracknum < 0) {
|
|
s32 numunlocked = mpGetNumUnlockedTracks();
|
|
|
|
do {
|
|
tracknum = mpGetTrackNumAtSlotIndex(random() % numunlocked);
|
|
} while (tracknum == g_MpLockInfo.unk04);
|
|
|
|
g_MpLockInfo.unk04 = tracknum;
|
|
g_MusicLife60 = g_MpTracks[tracknum].duration * TICKS(60);
|
|
return g_MpTracks[tracknum].musicnum;
|
|
}
|
|
|
|
tracknum = mpGetTrackNumAtSlotIndex(tracknum);
|
|
g_MpLockInfo.unk04 = tracknum;
|
|
g_MusicLife60 = g_MpTracks[tracknum].duration * TICKS(60);
|
|
|
|
return g_MpTracks[tracknum].musicnum;
|
|
}
|
|
|
|
struct mpchrconfig *mpGetChrConfigBySlotNum(s32 slot)
|
|
{
|
|
s32 count = 0;
|
|
struct mpchrconfig *result = NULL;
|
|
s32 i;
|
|
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
if (count == slot) {
|
|
result = MPCHR(i);
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
s32 mpGetChrIndexBySlotNum(s32 slot)
|
|
{
|
|
s32 count = 0;
|
|
s32 result = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
if (count == slot) {
|
|
result = i;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
s32 mpGetNumChrs(void)
|
|
{
|
|
s32 count = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i != MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
u8 mpFindUnusedTeamNum(void)
|
|
{
|
|
u8 teamnum = 0;
|
|
bool available = false;
|
|
s32 i;
|
|
|
|
while (teamnum < 7 && !available) {
|
|
available = true;
|
|
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
struct mpchrconfig *mpchr = MPCHR(i);
|
|
|
|
if (mpchr->team == teamnum) {
|
|
available = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!available) {
|
|
teamnum++;
|
|
}
|
|
}
|
|
|
|
if (teamnum > 7) {
|
|
teamnum = 7;
|
|
}
|
|
|
|
return teamnum;
|
|
}
|
|
|
|
void mpCreateBotFromProfile(s32 botnum, u8 profilenum)
|
|
{
|
|
s32 headnum = 0;
|
|
u8 team = mpFindUnusedTeamNum();
|
|
bool available = false;
|
|
s32 i;
|
|
|
|
g_BotConfigsArray[botnum].type = g_BotProfiles[profilenum].type;
|
|
g_BotConfigsArray[botnum].difficulty = g_BotProfiles[profilenum].difficulty;
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
g_MpSimulantDifficultiesPerNumPlayers[botnum][i] = g_BotConfigsArray[botnum].difficulty;
|
|
}
|
|
|
|
g_MpSetup.chrslots |= 1 << (botnum + 4);
|
|
strcpy(g_BotConfigsArray[botnum].base.name, "Sim\n");
|
|
g_BotConfigsArray[botnum].base.team = team;
|
|
|
|
while (!available) {
|
|
headnum = g_BotHeads[random() % ARRAYCOUNT(g_BotHeads)];
|
|
available = true;
|
|
|
|
for (i = 0; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
struct mpchrconfig *mpchr = MPCHR(i);
|
|
|
|
if (mpchr->mpheadnum == headnum) {
|
|
available = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
g_BotConfigsArray[botnum].base.mpheadnum = headnum;
|
|
g_BotConfigsArray[botnum].base.mpbodynum = g_BotProfiles[profilenum].body;
|
|
}
|
|
|
|
void mpSetBotDifficulty(s32 botnum, s32 difficulty)
|
|
{
|
|
s32 i;
|
|
|
|
g_BotConfigsArray[botnum].difficulty = difficulty;
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
g_MpSimulantDifficultiesPerNumPlayers[botnum][i] = g_BotConfigsArray[botnum].difficulty;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the first unused bot slot (0-7), or return 7 if all slots are full.
|
|
*
|
|
* This is used for the quick team feature.
|
|
*/
|
|
s32 mpGetSlotForNewBot(void)
|
|
{
|
|
s32 i = 0;
|
|
|
|
while (i < MAX_BOTS - 1 && g_MpSetup.chrslots & (1 << (i + 4))) {
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void mpRemoveSimulant(s32 index)
|
|
{
|
|
g_MpSetup.chrslots &= ~(1 << (index + 4));
|
|
g_BotConfigsArray[index].base.name[0] = '\0';
|
|
func0f1881d4(index);
|
|
mpGenerateBotNames();
|
|
}
|
|
|
|
bool mpHasSimulants(void)
|
|
{
|
|
if ((g_MpSetup.chrslots & 0xfff0) != 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mpHasUnusedBotSlots(void)
|
|
{
|
|
s32 numvacant = challengeIsFeatureUnlocked(MPFEATURE_8BOTS) ? MAX_BOTS : 4;
|
|
s32 i;
|
|
|
|
for (i = 4; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
numvacant--;
|
|
}
|
|
}
|
|
|
|
if (numvacant > 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mpIsSimSlotEnabled(s32 slot)
|
|
{
|
|
s32 numfree = MAX_BOTS;
|
|
s32 i;
|
|
|
|
if ((g_MpSetup.chrslots & (1 << (slot + 4))) == 0) {
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << (i + 4))) {
|
|
numfree--;
|
|
}
|
|
}
|
|
|
|
if (numfree > 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
s32 mpFindBotProfile(s32 type, s32 difficulty)
|
|
{
|
|
s32 i;
|
|
|
|
if (type == BOTTYPE_GENERAL) {
|
|
for (i = 0; i < ARRAYCOUNT(g_BotProfiles); i++) {
|
|
if (g_BotProfiles[i].difficulty == difficulty) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < ARRAYCOUNT(g_BotProfiles); i++) {
|
|
if (g_BotProfiles[i].type == type) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i >= ARRAYCOUNT(g_BotProfiles)) {
|
|
i = -1;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void mpGenerateBotNames(void)
|
|
{
|
|
s32 counts[ARRAYCOUNT(g_BotProfiles)];
|
|
s32 profilenum;
|
|
s32 i;
|
|
char name[16];
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_BotProfiles); i++) {
|
|
counts[i] = 0;
|
|
}
|
|
|
|
// Count the number of bots using each profile (MeatSim, TurtleSim etc)
|
|
for (i = 4; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
profilenum = mpFindBotProfile(g_BotConfigsArray[i - 4].type, g_BotConfigsArray[i - 4].difficulty);
|
|
|
|
if (profilenum >= 0 && profilenum < ARRAYCOUNT(g_BotProfiles)) {
|
|
counts[profilenum]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Profiles with only one bot don't need to have to number appended to the
|
|
// name, so mark those as -1. For profiles with multiple bots, reset them
|
|
// to 0 because they'll be a counter for the final loop.
|
|
for (i = 0; i < ARRAYCOUNT(g_BotProfiles); i++) {
|
|
if (counts[i] <= 1) {
|
|
counts[i] = -1;
|
|
} else {
|
|
counts[i] = 0;
|
|
}
|
|
}
|
|
|
|
for (i = 4; i < MAX_MPCHRS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << i)) {
|
|
profilenum = mpFindBotProfile(g_BotConfigsArray[i - 4].type, g_BotConfigsArray[i - 4].difficulty);
|
|
|
|
if (profilenum >= 0 && profilenum < ARRAYCOUNT(g_BotProfiles)) {
|
|
if (counts[profilenum] >= 0) {
|
|
// Multiple bots using this profile - append the number
|
|
counts[profilenum]++;
|
|
sprintf(name, "%s:%d\n", langGet(g_BotProfiles[profilenum].name), counts[profilenum]);
|
|
strcpy(g_BotConfigsArray[i - 4].base.name, name);
|
|
} else {
|
|
// One bots using this profile - just use the profile name
|
|
sprintf(name, "%s\n", langGet(g_BotProfiles[profilenum].name));
|
|
strcpy(g_BotConfigsArray[i - 4].base.name, name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
const char var7f1b8b80[] = "Adding GBCHead to load to slot %d: guid is %x-%x, player is %d\n";
|
|
const char var7f1b8bc0[] = "PakId for player %d: %d\n";
|
|
const char var7f1b8bdc[] = "Save Player Result: %d New GUID: %x\n";
|
|
const char var7f1b8c04[] = "PakId for player %d: %d\n";
|
|
const char var7f1b8c20[] = "Load Player - Result: %d\n";
|
|
#endif
|
|
|
|
s32 mpPlayerGetIndex(struct chrdata *chr)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_MpNumChrs; i++) {
|
|
if (g_MpAllChrPtrs[i] == chr) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
struct chrdata *mpGetChrFromPlayerIndex(s32 index)
|
|
{
|
|
if (index >= 0 && index < g_MpNumChrs) {
|
|
return g_MpAllChrPtrs[index];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
s32 func0f18d074(s32 index)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
if (&g_PlayerConfigsArray[i].base == g_MpAllChrConfigPtrs[index]) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
if (&g_BotConfigsArray[i].base == g_MpAllChrConfigPtrs[index]) {
|
|
return i + 4;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
s32 func0f18d0e8(s32 arg0)
|
|
{
|
|
s32 i;
|
|
|
|
if (arg0 < 4) {
|
|
for (i = 0; i < g_MpNumChrs; i++) {
|
|
if (g_MpAllChrConfigPtrs[i] == &g_PlayerConfigsArray[arg0].base) {
|
|
return i;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < g_MpNumChrs; i++) {
|
|
if (g_MpAllChrConfigPtrs[i] == &g_BotConfigsArray[arg0 - 4].base) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void mpplayerfileLoadGunFuncs(struct savebuffer *buffer, s32 playernum)
|
|
{
|
|
s32 bitsremaining = 35;
|
|
s32 i = 0;
|
|
|
|
while (bitsremaining > 0) {
|
|
s32 numbits = bitsremaining;
|
|
|
|
if (numbits > 8) {
|
|
numbits = 8;
|
|
}
|
|
|
|
g_PlayerConfigsArray[playernum].gunfuncs[i] = savebufferReadBits(buffer, numbits);
|
|
|
|
bitsremaining -= 8;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void mpplayerfileSaveGunFuncs(struct savebuffer *buffer, s32 playernum)
|
|
{
|
|
s32 bitsremaining = 35;
|
|
s32 i = 0;
|
|
|
|
while (bitsremaining > 0) {
|
|
s32 numbits = bitsremaining;
|
|
|
|
if (numbits > 8) {
|
|
numbits = 8;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].gunfuncs[i], numbits);
|
|
|
|
bitsremaining -= 8;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void mpplayerfileLoadWad(s32 playernum, struct savebuffer *buffer, s32 arg2)
|
|
{
|
|
struct fileguid guid;
|
|
u32 stack;
|
|
s32 i;
|
|
s32 j;
|
|
|
|
savebufferReadString(buffer, g_PlayerConfigsArray[playernum].base.name, 1);
|
|
g_PlayerConfigsArray[playernum].time = savebufferReadBits(buffer, 28);
|
|
|
|
if (arg2 != 0) {
|
|
g_PlayerConfigsArray[playernum].base.mpheadnum = savebufferReadBits(buffer, 7);
|
|
g_PlayerConfigsArray[playernum].base.mpbodynum = savebufferReadBits(buffer, 7);
|
|
|
|
savebufferReadGuid(buffer, &guid);
|
|
|
|
if (g_PlayerConfigsArray[playernum].base.mpheadnum >= mpGetNumHeads2()) {
|
|
if (guid.fileid != 0 && guid.deviceserial != 0) {
|
|
if (g_MenuData.unk668 < 11) {
|
|
g_MenuData.unk668++;
|
|
g_MenuData.unk5d8[g_MenuData.unk668].fileguid.fileid = guid.fileid;
|
|
g_MenuData.unk5d8[g_MenuData.unk668].fileguid.deviceserial = guid.deviceserial;
|
|
g_MenuData.unk5d8[g_MenuData.unk668].unk08 = g_MpPlayerNum;
|
|
g_MenuData.unk5d8[g_MenuData.unk668].unk09 = g_MpPlayerNum;
|
|
}
|
|
} else {
|
|
g_PlayerConfigsArray[playernum].base.mpheadnum = MPHEAD_DARK_COMBAT;
|
|
}
|
|
}
|
|
} else {
|
|
savebufferReadBits(buffer, 7);
|
|
savebufferReadBits(buffer, 7);
|
|
savebufferReadGuid(buffer, &guid);
|
|
}
|
|
|
|
g_PlayerConfigsArray[playernum].base.displayoptions = savebufferReadBits(buffer, 8);
|
|
g_PlayerConfigsArray[playernum].kills = savebufferReadBits(buffer, 20);
|
|
g_PlayerConfigsArray[playernum].deaths = savebufferReadBits(buffer, 20);
|
|
g_PlayerConfigsArray[playernum].gamesplayed = savebufferReadBits(buffer, 19);
|
|
g_PlayerConfigsArray[playernum].gameswon = savebufferReadBits(buffer, 19);
|
|
g_PlayerConfigsArray[playernum].gameslost = savebufferReadBits(buffer, 19);
|
|
g_PlayerConfigsArray[playernum].distance = savebufferReadBits(buffer, 25);
|
|
g_PlayerConfigsArray[playernum].accuracy = savebufferReadBits(buffer, 10);
|
|
g_PlayerConfigsArray[playernum].damagedealt = savebufferReadBits(buffer, 26);
|
|
g_PlayerConfigsArray[playernum].painreceived = savebufferReadBits(buffer, 26);
|
|
g_PlayerConfigsArray[playernum].headshots = savebufferReadBits(buffer, 20);
|
|
g_PlayerConfigsArray[playernum].ammoused = savebufferReadBits(buffer, 30);
|
|
g_PlayerConfigsArray[playernum].accuracymedals = savebufferReadBits(buffer, 18);
|
|
g_PlayerConfigsArray[playernum].headshotmedals = savebufferReadBits(buffer, 18);
|
|
g_PlayerConfigsArray[playernum].killmastermedals = savebufferReadBits(buffer, 18);
|
|
g_PlayerConfigsArray[playernum].survivormedals = savebufferReadBits(buffer, 16);
|
|
g_PlayerConfigsArray[playernum].controlmode = savebufferReadBits(buffer, 2);
|
|
g_PlayerConfigsArray[playernum].options = savebufferReadBits(buffer, 12);
|
|
|
|
#ifndef PLATFORM_N64
|
|
// override with PC controls if enabled in the config
|
|
if (g_PlayerExtCfg[playernum % MAX_PLAYERS].extcontrols) {
|
|
g_PlayerConfigsArray[playernum].controlmode = CONTROLMODE_PC;
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpChallenges); i++) {
|
|
for (j = 1; j < MAX_PLAYERS + 1; j++) {
|
|
challengeSetCompletedByPlayerWithNumPlayers(playernum, i, j, savebufferReadBits(buffer, 1));
|
|
}
|
|
}
|
|
|
|
challengeDetermineUnlockedFeatures();
|
|
mpCalculatePlayerTitle(&g_PlayerConfigsArray[playernum]);
|
|
mpplayerfileLoadGunFuncs(buffer, playernum);
|
|
}
|
|
|
|
void mpplayerfileSaveWad(s32 playernum, struct savebuffer *buffer)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
u32 stack;
|
|
|
|
func0f0d55a4(buffer, g_PlayerConfigsArray[playernum].base.name);
|
|
|
|
if (g_PlayerConfigsArray[playernum].time > 0x0fffffff) { // over 3106 days
|
|
g_PlayerConfigsArray[playernum].time = 0x0fffffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].time, 28);
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].base.mpheadnum, 7);
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].base.mpbodynum, 7);
|
|
|
|
if (g_PlayerConfigsArray[playernum].base.mpheadnum >= mpGetNumHeads2()) {
|
|
struct fileguid guid;
|
|
phGetGuid(g_PlayerConfigsArray[playernum].base.mpheadnum - mpGetNumHeads2(), &guid);
|
|
savebufferWriteGuid(buffer, &guid);
|
|
} else {
|
|
struct fileguid guid;
|
|
guid.deviceserial = 0;
|
|
guid.fileid = 0;
|
|
savebufferWriteGuid(buffer, &guid);
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].base.displayoptions, 8);
|
|
|
|
if (g_PlayerConfigsArray[playernum].kills > 0xfffff) { // 1,048,575
|
|
g_PlayerConfigsArray[playernum].kills = 0xfffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].kills, 20);
|
|
|
|
if (g_PlayerConfigsArray[playernum].deaths > 0xfffff) { // 1,048,575
|
|
g_PlayerConfigsArray[playernum].deaths = 0xfffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].deaths, 20);
|
|
|
|
if (g_PlayerConfigsArray[playernum].gamesplayed > 0x7ffff) { // 524,287
|
|
g_PlayerConfigsArray[playernum].gamesplayed = 0x7ffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].gamesplayed, 19);
|
|
|
|
if (g_PlayerConfigsArray[playernum].gameswon > 0x7ffff) { // 524,287
|
|
g_PlayerConfigsArray[playernum].gameswon = 0x7ffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].gameswon, 19);
|
|
|
|
if (g_PlayerConfigsArray[playernum].gameslost > 0x7ffff) { // 524,287
|
|
g_PlayerConfigsArray[playernum].gameslost = 0x7ffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].gameslost, 19);
|
|
|
|
if (g_PlayerConfigsArray[playernum].distance > 0x1ffffff) { // 33,554,431
|
|
g_PlayerConfigsArray[playernum].distance = 0x1ffffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].distance, 25);
|
|
|
|
if (g_PlayerConfigsArray[playernum].accuracy > 0x3ff) { // 1023
|
|
g_PlayerConfigsArray[playernum].accuracy = 0x3ff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].accuracy, 10);
|
|
|
|
if (g_PlayerConfigsArray[playernum].damagedealt > 0x3ffffff) { // 67,108,863
|
|
g_PlayerConfigsArray[playernum].damagedealt = 0x3ffffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].damagedealt, 26);
|
|
|
|
if (g_PlayerConfigsArray[playernum].painreceived > 0x3ffffff) { // 67,108,863
|
|
g_PlayerConfigsArray[playernum].painreceived = 0x3ffffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].painreceived, 26);
|
|
|
|
if (g_PlayerConfigsArray[playernum].headshots > 0xfffff) { // 1,048,575
|
|
g_PlayerConfigsArray[playernum].headshots = 0xfffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].headshots, 20);
|
|
|
|
if (g_PlayerConfigsArray[playernum].ammoused > 0x3fffffff) { // 1,073,741,823
|
|
g_PlayerConfigsArray[playernum].ammoused = 0x3fffffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].ammoused, 30);
|
|
|
|
if (g_PlayerConfigsArray[playernum].accuracymedals > 0x3ffff) { // 262,143
|
|
g_PlayerConfigsArray[playernum].accuracymedals = 0x3ffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].accuracymedals, 18);
|
|
|
|
if (g_PlayerConfigsArray[playernum].headshotmedals > 0x3ffff) { // 262,143
|
|
g_PlayerConfigsArray[playernum].headshotmedals = 0x3ffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].headshotmedals, 18);
|
|
|
|
if (g_PlayerConfigsArray[playernum].killmastermedals > 0x3ffff) { // 262,143
|
|
g_PlayerConfigsArray[playernum].killmastermedals = 0x3ffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].killmastermedals, 18);
|
|
|
|
if (g_PlayerConfigsArray[playernum].survivormedals > 0xffff) { // 65,535
|
|
g_PlayerConfigsArray[playernum].survivormedals = 0xffff;
|
|
}
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].survivormedals, 16);
|
|
|
|
#ifdef PLATFORM_N64
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].controlmode, 2);
|
|
#else
|
|
// PC control mode is enabled in the .ini to avoid changing the save structure
|
|
const u32 controlmode = g_PlayerConfigsArray[playernum].controlmode;
|
|
savebufferOr(buffer, ((controlmode == CONTROLMODE_PC) ? CONTROLMODE_11 : controlmode), 2);
|
|
#endif
|
|
|
|
savebufferOr(buffer, g_PlayerConfigsArray[playernum].options, 12);
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpChallenges); i++) {
|
|
for (j = 1; j < MAX_PLAYERS + 1; j++) {
|
|
savebufferOr(buffer, challengeIsCompletedByPlayerWithNumPlayers(playernum, i, j), 1);
|
|
}
|
|
}
|
|
|
|
mpplayerfileSaveGunFuncs(buffer, playernum);
|
|
}
|
|
|
|
void mpplayerfileGetOverview(char *arg0, char *name, u32 *playtime)
|
|
{
|
|
struct savebuffer buffer;
|
|
|
|
func0f0d5484(&buffer, arg0, 15);
|
|
|
|
savebufferReadString(&buffer, name, 0);
|
|
*playtime = savebufferReadBits(&buffer, 28);
|
|
}
|
|
|
|
s32 mpplayerfileSave(s32 playernum, s32 device, s32 fileid, u16 deviceserial)
|
|
{
|
|
s32 ret;
|
|
s32 newfileid;
|
|
struct savebuffer buffer;
|
|
|
|
if (device >= 0) {
|
|
savebufferClear(&buffer);
|
|
mpplayerfileSaveWad(playernum, &buffer);
|
|
func0f0d54c4(&buffer);
|
|
|
|
var80075bd0[2] = true;
|
|
|
|
ret = pakSaveAtGuid(device, fileid, PAKFILETYPE_MPPLAYER, buffer.bytes, &newfileid, 0);
|
|
|
|
if (ret == 0) {
|
|
g_PlayerConfigsArray[playernum].fileguid.fileid = newfileid;
|
|
g_PlayerConfigsArray[playernum].fileguid.deviceserial = deviceserial;
|
|
return 0;
|
|
}
|
|
|
|
g_FilemgrLastPakError = ret;
|
|
return -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
s32 mpplayerfileLoad(s32 playernum, s32 device, s32 fileid, u16 deviceserial)
|
|
{
|
|
s32 ret;
|
|
struct savebuffer buffer;
|
|
|
|
if (device >= 0) {
|
|
savebufferClear(&buffer);
|
|
|
|
ret = pakReadBodyAtGuid(device, fileid, buffer.bytes, 0);
|
|
|
|
if (ret == 0) {
|
|
g_PlayerConfigsArray[playernum].fileguid.fileid = fileid;
|
|
g_PlayerConfigsArray[playernum].fileguid.deviceserial = deviceserial;
|
|
|
|
mpplayerfileLoadWad(playernum, &buffer, 1);
|
|
func0f0d54c4(&buffer);
|
|
|
|
g_PlayerConfigsArray[playernum].handicap = 0x80;
|
|
return 0;
|
|
}
|
|
|
|
g_FilemgrLastPakError = ret;
|
|
return -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
struct mppreset g_MpPresets[] = {
|
|
{ L_MPWEAPONS_025, MPCONFIG_NOSHIELD }, // "No Shield"
|
|
{ L_MPWEAPONS_026, MPCONFIG_AUTOMATICS }, // "Automatics"
|
|
{ L_MPWEAPONS_027, MPCONFIG_ROCKETS }, // "Rocket Launcher"
|
|
{ L_MPWEAPONS_028, MPCONFIG_SIMULANTS }, // "Simulants"
|
|
{ L_MPWEAPONS_029, MPCONFIG_SKEDARKING }, // "King of the Hill"
|
|
{ L_MPWEAPONS_030, MPCONFIG_FARSIGHT }, // "Complex FarSight"
|
|
{ L_MPWEAPONS_031, MPCONFIG_BRIEFCASE }, // "Hold the Briefcase"
|
|
{ L_MPWEAPONS_032, MPCONFIG_PISTOLS }, // "Pistol One-Hit Kills"
|
|
{ L_MPWEAPONS_033, MPCONFIG_FLAGS }, // "Capture the Case"
|
|
{ L_MPWEAPONS_034, MPCONFIG_CLOAKING }, // "Cloaking"
|
|
{ L_MPWEAPONS_035, MPCONFIG_TEMPLE }, // "Temple Explosives"
|
|
{ L_MPWEAPONS_036, MPCONFIG_SLAYER }, // "Slayer"
|
|
{ L_MPWEAPONS_037, MPCONFIG_DRUGGUN }, // "Tranquilizr"
|
|
{ L_MPWEAPONS_038, MPCONFIG_SLOWMO }, // "Slow Motion"
|
|
};
|
|
|
|
s32 mpGetNumPresets(void)
|
|
{
|
|
return ARRAYCOUNT(g_MpPresets);
|
|
}
|
|
|
|
bool mpIsPresetUnlocked(s32 presetnum)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpPresets[presetnum].requirefeatures); i++) {
|
|
if (!challengeIsFeatureUnlocked(g_MpPresets[presetnum].requirefeatures[i]) &&
|
|
g_MpPresets[presetnum].requirefeatures[i] != MPFEATURE_WEAPON_SHIELD) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
s32 mpGetNumUnlockedPresets(void)
|
|
{
|
|
s32 numunlocked = 0;
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpPresets); i++) {
|
|
if (mpIsPresetUnlocked(i)) {
|
|
numunlocked++;
|
|
}
|
|
}
|
|
|
|
return numunlocked;
|
|
}
|
|
|
|
char *mpGetPresetNameBySlot(s32 slot)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpPresets); i++) {
|
|
if (mpIsPresetUnlocked(i)) {
|
|
if (slot == 0) {
|
|
return langGet(g_MpPresets[i].name);
|
|
}
|
|
|
|
slot--;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
const char var7f1b8c40[] = "bot %d headId %d bodyId %d\n";
|
|
const char var7f1b8c5c[] = "team change %s %d\n";
|
|
|
|
#if VERSION >= VERSION_PAL_FINAL
|
|
const char var7f1b8c70[] = "options/mplayer.c";
|
|
#else
|
|
const char var7f1b8c70[] = "mplayer.c";
|
|
#endif
|
|
|
|
const char var7f1b8c7c[] = "team change %s %d\n";
|
|
|
|
#if VERSION >= VERSION_PAL_FINAL
|
|
const char var7f1b8c90[] = "options/mplayer.c";
|
|
#else
|
|
const char var7f1b8c90[] = "mplayer.c";
|
|
#endif
|
|
|
|
const char var7f1b8c9c[] = "SaveMultiGameFile : PakId=0x%x, FileId=0x%x\n";
|
|
const char var7f1b8ccc[] = "SaveGame Result: %d New GUID: %x\n";
|
|
const char var7f1b8cf0[] = "LoadMultiGameFile : PakId=0x%x, FileId=0x%x\n";
|
|
const char var7f1b8d20[] = "LoadGame Result: %d\n";
|
|
const char var7f1b8d38[] = "GBCHead: Call to create head for slot %d (gbcheadobjs[slotno]=%x)\n";
|
|
#endif // >= VERSION_NTSC_1_0
|
|
|
|
void mpApplyConfig(struct mpconfigfull *config)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
u16 chrslots;
|
|
|
|
g_MpSetup.scenario = config->config.setup.scenario;
|
|
|
|
#if VERSION >= VERSION_PAL_FINAL
|
|
chrslots = g_MpSetup.chrslots;
|
|
#endif
|
|
|
|
scenarioInit();
|
|
|
|
g_MpSetup = config->config.setup;
|
|
|
|
#if VERSION >= VERSION_PAL_FINAL
|
|
g_MpSetup.chrslots = chrslots;
|
|
#endif
|
|
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
g_BotConfigsArray[i].type = config->config.simulants[i].type;
|
|
|
|
for (j = 0; j < MAX_PLAYERS; j++) {
|
|
g_MpSimulantDifficultiesPerNumPlayers[i][j] = config->config.simulants[i].difficulties[j];
|
|
}
|
|
|
|
g_BotConfigsArray[i].difficulty = g_MpSimulantDifficultiesPerNumPlayers[i][0];
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
if (IS4MB()) {
|
|
// "ShockSim:%d"
|
|
sprintf(g_BotConfigsArray[i].base.name, langGet(L_MPWEAPONS_241), i + 1);
|
|
} else {
|
|
strcpy(g_BotConfigsArray[i].base.name, config->strings.aibotnames[i]);
|
|
}
|
|
#else
|
|
strcpy(g_BotConfigsArray[i].base.name, config->strings.aibotnames[i]);
|
|
#endif
|
|
|
|
g_BotConfigsArray[i].base.mpheadnum = config->config.simulants[i].mpheadnum;
|
|
g_BotConfigsArray[i].base.mpbodynum = config->config.simulants[i].mpbodynum;
|
|
g_BotConfigsArray[i].base.team = config->config.simulants[i].team;
|
|
}
|
|
|
|
if (!challengeIsFeatureUnlocked(MPFEATURE_WEAPON_SHIELD)) {
|
|
for (i = 0; i < ARRAYCOUNT(g_MpSetup.weapons); i++) {
|
|
if (g_MpSetup.weapons[i] == MPWEAPON_SHIELD) {
|
|
g_MpSetup.weapons[i] = MPWEAPON_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
func0f18913c();
|
|
challengeRemoveForceUnlocks();
|
|
}
|
|
|
|
void mp0f18dec4(s32 slot)
|
|
{
|
|
struct mpconfigfull *config;
|
|
u8 buffer[0x1ca];
|
|
s32 confignum = 0;
|
|
u32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpPresets); i++) {
|
|
if (mpIsPresetUnlocked(i)) {
|
|
if (slot == 0) {
|
|
confignum = g_MpPresets[i].confignum;
|
|
break;
|
|
}
|
|
|
|
slot--;
|
|
}
|
|
}
|
|
|
|
config = challengeLoadConfig(confignum, buffer, sizeof(buffer));
|
|
|
|
mpApplyConfig(config);
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
g_MpSetup.chrslots &= 0x0f;
|
|
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
if (g_BotConfigsArray[i].difficulty != BOTDIFF_DISABLED) {
|
|
g_MpSetup.chrslots |= 1 << (i + 4);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void mpsetupfileLoadWad(struct savebuffer *buffer)
|
|
{
|
|
s32 i;
|
|
s32 j;
|
|
|
|
savebufferReadString(buffer, g_MpSetup.name, false);
|
|
savebufferReadBits(buffer, 4);
|
|
|
|
g_MpSetup.stagenum = savebufferReadBits(buffer, 7);
|
|
g_MpSetup.scenario = savebufferReadBits(buffer, 3);
|
|
|
|
scenarioInit();
|
|
scenarioReadSave(buffer);
|
|
|
|
g_MpSetup.options = savebufferReadBits(buffer, 21);
|
|
g_MpSetup.chrslots &= 0x000f;
|
|
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
g_BotConfigsArray[i].base.name[0] = '\0';
|
|
g_BotConfigsArray[i].type = savebufferReadBits(buffer, 5);
|
|
g_BotConfigsArray[i].difficulty = savebufferReadBits(buffer, 3);
|
|
|
|
for (j = 0; j < MAX_PLAYERS; j++) {
|
|
g_MpSimulantDifficultiesPerNumPlayers[i][j] = g_BotConfigsArray[i].difficulty;
|
|
}
|
|
|
|
if (g_BotConfigsArray[i].difficulty != BOTDIFF_DISABLED) {
|
|
g_MpSetup.chrslots |= 1 << (i + 4);
|
|
}
|
|
|
|
g_BotConfigsArray[i].base.mpheadnum = savebufferReadBits(buffer, 7);
|
|
g_BotConfigsArray[i].base.mpbodynum = savebufferReadBits(buffer, 7);
|
|
g_BotConfigsArray[i].base.team = savebufferReadBits(buffer, 3);
|
|
}
|
|
|
|
mpGenerateBotNames();
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpSetup.weapons); i++) {
|
|
g_MpSetup.weapons[i] = savebufferReadBits(buffer, 7);
|
|
}
|
|
|
|
func0f18913c();
|
|
|
|
g_MpSetup.timelimit = savebufferReadBits(buffer, 6);
|
|
g_MpSetup.scorelimit = savebufferReadBits(buffer, 7);
|
|
g_MpSetup.teamscorelimit = savebufferReadBits(buffer, 9);
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
g_PlayerConfigsArray[i].base.team = savebufferReadBits(buffer, 3);
|
|
}
|
|
|
|
challengeForceUnlockBotFeatures();
|
|
}
|
|
|
|
void mpsetupfileSaveWad(struct savebuffer *buffer)
|
|
{
|
|
s32 numsims = 0;
|
|
s32 mpbodynum;
|
|
s32 i;
|
|
|
|
func0f0d55a4(buffer, g_MpSetup.name);
|
|
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
if (g_MpSetup.chrslots & (1 << (i + 4))) {
|
|
numsims++;
|
|
}
|
|
}
|
|
|
|
savebufferOr(buffer, numsims, 4);
|
|
savebufferOr(buffer, g_MpSetup.stagenum, 7);
|
|
savebufferOr(buffer, g_MpSetup.scenario, 3);
|
|
|
|
scenarioWriteSave(buffer);
|
|
|
|
savebufferOr(buffer, g_MpSetup.options, 21);
|
|
|
|
for (i = 0; i < MAX_BOTS; i++) {
|
|
savebufferOr(buffer, g_BotConfigsArray[i].type, 5);
|
|
|
|
if (g_MpSetup.chrslots & (1 << (i + 4))) {
|
|
savebufferOr(buffer, g_BotConfigsArray[i].difficulty, 3);
|
|
} else {
|
|
savebufferOr(buffer, BOTDIFF_DISABLED, 3);
|
|
}
|
|
|
|
savebufferOr(buffer, g_BotConfigsArray[i].base.mpheadnum, 7);
|
|
|
|
if (g_BotConfigsArray[i].base.mpbodynum == 0xff) {
|
|
s32 profilenum = mpFindBotProfile(g_BotConfigsArray[i].type, g_BotConfigsArray[i].difficulty);
|
|
|
|
if (profilenum < 0 || profilenum >= ARRAYCOUNT(g_BotProfiles)) {
|
|
profilenum = 0;
|
|
}
|
|
|
|
mpbodynum = g_BotProfiles[profilenum].body;
|
|
} else {
|
|
mpbodynum = g_BotConfigsArray[i].base.mpbodynum;
|
|
}
|
|
|
|
savebufferOr(buffer, mpbodynum, 7);
|
|
savebufferOr(buffer, g_BotConfigsArray[i].base.team, 3);
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_MpSetup.weapons); i++) {
|
|
savebufferOr(buffer, g_MpSetup.weapons[i], 7);
|
|
}
|
|
|
|
savebufferOr(buffer, g_MpSetup.timelimit, 6);
|
|
savebufferOr(buffer, g_MpSetup.scorelimit, 7);
|
|
savebufferOr(buffer, g_MpSetup.teamscorelimit, 9);
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
savebufferOr(buffer, g_PlayerConfigsArray[i].base.team, 3);
|
|
}
|
|
}
|
|
|
|
void mpsetupfileGetOverview(char *arg0, char *filename, u16 *numsims, u16 *stagenum, u16 *scenarionum)
|
|
{
|
|
struct savebuffer buffer;
|
|
|
|
func0f0d5484(&buffer, arg0, 15);
|
|
|
|
savebufferReadString(&buffer, filename, 0);
|
|
|
|
*numsims = savebufferReadBits(&buffer, 4);
|
|
*stagenum = savebufferReadBits(&buffer, 7);
|
|
*scenarionum = savebufferReadBits(&buffer, 3);
|
|
}
|
|
|
|
s32 mpsetupfileSave(s32 device, s32 fileid, u16 deviceserial)
|
|
{
|
|
s32 ret;
|
|
s32 newfileid;
|
|
struct savebuffer buffer;
|
|
|
|
if (device >= 0) {
|
|
savebufferClear(&buffer);
|
|
mpsetupfileSaveWad(&buffer);
|
|
func0f0d54c4(&buffer);
|
|
|
|
ret = pakSaveAtGuid(device, fileid, PAKFILETYPE_MPSETUP, buffer.bytes, &newfileid, 0);
|
|
var80075bd0[1] = true;
|
|
|
|
if (ret == 0) {
|
|
g_MpSetup.fileguid.fileid = newfileid;
|
|
g_MpSetup.fileguid.deviceserial = deviceserial;
|
|
return 0;
|
|
}
|
|
|
|
g_FilemgrLastPakError = ret;
|
|
return -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
s32 mpsetupfileLoad(s32 device, s32 fileid, u16 deviceserial)
|
|
{
|
|
s32 ret;
|
|
struct savebuffer buffer;
|
|
|
|
if (device >= 0) {
|
|
savebufferClear(&buffer);
|
|
ret = pakReadBodyAtGuid(device, fileid, buffer.bytes, 0);
|
|
|
|
if (ret == 0) {
|
|
g_MpSetup.fileguid.fileid = fileid;
|
|
g_MpSetup.fileguid.deviceserial = deviceserial;
|
|
|
|
mpsetupfileLoadWad(&buffer);
|
|
func0f0d54c4(&buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
g_FilemgrLastPakError = ret;
|
|
|
|
return -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void func0f18e558(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(var800acc28); i++) {
|
|
var800acc28[i] = NULL;
|
|
}
|
|
}
|
|
|
|
struct modeldef *func0f18e57c(s32 index, s32 *headnum)
|
|
{
|
|
return var800acc28[index];
|
|
}
|