fix map loading mismatches

This commit is contained in:
Martin Michelsen
2023-06-19 12:07:03 -07:00
parent 2de37a4733
commit 2b3cc6bcdf
7 changed files with 154 additions and 105 deletions
+2 -1
View File
@@ -51,7 +51,8 @@ With that said, I offer no guarantees on how or when this project will advance.
Current known issues / missing features / things to do:
- Implement the rest of PSOBB. Major areas of work:
- Find any remaining mismatches in enemy IDs / experience (Episode 1 is mostly fixed now, except for Dark Falz)
- Find any remaining mismatches in enemy IDs / experience
- Sale prices for non-rare weapons with specials are computed incorrectly when buying/selling at shops
- Replace enemy list, game episode, etc. with quest data when loading a quest
- Implement trade window
- Fix some edge cases on the BB proxy server (e.g. make sure Change Ship does the right thing, which is not the same as what it should do on other versions).
+34 -27
View File
@@ -26,6 +26,10 @@ const char* name_for_enum<EnemyType>(EnemyType type) {
return "BARBA_RAY";
case EnemyType::BARBAROUS_WOLF:
return "BARBAROUS_WOLF";
case EnemyType::BEE_L:
return "BEE_L";
case EnemyType::BEE_R:
return "BEE_R";
case EnemyType::BOOMA:
return "BOOMA";
case EnemyType::BOOTA:
@@ -58,6 +62,10 @@ const char* name_for_enum<EnemyType>(EnemyType type) {
return "DARVANT";
case EnemyType::DE_ROL_LE:
return "DE_ROL_LE";
case EnemyType::DE_ROL_LE_BODY:
return "DE_ROL_LE_BODY";
case EnemyType::DE_ROL_LE_MINE:
return "DE_ROL_LE_MINE";
case EnemyType::DEATH_GUNNER:
return "DEATH_GUNNER";
case EnemyType::DEL_LILY:
@@ -90,6 +98,8 @@ const char* name_for_enum<EnemyType>(EnemyType type) {
return "DUBWITCH";
case EnemyType::EGG_RAPPY:
return "EGG_RAPPY";
case EnemyType::EPSIGUARD:
return "EPSIGUARD";
case EnemyType::EPSILON:
return "EPSILON";
case EnemyType::EVIL_SHARK:
@@ -178,6 +188,8 @@ const char* name_for_enum<EnemyType>(EnemyType type) {
return "PAZUZU";
case EnemyType::PAZUZU_ALT:
return "PAZUZU_ALT";
case EnemyType::PIG_RAY:
return "PIG_RAY";
case EnemyType::POFUILLY_SLIME:
return "POFUILLY_SLIME";
case EnemyType::POUILLY_SLIME:
@@ -228,6 +240,14 @@ const char* name_for_enum<EnemyType>(EnemyType type) {
return "VOL_OPT_1";
case EnemyType::VOL_OPT_2:
return "VOL_OPT_2";
case EnemyType::VOL_OPT_AMP:
return "VOL_OPT_AMP";
case EnemyType::VOL_OPT_CORE:
return "VOL_OPT_CORE";
case EnemyType::VOL_OPT_MONITOR:
return "VOL_OPT_MONITOR";
case EnemyType::VOL_OPT_PILLAR:
return "VOL_OPT_PILLAR";
case EnemyType::YOWIE:
return "YOWIE";
case EnemyType::YOWIE_ALT:
@@ -255,6 +275,8 @@ EnemyType enum_for_name<EnemyType>(const char* name) {
{"BA_BOOTA", EnemyType::BA_BOOTA},
{"BARBA_RAY", EnemyType::BARBA_RAY},
{"BARBAROUS_WOLF", EnemyType::BARBAROUS_WOLF},
{"BEE_L", EnemyType::BEE_L},
{"BEE_R", EnemyType::BEE_R},
{"BOOMA", EnemyType::BOOMA},
{"BOOTA", EnemyType::BOOTA},
{"BULCLAW", EnemyType::BULCLAW},
@@ -272,6 +294,8 @@ EnemyType enum_for_name<EnemyType>(const char* name) {
{"DARVANT", EnemyType::DARVANT},
{"DARVANT_ULTIMATE", EnemyType::DARVANT_ULTIMATE},
{"DE_ROL_LE", EnemyType::DE_ROL_LE},
{"DE_ROL_LE_BODY", EnemyType::DE_ROL_LE_BODY},
{"DE_ROL_LE_MINE", EnemyType::DE_ROL_LE_MINE},
{"DEATH_GUNNER", EnemyType::DEATH_GUNNER},
{"DEL_LILY", EnemyType::DEL_LILY},
{"DEL_RAPPY", EnemyType::DEL_RAPPY},
@@ -288,6 +312,7 @@ EnemyType enum_for_name<EnemyType>(const char* name) {
{"DUBCHIC", EnemyType::DUBCHIC},
{"DUBWITCH", EnemyType::DUBWITCH},
{"EGG_RAPPY", EnemyType::EGG_RAPPY},
{"EPSIGUARD", EnemyType::EPSIGUARD},
{"EPSILON", EnemyType::EPSILON},
{"EVIL_SHARK", EnemyType::EVIL_SHARK},
{"GAEL", EnemyType::GAEL},
@@ -332,6 +357,7 @@ EnemyType enum_for_name<EnemyType>(const char* name) {
{"PAN_ARMS", EnemyType::PAN_ARMS},
{"PAZUZU", EnemyType::PAZUZU},
{"PAZUZU_ALT", EnemyType::PAZUZU_ALT},
{"PIG_RAY", EnemyType::PIG_RAY},
{"POFUILLY_SLIME", EnemyType::POFUILLY_SLIME},
{"POUILLY_SLIME", EnemyType::POUILLY_SLIME},
{"POISON_LILY", EnemyType::POISON_LILY},
@@ -357,6 +383,10 @@ EnemyType enum_for_name<EnemyType>(const char* name) {
{"UL_GIBBON", EnemyType::UL_GIBBON},
{"VOL_OPT_1", EnemyType::VOL_OPT_1},
{"VOL_OPT_2", EnemyType::VOL_OPT_2},
{"VOL_OPT_AMP", EnemyType::VOL_OPT_AMP},
{"VOL_OPT_CORE", EnemyType::VOL_OPT_CORE},
{"VOL_OPT_MONITOR", EnemyType::VOL_OPT_MONITOR},
{"VOL_OPT_PILLAR", EnemyType::VOL_OPT_PILLAR},
{"YOWIE", EnemyType::YOWIE},
{"YOWIE_ALT", EnemyType::YOWIE_ALT},
{"ZE_BOOTA", EnemyType::ZE_BOOTA},
@@ -399,8 +429,6 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
return 0x0E;
case EnemyType::DE_ROL_LE:
return 0x0F;
case EnemyType::DEATH_GUNNER:
throw runtime_error("DEATH_GUNNER entry is not specified");
case EnemyType::DRAGON:
return 0x12;
case EnemyType::SINOW_GOLD:
@@ -413,8 +441,6 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
return 0x1A;
case EnemyType::DUBCHIC:
return 0x1B;
case EnemyType::DUBWITCH:
throw runtime_error("no battle params for DUBWITCH");
case EnemyType::GILLCHIC:
return 0x1C;
case EnemyType::GARANZ:
@@ -425,8 +451,6 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
return 0x1F;
case EnemyType::CLAW:
return 0x20;
case EnemyType::VOL_OPT_1:
throw runtime_error("no battle params for VOL_OPT_1");
case EnemyType::VOL_OPT_2:
return 0x25;
case EnemyType::POUILLY_SLIME:
@@ -476,7 +500,7 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
case EnemyType::SO_DIMENIAN:
return 0x55;
default:
throw runtime_error("incorrect enemy type for Episode 1");
throw runtime_error(string_printf("%s does not have battle parameters in Episode 1", name_for_enum(enemy_type)));
}
break;
case Episode::EP2:
@@ -520,8 +544,6 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
return 0x1A;
case EnemyType::DUBCHIC:
return 0x1B;
case EnemyType::DUBWITCH:
throw runtime_error("no battle params for DUBWITCH");
case EnemyType::GILLCHIC:
return 0x1C;
case EnemyType::GARANZ:
@@ -593,7 +615,7 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
case EnemyType::SO_DIMENIAN:
return 0x55;
default:
throw runtime_error("incorrect enemy type for Episode 2");
throw runtime_error(string_printf("%s does not have battle parameters in Episode 2", name_for_enum(enemy_type)));
}
break;
case Episode::EP4:
@@ -651,7 +673,7 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
case EnemyType::KONDRIEU:
return 0x22;
default:
throw runtime_error("incorrect enemy type for Episode 4");
throw runtime_error(string_printf("%s does not have battle parameters in Episode 4", name_for_enum(enemy_type)));
}
break;
default:
@@ -691,21 +713,14 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
return 0x26;
case EnemyType::DARK_BELRA:
return 0x25;
case EnemyType::DARK_FALZ_1:
throw runtime_error("DARK_FALZ_1 does not have a rare table entry");
case EnemyType::DARK_FALZ_2:
return 0x2F;
case EnemyType::DARK_FALZ_3:
return 0x2F;
case EnemyType::DARK_GUNNER:
return 0x22;
case EnemyType::DARVANT:
case EnemyType::DARVANT_ULTIMATE:
throw runtime_error("DARVANT and DARVANT_ULTIMATE do not have rare table entries");
case EnemyType::DE_ROL_LE:
return 0x2D;
case EnemyType::DEATH_GUNNER:
throw runtime_error("DEATH_GUNNER does not have a rare table entry");
case EnemyType::DEL_LILY:
return 0x53;
case EnemyType::DEL_RAPPY:
@@ -731,16 +746,12 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
return 0x2C;
case EnemyType::DUBCHIC:
return 0x18;
case EnemyType::DUBWITCH:
throw runtime_error("DUBWITCH does not have a rare table entry");
case EnemyType::EGG_RAPPY:
return 0x51;
case EnemyType::EPSILON:
return 0x54;
case EnemyType::EVIL_SHARK:
return 0x10;
case EnemyType::GAEL:
throw runtime_error("GAEL does not have a rare table entry");
case EnemyType::GAL_GRYPHON:
return 0x4D;
case EnemyType::GARANZ:
@@ -811,8 +822,6 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
return 0x0F;
case EnemyType::NAR_LILY:
return 0x0E;
case EnemyType::OLGA_FLOW_1:
throw runtime_error("OLGA_FLOW_1 does not have a rare table entry");
case EnemyType::OLGA_FLOW_2:
return 0x4E;
case EnemyType::PAL_SHARK:
@@ -866,8 +875,6 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
return 0x2B;
case EnemyType::UL_GIBBON:
return 0x3B;
case EnemyType::VOL_OPT_1:
throw runtime_error("VOL_OPT_1 does not have a rare table entry");
case EnemyType::VOL_OPT_2:
return 0x2E;
case EnemyType::YOWIE:
@@ -881,6 +888,6 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::ZU_ALT:
return 0x08;
default:
throw logic_error("invalid enemy type");
throw runtime_error(string_printf("%s does not have a rare table entry", name_for_enum(enemy_type)));
}
}
+10
View File
@@ -14,6 +14,8 @@ enum class EnemyType {
BA_BOOTA,
BARBA_RAY,
BARBAROUS_WOLF,
BEE_L,
BEE_R,
BOOMA,
BOOTA,
BULCLAW,
@@ -31,6 +33,8 @@ enum class EnemyType {
DARVANT,
DARVANT_ULTIMATE,
DE_ROL_LE,
DE_ROL_LE_BODY,
DE_ROL_LE_MINE,
DEATH_GUNNER,
DEL_LILY,
DEL_RAPPY,
@@ -47,6 +51,7 @@ enum class EnemyType {
DUBCHIC,
DUBWITCH, // Has no entry in battle params
EGG_RAPPY,
EPSIGUARD,
EPSILON,
EVIL_SHARK,
GAEL,
@@ -91,6 +96,7 @@ enum class EnemyType {
PAN_ARMS,
PAZUZU,
PAZUZU_ALT,
PIG_RAY,
POFUILLY_SLIME,
POUILLY_SLIME,
POISON_LILY,
@@ -116,6 +122,10 @@ enum class EnemyType {
UL_GIBBON,
VOL_OPT_1,
VOL_OPT_2,
VOL_OPT_AMP,
VOL_OPT_CORE,
VOL_OPT_MONITOR,
VOL_OPT_PILLAR,
YOWIE,
YOWIE_ALT,
ZE_BOOTA,
+4 -4
View File
@@ -235,11 +235,11 @@ ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(
for (const auto& spec : rare_specs) {
item = this->check_rate_and_create_rare_item(spec);
if (!item.empty()) {
this->log.info("Box spec %08" PRIX32 " => %02hhX%02hhX%02hhX produced an item",
this->log.info("Box spec %08" PRIX32 " produced item %02hhX%02hhX%02hhX",
spec.probability, spec.item_code[0], spec.item_code[1], spec.item_code[2]);
break;
}
this->log.info("Box spec %08" PRIX32 " => %02hhX%02hhX%02hhX did not produce",
this->log.info("Box spec %08" PRIX32 " did not produce item %02hhX%02hhX%02hhX",
spec.probability, spec.item_code[0], spec.item_code[1], spec.item_code[2]);
}
return item;
@@ -289,11 +289,11 @@ ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(
for (const auto& spec : rare_specs) {
item = this->check_rate_and_create_rare_item(spec);
if (!item.empty()) {
this->log.info("Enemy spec %08" PRIX32 " => %02hhX%02hhX%02hhX did not produce",
this->log.info("Enemy spec %08" PRIX32 " produced item %02hhX%02hhX%02hhX",
spec.probability, spec.item_code[0], spec.item_code[1], spec.item_code[2]);
break;
}
this->log.info("Enemy spec %08" PRIX32 " => %02hhX%02hhX%02hhX did not produce",
this->log.info("Enemy spec %08" PRIX32 " did not produce item %02hhX%02hhX%02hhX",
spec.probability, spec.item_code[0], spec.item_code[1], spec.item_code[2]);
}
}
+100 -66
View File
@@ -9,25 +9,23 @@
using namespace std;
uint64_t Map::Enemy::next_enemy_id = 1;
Map::Enemy::Enemy(EnemyType type)
: id(Map::Enemy::next_enemy_id++),
type(type),
: type(type),
flags(0),
last_hit_by_client_id(0) {}
string Map::Enemy::str() const {
return string_printf("[Map::Enemy E-%" PRIX64 " type=%s flags=%02hhX last_hit_by_client_id=%hu]",
this->id, name_for_enum(this->type), this->flags, this->last_hit_by_client_id);
return string_printf("[Map::Enemy %s flags=%02hhX last_hit_by_client_id=%hu]",
name_for_enum(this->type), this->flags, this->last_hit_by_client_id);
}
struct EnemyEntry {
/* 00 */ le_uint32_t base;
/* 04 */ le_uint16_t unknown_a1;
/* 00 */ le_uint16_t base_type;
/* 02 */ le_uint16_t unknown_a0; // Overwritten by client at load time
/* 04 */ le_uint16_t enemy_index; // Overwritten by client at load time
/* 06 */ le_uint16_t num_clones;
/* 08 */ le_uint16_t area;
/* 0A */ le_uint16_t unknown_a2;
/* 0A */ le_uint16_t entity_id; // == enemy_index + 0x1000
/* 0C */ le_uint16_t section;
/* 0E */ le_uint16_t wave_number;
/* 10 */ le_uint32_t wave_number2;
@@ -47,9 +45,9 @@ struct EnemyEntry {
/* 48 */
string str() const {
return string_printf("EnemyEntry(base=%" PRIX32 ", a1=%hX, num_clones=%hX, area=%hX, a2=%hX, section=%hX, wave_number=%hX, wave_number2=%" PRIX32 ", x=%g, y=%g, z=%g, x_angle=%" PRIX32 ", y_angle=%" PRIX32 ", z_angle=%" PRIX32 ", a3=%" PRIX32 ", a4=%" PRIX32 ", a5=%" PRIX32 ", a6=%" PRIX32 ", a7=%" PRIX32 ", skin=%" PRIX32 ", a8=%" PRIX32 ")",
this->base.load(), this->unknown_a1.load(), this->num_clones.load(), this->area.load(),
this->unknown_a2.load(), this->section.load(), this->wave_number.load(),
return string_printf("EnemyEntry(base_type=%hX, a0=%hX, enemy_index=%hX, num_clones=%hX, area=%hX, entity_id=%hX, section=%hX, wave_number=%hX, wave_number2=%" PRIX32 ", x=%g, y=%g, z=%g, x_angle=%" PRIX32 ", y_angle=%" PRIX32 ", z_angle=%" PRIX32 ", a3=%" PRIX32 ", a4=%" PRIX32 ", a5=%" PRIX32 ", a6=%" PRIX32 ", a7=%" PRIX32 ", skin=%" PRIX32 ", a8=%" PRIX32 ")",
this->base_type.load(), this->unknown_a0.load(), this->enemy_index.load(), this->num_clones.load(), this->area.load(),
this->entity_id.load(), this->section.load(), this->wave_number.load(),
this->wave_number2.load(), this->x.load(), this->y.load(), this->z.load(), this->x_angle.load(),
this->y_angle.load(), this->z_angle.load(), this->unknown_a3.load(), this->unknown_a4.load(),
this->unknown_a5.load(), this->unknown_a6.load(), this->unknown_a7.load(), this->skin.load(),
@@ -58,7 +56,7 @@ struct EnemyEntry {
} __attribute__((packed));
struct ObjectEntry {
/* 00 */ le_uint16_t type;
/* 00 */ le_uint16_t base_type;
/* 02 */ le_uint16_t unknown_a1;
/* 04 */ le_uint32_t unknown_a2;
/* 08 */ le_uint16_t id;
@@ -81,8 +79,8 @@ struct ObjectEntry {
/* 44 */
string str() const {
return string_printf("ObjectEntry(type=%hX, a1=%hX, a2=%" PRIX32 ", id=%hX, group=%hX, section=%hX, a3=%hX, x=%g, y=%g, z=%g, x_angle=%" PRIX32 ", y_angle=%" PRIX32 ", z_angle=%" PRIX32 ", a3=%" PRIX32 ", a4=%" PRIX32 ", a5=%" PRIX32 ", a6=%" PRIX32 ", a7=%" PRIX32 ", a8=%" PRIX32 ", a9=%" PRIX32 ")",
this->type.load(), this->unknown_a1.load(), this->unknown_a2.load(), this->id.load(), this->group.load(),
return string_printf("ObjectEntry(base_type=%hX, a1=%hX, a2=%" PRIX32 ", id=%hX, group=%hX, section=%hX, a3=%hX, x=%g, y=%g, z=%g, x_angle=%" PRIX32 ", y_angle=%" PRIX32 ", z_angle=%" PRIX32 ", a3=%" PRIX32 ", a4=%" PRIX32 ", a5=%" PRIX32 ", a6=%" PRIX32 ", a7=%" PRIX32 ", a8=%" PRIX32 ", a9=%" PRIX32 ")",
this->base_type.load(), this->unknown_a1.load(), this->unknown_a2.load(), this->id.load(), this->group.load(),
this->section.load(), this->unknown_a3.load(), this->x.load(), this->y.load(), this->z.load(), this->x_angle.load(),
this->y_angle.load(), this->z_angle.load(), this->unknown_a3.load(), this->unknown_a4.load(),
this->unknown_a5.load(), this->unknown_a6.load(), this->unknown_a7.load(), this->unknown_a8.load(),
@@ -103,16 +101,13 @@ void Map::add_enemies_from_map_data(
throw runtime_error("data size is not a multiple of entry size");
}
auto create_clones = [&](size_t count) {
for (; count > 0; count--) {
this->enemies.emplace_back(EnemyType::NONE);
}
};
for (size_t y = 0; y < entry_count; y++) {
const auto& e = map[y];
switch (e.base) {
string hex = format_data_string(&e, sizeof(e));
fprintf(stderr, "[%04zX] %s\n", y, hex.c_str());
switch (e.base_type) {
case 0x40:
enemies.emplace_back((e.skin & 0x01) ? EnemyType::HILDEBLUE : EnemyType::HILDEBEAR);
break;
@@ -123,18 +118,22 @@ void Map::add_enemies_from_map_data(
enemies.emplace_back(is_rare ? EnemyType::AL_RAPPY : EnemyType::RAG_RAPPY);
break;
case Episode::EP2:
switch (event) {
case 0x01:
enemies.emplace_back(EnemyType::SAINT_RAPPY);
break;
case 0x04:
enemies.emplace_back(EnemyType::EGG_RAPPY);
break;
case 0x05:
enemies.emplace_back(EnemyType::HALLO_RAPPY);
break;
default:
enemies.emplace_back(EnemyType::LOVE_RAPPY);
if (is_rare) {
switch (event) {
case 0x01:
enemies.emplace_back(EnemyType::SAINT_RAPPY);
break;
case 0x04:
enemies.emplace_back(EnemyType::EGG_RAPPY);
break;
case 0x05:
enemies.emplace_back(EnemyType::HALLO_RAPPY);
break;
default:
enemies.emplace_back(EnemyType::LOVE_RAPPY);
}
} else {
enemies.emplace_back(EnemyType::RAG_RAPPY);
}
break;
case Episode::EP4:
@@ -149,14 +148,15 @@ void Map::add_enemies_from_map_data(
}
break;
}
case 0x42:
case 0x42: {
enemies.emplace_back(EnemyType::MONEST);
for (size_t x = 0; x < e.num_clones; x++) {
enemies.emplace_back((x < 30) ? EnemyType::MOTHMANT : EnemyType::UNKNOWN);
for (size_t x = 0; x < 30; x++) {
enemies.emplace_back(EnemyType::MOTHMANT);
}
break;
}
case 0x43: {
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::BARBAROUS_WOLF : EnemyType::SAVAGE_WOLF);
enemies.emplace_back(e.unknown_a4 ? EnemyType::BARBAROUS_WOLF : EnemyType::SAVAGE_WOLF);
break;
}
case 0x44:
@@ -170,7 +170,7 @@ void Map::add_enemies_from_map_data(
if ((episode == Episode::EP2) && (e.area > 0x0F)) {
enemies.emplace_back(EnemyType::DEL_LILY);
} else {
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::NAR_LILY : EnemyType::POISON_LILY);
enemies.emplace_back((e.skin & 1) ? EnemyType::NAR_LILY : EnemyType::POISON_LILY);
}
break;
case 0x62:
@@ -182,9 +182,9 @@ void Map::add_enemies_from_map_data(
break;
}
case 0x64: {
bool is_common = ((e.unknown_a4 & 0x800000) ? true : false);
bool is_rare = (e.skin & 1);
for (size_t x = 0; x < 5; x++) { // Main slime + 4 clones
enemies.emplace_back(is_common ? EnemyType::POFUILLY_SLIME : EnemyType::POUILLY_SLIME);
enemies.emplace_back(is_rare ? EnemyType::POFUILLY_SLIME : EnemyType::POUILLY_SLIME);
}
break;
}
@@ -199,19 +199,21 @@ void Map::add_enemies_from_map_data(
case 0x81:
enemies.emplace_back(EnemyType::GARANZ);
break;
case 0x82:
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::SINOW_GOLD : EnemyType::SINOW_BEAT);
if (e.num_clones == 0) {
create_clones(4);
case 0x82: {
EnemyType type = e.unknown_a4 ? EnemyType::SINOW_GOLD : EnemyType::SINOW_BEAT;
size_t count = (e.num_clones == 0) ? 5 : (e.num_clones + 1);
for (size_t z = 0; z < count; z++) {
enemies.emplace_back(type);
}
break;
}
case 0x83:
enemies.emplace_back(EnemyType::CANADINE);
break;
case 0x84:
enemies.emplace_back(EnemyType::CANANE);
for (size_t x = 0; x < 8; x++) {
enemies.emplace_back(EnemyType::CANADINE);
enemies.emplace_back(EnemyType::CANADINE_GROUP);
}
break;
case 0x85:
@@ -222,7 +224,8 @@ void Map::add_enemies_from_map_data(
break;
case 0xA1:
enemies.emplace_back(EnemyType::CHAOS_SORCERER);
create_clones(2);
enemies.emplace_back(EnemyType::BEE_R);
enemies.emplace_back(EnemyType::BEE_L);
break;
case 0xA2:
enemies.emplace_back(EnemyType::DARK_GUNNER);
@@ -261,9 +264,27 @@ void Map::add_enemies_from_map_data(
break;
case 0xC1:
enemies.emplace_back(EnemyType::DE_ROL_LE);
for (size_t z = 0; z < 0x0A; z++) {
enemies.emplace_back(EnemyType::DE_ROL_LE_BODY);
}
for (size_t z = 0; z < 0x09; z++) {
enemies.emplace_back(EnemyType::DE_ROL_LE_MINE);
}
break;
case 0xC2:
enemies.emplace_back(EnemyType::VOL_OPT_1);
for (size_t z = 0; z < 6; z++) {
enemies.emplace_back(EnemyType::VOL_OPT_PILLAR);
}
for (size_t z = 0; z < 24; z++) {
enemies.emplace_back(EnemyType::VOL_OPT_MONITOR);
}
for (size_t z = 0; z < 2; z++) {
enemies.emplace_back(EnemyType::NONE);
}
enemies.emplace_back(EnemyType::VOL_OPT_AMP);
enemies.emplace_back(EnemyType::VOL_OPT_CORE);
enemies.emplace_back(EnemyType::NONE);
break;
case 0xC5:
enemies.emplace_back(EnemyType::VOL_OPT_2);
@@ -274,26 +295,36 @@ void Map::add_enemies_from_map_data(
} else {
enemies.emplace_back(EnemyType::DARK_FALZ_2);
}
for (size_t x = 0; x < 510; x++) {
for (size_t x = 0; x < 0x1FD; x++) {
enemies.emplace_back(difficulty == 3 ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT);
}
enemies.emplace_back(EnemyType::DARK_FALZ_3);
enemies.emplace_back(EnemyType::DARK_FALZ_2);
enemies.emplace_back(EnemyType::DARK_FALZ_1);
break;
case 0xCA:
enemies.emplace_back(EnemyType::OLGA_FLOW_2);
create_clones(0x200);
for (size_t z = 0; z < 0x201; z++) {
enemies.emplace_back(EnemyType::OLGA_FLOW_2);
}
break;
case 0xCB:
enemies.emplace_back(EnemyType::BARBA_RAY);
create_clones(0x2F);
for (size_t z = 0; z < 0x2F; z++) {
enemies.emplace_back(EnemyType::PIG_RAY);
}
break;
case 0xCC:
enemies.emplace_back(EnemyType::GOL_DRAGON);
create_clones(5);
for (size_t z = 0; z < 6; z++) {
enemies.emplace_back(EnemyType::GOL_DRAGON);
}
break;
case 0xD4:
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::SINOW_SPIGELL : EnemyType::SINOW_BERILL);
create_clones(4);
case 0xD4: {
EnemyType type = (e.skin & 1) ? EnemyType::SINOW_SPIGELL : EnemyType::SINOW_BERILL;
for (size_t z = 0; z < 5; z++) {
enemies.emplace_back(type);
}
break;
}
case 0xD5:
enemies.emplace_back((e.skin & 0x01) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
break;
@@ -337,7 +368,9 @@ void Map::add_enemies_from_map_data(
case 0xE0:
if ((episode == Episode::EP2) && (e.area > 0x0F)) {
enemies.emplace_back(EnemyType::EPSILON);
create_clones(4);
for (size_t z = 0; z < 4; z++) {
enemies.emplace_back(EnemyType::EPSIGUARD);
}
} else {
enemies.emplace_back((e.skin & 0x01) ? EnemyType::SINOW_ZELE : EnemyType::SINOW_ZOA);
}
@@ -350,9 +383,9 @@ void Map::add_enemies_from_map_data(
break;
case 0x0111:
if (e.area > 0x05) {
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::YOWIE_ALT : EnemyType::SATELLITE_LIZARD_ALT);
enemies.emplace_back(e.unknown_a4 ? EnemyType::YOWIE_ALT : EnemyType::SATELLITE_LIZARD_ALT);
} else {
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::YOWIE : EnemyType::SATELLITE_LIZARD);
enemies.emplace_back(e.unknown_a4 ? EnemyType::YOWIE : EnemyType::SATELLITE_LIZARD);
}
break;
case 0x0112:
@@ -363,9 +396,9 @@ void Map::add_enemies_from_map_data(
break;
case 0x0114:
if (e.area > 0x05) {
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::PAZUZU_ALT : EnemyType::ZU_ALT);
enemies.emplace_back((e.skin & 1) ? EnemyType::PAZUZU_ALT : EnemyType::ZU_ALT);
} else {
enemies.emplace_back((e.unknown_a4 & 0x800000) ? EnemyType::PAZUZU : EnemyType::ZU);
enemies.emplace_back((e.skin & 1) ? EnemyType::PAZUZU : EnemyType::ZU);
}
break;
case 0x0115:
@@ -384,20 +417,21 @@ void Map::add_enemies_from_map_data(
break;
}
case 0x0119: // Saint-Million, Shambertin, Kondrieu
if (e.unknown_a4 & 0x800000) {
if (e.unknown_a4) {
enemies.emplace_back(EnemyType::KONDRIEU);
} else {
enemies.emplace_back((e.skin & 1) ? EnemyType::SHAMBERTIN : EnemyType::SAINT_MILLION);
}
break;
default:
enemies.emplace_back(EnemyType::UNKNOWN);
for (size_t z = 0; z < e.num_clones + 1; z++) {
enemies.emplace_back(EnemyType::UNKNOWN);
}
static_game_data_log.warning(
"(Entry %zu, offset %zX in file) Unknown enemy type %08" PRIX32 " %08" PRIX32,
y, y * sizeof(EnemyEntry), e.base.load(), e.skin.load());
"(Entry %zu, offset %zX in file) Unknown enemy type %04hX",
y, y * sizeof(EnemyEntry), e.base_type.load());
break;
}
create_clones(e.num_clones);
}
}
-3
View File
@@ -15,8 +15,6 @@
struct Map {
struct Enemy {
static uint64_t next_enemy_id;
enum Flag {
HIT_BY_PLAYER0 = 0x01,
HIT_BY_PLAYER1 = 0x02,
@@ -25,7 +23,6 @@ struct Map {
DEFEATED = 0x10,
ITEM_DROPPED = 0x20,
};
uint64_t id;
EnemyType type;
uint8_t flags;
uint8_t last_hit_by_client_id;
+4 -4
View File
@@ -1237,10 +1237,10 @@ static void on_enemy_killed_bb(shared_ptr<ServerState> s,
auto& e = l->map->enemies[cmd.enemy_id];
string e_str = e.str();
c->log.info("Enemy killed: entry %hu => %s", cmd.enemy_id.load(), e_str.c_str());
c->log.info("Enemy killed: E-%hX => %s", cmd.enemy_id.load(), e_str.c_str());
if (e.flags & Map::Enemy::Flag::DEFEATED) {
if (c->options.debug) {
send_text_message_printf(c, "$C5E-%hX (already defeated)", cmd.enemy_id.load());
send_text_message_printf(c, "$C5E-%hX __DEFEATED__", cmd.enemy_id.load());
}
return;
}
@@ -1250,7 +1250,7 @@ static void on_enemy_killed_bb(shared_ptr<ServerState> s,
experience = s->battle_params->get(l->mode == GameMode::SOLO, l->episode, l->difficulty, e.type).experience;
} catch (const exception& e) {
if (c->options.debug) {
send_text_message_printf(c, "$C5E-%hX (missing definition)\n%s", cmd.enemy_id.load(), e.what());
send_text_message_printf(c, "$C5E-%hX __MISSING__\n%s", cmd.enemy_id.load(), e.what());
} else {
send_text_message_printf(c, "$C4Unknown enemy type killed:\n%s", e.what());
}
@@ -1279,7 +1279,7 @@ static void on_enemy_killed_bb(shared_ptr<ServerState> s,
other_c->game_data.player()->disp.experience += player_exp;
send_give_experience(l, other_c, player_exp);
if (other_c->options.debug) {
send_text_message_printf(other_c, "$C5+%" PRIu32 " E-%hX (%s)",
send_text_message_printf(other_c, "$C5+%" PRIu32 " E-%hX %s",
player_exp, cmd.enemy_id.load(), name_for_enum(e.type));
}