mirror of
https://gitlab.com/ryandwyer/perfect-dark
synced 2026-06-10 04:53:21 -04:00
1165 lines
26 KiB
C
1165 lines
26 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "game/cheats.h"
|
|
#include "game/bondgun.h"
|
|
#include "game/gset.h"
|
|
#include "game/inv.h"
|
|
#include "game/training.h"
|
|
#include "game/lang.h"
|
|
#include "bss.h"
|
|
#include "lib/main.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
void inv_clear(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_Vars.currentplayer->equipmaxitems; i++) {
|
|
g_Vars.currentplayer->equipment[i].type = -1;
|
|
}
|
|
|
|
g_Vars.currentplayer->weapons = NULL;
|
|
g_Vars.currentplayer->equipcuritem = 0;
|
|
}
|
|
|
|
/**
|
|
* Sorts subject into its correct position in the inventory list.
|
|
*
|
|
* Subject is expected to initially be at the head of the list. It works by
|
|
* swapping the subject with the item to its right as many times as needed.
|
|
*/
|
|
void inv_sort_item(struct invitem *subject)
|
|
{
|
|
struct invitem *candidate;
|
|
s32 subjweapon1 = -1;
|
|
s32 subjweapon2 = -1;
|
|
s32 candweapon1;
|
|
s32 candweapon2;
|
|
|
|
// Prepare subject's properties for comparisons
|
|
if (subject->type == INVITEMTYPE_WEAP) {
|
|
subjweapon1 = subject->type_weap.weapon1;
|
|
} else if (subject->type == INVITEMTYPE_DUAL) {
|
|
subjweapon1 = subject->type_dual.weapon1;
|
|
subjweapon2 = subject->type_dual.weapon2;
|
|
} else if (subject->type == INVITEMTYPE_PROP) {
|
|
subjweapon1 = 2000;
|
|
}
|
|
|
|
candidate = subject->next;
|
|
|
|
while (g_Vars.currentplayer->weapons != subject->next) {
|
|
// Prepare candidate's properties for comparisons
|
|
candweapon1 = -1;
|
|
candweapon2 = -1;
|
|
|
|
if (subject->next->type == INVITEMTYPE_WEAP) {
|
|
candweapon1 = subject->next->type_weap.weapon1;
|
|
} else if (subject->next->type == INVITEMTYPE_DUAL) {
|
|
candweapon1 = subject->next->type_dual.weapon1;
|
|
candweapon2 = subject->next->type_dual.weapon2;
|
|
} else if (subject->next->type == INVITEMTYPE_PROP) {
|
|
candweapon1 = 1000;
|
|
}
|
|
|
|
// If the candidate should sort ahead of subject
|
|
// then subject is in the desired position.
|
|
if (candweapon1 >= subjweapon1 &&
|
|
(subjweapon1 != candweapon1 || subjweapon2 <= candweapon2)) {
|
|
return;
|
|
}
|
|
|
|
// If there's only two items in the list then there's no point swapping
|
|
// them. Just set the list head to the candidate.
|
|
if (candidate->next == subject) {
|
|
g_Vars.currentplayer->weapons = candidate;
|
|
} else {
|
|
// Swap subject with candidate
|
|
subject->next = candidate->next;
|
|
candidate->prev = subject->prev;
|
|
subject->prev = candidate;
|
|
candidate->next = subject;
|
|
subject->next->prev = subject;
|
|
candidate->prev->next = candidate;
|
|
|
|
// Set new list head if subject was the head
|
|
if (subject == g_Vars.currentplayer->weapons) {
|
|
g_Vars.currentplayer->weapons = candidate;
|
|
}
|
|
}
|
|
|
|
candidate = subject->next;
|
|
}
|
|
}
|
|
|
|
void inv_insert_item(struct invitem *item)
|
|
{
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
|
|
if (prop && prop->obj) {
|
|
struct textoverride *override = inv_get_text_override_for_obj(prop->obj);
|
|
bool setflag = true;
|
|
|
|
if (override) {
|
|
if (override->weapon >= WEAPON_UNARMED && override->weapon <= WEAPON_NECKLACE) {
|
|
setflag = false;
|
|
}
|
|
if (override->weapon == WEAPON_MPSHIELD) {
|
|
setflag = false;
|
|
}
|
|
if (override->weapon == WEAPON_SUICIDEPILL) {
|
|
setflag = false;
|
|
}
|
|
if (override->weapon == WEAPON_BRIEFCASE2) {
|
|
setflag = false;
|
|
}
|
|
}
|
|
|
|
if (setflag && prop->type == PROPTYPE_OBJ) {
|
|
struct defaultobj *obj = prop->obj;
|
|
obj->flags2 |= OBJFLAG2_INVHIDDEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Place item at head of weapons list
|
|
if (g_Vars.currentplayer->weapons) {
|
|
item->next = g_Vars.currentplayer->weapons;
|
|
item->prev = g_Vars.currentplayer->weapons->prev;
|
|
item->next->prev = item;
|
|
item->prev->next = item;
|
|
} else {
|
|
item->next = item;
|
|
item->prev = item;
|
|
}
|
|
|
|
g_Vars.currentplayer->weapons = item;
|
|
|
|
inv_sort_item(item);
|
|
inv_calculate_current_index();
|
|
}
|
|
|
|
void inv_remove_item(struct invitem *item)
|
|
{
|
|
struct invitem *next = item->next;
|
|
struct invitem *prev = item->prev;
|
|
|
|
if (g_Vars.currentplayer->weapons == item) {
|
|
if (item == item->next) {
|
|
g_Vars.currentplayer->weapons = NULL;
|
|
} else {
|
|
g_Vars.currentplayer->weapons = item->next;
|
|
}
|
|
}
|
|
|
|
next->prev = prev;
|
|
prev->next = next;
|
|
item->type = -1;
|
|
|
|
inv_calculate_current_index();
|
|
}
|
|
|
|
struct invitem *inv_find_unused_slot(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < g_Vars.currentplayer->equipmaxitems; i++) {
|
|
if (g_Vars.currentplayer->equipment[i].type == -1) {
|
|
return &g_Vars.currentplayer->equipment[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void inv_set_all_guns(bool enable)
|
|
{
|
|
s32 weaponnum;
|
|
|
|
g_Vars.currentplayer->equipallguns = enable;
|
|
inv_calculate_current_index();
|
|
weaponnum = inv_get_weapon_num_by_index(g_Vars.currentplayer->equipcuritem);
|
|
bgun_equip_weapon(weaponnum);
|
|
}
|
|
|
|
bool inv_has_all_guns(void)
|
|
{
|
|
return g_Vars.currentplayer->equipallguns;
|
|
}
|
|
|
|
struct invitem *inv_find_single_weapon(s32 weaponnum)
|
|
{
|
|
struct invitem *first = g_Vars.currentplayer->weapons;
|
|
struct invitem *item = first;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_WEAP && item->type_weap.weapon1 == weaponnum) {
|
|
return item;
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == first) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool inv_has_single_weapon_exc_all_guns(s32 weaponnum)
|
|
{
|
|
return inv_find_single_weapon(weaponnum) != NULL;
|
|
}
|
|
|
|
struct invitem *inv_find_double_weapon(s32 weapon1, s32 weapon2)
|
|
{
|
|
struct invitem *first = g_Vars.currentplayer->weapons;
|
|
struct invitem *item = first;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_DUAL
|
|
&& item->type_dual.weapon1 == weapon1
|
|
&& item->type_dual.weapon2 == weapon2) {
|
|
return item;
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == first) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool inv_has_double_weapon_exc_all_guns(s32 weapon1, s32 weapon2)
|
|
{
|
|
return inv_find_double_weapon(weapon1, weapon2) != NULL;
|
|
}
|
|
|
|
bool inv_has_single_weapon_or_prop(s32 weaponnum)
|
|
{
|
|
struct invitem *item = g_Vars.currentplayer->weapons;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_WEAP) {
|
|
if (weaponnum == item->type_weap.weapon1) {
|
|
return true;
|
|
}
|
|
} else if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
|
|
if (prop && prop->type == PROPTYPE_WEAPON) {
|
|
struct defaultobj *obj = prop->obj;
|
|
|
|
if (obj && obj->type == OBJTYPE_WEAPON) {
|
|
struct weaponobj *weapon = (struct weaponobj *)prop->obj;
|
|
|
|
if (weapon->weaponnum == weaponnum) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == g_Vars.currentplayer->weapons) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
s32 inv_add_one_if_cant_have_slayer(s32 index)
|
|
{
|
|
if (main_get_stage_num());
|
|
|
|
if (main_get_stage_num() != STAGE_ATTACKSHIP
|
|
&& main_get_stage_num() != STAGE_SKEDARRUINS
|
|
&& index >= WEAPON_SLAYER) {
|
|
index++;
|
|
}
|
|
|
|
#if VERSION >= VERSION_JPN_FINAL
|
|
if (index >= 26) {
|
|
index++;
|
|
}
|
|
#endif
|
|
|
|
return index;
|
|
}
|
|
|
|
s32 current_stage_forbids_slayer(void)
|
|
{
|
|
bool value = VERSION >= VERSION_JPN_FINAL ? 1 : 0;
|
|
|
|
if (main_get_stage_num() != STAGE_ATTACKSHIP && main_get_stage_num() != STAGE_SKEDARRUINS) {
|
|
value++;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
bool inv_can_have_all_guns_weapon(s32 weaponnum)
|
|
{
|
|
bool canhave = true;
|
|
|
|
#if VERSION == VERSION_JPN_FINAL
|
|
if (weaponnum == WEAPON_COMBATKNIFE) {
|
|
canhave = false;
|
|
}
|
|
#endif
|
|
|
|
if (weaponnum == WEAPON_SLAYER) {
|
|
canhave = false;
|
|
}
|
|
|
|
// @bug: The stage conditions need an OR. This condition can never pass.
|
|
if ((main_get_stage_num() == STAGE_ATTACKSHIP && main_get_stage_num() == STAGE_SKEDARRUINS)
|
|
&& weaponnum == WEAPON_SLAYER) {
|
|
canhave = true;
|
|
}
|
|
|
|
return canhave;
|
|
}
|
|
|
|
bool inv_has_single_weapon_inc_all_guns(s32 weaponnum)
|
|
{
|
|
if (g_Vars.currentplayer->equipallguns &&
|
|
weaponnum && weaponnum <= WEAPON_PSYCHOSISGUN &&
|
|
inv_can_have_all_guns_weapon(weaponnum)) {
|
|
return true;
|
|
}
|
|
|
|
return inv_has_single_weapon_exc_all_guns(weaponnum);
|
|
}
|
|
|
|
bool inv_has_double_weapon_inc_all_guns(s32 weapon1, s32 weapon2)
|
|
{
|
|
if (weapon2 == WEAPON_NONE) {
|
|
return true;
|
|
}
|
|
|
|
if (g_Vars.currentplayer->equipallguns &&
|
|
weapon1 <= WEAPON_PSYCHOSISGUN &&
|
|
weapon1 == weapon2 &&
|
|
gset_has_weapon_flag(weapon1, WEAPONFLAG_DUALWIELD) &&
|
|
inv_can_have_all_guns_weapon(weapon1)) {
|
|
return true;
|
|
}
|
|
|
|
return inv_has_double_weapon_exc_all_guns(weapon1, weapon2);
|
|
}
|
|
|
|
bool inv_give_single_weapon(s32 weaponnum)
|
|
{
|
|
fr_set_weapon_found(weaponnum);
|
|
|
|
if (inv_has_single_weapon_exc_all_guns(weaponnum) == 0) {
|
|
struct invitem *item;
|
|
|
|
if (g_Vars.currentplayer->equipallguns &&
|
|
weaponnum <= WEAPON_PSYCHOSISGUN &&
|
|
inv_can_have_all_guns_weapon(weaponnum)) {
|
|
return false;
|
|
}
|
|
|
|
item = inv_find_unused_slot();
|
|
|
|
if (item) {
|
|
item->type = INVITEMTYPE_WEAP;
|
|
item->type_weap.weapon1 = weaponnum;
|
|
item->type_weap.pickuppad = -1;
|
|
inv_insert_item(item);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool inv_give_double_weapon(s32 weapon1, s32 weapon2)
|
|
{
|
|
if (inv_has_double_weapon_exc_all_guns(weapon1, weapon2) == 0) {
|
|
if (gset_has_weapon_flag(weapon1, WEAPONFLAG_DUALWIELD)) {
|
|
struct invitem *item = inv_find_unused_slot();
|
|
|
|
if (item) {
|
|
item->type = INVITEMTYPE_DUAL;
|
|
item->type_dual.weapon1 = weapon1;
|
|
item->type_dual.weapon2 = weapon2;
|
|
inv_insert_item(item);
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void inv_remove_item_by_num(s32 weaponnum)
|
|
{
|
|
if (g_Vars.currentplayer->weapons) {
|
|
// Begin iterating from the second item in the list. This is required
|
|
// because the item might be removed from the list when iterating it,
|
|
// and it needs to determine when the end of the list has been reached.
|
|
struct invitem *item = g_Vars.currentplayer->weapons->next;
|
|
|
|
while (true) {
|
|
// Have to preload this because item->next shouldn't be trusted
|
|
// after calling inv_remove_item()
|
|
struct invitem *next = item->next;
|
|
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
struct textoverride *override = inv_get_text_override_for_obj(prop->obj);
|
|
|
|
if (override && override->weapon == weaponnum) {
|
|
inv_remove_item(item);
|
|
}
|
|
} else if (item->type == INVITEMTYPE_WEAP) {
|
|
if (item->type_weap.weapon1 == weaponnum) {
|
|
inv_remove_item(item);
|
|
}
|
|
} else if (item->type == INVITEMTYPE_DUAL) {
|
|
if (item->type_dual.weapon1 == weaponnum || item->type_dual.weapon2 == weaponnum) {
|
|
inv_remove_item(item);
|
|
}
|
|
}
|
|
|
|
if (item == g_Vars.currentplayer->weapons || !g_Vars.currentplayer->weapons) {
|
|
break;
|
|
}
|
|
|
|
item = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool inv_give_prop(struct prop *prop)
|
|
{
|
|
struct invitem *item;
|
|
|
|
// Don't add duplicate night vision to inventory
|
|
// (night vision is already there when using perfect darkness)
|
|
// Note that this check doesn't work on Investigation because it uses the
|
|
// IR specs model. See bug note in Investigation's setup file (setupear.c).
|
|
if (cheat_is_active(CHEAT_PERFECTDARKNESS)
|
|
&& prop->type == PROPTYPE_OBJ
|
|
&& prop->obj
|
|
&& prop->obj->modelnum == MODEL_CHRNIGHTSIGHT) {
|
|
return true;
|
|
}
|
|
|
|
item = inv_find_unused_slot();
|
|
|
|
if (item) {
|
|
item->type = INVITEMTYPE_PROP;
|
|
item->type_prop.prop = prop;
|
|
inv_insert_item(item);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void inv_remove_prop(struct prop *prop)
|
|
{
|
|
if (g_Vars.currentplayer->weapons) {
|
|
struct invitem *item = g_Vars.currentplayer->weapons->next;
|
|
|
|
while (true) {
|
|
struct invitem *next = item->next;
|
|
|
|
if (item->type == INVITEMTYPE_PROP && item->type_prop.prop == prop) {
|
|
inv_remove_item(item);
|
|
}
|
|
|
|
if (item == g_Vars.currentplayer->weapons || !g_Vars.currentplayer->weapons) {
|
|
break;
|
|
}
|
|
|
|
item = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 inv_give_weapons_by_prop(struct prop *prop)
|
|
{
|
|
s32 numgiven = 0;
|
|
|
|
if (prop->type == PROPTYPE_WEAPON) {
|
|
struct defaultobj *obj = prop->obj;
|
|
struct weaponobj *weapon;
|
|
struct weaponobj *otherweapon;
|
|
s32 weaponnum;
|
|
s32 otherweaponnum;
|
|
|
|
if (obj->type == OBJTYPE_WEAPON) {
|
|
weapon = prop->weapon;
|
|
weaponnum = weapon->weaponnum;
|
|
otherweaponnum;
|
|
|
|
if (cheat_is_active(CHEAT_PERFECTDARKNESS) && weaponnum == WEAPON_NIGHTVISION) {
|
|
return 1;
|
|
}
|
|
|
|
if (inv_give_single_weapon(weaponnum)) {
|
|
numgiven = 1;
|
|
}
|
|
|
|
if (g_Vars.normmplayerisrunning
|
|
&& gset_has_weapon_flag(weaponnum, WEAPONFLAG_DUALWIELD)
|
|
&& !inv_has_double_weapon_exc_all_guns(weaponnum, weaponnum)) {
|
|
struct invitem *invitem = inv_find_single_weapon(weaponnum);
|
|
|
|
if (invitem) {
|
|
if (invitem->type_weap.pickuppad < 0) {
|
|
if (obj->pad >= 0) {
|
|
invitem->type_weap.pickuppad = obj->pad;
|
|
}
|
|
} else if (obj->pad >= 0 && invitem->type_weap.pickuppad != obj->pad) {
|
|
if (inv_give_double_weapon(weaponnum, weaponnum)) {
|
|
numgiven = 2;
|
|
} else {
|
|
numgiven = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
otherweapon = weapon->dualweapon;
|
|
|
|
if (otherweapon) {
|
|
if (weapon->base.flags & OBJFLAG_WEAPON_LEFTHANDED) {
|
|
numgiven = inv_has_double_weapon_exc_all_guns(otherweapon->weaponnum, weaponnum) == 0;
|
|
} else {
|
|
numgiven = inv_has_double_weapon_exc_all_guns(weaponnum, otherweapon->weaponnum) == 0;
|
|
}
|
|
|
|
weapon->dualweapon->dualweaponnum = weaponnum;
|
|
weapon->dualweapon->dualweapon = NULL;
|
|
weapon->dualweapon = NULL;
|
|
} else if (weapon->dualweaponnum >= 0) {
|
|
if (weapon->base.flags & OBJFLAG_WEAPON_LEFTHANDED) {
|
|
if (inv_give_double_weapon(weapon->dualweaponnum, weaponnum)) {
|
|
numgiven = 2;
|
|
} else {
|
|
numgiven = 0;
|
|
}
|
|
} else {
|
|
if (inv_give_double_weapon(weaponnum, weapon->dualweaponnum)) {
|
|
numgiven = 2;
|
|
} else {
|
|
numgiven = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return numgiven;
|
|
}
|
|
|
|
void inv_choose_cycle_forward_weapon(s32 *ptr1, s32 *ptr2, bool arg2)
|
|
{
|
|
s32 weapon1 = *ptr1;
|
|
s32 weapon2 = *ptr2;
|
|
|
|
if (g_Vars.currentplayer->equipallguns) {
|
|
s32 candidate = *ptr1;
|
|
|
|
if (gset_has_weapon_flag(*ptr1, WEAPONFLAG_DUALWIELD) && *ptr2 != *ptr1) {
|
|
// Switching to dual from single
|
|
weapon1 = *ptr1;
|
|
weapon2 = *ptr1;
|
|
} else {
|
|
// Find next weapon
|
|
do {
|
|
candidate = (candidate + 1) % NUM_CYCLEABLE_WEAPONS;
|
|
|
|
if (candidate == WEAPON_NONE) {
|
|
candidate = (candidate + 1) % NUM_CYCLEABLE_WEAPONS;
|
|
}
|
|
|
|
if ((!arg2 || bgun0f0a1a10(candidate)) && inv_can_have_all_guns_weapon(candidate)) {
|
|
weapon1 = candidate;
|
|
weapon2 = WEAPON_NONE;
|
|
break;
|
|
}
|
|
} while (candidate != weapon1);
|
|
}
|
|
} else {
|
|
struct invitem *item = g_Vars.currentplayer->weapons;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_WEAP) {
|
|
if (item->type_weap.weapon1 < NUM_CYCLEABLE_WEAPONS && item->type_weap.weapon1 > weapon1) {
|
|
if (!arg2 || bgun0f0a1a10(item->type_weap.weapon1)) {
|
|
weapon1 = item->type_weap.weapon1;
|
|
weapon2 = WEAPON_NONE;
|
|
break;
|
|
}
|
|
}
|
|
} else if (item->type == INVITEMTYPE_DUAL) {
|
|
if (item->type_dual.weapon1 > weapon1
|
|
|| (weapon1 == item->type_dual.weapon1 && item->type_dual.weapon2 > weapon2)) {
|
|
if (!arg2 || bgun0f0a1a10(item->type_dual.weapon1) || bgun0f0a1a10(item->type_dual.weapon2)) {
|
|
weapon1 = item->type_dual.weapon1;
|
|
weapon2 = item->type_dual.weapon2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == g_Vars.currentplayer->weapons) {
|
|
if (arg2) {
|
|
break;
|
|
}
|
|
|
|
weapon1 = -1;
|
|
weapon2 = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
*ptr1 = weapon1;
|
|
*ptr2 = weapon2;
|
|
}
|
|
|
|
void inv_choose_cycle_back_weapon(s32 *ptr1, s32 *ptr2, bool arg2)
|
|
{
|
|
s32 weapon1 = *ptr1;
|
|
s32 weapon2 = *ptr2;
|
|
|
|
if (g_Vars.currentplayer->equipallguns) {
|
|
s32 candidate = *ptr1;
|
|
|
|
if (gset_has_weapon_flag(weapon1, WEAPONFLAG_DUALWIELD) && weapon1 == weapon2) {
|
|
// Switching from dual to single
|
|
weapon1 = candidate;
|
|
weapon2 = WEAPON_NONE;
|
|
} else {
|
|
// Find prev weapon
|
|
do {
|
|
candidate = (candidate + NUM_CYCLEABLE_WEAPONS - 1) % NUM_CYCLEABLE_WEAPONS;
|
|
|
|
if (candidate == WEAPON_NONE) {
|
|
candidate = (candidate + NUM_CYCLEABLE_WEAPONS - 1) % NUM_CYCLEABLE_WEAPONS;
|
|
}
|
|
} while ((arg2 && !bgun0f0a1a10(candidate)) || !inv_can_have_all_guns_weapon(candidate));
|
|
|
|
if (gset_has_weapon_flag(candidate, WEAPONFLAG_DUALWIELD)) {
|
|
weapon1 = candidate;
|
|
weapon2 = candidate;
|
|
} else {
|
|
weapon1 = candidate;
|
|
weapon2 = WEAPON_NONE;
|
|
}
|
|
}
|
|
} else if (g_Vars.currentplayer->weapons != NULL) {
|
|
struct invitem *item = g_Vars.currentplayer->weapons->prev;
|
|
|
|
while (true) {
|
|
if (item->type == INVITEMTYPE_WEAP) {
|
|
if (item->type_weap.weapon1 < NUM_CYCLEABLE_WEAPONS
|
|
&& (item->type_weap.weapon1 < weapon1 || (weapon1 == item->type_weap.weapon1 && weapon2 > 0))) {
|
|
if (!arg2 || bgun0f0a1a10(item->type_weap.weapon1)) {
|
|
weapon1 = item->type_weap.weapon1;
|
|
weapon2 = WEAPON_NONE;
|
|
break;
|
|
}
|
|
}
|
|
} else if (item->type == INVITEMTYPE_DUAL) {
|
|
if (item->type_dual.weapon1 < weapon1
|
|
|| (weapon1 == item->type_dual.weapon1 && item->type_dual.weapon2 < weapon2)) {
|
|
if (!arg2 || bgun0f0a1a10(item->type_dual.weapon1) || bgun0f0a1a10(item->type_dual.weapon2)) {
|
|
weapon1 = item->type_dual.weapon1;
|
|
weapon2 = item->type_dual.weapon2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (item == g_Vars.currentplayer->weapons) {
|
|
if (arg2) {
|
|
break;
|
|
}
|
|
|
|
weapon1 = 1000;
|
|
weapon2 = 1000;
|
|
}
|
|
|
|
item = item->prev;
|
|
}
|
|
}
|
|
|
|
*ptr1 = weapon1;
|
|
*ptr2 = weapon2;
|
|
}
|
|
|
|
bool inv_has_key_flags(u32 wantkeyflags)
|
|
{
|
|
u32 heldkeyflags = 0;
|
|
struct invitem *item = g_Vars.currentplayer->weapons;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
|
|
if (prop && prop->type == PROPTYPE_OBJ) {
|
|
struct defaultobj *obj = prop->obj;
|
|
|
|
if (obj && obj->type == OBJTYPE_KEY) {
|
|
struct keyobj *key = (struct keyobj *)prop->obj;
|
|
|
|
heldkeyflags |= key->keyflags;
|
|
|
|
if ((wantkeyflags & heldkeyflags) == wantkeyflags) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == g_Vars.currentplayer->weapons) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool inv_has_goldeneyekey(void)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool inv_has_briefcase(void)
|
|
{
|
|
if (g_Vars.currentplayer->isdead == false) {
|
|
return inv_has_single_weapon_exc_all_guns(WEAPON_BRIEFCASE2);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool inv_has_data_uplink(void)
|
|
{
|
|
if (g_Vars.currentplayer->isdead == false) {
|
|
return inv_has_single_weapon_exc_all_guns(WEAPON_DATAUPLINK);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool inv_has_goldengun(void)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool inv_has_prop(struct prop *prop)
|
|
{
|
|
struct invitem *item = g_Vars.currentplayer->weapons;
|
|
struct prop *child;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_PROP && item->type_prop.prop == prop) {
|
|
return true;
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == g_Vars.currentplayer->weapons) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
child = g_Vars.currentplayer->prop->child;
|
|
|
|
while (child) {
|
|
if (child == prop) {
|
|
return true;
|
|
}
|
|
|
|
child = child->next;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
s32 inv_get_count(void)
|
|
{
|
|
s32 numitems = 0;
|
|
struct invitem *item;
|
|
|
|
if (g_Vars.currentplayer->equipallguns) {
|
|
numitems = WEAPON_PSYCHOSISGUN - current_stage_forbids_slayer();
|
|
}
|
|
|
|
item = g_Vars.currentplayer->weapons;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
|
|
if (prop) {
|
|
struct defaultobj *obj = prop->obj;
|
|
|
|
if (obj) {
|
|
if (prop->type == PROPTYPE_WEAPON) {
|
|
if (obj->hidden & OBJHFLAG_HASTEXTOVERRIDE) {
|
|
numitems++;
|
|
}
|
|
} else if (prop->type == PROPTYPE_OBJ) {
|
|
if ((obj->flags2 & OBJFLAG2_INVHIDDEN) == 0) {
|
|
numitems++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (item->type == INVITEMTYPE_WEAP) {
|
|
if (g_Vars.currentplayer->equipallguns == false
|
|
|| item->type_weap.weapon1 > WEAPON_PSYCHOSISGUN) {
|
|
numitems++;
|
|
}
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == g_Vars.currentplayer->weapons) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return numitems;
|
|
}
|
|
|
|
struct invitem *inv_get_item_by_index(s32 index)
|
|
{
|
|
struct invitem *item;
|
|
|
|
if (g_Vars.currentplayer->equipallguns) {
|
|
if (index < WEAPON_PSYCHOSISGUN - current_stage_forbids_slayer()) {
|
|
return NULL;
|
|
}
|
|
|
|
index += current_stage_forbids_slayer() - WEAPON_PSYCHOSISGUN;
|
|
}
|
|
|
|
item = g_Vars.currentplayer->weapons;
|
|
|
|
while (item) {
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
|
|
if (prop) {
|
|
struct defaultobj *obj = prop->obj;
|
|
|
|
if (obj) {
|
|
if (prop->type == PROPTYPE_WEAPON) {
|
|
if (obj->hidden & OBJHFLAG_HASTEXTOVERRIDE) {
|
|
if (index == 0) {
|
|
return item;
|
|
}
|
|
index--;
|
|
}
|
|
} else if (prop->type == PROPTYPE_OBJ) {
|
|
if ((obj->flags2 & OBJFLAG2_INVHIDDEN) == 0) {
|
|
if (index == 0) {
|
|
return item;
|
|
}
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (item->type == INVITEMTYPE_WEAP) {
|
|
if (g_Vars.currentplayer->equipallguns == false
|
|
|| item->type_weap.weapon1 > WEAPON_PSYCHOSISGUN) {
|
|
if (index == 0) {
|
|
return item;
|
|
}
|
|
index--;
|
|
}
|
|
}
|
|
|
|
item = item->next;
|
|
|
|
if (item == g_Vars.currentplayer->weapons) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct textoverride *inv_get_text_override_for_obj(struct defaultobj *obj)
|
|
{
|
|
struct textoverride *override = g_Vars.textoverrides;
|
|
|
|
while (override) {
|
|
if (override->obj == obj) {
|
|
return override;
|
|
}
|
|
|
|
override = override->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct textoverride *inv_get_text_override_for_weapon(s32 weaponnum)
|
|
{
|
|
struct textoverride *override = g_Vars.textoverrides;
|
|
|
|
while (override) {
|
|
if (override->objoffset == 0 && override->weapon == weaponnum) {
|
|
return override;
|
|
}
|
|
|
|
override = override->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
s32 inv_get_weapon_num_by_index(s32 index)
|
|
{
|
|
struct invitem *item = inv_get_item_by_index(index);
|
|
|
|
if (item) {
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
struct textoverride *override = inv_get_text_override_for_obj(prop->obj);
|
|
|
|
if (override) {
|
|
return override->weapon;
|
|
}
|
|
} else if (item->type == INVITEMTYPE_WEAP) {
|
|
return item->type_weap.weapon1;
|
|
}
|
|
} else if (g_Vars.currentplayer->equipallguns) {
|
|
if (index < WEAPON_PSYCHOSISGUN - current_stage_forbids_slayer()) {
|
|
index++;
|
|
return inv_add_one_if_cant_have_slayer(index);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u16 inv_get_name_id_by_index(s32 index)
|
|
{
|
|
struct invitem *item = inv_get_item_by_index(index);
|
|
s32 weaponnum = 0;
|
|
struct textoverride *override;
|
|
|
|
if (item) {
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
override = inv_get_text_override_for_obj(prop->obj);
|
|
|
|
if (override) {
|
|
if (override->inventorytext) {
|
|
return override->inventorytext;
|
|
}
|
|
|
|
weaponnum = override->weapon;
|
|
}
|
|
} else if (item->type == INVITEMTYPE_WEAP) {
|
|
weaponnum = item->type_weap.weapon1;
|
|
override = inv_get_text_override_for_weapon(weaponnum);
|
|
|
|
if (override && override->inventorytext) {
|
|
return override->inventorytext;
|
|
}
|
|
}
|
|
} else {
|
|
if (g_Vars.currentplayer->equipallguns) {
|
|
if (index < WEAPON_PSYCHOSISGUN - current_stage_forbids_slayer()) {
|
|
index++;
|
|
return bgun_get_name_id(inv_add_one_if_cant_have_slayer(index));
|
|
}
|
|
}
|
|
}
|
|
|
|
return bgun_get_name_id(weaponnum);
|
|
}
|
|
|
|
char *inv_get_name_by_index(s32 index)
|
|
{
|
|
return lang_get(inv_get_name_id_by_index(index));
|
|
}
|
|
|
|
char *inv_get_short_name_by_index(s32 index)
|
|
{
|
|
struct invitem *item = inv_get_item_by_index(index);
|
|
s32 weaponnum = 0;
|
|
struct textoverride *override;
|
|
|
|
if (item) {
|
|
if (item->type == INVITEMTYPE_PROP) {
|
|
struct prop *prop = item->type_prop.prop;
|
|
override = inv_get_text_override_for_obj(prop->obj);
|
|
|
|
if (override) {
|
|
#if VERSION < VERSION_JPN_FINAL
|
|
if (override->inventorytext) {
|
|
return lang_get(override->inventorytext);
|
|
}
|
|
#endif
|
|
|
|
weaponnum = override->weapon;
|
|
}
|
|
} else if (item->type == INVITEMTYPE_WEAP) {
|
|
weaponnum = item->type_weap.weapon1;
|
|
#if VERSION < VERSION_JPN_FINAL
|
|
override = inv_get_text_override_for_weapon(weaponnum);
|
|
|
|
if (override && override->inventorytext) {
|
|
return lang_get(override->inventorytext);
|
|
}
|
|
#endif
|
|
}
|
|
} else if (g_Vars.currentplayer->equipallguns) {
|
|
if (index < WEAPON_PSYCHOSISGUN - current_stage_forbids_slayer()) {
|
|
index++;
|
|
return bgun_get_short_name(inv_add_one_if_cant_have_slayer(index));
|
|
}
|
|
}
|
|
|
|
return bgun_get_short_name(weaponnum);
|
|
}
|
|
|
|
void inv_insert_text_override(struct textoverride *override)
|
|
{
|
|
override->next = g_Vars.textoverrides;
|
|
g_Vars.textoverrides = override;
|
|
}
|
|
|
|
u32 inv_get_current_index(void)
|
|
{
|
|
return g_Vars.currentplayer->equipcuritem;
|
|
}
|
|
|
|
void inv_set_current_index(u32 item)
|
|
{
|
|
g_Vars.currentplayer->equipcuritem = item;
|
|
}
|
|
|
|
void inv_calculate_current_index(void)
|
|
{
|
|
s32 curweaponnum = bgun_get_weapon_num(HAND_RIGHT);
|
|
s32 i;
|
|
|
|
g_Vars.currentplayer->equipcuritem = 0;
|
|
|
|
for (i = 0; i < inv_get_count(); i++) {
|
|
if (inv_get_weapon_num_by_index(i) == curweaponnum) {
|
|
g_Vars.currentplayer->equipcuritem = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
char *inv_get_pickup_text_by_obj(struct defaultobj *obj)
|
|
{
|
|
struct textoverride *override = inv_get_text_override_for_obj(obj);
|
|
|
|
if (override && override->pickuptext) {
|
|
return lang_get(override->pickuptext);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *inv_get_pickup_text_by_weapon_num(s32 weaponnum)
|
|
{
|
|
struct textoverride *override = inv_get_text_override_for_weapon(weaponnum);
|
|
|
|
if (override && override->pickuptext) {
|
|
return lang_get(override->pickuptext);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void inv_increment_held_time(s32 weapon1, s32 weapon2)
|
|
{
|
|
s32 leastusedtime;
|
|
s32 leastusedindex;
|
|
s32 i;
|
|
|
|
if (!gset_has_weapon_flag(weapon1, WEAPONFLAG_TRACKTIMEUSED)) {
|
|
return;
|
|
}
|
|
|
|
leastusedtime = 0x7fffffff;
|
|
leastusedindex = 0;
|
|
|
|
if (!gset_has_weapon_flag(weapon2, WEAPONFLAG_TRACKTIMEUSED)) {
|
|
weapon2 = 0;
|
|
}
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_Vars.currentplayer->gunheldarr); i++) {
|
|
s32 time = g_Vars.currentplayer->gunheldarr[i].totaltime240_60;
|
|
|
|
if (time >= 0) {
|
|
if (weapon1 == g_Vars.currentplayer->gunheldarr[i].weapon1 &&
|
|
weapon2 == g_Vars.currentplayer->gunheldarr[i].weapon2) {
|
|
g_Vars.currentplayer->gunheldarr[i].totaltime240_60 = time + g_Vars.lvupdate60;
|
|
break;
|
|
}
|
|
|
|
if (time < leastusedtime) {
|
|
leastusedtime = time;
|
|
leastusedindex = i;
|
|
}
|
|
} else {
|
|
leastusedindex = i;
|
|
i = ARRAYCOUNT(g_Vars.currentplayer->gunheldarr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == ARRAYCOUNT(g_Vars.currentplayer->gunheldarr)) {
|
|
g_Vars.currentplayer->gunheldarr[leastusedindex].totaltime240_60 = g_Vars.lvupdate60;
|
|
g_Vars.currentplayer->gunheldarr[leastusedindex].weapon1 = weapon1;
|
|
g_Vars.currentplayer->gunheldarr[leastusedindex].weapon2 = weapon2;
|
|
}
|
|
}
|
|
|
|
void inv_get_weapon_of_choice(s32 *weapon1, s32 *weapon2)
|
|
{
|
|
s32 mosttime = -1;
|
|
s32 i;
|
|
|
|
*weapon1 = 0;
|
|
*weapon2 = 0;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_Vars.currentplayer->gunheldarr); i++) {
|
|
if (g_Vars.currentplayer->gunheldarr[i].totaltime240_60 >= 0
|
|
&& g_Vars.currentplayer->gunheldarr[i].totaltime240_60 > mosttime) {
|
|
mosttime = g_Vars.currentplayer->gunheldarr[i].totaltime240_60;
|
|
*weapon1 = g_Vars.currentplayer->gunheldarr[i].weapon1;
|
|
*weapon2 = g_Vars.currentplayer->gunheldarr[i].weapon2;
|
|
}
|
|
}
|
|
}
|