Files
oot/src/code/z_elf_message.c
T
fig02 fd24ad6e67 Document elf_message scripting system (#1398)
* `cUpElfMsgs` -> `cUpMsgs`

Motivation:
ElfMessage is the name of the system that handles c-up messages from navi, but also messages from child/adult saria
so it would make sense to name this after the system name like "ElfMsg"
but "cUpElf" primarily reads like (to me) "c up navi" since the actor implementing navi is EnElf

I think it'd be better to clear that confusion between "navi" and "elfmsg system" by just not using "elf" in the name

* Standardize c-up navi messages ElfMessage data to be named after "CUp"

* -> "Navi"

* Make symbols in `elf_message_` segments static

* new docs

* change commands, format

* introduce navi file id

* use enum

* remove concat

* add clarifying comment for skip

* concat cmd type

* Hints -> QuestHint

* rename command header

* s -> g

* missed enum name

* singular

* missed one

Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com>
2022-10-15 13:24:13 -04:00

199 lines
6.9 KiB
C

#include "global.h"
#include "z64quest_hint_commands.h"
QuestHintCmd sChildSariaQuestHints[] = {
QUEST_HINT_STRENGTH_UPG(SKIP, 0, false, 3),
QUEST_HINT_FLAG(CHECK, EVENTCHKINF_37, false, 0x61),
QUEST_HINT_END(0x64),
QUEST_HINT_FLAG(CHECK, EVENTCHKINF_25, false, 0x62),
QUEST_HINT_FLAG(CHECK, EVENTCHKINF_37, false, 0x63),
QUEST_HINT_FLAG(CHECK, EVENTCHKINF_43, false, 0x65),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FOREST, false, 0x66),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FIRE, false, 0x66),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_WATER, false, 0x66),
QUEST_HINT_SONG(CHECK, ITEM_SONG_STORMS, false, 0x67),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SPIRIT, false, 0x68),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SHADOW, false, 0x68),
QUEST_HINT_END(0x69),
};
QuestHintCmd sAdultSariaQuestHints[] = {
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FOREST, false, 0x6A),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FIRE, false, 0x6B),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_WATER, false, 0x6B),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SPIRIT, false, 0x6C),
QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SHADOW, false, 0x6C),
QUEST_HINT_END(0x6D),
};
u32 QuestHint_CheckCondition(QuestHintCmd* hintCmd) {
s32 type = hintCmd->byte0 & 0x1E;
u16 flag;
switch (type) {
case (QUEST_HINT_CONDITION_FLAG << 1):
flag = 1 << (hintCmd->byte1 & 0x0F);
return ((hintCmd->byte0 & 1) == 1) ==
((flag & gSaveContext.eventChkInf[(hintCmd->byte1 & 0xF0) >> 4]) != 0);
case (QUEST_HINT_CONDITION_DUNGEON_ITEM << 1):
return ((hintCmd->byte0 & 1) == 1) ==
(CHECK_DUNGEON_ITEM(hintCmd->byte1 - ITEM_KEY_BOSS, gSaveContext.mapIndex) != 0);
case (QUEST_HINT_CONDITION_ITEM << 1):
return ((hintCmd->byte0 & 1) == 1) == (hintCmd->byte3 == INV_CONTENT(hintCmd->byte1));
case (QUEST_HINT_CONDITION_OTHER << 1):
switch (hintCmd->byte1 & 0xF0) {
case (QUEST_HINT_CONDITION_STRENGTH_UPG << 4):
return ((hintCmd->byte0 & 1) == 1) == ((hintCmd->byte1 & 0x0F) == CUR_UPG_VALUE(UPG_STRENGTH));
case (QUEST_HINT_CONDITION_BOOTS << 4):
return ((hintCmd->byte0 & 1) == 1) ==
(CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS,
hintCmd->byte3 - ITEM_BOOTS_KOKIRI + EQUIP_INV_BOOTS_KOKIRI) != 0);
case (QUEST_HINT_CONDITION_SONG << 4):
return ((hintCmd->byte0 & 1) == 1) ==
(CHECK_QUEST_ITEM(hintCmd->byte3 - ITEM_SONG_MINUET + QUEST_SONG_MINUET) != 0);
case (QUEST_HINT_CONDITION_MEDALLION << 4):
return ((hintCmd->byte0 & 1) == 1) ==
(CHECK_QUEST_ITEM(hintCmd->byte3 - ITEM_MEDALLION_FOREST + QUEST_MEDALLION_FOREST) != 0);
case (QUEST_HINT_CONDITION_MAGIC << 4):
return ((hintCmd->byte0 & 1) == 1) == (((void)0, gSaveContext.isMagicAcquired) != 0);
}
}
LOG_STRING("企画外 条件", "../z_elf_message.c", 156); // "Unplanned conditions"
ASSERT(0, "0", "../z_elf_message.c", 157);
return false;
}
u32 QuestHint_CheckConditionChain(QuestHintCmd** hintCmdPtr) {
u32 allConditionsMet = true;
while (((*hintCmdPtr)->byte0 & 0xE0) == (QUEST_HINT_TYPE_CHAIN << 5)) {
// if any of the conditions checked in the chain are not met,
// the whole chain is considered false
if (!QuestHint_CheckCondition(*hintCmdPtr)) {
allConditionsMet = false;
}
*hintCmdPtr += 1;
}
if (allConditionsMet) {
return QuestHint_CheckCondition(*hintCmdPtr);
} else {
return false;
}
}
u32 QuestHint_CheckRandomCondition(QuestHintCmd** hintCmdPtr) {
QuestHintCmd* hintCmd = *hintCmdPtr;
u32 conditions[10];
s32 i = 0;
s32 totalChecked = 0;
s32 rand;
do {
conditions[totalChecked] = QuestHint_CheckCondition(hintCmd);
// i is incremented if the condition was met
i += conditions[totalChecked];
totalChecked++;
hintCmd++;
} while ((hintCmd->byte0 & 0xE0) == (QUEST_HINT_TYPE_RANDOM << 5));
// if none of the conditions checked were satisfied, there are none to use
if (i == 0) {
return false;
}
// choose a random number between 0 and the total amount of conditions met
rand = Rand_ZeroFloat(i);
for (i = 0; i < totalChecked; i++) {
// keep decrementing the random number until it reaches 0 then use that hint
if (conditions[i]) {
if (rand > 0) {
rand--;
} else {
return true;
}
}
*hintCmdPtr += 1;
}
return false;
}
u16 QuestHint_GetTextIdFromScript(QuestHintCmd* hintCmd) {
while (true) {
switch (hintCmd->byte0 & 0xE0) {
case (QUEST_HINT_TYPE_CHECK << 5):
if (QuestHint_CheckCondition(hintCmd)) {
return hintCmd->byte2 | 0x100;
}
break;
case (QUEST_HINT_TYPE_CHAIN << 5):
if (QuestHint_CheckConditionChain(&hintCmd)) {
return hintCmd->byte2 | 0x100;
}
break;
case (QUEST_HINT_TYPE_RANDOM << 5):
if (QuestHint_CheckRandomCondition(&hintCmd)) {
return hintCmd->byte2 | 0x100;
}
break;
case (QUEST_HINT_TYPE_SKIP << 5):
if (QuestHint_CheckCondition(hintCmd)) {
hintCmd += hintCmd->byte2; // skip the specified amount
hintCmd--; // decrement by 1 because it will be incremented again below
}
break;
case (QUEST_HINT_TYPE_END << 5):
return hintCmd->byte2 | 0x100;
default:
LOG_STRING("企画外 条件", "../z_elf_message.c", 281); // "Unplanned conditions"
ASSERT(0, "0", "../z_elf_message.c", 282);
}
hintCmd++;
}
}
u16 QuestHint_GetSariaTextId(PlayState* play) {
Player* player = GET_PLAYER(play);
QuestHintCmd* sariaQuestHints;
if (!LINK_IS_ADULT) {
if (Actor_FindNearby(play, &player->actor, ACTOR_EN_SA, 4, 800.0f) == NULL) {
sariaQuestHints = sChildSariaQuestHints;
} else {
return 0x0160; // Special text about Saria preferring to talk to you face-to-face
}
} else {
sariaQuestHints = sAdultSariaQuestHints;
}
return QuestHint_GetTextIdFromScript(sariaQuestHints);
}
u16 QuestHint_GetNaviTextId(PlayState* play) {
if (play->naviQuestHints == NULL) {
return 0;
} else {
return QuestHint_GetTextIdFromScript(play->naviQuestHints);
}
}