From 2e667bbe509fc039f0e46b85e432376f680e399f Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Wed, 13 May 2026 18:14:24 -0700 Subject: [PATCH] support multiple aliases for quest opcodes --- src/QuestScript.cc | 1238 ++++++++++++++++++++++---------------------- 1 file changed, 616 insertions(+), 622 deletions(-) diff --git a/src/QuestScript.cc b/src/QuestScript.cc index 9d434a0c..818a6f4c 100644 --- a/src/QuestScript.cc +++ b/src/QuestScript.cc @@ -177,18 +177,14 @@ struct QuestScriptOpcodeDefinition { }; uint16_t opcode; - const char* name; - const char* qedit_name; + // By convention, the first of these names is the canonical name (used when disassembling) and the last is the Qedit + // name. When assembling, all names in this list are considered valid. + std::vector names; std::vector args; uint32_t flags; - QuestScriptOpcodeDefinition( - uint16_t opcode, const char* name, const char* qedit_name, std::vector args, uint32_t flags) - : opcode(opcode), name(name), qedit_name(qedit_name), args(args), flags(flags) {} - std::string str() const { - string name_str = this->qedit_name ? std::format("{} (qedit: {})", this->name, this->qedit_name) : this->name; - return std::format("{:04X}: {} flags={:04X}", this->opcode, name_str, this->flags); + return std::format("{:04X}: {} flags={:04X}", this->opcode, phosg::join(this->names, "/"), this->flags); } }; @@ -311,265 +307,265 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // referred to as an array, as regsA[0], regsA[1], etc. // Does nothing - {0x00, "nop", nullptr, {}, F_V0_V4}, + {0x00, {"nop"}, {}, F_V0_V4}, // Pops new PC off stack - {0x01, "ret", nullptr, {}, F_V0_V4 | F_TERMINATOR}, + {0x01, {"ret"}, {}, F_V0_V4 | F_TERMINATOR}, // Stops execution for the current frame. Execution resumes immediately after this opcode on the next frame. - {0x02, "sync", nullptr, {}, F_V0_V4}, + {0x02, {"sync"}, {}, F_V0_V4}, // Exits entirely - {0x03, "exit", nullptr, {I32}, F_V0_V4 | F_TERMINATOR}, + {0x03, {"exit"}, {I32}, F_V0_V4 | F_TERMINATOR}, // Starts a new thread at labelA - {0x04, "thread", nullptr, {SCRIPT16}, F_V0_V4}, + {0x04, {"thread"}, {SCRIPT16}, F_V0_V4}, // Pushes r1-r7 to the stack - {0x05, "va_start", nullptr, {}, F_V3_V4 | F_CLEAR_ARGS}, + {0x05, {"va_start"}, {}, F_V3_V4 | F_CLEAR_ARGS}, // Pops r7-r1 from the stack - {0x06, "va_end", nullptr, {}, F_V3_V4}, + {0x06, {"va_end"}, {}, F_V3_V4}, // Replaces r1-r7 with the args list, then calls labelA. This opcode doesn't directly clear the args list, but we // assume during disassembly that the code being called does so. - {0x07, "va_call", nullptr, {SCRIPT16}, F_V3_V4 | F_CLEAR_ARGS}, + {0x07, {"va_call"}, {SCRIPT16}, F_V3_V4 | F_CLEAR_ARGS}, // Copies a value from regB to regA - {0x08, "let", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x08, {"let"}, {W_REG, R_REG}, F_V0_V4}, // Sets regA to valueB - {0x09, "leti", nullptr, {W_REG, I32}, F_V0_V4}, + {0x09, {"leti"}, {W_REG, I32}, F_V0_V4}, // Sets regA to the memory address of regB. Note that this opcode was moved to 0C in v3 and later. - {0x0A, "leta", nullptr, {W_REG, R_REG}, F_V0_V2}, + {0x0A, {"leta"}, {W_REG, R_REG}, F_V0_V2}, // Sets regA to valueB - {0x0A, "letb", nullptr, {W_REG, I8}, F_V3_V4}, + {0x0A, {"letb"}, {W_REG, I8}, F_V3_V4}, // Sets regA to valueB - {0x0B, "letw", nullptr, {W_REG, I16}, F_V3_V4}, + {0x0B, {"letw"}, {W_REG, I16}, F_V3_V4}, // Sets regA to the memory address of regB - {0x0C, "leta", nullptr, {W_REG, R_REG}, F_V3_V4}, + {0x0C, {"leta"}, {W_REG, R_REG}, F_V3_V4}, // Sets regA to the address of the offset of labelB in the function table (to get the offset, use read4 after this) - {0x0D, "leto", nullptr, {W_REG, LABEL16}, F_V3_V4}, + {0x0D, {"leto"}, {W_REG, LABEL16}, F_V3_V4}, // Sets regA to 1 - {0x10, "set", nullptr, {W_REG}, F_V0_V4}, + {0x10, {"set"}, {W_REG}, F_V0_V4}, // Sets regA to 0 - {0x11, "clear", nullptr, {W_REG}, F_V0_V4}, + {0x11, {"clear"}, {W_REG}, F_V0_V4}, // Sets a regA to 0 if it's nonzero and vice versa - {0x12, "rev", nullptr, {W_REG}, F_V0_V4}, + {0x12, {"rev"}, {W_REG}, F_V0_V4}, // Sets flagA to 1. Sends 6x75. - {0x13, "gset", nullptr, {I16}, F_V0_V4}, + {0x13, {"gset"}, {I16}, F_V0_V4}, // Clears flagA to 0. Sends 6x75 on BB, but does not send anything on other versions. - {0x14, "gclear", nullptr, {I16}, F_V0_V4}, + {0x14, {"gclear"}, {I16}, F_V0_V4}, // Inverts flagA. Like the above two opcodes, sends 6x75 if the flag is set by this opcode. Only BB sends 6x75 if // the flag is cleared by this opcode. - {0x15, "grev", nullptr, {I16}, F_V0_V4}, + {0x15, {"grev"}, {I16}, F_V0_V4}, // If regB is nonzero, sets flagA; otherwise, clears it - {0x16, "glet", nullptr, {I16, R_REG}, F_V0_V4}, + {0x16, {"glet"}, {I16, R_REG}, F_V0_V4}, // Sets regB to the value of flagA - {0x17, "gget", nullptr, {I16, R_REG}, F_V0_V4}, + {0x17, {"gget"}, {I16, R_REG}, F_V0_V4}, // regA += regB - {0x18, "add", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x18, {"add"}, {W_REG, R_REG}, F_V0_V4}, // regA += valueB - {0x19, "addi", nullptr, {W_REG, I32}, F_V0_V4}, + {0x19, {"addi"}, {W_REG, I32}, F_V0_V4}, // regA -= regB - {0x1A, "sub", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x1A, {"sub"}, {W_REG, R_REG}, F_V0_V4}, // regA -= valueB - {0x1B, "subi", nullptr, {W_REG, I32}, F_V0_V4}, + {0x1B, {"subi"}, {W_REG, I32}, F_V0_V4}, // regA *= regB - {0x1C, "mul", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x1C, {"mul"}, {W_REG, R_REG}, F_V0_V4}, // regA *= valueB - {0x1D, "muli", nullptr, {W_REG, I32}, F_V0_V4}, + {0x1D, {"muli"}, {W_REG, I32}, F_V0_V4}, // regA /= regB - {0x1E, "div", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x1E, {"div"}, {W_REG, R_REG}, F_V0_V4}, // regA /= valueB - {0x1F, "divi", nullptr, {W_REG, I32}, F_V0_V4}, + {0x1F, {"divi"}, {W_REG, I32}, F_V0_V4}, // regA &= regB - {0x20, "and", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x20, {"and"}, {W_REG, R_REG}, F_V0_V4}, // regA &= valueB - {0x21, "andi", nullptr, {W_REG, I32}, F_V0_V4}, + {0x21, {"andi"}, {W_REG, I32}, F_V0_V4}, // regA |= regB - {0x22, "or", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x22, {"or"}, {W_REG, R_REG}, F_V0_V4}, // regA |= valueB - {0x23, "ori", nullptr, {W_REG, I32}, F_V0_V4}, + {0x23, {"ori"}, {W_REG, I32}, F_V0_V4}, // regA ^= regB - {0x24, "xor", nullptr, {W_REG, R_REG}, F_V0_V4}, + {0x24, {"xor"}, {W_REG, R_REG}, F_V0_V4}, // regA ^= valueB - {0x25, "xori", nullptr, {W_REG, I32}, F_V0_V4}, + {0x25, {"xori"}, {W_REG, I32}, F_V0_V4}, // regA %= regB // Note: This does signed division, so if the value is negative, you might get unexpected results. - {0x26, "mod", nullptr, {W_REG, R_REG}, F_V3_V4}, + {0x26, {"mod"}, {W_REG, R_REG}, F_V3_V4}, // regA %= valueB // Note: Unlike mod, this does unsigned division. - {0x27, "modi", nullptr, {W_REG, I32}, F_V3_V4}, + {0x27, {"modi"}, {W_REG, I32}, F_V3_V4}, // Jumps to labelA - {0x28, "jmp", nullptr, {SCRIPT16}, F_V0_V4 | F_TERMINATOR}, + {0x28, {"jmp"}, {SCRIPT16}, F_V0_V4 | F_TERMINATOR}, // Pushes the script offset immediately after this opcode and jumps to labelA // Note: This opcode doesn't directly clear the args list, but we assume during disassembly that the code being // called does so. - {0x29, "call", nullptr, {SCRIPT16}, F_V0_V4 | F_CLEAR_ARGS}, + {0x29, {"call"}, {SCRIPT16}, F_V0_V4 | F_CLEAR_ARGS}, // If all values in regsB are nonzero, jumps to labelA - {0x2A, "jmp_on", nullptr, {SCRIPT16, R_REG_SET}, F_V0_V4}, + {0x2A, {"jmp_on"}, {SCRIPT16, R_REG_SET}, F_V0_V4}, // If all values in regsB are zero, jumps to labelA - {0x2B, "jmp_off", nullptr, {SCRIPT16, R_REG_SET}, F_V0_V4}, + {0x2B, {"jmp_off"}, {SCRIPT16, R_REG_SET}, F_V0_V4}, // If regA == regB, jumps to labelC - {0x2C, "jmp_eq", "jmp_=", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x2C, {"jmp_eq", "jmp_="}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA == valueB, jumps to labelC - {0x2D, "jmpi_eq", "jmpi_=", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x2D, {"jmpi_eq", "jmpi_="}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA != regB, jumps to labelC - {0x2E, "jmp_ne", "jmp_!=", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x2E, {"jmp_ne", "jmp_!="}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA != valueB, jumps to labelC - {0x2F, "jmpi_ne", "jmpi_!=", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x2F, {"jmpi_ne", "jmpi_!="}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA > regB (unsigned), jumps to labelC - {0x30, "ujmp_gt", "ujmp_>", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x30, {"ujmp_gt", "ujmp_>"}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA > valueB (unsigned), jumps to labelC - {0x31, "ujmpi_gt", "ujmpi_>", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x31, {"ujmpi_gt", "ujmpi_>"}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA > regB (signed), jumps to labelC - {0x32, "jmp_gt", "jmp_>", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x32, {"jmp_gt", "jmp_>"}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA > valueB (signed), jumps to labelC - {0x33, "jmpi_gt", "jmpi_>", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x33, {"jmpi_gt", "jmpi_>"}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA < regB (unsigned), jumps to labelC - {0x34, "ujmp_lt", "ujmp_<", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x34, {"ujmp_lt", "ujmp_<"}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA < valueB (unsigned), jumps to labelC - {0x35, "ujmpi_lt", "ujmpi_<", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x35, {"ujmpi_lt", "ujmpi_<"}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA < regB (signed), jumps to labelC - {0x36, "jmp_lt", "jmp_<", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x36, {"jmp_lt", "jmp_<"}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA < valueB (signed), jumps to labelC - {0x37, "jmpi_lt", "jmpi_<", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x37, {"jmpi_lt", "jmpi_<"}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA >= regB (unsigned), jumps to labelC - {0x38, "ujmp_ge", "ujmp_>=", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x38, {"ujmp_ge", "ujmp_>="}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA >= valueB (unsigned), jumps to labelC - {0x39, "ujmpi_ge", "ujmpi_>=", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x39, {"ujmpi_ge", "ujmpi_>="}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA >= regB (signed), jumps to labelC - {0x3A, "jmp_ge", "jmp_>=", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x3A, {"jmp_ge", "jmp_>="}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA >= valueB (signed), jumps to labelC - {0x3B, "jmpi_ge", "jmpi_>=", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x3B, {"jmpi_ge", "jmpi_>="}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA <= regB (unsigned), jumps to labelC - {0x3C, "ujmp_le", "ujmp_<=", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x3C, {"ujmp_le", "ujmp_<="}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA <= valueB (unsigned), jumps to labelC - {0x3D, "ujmpi_le", "ujmpi_<=", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x3D, {"ujmpi_le", "ujmpi_<="}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // If regA <= regB (signed), jumps to labelC - {0x3E, "jmp_le", "jmp_<=", {R_REG, R_REG, SCRIPT16}, F_V0_V4}, + {0x3E, {"jmp_le", "jmp_<="}, {R_REG, R_REG, SCRIPT16}, F_V0_V4}, // If regA <= valueB (signed), jumps to labelC - {0x3F, "jmpi_le", "jmpi_<=", {R_REG, I32, SCRIPT16}, F_V0_V4}, + {0x3F, {"jmpi_le", "jmpi_<="}, {R_REG, I32, SCRIPT16}, F_V0_V4}, // Jumps to labelsB[regA]; if regA is out of range of labelsB, does nothing - {0x40, "switch_jmp", nullptr, {R_REG, SCRIPT16_SET}, F_V0_V4}, + {0x40, {"switch_jmp"}, {R_REG, SCRIPT16_SET}, F_V0_V4}, // Calls labelsB[regA]; if regA is out of range of labelsB, does nothing // Note: This opcode doesn't directly clear the args list, but we assume during disassembly that the code being // called does so. - {0x41, "switch_call", nullptr, {R_REG, SCRIPT16_SET}, F_V0_V4 | F_CLEAR_ARGS}, + {0x41, {"switch_call"}, {R_REG, SCRIPT16_SET}, F_V0_V4 | F_CLEAR_ARGS}, // Does nothing - {0x42, "nop_42", nullptr, {I32}, F_V0_V2}, + {0x42, {"nop_42"}, {I32}, F_V0_V2}, // Pushes the value in regA to the stack - {0x42, "stack_push", nullptr, {R_REG}, F_V3_V4}, + {0x42, {"stack_push"}, {R_REG}, F_V3_V4}, // Pops a value from the stack and puts it into regA - {0x43, "stack_pop", nullptr, {W_REG}, F_V3_V4}, + {0x43, {"stack_pop"}, {W_REG}, F_V3_V4}, // Pushes (valueB) regs in increasing order starting at regA - {0x44, "stack_pushm", nullptr, {R_REG, I32}, F_V3_V4}, + {0x44, {"stack_pushm"}, {R_REG, I32}, F_V3_V4}, // Pops (valueB) regs in decreasing order ending at regA - {0x45, "stack_popm", nullptr, {W_REG, I32}, F_V3_V4}, + {0x45, {"stack_popm"}, {W_REG, I32}, F_V3_V4}, // Appends regA to the args list - {0x48, "arg_pushr", nullptr, {R_REG}, F_V3_V4 | F_PUSH_ARG}, + {0x48, {"arg_pushr"}, {R_REG}, F_V3_V4 | F_PUSH_ARG}, // Appends valueA to the args list - {0x49, "arg_pushl", nullptr, {I32}, F_V3_V4 | F_PUSH_ARG}, - {0x4A, "arg_pushb", nullptr, {I8}, F_V3_V4 | F_PUSH_ARG}, - {0x4B, "arg_pushw", nullptr, {I16}, F_V3_V4 | F_PUSH_ARG}, + {0x49, {"arg_pushl"}, {I32}, F_V3_V4 | F_PUSH_ARG}, + {0x4A, {"arg_pushb"}, {I8}, F_V3_V4 | F_PUSH_ARG}, + {0x4B, {"arg_pushw"}, {I16}, F_V3_V4 | F_PUSH_ARG}, // Appends the memory address of regA to the args list - {0x4C, "arg_pusha", nullptr, {R_REG}, F_V3_V4 | F_PUSH_ARG}, + {0x4C, {"arg_pusha"}, {R_REG}, F_V3_V4 | F_PUSH_ARG}, // Appends the script offset of labelA to the args list - {0x4D, "arg_pusho", nullptr, {LABEL16}, F_V3_V4 | F_PUSH_ARG}, + {0x4D, {"arg_pusho"}, {LABEL16}, F_V3_V4 | F_PUSH_ARG}, // Appends strA to the args list - {0x4E, "arg_pushs", nullptr, {CSTRING}, F_V3_V4 | F_PUSH_ARG}, + {0x4E, {"arg_pushs"}, {CSTRING}, F_V3_V4 | F_PUSH_ARG}, // Creates dialogue with an object/NPC (valueA) starting with message strB - {0x50, "message", nullptr, {I32, CSTRING}, F_V0_V4 | F_ARGS}, + {0x50, {"message"}, {I32, CSTRING}, F_V0_V4 | F_ARGS}, // Prompts the player with a list of choices (strB; items separated by newlines) and returns the index of their // choice in regA - {0x51, "list", nullptr, {W_REG, CSTRING}, F_V0_V4 | F_ARGS}, + {0x51, {"list"}, {W_REG, CSTRING}, F_V0_V4 | F_ARGS}, // Fades from black - {0x52, "fadein", nullptr, {}, F_V0_V4}, + {0x52, {"fadein"}, {}, F_V0_V4}, // Fades to black - {0x53, "fadeout", nullptr, {}, F_V0_V4}, + {0x53, {"fadeout"}, {}, F_V0_V4}, // Plays a sound effect - {0x54, "sound_effect", "se", {I32}, F_V0_V4 | F_ARGS}, + {0x54, {"sound_effect", "se"}, {I32}, F_V0_V4 | F_ARGS}, // Plays a fanfare (clear.adx if valueA is 0, or miniclear.adx if it's 1). There is no bounds check on this; values // other than 0 or 1 will result in undefined behavior. - {0x55, "bgm", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x55, {"bgm"}, {I32}, F_V0_V4 | F_ARGS}, // Does nothing - {0x56, "nop_56", nullptr, {}, F_V0_V2}, - {0x57, "nop_57", nullptr, {}, F_V0_V2}, - {0x58, "nop_58", "enable", {I32}, F_V0_V2}, - {0x59, "nop_59", "disable", {I32}, F_V0_V2}, + {0x56, {"nop_56"}, {}, F_V0_V2}, + {0x57, {"nop_57"}, {}, F_V0_V2}, + {0x58, {"nop_58", "enable"}, {I32}, F_V0_V2}, + {0x59, {"nop_59", "disable"}, {I32}, F_V0_V2}, // Displays a message. Special tokens are interpolated within the string; these special tokens are: // => value of rXX as %d (signed integer) @@ -589,38 +585,38 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // => name of the game (set by 8A command) (v2 and later) // => last chat message (v2 and later) // => the description of the last item sent by the server in a 6xE3 command (BB only) - {0x5A, "window_msg", nullptr, {CSTRING}, F_V0_V4 | F_ARGS}, + {0x5A, {"window_msg"}, {CSTRING}, F_V0_V4 | F_ARGS}, // Adds a message to an existing message (or window_msg). Tokens are interpolated as for window_msg. - {0x5B, "add_msg", nullptr, {CSTRING}, F_V0_V4 | F_ARGS}, + {0x5B, {"add_msg"}, {CSTRING}, F_V0_V4 | F_ARGS}, // Closes the current message box - {0x5C, "message_end", "mesend", {}, F_V0_V4}, + {0x5C, {"message_end", "mesend"}, {}, F_V0_V4}, // Gets the current time, in seconds since 00:00:00 on 1 January 2000 - {0x5D, "gettime", nullptr, {W_REG}, F_V0_V4}, + {0x5D, {"gettime"}, {W_REG}, F_V0_V4}, // Closes a window_msg - {0x5E, "window_msg_end", "winend", {}, F_V0_V4}, + {0x5E, {"window_msg_end", "winend"}, {}, F_V0_V4}, // Creates an NPC as client ID 1. Sends 6x69 with command = 0. // valueA = initial state (see npc_crp below) // valueB = template index (see 6x69 in CommandFormats.hh) - {0x60, "npc_crt", "npc_crt_V1", {I32, I32}, F_V0_V2 | F_ARGS}, - {0x60, "npc_crt", "npc_crt_V3", {I32, I32}, F_V3_V4 | F_ARGS}, + {0x60, {"npc_crt", "npc_crt_V1"}, {I32, I32}, F_V0_V2 | F_ARGS}, + {0x60, {"npc_crt", "npc_crt_V3"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Tells an NPC (by client ID) to stop following - {0x61, "npc_stop", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x61, {"npc_stop"}, {I32}, F_V0_V4 | F_ARGS}, // Tells an NPC (by client ID) to follow the player - {0x62, "npc_play", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x62, {"npc_play"}, {I32}, F_V0_V4 | F_ARGS}, // Destroys an NPC (by client ID) - {0x63, "npc_kill", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x63, {"npc_kill"}, {I32}, F_V0_V4 | F_ARGS}, // Disables or enables the ability to talk to NPCs - {0x64, "npc_talk_off", "npc_nont", {}, F_V0_V4}, - {0x65, "npc_talk_on", "npc_talk", {}, F_V0_V4}, + {0x64, {"npc_talk_off", "npc_nont"}, {}, F_V0_V4}, + {0x65, {"npc_talk_on", "npc_talk"}, {}, F_V0_V4}, // Creates an NPC as client ID 1. Sends 6x69 with command = 0. // regsA[0-2] = position (x, y, z as integers) @@ -628,62 +624,62 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[4] = initial state (0 = alive, 1 = dead, 2 = invisible text box, according to qedit.info) // regsA[5] = template index (see 6x69 in CommandFormats.hh) // valueB is required in pre-v3 but is ignored - {0x66, "npc_crp", "npc_crp_V1", {{R_REG_SET_FIXED, 6}, I32}, F_V0_V2}, - {0x66, "npc_crp", "npc_crp_V3", {{R_REG_SET_FIXED, 6}}, F_V3_V4}, + {0x66, {"npc_crp", "npc_crp_V1"}, {{R_REG_SET_FIXED, 6}, I32}, F_V0_V2}, + {0x66, {"npc_crp", "npc_crp_V3"}, {{R_REG_SET_FIXED, 6}}, F_V3_V4}, // Creates a pipe. valueA is client ID - {0x68, "create_pipe", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x68, {"create_pipe"}, {I32}, F_V0_V4 | F_ARGS}, // Checks player HP, but not in a straightforward manner: sets regA to 1 if (current_hp / max_hp) < (1 / valueA) // for the client specified by valueB. Sets regA to 0 otherwise. - {0x69, "p_hpstat", "p_hpstat_V1", {W_REG, CLIENT_ID}, F_V0_V2 | F_ARGS}, - {0x69, "p_hpstat", "p_hpstat_V3", {W_REG, CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0x69, {"p_hpstat", "p_hpstat_V1"}, {W_REG, CLIENT_ID}, F_V0_V2 | F_ARGS}, + {0x69, {"p_hpstat", "p_hpstat_V3"}, {W_REG, CLIENT_ID}, F_V3_V4 | F_ARGS}, // Sets regA to 1 if player in slot (valueB) is dead, or 0 if alive. - {0x6A, "p_dead", "p_dead_V1", {W_REG, CLIENT_ID}, F_V0_V2 | F_ARGS}, - {0x6A, "p_dead", "p_dead_V3", {W_REG, CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0x6A, {"p_dead", "p_dead_V1"}, {W_REG, CLIENT_ID}, F_V0_V2 | F_ARGS}, + {0x6A, {"p_dead", "p_dead_V3"}, {W_REG, CLIENT_ID}, F_V3_V4 | F_ARGS}, // Disables/enables telepipes/Ryuker - {0x6B, "p_disablewarp", nullptr, {}, F_V0_V4}, - {0x6C, "p_enablewarp", nullptr, {}, F_V0_V4}, + {0x6B, {"p_disablewarp"}, {}, F_V0_V4}, + {0x6C, {"p_enablewarp"}, {}, F_V0_V4}, // Moves a player. // regsA[0-2] = destination (x, y, z as integers) // regsA[3] = angle // regsA[4] = client ID // valueB is required in pre-v3 but is ignored - {0x6D, "p_move", "p_move_v1", {{R_REG_SET_FIXED, 5}, I32}, F_V0_V2}, - {0x6D, "p_move", "p_move_V3", {{R_REG_SET_FIXED, 5}}, F_V3_V4}, + {0x6D, {"p_move", "p_move_v1"}, {{R_REG_SET_FIXED, 5}, I32}, F_V0_V2}, + {0x6D, {"p_move", "p_move_V3"}, {{R_REG_SET_FIXED, 5}}, F_V3_V4}, // Causes the player with client ID valueA to look at an unspecified other player. The specified player looks at // the player with the lowest client ID (except for the specified player). // TODO: TObjPlayer::state is involved in determining which player to look at; figure out exactly what this does - {0x6E, "p_look", nullptr, {CLIENT_ID}, F_V0_V4 | F_ARGS}, + {0x6E, {"p_look"}, {CLIENT_ID}, F_V0_V4 | F_ARGS}, // Disables/enables attacks for all players - {0x70, "p_action_disable", nullptr, {}, F_V0_V4}, - {0x71, "p_action_enable", nullptr, {}, F_V0_V4}, + {0x70, {"p_action_disable"}, {}, F_V0_V4}, + {0x71, {"p_action_enable"}, {}, F_V0_V4}, // Disables/enables movement for the given player - {0x72, "disable_movement1", nullptr, {CLIENT_ID}, F_V0_V4 | F_ARGS}, - {0x73, "enable_movement1", nullptr, {CLIENT_ID}, F_V0_V4 | F_ARGS}, + {0x72, {"disable_movement1"}, {CLIENT_ID}, F_V0_V4 | F_ARGS}, + {0x73, {"enable_movement1"}, {CLIENT_ID}, F_V0_V4 | F_ARGS}, // These appear to do nothing at all. On v3, they set and clear a flag that is never read. On DC NTE and v1, code // exists to read this flag, but it belongs to an object that appears to never be constructed anywhere. - {0x74, "clear_unused_flag_74", "p_noncol", {}, F_V0_V4}, - {0x75, "set_unused_flag_75", "p_col", {}, F_V0_V4}, + {0x74, {"clear_unused_flag_74", "p_noncol"}, {}, F_V0_V4}, + {0x75, {"set_unused_flag_75", "p_col"}, {}, F_V0_V4}, // Sets a player's starting position. // valueA = client ID // regsB[0-2] = position (x, y, z as integers) // regsB[3] = angle - {0x76, "set_player_start_position", "p_setpos", {CLIENT_ID, {R_REG_SET_FIXED, 4}}, F_V0_V4 | F_ARGS}, + {0x76, {"set_player_start_position", "p_setpos"}, {CLIENT_ID, {R_REG_SET_FIXED, 4}}, F_V0_V4 | F_ARGS}, // Returns players to the Hunter's Guild counter. - {0x77, "p_return_guild", nullptr, {}, F_V0_V4}, + {0x77, {"p_return_guild"}, {}, F_V0_V4}, // Opens the Hunter's Guild counter menu. valueA should be the player's client ID. - {0x78, "p_talk_guild", nullptr, {CLIENT_ID}, F_V0_V4 | F_ARGS}, + {0x78, {"p_talk_guild"}, {CLIENT_ID}, F_V0_V4 | F_ARGS}, // Creates an NPC which only appears near a given location // regsA[0-2] = position (x, y, z as integers) @@ -692,20 +688,20 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[5] = template index (see 6x69 in CommandFormats.hh) // regsA[6] = initial state (0 = alive, 1 = dead, 2 = invisible text box, according to qedit.info) // regsA[7] = client ID - {0x79, "npc_talk_pl", "npc_talk_pl_V1", {{R_REG32_SET_FIXED, 8}}, F_V0_V2}, - {0x79, "npc_talk_pl", "npc_talk_pl_V3", {{R_REG_SET_FIXED, 8}}, F_V3_V4}, + {0x79, {"npc_talk_pl", "npc_talk_pl_V1"}, {{R_REG32_SET_FIXED, 8}}, F_V0_V2}, + {0x79, {"npc_talk_pl", "npc_talk_pl_V3"}, {{R_REG_SET_FIXED, 8}}, F_V3_V4}, // Destroys an NPC created with npc_talk_pl. This opcode cannot be executed multiple times on the same frame; if it // is, only the last one will take effect. - {0x7A, "npc_talk_kill", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x7A, {"npc_talk_kill"}, {I32}, F_V0_V4 | F_ARGS}, // Creates attacker NPC - {0x7B, "npc_crtpk", "npc_crtpk_V1", {I32, I32}, F_V0_V2 | F_ARGS}, - {0x7B, "npc_crtpk", "npc_crtpk_V3", {I32, I32}, F_V3_V4 | F_ARGS}, + {0x7B, {"npc_crtpk", "npc_crtpk_V1"}, {I32, I32}, F_V0_V2 | F_ARGS}, + {0x7B, {"npc_crtpk", "npc_crtpk_V3"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Creates attacker NPC - {0x7C, "npc_crppk", "npc_crppk_V1", {{R_REG32_SET_FIXED, 7}, I32}, F_V0_V2}, - {0x7C, "npc_crppk", "npc_crppk_V3", {{R_REG_SET_FIXED, 7}}, F_V3_V4}, + {0x7C, {"npc_crppk", "npc_crppk_V1"}, {{R_REG32_SET_FIXED, 7}, I32}, F_V0_V2}, + {0x7C, {"npc_crppk", "npc_crppk_V3"}, {{R_REG_SET_FIXED, 7}}, F_V3_V4}, // Creates an NPC with client ID 1. It is not recommended to use this opcode if a player can be in that slot; use // npc_crptalk_id instead. @@ -713,11 +709,11 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[3] = angle // regsA[4] = initial state (0 = alive, 1 = dead, 2 = invisible text box, according to qedit.info) // regsA[5] = template index (see 6x69 in CommandFormats.hh) - {0x7D, "npc_crptalk", "npc_crptalk_v1", {{R_REG32_SET_FIXED, 6}, I32}, F_V0_V2}, - {0x7D, "npc_crptalk", "npc_crptalk_V3", {{R_REG_SET_FIXED, 6}}, F_V3_V4}, + {0x7D, {"npc_crptalk", "npc_crptalk_v1"}, {{R_REG32_SET_FIXED, 6}, I32}, F_V0_V2}, + {0x7D, {"npc_crptalk", "npc_crptalk_V3"}, {{R_REG_SET_FIXED, 6}}, F_V3_V4}, // Causes client ID valueA to look at client ID valueB. Sends 6x3E. - {0x7E, "p_look_at", nullptr, {CLIENT_ID, CLIENT_ID}, F_V0_V4 | F_ARGS}, + {0x7E, {"p_look_at"}, {CLIENT_ID, CLIENT_ID}, F_V0_V4 | F_ARGS}, // Creates an NPC. // regsA[0-2] = position (x, y, z as integers) @@ -725,55 +721,55 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[4] = initial state (0 = alive, 1 = dead, 2 = invisible text box, according to qedit.info) // regsA[5] = client ID // regsA[6] = template index (see 6x69 in CommandFormats.hh) - {0x7F, "npc_crp_id", "npc_crp_id_V1", {{R_REG32_SET_FIXED, 7}, I32}, F_V0_V2}, - {0x7F, "npc_crp_id", "npc_crp_id_v3", {{R_REG_SET_FIXED, 7}}, F_V3_V4}, + {0x7F, {"npc_crp_id", "npc_crp_id_V1"}, {{R_REG32_SET_FIXED, 7}, I32}, F_V0_V2}, + {0x7F, {"npc_crp_id", "npc_crp_id_v3"}, {{R_REG_SET_FIXED, 7}}, F_V3_V4}, // Causes the camera to shake. - {0x80, "cam_quake", nullptr, {}, F_V0_V4}, + {0x80, {"cam_quake"}, {}, F_V0_V4}, // Moves the camera to where your character is looking. - {0x81, "cam_adj", nullptr, {}, F_V0_V4}, + {0x81, {"cam_adj"}, {}, F_V0_V4}, // Zooms the camera in or out. - {0x82, "cam_zmin", nullptr, {}, F_V0_V4}, - {0x83, "cam_zmout", nullptr, {}, F_V0_V4}, + {0x82, {"cam_zmin"}, {}, F_V0_V4}, + {0x83, {"cam_zmout"}, {}, F_V0_V4}, // Pans the camera. // regsA[0-2] = destination (x, y, z as integers) // regsA[3] = pan time (in frames; 30 frames/sec) // regsA[4] = end time (in frames; 30 frames/sec) - {0x84, "cam_pan", "cam_pan_V1", {{R_REG32_SET_FIXED, 5}, I32}, F_V0_V2}, - {0x84, "cam_pan", "cam_pan_V3", {{R_REG_SET_FIXED, 5}}, F_V3_V4}, + {0x84, {"cam_pan", "cam_pan_V1"}, {{R_REG32_SET_FIXED, 5}, I32}, F_V0_V2}, + {0x84, {"cam_pan", "cam_pan_V3"}, {{R_REG_SET_FIXED, 5}}, F_V3_V4}, // Temporarily sets the game's difficulty to Very Hard (even on v2). On v3 and later, does nothing. - {0x85, "game_lev_super", nullptr, {}, F_V0_V2}, - {0x85, "nop_85", nullptr, {}, F_V3_V4}, + {0x85, {"game_lev_super"}, {}, F_V0_V2}, + {0x85, {"nop_85"}, {}, F_V3_V4}, // Restores the previous difficulty level after game_lev_super. On v3 and later, does nothing. - {0x86, "game_lev_reset", nullptr, {}, F_V0_V2}, - {0x86, "nop_86", nullptr, {}, F_V3_V4}, + {0x86, {"game_lev_reset"}, {}, F_V0_V2}, + {0x86, {"nop_86"}, {}, F_V3_V4}, // Creates a telepipe. The telepipe disappears upon being used. // regsA[0-2] = location (x, y, z as integers) // regsA[3] = owner client ID (player or NPC must exist in the game) - {0x87, "pos_pipe", "pos_pipe_V1", {{R_REG32_SET_FIXED, 4}, I32}, F_V0_V2}, - {0x87, "pos_pipe", "pos_pipe_V3", {{R_REG_SET_FIXED, 4}}, F_V3_V4}, + {0x87, {"pos_pipe", "pos_pipe_V1"}, {{R_REG32_SET_FIXED, 4}, I32}, F_V0_V2}, + {0x87, {"pos_pipe", "pos_pipe_V3"}, {{R_REG_SET_FIXED, 4}}, F_V3_V4}, // Checks if all set events (enemies) have been destroyed in a given room. // regA = result (0 = not cleared, 1 = cleared) // regsB[0] = floor number // regsB[1] = room ID - {0x88, "if_zone_clear", nullptr, {W_REG, {R_REG_SET_FIXED, 2}}, F_V0_V4}, + {0x88, {"if_zone_clear"}, {W_REG, {R_REG_SET_FIXED, 2}}, F_V0_V4}, // Returns the number of enemies destroyed so far in this game (since the quest began). - {0x89, "chk_ene_num", nullptr, {W_REG}, F_V0_V4}, + {0x89, {"chk_ene_num"}, {W_REG}, F_V0_V4}, // Constructs all objects or enemies that match the conditions: // regsA[0] = floor // regsA[1] = room ID // regsA[2] = group - {0x8A, "construct_delayed_object", "unhide_obj", {{R_REG_SET_FIXED, 3}}, F_V0_V4}, - {0x8B, "construct_delayed_enemy", "unhide_ene", {{R_REG_SET_FIXED, 3}}, F_V0_V4}, + {0x8A, {"construct_delayed_object", "unhide_obj"}, {{R_REG_SET_FIXED, 3}}, F_V0_V4}, + {0x8B, {"construct_delayed_enemy", "unhide_ene"}, {{R_REG_SET_FIXED, 3}}, F_V0_V4}, // Starts a new thread when the player is close enough to the given point. The collision is created on the current // floor; the thread is created when the player enters the given radius. @@ -782,14 +778,14 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[4] = label index where thread should start // After executing this opcode, regsA[0] is replaced with the object's token, which can be used in del_obj_param, // move_coords_object, etc. - {0x8C, "at_coords_call", nullptr, {{R_REG_SET_FIXED, 5}}, F_V0_V4}, + {0x8C, {"at_coords_call"}, {{R_REG_SET_FIXED, 5}}, F_V0_V4}, // Like at_coords_call, but the thread is not started automatically. Instead, the player's primary action button // becomes "talk" within the radius, and the label is called when the player presses that button. - {0x8D, "at_coords_talk", nullptr, {{R_REG_SET_FIXED, 5}}, F_V0_V4}, + {0x8D, {"at_coords_talk"}, {{R_REG_SET_FIXED, 5}}, F_V0_V4}, // Like at_coords_call, but only triggers if an NPC enters the radius. - {0x8E, "npc_coords_call", "walk_to_coord_call", {{R_REG_SET_FIXED, 5}}, F_V0_V4}, + {0x8E, {"npc_coords_call", "walk_to_coord_call"}, {{R_REG_SET_FIXED, 5}}, F_V0_V4}, // Like at_coords_call, but triggers when a player within the event radius is also within the player radius of any // other player. @@ -797,23 +793,23 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[3] = event radius (centered at x, y, z defined above) // regsA[4] = player radius (centered at player) // regsA[5] = label index where thread should start - {0x8F, "party_coords_call", "col_npcinr", {{R_REG_SET_FIXED, 6}}, F_V0_V4}, + {0x8F, {"party_coords_call", "col_npcinr"}, {{R_REG_SET_FIXED, 6}}, F_V0_V4}, // Enables/disables a switch flag (valueA). Does NOT send 6x05, so other players will not know about this change! // Use sw_send instead to keep switch flag state synced. - {0x90, "switch_on", nullptr, {I32}, F_V0_V4 | F_ARGS}, - {0x91, "switch_off", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x90, {"switch_on"}, {I32}, F_V0_V4 | F_ARGS}, + {0x91, {"switch_off"}, {I32}, F_V0_V4 | F_ARGS}, // Plays a BGM. Values for valueA: // 1: epi1.adx 2: epi2.adx 3: ED_PIANO.adx 4: matter.adx 5: open.adx // 6: dreams.adx 7: mambo.adx 8: carnaval.adx 9: hearts.adx 10: smile.adx // 11: nomal.adx 12: chu_f.adx 13: ENDING_LOOP.adx 14: DreamS_KIDS.adx 15: ESCAPE.adx // 16: LIVE.adx 17: MILES.adx - {0x92, "playbgm_epi", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x92, {"playbgm_epi"}, {I32}, F_V0_V4 | F_ARGS}, // Enables access to a floor (valueA) via the Pioneer 2 Ragol warp. Floors are 0-17 for Episode 1, 18-35 for // Episode 2, 36-46 for Episode 4. - {0x93, "set_mainwarp", nullptr, {I32}, F_V0_V4 | F_ARGS}, + {0x93, {"set_mainwarp"}, {I32}, F_V0_V4 | F_ARGS}, // Creates an object that the player can talk to. A reticle appears when the player is nearby. // regsA[0-2] = location (x, y, z as integers) @@ -821,15 +817,15 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[4] = label number to call // regsA[5] = distance from ground to target // regB = returned object token (can be used with del_obj_param) - {0x94, "set_obj_param", nullptr, {{R_REG_SET_FIXED, 6}, W_REG}, F_V0_V4}, + {0x94, {"set_obj_param"}, {{R_REG_SET_FIXED, 6}, W_REG}, F_V0_V4}, // Causes the labelB to be called on a new thread when the player warps to floorA. If the given floor already has a // registered handler, it is replaced with the new one (each floor may have at most one handler). - {0x95, "set_floor_handler", nullptr, {FLOOR, SCRIPT32}, F_V0_V2}, - {0x95, "set_floor_handler", nullptr, {FLOOR, SCRIPT16}, F_V3_V4 | F_ARGS}, + {0x95, {"set_floor_handler"}, {FLOOR, SCRIPT32}, F_V0_V2}, + {0x95, {"set_floor_handler"}, {FLOOR, SCRIPT16}, F_V3_V4 | F_ARGS}, // Deletes the handler for floorA. - {0x96, "clr_floor_handler", nullptr, {FLOOR}, F_V0_V4 | F_ARGS}, + {0x96, {"clr_floor_handler"}, {FLOOR}, F_V0_V4 | F_ARGS}, // Creates a collision object that checks if the NPC with client ID 1 is too far away from the player, when the // player enters its check radius. @@ -838,72 +834,72 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[4] = label index for triggered function // regsA[5] = player radius (if NPC is closer, label is not called) // regsA[6-8] = warp location (x, y, z as integers) for use with npc_chkwarp within the triggered function - {0x97, "check_npc_straggle", "col_plinaw", {{R_REG_SET_FIXED, 9}}, F_V1_V4}, + {0x97, {"check_npc_straggle", "col_plinaw"}, {{R_REG_SET_FIXED, 9}}, F_V1_V4}, // Hides or shows the HUD. - {0x98, "hud_hide", nullptr, {}, F_V0_V4}, - {0x99, "hud_show", nullptr, {}, F_V0_V4}, + {0x98, {"hud_hide"}, {}, F_V0_V4}, + {0x99, {"hud_show"}, {}, F_V0_V4}, // Enables/disables the cinema effect (black bars above/below screen) - {0x9A, "cine_enable", nullptr, {}, F_V0_V4}, - {0x9B, "cine_disable", nullptr, {}, F_V0_V4}, + {0x9A, {"cine_enable"}, {}, F_V0_V4}, + {0x9B, {"cine_disable"}, {}, F_V0_V4}, // Unused opcode. It's not clear what this was supposed to do. The behavior appears to be the same on all versions // of PSO, from DC NTE through BB. argA is ignored. The game constructs a message list object from strB; the game // will softlock unless this string contains exactly 2 messages (separated by \n). After doing this, it destroys // the message list and does nothing else. - {0xA0, "nop_A0_debug", "broken_list", {I32, CSTRING}, F_V0_V4 | F_ARGS}, + {0xA0, {"nop_A0_debug", "broken_list"}, {I32, CSTRING}, F_V0_V4 | F_ARGS}, // Sets a function to be called (on a new thread) when the quest is failed. The quest is considered failed when you // talk to the Hunter's Guild counter and r253 has the value 1 (specifically 1; other nonzero values do not trigger // this function). - {0xA1, "set_qt_failure", nullptr, {SCRIPT32}, F_V0_V2}, - {0xA1, "set_qt_failure", nullptr, {SCRIPT16}, F_V3_V4}, + {0xA1, {"set_qt_failure"}, {SCRIPT32}, F_V0_V2}, + {0xA1, {"set_qt_failure"}, {SCRIPT16}, F_V3_V4}, // Like set_qt_failure, but uses r255 instead. If r255 and r253 both have the value 1 when the player talks to the // Hunter's Guild counter, the success label is called and the failure label is not called. - {0xA2, "set_qt_success", nullptr, {SCRIPT32}, F_V0_V2}, - {0xA2, "set_qt_success", nullptr, {SCRIPT16}, F_V3_V4}, + {0xA2, {"set_qt_success"}, {SCRIPT32}, F_V0_V2}, + {0xA2, {"set_qt_success"}, {SCRIPT16}, F_V3_V4}, // Clears the quest failure handler (opposite of set_qt_failure). - {0xA3, "clr_qt_failure", nullptr, {}, F_V0_V4}, + {0xA3, {"clr_qt_failure"}, {}, F_V0_V4}, // Clears the quest success handler (opposite of set_qt_success). - {0xA4, "clr_qt_success", nullptr, {}, F_V0_V4}, + {0xA4, {"clr_qt_success"}, {}, F_V0_V4}, // Sets a function to be called when the quest is cancelled via the Hunter's Guild counter. - {0xA5, "set_qt_cancel", nullptr, {SCRIPT32}, F_V0_V2}, - {0xA5, "set_qt_cancel", nullptr, {SCRIPT16}, F_V3_V4}, + {0xA5, {"set_qt_cancel"}, {SCRIPT32}, F_V0_V2}, + {0xA5, {"set_qt_cancel"}, {SCRIPT16}, F_V3_V4}, // Clears the quest cancel handler (opposite of set_qt_cancel). - {0xA6, "clr_qt_cancel", nullptr, {}, F_V0_V4}, + {0xA6, {"clr_qt_cancel"}, {}, F_V0_V4}, // Makes a player or NPC walk to a location. // regsA[0-2] = location (x, y, z as integers; y is ignored) // regsA[3] = client ID - {0xA8, "pl_walk", "pl_walk_V1", {{R_REG32_SET_FIXED, 4}, I32}, F_V0_V2}, - {0xA8, "pl_walk", "pl_walk_V3", {{R_REG_SET_FIXED, 4}}, F_V3_V4}, + {0xA8, {"pl_walk", "pl_walk_V1"}, {{R_REG32_SET_FIXED, 4}, I32}, F_V0_V2}, + {0xA8, {"pl_walk", "pl_walk_V3"}, {{R_REG_SET_FIXED, 4}}, F_V3_V4}, // Gives valueB Meseta to the player with client ID valueA. Negative values do not appear to be handled properly; // if this opcode attempts to take more meseta than the player has, the player ends up with 999999 Meseta. - {0xB0, "pl_add_meseta", nullptr, {CLIENT_ID, I32}, F_V0_V4 | F_ARGS}, + {0xB0, {"pl_add_meseta"}, {CLIENT_ID, I32}, F_V0_V4 | F_ARGS}, // Starts a new thread at labelA in the quest script. - {0xB1, "thread_stg", nullptr, {SCRIPT16}, F_V0_V4}, + {0xB1, {"thread_stg"}, {SCRIPT16}, F_V0_V4}, // Deletes an interactable object previously created by set_obj_param. valueA is the object's token, as returned by // regB from set_obj_param, or regsA[0] from e.g. at_coords_call. - {0xB2, "del_obj_param", nullptr, {R_REG}, F_V0_V4}, + {0xB2, {"del_obj_param"}, {R_REG}, F_V0_V4}, // Creates an item in the player's inventory. If the item is successfully created, this opcode sends 6x2B on all // versions except BB. On BB, this opcode sends 6xCA, and the server sends 6xBE to create the item; the requested // item must match one of the item creation masks in the quest script's header. // regsA[0-2] = item.data1[0-2] // regB = returned item ID, or FFFFFFFF if item can't be created - {0xB3, "item_create", nullptr, {{R_REG_SET_FIXED, 3}, W_REG}, F_V0_V4}, + {0xB3, {"item_create"}, {{R_REG_SET_FIXED, 3}, W_REG}, F_V0_V4}, // Like item_create, but regsA specify all of item.data1 instead of only the first 3 bytes. - {0xB4, "item_create2", nullptr, {{R_REG_SET_FIXED, 12}, W_REG}, F_V0_V4}, + {0xB4, {"item_create2"}, {{R_REG_SET_FIXED, 12}, W_REG}, F_V0_V4}, // Deletes an item from the player's inventory. Sends 6x29 if ths item is found and deleted. If the item is // stackable, only one of it is deleted; the rest of the stack is not deleted. @@ -911,12 +907,12 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsB[0-11] = item.data1[0-11] for deleted item // regsB must not wrap around (that is, the first register in regsB cannot be in the range [r245, r255]). If the // item is not found, regsB[0] is set to 0xFFFFFFFF and the rest of regsB are not affected. - {0xB5, "item_delete", nullptr, {R_REG, {W_REG_SET_FIXED, 12}}, F_V0_V4}, + {0xB5, {"item_delete"}, {R_REG, {W_REG_SET_FIXED, 12}}, F_V0_V4}, // Like item_delete, but searches by item.data1[0-2] instead of by item ID. // regsA[0-2] = item.data1[0-2] to search for // regsB[0-11] = item.data1[0-11] for deleted item - {0xB6, "item_delete_by_type", "item_delete2", {{R_REG_SET_FIXED, 3}, {W_REG_SET_FIXED, 12}}, F_V0_V4}, + {0xB6, {"item_delete_by_type", "item_delete2"}, {{R_REG_SET_FIXED, 3}, {W_REG_SET_FIXED, 12}}, F_V0_V4}, // Searches the player's inventory for an item and returns its item ID. // regsA[0-2] = item.data1[0-2] to search for, as above @@ -926,34 +922,34 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // Armor/shield/unit: data1[0-2] must match // Mag: data1[0-1] must match; regsA[2] is ignored // Tool: data1[0-2] must match; if it's a tech disk, data1[4] must be 0 - {0xB7, "find_inventory_item", "item_check", {{R_REG_SET_FIXED, 3}, W_REG}, F_V0_V4}, + {0xB7, {"find_inventory_item", "item_check"}, {{R_REG_SET_FIXED, 3}, W_REG}, F_V0_V4}, // Triggers set event valueA on the current floor. Sends 6x67. - {0xB8, "setevt", nullptr, {I32}, F_V05_V4 | F_ARGS}, + {0xB8, {"setevt"}, {I32}, F_V05_V4 | F_ARGS}, // Returns the current difficulty level in regA. If game_lev_super has been executed, returns 2. This opcode only // returns 0-2, even in Ultimate (which results in 2 as well). All non-v1 quests should use get_difficulty_level_v2 // instead. - {0xB9, "get_difficulty_level_v1", "get_difflvl", {W_REG}, F_V05_V4}, + {0xB9, {"get_difficulty_level_v1", "get_difflvl"}, {W_REG}, F_V05_V4}, // Sets a label to be called (in a new thread) when the quest exits. This happens during the unload procedure when // leaving the game, so most opcodes cannot be used in these handlers. Generally they should only be used for // setting quest flags, event flags, or quest counters. - {0xBA, "set_qt_exit", nullptr, {SCRIPT32}, F_V05_V2}, - {0xBA, "set_qt_exit", nullptr, {SCRIPT16}, F_V3_V4}, + {0xBA, {"set_qt_exit"}, {SCRIPT32}, F_V05_V2}, + {0xBA, {"set_qt_exit"}, {SCRIPT16}, F_V3_V4}, // Clears the quest exit handler (opposite of set_qt_exit). - {0xBB, "clr_qt_exit", nullptr, {}, F_V05_V4}, + {0xBB, {"clr_qt_exit"}, {}, F_V05_V4}, // This opcode does nothing, even on 11/2000. - {0xBC, "nop_BC", "unknownBC", {CSTRING}, F_V05_V4}, + {0xBC, {"nop_BC", "unknownBC"}, {CSTRING}, F_V05_V4}, // Creates a timed particle effect. // regsA[0-2] = location (x, y, z as integers) // regsA[3] = effect type // regsA[4] = duration (in frames; 30 frames/sec) - {0xC0, "particle", "particle_V1", {{R_REG32_SET_FIXED, 5}, I32}, F_V05_V2}, - {0xC0, "particle", "particle_V3", {{R_REG_SET_FIXED, 5}}, F_V3_V4}, + {0xC0, {"particle", "particle_V1"}, {{R_REG32_SET_FIXED, 5}, I32}, F_V05_V2}, + {0xC0, {"particle", "particle_V3"}, {{R_REG_SET_FIXED, 5}}, F_V3_V4}, // Specifies what NPCs should say in various situations. This opcode sets strings for all NPCs; to set strings for // only specific NPCs, use npc_text_id (on v3 and later). @@ -984,13 +980,13 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 17: NPC casting Foie, Zonde, or Barta // 18: NPC regained sight of player (not valid on 11/2000) // strB = string for NPC to say (up to 52 characters) - {0xC1, "npc_text", nullptr, {I32, CSTRING}, F_V05_V4 | F_ARGS}, + {0xC1, {"npc_text"}, {I32, CSTRING}, F_V05_V4 | F_ARGS}, // Warps an NPC to a predetermined location. See npc_check_straggle for more details. - {0xC2, "npc_chkwarp", nullptr, {}, F_V05_V4}, + {0xC2, {"npc_chkwarp"}, {}, F_V05_V4}, // Disables PK mode (battle mode) for a specific player. Sends 6x1C. - {0xC3, "pl_pkoff", nullptr, {}, F_V05_V4}, + {0xC3, {"pl_pkoff"}, {}, F_V05_V4}, // Specifies how objects and enemies should be populated for a floor. On v2, the ability to reassign areas was // added, which can be done with map_designate_ex (F80D). @@ -998,24 +994,24 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[1] = type (0: use layout, 1: use offline template, 2: use online template, 3: nothing) // regsA[2] = major variation (minor variation is set to zero) // regsA[3] = ignored - {0xC4, "map_designate", nullptr, {{R_REG_SET_FIXED, 4}}, F_V05_V4}, + {0xC4, {"map_designate"}, {{R_REG_SET_FIXED, 4}}, F_V05_V4}, // Locks (masterkey_on) or unlocks (masterkey_off) all doors - {0xC5, "masterkey_on", nullptr, {}, F_V05_V4}, - {0xC6, "masterkey_off", nullptr, {}, F_V05_V4}, + {0xC5, {"masterkey_on"}, {}, F_V05_V4}, + {0xC6, {"masterkey_off"}, {}, F_V05_V4}, // Enables/disables the timer window - {0xC7, "show_timer", "window_time", {}, F_V05_V4}, - {0xC8, "hide_timer", "winend_time", {}, F_V05_V4}, + {0xC7, {"show_timer", "window_time"}, {}, F_V05_V4}, + {0xC8, {"hide_timer", "winend_time"}, {}, F_V05_V4}, // Sets the time displayed in the timer window. The value in regA should be a number of seconds. - {0xC9, "winset_time", nullptr, {R_REG}, F_V05_V4}, + {0xC9, {"winset_time"}, {R_REG}, F_V05_V4}, // Returns the time from the system clock. The value returned in regA depends on the client's architecture: // - On DC, reads from hardware registers // - On GC, reads from the TBRs // - On PCv2, XB, and BB, calls QueryPerformanceCounter - {0xCA, "getmtime", nullptr, {W_REG}, F_V05_V4}, + {0xCA, {"getmtime"}, {W_REG}, F_V05_V4}, // Creates an item in the quest board. // valueA = index of item (0-5) @@ -1023,11 +1019,11 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // strC = name of item in the Quest Board window // Executing this opcode is not enough for the item to appear! The item only appears if its corresponding display // register is also set to 1. The display registers are r74-r79 (for QB indexes 0-5, respectively). - {0xCB, "set_quest_board_handler", nullptr, {I32, SCRIPT32, CSTRING}, F_V05_V2}, - {0xCB, "set_quest_board_handler", nullptr, {I32, SCRIPT16, CSTRING}, F_V3_V4 | F_ARGS}, + {0xCB, {"set_quest_board_handler"}, {I32, SCRIPT32, CSTRING}, F_V05_V2}, + {0xCB, {"set_quest_board_handler"}, {I32, SCRIPT16, CSTRING}, F_V3_V4 | F_ARGS}, // Deletes an item by index from the quest board. - {0xCC, "clear_quest_board_handler", nullptr, {I32}, F_V05_V4 | F_ARGS}, + {0xCC, {"clear_quest_board_handler"}, {I32}, F_V05_V4 | F_ARGS}, // Creates a particle effect on a given entity. // regsA[0] = effect type @@ -1035,8 +1031,8 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[2] = entity (client ID, 0x1000 + enemy ID, or 0x4000 + object ID) // regsA[3] = y offset (as integer) // valueB is required in pre-v3 but is ignored - {0xCD, "particle_id", "particle_id_V1", {{R_REG32_SET_FIXED, 4}, I32}, F_V05_V2}, - {0xCD, "particle_id", "particle_id_V3", {{R_REG_SET_FIXED, 4}}, F_V3_V4}, + {0xCD, {"particle_id", "particle_id_V1"}, {{R_REG32_SET_FIXED, 4}, I32}, F_V05_V2}, + {0xCD, {"particle_id", "particle_id_V3"}, {{R_REG_SET_FIXED, 4}}, F_V3_V4}, // Creates an NPC. // regsA[0-2] = position (x, y, z as integers) @@ -1044,58 +1040,58 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[4] = initial state (0 = alive, 1 = dead, 2 = invisible text box, according to qedit.info) // regsA[5] = template index (see 6x69 in CommandFormats.hh) // regsA[6] = client ID - {0xCE, "npc_crptalk_id", "npc_crptalk_id_V1", {{R_REG32_SET_FIXED, 7}, I32}, F_V05_V2}, - {0xCE, "npc_crptalk_id", "npc_crptalk_id_V3", {{R_REG_SET_FIXED, 7}}, F_V3_V4}, + {0xCE, {"npc_crptalk_id", "npc_crptalk_id_V1"}, {{R_REG32_SET_FIXED, 7}, I32}, F_V05_V2}, + {0xCE, {"npc_crptalk_id", "npc_crptalk_id_V3"}, {{R_REG_SET_FIXED, 7}}, F_V3_V4}, // Deletes all strings registered with npc_text. - {0xCF, "npc_text_clear_all", "npc_lang_clean", {}, F_V05_V4}, + {0xCF, {"npc_text_clear_all", "npc_lang_clean"}, {}, F_V05_V4}, // Enables PK mode (battle mode) for a specific player. Sends 6x1B. - {0xD0, "pl_pkon", nullptr, {}, F_V1_V4}, + {0xD0, {"pl_pkon"}, {}, F_V1_V4}, // Like find_inventory_item, but regsA specifies data1[0-2] as well as data1[4]. The matching conditions are the // same as in find_inventory_item except that data1[4] must match regsA[3], instead of zero. Returns the item ID in // regB, or FFFFFFFF if not found. - {0xD1, "find_inventory_item_ex", "pl_chk_item2", {{R_REG_SET_FIXED, 4}, W_REG}, F_V1_V4}, + {0xD1, {"find_inventory_item_ex", "pl_chk_item2"}, {{R_REG_SET_FIXED, 4}, W_REG}, F_V1_V4}, // Enables/disables the main menu and shortcut menu. - {0xD2, "enable_mainmenu", nullptr, {}, F_V1_V4}, - {0xD3, "disable_mainmenu", nullptr, {}, F_V1_V4}, + {0xD2, {"enable_mainmenu"}, {}, F_V1_V4}, + {0xD3, {"disable_mainmenu"}, {}, F_V1_V4}, // Enables or disables battle music override. When enabled, the battle segments of the BGM will play regardless of // whether there are enemies nearby. Changing this value only takes effect after any currently-queued music // segments are done playing. The override is cleared upon changing areas. - {0xD4, "start_battlebgm", nullptr, {}, F_V1_V4}, - {0xD5, "end_battlebgm", nullptr, {}, F_V1_V4}, + {0xD4, {"start_battlebgm"}, {}, F_V1_V4}, + {0xD5, {"end_battlebgm"}, {}, F_V1_V4}, // Shows a message in the Quest Board message window - {0xD6, "disp_msg_qb", nullptr, {CSTRING}, F_V1_V4 | F_ARGS}, + {0xD6, {"disp_msg_qb"}, {CSTRING}, F_V1_V4 | F_ARGS}, // Closes the Quest Board message window - {0xD7, "close_msg_qb", nullptr, {}, F_V1_V4}, + {0xD7, {"close_msg_qb"}, {}, F_V1_V4}, // Writes the valueB (a single byte) to the event flag specified by valueA. - {0xD8, "set_eventflag", "set_eventflag_v1", {I32, I32}, F_V1_V2 | F_ARGS}, - {0xD8, "set_eventflag", "set_eventflag_v3", {I32, I32}, F_V3_V4 | F_ARGS}, + {0xD8, {"set_eventflag", "set_eventflag_v1"}, {I32, I32}, F_V1_V2 | F_ARGS}, + {0xD8, {"set_eventflag", "set_eventflag_v3"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Sets regA to valueB, and sends 6x77 so other clients will also set their local regA to valueB. - {0xD9, "sync_register", "sync_leti", {W_REG32, I32}, F_V1_V2 | F_ARGS}, - {0xD9, "sync_register", "sync_leti", {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xD9, {"sync_register", "sync_leti"}, {W_REG32, I32}, F_V1_V2 | F_ARGS}, + {0xD9, {"sync_register", "sync_leti"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, // TODO: Document these - {0xDA, "set_returnhunter", nullptr, {}, F_V1_V4}, - {0xDB, "set_returncity", nullptr, {}, F_V1_V4}, + {0xDA, {"set_returnhunter"}, {}, F_V1_V4}, + {0xDB, {"set_returncity"}, {}, F_V1_V4}, // TODO: Document this - {0xDC, "load_pvr", nullptr, {}, F_V1_V4}, + {0xDC, {"load_pvr"}, {}, F_V1_V4}, // TODO: Document this // Does nothing on all non-DC versions. - {0xDD, "load_midi", nullptr, {}, F_V1_V4}, + {0xDD, {"load_midi"}, {}, F_V1_V4}, // Finds an item in the player's bank, and clears its entry in the bank. // regsA[0-5] = item.data1[0-5] (bank item must exactly match all bytes) // regB = set to 1 if item was found and cleared, 0 if not - {0xDE, "delete_bank_item", "unknownDE", {{R_REG_SET_FIXED, 6}, W_REG}, F_V1_V4}, + {0xDE, {"delete_bank_item", "unknownDE"}, {{R_REG_SET_FIXED, 6}, W_REG}, F_V1_V4}, // Sets NPC AI behaviors. // regsA[0] = TODO: this doesn't appear to be used anywhere internally; official quests used it for the NPC ID? @@ -1127,87 +1123,87 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // NPCs that attack the player and do not have technique_flags & 0x80; could be backoff distance when attacked // by a player // valueB = NPC template to modify (00-3F) - {0xDF, "npc_param", "npc_param_V1", {{R_REG32_SET_FIXED, 14}, I32}, F_V1_V2}, - {0xDF, "npc_param", "npc_param_V3", {{R_REG_SET_FIXED, 14}, I32}, F_V3_V4 | F_ARGS}, + {0xDF, {"npc_param", "npc_param_V1"}, {{R_REG32_SET_FIXED, 14}, I32}, F_V1_V2}, + {0xDF, {"npc_param", "npc_param_V3"}, {{R_REG_SET_FIXED, 14}, I32}, F_V3_V4 | F_ARGS}, // TODO(DX): Document this. It enables a flag that affects some logic in TBoss1Dragon::update. The flag is disabled // when the Dragon's boss arena unloads, but not when it loads, so it can be set when the player is in a different // area. It appears the flag is not cleared if the player never enters the Dragon arena, so it seems the only // advisable place to use this would be immediately after the player enters the Dragon arena. - {0xE0, "pad_dragon", nullptr, {}, F_V1_V4}, + {0xE0, {"pad_dragon"}, {}, F_V1_V4}, // Disables access to a floor (valueA) via the Pioneer 2 Ragol warp. This is the logical opposite of set_mainwarp. - {0xE1, "clear_mainwarp", nullptr, {I32}, F_V1_V4 | F_ARGS}, + {0xE1, {"clear_mainwarp"}, {I32}, F_V1_V4 | F_ARGS}, // Sets camera parameters for the current frame. // regsA[0-2] = relative location of focus point from player // regsA[3-5] = relative location of camera from player - {0xE2, "pcam_param", "pcam_param_V1", {{R_REG32_SET_FIXED, 6}}, F_V1_V2}, - {0xE2, "pcam_param", "pcam_param_V3", {{R_REG_SET_FIXED, 6}}, F_V3_V4}, + {0xE2, {"pcam_param", "pcam_param_V1"}, {{R_REG32_SET_FIXED, 6}}, F_V1_V2}, + {0xE2, {"pcam_param", "pcam_param_V3"}, {{R_REG_SET_FIXED, 6}}, F_V3_V4}, // Triggers set event (valueB) on floor (valueA). Sends 6x67. - {0xE3, "start_setevt", "start_setevt_v1", {I32, I32}, F_V1_V2 | F_ARGS}, - {0xE3, "start_setevt", "start_setevt_v3", {I32, I32}, F_V3_V4 | F_ARGS}, + {0xE3, {"start_setevt", "start_setevt_v1"}, {I32, I32}, F_V1_V2 | F_ARGS}, + {0xE3, {"start_setevt", "start_setevt_v3"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Enables or disables warps - {0xE4, "warp_on", nullptr, {}, F_V1_V4}, - {0xE5, "warp_off", nullptr, {}, F_V1_V4}, + {0xE4, {"warp_on"}, {}, F_V1_V4}, + {0xE5, {"warp_off"}, {}, F_V1_V4}, // Returns the client ID of the local client - {0xE6, "get_client_id", "get_slotnumber", {W_REG}, F_V1_V4}, + {0xE6, {"get_client_id", "get_slotnumber"}, {W_REG}, F_V1_V4}, // Returns the client ID of the lobby/game leader - {0xE7, "get_leader_id", "get_servernumber", {W_REG}, F_V1_V4}, + {0xE7, {"get_leader_id", "get_servernumber"}, {W_REG}, F_V1_V4}, // Sets an event flag from a register. In v3 and later, this is not needed, since set_eventflag can be called with // F_ARGS, but it still exists. - {0xE8, "set_eventflag2", nullptr, {I32, R_REG}, F_V1_V4 | F_ARGS}, + {0xE8, {"set_eventflag2"}, {I32, R_REG}, F_V1_V4 | F_ARGS}, // regA %= regB // This is exactly the same as the mod opcode (including its quirk). - {0xE9, "mod2", "res", {W_REG, R_REG}, F_V1_V4}, + {0xE9, {"mod2", "res"}, {W_REG, R_REG}, F_V1_V4}, // regA %= valueB // This is exactly the same as the modi opcode (including its quirk). - {0xEA, "modi2", "unknownEA", {W_REG, I32}, F_V1_V4}, + {0xEA, {"modi2", "unknownEA"}, {W_REG, I32}, F_V1_V4}, // Changes the background music. create_bgmctrl must be run before doing this. The values for valueA are the same // as for playbgm_epi. - {0xEB, "set_bgm", "enable_bgmctrl", {I32}, F_V1_V4 | F_ARGS}, + {0xEB, {"set_bgm", "enable_bgmctrl"}, {I32}, F_V1_V4 | F_ARGS}, // Changes the state of a switch flag and sends the update to all players (unlike switch_on/switch_off). // regsA[0] = switch flag number // regsA[1] = floor number // regsA[2] = flags (see 6x05 definition in CommandFormats.hh) - {0xEC, "update_switch_flag", "sw_send", {{R_REG_SET_FIXED, 3}}, F_V1_V4}, + {0xEC, {"update_switch_flag", "sw_send"}, {{R_REG_SET_FIXED, 3}}, F_V1_V4}, // Creates a BGM controller object. Use this before set_bgm. - {0xED, "create_bgmctrl", nullptr, {}, F_V1_V4}, + {0xED, {"create_bgmctrl"}, {}, F_V1_V4}, // Like pl_add_meseta, but can only give Meseta to the local player. - {0xEE, "pl_add_meseta2", nullptr, {I32}, F_V1_V4 | F_ARGS}, + {0xEE, {"pl_add_meseta2"}, {I32}, F_V1_V4 | F_ARGS}, // Like sync_register, but takes the value from another register rather than using an immediate value. On v3 and // later, this is identical to sync_register. - {0xEF, "sync_register2", "sync_let", {W_REG32, R_REG32}, F_V1_V2}, - {0xEF, "sync_register2", nullptr, {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xEF, {"sync_register2", "sync_let"}, {W_REG32, R_REG32}, F_V1_V2}, + {0xEF, {"sync_register2"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, // Same as sync_register2, but sends the value via UDP if UDP is enabled. This opcode was removed after GC NTE and // is missing from v3 and v4. - {0xF0, "sync_register2_udp", "send_regwork", {W_REG32, W_REG32}, F_V1_V2}, + {0xF0, {"sync_register2_udp", "send_regwork"}, {W_REG32, W_REG32}, F_V1_V2}, // Sets the camera's location and angle. // regsA[0-2] = camera location (x, y, z as integers) // regsA[3-5] = camera focus location (x, y, z as integers) - {0xF1, "leti_fixed_camera", "leti_fixed_camera_V1", {{R_REG32_SET_FIXED, 6}}, F_V2}, - {0xF1, "leti_fixed_camera", "leti_fixed_camera_V3", {{R_REG_SET_FIXED, 6}}, F_V3_V4}, + {0xF1, {"leti_fixed_camera", "leti_fixed_camera_V1"}, {{R_REG32_SET_FIXED, 6}}, F_V2}, + {0xF1, {"leti_fixed_camera", "leti_fixed_camera_V3"}, {{R_REG_SET_FIXED, 6}}, F_V3_V4}, // Resets the camera to non-fixed (default behavior). - {0xF2, "default_camera_pos1", nullptr, {}, F_V2_V4}, + {0xF2, {"default_camera_pos1"}, {}, F_V2_V4}, // Same as 50, but uses fixed arguments - with the string "俺は節政だ!!", which Google Translate translates as "I // am frugal!!" - {0xF800, "debug_F800", nullptr, {}, F_V2}, + {0xF800, {"debug_F800"}, {}, F_V2}, // Creates a region that calls a label if a specified string is said (via chat) by a player within the region. This // is implemented by the TOChatSensor object; see that object's comments on Map.cc for details. @@ -1225,22 +1221,22 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // is expected to end with an empty string, but TOChatSensor doesn't correctly terminate it this way. However, the // following field is the TQuestThread pointer, which is non-null only if the sensor has already triggered, so it // doesn't misbehave. - {0xF801, "set_chat_callback", "set_chat_callback?", {{R_REG32_SET_FIXED, 5}, CSTRING}, F_V2_V4 | F_ARGS}, + {0xF801, {"set_chat_callback", "set_chat_callback?"}, {{R_REG32_SET_FIXED, 5}, CSTRING}, F_V2_V4 | F_ARGS}, // Returns the difficulty level. Unlike get_difficulty_level_v1, this correctly returns 3 in Ultimate. - {0xF808, "get_difficulty_level_v2", "get_difflvl2", {W_REG}, F_V2_V4}, + {0xF808, {"get_difficulty_level_v2", "get_difflvl2"}, {W_REG}, F_V2_V4}, // Returns the number of players in the game. - {0xF809, "get_number_of_players", "get_number_of_player1", {W_REG}, F_V2_V4}, + {0xF809, {"get_number_of_players", "get_number_of_player1"}, {W_REG}, F_V2_V4}, // Returns the location of the specified player. // regsA[0-2] = returned location (x, y, z as integers) // regB = client ID - {0xF80A, "get_coord_of_player", nullptr, {{W_REG_SET_FIXED, 3}, R_REG}, F_V2_V4}, + {0xF80A, {"get_coord_of_player"}, {{W_REG_SET_FIXED, 3}, R_REG}, F_V2_V4}, // Enables or disables the area map and minimap. - {0xF80B, "enable_map", nullptr, {}, F_V2_V4}, - {0xF80C, "disable_map", nullptr, {}, F_V2_V4}, + {0xF80B, {"enable_map"}, {}, F_V2_V4}, + {0xF80C, {"disable_map"}, {}, F_V2_V4}, // Like map_designate, but allows changing the area assignment. // regsA[0] = floor number @@ -1248,11 +1244,11 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[2] = type (0: use layout, 1: use offline template, 2: use online template, 3: nothing) // regsA[3] = major variation // regsA[4] = minor variation - {0xF80D, "map_designate_ex", nullptr, {{R_REG_SET_FIXED, 5}}, F_V2_V4}, + {0xF80D, {"map_designate_ex"}, {{R_REG_SET_FIXED, 5}}, F_V2_V4}, // Enables or disables weapon dropping upon death for a player. Sends 6x81 (disable) or 6x82 (enable). - {0xF80E, "disable_weapon_drop", "unknownF80E", {CLIENT_ID}, F_V2_V4 | F_ARGS}, - {0xF80F, "enable_weapon_drop", "unknownF80F", {CLIENT_ID}, F_V2_V4 | F_ARGS}, + {0xF80E, {"disable_weapon_drop", "unknownF80E"}, {CLIENT_ID}, F_V2_V4 | F_ARGS}, + {0xF80F, {"enable_weapon_drop", "unknownF80F"}, {CLIENT_ID}, F_V2_V4 | F_ARGS}, // Sets the floor where the players will start. This generally should be used in the start label (where // map_designate, etc. are used). valueA specifies which floor to start on, but indirectly: @@ -1265,149 +1261,149 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 1 game will result in Episode 2 enemies' drop-anything rates being all zeroes, since the Episode 1 ItemPT is // still loaded. This is only an issue in client drop mode; on newserv, you can forbid client drop mode in the // quest's metadata JSON file if needed. (See q058.json for documentation on how to do this.) - {0xF810, "ba_initial_floor", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF810, {"ba_initial_floor"}, {I32}, F_V2_V4 | F_ARGS}, // Clears all battle rules. - {0xF811, "clear_ba_rules", "set_ba_rules", {}, F_V2_V4}, + {0xF811, {"clear_ba_rules", "set_ba_rules"}, {}, F_V2_V4}, // Sets the tech disk mode in battle. valueA (does NOT match enum): // 0 => FORBID_ALL // 1 => ALLOW // 2 => LIMIT_LEVEL - {0xF812, "ba_set_tech_disk_mode", "ba_set_tech", {I32}, F_V2_V4 | F_ARGS}, + {0xF812, {"ba_set_tech_disk_mode", "ba_set_tech"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the weapon and armor mode in battle. valueA (does NOT match enum): // 0 => FORBID_ALL // 1 => ALLOW // 2 => CLEAR_AND_ALLOW // 3 => FORBID_RARES - {0xF813, "ba_set_weapon_and_armor_mode", "ba_set_equip", {I32}, F_V2_V4 | F_ARGS}, + {0xF813, {"ba_set_weapon_and_armor_mode", "ba_set_equip"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the mag mode in battle. valueA (does NOT match enum): // 0 => FORBID_ALL // 1 => ALLOW - {0xF814, "ba_set_forbid_mags", "ba_set_mag", {I32}, F_V2_V4 | F_ARGS}, + {0xF814, {"ba_set_forbid_mags", "ba_set_mag"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the tool mode in battle. valueA (does NOT match enum): // 0 => FORBID_ALL // 1 => ALLOW // 2 => CLEAR_AND_ALLOW - {0xF815, "ba_set_tool_mode", "ba_set_item", {I32}, F_V2_V4 | F_ARGS}, + {0xF815, {"ba_set_tool_mode", "ba_set_item"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the trap mode in battle. valueA (matches enum): // 0 => DEFAULT // 1 => ALL_PLAYERS - {0xF816, "ba_set_trap_mode", "ba_set_trapmenu", {I32}, F_V2_V4 | F_ARGS}, + {0xF816, {"ba_set_trap_mode", "ba_set_trapmenu"}, {I32}, F_V2_V4 | F_ARGS}, // This appears to be unused - the value is copied into the main battle rules struct, but the field is never read // from there. This may have been an early implementation of F851 that affected all trap types, but this field is // no longer used. - {0xF817, "ba_set_unused_F817", "unknownF817", {I32}, F_V2_V4 | F_ARGS}, + {0xF817, {"ba_set_unused_F817", "unknownF817"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the respawn mode in battle. valueA (does NOT match enum): // 0 => FORBID // 1 => ALLOW // 2 => LIMIT_LIVES - {0xF818, "ba_set_respawn", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF818, {"ba_set_respawn"}, {I32}, F_V2_V4 | F_ARGS}, // Enables (1) or disables (0) character replacement in battle mode - {0xF819, "ba_set_replace_char", "ba_set_char", {I32}, F_V2_V4 | F_ARGS}, + {0xF819, {"ba_set_replace_char", "ba_set_char"}, {I32}, F_V2_V4 | F_ARGS}, // Enables (1) or disables (0) weapon dropping upon death in battle - {0xF81A, "ba_dropwep", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF81A, {"ba_dropwep"}, {I32}, F_V2_V4 | F_ARGS}, // Enables (1) or disables (0) teams in battle - {0xF81B, "ba_teams", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF81B, {"ba_teams"}, {I32}, F_V2_V4 | F_ARGS}, // Shows the rules window and starts the battle. This should be used after setting up all the rules with the // various ba_* opcodes. - {0xF81C, "ba_start", "ba_disp_msg", {CSTRING}, F_V2_V4 | F_ARGS}, + {0xF81C, {"ba_start", "ba_disp_msg"}, {CSTRING}, F_V2_V4 | F_ARGS}, // Sets the number of levels to gain upon respawn in battle - {0xF81D, "ba_death_lvl_up", "death_lvl_up", {I32}, F_V2_V4 | F_ARGS}, + {0xF81D, {"ba_death_lvl_up", "death_lvl_up"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the Meseta mode in battle. valueA (matches enum): // 0 => ALLOW // 1 => FORBID_ALL // 2 => CLEAR_AND_ALLOW - {0xF81E, "ba_set_meseta_drop_mode", "ba_set_meseta", {I32}, F_V2_V4 | F_ARGS}, + {0xF81E, {"ba_set_meseta_drop_mode", "ba_set_meseta"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the challenge mode stage number - {0xF820, "cmode_stage", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF820, {"cmode_stage"}, {I32}, F_V2_V4 | F_ARGS}, // regsA[3-8] specify first 6 bytes of an ItemData. This opcode consumes an item ID, but does nothing else. - {0xF821, "nop_F821", nullptr, {{R_REG_SET_FIXED, 9}}, F_V2_V4}, + {0xF821, {"nop_F821"}, {{R_REG_SET_FIXED, 9}}, F_V2_V4}, // This opcode does nothing. It has two branches (one for online, one for offline), but both branches do nothing. - {0xF822, "nop_F822", nullptr, {R_REG}, F_V2_V4}, + {0xF822, {"nop_F822"}, {R_REG}, F_V2_V4}, // Sets the challenge template index. See Client::create_challenge_overlay for details on how the template is used. - {0xF823, "set_cmode_char_template", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF823, {"set_cmode_char_template"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the game difficulty (0-3) in challenge mode. Does nothing in modes other than challenge. - {0xF824, "set_cmode_difficulty", "set_cmode_diff", {I32}, F_V2_V4 | F_ARGS}, + {0xF824, {"set_cmode_difficulty", "set_cmode_diff"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the factor by which all EXP is multiplied in challenge mode. // The multiplier value is regsA[0] + (regsA[1] / regsA[2]). - {0xF825, "exp_multiplication", nullptr, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF825, {"exp_multiplication"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, // Checks if any player is still alive in challenge mode. Returns 1 if all players are dead, or 0 if not. - {0xF826, "cmode_check_all_players_dead", "if_player_alive_cm", {W_REG}, F_V2_V4}, + {0xF826, {"cmode_check_all_players_dead", "if_player_alive_cm"}, {W_REG}, F_V2_V4}, // Checks if all players are still alive in challenge mode. Returns 1 if any player is dead, or 0 if not. - {0xF827, "cmode_check_any_player_dead", "get_user_is_dead?", {W_REG}, F_V2_V4}, + {0xF827, {"cmode_check_any_player_dead", "get_user_is_dead?"}, {W_REG}, F_V2_V4}, // Sends the player with client ID regA to floor regB. Does nothing if regA doesn't refer to the local player. - {0xF828, "go_floor", nullptr, {R_REG, R_REG}, F_V2_V4}, + {0xF828, {"go_floor"}, {R_REG, R_REG}, F_V2_V4}, // Returns the number of enemies killed (in regB) by the player specified by regA. This value is capped to 999. - {0xF829, "get_num_kills", nullptr, {R_REG, W_REG}, F_V2_V4}, + {0xF829, {"get_num_kills"}, {R_REG, W_REG}, F_V2_V4}, // Resets the kill count for the player specified by regA. - {0xF82A, "reset_kills", nullptr, {R_REG}, F_V2_V4}, + {0xF82A, {"reset_kills"}, {R_REG}, F_V2_V4}, // Sets or clears a switch flag, and synchronizes the value to all players. // valueA = floor // valueB = switch flag index - {0xF82B, "set_switch_flag_sync", "unlock_door2", {I32, I32}, F_V2_V4 | F_ARGS}, - {0xF82C, "clear_switch_flag_sync", "lock_door2", {I32, I32}, F_V2_V4 | F_ARGS}, + {0xF82B, {"set_switch_flag_sync", "unlock_door2"}, {I32, I32}, F_V2_V4 | F_ARGS}, + {0xF82C, {"clear_switch_flag_sync", "lock_door2"}, {I32, I32}, F_V2_V4 | F_ARGS}, // Checks a switch flag on the current floor // regsA[0] = switch flag index // regsA[1] = result (0 or 1) - {0xF82D, "read_switch_flag", "if_switch_not_pressed", {{W_REG_SET_FIXED, 2}}, F_V2_V4}, + {0xF82D, {"read_switch_flag", "if_switch_not_pressed"}, {{W_REG_SET_FIXED, 2}}, F_V2_V4}, // Checks a switch flag on any floor // regsA[0] = floor // regsA[1] = switch flag index // regsA[2] = result (0 or 1) - {0xF82E, "read_switch_flag_on_floor", "if_switch_pressed", {{W_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF82E, {"read_switch_flag_on_floor", "if_switch_pressed"}, {{W_REG_SET_FIXED, 3}}, F_V2_V4}, // Enables a player to control the Dragon. valueA specifies the client ID. - {0xF830, "control_dragon", nullptr, {R_REG}, F_V2_V4}, + {0xF830, {"control_dragon"}, {R_REG}, F_V2_V4}, // Disables player control of the Dragon. - {0xF831, "release_dragon", nullptr, {}, F_V2_V4}, + {0xF831, {"release_dragon"}, {}, F_V2_V4}, // Shrinks a player or returns them to normal size. regA specifies the client ID. - {0xF838, "shrink", nullptr, {R_REG}, F_V2_V4}, - {0xF839, "unshrink", nullptr, {R_REG}, F_V2_V4}, + {0xF838, {"shrink"}, {R_REG}, F_V2_V4}, + {0xF839, {"unshrink"}, {R_REG}, F_V2_V4}, // These set some camera parameters for the specified player. These parameters appear to be unused, so these // opcodes essentially do nothing. // regsA[0] = client ID // regsA[1-3] = a Vector3F (x, y, z as integers) - {0xF83A, "set_shrink_cam1", nullptr, {{R_REG_SET_FIXED, 4}}, F_V2_V4}, - {0xF83B, "set_shrink_cam2", nullptr, {{R_REG_SET_FIXED, 4}}, F_V2_V4}, + {0xF83A, {"set_shrink_cam1"}, {{R_REG_SET_FIXED, 4}}, F_V2_V4}, + {0xF83B, {"set_shrink_cam2"}, {{R_REG_SET_FIXED, 4}}, F_V2_V4}, // Shows the timer window in challenge mode. regA is the time value to display, in seconds. - {0xF83C, "disp_time_cmode", nullptr, {R_REG}, F_V2_V4}, + {0xF83C, {"disp_time_cmode"}, {R_REG}, F_V2_V4}, // Sets the total number of areas across all challenge quests for the current episode. - {0xF83D, "set_area_total", "unknownF83D", {I32}, F_V2_V4 | F_ARGS}, + {0xF83D, {"set_area_total", "unknownF83D"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the number of the current challenge mode area. - {0xF83E, "set_current_area_number", "delete_area_title?", {I32}, F_V2_V4 | F_ARGS}, + {0xF83E, {"set_current_area_number", "delete_area_title?"}, {I32}, F_V2_V4 | F_ARGS}, // Loads a custom visual config for creating NPCs. Generally the sequence should go like this: // prepare_npc_visual label_containing_PlayerVisualConfig @@ -1415,8 +1411,8 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // // After any NPC is created, the effects of these opcodes are undone; if the script wants to create another NPC // with a custom visual config, it must run these opcodes again. - {0xF840, "enable_npc_visual", "load_npc_data", {}, F_V2_V4}, - {0xF841, "prepare_npc_visual", "get_npc_data", {{LABEL16, Arg::DataType::PLAYER_VISUAL_CONFIG, "visual_config"}}, F_V2_V4}, + {0xF840, {"enable_npc_visual", "load_npc_data"}, {}, F_V2_V4}, + {0xF841, {"prepare_npc_visual", "get_npc_data"}, {{LABEL16, Arg::DataType::PLAYER_VISUAL_CONFIG, "visual_config"}}, F_V2_V4}, // These are used to set the scores for each type of action in battle mode. The value used in each of these are // regsA[0] + (regsA[1] / regsA[2]), treated as a floating-point value. For example, one way to specify the value @@ -1432,47 +1428,47 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // ba_enemy_kill_score: Score earned by killing a non-player enemy. (Default 3) // ba_enemy_death_score: Score lost by dying to a non-player enemy. (Default 7) // ba_meseta_score: Score earned per Meseta in the player's inventory. (Default 0) - {0xF848, "ba_player_give_damage_score", "give_damage_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF849, "ba_player_take_damage_score", "take_damage_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF84A, "ba_enemy_give_damage_score", "enemy_give_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF84B, "ba_enemy_take_damage_score", "enemy_take_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF84C, "ba_player_kill_score", "kill_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF84D, "ba_player_death_score", "death_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF84E, "ba_enemy_kill_score", "enemy_kill_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF84F, "ba_enemy_death_score", "enemy_death_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, - {0xF850, "ba_meseta_score", "meseta_score", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF848, {"ba_player_give_damage_score", "give_damage_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF849, {"ba_player_take_damage_score", "take_damage_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF84A, {"ba_enemy_give_damage_score", "enemy_give_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF84B, {"ba_enemy_take_damage_score", "enemy_take_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF84C, {"ba_player_kill_score", "kill_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF84D, {"ba_player_death_score", "death_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF84E, {"ba_enemy_kill_score", "enemy_kill_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF84F, {"ba_enemy_death_score", "enemy_death_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF850, {"ba_meseta_score", "meseta_score"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, // Sets the number of traps players can use in battle mode. regsA[1] is the amount; regsA[0] is the trap type: // 0 = Damage trap (internal type 0) // 1 = Slow trap (internal type 2) // 2 = Confuse trap (internal type 3) // 3 = Freeze trap (internal type 1) - {0xF851, "ba_set_trap_count", "ba_set_trap", {{R_REG_SET_FIXED, 2}}, F_V2_V4}, + {0xF851, {"ba_set_trap_count", "ba_set_trap"}, {{R_REG_SET_FIXED, 2}}, F_V2_V4}, // Enables (0) or disables (1) the targeting reticle in battle - {0xF852, "ba_hide_target_reticle", "ba_set_target", {I32}, F_V2_V4 | F_ARGS}, + {0xF852, {"ba_hide_target_reticle", "ba_set_target"}, {I32}, F_V2_V4 | F_ARGS}, // Enables or disables overrides of warp destination floors. When enabled, area warps will always go to the next // floor (current floor + 1); when disabled, they will go to the floor specified in their constructor args. - {0xF853, "override_warp_dest_floor", "reverse_warps", {}, F_V2_V4}, - {0xF854, "restore_warp_dest_floor", "unreverse_warps", {}, F_V2_V4}, + {0xF853, {"override_warp_dest_floor", "reverse_warps"}, {}, F_V2_V4}, + {0xF854, {"restore_warp_dest_floor", "unreverse_warps"}, {}, F_V2_V4}, // Enables or disables overriding graphical features with those used in Ultimate - {0xF855, "set_ult_map", nullptr, {}, F_V2_V4}, - {0xF856, "unset_ult_map", nullptr, {}, F_V2_V4}, + {0xF855, {"set_ult_map"}, {}, F_V2_V4}, + {0xF856, {"unset_ult_map"}, {}, F_V2_V4}, // Sets the area title in challenge mode - {0xF857, "set_area_title", nullptr, {CSTRING}, F_V2_V4 | F_ARGS}, + {0xF857, {"set_area_title"}, {CSTRING}, F_V2_V4 | F_ARGS}, // Enables or disables the ability to see your own traps. - {0xF858, "ba_show_self_traps", "BA_Show_Self_Traps", {}, F_V2_V4}, - {0xF859, "ba_hide_self_traps", "BA_Hide_Self_Traps", {}, F_V2_V4}, + {0xF858, {"ba_show_self_traps", "BA_Show_Self_Traps"}, {}, F_V2_V4}, + {0xF859, {"ba_hide_self_traps", "BA_Hide_Self_Traps"}, {}, F_V2_V4}, // Creates an item in a player's inventory and equips it. // regsA[0] = client ID // regsA[1-3] = item.data1[0-2] - {0xF85A, "equip_item", "equip_item_v2", {{R_REG32_SET_FIXED, 4}}, F_V2}, - {0xF85A, "equip_item", "equip_item_v3", {{R_REG_SET_FIXED, 4}}, F_V3_V4}, + {0xF85A, {"equip_item", "equip_item_v2"}, {{R_REG32_SET_FIXED, 4}}, F_V2}, + {0xF85A, {"equip_item", "equip_item_v3"}, {{R_REG_SET_FIXED, 4}}, F_V3_V4}, // Unequips an item from a client. Sends 6x26 if an item is unequipped. // valueA = client ID @@ -1482,11 +1478,11 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 2 = shield // 3 = mag // 4-7 = units 1-4 - {0xF85B, "unequip_item", "unequip_item_V2", {CLIENT_ID, I32}, F_V2 | F_ARGS}, - {0xF85B, "unequip_item", "unequip_item_V3", {CLIENT_ID, I32}, F_V3_V4 | F_ARGS}, + {0xF85B, {"unequip_item", "unequip_item_V2"}, {CLIENT_ID, I32}, F_V2 | F_ARGS}, + {0xF85B, {"unequip_item", "unequip_item_V3"}, {CLIENT_ID, I32}, F_V3_V4 | F_ARGS}, // Same as p_talk_guild except it always refers to the local player. valueA is ignored. - {0xF85C, "p_talk_guild_local", "QEXIT2", {I32}, F_V2_V4}, + {0xF85C, {"p_talk_guild_local", "QEXIT2"}, {I32}, F_V2_V4}, // Sets flags that forbid types of items from being used. To forbid multiple types of items, use this opcode // multiple times. valueA: @@ -1497,19 +1493,19 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 4 = disallow units // 5 = disallow mags // 6 = disallow tools - {0xF85D, "set_allow_item_flags", "allow_weapons", {I32}, F_V2_V4 | F_ARGS}, + {0xF85D, {"set_allow_item_flags", "allow_weapons"}, {I32}, F_V2_V4 | F_ARGS}, // Enables (1) or disables (0) sonar in battle - {0xF85E, "ba_enable_sonar", "unknownF85E", {I32}, F_V2_V4 | F_ARGS}, + {0xF85E, {"ba_enable_sonar", "unknownF85E"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the number of sonar uses per character in battle - {0xF85F, "ba_sonar_count", "ba_use_sonar", {I32}, F_V2_V4 | F_ARGS}, + {0xF85F, {"ba_sonar_count", "ba_use_sonar"}, {I32}, F_V2_V4 | F_ARGS}, // Specifies when score announcements should occur during battle. The values are measured in minutes remaining. // There can be up to 8 score announcements; any further set_score_announce calls are ignored. clear_score_announce // deletes all announcement times. - {0xF860, "clear_score_announce", "unknownF860", {}, F_V2_V4}, - {0xF861, "set_score_announce", "unknownF861", {I32}, F_V2_V4 | F_ARGS}, + {0xF860, {"clear_score_announce", "unknownF860"}, {}, F_V2_V4}, + {0xF861, {"set_score_announce", "unknownF861"}, {I32}, F_V2_V4 | F_ARGS}, // Creates an S-rank weapon in the player's inventory. This opcode is not used in challenge mode, presumably since // it doesn't offer a mechanism for the player to choose their weapon's name. The award_item_give opcode is used @@ -1517,8 +1513,8 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regA/valueA = client ID (must match local client ID) // regB (must be a register, even on v3/v4) = item.data1[1] // strC = custom name - {0xF862, "give_s_rank_weapon", nullptr, {R_REG32, R_REG32, CSTRING}, F_V2}, - {0xF862, "give_s_rank_weapon", nullptr, {I32, R_REG, CSTRING}, F_V3_V4 | F_ARGS}, + {0xF862, {"give_s_rank_weapon"}, {R_REG32, R_REG32, CSTRING}, F_V2}, + {0xF862, {"give_s_rank_weapon"}, {I32, R_REG, CSTRING}, F_V3_V4 | F_ARGS}, // Returns the currently-equipped mag's levels. If no mag is equipped, regsA are unaffected! Make sure to // initialize them before using this. @@ -1526,32 +1522,32 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[1] = returned POW level // regsA[2] = returned DEX level // regsA[3] = returned MIND level - {0xF863, "get_mag_levels", nullptr, {{W_REG32_SET_FIXED, 4}}, F_V2}, - {0xF863, "get_mag_levels", nullptr, {{W_REG_SET_FIXED, 4}}, F_V3_V4}, + {0xF863, {"get_mag_levels"}, {{W_REG32_SET_FIXED, 4}}, F_V2}, + {0xF863, {"get_mag_levels"}, {{W_REG_SET_FIXED, 4}}, F_V3_V4}, // Sets the color and rank text if the player manages to complete the current challenge stage. // valueA = color as XRGB8888 // strB = rank text (up to 11 characters) - {0xF864, "set_cmode_rank_result", "cmode_rank", {I32, CSTRING}, F_V2_V4 | F_ARGS}, + {0xF864, {"set_cmode_rank_result", "cmode_rank"}, {I32, CSTRING}, F_V2_V4 | F_ARGS}, // Shows the item name entry window and suspends the calling thread - {0xF865, "award_item_name", "award_item_name?", {}, F_V2_V4}, + {0xF865, {"award_item_name", "award_item_name?"}, {}, F_V2_V4}, // Shows the item choice window and suspends the calling thread - {0xF866, "award_item_select", "award_item_select?", {}, F_V2_V4}, + {0xF866, {"award_item_select", "award_item_select?"}, {}, F_V2_V4}, // Creates an item in the player's inventory, chosen via the previous award_item_name and award_item_select // opcodes, and updates the player's challenge rank text and color according to the rank they achieved. Sends 07DF // on BB; on other versions, sends nothing. // regA = return value (1 if item successfully created; 0 otherwise) - {0xF867, "award_item_give", "award_item_give_to?", {W_REG}, F_V2_V4}, + {0xF867, {"award_item_give", "award_item_give_to?"}, {W_REG}, F_V2_V4}, // Specifies where the time threshold is for a challenge rank. // regsA[0] = rank (0 = B, 1 = A, 2 = S) // regsA[1] = time in seconds (times faster than this are considered to be this rank or better) // regsA[2] = award flags mask (generally should be (1 << regsA[0])) // regB = result (0 = failed, 1 = success) - {0xF868, "set_cmode_rank_threshold", "set_cmode_rank", {{R_REG_SET_FIXED, 3}, W_REG}, F_V2_V4}, + {0xF868, {"set_cmode_rank_threshold", "set_cmode_rank"}, {{R_REG_SET_FIXED, 3}, W_REG}, F_V2_V4}, // Registers a timing result of (regA) seconds for the current challenge mode stage. Returns a result code in regB: // 0 = player achieved rank B and has not completed this stage before @@ -1561,65 +1557,65 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 4 = player did not achieve a new rank this time // 5 = player's inventory is full and can't receive the prize // 6 = internal errors (e.g. save file is missing, stage number not set) - {0xF869, "check_rank_time", nullptr, {R_REG, W_REG}, F_V2_V4}, + {0xF869, {"check_rank_time"}, {R_REG, W_REG}, F_V2_V4}, // Creates an item in the local player's bank, and saves the player's challenge rank and title color. Sends 07DF on // BB. This is used for the A and B rank prizes. // regsA = item.data1[0-5] // regB = returned success code (1 = success, 0 = failed) - {0xF86A, "item_create_cmode", nullptr, {{R_REG_SET_FIXED, 6}, W_REG}, F_V2_V4}, + {0xF86A, {"item_create_cmode"}, {{R_REG_SET_FIXED, 6}, W_REG}, F_V2_V4}, // Sets the effective area for item drops in battle. valueA should be in the range [1, 10]. - {0xF86B, "ba_set_box_drop_area", "ba_box_drops", {R_REG}, F_V2_V4}, + {0xF86B, {"ba_set_box_drop_area", "ba_box_drops"}, {R_REG}, F_V2_V4}, // Shows a confirmation window asking if the player is satsified with their choice of S rank prize and weapon name. // regA = result code (0 = OK, 1 = reconsider) - {0xF86C, "award_item_ok", "award_item_ok?", {W_REG}, F_V2_V4}, + {0xF86C, {"award_item_ok", "award_item_ok?"}, {W_REG}, F_V2_V4}, // Enables or disables traps' ability to hurt the player who set them - {0xF86D, "ba_set_trapself", nullptr, {}, F_V2_V4}, - {0xF86E, "ba_clear_trapself", "ba_ignoretrap", {}, F_V2_V4}, + {0xF86D, {"ba_set_trapself"}, {}, F_V2_V4}, + {0xF86E, {"ba_clear_trapself", "ba_ignoretrap"}, {}, F_V2_V4}, // Sets the number of lives each player gets in battle when the LIMIT_LIVES respawn mode is used. - {0xF86F, "ba_set_lives", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF86F, {"ba_set_lives"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the maximum level for any technique in battle - {0xF870, "ba_set_max_tech_level", "ba_set_tech_lvl", {I32}, F_V2_V4 | F_ARGS}, + {0xF870, {"ba_set_max_tech_level", "ba_set_tech_lvl"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the character's overlay level in battle - {0xF871, "ba_set_char_level", "ba_set_lvl", {I32}, F_V2_V4 | F_ARGS}, + {0xF871, {"ba_set_char_level", "ba_set_lvl"}, {I32}, F_V2_V4 | F_ARGS}, // Sets the battle time limit. valueA is measured in minutes - {0xF872, "ba_set_time_limit", nullptr, {I32}, F_V2_V4 | F_ARGS}, + {0xF872, {"ba_set_time_limit"}, {I32}, F_V2_V4 | F_ARGS}, // Sets regA to 1 if Dark Falz has been defeated, or 0 otherwise. - {0xF873, "dark_falz_is_dead", "falz_is_dead", {W_REG}, F_V2_V4}, + {0xF873, {"dark_falz_is_dead", "falz_is_dead"}, {W_REG}, F_V2_V4}, // Sets an override for the challenge rank text. At the time the rank is checked via check_rank_time, these // overrides are applied in the order they were created. For each one, if the existing rank matches the check // string, it is replaced with the override string and the color is replaced with the override color. // valueA = override color (XRGB8888) // strB = check string and override string (separated by \t or \n) - {0xF874, "set_cmode_rank_override", "unknownF874", {I32, CSTRING}, F_V2_V4 | F_ARGS}, + {0xF874, {"set_cmode_rank_override", "unknownF874"}, {I32, CSTRING}, F_V2_V4 | F_ARGS}, // Enables or disables the transparency effect, similar to the Stealth Suit. regA is the client ID. - {0xF875, "enable_stealth_suit_effect", nullptr, {R_REG}, F_V2_V4}, - {0xF876, "disable_stealth_suit_effect", nullptr, {R_REG}, F_V2_V4}, + {0xF875, {"enable_stealth_suit_effect"}, {R_REG}, F_V2_V4}, + {0xF876, {"disable_stealth_suit_effect"}, {R_REG}, F_V2_V4}, // Enables or disables the use of techniques for a player. regA is the client ID. - {0xF877, "enable_techs", nullptr, {R_REG}, F_V2_V4}, - {0xF878, "disable_techs", nullptr, {R_REG}, F_V2_V4}, + {0xF877, {"enable_techs"}, {R_REG}, F_V2_V4}, + {0xF878, {"disable_techs"}, {R_REG}, F_V2_V4}, // Returns the gender of a character. // regA = client ID // regB = returned gender (0 = male, 1 = female, 2 = no player present or invalid class flags) - {0xF879, "get_gender", nullptr, {R_REG, W_REG}, F_V2_V4}, + {0xF879, {"get_gender"}, {R_REG, W_REG}, F_V2_V4}, // Returns the race and class of a character. // regA = client ID // regsB[0] = returned race (0 = human, 1 = newman, 2 = android, 3 = no player present or invalid class flags) // regsB[1] = returned class (0 = hunter, 1 = ranger, 2 = force, 3 = no player present or invalid class flags) - {0xF87A, "get_chara_class", nullptr, {R_REG, {W_REG_SET_FIXED, 2}}, F_V2_V4}, + {0xF87A, {"get_chara_class"}, {R_REG, {W_REG_SET_FIXED, 2}}, F_V2_V4}, // Removes Meseta from a player. Sends 6xC9 on BB. // regsA[0] = client ID @@ -1628,64 +1624,64 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsB[1] = result code if player not present (set to 0 if there is no player with the specified client ID) // Note that only one of regsB[0] and regsB[1] is written; the other is unchanged. Therefore, it's good practice to // set regsB[1] to a nonzero value before using this opcode. - {0xF87B, "take_slot_meseta", nullptr, {{R_REG_SET_FIXED, 2}, {W_REG_SET_FIXED, 2}}, F_V2_V4}, + {0xF87B, {"take_slot_meseta"}, {{R_REG_SET_FIXED, 2}, {W_REG_SET_FIXED, 2}}, F_V2_V4}, // Returns the Guild Card file creation time in seconds since 00:00:00 on 1 January 2000. - {0xF87C, "get_guild_card_file_creation_time", "get_encryption_key", {W_REG}, F_V2_V4}, + {0xF87C, {"get_guild_card_file_creation_time", "get_encryption_key"}, {W_REG}, F_V2_V4}, // Kills the player whose client ID is regA. - {0xF87D, "kill_player", nullptr, {R_REG}, F_V2_V4}, + {0xF87D, {"kill_player"}, {R_REG}, F_V2_V4}, // Returns (in regA) the player's serial number. On BB, returns 0. - {0xF87E, "get_serial_number", nullptr, {W_REG}, F_V2_V3}, - {0xF87E, "return_0_F87E", nullptr, {W_REG}, F_V4}, + {0xF87E, {"get_serial_number"}, {W_REG}, F_V2_V3}, + {0xF87E, {"return_0_F87E"}, {W_REG}, F_V4}, // Reads an event flag from the system file. // regA = event flag index (0x00-0xFF) // regB = returned event flag value (1 byte) - {0xF87F, "get_eventflag", "read_guildcard_flag", {R_REG, W_REG}, F_V2_V4}, + {0xF87F, {"get_eventflag", "read_guildcard_flag"}, {R_REG, W_REG}, F_V2_V4}, // Normally, trap damage is computed with the following formula: // (700.0 * area_factor[area] * 2.0 * (0.01 * level + 0.1)) // This opcode overrides that computation. The value is specified with the integer and fractional parts split up: // the actual value used by the game will be regsA[0] + (regsA[1] / regsA[2]). - {0xF880, "set_trap_damage", "ba_set_dmgtrap", {{R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF880, {"set_trap_damage", "ba_set_dmgtrap"}, {{R_REG_SET_FIXED, 3}}, F_V2_V4}, // Loads the name of the player whose client ID is regA into a static buffer, which can later be referred to with // "" in message strings. - {0xF881, "get_pl_name", "get_pl_name?", {R_REG}, F_V2_V4}, + {0xF881, {"get_pl_name", "get_pl_name?"}, {R_REG}, F_V2_V4}, // Loads the job (Hunter, Ranger, or Force) of the player whose client ID is regA into a static buffer, which can // later be referred to with "" in message strings. - {0xF882, "get_pl_job", nullptr, {R_REG}, F_V2_V4}, + {0xF882, {"get_pl_job"}, {R_REG}, F_V2_V4}, // Counts the number of players near the specified player. // regsA[0] = client ID // regsA[1] = radius (as integer) // regB = count - {0xF883, "get_player_proximity", "players_in_range", {{R_REG_SET_FIXED, 2}, W_REG}, F_V2_V4}, + {0xF883, {"get_player_proximity", "players_in_range"}, {{R_REG_SET_FIXED, 2}, W_REG}, F_V2_V4}, // Writes 2 bytes to the event flags in the system file. // valueA = flag index (must be 254 or less) // regB/valueB = value - {0xF884, "set_eventflag16", "write_guild_flagw", {I32, R_REG}, F_V2}, - {0xF884, "set_eventflag16", "write_guild_flagw", {I32, I32}, F_V3_V4 | F_ARGS}, + {0xF884, {"set_eventflag16", "write_guild_flagw"}, {I32, R_REG}, F_V2}, + {0xF884, {"set_eventflag16", "write_guild_flagw"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Writes 4 bytes to the event flags in the system file. // valueA = flag index (must be 252 or less) // regB/valueB = value - {0xF885, "set_eventflag32", "write_guild_flagl", {I32, R_REG}, F_V2}, - {0xF885, "set_eventflag32", "write_guild_flagl", {I32, I32}, F_V3_V4 | F_ARGS}, + {0xF885, {"set_eventflag32", "write_guild_flagl"}, {I32, R_REG}, F_V2}, + {0xF885, {"set_eventflag32", "write_guild_flagl"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Returns (in regB) the battle result place (1, 2, 3, or 4) of the player specified by regA. - {0xF886, "ba_get_place", nullptr, {R_REG, W_REG}, F_V2_V4}, + {0xF886, {"ba_get_place"}, {R_REG, W_REG}, F_V2_V4}, // Returns (in regB) the battle score of the player specified by regA. - {0xF887, "ba_get_score", nullptr, {R_REG, W_REG}, F_V2_V4}, + {0xF887, {"ba_get_score"}, {R_REG, W_REG}, F_V2_V4}, // TODO: Document these - {0xF888, "enable_win_pfx", "ba_close_msg", {}, F_V2_V4}, - {0xF889, "disable_win_pfx", nullptr, {}, F_V2_V4}, + {0xF888, {"enable_win_pfx", "ba_close_msg"}, {}, F_V2_V4}, + {0xF889, {"disable_win_pfx"}, {}, F_V2_V4}, // Returns (in regB) the state of the player specified by regA. State values: // 0 = no player present, or warping (non-BB) @@ -1702,27 +1698,27 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 11 = reviving // 12 = frozen // 13 = warping (BB only) - {0xF88A, "get_player_state", "get_player_status", {R_REG, W_REG}, F_V2_V4}, + {0xF88A, {"get_player_state", "get_player_status"}, {R_REG, W_REG}, F_V2_V4}, // Sends a Simple Mail message to the player. // regA (v2) = sender's Guild Card number // valueA (v3+) = number of register that holds sender's Guild Card number // strB = sender name and message (separated by \n) - {0xF88B, "send_mail", nullptr, {R_REG, CSTRING}, F_V2_V4 | F_ARGS}, + {0xF88B, {"send_mail"}, {R_REG, CSTRING}, F_V2_V4 | F_ARGS}, // Returns the game's major version (2 on DCv2/PC/GCNTE, 3 on GC/Ep3, 4 on Xbox and BB) - {0xF88C, "get_game_version", nullptr, {W_REG}, F_V2_V4}, + {0xF88C, {"get_game_version"}, {W_REG}, F_V2_V4}, // Sets the local player's stage completion time in challenge mode. // regA = time in seconds // regB = value to be used in computation of token_v4 (BB only; see 6x95 in CommandFormats.hh for details) - {0xF88D, "chl_set_timerecord", "chl_set_timerecord?", {R_REG}, F_V2 | F_V3}, - {0xF88D, "chl_set_timerecord", "chl_set_timerecord?", {R_REG, R_REG}, F_V4}, + {0xF88D, {"chl_set_timerecord", "chl_set_timerecord?"}, {R_REG}, F_V2 | F_V3}, + {0xF88D, {"chl_set_timerecord", "chl_set_timerecord?"}, {R_REG, R_REG}, F_V4}, // Gets the current player's completion time for the current challenge stage in seconds. If the player's time is // invalid or faster than the time set by chl_set_min_time_online (or 5 minutes, if offline), returns -2. If used // in non-challenge mode, returns -1. - {0xF88E, "chl_get_timerecord", "chl_get_timerecord?", {W_REG}, F_V2_V4}, + {0xF88E, {"chl_get_timerecord", "chl_get_timerecord?"}, {W_REG}, F_V2_V4}, // Sets the probabilities of getting recovery items from challenge mode graves. There are 10 floating-point values, // specified as fractions in an array of 20 registers (pairs of numerator and denominator). The number of items @@ -1740,33 +1736,33 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[14] / regsA[15]: Chance of getting Monofluid x1 // regsA[16] / regsA[17]: Chance of getting Difluid x1 // regsA[18] / regsA[19]: Chance of getting Trifluid x1 - {0xF88F, "set_cmode_grave_rates", nullptr, {{R_REG_SET_FIXED, 20}}, F_V2_V4}, + {0xF88F, {"set_cmode_grave_rates"}, {{R_REG_SET_FIXED, 20}}, F_V2_V4}, // Clears all levels from the main warp. - {0xF890, "clear_mainwarp_all", "clear_area_list", {}, F_V2_V4}, + {0xF890, {"clear_mainwarp_all", "clear_area_list"}, {}, F_V2_V4}, // Specifies which enemy should be affected by subsequent get_*_data opcodes (the following 4 definitions). valueA // is the battle parameter index for the desired enemy. - {0xF891, "set_override_enemy_bp_index", "load_enemy_data", {I32}, F_V2_V4 | F_ARGS}, + {0xF891, {"set_override_enemy_bp_index", "load_enemy_data"}, {I32}, F_V2_V4 | F_ARGS}, // Replaces enemy stats with the given structures (PlayerStats, AttackData, ResistData, or MovementData) for the // enemy previously specified with load_enemy_data. - {0xF892, "set_enemy_physical_data", "get_physical_data", {{LABEL16, Arg::DataType::PLAYER_STATS, "stats"}}, F_V2_V4}, - {0xF893, "set_enemy_attack_data", "get_attack_data", {{LABEL16, Arg::DataType::ATTACK_DATA, "attack_data"}}, F_V2_V4}, - {0xF894, "set_enemy_resist_data", "get_resist_data", {{LABEL16, Arg::DataType::RESIST_DATA, "resist_data"}}, F_V2_V4}, - {0xF895, "set_enemy_movement_data", "get_movement_data", {{LABEL16, Arg::DataType::MOVEMENT_DATA, "movement_data"}}, F_V2_V4}, + {0xF892, {"set_enemy_physical_data", "get_physical_data"}, {{LABEL16, Arg::DataType::PLAYER_STATS, "stats"}}, F_V2_V4}, + {0xF893, {"set_enemy_attack_data", "get_attack_data"}, {{LABEL16, Arg::DataType::ATTACK_DATA, "attack_data"}}, F_V2_V4}, + {0xF894, {"set_enemy_resist_data", "get_resist_data"}, {{LABEL16, Arg::DataType::RESIST_DATA, "resist_data"}}, F_V2_V4}, + {0xF895, {"set_enemy_movement_data", "get_movement_data"}, {{LABEL16, Arg::DataType::MOVEMENT_DATA, "movement_data"}}, F_V2_V4}, // Reads 2 bytes or 4 bytes from the event flags in the system file. // regA = event flag index // regB = returned value - {0xF896, "get_eventflag16", "read_guildflag_16b", {R_REG, W_REG}, F_V2_V4}, - {0xF897, "get_eventflag32", "read_guildflag_32b", {R_REG, W_REG}, F_V2_V4}, + {0xF896, {"get_eventflag16", "read_guildflag_16b"}, {R_REG, W_REG}, F_V2_V4}, + {0xF897, {"get_eventflag32", "read_guildflag_32b"}, {R_REG, W_REG}, F_V2_V4}, // regA <<= regB - {0xF898, "shift_left", nullptr, {W_REG, R_REG}, F_V2_V4}, + {0xF898, {"shift_left"}, {W_REG, R_REG}, F_V2_V4}, // regA >>= regB - {0xF899, "shift_right", nullptr, {W_REG, R_REG}, F_V2_V4}, + {0xF899, {"shift_right"}, {W_REG, R_REG}, F_V2_V4}, // Generates a random number by calling rand(). Note that the returned value is not uniform! The algorithm // generates a uniform random number, scales it to the range 0 through (max-1) inclusive, then clamps it to the @@ -1778,43 +1774,43 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[0] = minimum value // regsA[1] = maximum value // regB = generated random value - {0xF89A, "get_random", nullptr, {{R_REG_SET_FIXED, 2}, W_REG}, F_V2_V4}, + {0xF89A, {"get_random"}, {{R_REG_SET_FIXED, 2}, W_REG}, F_V2_V4}, // Clears all game state, including all floor items, set states (enemy and object), enemy and object states, wave // event flags, and switch flags. Also destroys all running quest threads. - {0xF89B, "reset_map", nullptr, {}, F_V2_V4}, + {0xF89B, {"reset_map"}, {}, F_V2_V4}, // Returns the leader's choice when a challenge is failed in regA. Values: // 0 = not chosen yet // 1 = no (releases players to interact on Pioneer 2) // 2 = yes (restarts the stage) - {0xF89C, "get_chl_retry_choice", "retry_menu", {W_REG}, F_V2_V4}, + {0xF89C, {"get_chl_retry_choice", "retry_menu"}, {W_REG}, F_V2_V4}, // Creates the retry menu when a challenge stage is failed - {0xF89D, "chl_create_retry_menu", "chl_enable_retry", {}, F_V2_V4}, + {0xF89D, {"chl_create_retry_menu", "chl_enable_retry"}, {}, F_V2_V4}, // Enables (0) or disables (1) the use of scape dolls in battle - {0xF89E, "ba_forbid_scape_dolls", "ba_forbid_scape_doll", {I32}, F_V2_V4 | F_ARGS}, + {0xF89E, {"ba_forbid_scape_dolls", "ba_forbid_scape_doll"}, {I32}, F_V2_V4 | F_ARGS}, // Restores a player's HP and TP, clears status effects, and revives the player if dead. // regA = client ID - {0xF89F, "player_recovery", "unknownF89F", {R_REG}, F_V2_V4}, + {0xF89F, {"player_recovery", "unknownF89F"}, {R_REG}, F_V2_V4}, // These opcodes set, clear, and check (respectively) a flag that appears to do nothing at all. - {0xF8A0, "disable_bosswarp_option", "unknownF8A0", {}, F_V2_V4}, - {0xF8A1, "enable_bosswarp_option", "unknownF8A1", {}, F_V2_V4}, - {0xF8A2, "is_bosswarp_opt_disabled", "get_bosswarp_option", {W_REG}, F_V2_V4}, + {0xF8A0, {"disable_bosswarp_option", "unknownF8A0"}, {}, F_V2_V4}, + {0xF8A1, {"enable_bosswarp_option", "unknownF8A1"}, {}, F_V2_V4}, + {0xF8A2, {"is_bosswarp_opt_disabled", "get_bosswarp_option"}, {W_REG}, F_V2_V4}, // Loads the player's serial number into the "flag buffer", which is a 4-byte buffer that can be written to event // flags. It's not obvious why this can't just be done with get_serial_number and set_eventflag32. This opcode // loads 0 to the flag buffer on BB. - {0xF8A3, "load_serial_number_to_flag_buf", "init_online_key?", {}, F_V2_V4}, + {0xF8A3, {"load_serial_number_to_flag_buf", "init_online_key?"}, {}, F_V2_V4}, // Writes the flag buffer to event flags. regA specifies which event flag (the first of 4 consecutive flags). - {0xF8A4, "write_flag_buf_to_event_flags", "encrypt_gc_entry_auto", {R_REG}, F_V2_V4}, + {0xF8A4, {"write_flag_buf_to_event_flags", "encrypt_gc_entry_auto"}, {R_REG}, F_V2_V4}, // Like set_chat_callback, but without a filter string. The meaning of regsA is the same as for set_chat_callback. - {0xF8A5, "set_chat_callback_no_filter", "chat_detect", {{R_REG_SET_FIXED, 5}}, F_V2_V4}, + {0xF8A5, {"set_chat_callback_no_filter", "chat_detect"}, {{R_REG_SET_FIXED, 5}}, F_V2_V4}, // Creates a symbol chat collision object. See the description of TOSymbolchatColli in Map.cc for details on how // this object behaves and what these arguments mean. @@ -1825,28 +1821,28 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // PlayerSubordinates.hh). Note that this structure is not byteswapped properly, so GameCube quests that use // this opcode should use the big-endian version of the struct. (Practically, this means the first 32-bit field // and the following 4 16-bit fields must be byteswapped.) - {0xF8A6, "set_symbol_chat_collision", "symbol_chat_create", {{R_REG_SET_FIXED, 10}}, F_V2_V4}, + {0xF8A6, {"set_symbol_chat_collision", "symbol_chat_create"}, {{R_REG_SET_FIXED, 10}}, F_V2_V4}, // Sets the size that a player shrinks to when using the shrink opcode. regA specified the client ID. The actual // shrink size used is regsB[0] + (regsB[1] / regsB[2]). If regsB[2] is 0, the fractional part is considered to be // zero and not used. - {0xF8A7, "set_shrink_size", nullptr, {R_REG, {R_REG_SET_FIXED, 3}}, F_V2_V4}, + {0xF8A7, {"set_shrink_size"}, {R_REG, {R_REG_SET_FIXED, 3}}, F_V2_V4}, // Sets the amount by which techniques level up upon respawn in battle. - {0xF8A8, "ba_death_tech_level_up", "death_tech_lvl_up2", {I32}, F_V2_V4 | F_ARGS}, + {0xF8A8, {"ba_death_tech_level_up", "death_tech_lvl_up2"}, {I32}, F_V2_V4 | F_ARGS}, // Returns 1 if Vol Opt has been defeated in the current game/quest. - {0xF8A9, "vol_opt_is_dead", "volopt_is_dead", {W_REG}, F_V2_V4}, + {0xF8A9, {"vol_opt_is_dead", "volopt_is_dead"}, {W_REG}, F_V2_V4}, // Returns 1 if the local player has a challenge mode grave message. - {0xF8AA, "is_there_grave_message", nullptr, {W_REG}, F_V2_V4}, + {0xF8AA, {"is_there_grave_message"}, {W_REG}, F_V2_V4}, // Returns the local player's battle mode records. The values returned are the first 7 fields of the // PlayerRecordsBattle structure (see PlayerSubordinates.hh). These are: // regsA[0-3] = number of times placed 1st, 2nd, 3rd, and 4th, respectively // regsA[4] = number of disconnects // regsA[5-6] = unknown (TODO) - {0xF8AB, "get_ba_record", nullptr, {{W_REG_SET_FIXED, 7}}, F_V2_V4}, + {0xF8AB, {"get_ba_record"}, {{W_REG_SET_FIXED, 7}}, F_V2_V4}, // Returns the current player's challenge mode rank. Reads from the state corresponding to the current game mode // (that is, reads from online Ep1 records in online Ep1 challenge mode, reads from offline Ep1 records in offline @@ -1855,37 +1851,37 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 1 = B rank // 2 = A rank // 3 = S rank - {0xF8AC, "get_cmode_prize_rank", nullptr, {W_REG}, F_V2_V4}, + {0xF8AC, {"get_cmode_prize_rank"}, {W_REG}, F_V2_V4}, // Returns the number of players (in regA). Unlike get_number_of_players, this counts the number of objects that // have entity IDs assigned in the players' ID space, whereas get_number_of_players counds the number of TObjPlayer // objects. For all practical purposes, these should result in the same number. - {0xF8AD, "get_number_of_players2", "get_number_of_player2", {W_REG}, F_V2_V4}, + {0xF8AD, {"get_number_of_players2", "get_number_of_player2"}, {W_REG}, F_V2_V4}, // Returns 1 (in regA) if the current game has a nonempty name. The game name is set by command 8A from the server. - {0xF8AE, "party_has_name", nullptr, {W_REG}, F_V2_V4}, + {0xF8AE, {"party_has_name"}, {W_REG}, F_V2_V4}, // Returns 1 (in regA) if there is a chat message available (that is, if anyone has sent a chat message in the // current game). - {0xF8AF, "someone_has_spoken", nullptr, {W_REG}, F_V2_V4}, + {0xF8AF, {"someone_has_spoken"}, {W_REG}, F_V2_V4}, // Reads a 1-byte, 2-byte, or 4-byte value from the address (regB/valueB) and places it in regA. - {0xF8B0, "read1", nullptr, {W_REG, R_REG}, F_V2}, - {0xF8B0, "read1", nullptr, {W_REG, I32}, F_V3_V4 | F_ARGS}, - {0xF8B1, "read2", nullptr, {W_REG, R_REG}, F_V2}, - {0xF8B1, "read2", nullptr, {W_REG, I32}, F_V3_V4 | F_ARGS}, - {0xF8B2, "read4", nullptr, {W_REG, R_REG}, F_V2}, - {0xF8B2, "read4", nullptr, {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xF8B0, {"read1"}, {W_REG, R_REG}, F_V2}, + {0xF8B0, {"read1"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xF8B1, {"read2"}, {W_REG, R_REG}, F_V2}, + {0xF8B1, {"read2"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xF8B2, {"read4"}, {W_REG, R_REG}, F_V2}, + {0xF8B2, {"read4"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, // Writes a 1-byte, 2-byte, or 4-byte value from regB/valueB to the address (regA/valueA). On v2 and GC NTE, these // opcodes have a bug which makes them essentially useless: they ignore regB and instead write the value in regA to // the address in regA. - {0xF8B3, "write1", nullptr, {R_REG, R_REG}, F_V2}, - {0xF8B3, "write1", nullptr, {I32, I32}, F_V3_V4 | F_ARGS}, - {0xF8B4, "write2", nullptr, {R_REG, R_REG}, F_V2}, - {0xF8B4, "write2", nullptr, {I32, I32}, F_V3_V4 | F_ARGS}, - {0xF8B5, "write4", nullptr, {R_REG, R_REG}, F_V2}, - {0xF8B5, "write4", nullptr, {I32, I32}, F_V3_V4 | F_ARGS}, + {0xF8B3, {"write1"}, {R_REG, R_REG}, F_V2}, + {0xF8B3, {"write1"}, {I32, I32}, F_V3_V4 | F_ARGS}, + {0xF8B4, {"write2"}, {R_REG, R_REG}, F_V2}, + {0xF8B4, {"write2"}, {I32, I32}, F_V3_V4 | F_ARGS}, + {0xF8B5, {"write4"}, {R_REG, R_REG}, F_V2}, + {0xF8B5, {"write4"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Returns a bitmask of 5 different types of detectable hacking. This opcode only works on DCv2 - it crashes on all // other versions, since it tries to access memory at 8C007220, which is only a valid address on DC. The bits are: @@ -1896,65 +1892,65 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // buses reports power usage (standby or max) above 4 amps; always zero on non-DC systems // 0x08 = hacked item flag has been set (see implementation of are_rare_drops_allowed in ItemCreator.cc) // 0x10 = any bits in validation_flags in the character file are set (see PSOGCCharacterFile::Character) - {0xF8B6, "check_for_hacking", "is_mag_hacked", {W_REG}, F_V2_V4}, + {0xF8B6, {"check_for_hacking", "is_mag_hacked"}, {W_REG}, F_V2_V4}, // Challenge mode cannot be completed unless this many seconds have passed since the stage began. If not set or if // offline, 5 minutes is used as the threshold instead. - {0xF8B7, "chl_set_min_time_online", "unknownF8B7", {R_REG}, F_V2_V4}, + {0xF8B7, {"chl_set_min_time_online", "unknownF8B7"}, {R_REG}, F_V2_V4}, // Disables the challenge mode retry menu - {0xF8B8, "disable_retry_menu", "unknownF8B8", {}, F_V2_V4}, + {0xF8B8, {"disable_retry_menu", "unknownF8B8"}, {}, F_V2_V4}, // Shows the list of dead players in challenge mode - {0xF8B9, "chl_show_dead_player_list", "chl_death_recap", {}, F_V2_V4}, + {0xF8B9, {"chl_show_dead_player_list", "chl_death_recap"}, {}, F_V2_V4}, // Loads the Guild Card file creation time to the flag buffer. (See F8A3 and F8A4 for more details.) - {0xF8BA, "load_guild_card_file_creation_time_to_flag_buf", "encrypt_gc_entry_auto2", {}, F_V2_V4}, + {0xF8BA, {"load_guild_card_file_creation_time_to_flag_buf", "encrypt_gc_entry_auto2"}, {}, F_V2_V4}, // Behaves exactly the same as write_flag_buf_to_event_flags (F8A4). - {0xF8BB, "write_flag_buf_to_event_flags2", "unknownF8BB", {R_REG}, F_V2_V4}, + {0xF8BB, {"write_flag_buf_to_event_flags2", "unknownF8BB"}, {R_REG}, F_V2_V4}, // Returns (in regB) the Guild Card number of the player in the slot specified by regA. If there is no player in // that slot, returns FFFFFFFF. This opcode is only implemented on certain later versions of PC v2, and not on any // v3 or later versions. - {0xF8BC, "get_player_guild_card_number", nullptr, {R_REG, W_REG}, F_PC_V2}, + {0xF8BC, {"get_player_guild_card_number"}, {R_REG, W_REG}, F_PC_V2}, // Sets the current episode. Must be used in the start label. valueA should be 0 for Episode 1 (which is the // default), 1 for Episode 2, or 2 for Episode 4 (BB only). This opcode also resets the floor configuration, so it // will undo any effects of the map_designate family of opcodes. - {0xF8BC, "set_episode", nullptr, {I32}, F_V3_V4}, + {0xF8BC, {"set_episode"}, {I32}, F_V3_V4}, // This opcode returns (in regsB) the full symbol chat data for the symbol chat currently being said by the player // specified in regA. The symbol chat data is only returned for 120 frames (4 seconds) after the corresponding 6x07 // command is received; after that, this opcode will return a blank symbol chat instead. This opcode only works if // create_symbol_chat_monitor is run first. // This opcode is only implemented on certain later versions of PC v2, and not on any v3 or later versions. - {0xF8BD, "get_current_symbol_chat", nullptr, {R_REG, {W_REG_SET_FIXED, 15}}, F_PC_V2}, + {0xF8BD, {"get_current_symbol_chat"}, {R_REG, {W_REG_SET_FIXED, 15}}, F_PC_V2}, // This opcode is enables the usage of get_current_symbol_chat. This opcode is only implemented on certain later // versions of PC v2, and not on any v3 or later versions. - {0xF8BE, "create_symbol_chat_monitor", nullptr, {}, F_PC_V2}, + {0xF8BE, {"create_symbol_chat_monitor"}, {}, F_PC_V2}, // This opcode causes the client to immediately save the PSO______COM (system) and PSO______GUD (Guild Card) files // to disk. This opcode is only implemented on certain later versions of PC v2, and not on v3 or later versions. - {0xF8BF, "save_system_and_gc_files", nullptr, {}, F_PC_V2}, + {0xF8BF, {"save_system_and_gc_files"}, {}, F_PC_V2}, // Requests a file from the server by sending a D7 command. valueA specifies header.flag, strB is the file name (up // to 16 characters). This opcode works on Xbox, but the GBA opcodes do not, so it's ultimately not useful there. // This opcode does nothing on BB. - {0xF8C0, "file_dl_req", nullptr, {I32, CSTRING}, F_V3 | F_ARGS}, - {0xF8C0, "nop_F8C0", nullptr, {I32, CSTRING}, F_V4 | F_ARGS}, + {0xF8C0, {"file_dl_req"}, {I32, CSTRING}, F_V3 | F_ARGS}, + {0xF8C0, {"nop_F8C0"}, {I32, CSTRING}, F_V4 | F_ARGS}, // Returns the status of the download requested with file_dl_req. Return values (in regA): // 0 = failed (server sent a D7 command) // 1 = pending // 2 = complete - {0xF8C1, "get_dl_status", nullptr, {W_REG}, F_V3}, - {0xF8C1, "nop_F8C1", nullptr, {R_REG}, F_V4}, + {0xF8C1, {"get_dl_status"}, {W_REG}, F_V3}, + {0xF8C1, {"nop_F8C1"}, {R_REG}, F_V4}, // Prepares to load a GBA ROM from a previous file_dl_req opcode. Does nothing on Xbox and BB. - {0xF8C2, "prepare_gba_rom_from_download", "gba_unknown4?", {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, - {0xF8C2, "nop_F8C2", nullptr, {}, F_XB_V3 | F_V4}, + {0xF8C2, {"prepare_gba_rom_from_download", "gba_unknown4?"}, {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, + {0xF8C2, {"nop_F8C2"}, {}, F_XB_V3 | F_V4}, // Starts loading a GBA ROM to a connected GBA, or checks the status of a previous load request. One of // prepare_gba_rom_from_download or prepare_gba_rom_from_disk must be called before calling this, then this should @@ -1965,78 +1961,78 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 3 = in progress // 4 = complete // This opcode always returns 0 on Xbox, and does nothing (doesn't even affect regA) on BB. - {0xF8C3, "start_or_update_gba_joyboot", "get_gba_state?", {W_REG}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, - {0xF8C3, "return_0_F8C3", nullptr, {W_REG}, F_XB_V3}, - {0xF8C3, "nop_F8C3", nullptr, {R_REG}, F_V4}, + {0xF8C3, {"start_or_update_gba_joyboot", "get_gba_state?"}, {W_REG}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, + {0xF8C3, {"return_0_F8C3"}, {W_REG}, F_XB_V3}, + {0xF8C3, {"nop_F8C3"}, {R_REG}, F_V4}, // Shows the challenge mode result window in split-screen mode. Does nothing on BB. // regA = completion time in seconds, as returned by chl_get_timerecord - {0xF8C4, "congrats_msg_multi_cm", "unknownF8C4", {R_REG}, F_V3}, - {0xF8C4, "nop_F8C4", nullptr, {R_REG}, F_V4}, + {0xF8C4, {"congrats_msg_multi_cm", "unknownF8C4"}, {R_REG}, F_V3}, + {0xF8C4, {"nop_F8C4"}, {R_REG}, F_V4}, // Checks if the stage is done in offline challenge mode. Returns 1 if the stage is still in progress, or 0 if it's // completed or failed. - {0xF8C5, "stage_in_progress_multi_cm", "stage_end_multi_cm", {W_REG}, F_V3}, - {0xF8C5, "nop_F8C5", nullptr, {R_REG}, F_V4}, + {0xF8C5, {"stage_in_progress_multi_cm", "stage_end_multi_cm"}, {W_REG}, F_V3}, + {0xF8C5, {"nop_F8C5"}, {R_REG}, F_V4}, // Causes a fade to black, then exits the game. This is the same result as receiving a 6x73 command. - {0xF8C6, "qexit", "QEXIT", {}, F_V3_V4}, + {0xF8C6, {"qexit", "QEXIT"}, {}, F_V3_V4}, // Causes a player to perform an animation. // regA = client ID // regB = animation number (TODO: document these) - {0xF8C7, "use_animation", nullptr, {R_REG, R_REG}, F_V3_V4}, + {0xF8C7, {"use_animation"}, {R_REG, R_REG}, F_V3_V4}, // Stops an animation started with use_animation. // regA = client ID - {0xF8C8, "stop_animation", nullptr, {R_REG}, F_V3_V4}, + {0xF8C8, {"stop_animation"}, {R_REG}, F_V3_V4}, // Causes a player to run to a location, as 6x42 does. Sends 6x42. // regsA[0-2] = location (x, y, z as integers; y is ignored) // regsA[3] = client ID - {0xF8C9, "run_to_coord", nullptr, {{R_REG_SET_FIXED, 4}, R_REG}, F_V3_V4}, + {0xF8C9, {"run_to_coord"}, {{R_REG_SET_FIXED, 4}, R_REG}, F_V3_V4}, // Makes a player invincible, or removes their invincibility. // regA = client ID // regB = enable invicibility (1 = enable, 0 = disable) - {0xF8CA, "set_slot_invincible", nullptr, {R_REG, R_REG}, F_V3_V4}, + {0xF8CA, {"set_slot_invincible"}, {R_REG, R_REG}, F_V3_V4}, // Removes a player's invicibility. clear_slot_invincible rXX is equivalent to set_slot_invincible rXX, 0. // regA = client ID - {0xF8CB, "clear_slot_invincible", "set_slot_targetable?", {R_REG}, F_V3_V4}, + {0xF8CB, {"clear_slot_invincible", "set_slot_targetable?"}, {R_REG}, F_V3_V4}, // These opcodes inflict various status conditions on a player. In the case of Shifta/Deband/Jellen/Zalure, the // effective technique level is 21. // regA = client ID - {0xF8CC, "set_slot_poison", nullptr, {R_REG}, F_V3_V4}, - {0xF8CD, "set_slot_paralyze", nullptr, {R_REG}, F_V3_V4}, - {0xF8CE, "set_slot_shock", nullptr, {R_REG}, F_V3_V4}, - {0xF8CF, "set_slot_freeze", nullptr, {R_REG}, F_V3_V4}, - {0xF8D0, "set_slot_slow", nullptr, {R_REG}, F_V3_V4}, - {0xF8D1, "set_slot_confuse", nullptr, {R_REG}, F_V3_V4}, - {0xF8D2, "set_slot_shifta", nullptr, {R_REG}, F_V3_V4}, - {0xF8D3, "set_slot_deband", nullptr, {R_REG}, F_V3_V4}, - {0xF8D4, "set_slot_jellen", nullptr, {R_REG}, F_V3_V4}, - {0xF8D5, "set_slot_zalure", nullptr, {R_REG}, F_V3_V4}, + {0xF8CC, {"set_slot_poison"}, {R_REG}, F_V3_V4}, + {0xF8CD, {"set_slot_paralyze"}, {R_REG}, F_V3_V4}, + {0xF8CE, {"set_slot_shock"}, {R_REG}, F_V3_V4}, + {0xF8CF, {"set_slot_freeze"}, {R_REG}, F_V3_V4}, + {0xF8D0, {"set_slot_slow"}, {R_REG}, F_V3_V4}, + {0xF8D1, {"set_slot_confuse"}, {R_REG}, F_V3_V4}, + {0xF8D2, {"set_slot_shifta"}, {R_REG}, F_V3_V4}, + {0xF8D3, {"set_slot_deband"}, {R_REG}, F_V3_V4}, + {0xF8D4, {"set_slot_jellen"}, {R_REG}, F_V3_V4}, + {0xF8D5, {"set_slot_zalure"}, {R_REG}, F_V3_V4}, // Same as leti_fixed_camera, but takes floating-point arguments. // regsA[0-2] = camera location (x, y, z as floats) // regsA[3-5] = camera focus location (x, y, z as floats) - {0xF8D6, "fleti_fixed_camera", nullptr, {{R_REG_SET_FIXED, 6}}, F_V3_V4 | F_ARGS}, + {0xF8D6, {"fleti_fixed_camera"}, {{R_REG_SET_FIXED, 6}}, F_V3_V4 | F_ARGS}, // Sets the camera to follow the player at a fixed angle. // valueA = client ID // regsB[0-2] = camera angle (x, y, z as floats) - {0xF8D7, "fleti_locked_camera", nullptr, {I32, {R_REG_SET_FIXED, 3}}, F_V3_V4 | F_ARGS}, + {0xF8D7, {"fleti_locked_camera"}, {I32, {R_REG_SET_FIXED, 3}}, F_V3_V4 | F_ARGS}, // This opcode appears to be exactly the same as default_camera_pos. TODO: Is there any difference? - {0xF8D8, "default_camera_pos2", nullptr, {}, F_V3_V4}, + {0xF8D8, {"default_camera_pos2"}, {}, F_V3_V4}, // Enables a motion blur visual effect. - {0xF8D9, "set_motion_blur", nullptr, {}, F_V3_V4}, + {0xF8D9, {"set_motion_blur"}, {}, F_V3_V4}, // Enables a monochrome visual effect. - {0xF8DA, "set_screen_bw", "set_screen_b&w", {}, F_V3_V4}, + {0xF8DA, {"set_screen_bw", "set_screen_b&w"}, {}, F_V3_V4}, // Computes a point along a path composed of multiple control points. // valueA = number of control points @@ -2046,65 +2042,65 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsE[0-2] = result point (x, y, z as floats) // regsE[3] = result code (0 = failed, 1 = success) // labelF = control point entries (array of valueA VectorXYZTF structures) - {0xF8DB, "get_vector_from_path", "unknownF8DB", {I32, FLOAT32, FLOAT32, I32, {W_REG_SET_FIXED, 4}, {LABEL16, Arg::DataType::VECTOR4F_LIST}}, F_V3_V4 | F_ARGS}, + {0xF8DB, {"get_vector_from_path", "unknownF8DB"}, {I32, FLOAT32, FLOAT32, I32, {W_REG_SET_FIXED, 4}, {LABEL16, Arg::DataType::VECTOR4F_LIST}}, F_V3_V4 | F_ARGS}, // Same as npc_text, but only applies to a specific player slot. // valueA = client ID // valueB = situation number (same as for npc_text) // strC = string for NPC to say (up to 52 characters) - {0xF8DC, "npc_text_id", "NPC_action_string", {R_REG, R_REG, CSTRING_LABEL16}, F_V3_V4}, + {0xF8DC, {"npc_text_id", "NPC_action_string"}, {R_REG, R_REG, CSTRING_LABEL16}, F_V3_V4}, // Returns a bitmask of the buttons which are currently pressed or held on this frame. // regA = controller port number // regB = returned button flags - {0xF8DD, "get_held_buttons", "get_pad_cond", {R_REG, W_REG}, F_V3_V4}, + {0xF8DD, {"get_held_buttons", "get_pad_cond"}, {R_REG, W_REG}, F_V3_V4}, // Returns a bitmask of the buttons which were newly pressed on this frame. Buttons which were pressed on previous // frames and still held down are not returned. Same arguments as get_held_buttons. - {0xF8DE, "get_pressed_buttons", "get_button_cond", {R_REG, W_REG}, F_V3_V4}, + {0xF8DE, {"get_pressed_buttons", "get_button_cond"}, {R_REG, W_REG}, F_V3_V4}, // Freezes enemies and makes them untargetable, or unfreezes them and makes them targetable again. Internally, this // toggles a flag that disables updates for every child object of TL_04. - {0xF8DF, "toggle_freeze_enemies", "freeze_enemies", {}, F_V3_V4}, + {0xF8DF, {"toggle_freeze_enemies", "freeze_enemies"}, {}, F_V3_V4}, // Unfreezes enemies and makes them targetable again. - {0xF8E0, "unfreeze_enemies", nullptr, {}, F_V3_V4}, + {0xF8E0, {"unfreeze_enemies"}, {}, F_V3_V4}, // Freezes (almost) everything, or unfreezes (almost) everything. Internally, this toggles the disable-updates flag // for every child of all root objects except TL_SU, TL_00, TL_CAMERA, and TL_01. - {0xF8E1, "toggle_freeze_everything", "freeze_everything", {}, F_V3_V4}, + {0xF8E1, {"toggle_freeze_everything", "freeze_everything"}, {}, F_V3_V4}, // Unfreezes (almost) everything, if toggle_freeze_everything has been executed before. - {0xF8E2, "unfreeze_everything", nullptr, {}, F_V3_V4}, + {0xF8E2, {"unfreeze_everything"}, {}, F_V3_V4}, // Sets a player's HP or TP to their maximum HP or TP. // regA = client ID - {0xF8E3, "restore_hp", nullptr, {R_REG}, F_V3_V4}, - {0xF8E4, "restore_tp", nullptr, {R_REG}, F_V3_V4}, + {0xF8E3, {"restore_hp"}, {R_REG}, F_V3_V4}, + {0xF8E4, {"restore_tp"}, {R_REG}, F_V3_V4}, // Closes a chat bubble for a player, if one is open. // regA = client ID - {0xF8E5, "close_chat_bubble", nullptr, {R_REG}, F_V3_V4}, + {0xF8E5, {"close_chat_bubble"}, {R_REG}, F_V3_V4}, // Moves a dynamic collision object. // regA = object token (returned by set_obj_param, etc.) // regsB[0-2] = location (x, y, z as integers) - {0xF8E6, "move_coords_object", nullptr, {R_REG, {R_REG_SET_FIXED, 3}}, F_V3_V4}, + {0xF8E6, {"move_coords_object"}, {R_REG, {R_REG_SET_FIXED, 3}}, F_V3_V4}, // These are the same as their counterparts without _ex, but these also return the object ID (which is distinct // from the object token) in regB. There is a bug in set_obj_param_ex - the object token is not returned anywhere, // and only the object ID is returned in regB. - {0xF8E7, "at_coords_call_ex", nullptr, {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4}, - {0xF8E8, "at_coords_talk_ex", nullptr, {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4}, - {0xF8E9, "npc_coords_call_ex", "walk_to_coord_call_ex", {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4}, - {0xF8EA, "party_coords_call_ex", "col_npcinr_ex", {{R_REG_SET_FIXED, 6}, W_REG}, F_V3_V4}, - {0xF8EB, "set_obj_param_ex", "unknownF8EB", {{R_REG_SET_FIXED, 6}, W_REG}, F_V3_V4}, - {0xF8EC, "npc_check_straggle_ex", "col_plinaw_ex", {{R_REG_SET_FIXED, 9}, W_REG}, F_V3_V4}, + {0xF8E7, {"at_coords_call_ex"}, {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4}, + {0xF8E8, {"at_coords_talk_ex"}, {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4}, + {0xF8E9, {"npc_coords_call_ex", "walk_to_coord_call_ex"}, {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4}, + {0xF8EA, {"party_coords_call_ex", "col_npcinr_ex"}, {{R_REG_SET_FIXED, 6}, W_REG}, F_V3_V4}, + {0xF8EB, {"set_obj_param_ex", "unknownF8EB"}, {{R_REG_SET_FIXED, 6}, W_REG}, F_V3_V4}, + {0xF8EC, {"check_npc_straggle_ex", "npc_check_straggle_ex", "col_plinaw_ex"}, {{R_REG_SET_FIXED, 9}, W_REG}, F_V3_V4}, // Returns 1 if the player is doing certain animations. (TODO: Which ones?) // regA = client ID // regB = returned value - {0xF8ED, "animation_check", nullptr, {R_REG, W_REG}, F_V3_V4}, + {0xF8ED, {"animation_check"}, {R_REG, W_REG}, F_V3_V4}, // Specifies which image to use for the image board in Pioneer 2 (if placed). Only one image may be loaded at a // time. Images must be square and dimensions must be a power of two, similar to the B9 command on Ep3. On DC or @@ -2112,16 +2108,16 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // an XVR file. // regA = decompressed size // labelB = label containing PRS-compressed image file data - {0xF8EE, "call_image_data", nullptr, {I32, {LABEL16, Arg::DataType::IMAGE_DATA}}, F_V3_V4 | F_ARGS}, + {0xF8EE, {"call_image_data"}, {I32, {LABEL16, Arg::DataType::IMAGE_DATA}}, F_V3_V4 | F_ARGS}, // This opcode does nothing on all versions where it's implemented. - {0xF8EF, "nop_F8EF", "unknownF8EF", {}, F_V3_V4}, + {0xF8EF, {"nop_F8EF", "unknownF8EF"}, {}, F_V3_V4}, // Disables or enables the background music on Pioneer 2. If the BGM has not been overridden with create_bgmctrl // and set_bgm, the music will resume at the points where it would normally change (e.g. entering or leaving the // shop area). turn_off_bgm_p2 may be repeated every frame to avoid this consequence. - {0xF8F0, "turn_off_bgm_p2", nullptr, {}, F_V3_V4}, - {0xF8F1, "turn_on_bgm_p2", nullptr, {}, F_V3_V4}, + {0xF8F0, {"turn_off_bgm_p2"}, {}, F_V3_V4}, + {0xF8F1, {"turn_on_bgm_p2"}, {}, F_V3_V4}, // Computes a point along a Bezier curve defined by a sequence of vectors. This is similar to get_vector_from_path, // but results in a smoother curve. Arguments: @@ -2132,59 +2128,59 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsE[0-2] = result point (x, y, z as floats) // regsE[3] = result code (0 = failed, 1 = success) // labelF = control point entries (array of valueA VectorXYZTF structures) - {0xF8F2, "compute_bezier_curve_point", "load_unk_data", {I32, FLOAT32, FLOAT32, I32, {W_REG_SET_FIXED, 4}, {LABEL16, Arg::DataType::VECTOR4F_LIST}}, F_V3_V4 | F_ARGS}, + {0xF8F2, {"compute_bezier_curve_point", "load_unk_data"}, {I32, FLOAT32, FLOAT32, I32, {W_REG_SET_FIXED, 4}, {LABEL16, Arg::DataType::VECTOR4F_LIST}}, F_V3_V4 | F_ARGS}, // Creates a timed particle effect. Like the particle opcode, but the location duration are floats. // regsA[0-2] = location (x, y, z as floats) // valueB = effect type // valueC = duration as float (in frames; 30 frames/sec) - {0xF8F3, "particle2", nullptr, {{R_REG_SET_FIXED, 3}, I32, FLOAT32}, F_V3_V4 | F_ARGS}, + {0xF8F3, {"particle2"}, {{R_REG_SET_FIXED, 3}, I32, FLOAT32}, F_V3_V4 | F_ARGS}, // Converts the integer in regB into a float in regA. - {0xF901, "dec2float", nullptr, {W_REG, R_REG}, F_V3_V4}, + {0xF901, {"dec2float"}, {W_REG, R_REG}, F_V3_V4}, // Converts the float in regB into an integer in regA. - {0xF902, "float2dec", nullptr, {W_REG, R_REG}, F_V3_V4}, + {0xF902, {"float2dec"}, {W_REG, R_REG}, F_V3_V4}, // These are the same as let and leti. Nominally regB/valueB should be a float, but the implementation treats it as // an int (which is still correct, since the float is already encoded before the handler function is called). - {0xF903, "flet", "floatlet", {W_REG, R_REG}, F_V3_V4}, - {0xF904, "fleti", "floati", {W_REG, FLOAT32}, F_V3_V4}, + {0xF903, {"flet", "floatlet"}, {W_REG, R_REG}, F_V3_V4}, + {0xF904, {"fleti", "floati"}, {W_REG, FLOAT32}, F_V3_V4}, // regA += regB (or valueB), as floats - {0xF908, "fadd", nullptr, {W_REG, R_REG}, F_V3_V4}, - {0xF909, "faddi", nullptr, {W_REG, FLOAT32}, F_V3_V4}, + {0xF908, {"fadd"}, {W_REG, R_REG}, F_V3_V4}, + {0xF909, {"faddi"}, {W_REG, FLOAT32}, F_V3_V4}, // regA -= regB (or valueB), as floats - {0xF90A, "fsub", nullptr, {W_REG, R_REG}, F_V3_V4}, - {0xF90B, "fsubi", nullptr, {W_REG, FLOAT32}, F_V3_V4}, + {0xF90A, {"fsub"}, {W_REG, R_REG}, F_V3_V4}, + {0xF90B, {"fsubi"}, {W_REG, FLOAT32}, F_V3_V4}, // regA *= regB (or valueB), as floats - {0xF90C, "fmul", nullptr, {W_REG, R_REG}, F_V3_V4}, - {0xF90D, "fmuli", nullptr, {W_REG, FLOAT32}, F_V3_V4}, + {0xF90C, {"fmul"}, {W_REG, R_REG}, F_V3_V4}, + {0xF90D, {"fmuli"}, {W_REG, FLOAT32}, F_V3_V4}, // regA /= regB (or valueB), as floats - {0xF90E, "fdiv", nullptr, {W_REG, R_REG}, F_V3_V4}, - {0xF90F, "fdivi", nullptr, {W_REG, FLOAT32}, F_V3_V4}, + {0xF90E, {"fdiv"}, {W_REG, R_REG}, F_V3_V4}, + {0xF90F, {"fdivi"}, {W_REG, FLOAT32}, F_V3_V4}, // Returns the number of times a player has ever died - not just in the current quest/game/session! // regA = client ID // regB = returned death count - {0xF910, "get_total_deaths", "get_unknown_count?", {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, + {0xF910, {"get_total_deaths", "get_unknown_count?"}, {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, // Returns the stack size of the specified item in the player's inventory. // regsA[0] = client ID // regsA[1-3] = item.data1[0-2] // regB = returned amount of item present in player's inventory, or 0 if not found - {0xF911, "get_stackable_item_count", "get_item_count", {{R_REG_SET_FIXED, 4}, W_REG}, F_V3_V4}, + {0xF911, {"get_stackable_item_count", "get_item_count"}, {{R_REG_SET_FIXED, 4}, W_REG}, F_V3_V4}, // Freezes a character and hides their equips, or does the opposite. Internally, this toggles the disable-update // flag on TL_03. - {0xF912, "toggle_freeze_and_hide_equip", "freeze_and_hide_equip", {}, F_V3_V4}, + {0xF912, {"toggle_freeze_and_hide_equip", "freeze_and_hide_equip"}, {}, F_V3_V4}, // Undoes the effect of toggle_freeze_and_hide_equip, if it has been run an odd number of times. (Otherwise, does // nothing.) - {0xF913, "thaw_and_show_equip", nullptr, {}, F_V3_V4}, + {0xF913, {"thaw_and_show_equip"}, {}, F_V3_V4}, // These opcodes are generally used in the following sequence: // set_palettex_callback r:client_id, callback_label @@ -2197,16 +2193,16 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // Arguments: // regA = client ID // labelB (for set_palettex_callback only) = function to call when X button is pressed - {0xF914, "set_palettex_callback", "set_paletteX_callback", {CLIENT_ID, SCRIPT16}, F_V3_V4 | F_ARGS}, - {0xF915, "activate_palettex", "activate_paletteX", {CLIENT_ID}, F_V3_V4 | F_ARGS}, - {0xF916, "enable_palettex", "enable_paletteX", {CLIENT_ID}, F_V3_V4 | F_ARGS}, - {0xF917, "restore_palettex", "restore_paletteX", {CLIENT_ID}, F_V3_V4 | F_ARGS}, - {0xF918, "disable_palettex", "disable_paletteX", {CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0xF914, {"set_palettex_callback", "set_paletteX_callback"}, {CLIENT_ID, SCRIPT16}, F_V3_V4 | F_ARGS}, + {0xF915, {"activate_palettex", "activate_paletteX"}, {CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0xF916, {"enable_palettex", "enable_paletteX"}, {CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0xF917, {"restore_palettex", "restore_paletteX"}, {CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0xF918, {"disable_palettex", "disable_paletteX"}, {CLIENT_ID}, F_V3_V4 | F_ARGS}, // Checks if activate_palettex has been run for a player. // regA = client ID // regB = returned flag (0 = not overridden, 1 = overridden) - {0xF919, "get_palettex_activated", "get_paletteX_activated", {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, + {0xF919, {"get_palettex_activated", "get_paletteX_activated"}, {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, // Checks if activate_palettex has been run for a player. // regA = client ID @@ -2215,33 +2211,33 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // The ignored argument here seems to be a bug. At least one official quest uses this opcode preceded by only two // arg_push opcodes, implying that it was intended to take two arguments, but the client really does only use // quest_arg_list[0] and quest_arg_list[2]. - {0xF91A, "get_palettex_enabled", "get_unknown_paletteX_status?", {CLIENT_ID, I32, W_REG}, F_V3_V4 | F_ARGS}, + {0xF91A, {"get_palettex_enabled", "get_unknown_paletteX_status?"}, {CLIENT_ID, I32, W_REG}, F_V3_V4 | F_ARGS}, // Disables/enables movement for a player. Unlike disable_movement1, this does not send 6x2C. // regA = client ID - {0xF91B, "disable_movement2", nullptr, {CLIENT_ID}, F_V3_V4 | F_ARGS}, - {0xF91C, "enable_movement2", nullptr, {CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0xF91B, {"disable_movement2"}, {CLIENT_ID}, F_V3_V4 | F_ARGS}, + {0xF91C, {"enable_movement2"}, {CLIENT_ID}, F_V3_V4 | F_ARGS}, // Returns the local player's play time in seconds. - {0xF91D, "get_time_played", nullptr, {W_REG}, F_V3_V4}, + {0xF91D, {"get_time_played"}, {W_REG}, F_V3_V4}, // Returns the number of Guild Cards saved to the Guild Card file. - {0xF91E, "get_guildcard_total", nullptr, {W_REG}, F_V3_V4}, + {0xF91E, {"get_guildcard_total"}, {W_REG}, F_V3_V4}, // Returns the amount of Meseta the player has in both their inventory and bank. // regsA[0] = returned Meseta amount in inventory // regsA[1] = returned Meseta amount in bank - {0xF91F, "get_slot_meseta", nullptr, {{W_REG_SET_FIXED, 2}}, F_V3_V4}, + {0xF91F, {"get_slot_meseta"}, {{W_REG_SET_FIXED, 2}}, F_V3_V4}, // Returns a player's level. // valueA = client ID // regB = returned level - {0xF920, "get_player_level", nullptr, {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, + {0xF920, {"get_player_level"}, {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, // Returns a player's section ID. // valueA = client ID // regB = returned section ID (see name_to_section_id in StaticGameData.cc) - {0xF921, "get_section_id", "get_Section_ID", {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, + {0xF921, {"get_section_id", "get_Section_ID"}, {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, // Returns a player's maximum and current HP and TP. // valueA = client ID @@ -2250,55 +2246,55 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsB[2] = returned maximum TP // regsB[3] = returned current TP // If there's no player in the given slot, the returned values are all FFFFFFFF. - {0xF922, "get_player_hp_tp", "get_player_hp", {CLIENT_ID, {W_REG_SET_FIXED, 4}}, F_V3_V4 | F_ARGS}, + {0xF922, {"get_player_hp_tp", "get_player_hp"}, {CLIENT_ID, {W_REG_SET_FIXED, 4}}, F_V3_V4 | F_ARGS}, // Returns the floor and room ID of the given player. // valueA = client ID // regsB[0] = returned floor number // regsB[1] = returned room number // If there's no player in the given slot, the returned values are both FFFFFFFF. - {0xF923, "get_player_room", "get_floor_number", {CLIENT_ID, {W_REG_SET_FIXED, 2}}, F_V3_V4 | F_ARGS}, + {0xF923, {"get_player_room", "get_floor_number"}, {CLIENT_ID, {W_REG_SET_FIXED, 2}}, F_V3_V4 | F_ARGS}, // Checks if each player (individually) is near the given location. // regsA[0-1] = location (x, z as integers; y not included) // regsA[2] = radius as integer // regsB[0-3] = results for each player (0 = player not present or outside radius; 1 = player within radius) - {0xF924, "get_coord_player_detect", nullptr, {{R_REG_SET_FIXED, 3}, {W_REG_SET_FIXED, 4}}, F_V3_V4}, + {0xF924, {"get_coord_player_detect"}, {{R_REG_SET_FIXED, 3}, {W_REG_SET_FIXED, 4}}, F_V3_V4}, // Reads the value of a quest counter. // valueA = counter index (0-15) // regB = returned value - {0xF925, "read_counter", "read_global_flag", {I32, W_REG}, F_V3_V4 | F_ARGS}, + {0xF925, {"read_counter", "read_global_flag"}, {I32, W_REG}, F_V3_V4 | F_ARGS}, // Writes a value to a quest counter. // valueA = counter index (0-15) // valueB = value to write - {0xF926, "write_counter", "write_global_flag", {I32, I32}, F_V3_V4 | F_ARGS}, + {0xF926, {"write_counter", "write_global_flag"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Checks if an item exists in the local player's bank. The matching logic is the same as in find_inventory_item. // regsA[0-2] = item.data1[0-2] // regsA[3] = item.data1[4] // regB = 1 if item was found, 0 if not - {0xF927, "find_bank_item", "item_check_bank", {{R_REG_SET_FIXED, 4}, W_REG}, F_V3_V4}, + {0xF927, {"find_bank_item", "item_check_bank"}, {{R_REG_SET_FIXED, 4}, W_REG}, F_V3_V4}, // Returns whether each player is present. // regsA[0-3] = returned flags (for each player: 0 if absent, 1 if present) - {0xF928, "get_players_present", "floor_player_detect", {{W_REG_SET_FIXED, 4}}, F_V3_V4}, + {0xF928, {"get_players_present", "floor_player_detect"}, {{W_REG_SET_FIXED, 4}}, F_V3_V4}, // Prepares to load a GBA ROM from a local GSL file. Does nothing on Xbox and BB. - {0xF929, "prepare_gba_rom_from_disk", "read_disk_file?", {CSTRING}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3 | F_ARGS}, - {0xF929, "nop_F929", nullptr, {CSTRING}, F_XB_V3 | F_V4 | F_ARGS}, + {0xF929, {"prepare_gba_rom_from_disk", "read_disk_file?"}, {CSTRING}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3 | F_ARGS}, + {0xF929, {"nop_F929"}, {CSTRING}, F_XB_V3 | F_V4 | F_ARGS}, // Opens a window for the player to choose an item. - {0xF92A, "open_pack_select", nullptr, {}, F_V3_V4}, + {0xF92A, {"open_pack_select"}, {}, F_V3_V4}, // Prevents the player from choosing a specific item in the item select window opened by open_pack_select. // Generally used for subsequent item choices when trading multiple items. // regA = item ID - {0xF92B, "prevent_item_select", "item_select", {R_REG}, F_V3_V4}, + {0xF92B, {"prevent_item_select", "item_select"}, {R_REG}, F_V3_V4}, // Returns the item chosen by the player in an open_pack_select window, or 0xFFFFFFFF if they canceled it. - {0xF92C, "get_chosen_item_id", "get_item_id", {W_REG}, F_V3_V4}, + {0xF92C, {"get_chosen_item_id", "get_item_id"}, {W_REG}, F_V3_V4}, // Adds a color overlay on the player's screen. The overlay fades in linearly over the given number of frames. The // overlay is not deleted until the player changes areas or leaves the game, but it can be overwritten with another @@ -2306,20 +2302,20 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // text messages from the server, but is above everything else. // valueA, valueB, valueC, valueD = red, green, blue, alpha components of color (00-FF each) // valueE = fade speed (number of frames; 30 frames/sec) - {0xF92D, "add_color_overlay", "color_change", {I32, I32, I32, I32, I32}, F_V3_V4 | F_ARGS}, + {0xF92D, {"add_color_overlay", "color_change"}, {I32, I32, I32, I32, I32}, F_V3_V4 | F_ARGS}, // Sends a statistic to the server via the AA command. The server is expected to respond with an AB command // containing one of the label indexes set by prepare_statistic. The arguments to this opcode are sent verbatim in // the params field of the AA command; the other fields in the AA command come from prepare_statistic. - {0xF92E, "send_statistic", "send_statistic?", {I32, I32, I32, I32, I32, I32, I32, I32}, F_V3_V4 | F_ARGS}, + {0xF92E, {"send_statistic", "send_statistic?"}, {I32, I32, I32, I32, I32, I32, I32, I32}, F_V3_V4 | F_ARGS}, // Enables patching a GBA ROM before sending it to the GBA. valueA is ignored. If valueB is 1, the game writes two // 32-bit values to the GBA ROM data before sending it: // At offset 0x2C0, writes system_file->creation_timestamp // At offset 0x2C4, writes current_time + rand(0, 100) // current_time is in seconds since 00:00:00 on 1 January 2000. On Xbox and BB, this opcode does nothing. - {0xF92F, "gba_write_identifiers", "gba_unknown5", {I32, I32}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3 | F_ARGS}, - {0xF92F, "nop_F92F", nullptr, {I32, I32}, F_XB_V3 | F_V4 | F_ARGS}, + {0xF92F, {"gba_write_identifiers", "gba_unknown5"}, {I32, I32}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3 | F_ARGS}, + {0xF92F, {"nop_F92F"}, {I32, I32}, F_XB_V3 | F_V4 | F_ARGS}, // Shows a message in a chat window. Can be closed with the winend opcode. // valueA = X position on screen @@ -2328,23 +2324,23 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueD = window height // valueE = window style (0 = white window, 1 = chat box, anything else = no window, just text) // strF = initial message - {0xF930, "chat_box", nullptr, {I32, I32, I32, I32, I32, CSTRING}, F_V3_V4 | F_ARGS}, + {0xF930, {"chat_box"}, {I32, I32, I32, I32, I32, CSTRING}, F_V3_V4 | F_ARGS}, // Shows a chat bubble linked to the given entity ID. // valueA = entity (client ID, 0x1000 + enemy ID, or 0x4000 + object ID) // strB = message - {0xF931, "chat_bubble", nullptr, {I32, CSTRING}, F_V3_V4 | F_ARGS}, + {0xF931, {"chat_bubble"}, {I32, CSTRING}, F_V3_V4 | F_ARGS}, // Sets the episode to be loaded the next time an area is loaded (e.g. by the player changing floors). regA is the // same as for set_episode. Like set_episode, it resets the floor configuration to the defaults, but this happens // at the time the player changes floors, not when the opcode is executed. - {0xF932, "delayed_switch_episode", "set_episode2", {R_REG}, F_V3_V4}, + {0xF932, {"delayed_switch_episode", "set_episode2"}, {R_REG}, F_V3_V4}, // Sets the rank prizes in offline challenge mode. // regsA[0] = rank (unusual value order: 0 = S, 1 = B, 2 = A) // regsA[1-6] = item.data1[0-5] - {0xF933, "item_create_multi_cm", "unknownF933", {{R_REG_SET_FIXED, 7}}, F_V3}, - {0xF933, "nop_F933", nullptr, {{R_REG_SET_FIXED, 7}}, F_V4}, + {0xF933, {"item_create_multi_cm", "unknownF933"}, {{R_REG_SET_FIXED, 7}}, F_V3}, + {0xF933, {"nop_F933"}, {{R_REG_SET_FIXED, 7}}, F_V4}, // Shows a scrolling text window. // valueA = X position on screen @@ -2355,31 +2351,31 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueF = scrolling speed // regG = set to 1 when message has entirely scrolled past // strH = message - {0xF934, "scroll_text", nullptr, {I32, I32, I32, I32, I32, FLOAT32, W_REG, CSTRING}, F_V3_V4 | F_ARGS}, + {0xF934, {"scroll_text"}, {I32, I32, I32, I32, I32, FLOAT32, W_REG, CSTRING}, F_V3_V4 | F_ARGS}, // Creates, destroys, or updates the GBA loading progress bar (same as the quest download progress bar). These // opcodes do nothing on Xbox and BB. - {0xF935, "gba_create_dl_graph", "gba_unknown1", {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, - {0xF935, "nop_F935", nullptr, {}, F_XB_V3 | F_V4}, - {0xF936, "gba_destroy_dl_graph", "gba_unknown2", {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, - {0xF936, "nop_F936", nullptr, {}, F_XB_V3 | F_V4}, - {0xF937, "gba_update_dl_graph", "gba_unknown3", {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, - {0xF937, "nop_F937", nullptr, {}, F_XB_V3 | F_V4}, + {0xF935, {"gba_create_dl_graph", "gba_unknown1"}, {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, + {0xF935, {"nop_F935"}, {}, F_XB_V3 | F_V4}, + {0xF936, {"gba_destroy_dl_graph", "gba_unknown2"}, {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, + {0xF936, {"nop_F936"}, {}, F_XB_V3 | F_V4}, + {0xF937, {"gba_update_dl_graph", "gba_unknown3"}, {}, F_GC_V3 | F_GC_EP3TE | F_GC_EP3}, + {0xF937, {"nop_F937"}, {}, F_XB_V3 | F_V4}, // Damages a player. // regA = client ID // regB = amount - {0xF938, "add_damage_to", "add_damage_to?", {I32, I32}, F_V3_V4 | F_ARGS}, + {0xF938, {"add_damage_to", "add_damage_to?"}, {I32, I32}, F_V3_V4 | F_ARGS}, // Deletes an item from the local player's inventory. Like item_delete, but doesn't return anything. // valueA = item ID - {0xF939, "item_delete_noreturn", "item_delete_slot", {I32}, F_V3_V4 | F_ARGS}, + {0xF939, {"item_delete_noreturn", "item_delete_slot"}, {I32}, F_V3_V4 | F_ARGS}, // Returns the item data for an item chosen with open_pack_select. // valueA = item ID // regsB[0-11] = returned item.data1[0-11] // If the item doesn't exist, regsB[0] is set to 0xFFFFFFFF and the rest of regsB are unaffected. - {0xF93A, "get_item_info", nullptr, {ITEM_ID, {W_REG_SET_FIXED, 12}}, F_V3_V4 | F_ARGS}, + {0xF93A, {"get_item_info"}, {ITEM_ID, {W_REG_SET_FIXED, 12}}, F_V3_V4 | F_ARGS}, // Wraps an item in the player's inventory. The specified item is deleted and a new one is created with the wrapped // flag set. The new item has a different item ID, which is not returned. Sends 6x29, then 6x26 if deleting the @@ -2387,26 +2383,26 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // this to be used on BB, since the behavior wasn't changed - normally, item-related functions should be done by // the server on BB, as the following opcode does. // valueA = item ID - {0xF93B, "wrap_item", "item_packing1", {ITEM_ID}, F_V3_V4 | F_ARGS}, + {0xF93B, {"wrap_item", "item_packing1"}, {ITEM_ID}, F_V3_V4 | F_ARGS}, // Like wrap_item, but also sets the present color. On BB, sends 6xD6, and the server should respond with the // sequence of 6x29, 6x26 (if it was equipped), then 6xBE. // valueA = item ID // valueB = present color (0-15; high 4 bits are masked out) - {0xF93C, "wrap_item_with_color", "item_packing2", {ITEM_ID, I32}, F_V3_V4 | F_ARGS}, + {0xF93C, {"wrap_item_with_color", "item_packing2"}, {ITEM_ID, I32}, F_V3_V4 | F_ARGS}, // Returns the local player's language setting. For values, see name_for_language in StaticGameData.cc. - {0xF93D, "get_lang_setting", "get_lang_setting?", {W_REG}, F_V3_V4 | F_ARGS}, + {0xF93D, {"get_lang_setting", "get_lang_setting?"}, {W_REG}, F_V3_V4 | F_ARGS}, // Sets some values to be sent to the server with send_statistic. // valueA = stat_id (used in send_statistic); this is set to the quest number from the header when the quest // starts, but that is overwritten by prepare_statistic // labelB = label1 (used in send_statistic) // labelC = label2 (used in send_statistic) - {0xF93E, "prepare_statistic", "prepare_statistic?", {I32, SCRIPT32, SCRIPT32}, F_V3_V4 | F_ARGS}, + {0xF93E, {"prepare_statistic", "prepare_statistic?"}, {I32, SCRIPT32, SCRIPT32}, F_V3_V4 | F_ARGS}, // Enables use of the check_for_keyword opcode. - {0xF93F, "enable_keyword_detect", "keyword_detect", {}, F_V3_V4}, + {0xF93F, {"enable_keyword_detect", "keyword_detect"}, {}, F_V3_V4}, // Checks if a word was said by a specific player. All of the same behaviors surrounding the message string apply // as for set_chat_callback. Generally, this should be called every frame until the keyword is found (or the quest @@ -2414,12 +2410,12 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regA = result (0 = word not said, 1 = word said) // valueB = client ID // strC = string to match (same semantics as for set_chat_callback) - {0xF940, "check_for_keyword", "keyword", {W_REG, CLIENT_ID, CSTRING}, F_V3_V4 | F_ARGS}, + {0xF940, {"check_for_keyword", "keyword"}, {W_REG, CLIENT_ID, CSTRING}, F_V3_V4 | F_ARGS}, // Returns a player's Guild Card number. // valueA = client ID // regB = returned Guild Card number - {0xF941, "get_guildcard_num", nullptr, {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, + {0xF941, {"get_guildcard_num"}, {CLIENT_ID, W_REG}, F_V3_V4 | F_ARGS}, // Returns the last symbol chat that a player said (at least, since the capture buffer was created). Use // create_symbol_chat_capture_buffer before using this opcode. The data is returned as raw values in a sequence of @@ -2427,44 +2423,44 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // it is SymbolChat (see SymbolChatT in PlayerSubordinates.hh for details). // valueA = client ID // regsB[0-14] = returned symbol chat data - {0xF942, "get_recent_symbol_chat", "symchat_unknown", {I32, {W_REG_SET_FIXED, 15}}, F_V3_V4 | F_ARGS}, + {0xF942, {"get_recent_symbol_chat", "symchat_unknown"}, {I32, {W_REG_SET_FIXED, 15}}, F_V3_V4 | F_ARGS}, // Creates the capture buffer required by get_recent_symbol_chat. - {0xF943, "create_symbol_chat_capture_buffer", "unknownF943", {}, F_V3_V4}, + {0xF943, {"create_symbol_chat_capture_buffer", "unknownF943"}, {}, F_V3_V4}, // Checks whether an item is stackable. // valueA = item ID // regB = result (0 = not stackable, 1 = stackable, FFFFFFFF = not found) - {0xF944, "get_item_stackability", "get_wrap_status", {ITEM_ID, W_REG}, F_V3_V4 | F_ARGS}, + {0xF944, {"get_item_stackability", "get_wrap_status"}, {ITEM_ID, W_REG}, F_V3_V4 | F_ARGS}, // Sets the floor where the players will start. This generally should be used in the start label (where // map_designate, etc. are used). This is exactly the same as ba_initial_floor, except the floor numbers are not // remapped: in Episode 1, 0 means Pioneer 2, 1 means Forest 1, etc. The warning on ba_initial_floor about common // and rare item tables also applies to this opcode. - {0xF945, "initial_floor", nullptr, {I32}, F_V3_V4 | F_ARGS}, + {0xF945, {"initial_floor"}, {I32}, F_V3_V4 | F_ARGS}, // Computes the sine, cosine, or tangent of the input angle. Angles are in the range [0, 65535]. // regA = result value // valueB = input angle - {0xF946, "sin", nullptr, {W_REG, I32}, F_V3_V4 | F_ARGS}, - {0xF947, "cos", nullptr, {W_REG, I32}, F_V3_V4 | F_ARGS}, - {0xF948, "tan", nullptr, {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xF946, {"sin"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xF947, {"cos"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, + {0xF948, {"tan"}, {W_REG, I32}, F_V3_V4 | F_ARGS}, // Computes the arctangent of the input ratio. // regA = result (integer angle, 0-65535; C expression: (int)((atan2(valueB, valueC) * 65536.0) / (2 * M_PI))) // valueB = numerator as float // valueC = denominator as float - {0xF949, "atan2_int", "atan", {W_REG, FLOAT32, FLOAT32}, F_V3_V4 | F_ARGS}, + {0xF949, {"atan2_int", "atan"}, {W_REG, FLOAT32, FLOAT32}, F_V3_V4 | F_ARGS}, // Sets regA to 1 if Olga Flow has been defeated, or 0 otherwise. - {0xF94A, "olga_flow_is_dead", "olga_is_dead", {W_REG}, F_V3_V4}, + {0xF94A, {"olga_flow_is_dead", "olga_is_dead"}, {W_REG}, F_V3_V4}, // Creates a timed particle effect. Similar to the particle opcode, but the created particles have no draw // distance, so they are visible from very far away. // regsA[0-2] = location (x, y, z as integers) // regsA[3] = effect type // regsA[4] = duration (in frames; 30 frames/sec) - {0xF94B, "particle_effect_nc", "particle3", {{R_REG_SET_FIXED, 5}}, F_V3_V4}, + {0xF94B, {"particle_effect_nc", "particle3"}, {{R_REG_SET_FIXED, 5}}, F_V3_V4}, // Creates a particle effect on a given entity. Similar to the particle_id opcode, but the created particles have // no draw distance, so they are visible from very far away. @@ -2472,29 +2468,29 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // regsA[1] = duration in frames // regsA[2] = entity (client ID, 0x1000 + enemy ID, or 0x4000 + object ID) // regsA[3] = y offset (as integer) - {0xF94C, "player_effect_nc", "particle3f_id", {{R_REG_SET_FIXED, 4}}, F_V3_V4}, + {0xF94C, {"player_effect_nc", "particle3f_id"}, {{R_REG_SET_FIXED, 4}}, F_V3_V4}, // Returns 1 in regA if a file named PSO3_CHARACTER is present on either memory card. This opcode is only available // on PSO Plus on GC; that is, it only exists on JP v1.4, JP v1.5, and US v1.2. - {0xF94D, "has_ep3_save_file", nullptr, {W_REG}, F_GC_V3 | F_ARGS}, + {0xF94D, {"has_ep3_save_file"}, {W_REG}, F_GC_V3 | F_ARGS}, // Gives the player one copy of a card. regA is the card ID. - {0xF94D, "give_card", "is_there_cardbattle?", {R_REG}, F_GC_EP3TE}, + {0xF94D, {"give_card", "is_there_cardbattle?"}, {R_REG}, F_GC_EP3TE}, // Gives the player one copy of a card, or takes one copy away. // regsA[0] = card_id // regsA[1] = action (give card if >= 0, take card if < 0) - {0xF94D, "give_or_take_card", "is_there_cardbattle?", {{R_REG_SET_FIXED, 2}}, F_GC_EP3}, + {0xF94D, {"give_or_take_card", "is_there_cardbattle?"}, {{R_REG_SET_FIXED, 2}}, F_GC_EP3}, // TODO(DX): Related to voice chat, but functionality is unknown. valueA is a client ID; a value is read from that // player's TVoiceChatClient object and (!!value) is placed in regB. This value is set by the 6xB3 command. - {0xF94D, "unknown_F94D", nullptr, {I32, W_REG}, F_XB_V3 | F_ARGS}, + {0xF94D, {"unknown_F94D"}, {I32, W_REG}, F_XB_V3 | F_ARGS}, // These opcodes all do nothing on BB. F94D is presumably the voice chat opcode from Xbox, which was removed, but // it's not clear what the other two might have originally been. - {0xF94D, "nop_F94D", nullptr, {}, F_V4}, - {0xF94E, "nop_F94E", nullptr, {}, F_V4}, - {0xF94F, "nop_F94F", nullptr, {}, F_V4}, + {0xF94D, {"nop_F94D"}, {}, F_V4}, + {0xF94E, {"nop_F94E"}, {}, F_V4}, + {0xF94F, {"nop_F94F"}, {}, F_V4}, // Opens one of the Pioneer 2 counter menus, specified by valueA. Values: // 0 = weapon shop @@ -2508,20 +2504,20 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 8 = Momoka item exchange (this opens a menu displaying the items specified in the quest header, which sends // 6xD9 when the player chooses an item) // valueA is not bounds-checked, so it could be used to write a byte with the value 1 anywhere in memory. - {0xF950, "bb_p2_menu", "BB_p2_menu", {I32}, F_V4 | F_ARGS}, + {0xF950, {"bb_p2_menu", "BB_p2_menu"}, {I32}, F_V4 | F_ARGS}, // Behaves exactly the same as map_designate_ex, but the arguments are specified as immediate values and not via // registers or arg_push. Sega probably added this opcode so their quest authoring tools could easily generate the - // necessary header fields without doing any fancy script analysis. + // necessary header fields without doing any fancy static analysis. // valueA = floor number // valueB = area number // valueC = type (0: use layout, 1: use offline template, 2: use online template, 3: nothing) // valueD = major variation // valueE = minor variation - {0xF951, "bb_map_designate", "BB_Map_Designate", {I8, I8, I8, I8, I8}, F_V4}, + {0xF951, {"bb_map_designate", "BB_Map_Designate"}, {I8, I8, I8, I8, I8}, F_V4}, // Returns the number of items in the player's inventory. - {0xF952, "bb_get_number_in_pack", "BB_get_number_in_pack", {W_REG}, F_V4}, + {0xF952, {"bb_get_number_in_pack", "BB_get_number_in_pack"}, {W_REG}, F_V4}, // Requests an item exchange in the player's inventory. Sends 6xD5. The requested item must match one of the item // creation masks in the quest script's header. @@ -2529,19 +2525,19 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueD/valueE/valueF = item.data1[0-2] to replace it with // labelG = label to call on success // labelH = label to call on failure - {0xF953, "bb_swap_item", "BB_swap_item", {I32, I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, + {0xF953, {"bb_swap_item", "BB_swap_item"}, {I32, I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, // Checks if an item can be wrapped. // valueA = item ID // regB = returned status (0 = can't be wrapped, 1 = can be wrapped, 2 = item not found) - {0xF954, "bb_check_wrap", "BB_check_wrap", {I32, W_REG}, F_V4 | F_ARGS}, + {0xF954, {"bb_check_wrap", "BB_check_wrap"}, {I32, W_REG}, F_V4 | F_ARGS}, // Requests an item exchange for Photon Drops. Sends 6xD7. The requested item must match one of the item creation // masks in the quest script's header. // valueA/valueB/valueC = item.data1[0-2] for requested item // labelD = label to call on success // labelE = label to call on failure - {0xF955, "bb_exchange_pd_item", "BB_exchange_PD_item", {I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, + {0xF955, {"bb_exchange_pd_item", "BB_exchange_PD_item"}, {I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, // Requests an S-rank special upgrade in exchange for Photon Drops. Sends 6xD8. // valueA = item ID @@ -2549,7 +2545,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueE = special type // labelF = label to call on success // labelG = label to call on failure - {0xF956, "bb_exchange_pd_srank", "BB_exchange_PD_srank", {I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, + {0xF956, {"bb_exchange_pd_srank", "BB_exchange_PD_srank"}, {I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, // Requests a weapon attribute upgrade in exchange for Photon Drops. Sends 6xDA. // valueA = item ID @@ -2558,18 +2554,18 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueF = payment count (number of PDs) // labelG = label to call on success // labelH = label to call on failure - {0xF957, "bb_exchange_pd_percent", "BB_exchange_PD_special", {I32, I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, + {0xF957, {"bb_exchange_pd_percent", "BB_exchange_PD_special"}, {I32, I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, // Requests a weapon attribute upgrade in exchange for Photon Spheres. Sends 6xDA. Same arguments as // bb_exchange_pd_percent, except Photon Spheres are used instead. - {0xF958, "bb_exchange_ps_percent", "BB_exchange_PS_percent", {I32, I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, + {0xF958, {"bb_exchange_ps_percent", "BB_exchange_PS_percent"}, {I32, I32, I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, // Determines whether the Episode 4 boss can escape if undefeated after 20 minutes. // valueA = boss can escape (0 = no, 1 = yes (default)) - {0xF959, "bb_set_ep4_boss_can_escape", "BB_set_ep4boss_can_escape", {I32}, F_V4 | F_ARGS}, + {0xF959, {"bb_set_ep4_boss_can_escape", "BB_set_ep4boss_can_escape"}, {I32}, F_V4 | F_ARGS}, // Returns 1 if the Episode 4 boss death cutscene is playing, or 0 if not (even if the boss is already defeated). - {0xF95A, "bb_is_ep4_boss_dying", nullptr, {W_REG}, F_V4}, + {0xF95A, {"bb_is_ep4_boss_dying"}, {W_REG}, F_V4}, // Requests an item exchange. Sends 6xD9. The requested item must match one of the item creation masks in the quest // script's header. @@ -2579,7 +2575,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueD = token2 (see 6xD9 in CommandFormats.hh) // labelE = label to call on success // labelF = label to call on failure - {0xF95B, "bb_replace_item", "bb_send_6xD9", {I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, + {0xF95B, {"bb_replace_item", "bb_send_6xD9"}, {I32, I32, I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, // Requests an exchange of Secret Lottery Tickets for items. Sends 6xDE. The pool of items that can be returned by // this opcode is determined by the quest's header; newserv assembles/disassembles this field as .allow_create_item @@ -2589,15 +2585,15 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueB = unknown_a1 // labelC = label to call on success // labelD = label to call on failure (unused because of a client bug; see 6xDE description in CommandFormats.hh) - {0xF95C, "bb_exchange_slt", "BB_exchange_SLT", {I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, + {0xF95C, {"bb_exchange_slt", "BB_exchange_SLT"}, {I32, I32, SCRIPT16, SCRIPT16}, F_V4 | F_ARGS}, // Removes a Photon Crystal from the player's inventory, and disables drops for the rest of the quest. Sends 6xDF. - {0xF95D, "bb_exchange_pc", "BB_exchange_PC", {}, F_V4}, + {0xF95D, {"bb_exchange_pc", "BB_exchange_PC"}, {}, F_V4}, // Requests an item drop within a quest. Sends 6xE0. // valueA = type (corresponds to QuestF95EResultItems in config.json) // valueB/valueC = x, z coordinates as floats - {0xF95E, "bb_box_create_bp", "BB_box_create_BP", {I32, FLOAT32, FLOAT32}, F_V4 | F_ARGS}, + {0xF95E, {"bb_box_create_bp", "BB_box_create_BP"}, {I32, FLOAT32, FLOAT32}, F_V4 | F_ARGS}, // Requests an exchange of Photon Tickets for items. Sends 6xE1. // regA = result code reg (set to result code upon server response); the result codes are: @@ -2609,19 +2605,19 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // valueC = result index (index into QuestF95FResultItems in config.json) // labelD = label to call on success // labelE = label to call on failure - {0xF95F, "bb_exchange_pt", "BB_exchage_PT", {W_REG32, W_REG32, I32, I32, I32}, F_V4 | F_ARGS}, + {0xF95F, {"bb_exchange_pt", "BB_exchage_PT"}, {W_REG32, W_REG32, I32, I32, I32}, F_V4 | F_ARGS}, // Requests a prize from the Meseta gambling prize list. Sends 6xE2. The server responds with 6xE3, which sets the // replacement token in message strings. The status of this can be checked with // bb_get_6xE3_status. // valueA = result tier (see QuestF960SuccessResultItems and QuestF960FailureResultItems in config.json) - {0xF960, "bb_send_6xE2", "unknownF960", {I32}, F_V4 | F_ARGS}, + {0xF960, {"bb_send_6xE2", "unknownF960"}, {I32}, F_V4 | F_ARGS}, // Returns the status of the expected 6xE3 command from a preceding bb_send_6xE2 opcode. Return values: // 0 = 6xE3 hasn't been received // 1 = the received item is valid // 2 = the received item is invalid, or the item ID was already in use - {0xF961, "bb_get_6xE3_status", "unknownF961", {W_REG}, F_V4}, + {0xF961, {"bb_get_6xE3_status", "unknownF961"}, {W_REG}, F_V4}, }; static const unordered_map& opcodes_for_version(Version v) { @@ -2657,13 +2653,11 @@ static const unordered_map& opcodes_ if (!(def.flags & vf)) { continue; } - if (def.name && !index.emplace(phosg::tolower(def.name), &def).second) { - throw logic_error(std::format("duplicate definition for opcode {:04X}", def.opcode)); - } - if (def.qedit_name) { - string lower_qedit_name = phosg::tolower(phosg::tolower(def.qedit_name)); - if ((lower_qedit_name != def.name) && !index.emplace(lower_qedit_name, &def).second) { - throw logic_error(std::format("duplicate definition for opcode {:04X}", def.opcode)); + for (const char* name : def.names) { + auto emplace_ret = index.emplace(phosg::tolower(name), &def); + if (!emplace_ret.second && (emplace_ret.first->second->opcode != def.opcode)) { + throw logic_error(std::format("name conflict for opcode {} (existing: {:04X}, new: {:04X})", + name, emplace_ret.first->second->opcode, def.opcode)); } } } @@ -3237,7 +3231,7 @@ std::string disassemble_quest_script( } dasm_line = std::format(".unknown {:04X}", opcode); } else { - const char* op_name = (use_qedit_names && def->qedit_name) ? def->qedit_name : def->name; + const char* op_name = use_qedit_names ? def->names.back() : def->names.front(); dasm_line = op_name ? op_name : std::format("[{:04X}]", opcode); if (!version_has_args || !(def->flags & F_ARGS)) { dasm_line.resize(std::max(dasm_line.size() + 1, 0x20), ' '); @@ -4350,13 +4344,13 @@ AssembledQuestScript assemble_quest_script( if (opcode_def->args.empty()) { if (line_tokens.size() > 1) { - throw runtime_error(std::format("arguments not allowed for {}", opcode_def->name)); + throw runtime_error(std::format("arguments not allowed for {}", opcode_def->names.front())); } return; } if (line_tokens.size() < 2) { - throw runtime_error(std::format("arguments required for {}", opcode_def->name)); + throw runtime_error(std::format("arguments required for {}", opcode_def->names.front())); } phosg::strip_trailing_whitespace(line_tokens[1]); phosg::strip_leading_whitespace(line_tokens[1]); @@ -5040,7 +5034,7 @@ void populate_quest_metadata_from_script( auto get_single_int32_arg = [&]() -> uint32_t { if (use_args) { if (args_list.size() != 1) { - throw runtime_error(std::format("incorrect argument count to {}", def->name)); + throw runtime_error(std::format("incorrect argument count to {}", def->names.front())); } return args_list[0]; } else {