add learnings from Ep3 Trial Edition download quest
This commit is contained in:
+127
-6
@@ -1256,8 +1256,7 @@ void Rules::clear() {
|
||||
this->dice_exchange_mode = DiceExchangeMode::HIGH_ATK;
|
||||
this->disable_dice_boost = 0;
|
||||
this->def_dice_range = 0;
|
||||
this->unused1 = 0;
|
||||
this->unused2 = 0;
|
||||
this->unused.clear(0);
|
||||
}
|
||||
|
||||
string Rules::str() const {
|
||||
@@ -1396,6 +1395,40 @@ string Rules::str() const {
|
||||
return "Rules[" + join(tokens, ", ") + "]";
|
||||
}
|
||||
|
||||
RulesTrial::RulesTrial(const Rules& r)
|
||||
: overall_time_limit(r.overall_time_limit),
|
||||
phase_time_limit(r.phase_time_limit),
|
||||
allowed_cards(r.allowed_cards),
|
||||
atk_dice_max(r.max_dice),
|
||||
def_dice_max(r.max_dice),
|
||||
disable_deck_shuffle(r.disable_deck_shuffle),
|
||||
disable_deck_loop(r.disable_deck_loop),
|
||||
char_hp(r.char_hp),
|
||||
hp_type(r.hp_type),
|
||||
no_assist_cards(r.no_assist_cards),
|
||||
disable_dialogue(r.disable_dialogue),
|
||||
dice_exchange_mode(r.dice_exchange_mode) {}
|
||||
|
||||
RulesTrial::operator Rules() const {
|
||||
Rules ret;
|
||||
ret.overall_time_limit = this->overall_time_limit;
|
||||
ret.phase_time_limit = this->phase_time_limit;
|
||||
ret.allowed_cards = this->allowed_cards;
|
||||
ret.min_dice = 1;
|
||||
ret.max_dice = this->atk_dice_max;
|
||||
ret.disable_deck_shuffle = this->disable_deck_shuffle;
|
||||
ret.disable_deck_loop = this->disable_deck_loop;
|
||||
ret.char_hp = this->char_hp;
|
||||
ret.hp_type = this->hp_type;
|
||||
ret.no_assist_cards = this->no_assist_cards;
|
||||
ret.disable_dialogue = this->disable_dialogue;
|
||||
ret.dice_exchange_mode = this->dice_exchange_mode;
|
||||
ret.disable_dice_boost = 0;
|
||||
ret.def_dice_range = 0x10 | (this->def_dice_max ? this->def_dice_max : 0x06);
|
||||
ret.unused.clear(0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
StateFlags::StateFlags() {
|
||||
this->clear();
|
||||
}
|
||||
@@ -1518,9 +1551,6 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
" a5[0x70:0x74]=%02hhX %02hhX %02hhX %02hhX",
|
||||
this->unknown_a5[0x70], this->unknown_a5[0x71], this->unknown_a5[0x72], this->unknown_a5[0x73]));
|
||||
lines.emplace_back(" default_rules: " + this->default_rules.str());
|
||||
lines.emplace_back(string_printf(
|
||||
" a6=%02hhX %02hhX %02hhX %02hhX",
|
||||
this->unknown_a6[0], this->unknown_a6[1], this->unknown_a6[2], this->unknown_a6[3]));
|
||||
lines.emplace_back(" name: " + string(this->name));
|
||||
lines.emplace_back(" location_name: " + string(this->location_name));
|
||||
lines.emplace_back(" quest_name: " + string(this->quest_name));
|
||||
@@ -1715,7 +1745,6 @@ MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map)
|
||||
modification_tiles(map.modification_tiles),
|
||||
unknown_a5(map.unknown_a5),
|
||||
default_rules(map.default_rules),
|
||||
unknown_a6(map.unknown_a6),
|
||||
name(map.name),
|
||||
location_name(map.location_name),
|
||||
quest_name(map.quest_name),
|
||||
@@ -1744,6 +1773,98 @@ MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map)
|
||||
}
|
||||
}
|
||||
|
||||
MapDefinitionTrial::operator MapDefinition() const {
|
||||
MapDefinition ret;
|
||||
ret.unknown_a1 = this->unknown_a1;
|
||||
ret.map_number = this->map_number;
|
||||
ret.width = this->width;
|
||||
ret.height = this->height;
|
||||
ret.environment_number = this->environment_number;
|
||||
ret.num_camera_zones = this->num_camera_zones;
|
||||
ret.map_tiles = this->map_tiles;
|
||||
ret.start_tile_definitions = this->start_tile_definitions;
|
||||
ret.camera_zone_maps = this->camera_zone_maps;
|
||||
ret.camera_zone_specs = this->camera_zone_specs;
|
||||
ret.overview_specs = this->overview_specs;
|
||||
ret.modification_tiles = this->modification_tiles;
|
||||
ret.unknown_a5.clear(0xFF);
|
||||
ret.unknown_a5 = this->unknown_a5;
|
||||
ret.default_rules = this->default_rules;
|
||||
ret.name = this->name;
|
||||
ret.location_name = this->location_name;
|
||||
ret.quest_name = this->quest_name;
|
||||
ret.description = this->description;
|
||||
ret.map_x = this->map_x;
|
||||
ret.map_y = this->map_y;
|
||||
ret.npc_decks = this->npc_decks;
|
||||
ret.npc_ai_params = this->npc_ai_params;
|
||||
ret.unknown_a7 = this->unknown_a7;
|
||||
ret.npc_ai_params_entry_index = this->npc_ai_params_entry_index;
|
||||
ret.before_message = this->before_message;
|
||||
ret.after_message = this->after_message;
|
||||
ret.dispatch_message = this->dispatch_message;
|
||||
for (size_t z = 0; z < ret.dialogue_sets.size(); z++) {
|
||||
ret.dialogue_sets[z].sub<8>(0) = this->dialogue_sets[z];
|
||||
for (size_t x = 8; x < ret.dialogue_sets[z].size(); x++) {
|
||||
ret.dialogue_sets[z][x].unknown_a1 = 0xFFFF;
|
||||
ret.dialogue_sets[z][x].unknown_a2 = 0xFFFF;
|
||||
for (size_t w = 0; w < 4; w++) {
|
||||
ret.dialogue_sets[z][x].strings[w].clear(0xFF);
|
||||
ret.dialogue_sets[z][x].strings[w][0] = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret.reward_card_ids = this->reward_card_ids;
|
||||
ret.win_level_override = this->win_level_override;
|
||||
ret.loss_level_override = this->loss_level_override;
|
||||
ret.field_offset_x = this->field_offset_x;
|
||||
ret.field_offset_y = this->field_offset_y;
|
||||
ret.map_category = this->map_category;
|
||||
ret.cyber_block_type = this->cyber_block_type;
|
||||
ret.unknown_a11 = this->unknown_a11;
|
||||
ret.unavailable_sc_cards.clear(0xFFFF);
|
||||
// The trial edition doesn't seem to have entry_states at all, so we have to
|
||||
// guess and fill in the field appropriately here.
|
||||
size_t num_npc_decks = 0;
|
||||
for (size_t z = 0; z < ret.npc_decks.size(); z++) {
|
||||
if (ret.npc_decks[z].name[0]) {
|
||||
num_npc_decks++;
|
||||
}
|
||||
}
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
ret.entry_states[z].deck_type = 0xFF;
|
||||
}
|
||||
switch (num_npc_decks) {
|
||||
case 0: // No NPCs; it's a free battle map
|
||||
ret.entry_states[0].player_type = 0xFF;
|
||||
ret.entry_states[1].player_type = 0xFF;
|
||||
ret.entry_states[2].player_type = 0xFF;
|
||||
ret.entry_states[3].player_type = 0xFF;
|
||||
break;
|
||||
case 1: // One NPC; assume it's a 1v1 quest (Player vs. COM)
|
||||
ret.entry_states[0].player_type = 0x00;
|
||||
ret.entry_states[1].player_type = 0x04;
|
||||
ret.entry_states[2].player_type = 0x03;
|
||||
ret.entry_states[3].player_type = 0x04;
|
||||
break;
|
||||
case 2: // Two NPCs; assume it's a 2v2 quest (Player+Player/COM vs. COM+COM)
|
||||
ret.entry_states[0].player_type = 0x00;
|
||||
ret.entry_states[1].player_type = 0x02;
|
||||
ret.entry_states[2].player_type = 0x03;
|
||||
ret.entry_states[3].player_type = 0x03;
|
||||
break;
|
||||
case 3: // Three NPCs; assume it's a 2v2 quest (Player+COM vs. COM+COM)
|
||||
ret.entry_states[0].player_type = 0x00;
|
||||
ret.entry_states[1].player_type = 0x03;
|
||||
ret.entry_states[2].player_type = 0x03;
|
||||
ret.entry_states[3].player_type = 0x03;
|
||||
break;
|
||||
default: // Should be impossible
|
||||
throw logic_error("too many NPC decks in trial map definition");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Rules::check_invalid_fields() const {
|
||||
Rules t = *this;
|
||||
return t.check_and_reset_invalid_fields();
|
||||
|
||||
@@ -879,9 +879,8 @@ struct Rules {
|
||||
// NOTE: The following fields are unused in PSO's implementation, but newserv
|
||||
// uses them to implement extended rules.
|
||||
/* 0D */ uint8_t def_dice_range = 0; // High 4 bits = min, low 4 = max
|
||||
/* 0E */ uint8_t unused1 = 0;
|
||||
/* 0F */ uint8_t unused2 = 0;
|
||||
/* 10 */
|
||||
/* 0E */ parray<uint8_t, 6> unused;
|
||||
/* 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
|
||||
@@ -906,6 +905,28 @@ struct Rules {
|
||||
std::string str() const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RulesTrial {
|
||||
// The fields here have the same meaning as in the final version. The only
|
||||
// difference is that Dice Boost does not exist in the trial version.
|
||||
/* 00 */ uint8_t overall_time_limit = 0;
|
||||
/* 01 */ uint8_t phase_time_limit = 0;
|
||||
/* 02 */ AllowedCards allowed_cards = AllowedCards::ALL;
|
||||
/* 03 */ uint8_t atk_dice_max = 1;
|
||||
/* 04 */ uint8_t def_dice_max = 6;
|
||||
/* 05 */ uint8_t disable_deck_shuffle = 0;
|
||||
/* 06 */ uint8_t disable_deck_loop = 0;
|
||||
/* 07 */ uint8_t char_hp = 15;
|
||||
/* 08 */ HPType hp_type = HPType::DEFEAT_PLAYER;
|
||||
/* 09 */ uint8_t no_assist_cards = 0;
|
||||
/* 0A */ uint8_t disable_dialogue = 0;
|
||||
/* 0B */ DiceExchangeMode dice_exchange_mode = DiceExchangeMode::HIGH_ATK;
|
||||
/* 0C */
|
||||
|
||||
RulesTrial() = default;
|
||||
explicit RulesTrial(const Rules&);
|
||||
operator Rules() const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct StateFlags {
|
||||
/* 00 */ le_uint16_t turn_num;
|
||||
/* 02 */ BattlePhase battle_phase;
|
||||
@@ -1090,7 +1111,6 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
|
||||
/* 1D68 */ parray<uint8_t, 0x74> unknown_a5;
|
||||
/* 1DDC */ Rules default_rules;
|
||||
/* 1DEC */ parray<uint8_t, 4> unknown_a6;
|
||||
|
||||
/* 1DF0 */ ptext<char, 0x14> name;
|
||||
/* 1E04 */ ptext<char, 0x14> location_name;
|
||||
@@ -1261,9 +1281,8 @@ struct MapDefinitionTrial {
|
||||
/* 1518 */ parray<parray<MapDefinition::CameraSpec, 10>, 2> camera_zone_specs;
|
||||
/* 1AB8 */ parray<parray<MapDefinition::CameraSpec, 2>, 3> overview_specs;
|
||||
/* 1C68 */ parray<parray<uint8_t, 0x10>, 0x10> modification_tiles;
|
||||
/* 1D68 */ parray<uint8_t, 0x6C> unknown_a5;
|
||||
/* 1DD4 */ Rules default_rules;
|
||||
/* 1DE4 */ parray<uint8_t, 4> unknown_a6;
|
||||
/* 1D68 */ parray<uint8_t, 0x74> unknown_a5;
|
||||
/* 1DD4 */ RulesTrial default_rules;
|
||||
/* 1DE8 */ ptext<char, 0x14> name;
|
||||
/* 1DFC */ ptext<char, 0x14> location_name;
|
||||
/* 1E10 */ ptext<char, 0x3C> quest_name;
|
||||
@@ -1292,6 +1311,7 @@ struct MapDefinitionTrial {
|
||||
/* 41A0 */
|
||||
|
||||
MapDefinitionTrial(const MapDefinition& map);
|
||||
operator MapDefinition() const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct COMDeckDefinition {
|
||||
|
||||
@@ -47,7 +47,6 @@ void MapAndRulesState::clear() {
|
||||
this->map_number = 0;
|
||||
this->unused4 = 0;
|
||||
this->rules.clear();
|
||||
this->unused5 = 0;
|
||||
}
|
||||
|
||||
bool MapAndRulesState::loc_is_within_bounds(uint8_t x, uint8_t y) const {
|
||||
|
||||
@@ -34,7 +34,6 @@ struct MapAndRulesState {
|
||||
le_uint32_t map_number;
|
||||
uint32_t unused4;
|
||||
Rules rules;
|
||||
uint32_t unused5;
|
||||
|
||||
MapAndRulesState();
|
||||
void clear();
|
||||
|
||||
Reference in New Issue
Block a user