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) {
|
if (nearest_ene) {
|
||||||
const auto* set_entry = nearest_ene->super_ene->version(a.c->version()).set_entry;
|
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);
|
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}",
|
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);
|
type_name, nearest_worldspace_pos.x, nearest_worldspace_pos.z);
|
||||||
auto set_str = set_entry->str(a.c->version(), area);
|
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}",
|
a.c->log.info_f("Enemy found via $whatobj: E-{:03X} {} at x={:g} y={:g} z={:g}",
|
||||||
|
|||||||
+7
-8
@@ -226,15 +226,14 @@ 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) {
|
switch (this->type) {
|
||||||
case EnemyType::HILDEBEAR:
|
case EnemyType::HILDEBEAR:
|
||||||
return EnemyType::HILDEBLUE;
|
return EnemyType::HILDEBLUE;
|
||||||
case EnemyType::RAG_RAPPY:
|
case EnemyType::RAG_RAPPY:
|
||||||
switch (episode) {
|
if (area < 0x12) {
|
||||||
case Episode::EP1:
|
|
||||||
return EnemyType::AL_RAPPY;
|
return EnemyType::AL_RAPPY;
|
||||||
case Episode::EP2:
|
} else if (area < 0x24) {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case 0x01: // rappy_type 1
|
case 0x01: // rappy_type 1
|
||||||
return EnemyType::SAINT_RAPPY;
|
return EnemyType::SAINT_RAPPY;
|
||||||
@@ -245,10 +244,10 @@ EnemyType EnemyTypeDefinition::rare_type(Episode episode, uint8_t event, uint8_t
|
|||||||
default:
|
default:
|
||||||
return EnemyType::LOVE_RAPPY;
|
return EnemyType::LOVE_RAPPY;
|
||||||
}
|
}
|
||||||
case Episode::EP4:
|
} else if (area <= 0x28) {
|
||||||
return (floor > 0x05) ? EnemyType::DEL_RAPPY_DESERT : EnemyType::DEL_RAPPY_CRATER;
|
return EnemyType::DEL_RAPPY_CRATER;
|
||||||
default:
|
} else {
|
||||||
throw logic_error("invalid episode");
|
return EnemyType::DEL_RAPPY_DESERT;
|
||||||
}
|
}
|
||||||
case EnemyType::POISON_LILY:
|
case EnemyType::POISON_LILY:
|
||||||
return EnemyType::NAR_LILY;
|
return EnemyType::NAR_LILY;
|
||||||
|
|||||||
+1
-1
@@ -174,7 +174,7 @@ struct EnemyTypeDefinition {
|
|||||||
inline bool is_boss() const {
|
inline bool is_boss() const {
|
||||||
return (this->flags & Flag::IS_BOSS);
|
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);
|
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;
|
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);
|
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 {
|
shared_ptr<ServerState> Lobby::require_server_state() const {
|
||||||
|
|||||||
+67
-53
@@ -67,7 +67,7 @@ vector<string> SetDataTableBase::map_filenames_for_variations(
|
|||||||
return ret;
|
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
|
// For some inscrutable reason, Pioneer 2's area number in Episode 4 is
|
||||||
// discontiguous with all the rest. Why, Sega??
|
// discontiguous with all the rest. Why, Sega??
|
||||||
static const array<uint8_t, 0x12> areas_ep1 = {
|
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};
|
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 = {
|
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};
|
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 = {
|
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};
|
0x2D, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
switch (episode) {
|
switch (episode) {
|
||||||
case Episode::EP1:
|
case Episode::EP1:
|
||||||
return areas_ep1.at(floor);
|
return areas_ep1;
|
||||||
case Episode::EP2: {
|
case Episode::EP2:
|
||||||
const auto& areas = ((version == Version::GC_NTE) ? areas_ep2_gc_nte : areas_ep2);
|
return (version == Version::GC_NTE) ? areas_ep2_gc_nte : areas_ep2;
|
||||||
return areas.at(floor);
|
case Episode::EP3:
|
||||||
}
|
return areas_ep3;
|
||||||
case Episode::EP4:
|
case Episode::EP4:
|
||||||
return areas_ep4.at(floor);
|
return areas_ep4;
|
||||||
default:
|
default:
|
||||||
throw logic_error("incorrect episode");
|
throw logic_error("incorrect episode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t SetDataTableBase::default_area_for_floor(Episode episode, uint8_t floor) const {
|
std::array<uint8_t, 0x12> SetDataTableBase::default_floor_to_area(Episode episode) const {
|
||||||
return this->default_area_for_floor(this->version, episode, floor);
|
return this->default_floor_to_area(this->version, episode);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetDataTable::SetDataTable(Version version, const string& data) : SetDataTableBase(version) {
|
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 {
|
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) {
|
if (area == 0xFF) {
|
||||||
return Variations::Entry{.layout = 1, .entities = 1};
|
return Variations::Entry{.layout = 1, .entities = 1};
|
||||||
} else {
|
} 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 {
|
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) {
|
if (area == 0xFF) {
|
||||||
return Variations::Entry{.layout = 1, .entities = 1};
|
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(
|
string SetDataTable::map_filename_for_variation(
|
||||||
Episode episode, GameMode mode, uint8_t floor, uint32_t layout, uint32_t entities, FilenameType type) const {
|
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) {
|
if (area == 0xFF) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -4365,9 +4367,11 @@ string SuperMap::Event::str() const {
|
|||||||
return ret;
|
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] "),
|
: log("[SuperMap] "),
|
||||||
episode(episode) {
|
floor_to_area(floor_to_area) {
|
||||||
for (const auto& map_file : map_files) {
|
for (const auto& map_file : map_files) {
|
||||||
if (!map_file) {
|
if (!map_file) {
|
||||||
continue;
|
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(
|
shared_ptr<SuperMap::Object> SuperMap::add_object(
|
||||||
Version version,
|
Version version, uint8_t floor, const MapFile::ObjectSetEntry* set_entry) {
|
||||||
uint8_t floor,
|
|
||||||
const MapFile::ObjectSetEntry* set_entry) {
|
|
||||||
auto obj = make_shared<Object>();
|
auto obj = make_shared<Object>();
|
||||||
obj->super_id = this->objects.size();
|
obj->super_id = this->objects.size();
|
||||||
obj->floor = floor;
|
obj->floor = floor;
|
||||||
@@ -4591,19 +4593,13 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x0041: { // TObjEneLappy
|
case 0x0041: { // TObjEneLappy
|
||||||
bool is_rare_v123 = (set_entry->param6 != 0);
|
uint8_t area = this->area_for_floor(floor);
|
||||||
bool is_rare_bb = (set_entry->param6 & 1);
|
EnemyType type = (area < 0x24)
|
||||||
switch (this->episode) {
|
? EnemyType::RAG_RAPPY
|
||||||
case Episode::EP1:
|
: (area <= 0x28)
|
||||||
case Episode::EP2:
|
? EnemyType::SAND_RAPPY_CRATER
|
||||||
add(EnemyType::RAG_RAPPY, is_rare_v123, is_rare_bb);
|
: EnemyType::SAND_RAPPY_DESERT;
|
||||||
break;
|
add(type, (set_entry->param6 != 0), (set_entry->param6 & 1));
|
||||||
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");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x0042: // TObjEneBm3FlyNest
|
case 0x0042: // TObjEneBm3FlyNest
|
||||||
@@ -4622,9 +4618,10 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
|||||||
case 0x0060: // TObjGrass
|
case 0x0060: // TObjGrass
|
||||||
add(EnemyType::GRASS_ASSASSIN);
|
add(EnemyType::GRASS_ASSASSIN);
|
||||||
break;
|
break;
|
||||||
case 0x0061: // TObjEneRe2Flower
|
case 0x0061: { // TObjEneRe2Flower
|
||||||
add(((episode == Episode::EP2) && (floor == 0x11)) ? EnemyType::DEL_LILY : EnemyType::POISON_LILY);
|
add((this->area_for_floor(floor) == 0x23) ? EnemyType::DEL_LILY : EnemyType::POISON_LILY);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 0x0062: // TObjEneNanoDrago
|
case 0x0062: // TObjEneNanoDrago
|
||||||
add(EnemyType::NANO_DRAGON);
|
add(EnemyType::NANO_DRAGON);
|
||||||
break;
|
break;
|
||||||
@@ -4711,15 +4708,17 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
|||||||
case 0x00A8: // Unnamed subclass of TObjEneBalClawClaw
|
case 0x00A8: // Unnamed subclass of TObjEneBalClawClaw
|
||||||
add(EnemyType::CLAW);
|
add(EnemyType::CLAW);
|
||||||
break;
|
break;
|
||||||
case 0x00C0: // TBoss1Dragon or TBoss5Gryphon
|
case 0x00C0: { // TBoss1Dragon or TBoss5Gryphon
|
||||||
if (episode == Episode::EP1) {
|
uint8_t area = this->area_for_floor(floor);
|
||||||
|
if (area < 0x12) {
|
||||||
add(EnemyType::DRAGON);
|
add(EnemyType::DRAGON);
|
||||||
} else if (episode == Episode::EP2) {
|
} else if (area < 0x24) {
|
||||||
add(EnemyType::GAL_GRYPHON);
|
add(EnemyType::GAL_GRYPHON);
|
||||||
} else {
|
} 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;
|
break;
|
||||||
|
}
|
||||||
case 0x00C1: // TBoss2DeRolLe
|
case 0x00C1: // TBoss2DeRolLe
|
||||||
if ((set_entry->num_children != 0) && (set_entry->num_children != 0x13)) {
|
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);
|
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;
|
default_num_children = 5;
|
||||||
break;
|
break;
|
||||||
case 0x00D4: // TObjEneMe3StelthReal
|
case 0x00D4: // TObjEneMe3StelthReal
|
||||||
if (this->episode == Episode::EP3) {
|
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||||
add(EnemyType::NON_ENEMY_NPC);
|
add(EnemyType::NON_ENEMY_NPC);
|
||||||
} else {
|
} else {
|
||||||
add((set_entry->param6 > 0) ? EnemyType::SINOW_SPIGELL : EnemyType::SINOW_BERILL);
|
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;
|
break;
|
||||||
case 0x00D5: // TObjEneMerillLia
|
case 0x00D5: // TObjEneMerillLia
|
||||||
if (this->episode == Episode::EP3) {
|
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||||
add(EnemyType::NON_ENEMY_NPC);
|
add(EnemyType::NON_ENEMY_NPC);
|
||||||
} else {
|
} else {
|
||||||
add((set_entry->param6 > 0) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
|
add((set_entry->param6 > 0) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x00D6: // TObjEneBm9Mericarol
|
case 0x00D6: // TObjEneBm9Mericarol
|
||||||
if (this->episode == Episode::EP3) {
|
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||||
add(EnemyType::NON_ENEMY_NPC);
|
add(EnemyType::NON_ENEMY_NPC);
|
||||||
} else {
|
} else {
|
||||||
switch (set_entry->param6) {
|
switch (set_entry->param6) {
|
||||||
@@ -4821,7 +4820,7 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x00D7: // TObjEneBm5GibonU
|
case 0x00D7: // TObjEneBm5GibonU
|
||||||
if (this->episode == Episode::EP3) {
|
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||||
add(EnemyType::NON_ENEMY_NPC);
|
add(EnemyType::NON_ENEMY_NPC);
|
||||||
} else {
|
} else {
|
||||||
add((set_entry->param6 > 0) ? EnemyType::ZOL_GIBBON : EnemyType::UL_GIBBON);
|
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);
|
add(EnemyType::RECOBOX);
|
||||||
child_type = EnemyType::RECON;
|
child_type = EnemyType::RECON;
|
||||||
break;
|
break;
|
||||||
case 0x00E0: // TObjEneMe3SinowZoaReal or TObjEneEpsilonBody
|
case 0x00E0: { // TObjEneMe3SinowZoaReal or TObjEneEpsilonBody
|
||||||
if ((episode == Episode::EP2) && (floor > 0x0F)) {
|
uint8_t area = this->area_for_floor(floor);
|
||||||
|
if ((area == 0x22) || (area == 0x23)) {
|
||||||
add(EnemyType::EPSILON);
|
add(EnemyType::EPSILON);
|
||||||
default_num_children = 4;
|
default_num_children = 4;
|
||||||
child_type = EnemyType::EPSIGARD;
|
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);
|
add((set_entry->param6 > 0) ? EnemyType::SINOW_ZELE : EnemyType::SINOW_ZOA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 0x00E1: // TObjEneIllGill
|
case 0x00E1: // TObjEneIllGill
|
||||||
add(EnemyType::ILL_GILL);
|
add(EnemyType::ILL_GILL);
|
||||||
break;
|
break;
|
||||||
case 0x0110:
|
case 0x0110:
|
||||||
if (this->episode == Episode::EP3) {
|
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||||
add(EnemyType::NON_ENEMY_NPC);
|
add(EnemyType::NON_ENEMY_NPC);
|
||||||
} else {
|
} else {
|
||||||
add(EnemyType::ASTARK);
|
add(EnemyType::ASTARK);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x0111:
|
case 0x0111: {
|
||||||
if (this->episode == Episode::EP3) {
|
uint8_t area = this->area_for_floor(floor);
|
||||||
|
if (area == 0xFF) { // Ep3
|
||||||
add(EnemyType::NON_ENEMY_NPC);
|
add(EnemyType::NON_ENEMY_NPC);
|
||||||
} else if (floor > 0x05) {
|
} else if (area <= 0x28) {
|
||||||
add(set_entry->param2 ? EnemyType::YOWIE_DESERT : EnemyType::SATELLITE_LIZARD_DESERT);
|
|
||||||
} else {
|
|
||||||
add(set_entry->param2 ? EnemyType::YOWIE_CRATER : EnemyType::SATELLITE_LIZARD_CRATER);
|
add(set_entry->param2 ? EnemyType::YOWIE_CRATER : EnemyType::SATELLITE_LIZARD_CRATER);
|
||||||
|
} else {
|
||||||
|
add(set_entry->param2 ? EnemyType::YOWIE_DESERT : EnemyType::SATELLITE_LIZARD_DESERT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 0x0112:
|
case 0x0112:
|
||||||
if (this->episode == Episode::EP3) {
|
if (this->area_for_floor(floor) == 0xFF) { // Ep3
|
||||||
add(EnemyType::NON_ENEMY_NPC);
|
add(EnemyType::NON_ENEMY_NPC);
|
||||||
} else {
|
} else {
|
||||||
bool is_rare = (set_entry->param6 & 1);
|
bool is_rare = (set_entry->param6 & 1);
|
||||||
@@ -4893,7 +4896,7 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
|||||||
break;
|
break;
|
||||||
case 0x0114: {
|
case 0x0114: {
|
||||||
bool is_rare = (set_entry->param6 & 1);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 0x0115: {
|
case 0x0115: {
|
||||||
@@ -5804,7 +5807,8 @@ void SuperMap::verify() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SuperMap::print(FILE* stream) 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, " DCTE DCPR DCV1 DCV2 PCTE PCV2 GCTE GCV3 E3TE GCE3 XBV3 BBV4\n");
|
||||||
phosg::fwrite_fmt(stream, " MAP ");
|
phosg::fwrite_fmt(stream, " MAP ");
|
||||||
@@ -6066,6 +6070,11 @@ MapState::MapState(
|
|||||||
random_seed(random_seed),
|
random_seed(random_seed),
|
||||||
bb_rare_rates(bb_rare_rates) {
|
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);
|
this->floor_config_entries.resize(0x12);
|
||||||
for (size_t floor = 0; floor < this->floor_config_entries.size(); floor++) {
|
for (size_t floor = 0; floor < this->floor_config_entries.size(); floor++) {
|
||||||
auto& this_fc = this->floor_config_entries[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<const RareEnemyRates> bb_rare_rates,
|
||||||
std::shared_ptr<RandomGenerator> rand_crypt,
|
std::shared_ptr<RandomGenerator> rand_crypt,
|
||||||
std::shared_ptr<const SuperMap> quest_map_def)
|
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),
|
difficulty(difficulty),
|
||||||
event(event),
|
event(event),
|
||||||
random_seed(random_seed),
|
random_seed(random_seed),
|
||||||
@@ -6142,6 +6152,9 @@ void MapState::index_super_map(const FloorConfig& fc, shared_ptr<RandomGenerator
|
|||||||
if (!fc.super_map) {
|
if (!fc.super_map) {
|
||||||
throw logic_error("cannot index floor config with no map definition");
|
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()) {
|
for (const auto& obj : fc.super_map->all_objects()) {
|
||||||
auto& obj_st = this->object_states.emplace_back(make_shared<ObjectState>());
|
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;
|
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)) {
|
if ((type == EnemyType::MERICARAND) || (rare_type != type)) {
|
||||||
unordered_map<uint32_t, float> det_cache;
|
unordered_map<uint32_t, float> det_cache;
|
||||||
uint32_t bb_rare_rate = this->bb_rare_rates->get(type);
|
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(
|
std::vector<std::string> map_filenames_for_variations(
|
||||||
Episode episode, GameMode mode, const Variations& variations, FilenameType type) const;
|
Episode episode, GameMode mode, const Variations& variations, FilenameType type) const;
|
||||||
|
|
||||||
static uint8_t default_area_for_floor(Version version, Episode episode, uint8_t floor);
|
static std::array<uint8_t, 0x12> default_floor_to_area(Version version, Episode episode);
|
||||||
uint8_t default_area_for_floor(Episode episode, uint8_t floor) const;
|
std::array<uint8_t, 0x12> default_floor_to_area(Episode episode) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit SetDataTableBase(Version version);
|
explicit SetDataTableBase(Version version);
|
||||||
@@ -602,7 +602,9 @@ public:
|
|||||||
std::multimap<uint64_t, std::shared_ptr<Event>> event_for_floor_and_event_id;
|
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;
|
~SuperMap() = default;
|
||||||
|
|
||||||
inline EntitiesForVersion& version(Version v) {
|
inline EntitiesForVersion& version(Version v) {
|
||||||
@@ -612,9 +614,6 @@ public:
|
|||||||
return this->entities_for_version.at(static_cast<size_t>(v));
|
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 {
|
inline int64_t get_random_seed() const {
|
||||||
return this->random_seed;
|
return this->random_seed;
|
||||||
}
|
}
|
||||||
@@ -648,6 +647,10 @@ public:
|
|||||||
|
|
||||||
std::unordered_map<EnemyType, size_t> count_enemy_sets_for_version(Version version) const;
|
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 {
|
struct EfficiencyStats {
|
||||||
size_t filled_object_slots = 0;
|
size_t filled_object_slots = 0;
|
||||||
size_t total_object_slots = 0;
|
size_t total_object_slots = 0;
|
||||||
@@ -664,9 +667,11 @@ public:
|
|||||||
void print(FILE* stream) const;
|
void print(FILE* stream) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
friend class MapState;
|
||||||
|
|
||||||
phosg::PrefixedLogger log;
|
phosg::PrefixedLogger log;
|
||||||
|
|
||||||
Episode episode;
|
std::array<uint8_t, 0x12> floor_to_area = {};
|
||||||
int64_t random_seed = -1;
|
int64_t random_seed = -1;
|
||||||
std::vector<std::shared_ptr<Object>> objects;
|
std::vector<std::shared_ptr<Object>> objects;
|
||||||
std::vector<std::shared_ptr<Enemy>> enemies;
|
std::vector<std::shared_ptr<Enemy>> enemies;
|
||||||
@@ -797,7 +802,7 @@ public:
|
|||||||
inline void set_mericarand_variant_flag(Version version) {
|
inline void set_mericarand_variant_flag(Version version) {
|
||||||
this->mericarand_variant_flags |= (1 << static_cast<size_t>(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->super_ene->type == EnemyType::MERICARAND) {
|
||||||
if (this->is_rare(version)) {
|
if (this->is_rare(version)) {
|
||||||
return ((this->mericarand_variant_flags >> static_cast<size_t>(version)) & 1)
|
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)
|
return ((difficulty == Difficulty::NORMAL) && !this->super_ene->alias_target_ene)
|
||||||
? EnemyType::DARK_FALZ_2
|
? EnemyType::DARK_FALZ_2
|
||||||
: EnemyType::DARK_FALZ_3;
|
: EnemyType::DARK_FALZ_3;
|
||||||
|
} else if (this->is_rare(version)) {
|
||||||
|
return type_definition_for_enemy(this->super_ene->type).rare_type(area, event);
|
||||||
} else {
|
} else {
|
||||||
return this->is_rare(version)
|
return this->super_ene->type;
|
||||||
? type_definition_for_enemy(this->super_ene->type).rare_type(episode, event, this->super_ene->floor)
|
|
||||||
: this->super_ene->type;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline bool ever_hit_by_client_id(uint8_t client_id) const {
|
inline bool ever_hit_by_client_id(uint8_t client_id) const {
|
||||||
@@ -929,6 +934,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
phosg::PrefixedLogger log;
|
phosg::PrefixedLogger log;
|
||||||
|
std::array<uint8_t, 0x12> floor_to_area = {};
|
||||||
std::vector<FloorConfig> floor_config_entries;
|
std::vector<FloorConfig> floor_config_entries;
|
||||||
Difficulty difficulty = Difficulty::NORMAL;
|
Difficulty difficulty = Difficulty::NORMAL;
|
||||||
uint8_t event = 0;
|
uint8_t event = 0;
|
||||||
|
|||||||
+24
-11
@@ -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());
|
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data.data(), msg.data.size());
|
||||||
auto rec = reconcile_drop_request_with_map(
|
auto rec = reconcile_drop_request_with_map(
|
||||||
c,
|
c, cmd, c->proxy_session->lobby_difficulty, c->proxy_session->lobby_event, c->proxy_session->map_state, false);
|
||||||
cmd,
|
|
||||||
c->proxy_session->lobby_episode,
|
|
||||||
c->proxy_session->lobby_difficulty,
|
|
||||||
c->proxy_session->lobby_event,
|
|
||||||
c->proxy_session->map_state,
|
|
||||||
false);
|
|
||||||
|
|
||||||
ItemCreator::DropResult res;
|
ItemCreator::DropResult res;
|
||||||
if (rec.obj_st) {
|
if (rec.obj_st) {
|
||||||
@@ -1342,15 +1336,30 @@ static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Mes
|
|||||||
c->log.info_f("Download complete for file {}", sf->basename);
|
c->log.info_f("Download complete for file {}", sf->basename);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sf->is_download && sf->basename.ends_with(".dat")) {
|
if (!sf->is_download) {
|
||||||
auto quest_dat_data = make_shared<std::string>(prs_decompress(sf->data));
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->proxy_session->last_bin_contents && c->proxy_session->last_dat_contents) {
|
||||||
try {
|
try {
|
||||||
auto map_file = make_shared<MapFile>(quest_dat_data);
|
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());
|
||||||
|
|
||||||
|
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);
|
auto materialized_map_file = map_file->materialize_random_sections(c->proxy_session->lobby_random_seed);
|
||||||
|
|
||||||
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
|
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
|
||||||
map_files.at(static_cast<size_t>(c->version())) = materialized_map_file;
|
map_files.at(static_cast<size_t>(c->version())) = materialized_map_file;
|
||||||
auto supermap = make_shared<SuperMap>(c->proxy_session->lobby_episode, map_files);
|
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->proxy_session->map_state = make_shared<MapState>(
|
||||||
c->id,
|
c->id,
|
||||||
@@ -1365,6 +1374,10 @@ static asio::awaitable<HandlerResult> S_13_A7(shared_ptr<Client> c, Channel::Mes
|
|||||||
c->log.warning_f("Failed to load quest map: {}", e.what());
|
c->log.warning_f("Failed to load quest map: {}", e.what());
|
||||||
c->proxy_session->map_state.reset();
|
c->proxy_session->map_state.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c->proxy_session->last_bin_contents.reset();
|
||||||
|
c->proxy_session->last_dat_contents.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c->proxy_session->saving_files.erase(cmd.filename.decode());
|
c->proxy_session->saving_files.erase(cmd.filename.decode());
|
||||||
|
|||||||
+5
-3
@@ -53,9 +53,11 @@ struct ProxySession {
|
|||||||
std::shared_ptr<std::string> quest_dat_data;
|
std::shared_ptr<std::string> quest_dat_data;
|
||||||
std::shared_ptr<ItemCreator> item_creator;
|
std::shared_ptr<ItemCreator> item_creator;
|
||||||
std::shared_ptr<MapState> map_state;
|
std::shared_ptr<MapState> map_state;
|
||||||
// TODO: Be less lazy and track item IDs correctly in proxy games. (Then
|
std::shared_ptr<const std::string> last_bin_contents;
|
||||||
// change this to use the actual client's next item ID, not this hardcoded
|
std::shared_ptr<const std::string> last_dat_contents;
|
||||||
// default.)
|
// 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;
|
uint32_t next_item_id = 0x44000000;
|
||||||
|
|
||||||
struct PersistentConfig {
|
struct PersistentConfig {
|
||||||
|
|||||||
+1
-1
@@ -389,7 +389,7 @@ std::shared_ptr<const SuperMap> Quest::get_supermap(int64_t random_seed) const {
|
|||||||
return nullptr;
|
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) {
|
if (save_to_cache) {
|
||||||
this->supermap = supermap;
|
this->supermap = supermap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void QuestMetadata::assign_default_floors() {
|
|||||||
for (size_t z = 0; z < 0x12; z++) {
|
for (size_t z = 0; z < 0x12; z++) {
|
||||||
auto& fa = this->floor_assignments[z];
|
auto& fa = this->floor_assignments[z];
|
||||||
fa.floor = 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.type = 0;
|
||||||
fa.layout_var = 0;
|
fa.layout_var = 0;
|
||||||
fa.entities_var = 0;
|
fa.entities_var = 0;
|
||||||
|
|||||||
@@ -116,6 +116,14 @@ struct QuestMetadata {
|
|||||||
void apply_json_overrides(const phosg::JSON& json);
|
void apply_json_overrides(const phosg::JSON& json);
|
||||||
|
|
||||||
void assign_default_floors();
|
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;
|
void assert_compatible(const QuestMetadata& other) const;
|
||||||
phosg::JSON json() const;
|
phosg::JSON json() const;
|
||||||
std::string areas_str() const;
|
std::string areas_str() const;
|
||||||
|
|||||||
+27
-12
@@ -2830,7 +2830,6 @@ G_SpecializableItemDropRequest_6xA2 normalize_drop_request(const void* data, siz
|
|||||||
DropReconcileResult reconcile_drop_request_with_map(
|
DropReconcileResult reconcile_drop_request_with_map(
|
||||||
shared_ptr<Client> c,
|
shared_ptr<Client> c,
|
||||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||||
Episode episode,
|
|
||||||
Difficulty difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
shared_ptr<MapState> map,
|
shared_ptr<MapState> map,
|
||||||
@@ -2842,8 +2841,10 @@ DropReconcileResult reconcile_drop_request_with_map(
|
|||||||
res.effective_rt_index = 0xFF;
|
res.effective_rt_index = 0xFF;
|
||||||
res.should_drop = true;
|
res.should_drop = true;
|
||||||
res.ignore_def = (cmd.ignore_def != 0);
|
res.ignore_def = (cmd.ignore_def != 0);
|
||||||
|
if (!map) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
if (map) {
|
|
||||||
if (is_box) {
|
if (is_box) {
|
||||||
res.obj_st = map->object_state_for_index(version, cmd.floor, cmd.entity_index);
|
res.obj_st = map->object_state_for_index(version, cmd.floor, cmd.entity_index);
|
||||||
if (!res.obj_st->super_obj) {
|
if (!res.obj_st->super_obj) {
|
||||||
@@ -2883,13 +2884,14 @@ DropReconcileResult reconcile_drop_request_with_map(
|
|||||||
} else {
|
} else {
|
||||||
res.ref_ene_st = map->enemy_state_for_index(version, cmd.floor, cmd.entity_index);
|
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;
|
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);
|
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 {})",
|
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.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;
|
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
|
// rt_indexes in Episode 4 don't match those sent in the command; we just
|
||||||
// ignore what the client sends.
|
// ignore what the client sends.
|
||||||
if ((episode != Episode::EP4) && (cmd.rt_index != res.effective_rt_index)) {
|
if ((area < 0x24) && (cmd.rt_index != res.effective_rt_index)) {
|
||||||
// Special cases: BULCLAW => BULK and DARK_GUNNER => DEATH_GUNNER
|
// Special cases: BULCLAW => BULK and DARK_GUNNER => DEATH_GUNNER
|
||||||
if (cmd.rt_index == 0x27 && type == EnemyType::BULCLAW) {
|
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);
|
c->log.info_f("E-{:03X} killed as BULK instead of BULCLAW", res.target_ene_st->e_id);
|
||||||
@@ -2913,7 +2915,6 @@ DropReconcileResult reconcile_drop_request_with_map(
|
|||||||
send_text_message_fmt(c, "$C5E-{:03X} {}", res.target_ene_st->e_id, phosg::name_for_enum(type));
|
send_text_message_fmt(c, "$C5E-{:03X} {}", res.target_ene_st->e_id, phosg::name_for_enum(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (mark_drop) {
|
if (mark_drop) {
|
||||||
if (res.obj_st) {
|
if (res.obj_st) {
|
||||||
@@ -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
|
// mode, so that we can correctly mark enemies and objects as having dropped
|
||||||
// their items in persistent games.
|
// their items in persistent games.
|
||||||
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data, msg.size);
|
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, l->difficulty, l->event, l->map_state, true);
|
||||||
auto rec = reconcile_drop_request_with_map(c, cmd, episode, l->difficulty, l->event, l->map_state, true);
|
|
||||||
|
|
||||||
ServerDropMode drop_mode = l->drop_mode;
|
ServerDropMode drop_mode = l->drop_mode;
|
||||||
switch (drop_mode) {
|
switch (drop_mode) {
|
||||||
@@ -3228,7 +3228,20 @@ static asio::awaitable<void> on_sync_quest_register(shared_ptr<Client> c, Subcom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
forward_subcommand(c, msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static asio::awaitable<void> on_set_entity_set_flag(shared_ptr<Client> c, SubcommandMessage& 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) {
|
} else if (current_episode == Episode::EP4) {
|
||||||
uint8_t area = quest
|
uint8_t area = quest
|
||||||
? quest->meta.floor_assignments.at(floor).area
|
? 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
|
if (area <= 0x28) { // Crater
|
||||||
episode_order[1] = Episode::EP1;
|
episode_order[1] = Episode::EP1;
|
||||||
episode_order[2] = Episode::EP2;
|
episode_order[2] = Episode::EP2;
|
||||||
@@ -4039,8 +4052,9 @@ static asio::awaitable<void> on_steal_exp_bb(shared_ptr<Client> c, SubcommandMes
|
|||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto episode = episode_for_area(l->area_for_floor(c->version(), ene_st->super_ene->floor));
|
uint8_t area = l->area_for_floor(c->version(), ene_st->super_ene->floor);
|
||||||
auto type = ene_st->type(c->version(), episode, l->difficulty, l->event);
|
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(
|
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);
|
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;
|
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));
|
uint8_t area = l->area_for_floor(c->version(), ene_st->super_ene->floor);
|
||||||
auto type = ene_st->type(c->version(), episode, l->difficulty, l->event);
|
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(
|
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);
|
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);
|
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(
|
DropReconcileResult reconcile_drop_request_with_map(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||||
Episode episode,
|
|
||||||
Difficulty difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
std::shared_ptr<MapState> map,
|
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);
|
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);
|
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&) {
|
} 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);
|
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);
|
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