diff --git a/generated/meson.build b/generated/meson.build index ca4c1c2994..bea774d89c 100644 --- a/generated/meson.build +++ b/generated/meson.build @@ -82,6 +82,7 @@ metang_generators = { 'sdat': { 'type': 'enum', 'tag': 'SDATID' }, 'signpost_commands': { 'type': 'enum', 'tag': 'SignpostCommand' }, 'signpost_types': { 'type': 'enum', 'tag': 'SignpostType' }, + 'size_contest_results': { 'type': 'enum', 'tag': 'SizeContestResult' }, 'shadow_sizes': { 'type': 'enum', 'tag': 'ShadowSize' }, 'species': { 'type': 'enum', 'tag': 'Species' }, 'species_data_params': { 'type': 'enum', 'tag': 'SpeciesDataParam' }, diff --git a/generated/size_contest_results.txt b/generated/size_contest_results.txt new file mode 100644 index 0000000000..05a8d47388 --- /dev/null +++ b/generated/size_contest_results.txt @@ -0,0 +1,3 @@ +SIZE_CONTEST_SMALLER +SIZE_CONTEST_SAME_SIZE +SIZE_CONTEST_LARGER diff --git a/include/overlay005/ov5_021EE7D4.h b/include/overlay005/ov5_021EE7D4.h deleted file mode 100644 index d76c8cc2e1..0000000000 --- a/include/overlay005/ov5_021EE7D4.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef POKEPLATINUM_OV5_021EE7D4_H -#define POKEPLATINUM_OV5_021EE7D4_H - -#include "field/field_system_decl.h" - -u8 ov5_021EE920(FieldSystem *fieldSystem, u16 param1); -void ov5_021EE9BC(FieldSystem *fieldSystem, u16 param1); -void ov5_021EEA54(FieldSystem *fieldSystem, u8 param1, u8 param2, u16 param3); -void ov5_021EEA84(FieldSystem *fieldSystem, u8 param1, u8 param2, u16 param3); - -#endif // POKEPLATINUM_OV5_021EE7D4_H diff --git a/include/overlay005/size_contest.h b/include/overlay005/size_contest.h new file mode 100644 index 0000000000..6422426c76 --- /dev/null +++ b/include/overlay005/size_contest.h @@ -0,0 +1,11 @@ +#ifndef POKEPLATINUM_OV5_SIZE_CONTEST_H +#define POKEPLATINUM_OV5_SIZE_CONTEST_H + +#include "field/field_system_decl.h" + +u8 SizeContest_CalcResultForPartyMon(FieldSystem *fieldSystem, u16 partySlot); +void SizeContest_UpdateRecordFromPartyMon(FieldSystem *fieldSystem, u16 partySlot); +void SizeContest_SetRecordSizeStrParams(FieldSystem *fieldSystem, u8 intPartIdx, u8 fracPartIdx, u16 species); +void SizeContest_SetPartyMonSizeStrParams(FieldSystem *fieldSystem, u8 intPartIdx, u8 fracPartIdx, u16 partySlot); + +#endif // POKEPLATINUM_OV5_SIZE_CONTEST_H diff --git a/platinum.us/main.lsf b/platinum.us/main.lsf index 06ee0e9cd3..dc6da70396 100644 --- a/platinum.us/main.lsf +++ b/platinum.us/main.lsf @@ -505,7 +505,7 @@ Overlay overlay5 Object main.nef.p/src_overlay005_ov5_021ECC20.c.o Object main.nef.p/src_overlay005_ov5_021ECE40.c.o Object main.nef.p/src_overlay005_ov5_021EE75C.c.o - Object main.nef.p/src_overlay005_ov5_021EE7D4.c.o + Object main.nef.p/src_overlay005_size_contest.c.o Object main.nef.p/src_overlay005_loaded_map_buffers.c.o Object main.nef.p/src_overlay005_bdhc.c.o Object main.nef.p/src_overlay005_dynamic_terrain_height.c.o diff --git a/src/meson.build b/src/meson.build index 5b72f08e6d..915696906f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -398,7 +398,7 @@ pokeplatinum_c = files( 'overlay005/ov5_021ECC20.c', 'overlay005/ov5_021ECE40.c', 'overlay005/ov5_021EE75C.c', - 'overlay005/ov5_021EE7D4.c', + 'overlay005/size_contest.c', 'overlay005/loaded_map_buffers.c', 'overlay005/bdhc.c', 'overlay005/dynamic_terrain_height.c', diff --git a/src/overlay005/ov5_021EE7D4.c b/src/overlay005/ov5_021EE7D4.c deleted file mode 100644 index cd65dcdc06..0000000000 --- a/src/overlay005/ov5_021EE7D4.c +++ /dev/null @@ -1,178 +0,0 @@ -#include "overlay005/ov5_021EE7D4.h" - -#include -#include - -#include "struct_decls/struct_02098700_decl.h" - -#include "field/field_system.h" - -#include "party.h" -#include "pokedex_heightweight.h" -#include "pokemon.h" -#include "script_manager.h" -#include "string_template.h" -#include "system_vars.h" -#include "unk_0206CCB0.h" -#include "vars_flags.h" - -typedef struct { - u16 unk_00; - u16 unk_02; - u16 unk_04; - u16 unk_06; -} UnkStruct_ov5_021FF508; - -static const UnkStruct_ov5_021FF508 Unk_ov5_021FF508[] = { - { 0x122, 0x1, 0x0, 0x0 }, - { 0x12C, 0x1, 0xA, 0x0 }, - { 0x190, 0x2, 0x6E, 0x0 }, - { 0x1F4, 0x4, 0x136, 0x0 }, - { 0x258, 0x14, 0x2C6, 0x0 }, - { 0x2BC, 0x32, 0xA96, 0x0 }, - { 0x320, 0x64, 0x1E1E, 0x0 }, - { 0x384, 0x96, 0x452E, 0x0 }, - { 0x3E8, 0x96, 0x7FC6, 0x0 }, - { 0x44C, 0x64, 0xBA5E, 0x0 }, - { 0x4B0, 0x32, 0xE16E, 0x0 }, - { 0x514, 0x14, 0xF4F6, 0x0 }, - { 0x578, 0x5, 0xFCC6, 0x0 }, - { 0x5DC, 0x2, 0xFEBA, 0x0 }, - { 0x640, 0x1, 0xFF82, 0x0 }, - { 0x6A4, 0x1, 0xFFE6, 0x0 } -}; - -static int ov5_021EE7D4(Pokemon *param0) -{ - u16 v0; - u16 v1, v2, v3; - u16 v4, v5, v6; - u16 v7, v8; - - v0 = Pokemon_GetValue(param0, MON_DATA_PERSONALITY, NULL) & 0xffff; - v1 = Pokemon_GetValue(param0, MON_DATA_HP_IV, NULL) & 0xf; - v2 = Pokemon_GetValue(param0, MON_DATA_ATK_IV, NULL) & 0xf; - v3 = Pokemon_GetValue(param0, MON_DATA_DEF_IV, NULL) & 0xf; - v4 = Pokemon_GetValue(param0, MON_DATA_SPEED_IV, NULL) & 0xf; - v5 = Pokemon_GetValue(param0, MON_DATA_SPATK_IV, NULL) & 0xf; - v6 = Pokemon_GetValue(param0, MON_DATA_SPDEF_IV, NULL) & 0xf; - v7 = ((v2 ^ v3) * v1) ^ (v0 & 0xff); - v8 = ((v5 ^ v6) * v4) ^ (v0 >> 8); - - return v7 * 256 + v8; -} - -static u8 ov5_021EE880(u16 param0) -{ - u8 v0; - - for (v0 = 1; v0 < NELEMS(Unk_ov5_021FF508) - 1; v0++) { - if (param0 < Unk_ov5_021FF508[v0].unk_04) { - return v0 - 1; - } - } - - return v0; -} - -static u32 ov5_021EE8A8(u16 param0, u16 param1) -{ - u64 v0, v1, v2; - u32 v3; - u64 v4; - u8 v5; - HeightWeightData *v6 = Pokedex_HeightWeightData(HEAP_ID_FIELD); - Pokedex_HeightWeightData_Load(v6, 0, HEAP_ID_FIELD); - - v3 = Pokedex_HeightWeightData_Height((const HeightWeightData *)v6, param0); - Pokedex_HeightWeightData_Release(v6); - Pokedex_HeightWeightData_Free(v6); - - v5 = ov5_021EE880(param1); - v0 = Unk_ov5_021FF508[v5].unk_00; - v1 = Unk_ov5_021FF508[v5].unk_02; - v2 = Unk_ov5_021FF508[v5].unk_04; - v4 = v0 + (param1 - v2) / v1; - - return v3 * v4 / 10; -} - -u8 ov5_021EE920(FieldSystem *fieldSystem, u16 param1) -{ - Pokemon *v0; - vu16 v1, v2; - u16 v3; - u32 v4; - u32 v5; - - v0 = Party_GetPokemonBySlotIndex(SaveData_GetParty(fieldSystem->saveData), param1); - v3 = Pokemon_GetValue(v0, MON_DATA_SPECIES, NULL); - v1 = ov5_021EE7D4(v0); - v4 = ov5_021EE8A8(v3, v1); - v2 = SystemVars_GetSizeContestRecord(SaveData_GetVarsFlags(fieldSystem->saveData)); - v5 = ov5_021EE8A8(v3, v2); - - { - u32 v6 = (((v4 * 1000) / 254 + 5) / 10); - u32 v7 = (((v5 * 1000) / 254 + 5) / 10); - - if (v6 == v7) { - return 1; - } else { - if (v6 > v7) { - sub_0206DC6C(fieldSystem, v4, v0); - return 2; - } else { - return 0; - } - } - } -} - -void ov5_021EE9BC(FieldSystem *fieldSystem, u16 param1) -{ - Pokemon *v0; - vu16 v1; - - v0 = Party_GetPokemonBySlotIndex(SaveData_GetParty(fieldSystem->saveData), param1); - v1 = ov5_021EE7D4(v0); - - SystemVars_SetSizeContestRecord(SaveData_GetVarsFlags(fieldSystem->saveData), v1); -} - -static void ov5_021EE9E8(FieldSystem *fieldSystem, u8 param1, u8 param2, u16 param3, vu16 param4) -{ - u32 v0; - StringTemplate **v1 = FieldSystem_GetScriptMemberPtr(fieldSystem, SCRIPT_MANAGER_STR_TEMPLATE); - - v0 = ov5_021EE8A8(param3, param4); - - { - u32 v2 = v0; - v0 = (((v0 * 1000) / 254 + 5) / 10); - } - - StringTemplate_SetNumber(*v1, param1, v0 / 10, 3, 0, 1); - StringTemplate_SetNumber(*v1, param2, v0 % 10, 1, 0, 1); -} - -void ov5_021EEA54(FieldSystem *fieldSystem, u8 param1, u8 param2, u16 param3) -{ - vu16 v0; - - v0 = SystemVars_GetSizeContestRecord(SaveData_GetVarsFlags(fieldSystem->saveData)); - ov5_021EE9E8(fieldSystem, param1, param2, param3, v0); -} - -void ov5_021EEA84(FieldSystem *fieldSystem, u8 param1, u8 param2, u16 param3) -{ - Pokemon *v0; - u16 v1; - vu16 v2; - - v0 = Party_GetPokemonBySlotIndex(SaveData_GetParty(fieldSystem->saveData), param3); - v1 = Pokemon_GetValue(v0, MON_DATA_SPECIES, NULL); - v2 = ov5_021EE7D4(v0); - - ov5_021EE9E8(fieldSystem, param1, param2, v1, v2); -} diff --git a/src/overlay005/size_contest.c b/src/overlay005/size_contest.c new file mode 100644 index 0000000000..460c1aa612 --- /dev/null +++ b/src/overlay005/size_contest.c @@ -0,0 +1,169 @@ +#include "overlay005/size_contest.h" + +#include +#include + +#include "generated/size_contest_results.h" +#include "generated/string_padding_mode.h" + +#include "struct_decls/struct_02098700_decl.h" + +#include "field/field_system.h" + +#include "party.h" +#include "pokedex_heightweight.h" +#include "pokemon.h" +#include "script_manager.h" +#include "strbuf.h" +#include "string_template.h" +#include "system_vars.h" +#include "unk_0206CCB0.h" +#include "vars_flags.h" + +#define MM_TO_TENTH_OF_INCH(length) (((length * 1000) / 254 + 5) / 10) + +static u8 GetApproxIntervalIdx(u16 sizeFactor); + +/* + * Used to approximate a normal distribution from an uniformly distributed u16 + * variable through a piecewise approximation of the inverse error function (erf^-1) + * from 0..65535 into 290...1725 + */ +typedef struct NormalApproxIntervalParams { + u16 base; + u16 divisor; + u16 intervalStart; + u16 unused_06; +} NormalApproxIntervalParams; + +// clang-format off +static const NormalApproxIntervalParams sUniformToNormalIntervals[] = { + { .base = 290, .divisor = 1, .intervalStart = 0, .unused_06 = 0x0 }, + { .base = 300, .divisor = 1, .intervalStart = 10, .unused_06 = 0x0 }, + { .base = 400, .divisor = 2, .intervalStart = 110, .unused_06 = 0x0 }, + { .base = 500, .divisor = 4, .intervalStart = 310, .unused_06 = 0x0 }, + { .base = 600, .divisor = 20, .intervalStart = 710, .unused_06 = 0x0 }, + { .base = 700, .divisor = 50, .intervalStart = 2710, .unused_06 = 0x0 }, + { .base = 800, .divisor = 100, .intervalStart = 7710, .unused_06 = 0x0 }, + { .base = 900, .divisor = 150, .intervalStart = 17710, .unused_06 = 0x0 }, + { .base = 1000, .divisor = 150, .intervalStart = 32710, .unused_06 = 0x0 }, + { .base = 1100, .divisor = 100, .intervalStart = 47710, .unused_06 = 0x0 }, + { .base = 1200, .divisor = 50, .intervalStart = 57710, .unused_06 = 0x0 }, + { .base = 1300, .divisor = 20, .intervalStart = 62710, .unused_06 = 0x0 }, + { .base = 1400, .divisor = 5, .intervalStart = 64710, .unused_06 = 0x0 }, + { .base = 1500, .divisor = 2, .intervalStart = 65210, .unused_06 = 0x0 }, + { .base = 1600, .divisor = 1, .intervalStart = 65410, .unused_06 = 0x0 }, + { .base = 1700, .divisor = 1, .intervalStart = 65510, .unused_06 = 0x0 }, +}; +// clang-format on + +// Calculates the erf^-1 approximation. +// Using u64 is required for matching. +static inline u64 CalcMultFromSizeFactor(u16 sizeFactor) +{ + u8 approxPieceIdx = GetApproxIntervalIdx(sizeFactor); + u64 base = sUniformToNormalIntervals[approxPieceIdx].base; + u64 div = sUniformToNormalIntervals[approxPieceIdx].divisor; + u64 start = sUniformToNormalIntervals[approxPieceIdx].intervalStart; + return base + (sizeFactor - start) / div; +} + +static int CalcSizeFactor(Pokemon *mon) +{ + u16 personnalityLow = Pokemon_GetValue(mon, MON_DATA_PERSONALITY, NULL) & 0xffff; + u16 hpIV = Pokemon_GetValue(mon, MON_DATA_HP_IV, NULL) & 0xf; + u16 atkIV = Pokemon_GetValue(mon, MON_DATA_ATK_IV, NULL) & 0xf; + u16 defIV = Pokemon_GetValue(mon, MON_DATA_DEF_IV, NULL) & 0xf; + u16 speedIV = Pokemon_GetValue(mon, MON_DATA_SPEED_IV, NULL) & 0xf; + u16 spAtkIV = Pokemon_GetValue(mon, MON_DATA_SPATK_IV, NULL) & 0xf; + u16 spDefIV = Pokemon_GetValue(mon, MON_DATA_SPDEF_IV, NULL) & 0xf; + u16 high = ((atkIV ^ defIV) * hpIV) ^ (personnalityLow & 0xff); + u16 low = ((spAtkIV ^ spDefIV) * speedIV) ^ (personnalityLow >> 8); + + return high * 256 + low; +} + +static u8 GetApproxIntervalIdx(u16 sizeFactor) +{ + u8 i; + + for (i = 1; i < NELEMS(sUniformToNormalIntervals) - 1; i++) { + if (sizeFactor < sUniformToNormalIntervals[i].intervalStart) { + return i - 1; + } + } + + return i; +} + +static u32 CalcMillimeterSize(u16 species, u16 sizeFactor) +{ + HeightWeightData *heightWeightData = Pokedex_HeightWeightData(HEAP_ID_FIELD); + Pokedex_HeightWeightData_Load(heightWeightData, FALSE, HEAP_ID_FIELD); + + u32 height = Pokedex_HeightWeightData_Height(heightWeightData, species); + Pokedex_HeightWeightData_Release(heightWeightData); + Pokedex_HeightWeightData_Free(heightWeightData); + + u64 sizeMult = CalcMultFromSizeFactor(sizeFactor); + + return height * sizeMult / 10; +} + +u8 SizeContest_CalcResultForPartyMon(FieldSystem *fieldSystem, u16 partySlot) +{ + Pokemon *mon = Party_GetPokemonBySlotIndex(SaveData_GetParty(fieldSystem->saveData), partySlot); + u16 species = Pokemon_GetValue(mon, MON_DATA_SPECIES, NULL); + vu16 sizeFactor = CalcSizeFactor(mon); + u32 newSize = CalcMillimeterSize(species, sizeFactor); + vu16 recordSizeFactor = SystemVars_GetSizeContestRecord(SaveData_GetVarsFlags(fieldSystem->saveData)); + u32 recordSize = CalcMillimeterSize(species, recordSizeFactor); + + u32 newSizeInches = MM_TO_TENTH_OF_INCH(newSize); + u32 recordSizeInches = MM_TO_TENTH_OF_INCH(recordSize); + + if (newSizeInches == recordSizeInches) { + return SIZE_CONTEST_SAME_SIZE; + } else { + if (newSizeInches > recordSizeInches) { + sub_0206DC6C(fieldSystem, newSize, mon); + return SIZE_CONTEST_LARGER; + } else { + return SIZE_CONTEST_SMALLER; + } + } +} + +void SizeContest_UpdateRecordFromPartyMon(FieldSystem *fieldSystem, u16 partySlot) +{ + Pokemon *mon = Party_GetPokemonBySlotIndex(SaveData_GetParty(fieldSystem->saveData), partySlot); + vu16 sizeFactor = CalcSizeFactor(mon); + + SystemVars_SetSizeContestRecord(SaveData_GetVarsFlags(fieldSystem->saveData), sizeFactor); +} + +static void SetStrTemplateMonSizeParams(FieldSystem *fieldSystem, u8 intPartIdx, u8 fracPartIdx, u16 species, vu16 sizeFactor) +{ + StringTemplate **strTemplate = FieldSystem_GetScriptMemberPtr(fieldSystem, SCRIPT_MANAGER_STR_TEMPLATE); + + u32 size = CalcMillimeterSize(species, sizeFactor); + size = MM_TO_TENTH_OF_INCH(size); + + StringTemplate_SetNumber(*strTemplate, intPartIdx, size / 10, 3, PADDING_MODE_NONE, CHARSET_MODE_EN); + StringTemplate_SetNumber(*strTemplate, fracPartIdx, size % 10, 1, PADDING_MODE_NONE, CHARSET_MODE_EN); +} + +void SizeContest_SetRecordSizeStrParams(FieldSystem *fieldSystem, u8 intPartIdx, u8 fracPartIdx, u16 species) +{ + vu16 recordSizeFactor = SystemVars_GetSizeContestRecord(SaveData_GetVarsFlags(fieldSystem->saveData)); + SetStrTemplateMonSizeParams(fieldSystem, intPartIdx, fracPartIdx, species, recordSizeFactor); +} + +void SizeContest_SetPartyMonSizeStrParams(FieldSystem *fieldSystem, u8 intPartIdx, u8 fracPartIdx, u16 partySlot) +{ + Pokemon *mon = Party_GetPokemonBySlotIndex(SaveData_GetParty(fieldSystem->saveData), partySlot); + u16 species = Pokemon_GetValue(mon, MON_DATA_SPECIES, NULL); + vu16 sizeFactor = CalcSizeFactor(mon); + + SetStrTemplateMonSizeParams(fieldSystem, intPartIdx, fracPartIdx, species, sizeFactor); +} diff --git a/src/scrcmd.c b/src/scrcmd.c index 54154e0efa..8f97fe0e81 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -63,11 +63,11 @@ #include "overlay005/ov5_021DFB54.h" #include "overlay005/ov5_021EA874.h" #include "overlay005/ov5_021ECC20.h" -#include "overlay005/ov5_021EE7D4.h" #include "overlay005/ov5_021F6454.h" #include "overlay005/save_info_window.h" #include "overlay005/scrcmd_move_tutor.h" #include "overlay005/signpost.h" +#include "overlay005/size_contest.h" #include "overlay005/struct_ov5_021DD42C.h" #include "overlay005/vs_seeker.h" #include "overlay006/npc_trade.h" @@ -5820,7 +5820,7 @@ static BOOL ScrCmd_1C1(ScriptContext *ctx) u16 *v1 = ScriptContext_GetVarPointer(ctx); u16 v2 = ScriptContext_GetVar(ctx); - *v1 = ov5_021EE920(fieldSystem, v2); + *v1 = SizeContest_CalcResultForPartyMon(fieldSystem, v2); return FALSE; } @@ -5830,7 +5830,7 @@ static BOOL ScrCmd_1C2(ScriptContext *ctx) FieldSystem *fieldSystem = ctx->fieldSystem; u16 v2 = ScriptContext_GetVar(ctx); - ov5_021EE9BC(fieldSystem, v2); + SizeContest_UpdateRecordFromPartyMon(fieldSystem, v2); return FALSE; } @@ -5842,7 +5842,7 @@ static BOOL ScrCmd_1C3(ScriptContext *ctx) u16 v2 = ScriptContext_GetVar(ctx); u16 v3 = ScriptContext_GetVar(ctx); - ov5_021EEA84(fieldSystem, v1, v2, v3); + SizeContest_SetPartyMonSizeStrParams(fieldSystem, v1, v2, v3); return FALSE; } @@ -5853,7 +5853,7 @@ static BOOL ScrCmd_1C4(ScriptContext *ctx) u16 v2 = ScriptContext_GetVar(ctx); u16 v3 = ScriptContext_GetVar(ctx); - ov5_021EEA54(fieldSystem, v1, v2, v3); + SizeContest_SetRecordSizeStrParams(fieldSystem, v1, v2, v3); return FALSE; }