add learnings from Ep3 Trial Edition download quest

This commit is contained in:
Martin Michelsen
2023-09-28 14:48:26 -07:00
parent 5c5da8e10b
commit fbdfdb085a
11 changed files with 269 additions and 90 deletions
+127 -6
View File
@@ -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();
+27 -7
View File
@@ -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 {
-1
View File
@@ -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 {
-1
View File
@@ -34,7 +34,6 @@ struct MapAndRulesState {
le_uint32_t map_number;
uint32_t unused4;
Rules rules;
uint32_t unused5;
MapAndRulesState();
void clear();