use area instead of floor during map construction
This commit is contained in:
+2
-1
@@ -3003,8 +3003,9 @@ static void whatobj_whatene_fn(const Args& a, bool include_objs, bool include_en
|
||||
if (nearest_ene) {
|
||||
const auto* set_entry = nearest_ene->super_ene->version(a.c->version()).set_entry;
|
||||
string type_name = MapFile::name_for_enemy_type(set_entry->base_type, a.c->version(), area);
|
||||
uint8_t area = l->area_for_floor(a.c->version(), a.c->floor);
|
||||
send_text_message_fmt(a.c, "$C5E-{:03X}\n$C6{}\n$C2{}\n$C7X:{:.2f} Z:{:.2f}",
|
||||
nearest_ene->e_id, phosg::name_for_enum(nearest_ene->type(a.c->version(), l->episode, l->difficulty, l->event)),
|
||||
nearest_ene->e_id, phosg::name_for_enum(nearest_ene->type(a.c->version(), area, l->difficulty, l->event)),
|
||||
type_name, nearest_worldspace_pos.x, nearest_worldspace_pos.z);
|
||||
auto set_str = set_entry->str(a.c->version(), area);
|
||||
a.c->log.info_f("Enemy found via $whatobj: E-{:03X} {} at x={:g} y={:g} z={:g}",
|
||||
|
||||
+18
-19
@@ -226,29 +226,28 @@ const vector<EnemyType>& enemy_types_for_battle_param_index(Episode episode, uin
|
||||
}
|
||||
}
|
||||
|
||||
EnemyType EnemyTypeDefinition::rare_type(Episode episode, uint8_t event, uint8_t floor) const {
|
||||
EnemyType EnemyTypeDefinition::rare_type(uint8_t area, uint8_t event) const {
|
||||
switch (this->type) {
|
||||
case EnemyType::HILDEBEAR:
|
||||
return EnemyType::HILDEBLUE;
|
||||
case EnemyType::RAG_RAPPY:
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
return EnemyType::AL_RAPPY;
|
||||
case Episode::EP2:
|
||||
switch (event) {
|
||||
case 0x01: // rappy_type 1
|
||||
return EnemyType::SAINT_RAPPY;
|
||||
case 0x04: // rappy_type 2
|
||||
return EnemyType::EGG_RAPPY;
|
||||
case 0x05: // rappy_type 3
|
||||
return EnemyType::HALLO_RAPPY;
|
||||
default:
|
||||
return EnemyType::LOVE_RAPPY;
|
||||
}
|
||||
case Episode::EP4:
|
||||
return (floor > 0x05) ? EnemyType::DEL_RAPPY_DESERT : EnemyType::DEL_RAPPY_CRATER;
|
||||
default:
|
||||
throw logic_error("invalid episode");
|
||||
if (area < 0x12) {
|
||||
return EnemyType::AL_RAPPY;
|
||||
} else if (area < 0x24) {
|
||||
switch (event) {
|
||||
case 0x01: // rappy_type 1
|
||||
return EnemyType::SAINT_RAPPY;
|
||||
case 0x04: // rappy_type 2
|
||||
return EnemyType::EGG_RAPPY;
|
||||
case 0x05: // rappy_type 3
|
||||
return EnemyType::HALLO_RAPPY;
|
||||
default:
|
||||
return EnemyType::LOVE_RAPPY;
|
||||
}
|
||||
} else if (area <= 0x28) {
|
||||
return EnemyType::DEL_RAPPY_CRATER;
|
||||
} else {
|
||||
return EnemyType::DEL_RAPPY_DESERT;
|
||||
}
|
||||
case EnemyType::POISON_LILY:
|
||||
return EnemyType::NAR_LILY;
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ struct EnemyTypeDefinition {
|
||||
inline bool is_boss() const {
|
||||
return (this->flags & Flag::IS_BOSS);
|
||||
}
|
||||
EnemyType rare_type(Episode episode, uint8_t event, uint8_t floor) const;
|
||||
EnemyType rare_type(uint8_t area, uint8_t event) const;
|
||||
};
|
||||
|
||||
const EnemyTypeDefinition& type_definition_for_enemy(EnemyType type);
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ uint8_t Lobby::area_for_floor(Version version, uint8_t floor) const {
|
||||
return this->quest->meta.floor_assignments.at(floor).area;
|
||||
}
|
||||
auto sdt = this->require_server_state()->set_data_table(version, this->episode, this->mode, this->difficulty);
|
||||
return sdt->default_area_for_floor(this->episode, floor);
|
||||
return sdt->default_floor_to_area(this->episode).at(floor);
|
||||
}
|
||||
|
||||
shared_ptr<ServerState> Lobby::require_server_state() const {
|
||||
|
||||
+67
-53
@@ -67,7 +67,7 @@ vector<string> SetDataTableBase::map_filenames_for_variations(
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t SetDataTableBase::default_area_for_floor(Version version, Episode episode, uint8_t floor) {
|
||||
std::array<uint8_t, 0x12> SetDataTableBase::default_floor_to_area(Version version, Episode episode) {
|
||||
// For some inscrutable reason, Pioneer 2's area number in Episode 4 is
|
||||
// discontiguous with all the rest. Why, Sega??
|
||||
static const array<uint8_t, 0x12> areas_ep1 = {
|
||||
@@ -76,24 +76,26 @@ uint8_t SetDataTableBase::default_area_for_floor(Version version, Episode episod
|
||||
0x00, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0xFF, 0xFF};
|
||||
static const array<uint8_t, 0x12> areas_ep2 = {
|
||||
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23};
|
||||
static const array<uint8_t, 0x12> areas_ep3 = {
|
||||
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF};
|
||||
static const array<uint8_t, 0x12> areas_ep4 = {
|
||||
0x2D, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
return areas_ep1.at(floor);
|
||||
case Episode::EP2: {
|
||||
const auto& areas = ((version == Version::GC_NTE) ? areas_ep2_gc_nte : areas_ep2);
|
||||
return areas.at(floor);
|
||||
}
|
||||
return areas_ep1;
|
||||
case Episode::EP2:
|
||||
return (version == Version::GC_NTE) ? areas_ep2_gc_nte : areas_ep2;
|
||||
case Episode::EP3:
|
||||
return areas_ep3;
|
||||
case Episode::EP4:
|
||||
return areas_ep4.at(floor);
|
||||
return areas_ep4;
|
||||
default:
|
||||
throw logic_error("incorrect episode");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t SetDataTableBase::default_area_for_floor(Episode episode, uint8_t floor) const {
|
||||
return this->default_area_for_floor(this->version, episode, floor);
|
||||
std::array<uint8_t, 0x12> SetDataTableBase::default_floor_to_area(Episode episode) const {
|
||||
return this->default_floor_to_area(this->version, episode);
|
||||
}
|
||||
|
||||
SetDataTable::SetDataTable(Version version, const string& data) : SetDataTableBase(version) {
|
||||
@@ -138,7 +140,7 @@ void SetDataTable::load_table_t(const string& data) {
|
||||
}
|
||||
|
||||
Variations::Entry SetDataTable::num_available_variations_for_floor(Episode episode, uint8_t floor) const {
|
||||
uint8_t area = this->default_area_for_floor(episode, floor);
|
||||
uint8_t area = this->default_floor_to_area(episode).at(floor);
|
||||
if (area == 0xFF) {
|
||||
return Variations::Entry{.layout = 1, .entities = 1};
|
||||
} else {
|
||||
@@ -151,7 +153,7 @@ Variations::Entry SetDataTable::num_available_variations_for_floor(Episode episo
|
||||
}
|
||||
|
||||
Variations::Entry SetDataTable::num_free_play_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const {
|
||||
uint8_t area = this->default_area_for_floor(episode, floor);
|
||||
uint8_t area = this->default_floor_to_area(episode).at(floor);
|
||||
if (area == 0xFF) {
|
||||
return Variations::Entry{.layout = 1, .entities = 1};
|
||||
}
|
||||
@@ -188,7 +190,7 @@ Variations::Entry SetDataTable::num_free_play_variations_for_floor(Episode episo
|
||||
|
||||
string SetDataTable::map_filename_for_variation(
|
||||
Episode episode, GameMode mode, uint8_t floor, uint32_t layout, uint32_t entities, FilenameType type) const {
|
||||
uint8_t area = this->default_area_for_floor(episode, floor);
|
||||
uint8_t area = this->default_floor_to_area(episode).at(floor);
|
||||
if (area == 0xFF) {
|
||||
return "";
|
||||
}
|
||||
@@ -4365,9 +4367,11 @@ string SuperMap::Event::str() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
SuperMap::SuperMap(Episode episode, const std::array<std::shared_ptr<const MapFile>, NUM_VERSIONS>& map_files)
|
||||
SuperMap::SuperMap(
|
||||
const std::array<std::shared_ptr<const MapFile>, NUM_VERSIONS>& map_files,
|
||||
const std::array<uint8_t, 0x12>& floor_to_area)
|
||||
: log("[SuperMap] "),
|
||||
episode(episode) {
|
||||
floor_to_area(floor_to_area) {
|
||||
for (const auto& map_file : map_files) {
|
||||
if (!map_file) {
|
||||
continue;
|
||||
@@ -4400,9 +4404,7 @@ static uint64_t room_index_key(uint8_t floor, uint16_t room, uint16_t wave_numbe
|
||||
}
|
||||
|
||||
shared_ptr<SuperMap::Object> SuperMap::add_object(
|
||||
Version version,
|
||||
uint8_t floor,
|
||||
const MapFile::ObjectSetEntry* set_entry) {
|
||||
Version version, uint8_t floor, const MapFile::ObjectSetEntry* set_entry) {
|
||||
auto obj = make_shared<Object>();
|
||||
obj->super_id = this->objects.size();
|
||||
obj->floor = floor;
|
||||
@@ -4591,19 +4593,13 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
break;
|
||||
}
|
||||
case 0x0041: { // TObjEneLappy
|
||||
bool is_rare_v123 = (set_entry->param6 != 0);
|
||||
bool is_rare_bb = (set_entry->param6 & 1);
|
||||
switch (this->episode) {
|
||||
case Episode::EP1:
|
||||
case Episode::EP2:
|
||||
add(EnemyType::RAG_RAPPY, is_rare_v123, is_rare_bb);
|
||||
break;
|
||||
case Episode::EP4:
|
||||
add((floor > 0x05) ? EnemyType::SAND_RAPPY_DESERT : EnemyType::SAND_RAPPY_CRATER, is_rare_v123, is_rare_bb);
|
||||
break;
|
||||
default:
|
||||
throw logic_error("invalid episode");
|
||||
}
|
||||
uint8_t area = this->area_for_floor(floor);
|
||||
EnemyType type = (area < 0x24)
|
||||
? EnemyType::RAG_RAPPY
|
||||
: (area <= 0x28)
|
||||
? EnemyType::SAND_RAPPY_CRATER
|
||||
: EnemyType::SAND_RAPPY_DESERT;
|
||||
add(type, (set_entry->param6 != 0), (set_entry->param6 & 1));
|
||||
break;
|
||||
}
|
||||
case 0x0042: // TObjEneBm3FlyNest
|
||||
@@ -4622,9 +4618,10 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
case 0x0060: // TObjGrass
|
||||
add(EnemyType::GRASS_ASSASSIN);
|
||||
break;
|
||||
case 0x0061: // TObjEneRe2Flower
|
||||
add(((episode == Episode::EP2) && (floor == 0x11)) ? EnemyType::DEL_LILY : EnemyType::POISON_LILY);
|
||||
case 0x0061: { // TObjEneRe2Flower
|
||||
add((this->area_for_floor(floor) == 0x23) ? EnemyType::DEL_LILY : EnemyType::POISON_LILY);
|
||||
break;
|
||||
}
|
||||
case 0x0062: // TObjEneNanoDrago
|
||||
add(EnemyType::NANO_DRAGON);
|
||||
break;
|
||||
@@ -4711,15 +4708,17 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
case 0x00A8: // Unnamed subclass of TObjEneBalClawClaw
|
||||
add(EnemyType::CLAW);
|
||||
break;
|
||||
case 0x00C0: // TBoss1Dragon or TBoss5Gryphon
|
||||
if (episode == Episode::EP1) {
|
||||
case 0x00C0: { // TBoss1Dragon or TBoss5Gryphon
|
||||
uint8_t area = this->area_for_floor(floor);
|
||||
if (area < 0x12) {
|
||||
add(EnemyType::DRAGON);
|
||||
} else if (episode == Episode::EP2) {
|
||||
} else if (area < 0x24) {
|
||||
add(EnemyType::GAL_GRYPHON);
|
||||
} else {
|
||||
throw runtime_error("DRAGON placed outside of Episode 1 or 2");
|
||||
throw std::runtime_error("DRAGON placed outside of Episode 1 or 2");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x00C1: // TBoss2DeRolLe
|
||||
if ((set_entry->num_children != 0) && (set_entry->num_children != 0x13)) {
|
||||
this->log.warning_f("DE_ROL_LE has an unusual num_children (0x{:X})", set_entry->num_children);
|
||||
@@ -4787,7 +4786,7 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
default_num_children = 5;
|
||||
break;
|
||||
case 0x00D4: // TObjEneMe3StelthReal
|
||||
if (this->episode == Episode::EP3) {
|
||||
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||
add(EnemyType::NON_ENEMY_NPC);
|
||||
} else {
|
||||
add((set_entry->param6 > 0) ? EnemyType::SINOW_SPIGELL : EnemyType::SINOW_BERILL);
|
||||
@@ -4795,14 +4794,14 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
}
|
||||
break;
|
||||
case 0x00D5: // TObjEneMerillLia
|
||||
if (this->episode == Episode::EP3) {
|
||||
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||
add(EnemyType::NON_ENEMY_NPC);
|
||||
} else {
|
||||
add((set_entry->param6 > 0) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
|
||||
}
|
||||
break;
|
||||
case 0x00D6: // TObjEneBm9Mericarol
|
||||
if (this->episode == Episode::EP3) {
|
||||
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||
add(EnemyType::NON_ENEMY_NPC);
|
||||
} else {
|
||||
switch (set_entry->param6) {
|
||||
@@ -4821,7 +4820,7 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
}
|
||||
break;
|
||||
case 0x00D7: // TObjEneBm5GibonU
|
||||
if (this->episode == Episode::EP3) {
|
||||
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||
add(EnemyType::NON_ENEMY_NPC);
|
||||
} else {
|
||||
add((set_entry->param6 > 0) ? EnemyType::ZOL_GIBBON : EnemyType::UL_GIBBON);
|
||||
@@ -4852,8 +4851,9 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
add(EnemyType::RECOBOX);
|
||||
child_type = EnemyType::RECON;
|
||||
break;
|
||||
case 0x00E0: // TObjEneMe3SinowZoaReal or TObjEneEpsilonBody
|
||||
if ((episode == Episode::EP2) && (floor > 0x0F)) {
|
||||
case 0x00E0: { // TObjEneMe3SinowZoaReal or TObjEneEpsilonBody
|
||||
uint8_t area = this->area_for_floor(floor);
|
||||
if ((area == 0x22) || (area == 0x23)) {
|
||||
add(EnemyType::EPSILON);
|
||||
default_num_children = 4;
|
||||
child_type = EnemyType::EPSIGARD;
|
||||
@@ -4861,27 +4861,30 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
add((set_entry->param6 > 0) ? EnemyType::SINOW_ZELE : EnemyType::SINOW_ZOA);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x00E1: // TObjEneIllGill
|
||||
add(EnemyType::ILL_GILL);
|
||||
break;
|
||||
case 0x0110:
|
||||
if (this->episode == Episode::EP3) {
|
||||
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||
add(EnemyType::NON_ENEMY_NPC);
|
||||
} else {
|
||||
add(EnemyType::ASTARK);
|
||||
}
|
||||
break;
|
||||
case 0x0111:
|
||||
if (this->episode == Episode::EP3) {
|
||||
case 0x0111: {
|
||||
uint8_t area = this->area_for_floor(floor);
|
||||
if (area == 0xFF) { // Ep3
|
||||
add(EnemyType::NON_ENEMY_NPC);
|
||||
} else if (floor > 0x05) {
|
||||
add(set_entry->param2 ? EnemyType::YOWIE_DESERT : EnemyType::SATELLITE_LIZARD_DESERT);
|
||||
} else {
|
||||
} else if (area <= 0x28) {
|
||||
add(set_entry->param2 ? EnemyType::YOWIE_CRATER : EnemyType::SATELLITE_LIZARD_CRATER);
|
||||
} else {
|
||||
add(set_entry->param2 ? EnemyType::YOWIE_DESERT : EnemyType::SATELLITE_LIZARD_DESERT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0112:
|
||||
if (this->episode == Episode::EP3) {
|
||||
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||
add(EnemyType::NON_ENEMY_NPC);
|
||||
} else {
|
||||
bool is_rare = (set_entry->param6 & 1);
|
||||
@@ -4893,7 +4896,7 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
break;
|
||||
case 0x0114: {
|
||||
bool is_rare = (set_entry->param6 & 1);
|
||||
add((floor > 0x05) ? EnemyType::ZU_DESERT : EnemyType::ZU_CRATER, is_rare, is_rare);
|
||||
add((this->area_for_floor(floor) <= 0x28) ? EnemyType::ZU_CRATER : EnemyType::ZU_DESERT, is_rare, is_rare);
|
||||
break;
|
||||
}
|
||||
case 0x0115: {
|
||||
@@ -5804,7 +5807,8 @@ void SuperMap::verify() const {
|
||||
}
|
||||
|
||||
void SuperMap::print(FILE* stream) const {
|
||||
phosg::fwrite_fmt(stream, "SuperMap {} random={:08X}\n", name_for_episode(this->episode), this->random_seed);
|
||||
phosg::fwrite_fmt(stream, "SuperMap areas=[{}] random={:08X}\n",
|
||||
phosg::format_data_string(&this->floor_to_area, sizeof(this->floor_to_area)), this->random_seed);
|
||||
|
||||
phosg::fwrite_fmt(stream, " DCTE DCPR DCV1 DCV2 PCTE PCV2 GCTE GCV3 E3TE GCE3 XBV3 BBV4\n");
|
||||
phosg::fwrite_fmt(stream, " MAP ");
|
||||
@@ -6066,6 +6070,11 @@ MapState::MapState(
|
||||
random_seed(random_seed),
|
||||
bb_rare_rates(bb_rare_rates) {
|
||||
|
||||
if (floor_map_defs.empty()) {
|
||||
throw std::runtime_error("cannot construct a MapState with no floor maps");
|
||||
}
|
||||
this->floor_to_area = floor_map_defs[0]->floor_to_area;
|
||||
|
||||
this->floor_config_entries.resize(0x12);
|
||||
for (size_t floor = 0; floor < this->floor_config_entries.size(); floor++) {
|
||||
auto& this_fc = this->floor_config_entries[floor];
|
||||
@@ -6110,7 +6119,8 @@ MapState::MapState(
|
||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||
std::shared_ptr<RandomGenerator> rand_crypt,
|
||||
std::shared_ptr<const SuperMap> quest_map_def)
|
||||
: log(std::format("[MapState(free):{:08X}] ", lobby_or_session_id), lobby_log.min_level),
|
||||
: log(std::format("[MapState(quest):{:08X}] ", lobby_or_session_id), lobby_log.min_level),
|
||||
floor_to_area(quest_map_def->floor_to_area),
|
||||
difficulty(difficulty),
|
||||
event(event),
|
||||
random_seed(random_seed),
|
||||
@@ -6142,6 +6152,9 @@ void MapState::index_super_map(const FloorConfig& fc, shared_ptr<RandomGenerator
|
||||
if (!fc.super_map) {
|
||||
throw logic_error("cannot index floor config with no map definition");
|
||||
}
|
||||
if (fc.super_map->floor_to_area != this->floor_to_area) {
|
||||
throw runtime_error("supermaps have different floor configs");
|
||||
}
|
||||
|
||||
for (const auto& obj : fc.super_map->all_objects()) {
|
||||
auto& obj_st = this->object_states.emplace_back(make_shared<ObjectState>());
|
||||
@@ -6183,7 +6196,8 @@ void MapState::index_super_map(const FloorConfig& fc, shared_ptr<RandomGenerator
|
||||
type = ene->type;
|
||||
}
|
||||
|
||||
auto rare_type = type_definition_for_enemy(type).rare_type(fc.super_map->get_episode(), this->event, ene->floor);
|
||||
uint8_t area = fc.super_map->area_for_floor(ene->floor);
|
||||
auto rare_type = type_definition_for_enemy(type).rare_type(area, this->event);
|
||||
if ((type == EnemyType::MERICARAND) || (rare_type != type)) {
|
||||
unordered_map<uint32_t, float> det_cache;
|
||||
uint32_t bb_rare_rate = this->bb_rare_rates->get(type);
|
||||
|
||||
+17
-11
@@ -67,8 +67,8 @@ public:
|
||||
std::vector<std::string> map_filenames_for_variations(
|
||||
Episode episode, GameMode mode, const Variations& variations, FilenameType type) const;
|
||||
|
||||
static uint8_t default_area_for_floor(Version version, Episode episode, uint8_t floor);
|
||||
uint8_t default_area_for_floor(Episode episode, uint8_t floor) const;
|
||||
static std::array<uint8_t, 0x12> default_floor_to_area(Version version, Episode episode);
|
||||
std::array<uint8_t, 0x12> default_floor_to_area(Episode episode) const;
|
||||
|
||||
protected:
|
||||
explicit SetDataTableBase(Version version);
|
||||
@@ -602,7 +602,9 @@ public:
|
||||
std::multimap<uint64_t, std::shared_ptr<Event>> event_for_floor_and_event_id;
|
||||
};
|
||||
|
||||
SuperMap(Episode episode, const std::array<std::shared_ptr<const MapFile>, NUM_VERSIONS>& map_files);
|
||||
SuperMap(
|
||||
const std::array<std::shared_ptr<const MapFile>, NUM_VERSIONS>& map_files,
|
||||
const std::array<uint8_t, 0x12>& floor_to_area);
|
||||
~SuperMap() = default;
|
||||
|
||||
inline EntitiesForVersion& version(Version v) {
|
||||
@@ -612,9 +614,6 @@ public:
|
||||
return this->entities_for_version.at(static_cast<size_t>(v));
|
||||
}
|
||||
|
||||
inline Episode get_episode() const {
|
||||
return this->episode;
|
||||
}
|
||||
inline int64_t get_random_seed() const {
|
||||
return this->random_seed;
|
||||
}
|
||||
@@ -648,6 +647,10 @@ public:
|
||||
|
||||
std::unordered_map<EnemyType, size_t> count_enemy_sets_for_version(Version version) const;
|
||||
|
||||
uint8_t area_for_floor(uint8_t floor) const {
|
||||
return this->floor_to_area.at(floor);
|
||||
}
|
||||
|
||||
struct EfficiencyStats {
|
||||
size_t filled_object_slots = 0;
|
||||
size_t total_object_slots = 0;
|
||||
@@ -664,9 +667,11 @@ public:
|
||||
void print(FILE* stream) const;
|
||||
|
||||
protected:
|
||||
friend class MapState;
|
||||
|
||||
phosg::PrefixedLogger log;
|
||||
|
||||
Episode episode;
|
||||
std::array<uint8_t, 0x12> floor_to_area = {};
|
||||
int64_t random_seed = -1;
|
||||
std::vector<std::shared_ptr<Object>> objects;
|
||||
std::vector<std::shared_ptr<Enemy>> enemies;
|
||||
@@ -797,7 +802,7 @@ public:
|
||||
inline void set_mericarand_variant_flag(Version version) {
|
||||
this->mericarand_variant_flags |= (1 << static_cast<size_t>(version));
|
||||
}
|
||||
inline EnemyType type(Version version, Episode episode, Difficulty difficulty, uint8_t event) const {
|
||||
inline EnemyType type(Version version, uint8_t area, Difficulty difficulty, uint8_t event) const {
|
||||
if (this->super_ene->type == EnemyType::MERICARAND) {
|
||||
if (this->is_rare(version)) {
|
||||
return ((this->mericarand_variant_flags >> static_cast<size_t>(version)) & 1)
|
||||
@@ -810,10 +815,10 @@ public:
|
||||
return ((difficulty == Difficulty::NORMAL) && !this->super_ene->alias_target_ene)
|
||||
? EnemyType::DARK_FALZ_2
|
||||
: EnemyType::DARK_FALZ_3;
|
||||
} else if (this->is_rare(version)) {
|
||||
return type_definition_for_enemy(this->super_ene->type).rare_type(area, event);
|
||||
} else {
|
||||
return this->is_rare(version)
|
||||
? type_definition_for_enemy(this->super_ene->type).rare_type(episode, event, this->super_ene->floor)
|
||||
: this->super_ene->type;
|
||||
return this->super_ene->type;
|
||||
}
|
||||
}
|
||||
inline bool ever_hit_by_client_id(uint8_t client_id) const {
|
||||
@@ -929,6 +934,7 @@ public:
|
||||
};
|
||||
|
||||
phosg::PrefixedLogger log;
|
||||
std::array<uint8_t, 0x12> floor_to_area = {};
|
||||
std::vector<FloorConfig> floor_config_entries;
|
||||
Difficulty difficulty = Difficulty::NORMAL;
|
||||
uint8_t event = 0;
|
||||
|
||||
+39
-26
@@ -872,13 +872,7 @@ static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel
|
||||
|
||||
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data.data(), msg.data.size());
|
||||
auto rec = reconcile_drop_request_with_map(
|
||||
c,
|
||||
cmd,
|
||||
c->proxy_session->lobby_episode,
|
||||
c->proxy_session->lobby_difficulty,
|
||||
c->proxy_session->lobby_event,
|
||||
c->proxy_session->map_state,
|
||||
false);
|
||||
c, cmd, c->proxy_session->lobby_difficulty, c->proxy_session->lobby_event, c->proxy_session->map_state, false);
|
||||
|
||||
ItemCreator::DropResult res;
|
||||
if (rec.obj_st) {
|
||||
@@ -1342,28 +1336,47 @@ static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Mes
|
||||
c->log.info_f("Download complete for file {}", sf->basename);
|
||||
}
|
||||
|
||||
if (!sf->is_download && sf->basename.ends_with(".dat")) {
|
||||
auto quest_dat_data = make_shared<std::string>(prs_decompress(sf->data));
|
||||
try {
|
||||
auto map_file = make_shared<MapFile>(quest_dat_data);
|
||||
auto materialized_map_file = map_file->materialize_random_sections(c->proxy_session->lobby_random_seed);
|
||||
if (!sf->is_download) {
|
||||
if (sf->basename.ends_with(".bin")) {
|
||||
c->proxy_session->last_bin_contents = make_shared<std::string>(prs_decompress(sf->data));
|
||||
} else if (sf->basename.ends_with(".dat")) {
|
||||
c->proxy_session->last_dat_contents = make_shared<std::string>(prs_decompress(sf->data));
|
||||
}
|
||||
|
||||
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
|
||||
map_files.at(static_cast<size_t>(c->version())) = materialized_map_file;
|
||||
auto supermap = make_shared<SuperMap>(c->proxy_session->lobby_episode, map_files);
|
||||
if (c->proxy_session->last_bin_contents && c->proxy_session->last_dat_contents) {
|
||||
try {
|
||||
QuestMetadata meta;
|
||||
populate_quest_metadata_from_script(
|
||||
meta,
|
||||
c->proxy_session->last_bin_contents->data(),
|
||||
c->proxy_session->last_bin_contents->size(),
|
||||
c->version(),
|
||||
c->language());
|
||||
|
||||
c->proxy_session->map_state = make_shared<MapState>(
|
||||
c->id,
|
||||
c->proxy_session->lobby_difficulty,
|
||||
c->proxy_session->lobby_event,
|
||||
c->proxy_session->lobby_random_seed,
|
||||
MapState::DEFAULT_RARE_ENEMIES,
|
||||
make_shared<MT19937Generator>(c->proxy_session->lobby_random_seed),
|
||||
supermap);
|
||||
auto map_file = make_shared<MapFile>(c->proxy_session->last_dat_contents);
|
||||
auto materialized_map_file = map_file->materialize_random_sections(c->proxy_session->lobby_random_seed);
|
||||
|
||||
} catch (const exception& e) {
|
||||
c->log.warning_f("Failed to load quest map: {}", e.what());
|
||||
c->proxy_session->map_state.reset();
|
||||
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
|
||||
map_files.at(static_cast<size_t>(c->version())) = materialized_map_file;
|
||||
auto supermap = make_shared<SuperMap>(map_files, meta.get_floor_to_area());
|
||||
supermap->print(stderr); // NOCOMMIT
|
||||
|
||||
c->proxy_session->map_state = make_shared<MapState>(
|
||||
c->id,
|
||||
c->proxy_session->lobby_difficulty,
|
||||
c->proxy_session->lobby_event,
|
||||
c->proxy_session->lobby_random_seed,
|
||||
MapState::DEFAULT_RARE_ENEMIES,
|
||||
make_shared<MT19937Generator>(c->proxy_session->lobby_random_seed),
|
||||
supermap);
|
||||
|
||||
} catch (const exception& e) {
|
||||
c->log.warning_f("Failed to load quest map: {}", e.what());
|
||||
c->proxy_session->map_state.reset();
|
||||
}
|
||||
|
||||
c->proxy_session->last_bin_contents.reset();
|
||||
c->proxy_session->last_dat_contents.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -53,9 +53,11 @@ struct ProxySession {
|
||||
std::shared_ptr<std::string> quest_dat_data;
|
||||
std::shared_ptr<ItemCreator> item_creator;
|
||||
std::shared_ptr<MapState> map_state;
|
||||
// TODO: Be less lazy and track item IDs correctly in proxy games. (Then
|
||||
// change this to use the actual client's next item ID, not this hardcoded
|
||||
// default.)
|
||||
std::shared_ptr<const std::string> last_bin_contents;
|
||||
std::shared_ptr<const std::string> last_dat_contents;
|
||||
// Note: We intentionally don't use the client's item ID space because the
|
||||
// client may create items at the same time as the proxy, so server/client
|
||||
// state could go out of sync
|
||||
uint32_t next_item_id = 0x44000000;
|
||||
|
||||
struct PersistentConfig {
|
||||
|
||||
+1
-1
@@ -389,7 +389,7 @@ std::shared_ptr<const SuperMap> Quest::get_supermap(int64_t random_seed) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto supermap = make_shared<SuperMap>(this->meta.episode, map_files);
|
||||
auto supermap = make_shared<SuperMap>(map_files, this->meta.get_floor_to_area());
|
||||
if (save_to_cache) {
|
||||
this->supermap = supermap;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ void QuestMetadata::assign_default_floors() {
|
||||
for (size_t z = 0; z < 0x12; z++) {
|
||||
auto& fa = this->floor_assignments[z];
|
||||
fa.floor = z;
|
||||
fa.area = SetDataTableBase::default_area_for_floor(this->version, this->episode, z);
|
||||
fa.area = SetDataTableBase::default_floor_to_area(this->version, this->episode)[z];
|
||||
fa.type = 0;
|
||||
fa.layout_var = 0;
|
||||
fa.entities_var = 0;
|
||||
|
||||
@@ -116,6 +116,14 @@ struct QuestMetadata {
|
||||
void apply_json_overrides(const phosg::JSON& json);
|
||||
|
||||
void assign_default_floors();
|
||||
inline std::array<uint8_t, 0x12> get_floor_to_area() const {
|
||||
std::array<uint8_t, 0x12> ret;
|
||||
for (size_t z = 0; z < 0x12; z++) {
|
||||
ret[z] = this->floor_assignments[z].area;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void assert_compatible(const QuestMetadata& other) const;
|
||||
phosg::JSON json() const;
|
||||
std::string areas_str() const;
|
||||
|
||||
+89
-74
@@ -2830,7 +2830,6 @@ G_SpecializableItemDropRequest_6xA2 normalize_drop_request(const void* data, siz
|
||||
DropReconcileResult reconcile_drop_request_with_map(
|
||||
shared_ptr<Client> c,
|
||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||
Episode episode,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
shared_ptr<MapState> map,
|
||||
@@ -2842,76 +2841,78 @@ DropReconcileResult reconcile_drop_request_with_map(
|
||||
res.effective_rt_index = 0xFF;
|
||||
res.should_drop = true;
|
||||
res.ignore_def = (cmd.ignore_def != 0);
|
||||
if (!map) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (map) {
|
||||
if (is_box) {
|
||||
res.obj_st = map->object_state_for_index(version, cmd.floor, cmd.entity_index);
|
||||
if (!res.obj_st->super_obj) {
|
||||
throw std::runtime_error("referenced object from drop request is a player trap");
|
||||
}
|
||||
const auto* set_entry = res.obj_st->super_obj->version(version).set_entry;
|
||||
if (!set_entry) {
|
||||
throw std::runtime_error("object set entry is missing");
|
||||
}
|
||||
if (is_box) {
|
||||
res.obj_st = map->object_state_for_index(version, cmd.floor, cmd.entity_index);
|
||||
if (!res.obj_st->super_obj) {
|
||||
throw std::runtime_error("referenced object from drop request is a player trap");
|
||||
}
|
||||
const auto* set_entry = res.obj_st->super_obj->version(version).set_entry;
|
||||
if (!set_entry) {
|
||||
throw std::runtime_error("object set entry is missing");
|
||||
}
|
||||
string type_name = MapFile::name_for_object_type(set_entry->base_type, version);
|
||||
c->log.info_f("Drop check for K-{:03X} {} {}",
|
||||
res.obj_st->k_id,
|
||||
res.ignore_def ? 'G' : 'S',
|
||||
type_name);
|
||||
if (cmd.floor != res.obj_st->super_obj->floor) {
|
||||
c->log.warning_f("Floor {:02X} from command does not match object\'s expected floor {:02X}",
|
||||
cmd.floor, res.obj_st->super_obj->floor);
|
||||
}
|
||||
if (is_v1_or_v2(version) && (version != Version::GC_NTE)) {
|
||||
// V1 and V2 don't have 6xA2, so we can't get ignore_def or the object
|
||||
// parameters from the client on those versions
|
||||
cmd.param3 = set_entry->param3;
|
||||
cmd.param4 = set_entry->param4;
|
||||
cmd.param5 = set_entry->param5;
|
||||
cmd.param6 = set_entry->param6;
|
||||
}
|
||||
bool object_ignore_def = (set_entry->param1 > 0.0);
|
||||
if (res.ignore_def != object_ignore_def) {
|
||||
c->log.warning_f("ignore_def value {} from command does not match object\'s expected ignore_def {} (from p1={:g})",
|
||||
res.ignore_def ? "true" : "false", object_ignore_def ? "true" : "false", set_entry->param1);
|
||||
}
|
||||
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string type_name = MapFile::name_for_object_type(set_entry->base_type, version);
|
||||
c->log.info_f("Drop check for K-{:03X} {} {}",
|
||||
res.obj_st->k_id,
|
||||
res.ignore_def ? 'G' : 'S',
|
||||
type_name);
|
||||
if (cmd.floor != res.obj_st->super_obj->floor) {
|
||||
c->log.warning_f("Floor {:02X} from command does not match object\'s expected floor {:02X}",
|
||||
cmd.floor, res.obj_st->super_obj->floor);
|
||||
}
|
||||
if (is_v1_or_v2(version) && (version != Version::GC_NTE)) {
|
||||
// V1 and V2 don't have 6xA2, so we can't get ignore_def or the object
|
||||
// parameters from the client on those versions
|
||||
cmd.param3 = set_entry->param3;
|
||||
cmd.param4 = set_entry->param4;
|
||||
cmd.param5 = set_entry->param5;
|
||||
cmd.param6 = set_entry->param6;
|
||||
}
|
||||
bool object_ignore_def = (set_entry->param1 > 0.0);
|
||||
if (res.ignore_def != object_ignore_def) {
|
||||
c->log.warning_f("ignore_def value {} from command does not match object\'s expected ignore_def {} (from p1={:g})",
|
||||
res.ignore_def ? "true" : "false", object_ignore_def ? "true" : "false", set_entry->param1);
|
||||
}
|
||||
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string type_name = MapFile::name_for_object_type(set_entry->base_type, version);
|
||||
send_text_message_fmt(c, "$C5K-{:03X} {} {}", res.obj_st->k_id, res.ignore_def ? 'G' : 'S', type_name);
|
||||
}
|
||||
send_text_message_fmt(c, "$C5K-{:03X} {} {}", res.obj_st->k_id, res.ignore_def ? 'G' : 'S', type_name);
|
||||
}
|
||||
|
||||
} else {
|
||||
res.ref_ene_st = map->enemy_state_for_index(version, cmd.floor, cmd.entity_index);
|
||||
res.target_ene_st = res.ref_ene_st->alias_target_ene_st ? res.ref_ene_st->alias_target_ene_st : res.ref_ene_st;
|
||||
EnemyType type = res.target_ene_st->type(version, episode, difficulty, event);
|
||||
c->log.info_f("Drop check for E-{:03X} (target E-{:03X}, type {})",
|
||||
res.ref_ene_st->e_id, res.target_ene_st->e_id, phosg::name_for_enum(type));
|
||||
res.effective_rt_index = type_definition_for_enemy(type).rt_index;
|
||||
// rt_indexes in Episode 4 don't match those sent in the command; we just
|
||||
// ignore what the client sends.
|
||||
if ((episode != Episode::EP4) && (cmd.rt_index != res.effective_rt_index)) {
|
||||
// Special cases: BULCLAW => BULK and DARK_GUNNER => DEATH_GUNNER
|
||||
if (cmd.rt_index == 0x27 && type == EnemyType::BULCLAW) {
|
||||
c->log.info_f("E-{:03X} killed as BULK instead of BULCLAW", res.target_ene_st->e_id);
|
||||
res.effective_rt_index = 0x27;
|
||||
} else if (cmd.rt_index == 0x23 && type == EnemyType::DARK_GUNNER) {
|
||||
c->log.info_f("E-{:03X} killed as DEATH_GUNNER instead of DARK_GUNNER", res.target_ene_st->e_id);
|
||||
res.effective_rt_index = 0x23;
|
||||
} else {
|
||||
c->log.warning_f("rt_index {:02X} from command does not match entity\'s expected index {:02X}",
|
||||
cmd.rt_index, res.effective_rt_index);
|
||||
if (!is_v4(version)) {
|
||||
res.effective_rt_index = cmd.rt_index;
|
||||
}
|
||||
} else {
|
||||
res.ref_ene_st = map->enemy_state_for_index(version, cmd.floor, cmd.entity_index);
|
||||
res.target_ene_st = res.ref_ene_st->alias_target_ene_st ? res.ref_ene_st->alias_target_ene_st : res.ref_ene_st;
|
||||
uint8_t area = map->floor_to_area.at(res.target_ene_st->super_ene->floor);
|
||||
EnemyType type = res.target_ene_st->type(version, area, difficulty, event);
|
||||
c->log.info_f("Drop check for E-{:03X} (target E-{:03X}, type {})",
|
||||
res.ref_ene_st->e_id, res.target_ene_st->e_id, phosg::name_for_enum(type));
|
||||
res.effective_rt_index = type_definition_for_enemy(type).rt_index;
|
||||
// rt_indexes in Episode 4 don't match those sent in the command; we just
|
||||
// ignore what the client sends.
|
||||
if ((area < 0x24) && (cmd.rt_index != res.effective_rt_index)) {
|
||||
// Special cases: BULCLAW => BULK and DARK_GUNNER => DEATH_GUNNER
|
||||
if (cmd.rt_index == 0x27 && type == EnemyType::BULCLAW) {
|
||||
c->log.info_f("E-{:03X} killed as BULK instead of BULCLAW", res.target_ene_st->e_id);
|
||||
res.effective_rt_index = 0x27;
|
||||
} else if (cmd.rt_index == 0x23 && type == EnemyType::DARK_GUNNER) {
|
||||
c->log.info_f("E-{:03X} killed as DEATH_GUNNER instead of DARK_GUNNER", res.target_ene_st->e_id);
|
||||
res.effective_rt_index = 0x23;
|
||||
} else {
|
||||
c->log.warning_f("rt_index {:02X} from command does not match entity\'s expected index {:02X}",
|
||||
cmd.rt_index, res.effective_rt_index);
|
||||
if (!is_v4(version)) {
|
||||
res.effective_rt_index = cmd.rt_index;
|
||||
}
|
||||
}
|
||||
if (cmd.floor != res.target_ene_st->super_ene->floor) {
|
||||
c->log.warning_f("Floor {:02X} from command does not match entity\'s expected floor {:02X}",
|
||||
cmd.floor, res.target_ene_st->super_ene->floor);
|
||||
}
|
||||
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_fmt(c, "$C5E-{:03X} {}", res.target_ene_st->e_id, phosg::name_for_enum(type));
|
||||
}
|
||||
}
|
||||
if (cmd.floor != res.target_ene_st->super_ene->floor) {
|
||||
c->log.warning_f("Floor {:02X} from command does not match entity\'s expected floor {:02X}",
|
||||
cmd.floor, res.target_ene_st->super_ene->floor);
|
||||
}
|
||||
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_fmt(c, "$C5E-{:03X} {}", res.target_ene_st->e_id, phosg::name_for_enum(type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2948,8 +2949,7 @@ static asio::awaitable<void> on_entity_drop_item_request(shared_ptr<Client> c, S
|
||||
// mode, so that we can correctly mark enemies and objects as having dropped
|
||||
// their items in persistent games.
|
||||
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data, msg.size);
|
||||
Episode episode = episode_for_area(l->area_for_floor(c->version(), cmd.floor));
|
||||
auto rec = reconcile_drop_request_with_map(c, cmd, episode, l->difficulty, l->event, l->map_state, true);
|
||||
auto rec = reconcile_drop_request_with_map(c, cmd, l->difficulty, l->event, l->map_state, true);
|
||||
|
||||
ServerDropMode drop_mode = l->drop_mode;
|
||||
switch (drop_mode) {
|
||||
@@ -3228,7 +3228,20 @@ static asio::awaitable<void> on_sync_quest_register(shared_ptr<Client> c, Subcom
|
||||
}
|
||||
}
|
||||
|
||||
forward_subcommand(c, msg);
|
||||
// NOCOMMIT: Add condition here for l->quest->meta.enable_schtserv_commands
|
||||
if ((cmd.register_number == 0xF1) && (cmd.value.as_int == 0x52455650)) {
|
||||
// PVER => respond with specific_version in schtserv's format
|
||||
G_SyncQuestRegister_6x77 ret_cmd;
|
||||
ret_cmd.header.subcommand = 0x77;
|
||||
ret_cmd.header.size = sizeof(ret_cmd) / 4;
|
||||
ret_cmd.header.unused = 0;
|
||||
ret_cmd.register_number = 0xF2;
|
||||
ret_cmd.value.as_int = is_v4(c->version()) ? 0x50 : c->sub_version;
|
||||
send_command_t(c, 0x60, 0x00, ret_cmd);
|
||||
|
||||
} else {
|
||||
forward_subcommand(c, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static asio::awaitable<void> on_set_entity_set_flag(shared_ptr<Client> c, SubcommandMessage& msg) {
|
||||
@@ -3967,7 +3980,7 @@ static uint32_t base_exp_for_enemy_type(
|
||||
} else if (current_episode == Episode::EP4) {
|
||||
uint8_t area = quest
|
||||
? quest->meta.floor_assignments.at(floor).area
|
||||
: SetDataTableBase::default_area_for_floor(Version::BB_V4, Episode::EP4, floor);
|
||||
: SetDataTableBase::default_floor_to_area(Version::BB_V4, Episode::EP4).at(floor);
|
||||
if (area <= 0x28) { // Crater
|
||||
episode_order[1] = Episode::EP1;
|
||||
episode_order[2] = Episode::EP2;
|
||||
@@ -4039,8 +4052,9 @@ static asio::awaitable<void> on_steal_exp_bb(shared_ptr<Client> c, SubcommandMes
|
||||
co_return;
|
||||
}
|
||||
|
||||
auto episode = episode_for_area(l->area_for_floor(c->version(), ene_st->super_ene->floor));
|
||||
auto type = ene_st->type(c->version(), episode, l->difficulty, l->event);
|
||||
uint8_t area = l->area_for_floor(c->version(), ene_st->super_ene->floor);
|
||||
Episode episode = episode_for_area(area);
|
||||
auto type = ene_st->type(c->version(), area, l->difficulty, l->event);
|
||||
uint32_t enemy_exp = base_exp_for_enemy_type(
|
||||
s->battle_params, l->quest, type, episode, l->difficulty, ene_st->super_ene->floor, l->mode == GameMode::SOLO);
|
||||
|
||||
@@ -4090,8 +4104,9 @@ static asio::awaitable<void> on_enemy_exp_request_bb(shared_ptr<Client> c, Subco
|
||||
}
|
||||
ene_st->server_flags |= MapState::EnemyState::Flag::EXP_GIVEN;
|
||||
|
||||
auto episode = episode_for_area(l->area_for_floor(c->version(), ene_st->super_ene->floor));
|
||||
auto type = ene_st->type(c->version(), episode, l->difficulty, l->event);
|
||||
uint8_t area = l->area_for_floor(c->version(), ene_st->super_ene->floor);
|
||||
Episode episode = episode_for_area(area);
|
||||
auto type = ene_st->type(c->version(), area, l->difficulty, l->event);
|
||||
double base_exp = base_exp_for_enemy_type(
|
||||
s->battle_params, l->quest, type, episode, l->difficulty, ene_st->super_ene->floor, l->mode == GameMode::SOLO);
|
||||
l->log.info_f("Base EXP for this enemy ({}) is {:g}", phosg::name_for_enum(type), base_exp);
|
||||
|
||||
@@ -31,7 +31,6 @@ struct DropReconcileResult {
|
||||
DropReconcileResult reconcile_drop_request_with_map(
|
||||
std::shared_ptr<Client> c,
|
||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||
Episode episode,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
std::shared_ptr<MapState> map,
|
||||
|
||||
+1
-1
@@ -1739,7 +1739,7 @@ shared_ptr<const SuperMap> ServerState::get_free_play_supermap(
|
||||
supermap = this->supermap_for_source_hash_sum.at(source_hash_sum);
|
||||
static_game_data_log.info_f("Linking existing free play supermap {:016X} for key {:08X}", source_hash_sum, free_play_key);
|
||||
} catch (const out_of_range&) {
|
||||
supermap = make_shared<SuperMap>(episode, *map_files);
|
||||
supermap = make_shared<SuperMap>(*map_files, SetDataTableBase::default_floor_to_area(Version::BB_V4, episode));
|
||||
this->supermap_for_source_hash_sum.emplace(source_hash_sum, supermap);
|
||||
static_game_data_log.info_f("Constructed free play supermap {:016X} for key {:08X}", source_hash_sum, free_play_key);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user