Files
Mike Klaas 7ae0452e1a Add original symbol beside all function and symbol offsets (#332)
* Add original symbol beside all function offsets

e.g.

```
// 0x414E98 register_object_call
int animationRegisterCallback(

// 0x414E20 register_object_must_erase
int animationRegisterHideObjectForced(
```

This was programmatically generated so there could be errors.  Spot checking looks pretty good though
2026-05-11 19:49:15 -07:00

315 lines
7.6 KiB
C++

#include "trait.h"
#include <stdio.h>
#include "game.h"
#include "message.h"
#include "object.h"
#include "platform_compat.h"
#include "skill.h"
#include "stat.h"
namespace fallout {
// Provides metadata about traits.
typedef struct TraitDescription {
// The name of trait.
char* name;
// The description of trait.
//
// The description is only used in character editor to inform player about
// effects of this trait.
char* description;
// Identifier of art in [intrface.lst].
int frmId;
} TraitDescription;
// 0x66BE38 trait_message_file
static MessageList gTraitsMessageList;
// List of selected traits.
//
// 0x66BE40 pc_trait
static int gSelectedTraits[TRAITS_MAX_SELECTED_COUNT];
// 0x51DB84 trait_data
static TraitDescription gTraitDescriptions[TRAIT_COUNT] = {
{ nullptr, nullptr, 55 },
{ nullptr, nullptr, 56 },
{ nullptr, nullptr, 57 },
{ nullptr, nullptr, 58 },
{ nullptr, nullptr, 59 },
{ nullptr, nullptr, 60 },
{ nullptr, nullptr, 61 },
{ nullptr, nullptr, 62 },
{ nullptr, nullptr, 63 },
{ nullptr, nullptr, 64 },
{ nullptr, nullptr, 65 },
{ nullptr, nullptr, 66 },
{ nullptr, nullptr, 67 },
{ nullptr, nullptr, 94 },
{ nullptr, nullptr, 69 },
{ nullptr, nullptr, 70 },
};
// 0x4B39F0 trait_init
int traitsInit()
{
if (!messageListInit(&gTraitsMessageList)) {
return -1;
}
char path[COMPAT_MAX_PATH];
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "trait.msg");
if (!messageListLoad(&gTraitsMessageList, path)) {
return -1;
}
for (int trait = 0; trait < TRAIT_COUNT; trait++) {
MessageListItem messageListItem;
messageListItem.num = 100 + trait;
if (messageListGetItem(&gTraitsMessageList, &messageListItem)) {
gTraitDescriptions[trait].name = messageListItem.text;
}
messageListItem.num = 200 + trait;
if (messageListGetItem(&gTraitsMessageList, &messageListItem)) {
gTraitDescriptions[trait].description = messageListItem.text;
}
}
// NOTE: Uninline.
traitsReset();
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_TRAIT, &gTraitsMessageList);
return true;
}
// 0x4B3ADC trait_reset
void traitsReset()
{
for (int index = 0; index < TRAITS_MAX_SELECTED_COUNT; index++) {
gSelectedTraits[index] = -1;
}
}
// 0x4B3AF8 trait_exit
void traitsExit()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_TRAIT, nullptr);
messageListFree(&gTraitsMessageList);
}
// Loads trait system state from save game.
//
// 0x4B3B08 trait_load
int traitsLoad(File* stream)
{
return fileReadInt32List(stream, gSelectedTraits, TRAITS_MAX_SELECTED_COUNT);
}
// Saves trait system state to save game.
//
// 0x4B3B28 trait_save
int traitsSave(File* stream)
{
return fileWriteInt32List(stream, gSelectedTraits, TRAITS_MAX_SELECTED_COUNT);
}
// Sets selected traits.
//
// 0x4B3B48 trait_set
void traitsSetSelected(int trait1, int trait2)
{
gSelectedTraits[0] = trait1;
gSelectedTraits[1] = trait2;
}
// Returns selected traits.
//
// 0x4B3B54 trait_get
void traitsGetSelected(int* trait1, int* trait2)
{
*trait1 = gSelectedTraits[0];
*trait2 = gSelectedTraits[1];
}
// Returns a name of the specified trait, or `NULL` if the specified trait is
// out of range.
//
// 0x4B3B68 trait_name
char* traitGetName(int trait)
{
return trait >= 0 && trait < TRAIT_COUNT ? gTraitDescriptions[trait].name : nullptr;
}
// Returns a description of the specified trait, or `NULL` if the specified
// trait is out of range.
//
// 0x4B3B88 trait_description
char* traitGetDescription(int trait)
{
return trait >= 0 && trait < TRAIT_COUNT ? gTraitDescriptions[trait].description : nullptr;
}
// Return an art ID of the specified trait, or `0` if the specified trait is
// out of range.
//
// 0x4B3BA8 trait_pic
int traitGetFrmId(int trait)
{
return trait >= 0 && trait < TRAIT_COUNT ? gTraitDescriptions[trait].frmId : 0;
}
// Returns `true` if the specified trait is selected.
//
// 0x4B3BC8 trait_level
bool traitIsSelected(int trait)
{
return gSelectedTraits[0] == trait || gSelectedTraits[1] == trait;
}
// Returns stat modifier depending on selected traits.
//
// 0x4B3C7C trait_adjust_stat
int traitGetStatModifier(int stat)
{
int modifier = 0;
switch (stat) {
case STAT_STRENGTH:
if (traitIsSelected(TRAIT_GIFTED)) {
modifier += 1;
}
if (traitIsSelected(TRAIT_BRUISER)) {
modifier += 2;
}
break;
case STAT_PERCEPTION:
if (traitIsSelected(TRAIT_GIFTED)) {
modifier += 1;
}
break;
case STAT_ENDURANCE:
if (traitIsSelected(TRAIT_GIFTED)) {
modifier += 1;
}
break;
case STAT_CHARISMA:
if (traitIsSelected(TRAIT_GIFTED)) {
modifier += 1;
}
break;
case STAT_INTELLIGENCE:
if (traitIsSelected(TRAIT_GIFTED)) {
modifier += 1;
}
break;
case STAT_AGILITY:
if (traitIsSelected(TRAIT_GIFTED)) {
modifier += 1;
}
if (traitIsSelected(TRAIT_SMALL_FRAME)) {
modifier += 1;
}
break;
case STAT_LUCK:
if (traitIsSelected(TRAIT_GIFTED)) {
modifier += 1;
}
break;
case STAT_MAXIMUM_ACTION_POINTS:
if (traitIsSelected(TRAIT_BRUISER)) {
modifier -= 2;
}
break;
case STAT_ARMOR_CLASS:
if (traitIsSelected(TRAIT_KAMIKAZE)) {
modifier -= critterGetBaseStat(gDude, STAT_ARMOR_CLASS);
}
break;
case STAT_MELEE_DAMAGE:
if (traitIsSelected(TRAIT_HEAVY_HANDED)) {
modifier += 4;
}
break;
case STAT_CARRY_WEIGHT:
if (traitIsSelected(TRAIT_SMALL_FRAME)) {
modifier -= 10 * critterGetBaseStat(gDude, STAT_STRENGTH);
}
break;
case STAT_SEQUENCE:
if (traitIsSelected(TRAIT_KAMIKAZE)) {
modifier += 5;
}
break;
case STAT_HEALING_RATE:
if (traitIsSelected(TRAIT_FAST_METABOLISM)) {
modifier += 2;
}
break;
case STAT_CRITICAL_CHANCE:
if (traitIsSelected(TRAIT_FINESSE)) {
modifier += 10;
}
break;
case STAT_BETTER_CRITICALS:
if (traitIsSelected(TRAIT_HEAVY_HANDED)) {
modifier -= 30;
}
break;
case STAT_RADIATION_RESISTANCE:
if (traitIsSelected(TRAIT_FAST_METABOLISM)) {
modifier -= critterGetBaseStat(gDude, STAT_RADIATION_RESISTANCE);
}
break;
case STAT_POISON_RESISTANCE:
if (traitIsSelected(TRAIT_FAST_METABOLISM)) {
modifier -= critterGetBaseStat(gDude, STAT_POISON_RESISTANCE);
}
break;
}
return modifier;
}
// Returns skill modifier depending on selected traits.
//
// 0x4B40FC trait_adjust_skill
int traitGetSkillModifier(int skill)
{
int modifier = 0;
if (traitIsSelected(TRAIT_GIFTED)) {
modifier -= 10;
}
if (traitIsSelected(TRAIT_GOOD_NATURED)) {
switch (skill) {
case SKILL_SMALL_GUNS:
case SKILL_BIG_GUNS:
case SKILL_ENERGY_WEAPONS:
case SKILL_UNARMED:
case SKILL_MELEE_WEAPONS:
case SKILL_THROWING:
modifier -= 10;
break;
case SKILL_FIRST_AID:
case SKILL_DOCTOR:
case SKILL_SPEECH:
case SKILL_BARTER:
modifier += 15;
break;
}
}
return modifier;
}
} // namespace fallout