make episode an enum class
This commit is contained in:
+17
-30
@@ -106,7 +106,7 @@ static void server_command_lobby_info(shared_ptr<ServerState>, shared_ptr<Lobby>
|
||||
if (l->is_game()) {
|
||||
lines.emplace_back(string_printf("Game ID: $C6%08X$C7", l->lobby_id));
|
||||
|
||||
if (!(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (!l->is_ep3()) {
|
||||
if (l->max_level == 0xFFFFFFFF) {
|
||||
lines.emplace_back(string_printf("Levels: $C6%d+$C7", l->min_level + 1));
|
||||
} else {
|
||||
@@ -244,7 +244,7 @@ static void server_command_debug(shared_ptr<ServerState>, shared_ptr<Lobby>,
|
||||
static void server_command_auction(shared_ptr<ServerState>, shared_ptr<Lobby> l,
|
||||
shared_ptr<Client> c, const std::u16string&) {
|
||||
check_privileges(c, Privilege::DEBUG);
|
||||
if (l->is_game() && (l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (l->is_game() && l->is_ep3()) {
|
||||
G_InitiateCardAuction_GC_Ep3_6xB5x42 cmd;
|
||||
send_command_t(l, 0xC9, 0x00, cmd);
|
||||
}
|
||||
@@ -469,7 +469,7 @@ static void server_command_lobby_type(shared_ptr<ServerState>, shared_ptr<Lobby>
|
||||
}
|
||||
|
||||
l->type = new_type;
|
||||
if (l->type < ((l->flags & Lobby::Flag::EPISODE_3_ONLY) ? 20 : 15)) {
|
||||
if (l->type < (l->is_ep3() ? 20 : 15)) {
|
||||
l->type = l->block - 1;
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ static void server_command_playrec(shared_ptr<ServerState> s, shared_ptr<Lobby>
|
||||
if (l->battle_player) {
|
||||
l->battle_player->start();
|
||||
} else {
|
||||
uint32_t flags = Lobby::Flag::NON_V1_ONLY | Lobby::Flag::EPISODE_3_ONLY | Lobby::Flag::IS_SPECTATOR_TEAM;
|
||||
uint32_t flags = Lobby::Flag::NON_V1_ONLY | Lobby::Flag::IS_SPECTATOR_TEAM;
|
||||
string filename = encode_sjis(args);
|
||||
if (filename[0] == '!') {
|
||||
flags |= Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY;
|
||||
@@ -521,7 +521,7 @@ static void server_command_playrec(shared_ptr<ServerState> s, shared_ptr<Lobby>
|
||||
shared_ptr<Episode3::BattleRecord> record(new Episode3::BattleRecord(data));
|
||||
shared_ptr<Episode3::BattleRecordPlayer> battle_player(
|
||||
new Episode3::BattleRecordPlayer(record, s->game_server->get_base()));
|
||||
create_game_generic(s, c, args.c_str(), u"", 0xFF, 0, flags, nullptr, battle_player);
|
||||
create_game_generic(s, c, args.c_str(), u"", Episode::EP3, 0, flags, nullptr, battle_player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,7 +608,7 @@ static void server_command_spec(shared_ptr<ServerState>, shared_ptr<Lobby> l,
|
||||
check_is_game(l, true);
|
||||
check_is_leader(l, c);
|
||||
check_is_ep3(c, true);
|
||||
if (!(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (!l->is_ep3()) {
|
||||
throw logic_error("Episode 3 client in non-Episode 3 game");
|
||||
}
|
||||
|
||||
@@ -909,23 +909,15 @@ static void server_command_warp(shared_ptr<ServerState>, shared_ptr<Lobby> l,
|
||||
check_cheats_enabled(l);
|
||||
|
||||
uint32_t area = stoul(encode_sjis(args), nullptr, 0);
|
||||
if (!l->episode || (l->episode > 3)) {
|
||||
return;
|
||||
}
|
||||
if (c->area == area) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((l->episode == 1) && (area > 17)) {
|
||||
send_text_message(c, u"$C6Area numbers must be\n17 or less.");
|
||||
size_t limit = area_limit_for_episode(l->episode);
|
||||
if (limit == 0) {
|
||||
return;
|
||||
}
|
||||
if ((l->episode == 2) && (area > 17)) {
|
||||
send_text_message(c, u"$C6Area numbers must be\n17 or less.");
|
||||
return;
|
||||
}
|
||||
if ((l->episode == 3) && (area > 10)) {
|
||||
send_text_message(c, u"$C6Area numbers must be\n10 or less.");
|
||||
} else if (area > limit) {
|
||||
send_text_message_printf(c, "$C6Area numbers must\nbe %zu or less.", limit);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -939,6 +931,7 @@ static void proxy_command_warp(shared_ptr<ServerState>,
|
||||
return;
|
||||
}
|
||||
uint32_t area = stoul(encode_sjis(args), nullptr, 0);
|
||||
// TODO: Add limit check here like in the server command implementation
|
||||
send_warp(session.client_channel, session.lobby_client_id, area);
|
||||
session.area = area;
|
||||
}
|
||||
@@ -948,18 +941,11 @@ static void server_command_next(shared_ptr<ServerState>, shared_ptr<Lobby> l,
|
||||
check_is_game(l, true);
|
||||
check_cheats_enabled(l);
|
||||
|
||||
if (!l->episode || (l->episode > 3)) {
|
||||
throw runtime_error("invalid episode number");
|
||||
size_t limit = area_limit_for_episode(l->episode);
|
||||
if (limit == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t new_area = c->area + 1;
|
||||
if (((l->episode == 1) && (new_area > 17)) ||
|
||||
((l->episode == 2) && (new_area > 17)) ||
|
||||
((l->episode == 3) && (new_area > 10))) {
|
||||
new_area = 0;
|
||||
}
|
||||
|
||||
send_warp(c, new_area);
|
||||
send_warp(c, (c->area + 1) % limit);
|
||||
}
|
||||
|
||||
static void proxy_command_next(shared_ptr<ServerState>,
|
||||
@@ -976,7 +962,8 @@ static void proxy_command_next(shared_ptr<ServerState>,
|
||||
static void server_command_what(shared_ptr<ServerState>, shared_ptr<Lobby> l,
|
||||
shared_ptr<Client> c, const std::u16string&) {
|
||||
check_is_game(l, true);
|
||||
if (!l->episode || (l->episode > 3)) {
|
||||
|
||||
if (!episode_has_arpg_semantics(l->episode)) {
|
||||
return;
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
|
||||
+4
-4
@@ -298,12 +298,12 @@ int32_t CommonItemCreator::decide_item_type(bool is_box) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
|
||||
ItemData CommonItemCreator::create_drop_item(bool is_box, Episode episode,
|
||||
uint8_t difficulty, uint8_t area, uint8_t) const {
|
||||
// TODO: use the section ID (last argument) to vary drop frequencies appropriately
|
||||
// change the area if it's invalid (data for the bosses are actually in other areas)
|
||||
if (area > 10) {
|
||||
if (episode == 1) {
|
||||
if (episode == Episode::EP1) {
|
||||
if (area == 11) {
|
||||
area = 3; // dragon
|
||||
} else if (area == 12) {
|
||||
@@ -315,7 +315,7 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
|
||||
} else {
|
||||
area = 1; // unknown area -> forest 1
|
||||
}
|
||||
} else if (episode == 2) {
|
||||
} else if (episode == Episode::EP2) {
|
||||
if (area == 12) {
|
||||
area = 9; // gal gryphon
|
||||
} else if (area == 13) {
|
||||
@@ -327,7 +327,7 @@ ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
|
||||
} else {
|
||||
area = 10; // tower
|
||||
}
|
||||
} else if (episode == 3) {
|
||||
} else if (episode == Episode::EP4) {
|
||||
area = 1;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@
|
||||
#include <random>
|
||||
|
||||
#include "Client.hh"
|
||||
#include "StaticGameData.hh"
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +32,7 @@ struct CommonItemCreator {
|
||||
std::shared_ptr<std::mt19937> random);
|
||||
|
||||
int32_t decide_item_type(bool is_box) const;
|
||||
ItemData create_drop_item(bool is_box, uint8_t episode, uint8_t difficulty,
|
||||
ItemData create_drop_item(bool is_box, Episode episode, uint8_t difficulty,
|
||||
uint8_t area, uint8_t section_id) const;
|
||||
ItemData create_shop_item(uint8_t difficulty, uint8_t shop_type) const;
|
||||
};
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ Lobby::Lobby(uint32_t id)
|
||||
next_game_item_id(0x00810000),
|
||||
version(GameVersion::GC),
|
||||
section_id(0),
|
||||
episode(1),
|
||||
episode(Episode::NONE),
|
||||
difficulty(0),
|
||||
random_seed(random_object<uint32_t>()),
|
||||
random(new mt19937(this->random_seed)),
|
||||
@@ -140,7 +140,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
|
||||
}
|
||||
|
||||
// Send spectator count notifications if needed
|
||||
if (this->is_game() && (this->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (this->is_game() && this->is_ep3()) {
|
||||
if (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
auto watched_l = this->watched_lobby.lock();
|
||||
if (watched_l) {
|
||||
@@ -178,7 +178,7 @@ void Lobby::remove_client(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
// If the lobby is Episode 3, update the appropriate spectator counts
|
||||
if (this->is_game() && (this->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (this->is_game() && this->is_ep3()) {
|
||||
if (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
auto watched_l = this->watched_lobby.lock();
|
||||
if (watched_l) {
|
||||
|
||||
+17
-12
@@ -11,21 +11,23 @@
|
||||
#include <phosg/Encoding.hh>
|
||||
|
||||
#include "Client.hh"
|
||||
#include "Player.hh"
|
||||
#include "Map.hh"
|
||||
#include "RareItemSet.hh"
|
||||
#include "Text.hh"
|
||||
#include "Quest.hh"
|
||||
#include "Items.hh"
|
||||
#include "Episode3/BattleRecord.hh"
|
||||
#include "Episode3/Server.hh"
|
||||
#include "Items.hh"
|
||||
#include "Map.hh"
|
||||
#include "Player.hh"
|
||||
#include "Quest.hh"
|
||||
#include "RareItemSet.hh"
|
||||
#include "StaticGameData.hh"
|
||||
#include "Text.hh"
|
||||
|
||||
|
||||
|
||||
struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
enum Flag {
|
||||
GAME = 0x00000001,
|
||||
EPISODE_3_ONLY = 0x00000002,
|
||||
NON_V1_ONLY = 0x00000004, // DC NTE and DCv1 not allowed
|
||||
PERSISTENT = 0x00000008,
|
||||
NON_V1_ONLY = 0x00000002, // DC NTE and DCv1 not allowed
|
||||
PERSISTENT = 0x00000004,
|
||||
|
||||
// Flags used only for games
|
||||
CHEATS_ENABLED = 0x00000100,
|
||||
@@ -33,7 +35,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
BATTLE_IN_PROGRESS = 0x00000400,
|
||||
JOINABLE_QUEST_IN_PROGRESS = 0x00000800,
|
||||
ITEM_TRACKING_ENABLED = 0x00001000,
|
||||
IS_SPECTATOR_TEAM = 0x00002000, // EPISODE_3_ONLY must also be set
|
||||
IS_SPECTATOR_TEAM = 0x00002000, // episode must be EP3 also
|
||||
SPECTATORS_FORBIDDEN = 0x00004000,
|
||||
BATTLE_MODE = 0x00008000,
|
||||
CHALLENGE_MODE = 0x00010000,
|
||||
@@ -68,8 +70,8 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
// Game config
|
||||
GameVersion version;
|
||||
uint8_t section_id;
|
||||
uint8_t episode; // 1 = Ep1, 2 = Ep2, 3 = Ep4, 0xFF = Ep3
|
||||
uint8_t difficulty;
|
||||
Episode episode;
|
||||
uint8_t difficulty; // 0-3
|
||||
std::u16string password;
|
||||
std::u16string name;
|
||||
// This seed is also sent to the client for rare enemy generation
|
||||
@@ -113,6 +115,9 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
inline bool is_game() const {
|
||||
return this->flags & Flag::GAME;
|
||||
}
|
||||
inline bool is_ep3() const {
|
||||
return this->episode == Episode::EP3;
|
||||
}
|
||||
|
||||
void reassign_leader_on_client_departure(size_t leaving_client_id);
|
||||
size_t count_clients() const;
|
||||
|
||||
+42
-17
@@ -86,17 +86,30 @@ BattleParamsIndex::BattleParamsIndex(
|
||||
}
|
||||
|
||||
const BattleParamsIndex::Entry& BattleParamsIndex::get(
|
||||
bool solo, uint8_t episode, uint8_t difficulty, uint8_t monster_type) const {
|
||||
if (episode > 3) {
|
||||
throw invalid_argument("incorrect episode");
|
||||
}
|
||||
bool solo, Episode episode, uint8_t difficulty, uint8_t monster_type) const {
|
||||
if (difficulty > 4) {
|
||||
throw invalid_argument("incorrect difficulty");
|
||||
}
|
||||
if (monster_type > 0x60) {
|
||||
throw invalid_argument("incorrect monster type");
|
||||
}
|
||||
return this->files[!!solo][episode].table->difficulty[difficulty][monster_type];
|
||||
|
||||
uint8_t ep_index;
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
ep_index = 0;
|
||||
break;
|
||||
case Episode::EP2:
|
||||
ep_index = 1;
|
||||
break;
|
||||
case Episode::EP4:
|
||||
ep_index = 2;
|
||||
break;
|
||||
default:
|
||||
throw invalid_argument("invalid episode");
|
||||
}
|
||||
|
||||
return this->files[!!solo][ep_index].table->difficulty[difficulty][monster_type];
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +156,7 @@ static uint64_t next_enemy_id = 1;
|
||||
vector<PSOEnemy> parse_map(
|
||||
shared_ptr<const BattleParamsIndex> battle_params,
|
||||
bool is_solo,
|
||||
uint8_t episode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
shared_ptr<const string> data,
|
||||
bool alt_enemies) {
|
||||
@@ -185,7 +198,7 @@ vector<PSOEnemy> parse_map(
|
||||
create_enemy(e, 0x49 + (e.skin & 0x01), 0x01 + (e.skin & 0x01), "Hilde(bear|torr)");
|
||||
break;
|
||||
case 0x41: // Rappies
|
||||
if (episode == 3) { // Del Rappy and Sand Rappy
|
||||
if (episode == Episode::EP4) { // Del Rappy and Sand Rappy
|
||||
if (alt_enemies) {
|
||||
create_enemy(e, 0x17 + (e.skin & 0x01), 17 + (e.skin & 0x01), "(Del|Sand) Rappy");
|
||||
} else {
|
||||
@@ -217,7 +230,7 @@ vector<PSOEnemy> parse_map(
|
||||
create_enemy(e, 0x4E, 12, "Grass Assassin");
|
||||
break;
|
||||
case 0x61: // Del Lily, Poison Lily, Nar Lily
|
||||
if ((episode == 2) && (alt_enemies)) {
|
||||
if ((episode == Episode::EP2) && (alt_enemies)) {
|
||||
create_enemy(e, 0x25, 83, "Del Lily");
|
||||
} else {
|
||||
create_enemy(e, 0x04 + ((e.reserved[10] & 0x800000) ? 1 : 0),
|
||||
@@ -303,9 +316,9 @@ vector<PSOEnemy> parse_map(
|
||||
create_enemy(e, 0x20, 38, "Claw");
|
||||
break;
|
||||
case 0xC0: // Dragon or Gal Gryphon
|
||||
if (episode == 1) {
|
||||
if (episode == Episode::EP1) {
|
||||
create_enemy(e, 0x12, 44, "Dragon");
|
||||
} else if (episode == 2) {
|
||||
} else if (episode == Episode::EP2) {
|
||||
create_enemy(e, 0x1E, 77, "Gal Gryphon");
|
||||
}
|
||||
break;
|
||||
@@ -386,7 +399,7 @@ vector<PSOEnemy> parse_map(
|
||||
}
|
||||
break;
|
||||
case 0xE0: // Epsilon, Sinow Zoa and Zele
|
||||
if ((episode == 2) && (alt_enemies)) {
|
||||
if ((episode == Episode::EP2) && (alt_enemies)) {
|
||||
create_enemy(e, 0x23, 84, "Epsilon");
|
||||
create_clones(4);
|
||||
} else {
|
||||
@@ -524,8 +537,7 @@ struct AreaMapFileIndex {
|
||||
variation2_values(variation2_values) { }
|
||||
};
|
||||
|
||||
// These are indexed as [episode][is_solo][area]
|
||||
// (Note that Lobby::episode is 1-3, so we actually use episode - 1)
|
||||
// These are indexed as [episode][is_solo][area], where episode is 0-2
|
||||
static const vector<vector<vector<AreaMapFileIndex>>> map_file_info = {
|
||||
{ // Episode 1
|
||||
{ // Non-solo
|
||||
@@ -643,12 +655,25 @@ static const vector<vector<vector<AreaMapFileIndex>>> map_file_info = {
|
||||
},
|
||||
};
|
||||
|
||||
const vector<vector<AreaMapFileIndex>>& map_file_info_for_episode(Episode ep) {
|
||||
switch (ep) {
|
||||
case Episode::EP1:
|
||||
return map_file_info.at(0);
|
||||
case Episode::EP2:
|
||||
return map_file_info.at(1);
|
||||
case Episode::EP4:
|
||||
return map_file_info.at(2);
|
||||
default:
|
||||
throw invalid_argument("episode has no maps");
|
||||
}
|
||||
}
|
||||
|
||||
void generate_variations(
|
||||
parray<le_uint32_t, 0x20>& variations,
|
||||
shared_ptr<mt19937> random,
|
||||
uint8_t episode,
|
||||
Episode episode,
|
||||
bool is_solo) {
|
||||
const auto& ep_index = map_file_info.at(episode - 1);
|
||||
const auto& ep_index = map_file_info_for_episode(episode);
|
||||
for (size_t z = 0; z < 0x10; z++) {
|
||||
const AreaMapFileIndex* a = nullptr;
|
||||
if (is_solo) {
|
||||
@@ -670,7 +695,7 @@ void generate_variations(
|
||||
}
|
||||
|
||||
vector<string> map_filenames_for_variation(
|
||||
uint8_t episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2) {
|
||||
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2) {
|
||||
// Map filenames are like map_<name_token>[_VV][_VV][_off]<e|o>[_s].dat
|
||||
// name_token comes from AreaMapFileIndex
|
||||
// _VV are the values from the variation<1|2>_values vector (in contrast to
|
||||
@@ -678,7 +703,7 @@ vector<string> map_filenames_for_variation(
|
||||
// _off or _s are used for solo mode (try both - city uses _s whereas levels
|
||||
// use _off apparently)
|
||||
// e|o specifies what kind of data: e = enemies, o = objects
|
||||
const auto& ep_index = map_file_info.at(episode - 1);
|
||||
const auto& ep_index = map_file_info_for_episode(episode);
|
||||
const AreaMapFileIndex* a = nullptr;
|
||||
if (is_solo) {
|
||||
a = &ep_index.at(true).at(area);
|
||||
|
||||
+5
-4
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "StaticGameData.hh"
|
||||
#include "Text.hh"
|
||||
|
||||
|
||||
@@ -44,7 +45,7 @@ public:
|
||||
std::shared_ptr<const std::string> data_off_ep2, // BattleParamEntry_lab.dat
|
||||
std::shared_ptr<const std::string> data_off_ep4); // BattleParamEntry_ep4.dat
|
||||
|
||||
const Entry& get(bool solo, uint8_t episode, uint8_t difficulty,
|
||||
const Entry& get(bool solo, Episode episode, uint8_t difficulty,
|
||||
uint8_t monster_type) const;
|
||||
|
||||
private:
|
||||
@@ -84,7 +85,7 @@ struct PSOEnemy {
|
||||
std::vector<PSOEnemy> parse_map(
|
||||
std::shared_ptr<const BattleParamsIndex> battle_params,
|
||||
bool is_solo,
|
||||
uint8_t episode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
std::shared_ptr<const std::string> data,
|
||||
bool alt_enemies);
|
||||
@@ -125,8 +126,8 @@ private:
|
||||
void generate_variations(
|
||||
parray<le_uint32_t, 0x20>& variations,
|
||||
std::shared_ptr<std::mt19937> random,
|
||||
uint8_t episode,
|
||||
Episode episode,
|
||||
bool is_solo);
|
||||
std::vector<std::string> map_filenames_for_variation(
|
||||
uint8_t episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2);
|
||||
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2);
|
||||
void load_map_files();
|
||||
|
||||
+24
-27
@@ -299,11 +299,11 @@ const char* name_for_category(QuestCategory category) {
|
||||
case QuestCategory::TOWER:
|
||||
return "Tower";
|
||||
case QuestCategory::GOVERNMENT_EPISODE_1:
|
||||
return "GovernmentEpisode1";
|
||||
return "GovernmentEp1";
|
||||
case QuestCategory::GOVERNMENT_EPISODE_2:
|
||||
return "GovernmentEpisode2";
|
||||
return "GovernmentEp2";
|
||||
case QuestCategory::GOVERNMENT_EPISODE_4:
|
||||
return "GovernmentEpisode4";
|
||||
return "GovernmentEp4";
|
||||
case QuestCategory::DOWNLOAD:
|
||||
return "Download";
|
||||
case QuestCategory::BATTLE:
|
||||
@@ -319,21 +319,6 @@ const char* name_for_category(QuestCategory category) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* name_for_episode(uint8_t episode) {
|
||||
switch (episode) {
|
||||
case 0:
|
||||
return "Ep1";
|
||||
case 1:
|
||||
return "Ep2";
|
||||
case 2:
|
||||
return "Ep4";
|
||||
case 0xFF:
|
||||
return "Ep3";
|
||||
default:
|
||||
return "InvalidEpisode";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct PSOQuestHeaderDC { // Same format for DC v1 and v2, thankfully
|
||||
@@ -400,7 +385,7 @@ Quest::Quest(const string& bin_filename)
|
||||
: internal_id(-1),
|
||||
menu_item_id(0),
|
||||
category(QuestCategory::UNKNOWN),
|
||||
episode(0),
|
||||
episode(Episode::NONE),
|
||||
is_dcv1(false),
|
||||
joinable(false),
|
||||
file_format(FileFormat::BIN_DAT),
|
||||
@@ -523,7 +508,7 @@ Quest::Quest(const string& bin_filename)
|
||||
}
|
||||
auto* header = reinterpret_cast<const PSOQuestHeaderDC*>(bin_decompressed.data());
|
||||
this->joinable = false;
|
||||
this->episode = 0;
|
||||
this->episode = Episode::EP1;
|
||||
this->name = decode_sjis(header->name);
|
||||
this->short_description = decode_sjis(header->short_description);
|
||||
this->long_description = decode_sjis(header->long_description);
|
||||
@@ -537,7 +522,7 @@ Quest::Quest(const string& bin_filename)
|
||||
}
|
||||
auto* header = reinterpret_cast<const PSOQuestHeaderPC*>(bin_decompressed.data());
|
||||
this->joinable = false;
|
||||
this->episode = 0;
|
||||
this->episode = Episode::EP1;
|
||||
this->name = header->name;
|
||||
this->short_description = header->short_description;
|
||||
this->long_description = header->long_description;
|
||||
@@ -552,7 +537,7 @@ Quest::Quest(const string& bin_filename)
|
||||
}
|
||||
auto* header = reinterpret_cast<const Episode3::MapDefinition*>(bin_decompressed.data());
|
||||
this->joinable = false;
|
||||
this->episode = 0xFF;
|
||||
this->episode = Episode::EP3;
|
||||
this->name = decode_sjis(header->name);
|
||||
this->short_description = decode_sjis(header->quest_name);
|
||||
this->long_description = decode_sjis(header->description);
|
||||
@@ -562,7 +547,7 @@ Quest::Quest(const string& bin_filename)
|
||||
}
|
||||
auto* header = reinterpret_cast<const PSOQuestHeaderGC*>(bin_decompressed.data());
|
||||
this->joinable = false;
|
||||
this->episode = (header->episode == 1);
|
||||
this->episode = (header->episode == 1) ? Episode::EP2 : Episode::EP1;
|
||||
this->name = decode_sjis(header->name);
|
||||
this->short_description = decode_sjis(header->short_description);
|
||||
this->long_description = decode_sjis(header->long_description);
|
||||
@@ -576,16 +561,28 @@ Quest::Quest(const string& bin_filename)
|
||||
}
|
||||
auto* header = reinterpret_cast<const PSOQuestHeaderBB*>(bin_decompressed.data());
|
||||
this->joinable = header->joinable_in_progress;
|
||||
this->episode = header->episode;
|
||||
switch (header->episode) {
|
||||
case 0:
|
||||
this->episode = Episode::EP1;
|
||||
break;
|
||||
case 1:
|
||||
this->episode = Episode::EP2;
|
||||
break;
|
||||
case 2:
|
||||
this->episode = Episode::EP4;
|
||||
break;
|
||||
default:
|
||||
throw runtime_error("invalid episode number");
|
||||
}
|
||||
this->name = header->name;
|
||||
this->short_description = header->short_description;
|
||||
this->long_description = header->long_description;
|
||||
if (this->category == QuestCategory::GOVERNMENT_EPISODE_1) {
|
||||
if (this->episode == 1) {
|
||||
if (this->episode == Episode::EP2) {
|
||||
this->category = QuestCategory::GOVERNMENT_EPISODE_2;
|
||||
} else if (this->episode == 2) {
|
||||
} else if (this->episode == Episode::EP4) {
|
||||
this->category = QuestCategory::GOVERNMENT_EPISODE_4;
|
||||
} else if (this->episode != 0) {
|
||||
} else if (this->episode != Episode::EP1) {
|
||||
throw invalid_argument("government quest has invalid episode number");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "StaticGameData.hh"
|
||||
#include "Version.hh"
|
||||
|
||||
|
||||
@@ -47,7 +48,7 @@ public:
|
||||
int64_t internal_id;
|
||||
uint32_t menu_item_id;
|
||||
QuestCategory category;
|
||||
uint8_t episode; // 0 = ep1, 1 = ep2, 2 = ep4, 0xFF = ep3
|
||||
Episode episode;
|
||||
bool is_dcv1;
|
||||
bool joinable;
|
||||
GameVersion version;
|
||||
|
||||
+18
-5
@@ -16,17 +16,30 @@ RareItemSet::RareItemSet(shared_ptr<const string> data) : data(data) {
|
||||
}
|
||||
|
||||
const RareItemSet::Table& RareItemSet::get_table(
|
||||
uint8_t episode, uint8_t difficulty, uint8_t secid) const {
|
||||
if (episode > 2) {
|
||||
throw logic_error("incorrect episode number");
|
||||
}
|
||||
Episode episode, uint8_t difficulty, uint8_t secid) const {
|
||||
if (difficulty > 3) {
|
||||
throw logic_error("incorrect difficulty");
|
||||
}
|
||||
if (secid > 10) {
|
||||
throw logic_error("incorrect section id");
|
||||
}
|
||||
return this->tables[(episode * 10 * 4) + (difficulty * 10) + secid];
|
||||
|
||||
size_t ep_index;
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
ep_index = 0;
|
||||
break;
|
||||
case Episode::EP2:
|
||||
ep_index = 1;
|
||||
break;
|
||||
case Episode::EP4:
|
||||
ep_index = 2;
|
||||
break;
|
||||
default:
|
||||
throw invalid_argument("incorrect episode");
|
||||
}
|
||||
|
||||
return this->tables[(ep_index * 10 * 4) + (difficulty * 10) + secid];
|
||||
}
|
||||
|
||||
bool RareItemSet::sample(mt19937& random, uint8_t pc) {
|
||||
|
||||
+3
-1
@@ -6,6 +6,8 @@
|
||||
#include <string>
|
||||
#include <random>
|
||||
|
||||
#include "StaticGameData.hh"
|
||||
|
||||
|
||||
|
||||
class RareItemSet {
|
||||
@@ -28,7 +30,7 @@ public:
|
||||
|
||||
RareItemSet(std::shared_ptr<const std::string> data);
|
||||
|
||||
const Table& get_table(uint8_t episode, uint8_t difficulty, uint8_t secid) const;
|
||||
const Table& get_table(Episode episode, uint8_t difficulty, uint8_t secid) const;
|
||||
|
||||
static bool sample(std::mt19937& rand, uint8_t probability);
|
||||
|
||||
|
||||
+76
-55
@@ -1079,10 +1079,10 @@ static bool start_ep3_battle_table_game_if_ready(
|
||||
// begin, but create_game_generic can still return null if an internal
|
||||
// precondition fails (though this should never happen for Episode 3 games).
|
||||
|
||||
uint32_t flags = Lobby::Flag::NON_V1_ONLY | Lobby::Flag::EPISODE_3_ONLY;
|
||||
uint32_t flags = Lobby::Flag::NON_V1_ONLY;
|
||||
u16string name = tourn ? decode_sjis(tourn->get_name()) : u"<BattleTable>";
|
||||
auto game = create_game_generic(
|
||||
s, game_clients.begin()->second, name, u"", 0xFF, 0, flags);
|
||||
s, game_clients.begin()->second, name, u"", Episode::EP3, 0, flags);
|
||||
if (!game) {
|
||||
return false;
|
||||
}
|
||||
@@ -1144,7 +1144,7 @@ static void on_E4_Ep3(shared_ptr<ServerState> s,
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
if (l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (l->is_game() || !l->is_ep3()) {
|
||||
throw runtime_error("battle table join command sent in non-CARD lobby");
|
||||
}
|
||||
c->card_battle_table_number = cmd.table_number;
|
||||
@@ -1180,7 +1180,7 @@ static void on_E5_Ep3(shared_ptr<ServerState> s,
|
||||
shared_ptr<Client> c, uint16_t, uint32_t flag, const string& data) {
|
||||
check_size_t<S_CardBattleTableConfirmation_GC_Ep3_E5>(data);
|
||||
auto l = s->find_lobby(c->lobby_id);
|
||||
if (l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (l->is_game() || !l->is_ep3()) {
|
||||
throw runtime_error("battle table command sent in non-CARD lobby");
|
||||
}
|
||||
|
||||
@@ -1271,7 +1271,7 @@ static void on_CA_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
// command when it's not in any lobby at all. We just ignore such commands.
|
||||
return;
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::EPISODE_3_ONLY) || !l->is_game()) {
|
||||
if (!l->is_game() || !l->is_ep3()) {
|
||||
throw runtime_error("Episode 3 server data request sent outside of Episode 3 game");
|
||||
}
|
||||
|
||||
@@ -1420,7 +1420,7 @@ static void on_09(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (l->is_game()) {
|
||||
num_games++;
|
||||
if (l->version == c->version() &&
|
||||
(!(l->flags & Lobby::Flag::EPISODE_3_ONLY) == !(c->flags & Client::Flag::IS_EPISODE_3))) {
|
||||
(!l->is_ep3() == !(c->flags & Client::Flag::IS_EPISODE_3))) {
|
||||
num_compatible_games++;
|
||||
}
|
||||
}
|
||||
@@ -1514,8 +1514,7 @@ static void on_09(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (!game->is_game()) {
|
||||
send_ship_info(c, u"$C4Incorrect game ID");
|
||||
|
||||
} else if ((c->flags & Client::Flag::IS_EPISODE_3) &&
|
||||
(game->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
} else if ((c->flags & Client::Flag::IS_EPISODE_3) && game->is_ep3()) {
|
||||
send_ep3_game_details(c, game);
|
||||
|
||||
} else {
|
||||
@@ -1525,7 +1524,7 @@ static void on_09(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (game_c.get()) {
|
||||
auto player = game_c->game_data.player();
|
||||
auto name = encode_sjis(player->disp.name);
|
||||
if (game->flags & Lobby::Flag::EPISODE_3_ONLY) {
|
||||
if (game->is_ep3()) {
|
||||
info += string_printf("%zu: $C6%s$C7 L%" PRIu32 "\n",
|
||||
x + 1, name.c_str(), player->disp.level + 1);
|
||||
} else {
|
||||
@@ -1537,12 +1536,6 @@ static void on_09(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
}
|
||||
}
|
||||
|
||||
int episode = game->episode;
|
||||
if (episode == 3) {
|
||||
episode = 4;
|
||||
} else if (episode == 0xFF) {
|
||||
episode = 3;
|
||||
}
|
||||
string secid_str = name_for_section_id(game->section_id);
|
||||
const char* mode_abbrev = "Nml";
|
||||
if (game->flags & Lobby::Flag::BATTLE_MODE) {
|
||||
@@ -1552,8 +1545,8 @@ static void on_09(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
} else if (game->flags & Lobby::Flag::SOLO_MODE) {
|
||||
mode_abbrev = "Solo";
|
||||
}
|
||||
info += string_printf("Ep%d %c %s %s\n",
|
||||
episode,
|
||||
info += string_printf("%s %c %s %s\n",
|
||||
abbreviation_for_episode(game->episode),
|
||||
abbreviation_for_difficulty(game->difficulty),
|
||||
mode_abbrev,
|
||||
secid_str.c_str());
|
||||
@@ -1904,7 +1897,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
break;
|
||||
}
|
||||
if ((game->version != c->version()) ||
|
||||
(!(game->flags & Lobby::Flag::EPISODE_3_ONLY) != !(c->flags & Client::Flag::IS_EPISODE_3)) ||
|
||||
(!game->is_ep3() != !(c->flags & Client::Flag::IS_EPISODE_3)) ||
|
||||
((game->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1))) {
|
||||
send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO.");
|
||||
break;
|
||||
@@ -1986,7 +1979,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
}
|
||||
}
|
||||
|
||||
bool is_ep3 = (q->episode == 0xFF);
|
||||
bool is_ep3 = (q->episode == Episode::EP3);
|
||||
string bin_basename = q->bin_filename();
|
||||
shared_ptr<const string> bin_contents = q->bin_contents();
|
||||
string dat_basename;
|
||||
@@ -2178,7 +2171,7 @@ static void on_84(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
return;
|
||||
}
|
||||
|
||||
if ((new_lobby->flags & Lobby::Flag::EPISODE_3_ONLY) && !(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (new_lobby->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
send_lobby_message_box(c, u"$C6Can't change lobby\n\n$C7The lobby is for\nEpisode 3 only.");
|
||||
return;
|
||||
}
|
||||
@@ -2573,7 +2566,7 @@ static void on_chat_generic(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
|
||||
char private_flags = 0;
|
||||
u16string processed_text;
|
||||
if ((text[0] != '\t') && (l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if ((text[0] != '\t') && l->is_ep3()) {
|
||||
private_flags = text[0];
|
||||
processed_text = remove_language_marker(text.substr(1));
|
||||
} else {
|
||||
@@ -3111,24 +3104,16 @@ shared_ptr<Lobby> create_game_generic(
|
||||
shared_ptr<Client> c,
|
||||
const std::u16string& name,
|
||||
const std::u16string& password,
|
||||
uint8_t episode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
uint32_t flags,
|
||||
shared_ptr<Lobby> watched_lobby,
|
||||
shared_ptr<Episode3::BattleRecordPlayer> battle_player) {
|
||||
|
||||
// A player's actual level is their displayed level - 1, so the minimums for
|
||||
// Episode 1 (for example) are actually 1, 20, 40, 80.
|
||||
static const uint32_t default_minimum_levels[3][4] = {
|
||||
{0, 19, 39, 79}, // Episode 1
|
||||
{0, 29, 49, 89}, // Episode 2
|
||||
{0, 39, 79, 109}}; // Episode 4
|
||||
|
||||
bool is_ep3 = (flags & Lobby::Flag::EPISODE_3_ONLY);
|
||||
if (episode == 0) {
|
||||
episode = 0xFF;
|
||||
}
|
||||
if (((episode != 0xFF) && (episode > 3)) || (episode == 0)) {
|
||||
if ((episode != Episode::EP1) &&
|
||||
(episode != Episode::EP2) &&
|
||||
(episode != Episode::EP3) &&
|
||||
(episode != Episode::EP4)) {
|
||||
throw invalid_argument("incorrect episode number");
|
||||
}
|
||||
|
||||
@@ -3141,7 +3126,32 @@ shared_ptr<Lobby> create_game_generic(
|
||||
throw invalid_argument("cannot make a game from outside any lobby");
|
||||
}
|
||||
|
||||
uint8_t min_level = ((episode == 0xFF) ? 0 : default_minimum_levels[episode - 1][difficulty]);
|
||||
uint8_t min_level;
|
||||
// A player's actual level is their displayed level - 1, so the minimums for
|
||||
// Episode 1 (for example) are actually 1, 20, 40, 80.
|
||||
switch (episode) {
|
||||
case Episode::EP1: {
|
||||
static const uint32_t min_levels[4] = {0, 19, 39, 79};
|
||||
min_level = min_levels[difficulty];
|
||||
break;
|
||||
}
|
||||
case Episode::EP2: {
|
||||
static const uint32_t min_levels[4] = {0, 29, 49, 89};
|
||||
min_level = min_levels[difficulty];
|
||||
break;
|
||||
}
|
||||
case Episode::EP3:
|
||||
min_level = 0;
|
||||
break;
|
||||
case Episode::EP4: {
|
||||
static const uint32_t min_levels[4] = {0, 39, 79, 109};
|
||||
min_level = min_levels[difficulty];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw runtime_error("invalid episode");
|
||||
}
|
||||
|
||||
if (!(c->license->privileges & Privilege::FREE_JOIN_GAMES) &&
|
||||
(min_level > c->game_data.player()->disp.level)) {
|
||||
// Note: We don't throw here because this is a situation players might
|
||||
@@ -3163,7 +3173,6 @@ shared_ptr<Lobby> create_game_generic(
|
||||
game->name = name;
|
||||
game->flags = flags |
|
||||
Lobby::Flag::GAME |
|
||||
(is_ep3 ? Lobby::Flag::EPISODE_3_ONLY : 0) |
|
||||
(item_tracking_enabled ? Lobby::Flag::ITEM_TRACKING_ENABLED : 0);
|
||||
game->password = password;
|
||||
game->version = c->version();
|
||||
@@ -3194,7 +3203,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
bool is_solo = (game->flags & Lobby::Flag::SOLO_MODE);
|
||||
|
||||
// Generate the map variations
|
||||
if (is_ep3) {
|
||||
if (game->is_ep3()) {
|
||||
game->variations.clear(0);
|
||||
} else {
|
||||
generate_variations(game->variations, game->random, game->episode, is_solo);
|
||||
@@ -3268,7 +3277,8 @@ static void on_C1_PC(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (cmd.challenge_mode) {
|
||||
flags |= Lobby::Flag::CHALLENGE_MODE;
|
||||
}
|
||||
auto game = create_game_generic(s, c, cmd.name, cmd.password, 1, cmd.difficulty, flags);
|
||||
auto game = create_game_generic(
|
||||
s, c, cmd.name, cmd.password, Episode::EP1, cmd.difficulty, flags);
|
||||
if (game) {
|
||||
s->change_client_lobby(c, game);
|
||||
c->flags |= Client::Flag::LOADING;
|
||||
@@ -3282,21 +3292,22 @@ static void on_0C_C1_E7_EC(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
// Only allow E7/EC from Ep3 clients
|
||||
bool client_is_ep3 = !!(c->flags & Client::Flag::IS_EPISODE_3);
|
||||
if (((command & 0xF0) == 0xE0) != client_is_ep3) {
|
||||
return;
|
||||
throw runtime_error("invalid command");
|
||||
}
|
||||
|
||||
uint8_t episode = cmd.episode;
|
||||
Episode episode = Episode::NONE;
|
||||
uint32_t flags = 0;
|
||||
if (c->version() == GameVersion::DC) {
|
||||
if (episode) {
|
||||
if (cmd.episode) {
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
}
|
||||
episode = 1;
|
||||
episode = Episode::EP1;
|
||||
} else if (client_is_ep3) {
|
||||
flags |= (Lobby::Flag::NON_V1_ONLY | Lobby::Flag::EPISODE_3_ONLY);
|
||||
episode = 0xFF;
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
episode = Episode::EP3;
|
||||
} else { // XB/GC non-Ep3
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
episode = cmd.episode == 2 ? Episode::EP2 : Episode::EP1;
|
||||
}
|
||||
|
||||
u16string name = decode_sjis(cmd.name);
|
||||
@@ -3348,8 +3359,24 @@ static void on_C1_BB(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (cmd.solo_mode) {
|
||||
flags |= Lobby::Flag::SOLO_MODE;
|
||||
}
|
||||
|
||||
Episode episode;
|
||||
switch (cmd.episode) {
|
||||
case 1:
|
||||
episode = Episode::EP1;
|
||||
break;
|
||||
case 2:
|
||||
episode = Episode::EP2;
|
||||
break;
|
||||
case 3:
|
||||
episode = Episode::EP4;
|
||||
break;
|
||||
default:
|
||||
throw runtime_error("invalid episode number");
|
||||
}
|
||||
|
||||
auto game = create_game_generic(
|
||||
s, c, cmd.name, cmd.password, cmd.episode, cmd.difficulty, flags);
|
||||
s, c, cmd.name, cmd.password, episode, cmd.difficulty, flags);
|
||||
if (game) {
|
||||
s->change_client_lobby(c, game);
|
||||
c->flags |= Client::Flag::LOADING;
|
||||
@@ -3518,11 +3545,8 @@ static void on_EE_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
throw runtime_error("non-Ep3 client sent card trade command");
|
||||
}
|
||||
auto l = s->find_lobby(c->lobby_id);
|
||||
if (!(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
throw runtime_error("client sent card trade command outside of Ep3 lobby");
|
||||
}
|
||||
if (!l->is_game()) {
|
||||
throw runtime_error("client sent card trade command in non-game lobby");
|
||||
if (!l->is_game() || !l->is_ep3()) {
|
||||
throw runtime_error("client sent card trade command outside of Ep3 game");
|
||||
}
|
||||
|
||||
if (flag == 0xD0) {
|
||||
@@ -3626,11 +3650,8 @@ static void on_EF_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
throw runtime_error("non-Ep3 client sent card auction join command");
|
||||
}
|
||||
auto l = s->find_lobby(c->lobby_id);
|
||||
if (!(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
throw runtime_error("client sent card auction join command outside of Ep3 lobby");
|
||||
}
|
||||
if (!l->is_game()) {
|
||||
throw runtime_error("client sent card auction join command in non-game lobby");
|
||||
if (!l->is_game() || !l->is_ep3()) {
|
||||
throw runtime_error("client sent card auction join command outside of Ep3 game");
|
||||
}
|
||||
|
||||
if (c->flags & Client::Flag::AWAITING_CARD_AUCTION) {
|
||||
|
||||
@@ -11,7 +11,7 @@ std::shared_ptr<Lobby> create_game_generic(
|
||||
std::shared_ptr<Client> c,
|
||||
const std::u16string& name,
|
||||
const std::u16string& password,
|
||||
uint8_t episode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
uint32_t flags,
|
||||
std::shared_ptr<Lobby> watched_lobby = nullptr,
|
||||
|
||||
+11
-10
@@ -213,7 +213,7 @@ static void on_forward_check_size_ep3_lobby(shared_ptr<ServerState>,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const string& data) {
|
||||
check_size_sc<G_UnusedHeader>(data, sizeof(G_UnusedHeader), 0xFFFF);
|
||||
if (l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (l->is_game() || !l->is_ep3()) {
|
||||
return;
|
||||
}
|
||||
forward_subcommand(l, c, command, flag, data);
|
||||
@@ -223,7 +223,7 @@ static void on_forward_check_size_ep3_game(shared_ptr<ServerState>,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const string& data) {
|
||||
check_size_sc<G_UnusedHeader>(data, sizeof(G_UnusedHeader), 0xFFFF);
|
||||
if (!l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (!l->is_game() || !l->is_ep3()) {
|
||||
return;
|
||||
}
|
||||
forward_subcommand(l, c, command, flag, data);
|
||||
@@ -239,7 +239,7 @@ static void on_ep3_battle_subs(shared_ptr<ServerState> s,
|
||||
const string& orig_data) {
|
||||
const auto& header = check_size_sc<G_CardBattleCommandHeader>(
|
||||
orig_data, sizeof(G_CardBattleCommandHeader), 0xFFFF);
|
||||
if (!l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (!l->is_game() || !l->is_ep3()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -822,7 +822,7 @@ static void on_use_item(shared_ptr<ServerState>,
|
||||
static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const string& data) {
|
||||
if (l->flags & Lobby::Flag::EPISODE_3_ONLY) {
|
||||
if (l->is_ep3()) {
|
||||
on_ep3_battle_subs(s, l, c, command, flag, data);
|
||||
|
||||
} else if (!l->common_item_creator.get()) {
|
||||
@@ -858,7 +858,7 @@ static void on_open_bank_bb_or_card_trade_counter_ep3(shared_ptr<ServerState>,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag, const string& data) {
|
||||
if ((l->version == GameVersion::BB) && l->is_game()) {
|
||||
send_bank(c);
|
||||
} else if (l->version == GameVersion::GC && l->flags & Lobby::Flag::EPISODE_3_ONLY) {
|
||||
} else if ((l->version == GameVersion::GC) && l->is_ep3()) {
|
||||
forward_subcommand(l, c, command, flag, data);
|
||||
}
|
||||
}
|
||||
@@ -965,7 +965,7 @@ static bool drop_item(
|
||||
const RareItemSet::Table::Drop* drop = nullptr;
|
||||
if (s->rare_item_set) {
|
||||
const auto& table = s->rare_item_set->get_table(
|
||||
l->episode - 1, l->difficulty, l->section_id);
|
||||
l->episode, l->difficulty, l->section_id);
|
||||
if (enemy_id < 0) {
|
||||
for (size_t z = 0; z < 30; z++) {
|
||||
if (table.box_areas[z] != area) {
|
||||
@@ -1060,8 +1060,9 @@ static void on_phase_setup(shared_ptr<ServerState>,
|
||||
forward_subcommand(l, c, command, flag, data);
|
||||
|
||||
bool should_send_boss_drop_req = false;
|
||||
bool is_ep2 = (l->episode == Episode::EP2);
|
||||
if (cmd.difficulty == l->difficulty) {
|
||||
if ((l->episode == 1) && (c->area == 0x0E)) {
|
||||
if ((l->episode == Episode::EP1) && (c->area == 0x0E)) {
|
||||
// On Normal, Dark Falz does not have a third phase, so send the drop
|
||||
// request after the end of the second phase. On all other difficulty
|
||||
// levels, send it after the third phase.
|
||||
@@ -1069,7 +1070,7 @@ static void on_phase_setup(shared_ptr<ServerState>,
|
||||
((l->difficulty != 0) && (cmd.basic_cmd.phase == 0x00000037))) {
|
||||
should_send_boss_drop_req = true;
|
||||
}
|
||||
} else if ((l->episode == 2) && (cmd.basic_cmd.phase == 0x00000057) && (c->area == 0x0D)) {
|
||||
} else if (is_ep2 && (cmd.basic_cmd.phase == 0x00000057) && (c->area == 0x0D)) {
|
||||
should_send_boss_drop_req = true;
|
||||
}
|
||||
}
|
||||
@@ -1081,9 +1082,9 @@ static void on_phase_setup(shared_ptr<ServerState>,
|
||||
{
|
||||
{0x60, 0x06, 0x0000},
|
||||
static_cast<uint8_t>(c->area),
|
||||
static_cast<uint8_t>((l->episode == 2) ? 0x4E : 0x2F),
|
||||
static_cast<uint8_t>(is_ep2 ? 0x4E : 0x2F),
|
||||
0x0B4F,
|
||||
(l->episode == 2) ? -9999.0f : 10160.58984375f,
|
||||
is_ep2 ? -9999.0f : 10160.58984375f,
|
||||
0.0f,
|
||||
2,
|
||||
0,
|
||||
|
||||
+42
-9
@@ -894,7 +894,7 @@ void send_card_search_result_t(
|
||||
string encoded_lobby_name = encode_sjis(result_lobby->name);
|
||||
location_string = string_printf("%s,BLOCK01,%s",
|
||||
encoded_lobby_name.c_str(), encoded_server_name.c_str());
|
||||
} else if (result_lobby->flags & Lobby::Flag::EPISODE_3_ONLY) {
|
||||
} else if (result_lobby->is_ep3()) {
|
||||
location_string = string_printf("BLOCK01-C%02" PRIu32 ",BLOCK01,%s",
|
||||
result_lobby->lobby_id - 15, encoded_server_name.c_str());
|
||||
} else {
|
||||
@@ -1126,7 +1126,7 @@ void send_game_menu_t(
|
||||
continue;
|
||||
}
|
||||
|
||||
bool l_is_ep3 = !!(l->flags & Lobby::Flag::EPISODE_3_ONLY);
|
||||
bool l_is_ep3 = l->is_ep3();
|
||||
bool c_is_ep3 = !!(c->flags & Client::Flag::IS_EPISODE_3);
|
||||
if (l_is_ep3 != c_is_ep3) {
|
||||
continue;
|
||||
@@ -1143,6 +1143,24 @@ void send_game_menu_t(
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t episode_num;
|
||||
switch (l->episode) {
|
||||
case Episode::EP1:
|
||||
episode_num = 1;
|
||||
break;
|
||||
case Episode::EP2:
|
||||
episode_num = 2;
|
||||
break;
|
||||
case Episode::EP3:
|
||||
episode_num = 0;
|
||||
break;
|
||||
case Episode::EP4:
|
||||
episode_num = 3;
|
||||
break;
|
||||
default:
|
||||
throw runtime_error("lobby has incorrect episode number");
|
||||
}
|
||||
|
||||
auto& e = entries.emplace_back();
|
||||
e.menu_id = MenuID::GAME;
|
||||
e.game_id = l->lobby_id;
|
||||
@@ -1151,12 +1169,12 @@ void send_game_menu_t(
|
||||
if (c->version() == GameVersion::DC) {
|
||||
e.episode = (l->flags & Lobby::Flag::NON_V1_ONLY) ? 1 : 0;
|
||||
} else {
|
||||
e.episode = ((c->version() == GameVersion::BB) ? (l->max_clients << 4) : 0) | l->episode;
|
||||
e.episode = ((c->version() == GameVersion::BB) ? (l->max_clients << 4) : 0) | episode_num;
|
||||
}
|
||||
if (l->flags & Lobby::Flag::EPISODE_3_ONLY) {
|
||||
if (l->is_ep3()) {
|
||||
e.flags = (l->password.empty() ? 0 : 2) | ((l->flags & Lobby::Flag::BATTLE_IN_PROGRESS) ? 4 : 0);
|
||||
} else {
|
||||
e.flags = ((l->episode << 6) | (l->password.empty() ? 0 : 2));
|
||||
e.flags = ((episode_num << 6) | (l->password.empty() ? 0 : 2));
|
||||
if (l->flags & Lobby::Flag::BATTLE_MODE) {
|
||||
e.flags |= 0x10;
|
||||
}
|
||||
@@ -1280,7 +1298,7 @@ void send_lobby_list(shared_ptr<Client> c, shared_ptr<ServerState> s) {
|
||||
if ((l->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1)) {
|
||||
continue;
|
||||
}
|
||||
if ((l->flags & Lobby::Flag::EPISODE_3_ONLY) && !(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (l->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
continue;
|
||||
}
|
||||
auto& e = entries.emplace_back();
|
||||
@@ -1301,7 +1319,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
throw runtime_error("lobby is not Episode 3");
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (!l->is_ep3()) {
|
||||
throw runtime_error("lobby is not Episode 3");
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
@@ -1405,7 +1423,7 @@ void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_ep3 = (l->flags & Lobby::Flag::EPISODE_3_ONLY);
|
||||
bool is_ep3 = l->is_ep3();
|
||||
string data(is_ep3 ? sizeof(S_JoinGame_GC_Ep3_64) : sizeof(S_JoinGame<LobbyDataT, DispDataT>), '\0');
|
||||
|
||||
// TODO: This is a terrible way to handle the different Ep3 format within the
|
||||
@@ -1448,7 +1466,22 @@ void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
cmd->section_id = l->section_id;
|
||||
cmd->challenge_mode = (l->flags & Lobby::Flag::CHALLENGE_MODE) ? 1 : 0;
|
||||
cmd->rare_seed = l->random_seed;
|
||||
cmd->episode = l->episode;
|
||||
switch (l->episode) {
|
||||
case Episode::EP1:
|
||||
cmd->episode = 1;
|
||||
break;
|
||||
case Episode::EP2:
|
||||
cmd->episode = 2;
|
||||
break;
|
||||
case Episode::EP3:
|
||||
cmd->episode = 0xFF;
|
||||
break;
|
||||
case Episode::EP4:
|
||||
cmd->episode = 3;
|
||||
break;
|
||||
default:
|
||||
throw logic_error("invalid episode number in game");
|
||||
}
|
||||
cmd->unused2 = 0x01;
|
||||
cmd->solo_mode = (l->flags & Lobby::Flag::SOLO_MODE) ? 1 : 0;
|
||||
cmd->unused3 = 0x00;
|
||||
|
||||
+5
-3
@@ -49,12 +49,14 @@ ServerState::ServerState()
|
||||
Lobby::Flag::PUBLIC |
|
||||
Lobby::Flag::DEFAULT |
|
||||
Lobby::Flag::PERSISTENT |
|
||||
(is_non_v1_only ? Lobby::Flag::NON_V1_ONLY : 0) |
|
||||
(is_ep3_only ? Lobby::Flag::EPISODE_3_ONLY : 0);
|
||||
(is_non_v1_only ? Lobby::Flag::NON_V1_ONLY : 0);
|
||||
l->block = x + 1;
|
||||
l->type = x;
|
||||
l->name = lobby_name;
|
||||
l->max_clients = 12;
|
||||
if (is_ep3_only) {
|
||||
l->episode = Episode::EP3;
|
||||
}
|
||||
|
||||
if (!is_non_v1_only) {
|
||||
this->public_lobby_search_order_v1.emplace_back(l);
|
||||
@@ -221,7 +223,7 @@ void ServerState::remove_lobby(uint32_t lobby_id) {
|
||||
} else {
|
||||
// Tell all players in all spectator teams to go back to the lobby
|
||||
for (auto watcher_l : l->watcher_lobbies) {
|
||||
if (!(watcher_l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||
if (!watcher_l->is_ep3()) {
|
||||
throw logic_error("spectator team is not an Episode 3 lobby");
|
||||
}
|
||||
l->log.info("Disbanding watcher lobby %" PRIX32, watcher_l->lobby_id);
|
||||
|
||||
@@ -6,6 +6,60 @@ using namespace std;
|
||||
|
||||
|
||||
|
||||
size_t area_limit_for_episode(Episode ep) {
|
||||
switch (ep) {
|
||||
case Episode::EP1:
|
||||
case Episode::EP2:
|
||||
return 17;
|
||||
break;
|
||||
case Episode::EP4:
|
||||
return 10;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool episode_has_arpg_semantics(Episode ep) {
|
||||
return (ep == Episode::EP1) || (ep == Episode::EP2) || (ep == Episode::EP4);
|
||||
}
|
||||
|
||||
const char* name_for_episode(Episode ep) {
|
||||
switch (ep) {
|
||||
case Episode::NONE:
|
||||
return "No episode";
|
||||
case Episode::EP1:
|
||||
return "Episode 1";
|
||||
case Episode::EP2:
|
||||
return "Episode 2";
|
||||
case Episode::EP3:
|
||||
return "Episode 3";
|
||||
case Episode::EP4:
|
||||
return "Episode 4";
|
||||
default:
|
||||
return "Unknown episode";
|
||||
}
|
||||
}
|
||||
|
||||
const char* abbreviation_for_episode(Episode ep) {
|
||||
switch (ep) {
|
||||
case Episode::NONE:
|
||||
return "None";
|
||||
case Episode::EP1:
|
||||
return "Ep1";
|
||||
case Episode::EP2:
|
||||
return "Ep2";
|
||||
case Episode::EP3:
|
||||
return "Ep3";
|
||||
case Episode::EP4:
|
||||
return "Ep4";
|
||||
default:
|
||||
return "UnkEp";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const vector<string> section_id_to_name({
|
||||
"Viridia", "Greennill", "Skyly", "Bluefull", "Purplenum", "Pinkal", "Redria",
|
||||
"Oran", "Yellowboze", "Whitill"});
|
||||
|
||||
@@ -9,6 +9,21 @@
|
||||
|
||||
|
||||
|
||||
enum class Episode {
|
||||
NONE = 0,
|
||||
EP1 = 1,
|
||||
EP2 = 2,
|
||||
EP3 = 3,
|
||||
EP4 = 4,
|
||||
};
|
||||
|
||||
size_t area_limit_for_episode(Episode ep);
|
||||
bool episode_has_arpg_semantics(Episode ep);
|
||||
const char* name_for_episode(Episode ep);
|
||||
const char* abbreviation_for_episode(Episode ep);
|
||||
|
||||
|
||||
|
||||
size_t stack_size_for_item(uint8_t data0, uint8_t data1);
|
||||
size_t stack_size_for_item(const ItemData& item);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user