reformat CommandFormats and DataIndexes

This commit is contained in:
Martin Michelsen
2025-12-05 19:52:49 -08:00
parent 81169ba9d3
commit a89423e9f5
3 changed files with 1755 additions and 2443 deletions
+1193 -1761
View File
File diff suppressed because it is too large Load Diff
+244 -364
View File
@@ -113,17 +113,14 @@ enum class CardRank : uint8_t {
R3 = 0x09, R3 = 0x09,
R4 = 0x0A, R4 = 0x0A,
SS = 0x0B, SS = 0x0B,
// Cards with the D1 or D2 ranks are considered never usable by the player, // Cards with the D1 or D2 ranks are considered never usable by the player, and are automatically removed from player
// and are automatically removed from player decks before battle and when // decks before battle and when loading the deckbuilder. Cards with the D1 rank appear in the deck builder but are
// loading the deckbuilder. Cards with the D1 rank appear in the deckbuilder // grayed out (and cannot be added to decks); cards with the D2 rank don't appear in the deckbuilder at all.
// but are grayed out (and cannot be added to decks); cards with the D2 rank
// don't appear in the deckbuilder at all.
D1 = 0x0C, D1 = 0x0C,
D2 = 0x0D, D2 = 0x0D,
// The D3 rank is referenced in a few places, including the function that // The D3 rank is referenced in a few places, including the function that determines whether or not a card can appear
// determines whether or not a card can appear in post-battle draws, and the // in post-battle draws, and the function that determines whether a card should appear in the deckbuilder. In these
// function that determines whether a card should appear in the deckbuilder. // cases, it prevents the card from appearing.
// In these cases, it prevents the card from appearing.
D3 = 0x0E, D3 = 0x0E,
}; };
@@ -221,7 +218,7 @@ enum class ConditionType : uint8_t {
FREE_MANEUVER = 0x27, // Enable movement over occupied tiles FREE_MANEUVER = 0x27, // Enable movement over occupied tiles
SCALE_MV_COST = 0x28, // Multiply all move action costs by expr (which may be zero) SCALE_MV_COST = 0x28, // Multiply all move action costs by expr (which may be zero)
CLONE = 0x29, // Make setting this card free if at least one card of type N is already on the field CLONE = 0x29, // Make setting this card free if at least one card of type N is already on the field
DEF_DISABLE_BY_COST = 0x2A, // Disable use of any defense cards costing between (N / 10) and (N % 10) points, inclusive DEF_DISABLE_BY_COST = 0x2A, // Disable use of defense cards costing between (N / 10) and (N % 10) points, inclusive
FILIAL = 0x2B, // Increase controlling SC's HP by N when this card is destroyed FILIAL = 0x2B, // Increase controlling SC's HP by N when this card is destroyed
SNATCH = 0x2C, // Steal N EXP during attack SNATCH = 0x2C, // Steal N EXP during attack
HAND_DISRUPTER = 0x2D, // Discard N cards from hand immediately HAND_DISRUPTER = 0x2D, // Discard N cards from hand immediately
@@ -259,7 +256,7 @@ enum class ConditionType : uint8_t {
HEAVY_RAMPAGE = 0x4D, // If SC has 3 or more items equipped, attacks target SC and all equipped items HEAVY_RAMPAGE = 0x4D, // If SC has 3 or more items equipped, attacks target SC and all equipped items
AP_GROWTH = 0x4E, // Permanently increase AP by N AP_GROWTH = 0x4E, // Permanently increase AP by N
TP_GROWTH = 0x4F, // Permanently increase TP by N TP_GROWTH = 0x4F, // Permanently increase TP by N
REBORN = 0x50, // If any card of type N is on the field, this card goes to the hand when destroyed instead of being discarded REBORN = 0x50, // If any card of type N is on the field, this card goes back to the hand when destroyed
COPY = 0x51, // Temporarily set AP/TP to N percent (or 100% if N is 0) of opponent's values COPY = 0x51, // Temporarily set AP/TP to N percent (or 100% if N is 0) of opponent's values
UNKNOWN_52 = 0x52, UNKNOWN_52 = 0x52,
MISC_GUARDS = 0x53, // Add N to card's defense value MISC_GUARDS = 0x53, // Add N to card's defense value
@@ -272,7 +269,7 @@ enum class ConditionType : uint8_t {
ANTI_ABNORMALITY_2 = 0x5A, // Remove all conditions ANTI_ABNORMALITY_2 = 0x5A, // Remove all conditions
FIXED_RANGE = 0x5B, // Use SC's range instead of weapon or attack card ranges FIXED_RANGE = 0x5B, // Use SC's range instead of weapon or attack card ranges
ELUDE = 0x5C, // SC does not lose HP when equipped items are destroyed ELUDE = 0x5C, // SC does not lose HP when equipped items are destroyed
PARRY = 0x5D, // Forward attack to a random FC within one tile of original target, excluding attacker and original target PARRY = 0x5D, // Forward attack to random FC within one tile of original target, except attacker and original target
BLOCK_ATTACK = 0x5E, // Completely block attack BLOCK_ATTACK = 0x5E, // Completely block attack
UNKNOWN_5F = 0x5F, UNKNOWN_5F = 0x5F,
UNKNOWN_60 = 0x60, UNKNOWN_60 = 0x60,
@@ -513,16 +510,14 @@ struct CardDefinition {
} __packed_ws__(Stat, 4); } __packed_ws__(Stat, 4);
struct Effect { struct Effect {
// effect_num is the 1-based index of this effect within the card definition // effect_num is the 1-based index of this effect within the card definition (that is, .effects[0] should have
// (that is, .effects[0] should have effect_num == 1 if it is used). // effect_num == 1 if it is used).
/* 00 */ uint8_t effect_num; /* 00 */ uint8_t effect_num;
/* 01 */ ConditionType type; /* 01 */ ConditionType type;
// For ConditionTypes that need it, expr specifies "how much". (For those // For ConditionTypes that need it, expr specifies "how much". (For those that don't, expr may be blank.) The value
// that don't, expr may be blank.) The value may contain tokens that refer // may contain tokens that refer to stats from the current battle (see description_for_expr_token) and operators to
// to stats from the current battle (see description_for_expr_token) and // perform basic computations on them. Operators are evaluated left-to-right in the expression, and there are no
// operators to perform basic computations on them. Operators are evaluated // operator precedence rules; for example, the expression "4+4//2" results in 4, not 6.
// left-to-right in the expression, and there are no operator precedence
// rules; for example, the expression "4+4//2" results in 4, not 6.
/* 02 */ pstring<TextEncoding::ASCII, 0x0F> expr; /* 02 */ pstring<TextEncoding::ASCII, 0x0F> expr;
// when specifies in which phase the effect should activate. // when specifies in which phase the effect should activate.
/* 11 */ EffectWhen when; /* 11 */ EffectWhen when;
@@ -532,13 +527,11 @@ struct CardDefinition {
/* 16 */ pstring<TextEncoding::ASCII, 4> arg2; /* 16 */ pstring<TextEncoding::ASCII, 4> arg2;
// arg3 generally specifies who is targeted by the effect. // arg3 generally specifies who is targeted by the effect.
/* 1A */ pstring<TextEncoding::ASCII, 4> arg3; /* 1A */ pstring<TextEncoding::ASCII, 4> arg3;
// apply_criterion can be used to apply an additional condition for when the // apply_criterion can be used to apply an additional condition for when the effect should activate. For example,
// effect should activate. For example, it can be used to make the effect // it can be used to make the effect only activate if the target is not a Story Character.
// only activate if the target is not a Story Character.
/* 1E */ CriterionCode apply_criterion; /* 1E */ CriterionCode apply_criterion;
// name_index specifies which string from TextEnglish.pr2 is shown next to // name_index specifies which string from TextEnglish.pr2 is shown next to the card when it is attacking or
// the card when it is attacking or defending. Zero in this field means no // defending. Zero in this field means no string is shown for this ability.
// string is shown for this ability.
/* 1F */ uint8_t name_index; /* 1F */ uint8_t name_index;
/* 20 */ /* 20 */
@@ -551,16 +544,16 @@ struct CardDefinition {
/* 0000 */ be_uint32_t card_id; /* 0000 */ be_uint32_t card_id;
/* 0004 */ pstring<TextEncoding::SJIS, 0x40> jp_name; /* 0004 */ pstring<TextEncoding::SJIS, 0x40> jp_name;
// The list of card definitions ends with a "sentinel" definition that isn't a // The list of card definitions ends with a sentinel definition that isn't a real card, but instead has a negative
// real card, but instead has a negative number in the type field here. // number in the type field here.
/* 0044 */ CardType type; /* 0044 */ CardType type;
/* 0045 */ uint8_t self_cost; // ATK dice points required /* 0045 */ uint8_t self_cost; // ATK dice points required
/* 0046 */ uint8_t ally_cost; // ATK points from allies required; PBs use this /* 0046 */ uint8_t ally_cost; // ATK points from allies required; PBs use this
/* 0047 */ uint8_t unused1; /* 0047 */ uint8_t unused1;
// In the definitions file, only .code is populated here; .decode_code() must // In the definitions file, only .code is populated here; .decode_code() must be called to fill in .type and .stat
// be called to fill in .type and .stat within each of these. // within each of these.
/* 0048 */ Stat hp; /* 0048 */ Stat hp;
/* 004C */ Stat ap; /* 004C */ Stat ap;
/* 0050 */ Stat tp; /* 0050 */ Stat tp;
@@ -571,120 +564,94 @@ struct CardDefinition {
/* 0060 */ parray<uint8_t, 8> right_colors; /* 0060 */ parray<uint8_t, 8> right_colors;
/* 0068 */ parray<uint8_t, 8> top_colors; /* 0068 */ parray<uint8_t, 8> top_colors;
// The card's attack range is defined in a somewhat odd format here. Each // The card's attack range is defined in a somewhat odd format here. Each field in this array corresponds to a single
// field in this array corresponds to a single row of the range, and every // row of the range, and every fourth bit, starting with bit 15, corresponds to a tile in that row. The rest of the
// fourth bit, starting with bit 15, corresponds to a tile in that row. The // bits are ignored, except in two special cases described below. For example, Ohgun's range is:
// rest of the bits are ignored, except in two special cases described below.
// For example, Ohgun's range is:
// [0] = 0x00000000 => ----- // [0] = 0x00000000 => -----
// [1] = 0x00001110 => -***- // [1] = 0x00001110 => -***-
// [2] = 0x00001110 => -***- // [2] = 0x00001110 => -***-
// [3] = 0x00000000 => ----- // [3] = 0x00000000 => -----
// [4] = 0x00000000 => ----- (the card itself is in the center of this row) // [4] = 0x00000000 => ----- (the card itself is in the center of this row)
// [5] = 0x00000000 => ----- // [5] = 0x00000000 => -----
//
// The two special cases are as follows: // The two special cases are as follows:
// 1. If all six values in the range array are 0x000FFFFF, then the card's // 1. If all six values in the range array are 0x000FFFFF, then the card's range is the entire field.
// range is the entire field. // 2. If the cell corresponding to the card itself ((range[4] >> 8) & 0x0F) is not zero, then the rest of the range
// 2. If the cell corresponding to the card itself ((range[4] >> 8) & 0x0F) is // array is ignored and the card's range comes from a fixed set of ranges instead. See decode_range() for details.
// not zero, then the rest of the range array is ignored and the card's
// range comes from a fixed set of ranges instead. See decode_range() for
// more information.
/* 0070 */ parray<be_uint32_t, 6> range; /* 0070 */ parray<be_uint32_t, 6> range;
/* 0088 */ be_uint32_t unused2; /* 0088 */ be_uint32_t unused2;
/* 008C */ TargetMode target_mode; /* 008C */ TargetMode target_mode;
/* 008D */ uint8_t assist_turns; // 90 (dec) = once, 99 (dec) = forever /* 008D */ uint8_t assist_turns; // 90 (dec) = once, 99 (dec) = forever
// This field is 1 if the card cannot move by itself. Item cards hare 1 here // This field is 1 if the card cannot move by itself. Item cards hare 1 here because they cannot move on their own
// because they cannot move on their own and automatically move along with // and automatically move along with their SC instead. Generally only SCs and creatures have 0 here.
// their SC instead. Generally only SCs and creatures have 0 here.
/* 008E */ uint8_t cannot_move; /* 008E */ uint8_t cannot_move;
// This field is 1 if the card cannot take part in an attack. Unlike // This field is 1 if the card cannot take part in an attack. Unlike cannot_move, cards that cannot attack on their
// cannot_move, cards that cannot attack on their own but can take part in an // own but can take part in an attack (such as action cards) have 0 here. Most shields, mags, defense actions, and
// attack (such as action cards) have 0 here. Most shields, mags, defense // assist cards have 1 here.
// actions, and assist cards have 1 here.
/* 008F */ uint8_t cannot_attack; /* 008F */ uint8_t cannot_attack;
/* 0090 */ uint8_t unused3; /* 0090 */ uint8_t unused3;
// If cannot_drop is 1, this card can't appear in post-battle rewards and is // If cannot_drop is 1, this card can't appear in post-battle rewards and is considered unobtainable by players, so
// considered unobtainable by players, so the game will remove it from the // the game will remove it from the player's collection if they have any copies of it. A value of 1 here also
// player's collection if they have any copies of it. A value of 1 here also
// prevents the card from being used as a God Whim random assist. // prevents the card from being used as a God Whim random assist.
/* 0091 */ uint8_t cannot_drop; /* 0091 */ uint8_t cannot_drop;
// This criterion code specifies who can use the card, and when it can be // This criterion code specifies who can use the card, and when it can be used. This specifies which Hero-side SCs
// used. This specifies which Hero-side SCs can use which items, for example, // can use which items, for example, and when action cards can be played (when SC or FC is attacking, on self or
// and when action cards can be played (when SC or FC is attacking, on self or
// ally, etc.). // ally, etc.).
/* 0092 */ CriterionCode usable_criterion; /* 0092 */ CriterionCode usable_criterion;
/* 0093 */ CardRank rank; /* 0093 */ CardRank rank;
/* 0094 */ be_uint16_t unused4; /* 0094 */ be_uint16_t unused4;
// The card class is used for checking attributes (e.g. item types). It's // The card class is used for checking attributes (e.g. item types). It's stored big-endian here, so there's a helper
// stored big-endian here, so there's a helper function (card_class()) that // function (card_class()) that returns a usable CardClass enum value.
// returns a usable CardClass enum value.
/* 0096 */ be_uint16_t be_card_class; /* 0096 */ be_uint16_t be_card_class;
// If this card is an assist card, this field controls how COM players handle // If this card is an assist card, this field controls how COM players handle playing it. This field is ignored for
// playing it. This field is ignored for non-assist cards. This integer // non-assist cards. This integer encodes the following fields:
// encodes the following fields: // - assist_ai_params % 100 (that is, the two lowest decimal places) appears to specify the effect, though a few
// - assist_ai_params % 100 (that is, the two lowest decimal places) appears // unrelated cards share values in this field. It's not yet known how exactly this is used by the COM logic.
// to specify the effect, though a few unrelated cards share values in this // - (assist_ai_params / 100) % 10 specifies the priority. It appears the COM logic always chooses the assist card
// field. It's not yet known how exactly this is used by the COM logic. // with the highest value in this field if there are multiple cards to consider.
// - (assist_ai_params / 100) % 10 specifies the priority. It appears the COM // - (assist_ai_params / 1000) % 10 specifies on whom the assist card may be played (0 = any player, 1 = self, 2 =
// logic always chooses the assist card with the highest value in this field // self or ally, 3 = enemy only).
// if there are multiple cards to consider.
// - (assist_ai_params / 1000) % 10 specifies on whom the assist card may be
// played (0 = any player, 1 = self, 2 = self or ally, 3 = enemy only).
/* 0098 */ be_uint16_t assist_ai_params; /* 0098 */ be_uint16_t assist_ai_params;
// Most cards in the official definitions file have the same value stored in // Most cards in the official definitions file have the same value stored in unused5 as in assist_ai_params. Unlike
// unused5 as in assist_ai_params. Unlike assist_ai_params, unused5 does not // assist_ai_params, unused5 does not appear to be used anywhere.
// appear to be used anywhere.
/* 009A */ be_uint16_t unused5; /* 009A */ be_uint16_t unused5;
// The card drop rates control how likely the card is to appear in a standard // The card drop rates control how likely the card is to appear in a standard post-battle random draw. How this works
// post-battle random draw. How this works is fairly complex and is explained // is fairly complex and is explained below in detail. Before any of that logic, this card can never drop and no card
// below in detail. Before any of that logic, this card can never drop and no // can transform into this card if any of the following are true:
// card can transform into this card if any of the following are true:
// - type is SC_HUNTERS or SC_ARKZ // - type is SC_HUNTERS or SC_ARKZ
// - card_class is BOSS_ATTACK_ACTION (0x23) or BOSS_TECH (0x24) // - card_class is BOSS_ATTACK_ACTION (0x23) or BOSS_TECH (0x24)
// - rank is E, D1, or D2 // - rank is E, D1, or D2
// - cannot_drop is 1 (specifically 1; other nonzero values for cannot_drop // - cannot_drop is 1 (specifically 1; other nonzero values don't prevent the card from appearing in draws)
// don't prevent the card from appearing in post-battle draws)
// If none of these conditions apply, the logic below is used. // If none of these conditions apply, the logic below is used.
// //
// Drop rates are integers which encode the following data: // Drop rates are integers which encode the following data:
// - rate % 10 (that is, the lowest decimal place) specifies the required game // - rate % 10 (that is, the lowest decimal place) specifies the required game mode. 0 means any mode, 1 means
// mode. 0 means any mode, 1 means offline story mode, 2 means 1P free // offline story mode, 2 means 1P free battle, 3 means 2P+ free battle (specifically, PvP - two humans vs. two COMs
// battle, 3 means 2P+ free battle (specifically, PvP - two humans vs. two // counts as 1P free battle), 4 means online mode, 5 means tournament. Some cards have this field set to 6, which
// COMs counts as 1P free battle), 4 means online mode, 5 means tournament. // isn't a valid game mode; it seems Sega used this as a way to make sure the drop rate never applies.
// Some cards have this field set to 6, which isn't a valid game mode; it // - (rate / 10) % 100 (that is, the tens and hundreds decimal places) specify the environment number + 1. For
// seems Sega used this as a way to make sure the drop rate never applies. // example, if this field contains 5, then this drop only applies if the battle took place at Molae Venti
// - (rate / 10) % 100 (that is, the tens and hundreds decimal places) specify // (environment number 4). If this field is zero, the drop applies regardless of where the battle took place.
// the environment number + 1. For example, if this field contains 5, then // - rate / 1000 (the thousands decimal place) specifies the rarity class. This can be any number in the range [0,
// this drop only applies if the battle took place at Molae Venti // 9], and affects how likely the card is to appear based on the player's level. See below for details.
// (environment number 4). If this field is zero, the drop applies // - rate / 10000 (the ten-thousands decimal place) specifies if the drop rate applies only if the player used a
// regardless of where the battle took place. // Hunters deck (1), only if they used an Arkz deck (2), or if they used any deck (0).
// - rate / 1000 (the thousands decimal place) specifies the rarity class.
// This can be any number in the range [0, 9], and affects how likely the
// card is to appear based on the player's level. See below for details.
// - rate / 10000 (the ten-thousands decimal place) specifies if the drop rate
// applies only if the player used a Hunters deck (1), only if they used an
// Arkz deck (2), or if they used any deck (0).
// //
// When determining which cards to drop, the game first checks the drop rate // When determining which cards to drop, the game first checks the drop rate fields on all cards. For each drop rate
// fields on all cards. For each drop rate that applies, the game adds the // that applies, the game adds the card ID into an appropriate bucket based on the rarity class. (If both drop rates
// card ID into an appropriate bucket based on the rarity class. (If both drop // for a card apply, the card ID is added twice.) The player's level class is then computed according to the
// rates for a card apply, the card ID is added twice.) The player's level // following table:
// class is then computed according to the following table:
// 1 2 3 4 5 6 7 8 9 10 // 1 2 3 4 5 6 7 8 9 10
// CLvOff 1-2 3-4 5-9 10-14 15-19 20-25 26-29 30-39 40-49 50+ // CLvOff 1-2 3-4 5-9 10-14 15-19 20-25 26-29 30-39 40-49 50+
// CLvOn 1-2 3-4 5-10 11-16 17-23 24-32 33-39 40-49 50-99 100+ // CLvOn 1-2 3-4 5-10 11-16 17-23 24-32 33-39 40-49 50-99 100+
// For the purposes of this computation, the player's level is used by default // For the purposes of this computation, the player's level is used by default (CLvOn or CLvOff), but the map may
// (CLvOn or CLvOff), but the map may override it - see win_level_override and // override it - see win_level_override and loss_level_override in MapDefinition. This specifies which row in the
// loss_level_override in MapDefinition. This specifies which row in the
// following tables will be used. // following tables will be used.
// //
// Cards are then chosen from the buckets with a weighted distribution // Cards are then chosen from the buckets with a weighted distribution according to these tables (row is player's
// according to these tables (row is player's level class, column is card's // level class, column is card's rarity class):
// rarity class):
// Offline: // Offline:
// LC | RC = 0 1 2 3 4 5 6 7 8 9 // LC | RC = 0 1 2 3 4 5 6 7 8 9
// 1 | 8000 2000 50 // 1 | 8000 2000 50
@@ -709,43 +676,33 @@ struct CardDefinition {
// 8 | 160000 160000 190000 190000 130000 100000 50000 19999 1 // 8 | 160000 160000 190000 190000 130000 100000 50000 19999 1
// 9 | 120000 120000 150000 160000 150000 150000 100000 49989 10 1 // 9 | 120000 120000 150000 160000 150000 150000 100000 49989 10 1
// 10 | 120000 120000 130000 150000 160000 150000 100000 69965 30 5 // 10 | 120000 120000 130000 150000 160000 150000 100000 69965 30 5
// These values are all relative to other values in the same row. For example, // These values are all relative to other values in the same row. For example, if your character is in level class 1,
// if your character is in level class 1, you'll get cards of rarity class 0 // you'll get cards of rarity class 0 about 80% of the time, cards of rarity class 1 about 20% of the time, and cards
// about 80% of the time, cards of rarity class 1 about 20% of the time, and // of rarity class 2 about 0.5% of the time. (The actual probabilities are 8000/10050, 2000/10050, and 50/10050.)
// cards of rarity class 2 about 0.5% of the time. (The actual probabilities
// are 8000/10050, 2000/10050, and 50/10050.)
// //
// When choosing the contents of the four card packs after a battle, the game // When choosing the contents of the four card packs after a battle, the game first chooses how many cards to give
// first chooses how many cards to give the player based on the end-of-battle // the player based on the end-of-battle rank (9 for S, 8 for A+/A, 7 for B+/B, 6 for C+/C, 5 for D+/D/E, 2 if the
// rank (9 for S, 8 for A+/A, 7 for B+/B, 6 for C+/C, 5 for D+/D/E, 2 if the // player lost). It then decides the number of "restricted" cards; if the player is getting 6 or more cards, there
// player lost). It then decides the number of "restricted" cards; if the // are 2 restricted cards per pack, otherwise there is only one. The restricted cards are required to be a certain
// player is getting 6 or more cards, there are 2 restricted cards per pack, // type in each pack except the black pack:
// otherwise there is only one. The restricted cards are required to be a
// certain type in each pack except the black pack:
// - In the blue pack, the restricted cards must be creature cards. // - In the blue pack, the restricted cards must be creature cards.
// - In the red pack, the restricted cards must be item cards. // - In the red pack, the restricted cards must be item cards.
// - In the green pack, the restricted cards must be action cards. // - In the green pack, the restricted cards must be action cards.
// For example, if you get a B+ rank after winning a battle and pick the green // For example, if you get a B+ rank after winning a battle and pick the green pack, you will always get at least two
// pack, you will always get at least two action cards. // action cards.
// //
// The game then samples N card IDs from the appropriate buckets (where N is // The game then samples N card IDs from the appropriate buckets (where N is the number chosen above), but for the
// the number chosen above), but for the first 1 or 2 cards, it applies the // first 1 or 2 cards, it applies the restriction described above and re-draws if the card is the wrong type. After
// restriction described above and re-draws if the card is the wrong type. // sampling the N card IDs, it sorts them and presents them to the player.
// After sampling the N card IDs, it sorts them and presents them to the
// player.
// //
// There is one more effect to consider after cards are chosen: cards may // There is one more effect to consider after cards are chosen: cards may randomly transform into VIP cards or into
// randomly transform into VIP cards or into stronger (and rarer) cards. The // stronger (and rarer) cards. The chance of each of these occurring is based on the rarity of that card that may
// chance of each of these occurring is based on the rarity of that card that // transform, and the number of copies of that card which the player already has. In the below table, P(activate) is
// may transform, and the number of copies of that card which the player // the probability that any transformation is attempted at all; if this check passes, the player sees the glow effect
// already has. In the below table, P(activate) is the probability that any // and "The change will occur..." text. P(vip) is the probability that the card becomes a VIP card, after the glow
// transformation is attempted at all; if this check passes, the player sees // effect plays. P(rare) is the probability of the card becoming a rarer card after the glow effect. Therefore, the
// the glow effect and "The change will occur..." text. P(vip) is the // final probability that a card will transform into a VIP card is P(activate) * P(vip), and the final probability of
// probability that the card becomes a VIP card, after the glow effect plays. // transforming into a rarer card is P(activate) * P(rare).
// P(rare) is the probability of the card becoming a rarer card after the glow
// effect. Therefore, the final probability that a card will transform into a
// VIP card is P(activate) * P(vip), and the final probability of transforming
// into a rarer card is P(activate) * P(rare).
// ======== Card rank N4-N1 ======== ======== Card rank R4-R1 ======== // ======== Card rank N4-N1 ======== ======== Card rank R4-R1 ========
// Count P(activate) P(rare) P(vip) P(activate) P(rare) P(vip) // Count P(activate) P(rare) P(vip) P(activate) P(rare) P(vip)
// 0-4 0% 0% 0% 0% 0% 0% // 0-4 0% 0% 0% 0% 0% 0%
@@ -757,9 +714,8 @@ struct CardDefinition {
// 41-52 3.7037037% 80% 0.35714286% 4.1666668% 80% 0.45454544% // 41-52 3.7037037% 80% 0.35714286% 4.1666668% 80% 0.45454544%
// 53-99 5% 90% 0.33333334% 5.263158% 90% 0.4347826% // 53-99 5% 90% 0.33333334% 5.263158% 90% 0.4347826%
// //
// If a transformation occurs, the card transforms to a card of a different // If a transformation occurs, the card transforms to a card of a different rank. First, the game consults the
// rank. First, the game consults the following table to determine the rank of // following table to determine the rank of the resulting card (original card's rank on the left, new card's rank
// the resulting card (original card's rank on the left, new card's rank
// across the top): // across the top):
// N4 N3 N2 N1 R4 R3 R2 R1 S SS // N4 N3 N2 N1 R4 R3 R2 R1 S SS
// N4 => 60 30 10 // N4 => 60 30 10
@@ -770,37 +726,30 @@ struct CardDefinition {
// R3 => 20 50 28 1 // R3 => 20 50 28 1
// R2 => 30 60 5 // R2 => 30 60 5
// R1 => 900 100 1 // R1 => 900 100 1
// For example, when an N2 card transforms, there is a 60% chance to become // For example, when an N2 card transforms, there is a 60% chance to become R4, a 30% chance to become R3, and a 10%
// R4, a 30% chance to become R3, and a 10% chance to become R2. When an R1 // chance to become R2. When an R1 card transforms, there is a 900/1001 chance of becoming another R1, a 100/1001
// card transforms, there is a 900/1001 chance of becoming another R1, a // chance of becoming an S, and a 1/1001 chance of becoming an SS.
// 100/1001 chance of becoming an S, and a 1/1001 chance of becoming an SS.
// //
// Once a rank is chosen, the game puts all possible cards into buckets based // Once a rank is chosen, the game puts all possible cards into buckets based on how many of that card the player
// on how many of that card the player already has, then chooses a random card // already has, then chooses a random card out of bucket 0, then bucket 1, etc. all the way up to bucket 49 (or 2 if
// out of bucket 0, then bucket 1, etc. all the way up to bucket 49 (or 2 if // the final rank is S or SS). The first drawn card that has the final rank is the card that the original card
// the final rank is S or SS). The first drawn card that has the final rank is // transforms into. Notably, this logic means that cards are more likely to transform into cards that the player
// the card that the original card transforms into. Notably, this logic means // doesn't already have, or only has few copies of. Also notably, it is impossible for a card to transform into
// that cards are more likely to transform into cards that the player doesn't // another card that the player already has 50 or more copies of, or an S or SS card that the player already has 3
// already have, or only has few copies of. Also notably, it is impossible for // copies of.
// a card to transform into another card that the player already has 50 or
// more copies of, or an S or SS card that the player already has 3 copies of.
// //
// One curiosity about the above procedure is that the buckets can only hold // One curiosity about the above procedure is that the buckets can only hold 400 cards each for the N ranks, 300 each
// 400 cards each for the N ranks, 300 each for the R ranks, and 100 each for // for the R ranks, and 100 each for S and SS. It is possible for one bucket to overflow into the next, or to
// S and SS. It is possible for one bucket to overflow into the next, or to // overflow out of bounds and cause memory corruption, if there are (for example) more than 400 cards that have ranks
// overflow out of bounds and cause memory corruption, if there are (for // N1-N4, and the player has 99 of all of them.
// example) more than 400 cards that have ranks N1-N4, and the player has 99
// of all of them.
// //
// Remember the drop rates mentioned way back in the second paragraph of this // Remember the drop rates from back in the second paragraph of this enormous comment? That's what this array stores.
// enormous comment? That's what this array stores.
/* 009C */ parray<be_uint16_t, 2> drop_rates; /* 009C */ parray<be_uint16_t, 2> drop_rates;
/* 00A0 */ pstring<TextEncoding::ISO8859, 0x14> en_name; /* 00A0 */ pstring<TextEncoding::ISO8859, 0x14> en_name;
/* 00B4 */ pstring<TextEncoding::SJIS, 0x0B> jp_short_name; /* 00B4 */ pstring<TextEncoding::SJIS, 0x0B> jp_short_name;
/* 00BF */ pstring<TextEncoding::ISO8859, 0x08> en_short_name; /* 00BF */ pstring<TextEncoding::ISO8859, 0x08> en_short_name;
// These effects modify the card's behavior in various situations. Only // These effects modify the card's behavior in various situations. Only entries where effect_num != 0 are used.
// effects for which effect_num is not zero are used.
/* 00C7 */ parray<Effect, 3> effects; /* 00C7 */ parray<Effect, 3> effects;
/* 0127 */ uint8_t unused6; /* 0127 */ uint8_t unused6;
/* 0128 */ /* 0128 */
@@ -829,10 +778,9 @@ struct CardDefinitionsFooter {
struct DeckDefinition { struct DeckDefinition {
/* 00 */ pstring<TextEncoding::MARKED, 0x10> name; /* 00 */ pstring<TextEncoding::MARKED, 0x10> name;
/* 10 */ be_uint32_t client_id; // 0-3 /* 10 */ be_uint32_t client_id; // 0-3
// List of card IDs. The card count is the number of nonzero entries here // List of card IDs. The card count is the number of nonzero entries here before a zero entry (or 50 if no entries
// before a zero entry (or 50 if no entries are nonzero). The first card ID is // are nonzero). The first card ID is the SC card, which the game implicitly subtracts from the limit - so a valid
// the SC card, which the game implicitly subtracts from the limit - so a // deck should actually have 31 cards in it.
// valid deck should actually have 31 cards in it.
/* 14 */ parray<le_uint16_t, 50> card_ids; /* 14 */ parray<le_uint16_t, 50> card_ids;
/* 78 */ be_uint32_t unknown_a1; /* 78 */ be_uint32_t unknown_a1;
// Last modification time // Last modification time
@@ -847,64 +795,49 @@ struct DeckDefinition {
} __packed_ws__(DeckDefinition, 0x84); } __packed_ws__(DeckDefinition, 0x84);
struct PlayerConfig { struct PlayerConfig {
// The game splits this internally into two structures. The first column of // The game splits this internally into two structures. The first column of offsets is relative to the start of the
// offsets is relative to the start of the first structure; the second column // first structure; the second column is relative to the start of the second structure.
// is relative to the start of the second structure.
/* 0000:---- */ pstring<TextEncoding::MARKED, 12> rank_text; // From B7 command /* 0000:---- */ pstring<TextEncoding::MARKED, 12> rank_text; // From B7 command
/* 000C:---- */ parray<uint8_t, 0x1C> unknown_a1; /* 000C:---- */ parray<uint8_t, 0x1C> unknown_a1;
/* 0028:---- */ parray<be_uint16_t, 20> tech_menu_shortcut_entries; /* 0028:---- */ parray<be_uint16_t, 20> tech_menu_shortcut_entries;
/* 0050:---- */ parray<be_uint32_t, 10> choice_search_config; /* 0050:---- */ parray<be_uint32_t, 10> choice_search_config;
// This field maps to quest_counters on Episodes 1 & 2 // This field maps to quest_counters on Episodes 1 & 2
/* 0078:---- */ parray<be_uint32_t, 0x30> scenario_progress; /* 0078:---- */ parray<be_uint32_t, 0x30> scenario_progress;
// place_counts[0] and [1] from this field are added to the player's win and // place_counts[0] and [1] from this field are added to the player's win and loss count respectively when they're
// loss count respectively when they're shown in the status menu. However, // shown in the status menu. However, these values start at 0 and never seem to be modified. Perhaps in an earlier
// these values start at 0 and never seem to be modified. Perhaps in an // version, this was the offline records structure, but they later decided to just count online and offline records
// earlier version, this was the offline records structure, but they later // together in the main records structure and didn't remove the codepath that reads from this.
// decided to just count online and offline records together in the main
// records structure and didn't remove the codepath that reads from this.
/* 0138:---- */ PlayerRecordsBattleBE unused_offline_records; /* 0138:---- */ PlayerRecordsBattleBE unused_offline_records;
/* 0150:---- */ parray<uint8_t, 4> unknown_a4; /* 0150:---- */ parray<uint8_t, 4> unknown_a4;
// The PlayerDataSegment structure begins here. In newserv, we combine this // The PlayerDataSegment structure begins here. In newserv, we combine this structure into PlayerConfig since the two
// structure into PlayerConfig since the two are always used together on the // are always used together on the server side.
// server side.
/* 0154:0000 */ uint8_t is_encrypted; /* 0154:0000 */ uint8_t is_encrypted;
/* 0155:0001 */ uint8_t basis; /* 0155:0001 */ uint8_t basis;
/* 0156:0002 */ parray<uint8_t, 2> unused; /* 0156:0002 */ parray<uint8_t, 2> unused;
// The following fields (here through the beginning of decks) are encrypted // The following fields (here through the beginning of decks) are encrypted using the trivial algorithm, with the
// using the trivial algorithm, with the basis specified above, if // basis specified above, if is_encrypted is equal to 1.
// is_encrypted is equal to 1. // It appears the card counts field in this structure is actually 1000 (0x3E8) bytes long, even though in every other
// It appears the card counts field in this structure is actually 1000 (0x3E8) // place the counts array appears it's 0x2F0 bytes long. They presumably did this because of the checksum logic.
// bytes long, even though in every other place the counts array appears it's
// 0x2F0 bytes long. They presumably did this because of the checksum logic.
/* 0158:0004 */ parray<uint8_t, 1000> card_counts; /* 0158:0004 */ parray<uint8_t, 1000> card_counts;
// These appear to be an attempt at checksumming the card counts array, but // These appear to be an attempt at checksumming the card counts array, but the algorithm doesn't cover the entire
// the algorithm doesn't cover the entire array and instead reads from later // array and instead reads from later parts of this structure. This appears to be due to a copy/paste error in the
// parts of this structure. This appears to be due to a copy/paste error in // original code. The algorithm sums card_counts [0] through [19] and puts the result in card_count_checksums[0],
// the original code. The algorithm sums card_counts [0] through [19] and puts // then sums card counts [50] through [69] and puts the result in card_count_checksums[1], etc. Presumably they
// the result in card_count_checksums[0], then sums card counts [50] through // intended to use 20 as the stride instead of 50, which would have exactly covered the entire card_counts array.
// [69] and puts the result in card_count_checksums[1], etc. Presumably they
// intended to use 20 as the stride instead of 50, which would have exactly
// covered the entire card_counts array.
/* 0540:03EC */ parray<be_uint16_t, 50> card_count_checksums; /* 0540:03EC */ parray<be_uint16_t, 50> card_count_checksums;
// These 64-bit integers encode information about when rare cards (those with // These 64-bit integers encode information about when rare cards (those with ranks S, SS, E, or D2) were obtained.
// ranks S, SS, E, or D2) were obtained. Each integer contains the following // Each integer contains the following bit fields:
// fields:
// ???????? PPPPPPPP PPPPVVVV VVVVVVVV VVVVVVVV VVVVVVVV VVVVVVVV VVVVVVVV // ???????? PPPPPPPP PPPPVVVV VVVVVVVV VVVVVVVV VVVVVVVV VVVVVVVV VVVVVVVV
// The meaning of the high byte is unknown, but it is not used by the decoding // The meaning of the high byte is unknown, but it is not used by the decoding function. P is a prime number between
// function. P is a prime number between 1009 (0x3F1) and 2039 (0x7F7), // 1009 (0x3F1) and 2039 (0x7F7), inclusive. V is a 44-bit integer that, when modulated by P, yields the card ID
// inclusive. V is a 44-bit integer that, when modulated by P, yields the card // (that is, V % P == card_id). When a non-rare card is obtained or lost, the game just increments or decrements the
// ID (that is, V % P == card_id). When a non-rare card is obtained or lost, // value in the card_counts array above, but when a rare card is obtained or lost, the game adds or removes a token
// the game just increments or decrements the value in the card_counts array // in rare_tokens and recomputes the count for that card by scanning and decoding all rare tokens. It then writes
// above, but when a rare card is obtained or lost, the game adds or removes a // that count to card_counts. This seems to be an anti-cheating measure specifically targeted at memory editing - the
// token in rare_tokens and recomputes the count for that card by scanning and // server could verify that the count in card_counts is correct for rare cards by counting the valid tokens in this
// decoding all rare tokens. It then writes that count to card_counts. // array. (Sega seemed fairly concerned with memory editing in general in this game, since the card counts array is
// This seems to be an anti-cheating measure specifically targeted at memory // encrypted in memory most of the time, and they went out of their way to ensure the game uses an area of memory
// editing - the server could verify that the count in card_counts is correct // that almost no other game uses, which is also used by the Action Replay.)
// for rare cards by counting the valid tokens in this array. (Sega seemed
// fairly concerned with memory editing in general in this game, since the
// card counts array is encrypted in memory most of the time, and they went
// out of their way to ensure the game uses an area of memory that almost no
// other game uses, which is also used by the Action Replay.)
/* 05A4:0450 */ parray<be_uint64_t, 450> rare_tokens; /* 05A4:0450 */ parray<be_uint64_t, 450> rare_tokens;
/* 13B4:1260 */ parray<uint8_t, 0x80> unknown_a7; /* 13B4:1260 */ parray<uint8_t, 0x80> unknown_a7;
/* 1434:12E0 */ parray<DeckDefinition, 25> decks; /* 1434:12E0 */ parray<DeckDefinition, 25> decks;
@@ -915,24 +848,20 @@ struct PlayerConfig {
/* 00 */ be_uint32_t guild_card_number; /* 00 */ be_uint32_t guild_card_number;
/* 04 */ pstring<TextEncoding::MARKED, 0x18> name; /* 04 */ pstring<TextEncoding::MARKED, 0x18> name;
} __packed_ws__(PlayerReference, 0x1C); } __packed_ws__(PlayerReference, 0x1C);
// These two arrays are updated when a battle is started (via a 6xB4x05 // These two arrays are updated when a battle is started (via a 6xB4x05 command). The client adds the opposing
// command). The client adds the opposing players' info to ths first two // players' info to ths first two entries in recent_human_opponents if the opponents are human. (The existing entries
// entries in recent_human_opponents if the opponents are human. (The // are always moved back by two slots, but if one or both opponents are not humans, one or both of the newly-vacated
// existing entries are always moved back by two slots, but if one or both // slots is not filled in.) Both arrays have the most recent entries at the beginning.
// opponents are not humans, one or both of the newly-vacated slots is not
// filled in.) Both arrays have the most recent entries at the beginning.
/* 2128:1FD4 */ parray<PlayerReference, 10> recent_human_opponents; /* 2128:1FD4 */ parray<PlayerReference, 10> recent_human_opponents;
/* 2240:20EC */ parray<be_uint32_t, 5> recent_battle_start_timestamps; /* 2240:20EC */ parray<be_uint32_t, 5> recent_battle_start_timestamps;
/* 2254:2100 */ parray<uint8_t, 0x14> unknown_a10; /* 2254:2100 */ parray<uint8_t, 0x14> unknown_a10;
/* 2268:2114 */ be_uint32_t init_timestamp; /* 2268:2114 */ be_uint32_t init_timestamp;
/* 226C:2118 */ be_uint32_t last_online_battle_start_timestamp; /* 226C:2118 */ be_uint32_t last_online_battle_start_timestamp;
// In a certain situation, unknown_t3 is set to init_timestamp plus a multiple // In a certain situation, unknown_t3 is set to init_timestamp plus a multiple of two weeks (1209600 seconds).
// of two weeks (1209600 seconds). unknown_t3 appears never to be used for // unknown_t3 appears never to be used for anything, though.
// anything, though.
/* 2270:211C */ be_uint32_t unknown_t3; /* 2270:211C */ be_uint32_t unknown_t3;
// This visual config is copied to the player's main visual config when the // This visual config is copied to the player's main visual config when the player's name or proportions have
// player's name or proportions have changed, or when certain buttons on the // changed, or when certain buttons on the controller (L, R, X, Y) are held at game start time.
// controller (L, R, X, Y) are held at game start time.
/* 2274:2120 */ PlayerVisualConfig backup_visual; /* 2274:2120 */ PlayerVisualConfig backup_visual;
/* 22C4:2170 */ parray<uint8_t, 0x8C> unknown_a14; /* 22C4:2170 */ parray<uint8_t, 0x8C> unknown_a14;
/* 2350:21FC */ /* 2350:21FC */
@@ -998,11 +927,11 @@ enum class AllowedCards : uint8_t {
}; };
struct Rules { struct Rules {
// When this structure is used in a map/quest definition, FF in any of these // When this structure is used in a map/quest definition, FF in any of these fields means the user is allowed to
// fields means the user is allowed to override it. Any non-FF fields are // override it. Any non-FF fields are fixed for the map/quest and cannot be overridden.
// fixed for the map/quest and cannot be overridden.
// The overall time limit is specified in increments of 5 minutes; that is, // The overall time limit is specified in increments of 5 minutes; that is, 1 means 5 minutes, 2 means 10 minutes,
// 1 means 5 minutes, 2 means 10 minutes, etc. 0 means no overall time limit. // etc. 0 means no overall time limit.
/* 00 */ uint8_t overall_time_limit = 0; /* 00 */ uint8_t overall_time_limit = 0;
/* 01 */ uint8_t phase_time_limit = 0; // In seconds; 0 = unlimited /* 01 */ uint8_t phase_time_limit = 0; // In seconds; 0 = unlimited
/* 02 */ AllowedCards allowed_cards = AllowedCards::ALL; /* 02 */ AllowedCards allowed_cards = AllowedCards::ALL;
@@ -1016,21 +945,14 @@ struct Rules {
/* 0A */ uint8_t disable_dialogue = 0; // 0 = dialogue on, 1 = dialogue off /* 0A */ uint8_t disable_dialogue = 0; // 0 = dialogue on, 1 = dialogue off
/* 0B */ DiceExchangeMode dice_exchange_mode = DiceExchangeMode::HIGH_ATK; /* 0B */ DiceExchangeMode dice_exchange_mode = DiceExchangeMode::HIGH_ATK;
/* 0C */ uint8_t disable_dice_boost = 0; // 0 = dice boost on, 1 = off /* 0C */ uint8_t disable_dice_boost = 0; // 0 = dice boost on, 1 = off
// NOTE: The following fields are unused in PSO's implementation, but newserv // NOTE: The following fields are unused in PSO's implementation, but newserv uses them to implement extended rules.
// uses them to implement extended rules.
/* 0D */ uint8_t def_dice_value_range = 0; // High 4 bits = min, low 4 = max /* 0D */ uint8_t def_dice_value_range = 0; // High 4 bits = min, low 4 = max
// These fields specify override dice ranges for the 1-player team in 2v1 // These fields specify override dice ranges for the 1-player team in 2v1.
/* 0E */ uint8_t atk_dice_value_range_2v1 = 0; // High 4 bits = min, low 4 = max /* 0E */ uint8_t atk_dice_value_range_2v1 = 0; // High 4 bits = min, low 4 = max
/* 0F */ uint8_t def_dice_value_range_2v1 = 0; // High 4 bits = min, low 4 = max /* 0F */ uint8_t def_dice_value_range_2v1 = 0; // High 4 bits = min, low 4 = max
/* 10 */ parray<uint8_t, 4> unused; /* 10 */ parray<uint8_t, 4> unused;
/* 14 */ /* 14 */
// Annoyingly, this structure is a different size in Episode 3 Trial Edition.
// This means that many command formats, as well as the map format, are
// different, and the existing Server implementation can't serve Trial Edition
// clients. It'd be nice to support Trial Edition battles, but that would
// likely be more work than it's worth.
Rules() = default; Rules() = default;
explicit Rules(const phosg::JSON& json); explicit Rules(const phosg::JSON& json);
phosg::JSON json() const; phosg::JSON json() const;
@@ -1053,9 +975,8 @@ struct RulesTrial {
/* 00 */ uint8_t overall_time_limit = 0; /* 00 */ uint8_t overall_time_limit = 0;
/* 01 */ uint8_t phase_time_limit = 0; /* 01 */ uint8_t phase_time_limit = 0;
/* 02 */ AllowedCards allowed_cards = AllowedCards::ALL; /* 02 */ AllowedCards allowed_cards = AllowedCards::ALL;
// In NTE, the dice behave differently than in non-NTE. A zero in either of // In NTE, the dice behave differently than in non-NTE. A zero in either of these fields means the corresponding die
// these fields means the corresponding die is random in the range [1, 6]; // is random in the range [1, 6]; any nonzero value means that die will always take that value.
// any nonzero value means that die will always take that value.
/* 03 */ uint8_t atk_die_behavior = 0; /* 03 */ uint8_t atk_die_behavior = 0;
/* 04 */ uint8_t def_die_behavior = 0; /* 04 */ uint8_t def_die_behavior = 0;
/* 05 */ uint8_t disable_deck_shuffle = 0; /* 05 */ uint8_t disable_deck_shuffle = 0;
@@ -1101,14 +1022,13 @@ struct MapList {
be_uint32_t total_size; // Including header, entries, and strings be_uint32_t total_size; // Including header, entries, and strings
struct Entry { struct Entry {
// The fields in this structure have the same meanings as the corresponding // The fields in this structure have the same meanings as the corresponding fields in MapDefinition.
// fields in MapDefinition.
/* 0000 */ be_uint16_t map_x; /* 0000 */ be_uint16_t map_x;
/* 0002 */ be_uint16_t map_y; /* 0002 */ be_uint16_t map_y;
/* 0004 */ be_uint16_t environment_number; /* 0004 */ be_uint16_t environment_number;
/* 0006 */ be_uint16_t map_number; /* 0006 */ be_uint16_t map_number;
// Text offsets are from the beginning of the strings block after all map // Text offsets are from the beginning of the strings block after all map entries (that is, add strings_offset to
// entries (that is, add strings_offset to them to get the string offset) // them to get the string offset)
/* 0008 */ be_uint32_t name_offset; /* 0008 */ be_uint32_t name_offset;
/* 000C */ be_uint32_t location_name_offset; /* 000C */ be_uint32_t location_name_offset;
/* 0010 */ be_uint32_t quest_name_offset; /* 0010 */ be_uint32_t quest_name_offset;
@@ -1134,12 +1054,11 @@ struct CompressedMapHeader { // .mnm file format
} __packed_ws__(CompressedMapHeader, 8); } __packed_ws__(CompressedMapHeader, 8);
struct OverlayState { struct OverlayState {
// In the tiles array, the high 4 bits of each value are the tile type, and // In the tiles array, the high 4 bits are the tile type, and the low 4 bits are the subtype. The values are:
// the low 4 bits are the subtype. The types are: // 10-1F: blocked by rock (as if the corresponding map_tiles value was 00; low 4 bits specify rotation in
// 10-1F: blocked by rock (as if the corresponding map_tiles value was 00; // increments of 1/4 turn)
// low 4 bits specify rotation in increments of 1/4 turn) // 20-2F: blocked by fence (as if the corresponding map_tiles value was 00; low 4 bits specify rotation in
// 20-2F: blocked by fence (as if the corresponding map_tiles value was 00; // increments of 1/4 turn)
// low 4 bits specify rotation in increments of 1/4 turn)
// 30-34: teleporters (each value should appear exactly twice or not at all): // 30-34: teleporters (each value should appear exactly twice or not at all):
// 30: purple teleporters // 30: purple teleporters
// 31: red teleporters // 31: red teleporters
@@ -1147,29 +1066,26 @@ struct OverlayState {
// 33: blue teleporters // 33: blue teleporters
// 34: purple teleporters (appears the same as 30 but is a distinct set) // 34: purple teleporters (appears the same as 30 but is a distinct set)
// 40-4F: traps on NTE // 40-4F: traps on NTE
// 40-44: traps on non-NTE (there may be up to 8 of each type, and one of // 40-44: traps on non-NTE (there may be up to 8 of each type, and one of each is chosen to be a real trap at
// each is chosen to be a real trap at battle start); the trap types are: // battle start); the trap types are:
// 40: Dice Fever, Heavy Fog, Muscular, Immortality, Snail Pace // 40: Dice Fever, Heavy Fog, Muscular, Immortality, Snail Pace
// 41: Gold Rush, Charity, Requiem // 41: Gold Rush, Charity, Requiem
// 42: Powerless Rain, Trash 1, Empty Hand, Skip Draw // 42: Powerless Rain, Trash 1, Empty Hand, Skip Draw
// 43: Brave Wind, Homesick, Fly // 43: Brave Wind, Homesick, Fly
// 44: Dice+1, Battle Royale, Reverse Card, Giant Garden, Fix // 44: Dice+1, Battle Royale, Reverse Card, Giant Garden, Fix
// 50: blocked by metal box (appears as an improperly-z-buffered teal cube in // 50: blocked by metal box (appears as teal cube in preview; behaves like 10 and 20 in game)
// preview; behaves like 10 and 20 in game)
// Any other value here will behave like 00 (no special tile behavior). // Any other value here will behave like 00 (no special tile behavior).
parray<parray<uint8_t, 0x10>, 0x10> tiles; parray<parray<uint8_t, 0x10>, 0x10> tiles;
// This field appears to be unused in both NTE and the final version. Perhaps // This field appears to be unused in both NTE and the final version. Perhaps it had meaning in a pre-NTE version.
// it had some meaning in a pre-NTE version.
parray<le_uint32_t, 5> unused1; parray<le_uint32_t, 5> unused1;
// TODO: Figure out exactly where these colors are used // TODO: Figure out exactly where these colors are used
parray<le_uint32_t, 0x10> trap_tile_colors_nte; // Unused on non-NTE parray<le_uint32_t, 0x10> trap_tile_colors_nte; // Unused on non-NTE
// This specifies the assist card IDs that each trap value (40-4F) will set // This specifies the assist card IDs that each trap value (40-4F) will set when triggered. This only has an effect
// when triggered. This only has an effect on NTE; on non-NTE, this is unused // on NTE; on non-NTE, this is unused and a fixed set of assist cards is used instead. (On newserv, the set of used
// and a fixed set of assist cards is used instead. (On newserv, the set of // assist cards can be overridden in the server configuration.)
// used assist cards can be overridden in the server configuration.)
parray<le_uint16_t, 0x10> trap_card_ids_nte; parray<le_uint16_t, 0x10> trap_card_ids_nte;
OverlayState(); OverlayState();
@@ -1177,16 +1093,14 @@ struct OverlayState {
} __packed_ws__(OverlayState, 0x174); } __packed_ws__(OverlayState, 0x174);
struct MapDefinition { // .mnmd format; also the format of (decompressed) quests struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
// If tag is not 0x00000100, the game considers the map to be corrupt in // If tag is not 0x00000100, the game considers the map to be corrupt in offline mode and will delete it (if it's a
// offline mode and will delete it (if it's a download quest). The tag field // download quest). The tag field doesn't seem to have any other use. In Trial Edition, download quests are expected
// doesn't seem to have any other use. In Trial Edition, download quests are // to have 0x96 here instead.
// expected to have 0x96 here instead.
/* 0000 */ be_uint32_t tag; /* 0000 */ be_uint32_t tag;
/* 0004 */ be_uint32_t map_number; // Must be unique across all maps /* 0004 */ be_uint32_t map_number; // Must be unique across all maps
// The maximum map size is 16 tiles in either dimension, since the various // The maximum map size is 16 tiles in either dimension, since the various tiles arrays below are fixed sizes.
// tiles arrays below are fixed sizes.
/* 0008 */ uint8_t width; /* 0008 */ uint8_t width;
/* 0009 */ uint8_t height; /* 0009 */ uint8_t height;
@@ -1215,9 +1129,8 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
// 10 = (Amplum Umbra map) ("RUIN") // 10 = (Amplum Umbra map) ("RUIN")
// 11 = Via Tubus ("METRO") // 11 = Via Tubus ("METRO")
// 12 = Morgue ("NORMAL MORGUE") // 12 = Morgue ("NORMAL MORGUE")
// Environment numbers above 12 are replaced with 0B (Cyber) if specified in // Environment numbers above 12 are replaced with 0B (Cyber) if specified in a map definition. The following
// a map definition. The following environment numbers are used internally by // environment numbers are used internally by the game for various functions, but cannot be specified in maps:
// the game for various functions, but cannot be specified in MapDefinitions:
// 13 = TCardDemo (sends CAx14 and CAx13, then softlocks at black screen) // 13 = TCardDemo (sends CAx14 and CAx13, then softlocks at black screen)
// 14 = crashes // 14 = crashes
// 15 = crashes // 15 = crashes
@@ -1235,20 +1148,16 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
// In the map_tiles array, the values are usually: // In the map_tiles array, the values are usually:
// 00 = not a valid tile (blocked) // 00 = not a valid tile (blocked)
// 01 = valid tile unless modified out (via modification_tiles) // 01 = valid tile unless modified out (via modification_tiles)
// 02 = team A start (1v1) // 02 = team A start (1v1) (can be redefined by start_tile_definitions)
// 03, 04 = team A start (2v2) // 03, 04 = team A start (2v2) (can be redefined by start_tile_definitions)
// 06, 07 = team B start (2v2) // 06, 07 = team B start (2v2) (can be redefined by start_tile_definitions)
// 08 = team B start (1v1) // 08 = team B start (1v1) (can be redefined by start_tile_definitions)
// These values can be redefined by start_tile_definitions below, however. // Note that the game displays the map reversed vertically in the preview window. For example, player 1 is on team A,
// Note that the game displays the map reversed vertically in the preview // which usually starts at the top of the map as defined in this struct, or at the bottom as shown in the preview.
// window. For example, player 1 is on team A, which usually starts at the top
// of the map as defined in this struct, or at the bottom as shown in the
// preview window.
/* 000C */ parray<parray<uint8_t, 0x10>, 0x10> map_tiles; /* 000C */ parray<parray<uint8_t, 0x10>, 0x10> map_tiles;
// The start_tile_definitions field is a list of 6 bytes for each team. The // The start_tile_definitions field is a list of 6 bytes for each team. The low 6 bits of each byte match the
// low 6 bits of each byte match the starting location for the relevant player // starting location for the relevant player in map_tiles; the high 2 bits are the player's initial facing direction.
// in map_tiles; the high 2 bits are the player's initial facing direction.
// - If the team has 1 player, only byte [0] is used. // - If the team has 1 player, only byte [0] is used.
// - If the team has 2 players, bytes [1] and [2] are used. // - If the team has 2 players, bytes [1] and [2] are used.
// - If the team has 3 players, bytes [3] through [5] are used. // - If the team has 3 players, bytes [3] through [5] are used.
@@ -1259,11 +1168,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
/* 24 */ be_float camera_x; /* 24 */ be_float camera_x;
/* 28 */ be_float camera_y; /* 28 */ be_float camera_y;
/* 2C */ be_float camera_z; /* 2C */ be_float camera_z;
// It appears that the camera always aligns its +Y raster axis with +Y in // It appears that the camera always aligns its +Y raster axis with +Y in the virtual world. If the focus point is
// the virtual world. If the focus point is directly beneath the camera // directly beneath the camera point, the logic for deciding which direction should be "up" from the camera's
// point, the logic for deciding which direction should be "up" from the // perspective can get confused and jitter back and forth as the camera moves into position.
// camera's perspective can get confused and jitter back and forth as the
// camera moves into position.
/* 30 */ be_float focus_x; /* 30 */ be_float focus_x;
/* 34 */ be_float focus_y; /* 34 */ be_float focus_y;
/* 38 */ be_float focus_z; /* 38 */ be_float focus_z;
@@ -1274,29 +1181,22 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
phosg::JSON json() const; phosg::JSON json() const;
} __packed_ws__(CameraSpec, 0x48); } __packed_ws__(CameraSpec, 0x48);
// This array specifies the camera zone maps. A camera zone map is a subset of // This array specifies the camera zone maps. A camera zone map is a subset of the main map (specified in map_tiles).
// the main map (specified in map_tiles). Tiles that are part of each camera // Tiles that are part of each camera zone are 1 in these arrays; all other tiles are 0. The game evaluates each
// zone are 1 in these arrays; all other tiles are 0. The game evaluates each // camera zone in order; if all SCs and FCs are within a particular camera zone, then the corresponding camera
// camera zone in order; if all SCs and FCs are within a particular camera // location is used as the default camera location. If the player doesn't move the camera with the C stick, then the
// zone, then the corresponding camera location is used as the default camera // camera zones are evaluated continuously during the battle, and the camera will move to focus on the part of the
// location. If the player doesn't move the camera with the C stick, then the // field where the SCs/FCs are. (Or, more accurately, where the corresponding entry in camera_zone_specs says to
// camera zones are evaluated continuously during the battle, and the camera // focus.) camera_zone_maps is indexed as [team_id][camera_zone_num][y][x]; camera_zone_specs is indexed as
// will move to focus on the part of the field where the SCs/FCs are. (Or, // [team_id][camera_zone_num]. Unused entries (beyond num_camera_zones) in both arrays should be filled with 0xFF.
// more accurately, where the corresponding entry in camera_zone_specs says
// to focus.) camera_zone_maps is indexed as [team_id][camera_zone_num][y][x];
// camera_zone_specs is indexed as [team_id][camera_zone_num]. Unused entries
// (beyond num_camera_zones) in both arrays should be filled with FF bytes.
/* 0118 */ parray<parray<parray<parray<uint8_t, 0x10>, 0x10>, 10>, 2> camera_zone_maps; /* 0118 */ parray<parray<parray<parray<uint8_t, 0x10>, 0x10>, 10>, 2> camera_zone_maps;
/* 1518 */ parray<parray<CameraSpec, 10>, 2> camera_zone_specs; /* 1518 */ parray<parray<CameraSpec, 10>, 2> camera_zone_specs;
// These camera specs are used in the Move phase, when the player has chosen // These camera specs are used in the Move phase, when the player has chosen an SC or FC to move, or when the player
// an SC or FC to move, or when the player presses Start/Z. Normally these are // presses Start/Z. Normally these are defined such that the camera is placed high above the map, giving an overhead
// defined such that the camera is placed high above the map, giving an // view of the entire playfield. This is indexed as [???][team_id] (it is not yet known what the major index is).
// overhead view of the entire playfield. This is indexed as [???][team_id]
// (it is not yet known what the major index represents).
/* 1AB8 */ parray<parray<CameraSpec, 2>, 3> overview_specs; /* 1AB8 */ parray<parray<CameraSpec, 2>, 3> overview_specs;
// This specifies the locations of blocked tiles, teleporters, and traps. See // This specifies the locations of blocked tiles, teleporters, and traps. See comments in OverlayState for details.
// the comments in OverlayState for details.
/* 1C68 */ OverlayState overlay_state; /* 1C68 */ OverlayState overlay_state;
/* 1DDC */ Rules default_rules; /* 1DDC */ Rules default_rules;
@@ -1306,8 +1206,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
/* 1E18 */ pstring<TextEncoding::MARKED, 0x3C> quest_name; // == location_name if not a quest /* 1E18 */ pstring<TextEncoding::MARKED, 0x3C> quest_name; // == location_name if not a quest
/* 1E54 */ pstring<TextEncoding::MARKED, 0x190> description; /* 1E54 */ pstring<TextEncoding::MARKED, 0x190> description;
// These fields describe where the map cursor on the preview screen should // These fields describe where the map cursor on the preview screen should scroll to.
// scroll to
/* 1FE4 */ be_uint16_t map_x; /* 1FE4 */ be_uint16_t map_x;
/* 1FE6 */ be_uint16_t map_y; /* 1FE6 */ be_uint16_t map_y;
@@ -1319,10 +1218,8 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
} __packed_ws__(NPCDeck, 0x58); } __packed_ws__(NPCDeck, 0x58);
/* 1FE8 */ parray<NPCDeck, 3> npc_decks; // Unused if name[0] == 0 /* 1FE8 */ parray<NPCDeck, 3> npc_decks; // Unused if name[0] == 0
// These are almost (but not quite) the same format as the entries in // These are not quite the same format as the entries in aiprm.dat. These entries are only used if the corresponding
// aiprm.dat. These entries are only used if the corresponding NPC exists // NPC exists (if .name[0] is not 0) and if the corresponding entry in the npc_ai_params_entry_index is -1.
// (if .name[0] is not 0) and if the corresponding entry in the
// npc_ai_params_entry_index is -1.
struct AIParams { struct AIParams {
/* 0000 */ parray<be_uint16_t, 2> unknown_a1; /* 0000 */ parray<be_uint16_t, 2> unknown_a1;
/* 0004 */ uint8_t is_arkz; /* 0004 */ uint8_t is_arkz;
@@ -1337,10 +1234,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
/* 242C */ parray<uint8_t, 8> unknown_a7; /* 242C */ parray<uint8_t, 8> unknown_a7;
// This array specifies which set of predefined AI parameters (in aiprm.dat) // This array specifies which set of predefined AI parameters (in aiprm.dat) to use for each NPC. If a value in this
// to use for each NPC. If a value in this array is -1 (FFFFFFFF), then the // array is -1 (FFFFFFFF), then the corresponding NPC's AI parameters are defined in the AIParams structure above.
// corresponding NPC's AI parameters are defined in the AIParams structure // The names of the AI parameter sets defined in aiprm.dat are:
// above. The names of the AI parameter sets defined in aiprm.dat are:
// 00 => Sample_Hunter 0D => Sample_Dark 1A => LKnight // 00 => Sample_Hunter 0D => Sample_Dark 1A => LKnight
// 01 => Glustar 0E => Break 1B => Boss_Castor // 01 => Glustar 0E => Break 1B => Boss_Castor
// 02 => Guykild 0F => Creinu 1C => Boss_Pollux // 02 => Guykild 0F => Creinu 1C => Boss_Pollux
@@ -1354,82 +1250,70 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
// 0A => Sample_Hunter 17 => Reiz // 0A => Sample_Hunter 17 => Reiz
// 0B => Teifu 18 => Rio // 0B => Teifu 18 => Rio
// 0C => Viviana 19 => Rufina // 0C => Viviana 19 => Rufina
// Presumably 0A is meant to be Stella and 1D is meant to be Amplum Umbra, but // Presumably 0A is meant to be Stella and 1D is meant to be Amplum Umbra, but they forgot to change the names.
// they forgot to change the names.
/* 2434 */ parray<be_int32_t, 3> npc_ai_params_entry_index; /* 2434 */ parray<be_int32_t, 3> npc_ai_params_entry_index;
// before_message appears before the battle if it's not blank. after_message // before_message appears before the battle if it's not blank. after_message appears after the battle if it's not
// appears after the battle if it's not blank. dispatch_message appears right // blank. dispatch_message appears right before the player chooses a deck if it's not blank; usually it says
// before the player chooses a deck if it's not blank; usually it says
// something like "You can only dispatch <character>". // something like "You can only dispatch <character>".
/* 2440 */ pstring<TextEncoding::MARKED, 0x190> before_message; /* 2440 */ pstring<TextEncoding::MARKED, 0x190> before_message;
/* 25D0 */ pstring<TextEncoding::MARKED, 0x190> after_message; /* 25D0 */ pstring<TextEncoding::MARKED, 0x190> after_message;
/* 2760 */ pstring<TextEncoding::MARKED, 0x190> dispatch_message; /* 2760 */ pstring<TextEncoding::MARKED, 0x190> dispatch_message;
struct DialogueSet { struct DialogueSet {
// Dialogue sets specify lines that COMs can say at certain points during // Dialogue sets specify lines that COMs can say at certain points during the battle. They only apply to COMs which
// the battle. They only apply to COMs which are defined as NPCs in the map // are defined as NPCs in the map definition; human players and COMs chosen by humans never say these lines.
// definition; human players and COMs chosen by humans never say lines from
// dialogue sets.
// when specifies the situation in which this dialogue set activates. The // when specifies the situation in which this dialogue set activates. The values 0000-000C (inclusive) can be used
// values 0000-000C (inclusive) can be used here, or FFFF if the entire // here, or FFFF if the entire dialogue set is unused. The known values are:
// dialogue set is unused. The known values are:
// 0001: Activates at battle start if player is an opponent // 0001: Activates at battle start if player is an opponent
// 0006: Activates when below 50% HP // 0006: Activates when below 50% HP
// 0008: Activates at battle start if player is an ally // 0008: Activates at battle start if player is an ally
/* 0000 */ be_int16_t when; // 0x00-0x0C, or FFFF if unused /* 0000 */ be_int16_t when; // 0x00-0x0C, or FFFF if unused
/* 0002 */ be_uint16_t percent_chance; // 0-100, or FFFF if unused /* 0002 */ be_uint16_t percent_chance; // 0-100, or FFFF if unused
// If the dialogue set activates, the game randomly chooses one of these // If the dialogue set activates, the game randomly chooses one of these strings, excluding any that are empty or
// strings, excluding any that are empty or begin with the character '^'. // begin with the character '^'.
/* 0004 */ parray<pstring<TextEncoding::MARKED, 0x40>, 4> strings; /* 0004 */ parray<pstring<TextEncoding::MARKED, 0x40>, 4> strings;
/* 0104 */ /* 0104 */
phosg::JSON json(Language language) const; phosg::JSON json(Language language) const;
} __packed_ws__(DialogueSet, 0x104); } __packed_ws__(DialogueSet, 0x104);
// There are up to 0x10 of these per valid NPC, but only the first 13 of them // There are up to 0x10 of these per valid NPC, but only the first 13 of them are used, since each one must have a
// are used, since each one must have a unique value for .when and the values // unique value for .when and the values there can only be 0-12.
// there can only be 0-12.
/* 28F0 */ parray<parray<DialogueSet, 0x10>, 3> dialogue_sets; /* 28F0 */ parray<parray<DialogueSet, 0x10>, 3> dialogue_sets;
// These card IDs are always given to the player when they win a battle on // These card IDs are always given to the player when they win a battle on this map. Unused entries should be set to
// this map. Unused entries should be set to FFFF. Cards in this array are // FFFF. Cards in this array are ignored if they have any of these features (in the card definition):
// ignored if they have any of these features (in the card definition):
// - type is HUNTERS_SC or ARKZ_SC // - type is HUNTERS_SC or ARKZ_SC
// - card_class is BOSS_ATTACK_ACTION or BOSS_TECH // - card_class is BOSS_ATTACK_ACTION or BOSS_TECH
// - rank is D1, D2, or D3 // - rank is D1, D2, or D3
// - cannot_drop is 1 (specifically 1; other values don't prevent cards from // - cannot_drop is 1 (specifically 1; other values don't prevent cards from appearing)
// appearing)
/* 59B0 */ parray<be_uint16_t, 0x10> reward_card_ids; /* 59B0 */ parray<be_uint16_t, 0x10> reward_card_ids;
// These fields are used when determining which cards to drop after the battle // These fields are used when determining which cards to drop after the battle is complete. If either is negative,
// is complete. If either is negative, the player's CLv is used instead. // the player's CLv is used instead.
/* 59D0 */ be_int32_t win_level_override; /* 59D0 */ be_int32_t win_level_override;
/* 59D4 */ be_int32_t loss_level_override; /* 59D4 */ be_int32_t loss_level_override;
// The field offsets specify where the battlefield should appear relative to // The field offsets specify where the battlefield should appear relative to the center of the environment. The size
// the center of the environment. The size of one tile on the field is 25 // of one tile on the field is 25 units in these fields.
// units in these fields.
/* 59D8 */ be_int16_t field_offset_x; /* 59D8 */ be_int16_t field_offset_x;
/* 59DA */ be_int16_t field_offset_y; /* 59DA */ be_int16_t field_offset_y;
// map_category specifies where the map should appear in the maps menu. If // map_category specifies where the map should appear in the maps menu. If this is 0, 1, or 2, the map appears in the
// this is 0, 1, or 2, the map appears in the Quest section; otherwise, it // Quest section; otherwise, it appears in the Free Battle section instead. It's not known if this controls anything
// appears in the Free Battle section instead. It's not known if this controls // else, or what the difference is in behavior between 0, 1, and 2.
// anything else, or what the difference is in behavior between 0, 1, and 2.
/* 59DC */ uint8_t map_category; /* 59DC */ uint8_t map_category;
// This field determines block graphics to be used in the Cyber environment. // This field determines block graphics to be used in the Cyber environment. There are 10 block types (0-9); if this
// There are 10 block types (0-9); if this value is > 9, type 0 is used. This // value is > 9, type 0 is used. This field has no effect in Ep3 NTE, even though there are 6 different block texture
// field has no effect in Ep3 NTE, even though there are 6 different block // files on the NTE disc.
// texture files on the NTE disc.
/* 59DD */ uint8_t cyber_block_type; /* 59DD */ uint8_t cyber_block_type;
/* 59DE */ be_uint16_t unknown_a11; /* 59DE */ be_uint16_t unknown_a11;
// This array specifies which SC characters can't participate in the quest // This array specifies which SC characters can't participate in the quest (that is, the player is not allowed to
// (that is, the player is not allowed to choose decks with these SC cards). // choose decks with these SC cards). The values in this array don't match the SC card IDs, however:
// The values in this array don't match the SC card IDs, however:
// value in array => SC name (SC card ID) // value in array => SC name (SC card ID)
// 0000 => Guykild (0005) 000C => Hyze (0117) // 0000 => Guykild (0005) 000C => Hyze (0117)
// 0001 => Kylria (0006) 000D => Rufina (0118) // 0001 => Kylria (0006) 000D => Rufina (0118)
@@ -1443,8 +1327,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
// 0009 => Orland (0001) 0015 => Memoru (011D) // 0009 => Orland (0001) 0015 => Memoru (011D)
// 000A => Stella (0114) 0016 => K.C. (011E) // 000A => Stella (0114) 0016 => K.C. (011E)
// 000B => Glustar (0115) 0017 => Ohgun (011F) // 000B => Glustar (0115) 0017 => Ohgun (011F)
// These values normally can't be used by the player, but are recognized // These values normally can't be used by the player, but are recognized internally by the game:
// internally by the game:
// 0018 => HERO_1 (02AA) 0021 => DARK_4 (02B3) // 0018 => HERO_1 (02AA) 0021 => DARK_4 (02B3)
// 0019 => HERO_2 (02AB) 0022 => DARK_5 (02B4) // 0019 => HERO_2 (02AB) 0022 => DARK_5 (02B4)
// 001A => HERO_3 (02AC) 0023 => DARK_6 (02B5) // 001A => HERO_3 (02AC) 0023 => DARK_6 (02B5)
@@ -1484,9 +1367,8 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
return (this->map_category <= 2); return (this->map_category <= 2);
} }
// This function throws runtime_error if the passed-in map is not semantically // This function throws runtime_error if the passed-in map is not semantically equivalent to *this. Semantic
// equivalent to *this. Semantic equivalence means all fields that affect // equivalence means all fields that affect gameplay and visuals are equivalent, but dialogue, names, and description
// gameplay and visuals are equivalent, but dialogue, names, and description
// text may differ. // text may differ.
void assert_semantically_equivalent(const MapDefinition& other) const; void assert_semantically_equivalent(const MapDefinition& other) const;
@@ -1495,8 +1377,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
} __packed_ws__(MapDefinition, 0x5A18); } __packed_ws__(MapDefinition, 0x5A18);
struct MapDefinitionTrial { struct MapDefinitionTrial {
// This is the format of Episode 3 Trial Edition maps. See the comments in // This is the format of Episode 3 Trial Edition maps. See the comments in MapDefinition for what each field means.
// MapDefinition for what each field means.
/* 0000 */ be_uint32_t tag; /* 0000 */ be_uint32_t tag;
/* 0004 */ be_uint32_t map_number; /* 0004 */ be_uint32_t map_number;
@@ -1533,8 +1414,8 @@ struct MapDefinitionTrial {
/* 4174 */ uint8_t map_category; /* 4174 */ uint8_t map_category;
/* 4175 */ uint8_t cyber_block_type; /* 4175 */ uint8_t cyber_block_type;
/* 4176 */ be_uint16_t unknown_a11; /* 4176 */ be_uint16_t unknown_a11;
// TODO: This field may contain some version of unavailable_sc_cards and/or // TODO: This field may contain some version of unavailable_sc_cards and/or entry_states from MapDefinition, but the
// entry_states from MapDefinition, but the format isn't the same // format isn't the same
/* 4178 */ parray<uint8_t, 0x28> unknown_t12; /* 4178 */ parray<uint8_t, 0x28> unknown_t12;
/* 41A0 */ /* 41A0 */
@@ -1681,8 +1562,7 @@ public:
} }
private: private:
// The compressed map lists are generated on demand from the maps map below. // The compressed map lists are generated on demand; these are indexed as [language][num_players]
// THey are indexed as [language][num_players]
mutable std::vector<std::array<std::string, 4>> compressed_map_lists_trial; mutable std::vector<std::array<std::string, 4>> compressed_map_lists_trial;
mutable std::vector<std::array<std::string, 4>> compressed_map_lists_final; mutable std::vector<std::array<std::string, 4>> compressed_map_lists_final;
+1 -1
View File
@@ -3356,7 +3356,7 @@ void send_rare_enemy_index_list(shared_ptr<Client> c, const vector<size_t>& inde
} }
void send_quest_function_call(std::shared_ptr<Channel> ch, uint16_t label) { void send_quest_function_call(std::shared_ptr<Channel> ch, uint16_t label) {
S_CallQuestFunction_V3_BB_AB cmd; S_CallQuestLabel_V3_BB_AB cmd;
cmd.label = label; cmd.label = label;
ch->send(0xAB, 0x00, &cmd, sizeof(cmd)); ch->send(0xAB, 0x00, &cmd, sizeof(cmd));
} }