replace UnlockAllAreas and PreventPersistQuestFlags with generalized rewrite map
This commit is contained in:
+1
-1
@@ -114,7 +114,7 @@ set(SOURCES
|
||||
src/PSOGCObjectGraph.cc
|
||||
src/PSOProtocol.cc
|
||||
src/Quest.cc
|
||||
src/QuestAvailabilityExpression.cc
|
||||
src/IntegralExpression.cc
|
||||
src/QuestScript.cc
|
||||
src/RareItemSet.cc
|
||||
src/ReceiveCommands.cc
|
||||
|
||||
+3
-3
@@ -350,7 +350,7 @@ shared_ptr<const TeamIndex::Team> Client::team() const {
|
||||
}
|
||||
|
||||
bool Client::evaluate_quest_availability_expression(
|
||||
shared_ptr<const QuestAvailabilityExpression> expr,
|
||||
shared_ptr<const IntegralExpression> expr,
|
||||
shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
@@ -366,8 +366,8 @@ bool Client::evaluate_quest_availability_expression(
|
||||
throw logic_error("quest flags are missing from game");
|
||||
}
|
||||
auto p = this->character();
|
||||
QuestAvailabilityExpression::Env env = {
|
||||
.flags = (game && !game->quest_flags_known) ? &game->quest_flag_values->data.at(difficulty) : &p->quest_flags.data.at(difficulty),
|
||||
IntegralExpression::Env env = {
|
||||
.flags = &p->quest_flags.data.at(difficulty),
|
||||
.challenge_records = &p->challenge_records,
|
||||
.team = this->team(),
|
||||
.num_players = num_players,
|
||||
|
||||
+1
-1
@@ -294,7 +294,7 @@ public:
|
||||
std::shared_ptr<const TeamIndex::Team> team() const;
|
||||
|
||||
bool evaluate_quest_availability_expression(
|
||||
std::shared_ptr<const QuestAvailabilityExpression> expr,
|
||||
std::shared_ptr<const IntegralExpression> expr,
|
||||
std::shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "QuestAvailabilityExpression.hh"
|
||||
#include "IntegralExpression.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
@@ -21,16 +21,16 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
QuestAvailabilityExpression::QuestAvailabilityExpression(const string& text)
|
||||
IntegralExpression::IntegralExpression(const string& text)
|
||||
: root(this->parse_expr(text)) {}
|
||||
|
||||
QuestAvailabilityExpression::BinaryOperatorNode::BinaryOperatorNode(
|
||||
IntegralExpression::BinaryOperatorNode::BinaryOperatorNode(
|
||||
Type type, unique_ptr<const Node>&& left, unique_ptr<const Node>&& right)
|
||||
: type(type),
|
||||
left(std::move(left)),
|
||||
right(std::move(right)) {}
|
||||
|
||||
bool QuestAvailabilityExpression::BinaryOperatorNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::BinaryOperatorNode::operator==(const Node& other) const {
|
||||
try {
|
||||
const BinaryOperatorNode& other_bin = dynamic_cast<const BinaryOperatorNode&>(other);
|
||||
return other_bin.type == this->type && *other_bin.left == *this->left && *other_bin.right == *this->right;
|
||||
@@ -39,7 +39,7 @@ bool QuestAvailabilityExpression::BinaryOperatorNode::operator==(const Node& oth
|
||||
}
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::BinaryOperatorNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::BinaryOperatorNode::evaluate(const Env& env) const {
|
||||
switch (this->type) {
|
||||
case Type::LOGICAL_OR:
|
||||
return this->left->evaluate(env) || this->right->evaluate(env);
|
||||
@@ -82,7 +82,7 @@ int64_t QuestAvailabilityExpression::BinaryOperatorNode::evaluate(const Env& env
|
||||
}
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::BinaryOperatorNode::str() const {
|
||||
string IntegralExpression::BinaryOperatorNode::str() const {
|
||||
switch (this->type) {
|
||||
case Type::LOGICAL_OR:
|
||||
return "(" + this->left->str() + ") || (" + this->right->str() + ")";
|
||||
@@ -125,11 +125,11 @@ string QuestAvailabilityExpression::BinaryOperatorNode::str() const {
|
||||
}
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::UnaryOperatorNode::UnaryOperatorNode(Type type, unique_ptr<const Node>&& sub)
|
||||
IntegralExpression::UnaryOperatorNode::UnaryOperatorNode(Type type, unique_ptr<const Node>&& sub)
|
||||
: type(type),
|
||||
sub(std::move(sub)) {}
|
||||
|
||||
bool QuestAvailabilityExpression::UnaryOperatorNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::UnaryOperatorNode::operator==(const Node& other) const {
|
||||
try {
|
||||
const UnaryOperatorNode& other_un = dynamic_cast<const UnaryOperatorNode&>(other);
|
||||
return other_un.type == this->type && *other_un.sub == *this->sub;
|
||||
@@ -138,7 +138,7 @@ bool QuestAvailabilityExpression::UnaryOperatorNode::operator==(const Node& othe
|
||||
}
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::UnaryOperatorNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::UnaryOperatorNode::evaluate(const Env& env) const {
|
||||
switch (this->type) {
|
||||
case Type::LOGICAL_NOT:
|
||||
return !this->sub->evaluate(env);
|
||||
@@ -151,7 +151,7 @@ int64_t QuestAvailabilityExpression::UnaryOperatorNode::evaluate(const Env& env)
|
||||
}
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::UnaryOperatorNode::str() const {
|
||||
string IntegralExpression::UnaryOperatorNode::str() const {
|
||||
switch (this->type) {
|
||||
case Type::LOGICAL_NOT:
|
||||
return "!(" + this->sub->str() + ")";
|
||||
@@ -164,10 +164,10 @@ string QuestAvailabilityExpression::UnaryOperatorNode::str() const {
|
||||
}
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::FlagLookupNode::FlagLookupNode(uint16_t flag_index)
|
||||
IntegralExpression::FlagLookupNode::FlagLookupNode(uint16_t flag_index)
|
||||
: flag_index(flag_index) {}
|
||||
|
||||
bool QuestAvailabilityExpression::FlagLookupNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::FlagLookupNode::operator==(const Node& other) const {
|
||||
try {
|
||||
const FlagLookupNode& other_flag = dynamic_cast<const FlagLookupNode&>(other);
|
||||
return other_flag.flag_index == this->flag_index;
|
||||
@@ -176,20 +176,23 @@ bool QuestAvailabilityExpression::FlagLookupNode::operator==(const Node& other)
|
||||
}
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::FlagLookupNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::FlagLookupNode::evaluate(const Env& env) const {
|
||||
if (!env.flags) {
|
||||
throw runtime_error("quest flags not available");
|
||||
}
|
||||
return env.flags->get(this->flag_index) ? 1 : 0;
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::FlagLookupNode::str() const {
|
||||
string IntegralExpression::FlagLookupNode::str() const {
|
||||
return string_printf("F_%04hX", this->flag_index);
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::ChallengeCompletionLookupNode::ChallengeCompletionLookupNode(
|
||||
IntegralExpression::ChallengeCompletionLookupNode::ChallengeCompletionLookupNode(
|
||||
Episode episode, uint8_t stage_index)
|
||||
: episode(episode),
|
||||
stage_index(stage_index) {}
|
||||
|
||||
bool QuestAvailabilityExpression::ChallengeCompletionLookupNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::ChallengeCompletionLookupNode::operator==(const Node& other) const {
|
||||
try {
|
||||
const ChallengeCompletionLookupNode& other_cc = dynamic_cast<const ChallengeCompletionLookupNode&>(other);
|
||||
return other_cc.episode == this->episode && other_cc.stage_index == this->stage_index;
|
||||
@@ -198,7 +201,10 @@ bool QuestAvailabilityExpression::ChallengeCompletionLookupNode::operator==(cons
|
||||
}
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::ChallengeCompletionLookupNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::ChallengeCompletionLookupNode::evaluate(const Env& env) const {
|
||||
if (!env.challenge_records) {
|
||||
throw runtime_error("challenge records not available");
|
||||
}
|
||||
if (this->episode == Episode::EP1) {
|
||||
return env.challenge_records->times_ep1_online.at(this->stage_index).has_value();
|
||||
} else if (this->episode == Episode::EP2) {
|
||||
@@ -207,14 +213,14 @@ int64_t QuestAvailabilityExpression::ChallengeCompletionLookupNode::evaluate(con
|
||||
return false;
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::ChallengeCompletionLookupNode::str() const {
|
||||
string IntegralExpression::ChallengeCompletionLookupNode::str() const {
|
||||
return string_printf("CC_%s_%hhu", abbreviation_for_episode(this->episode), static_cast<uint8_t>(this->stage_index + 1));
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::TeamRewardLookupNode::TeamRewardLookupNode(const string& reward_name)
|
||||
IntegralExpression::TeamRewardLookupNode::TeamRewardLookupNode(const string& reward_name)
|
||||
: reward_name(reward_name) {}
|
||||
|
||||
bool QuestAvailabilityExpression::TeamRewardLookupNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::TeamRewardLookupNode::operator==(const Node& other) const {
|
||||
try {
|
||||
const TeamRewardLookupNode& other_team_reward = dynamic_cast<const TeamRewardLookupNode&>(other);
|
||||
return other_team_reward.reward_name == this->reward_name;
|
||||
@@ -223,60 +229,60 @@ bool QuestAvailabilityExpression::TeamRewardLookupNode::operator==(const Node& o
|
||||
}
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::TeamRewardLookupNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::TeamRewardLookupNode::evaluate(const Env& env) const {
|
||||
return (env.team && env.team->has_reward(this->reward_name)) ? 1 : 0;
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::TeamRewardLookupNode::str() const {
|
||||
string IntegralExpression::TeamRewardLookupNode::str() const {
|
||||
return "T_" + this->reward_name;
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::NumPlayersLookupNode::NumPlayersLookupNode() {}
|
||||
IntegralExpression::NumPlayersLookupNode::NumPlayersLookupNode() {}
|
||||
|
||||
bool QuestAvailabilityExpression::NumPlayersLookupNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::NumPlayersLookupNode::operator==(const Node& other) const {
|
||||
return dynamic_cast<const NumPlayersLookupNode*>(&other) != nullptr;
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::NumPlayersLookupNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::NumPlayersLookupNode::evaluate(const Env& env) const {
|
||||
return env.num_players;
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::NumPlayersLookupNode::str() const {
|
||||
string IntegralExpression::NumPlayersLookupNode::str() const {
|
||||
return "V_NumPlayers";
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::EventLookupNode::EventLookupNode() {}
|
||||
IntegralExpression::EventLookupNode::EventLookupNode() {}
|
||||
|
||||
bool QuestAvailabilityExpression::EventLookupNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::EventLookupNode::operator==(const Node& other) const {
|
||||
return dynamic_cast<const EventLookupNode*>(&other) != nullptr;
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::EventLookupNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::EventLookupNode::evaluate(const Env& env) const {
|
||||
return env.event;
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::EventLookupNode::str() const {
|
||||
string IntegralExpression::EventLookupNode::str() const {
|
||||
return "V_Event";
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::V1PresenceLookupNode::V1PresenceLookupNode() {}
|
||||
IntegralExpression::V1PresenceLookupNode::V1PresenceLookupNode() {}
|
||||
|
||||
bool QuestAvailabilityExpression::V1PresenceLookupNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::V1PresenceLookupNode::operator==(const Node& other) const {
|
||||
return dynamic_cast<const V1PresenceLookupNode*>(&other) != nullptr;
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::V1PresenceLookupNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::V1PresenceLookupNode::evaluate(const Env& env) const {
|
||||
return env.v1_present ? 1 : 0;
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::V1PresenceLookupNode::str() const {
|
||||
string IntegralExpression::V1PresenceLookupNode::str() const {
|
||||
return "V_V1Present";
|
||||
}
|
||||
|
||||
QuestAvailabilityExpression::ConstantNode::ConstantNode(int64_t value)
|
||||
IntegralExpression::ConstantNode::ConstantNode(int64_t value)
|
||||
: value(value) {}
|
||||
|
||||
bool QuestAvailabilityExpression::ConstantNode::operator==(const Node& other) const {
|
||||
bool IntegralExpression::ConstantNode::operator==(const Node& other) const {
|
||||
try {
|
||||
const ConstantNode& other_const = dynamic_cast<const ConstantNode&>(other);
|
||||
return other_const.value == this->value;
|
||||
@@ -285,15 +291,15 @@ bool QuestAvailabilityExpression::ConstantNode::operator==(const Node& other) co
|
||||
}
|
||||
}
|
||||
|
||||
int64_t QuestAvailabilityExpression::ConstantNode::evaluate(const Env&) const {
|
||||
int64_t IntegralExpression::ConstantNode::evaluate(const Env&) const {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
string QuestAvailabilityExpression::ConstantNode::str() const {
|
||||
string IntegralExpression::ConstantNode::str() const {
|
||||
return string_printf("%" PRId64, this->value);
|
||||
}
|
||||
|
||||
unique_ptr<const QuestAvailabilityExpression::Node> QuestAvailabilityExpression::parse_expr(string_view text) {
|
||||
unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string_view text) {
|
||||
// Strip off spaces and fully-enclosing parentheses
|
||||
for (;;) {
|
||||
size_t starting_size = text.size();
|
||||
@@ -334,13 +340,13 @@ unique_ptr<const QuestAvailabilityExpression::Node> QuestAvailabilityExpression:
|
||||
// Check for unary operators
|
||||
if (text[0] == '!') {
|
||||
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::LOGICAL_NOT,
|
||||
QuestAvailabilityExpression::parse_expr(text.substr(1)));
|
||||
IntegralExpression::parse_expr(text.substr(1)));
|
||||
} else if (text[0] == '~') {
|
||||
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::BITWISE_NOT,
|
||||
QuestAvailabilityExpression::parse_expr(text.substr(1)));
|
||||
IntegralExpression::parse_expr(text.substr(1)));
|
||||
} else if (text[0] == '-') {
|
||||
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::NEGATIVE,
|
||||
QuestAvailabilityExpression::parse_expr(text.substr(1)));
|
||||
IntegralExpression::parse_expr(text.substr(1)));
|
||||
}
|
||||
|
||||
// Check for binary operators at the root level
|
||||
@@ -377,8 +383,8 @@ unique_ptr<const QuestAvailabilityExpression::Node> QuestAvailabilityExpression:
|
||||
((z < oper.first.size()) || (text.compare(z - oper.first.size(), oper.first.size(), oper.first) != 0)) &&
|
||||
(text.compare(z, oper.first.size(), oper.first) == 0) &&
|
||||
(text.compare(z + oper.first.size(), oper.first.size(), oper.first) != 0)) {
|
||||
auto left = QuestAvailabilityExpression::parse_expr(text.substr(0, z));
|
||||
auto right = QuestAvailabilityExpression::parse_expr(text.substr(z + oper.first.size()));
|
||||
auto left = IntegralExpression::parse_expr(text.substr(0, z));
|
||||
auto right = IntegralExpression::parse_expr(text.substr(z + oper.first.size()));
|
||||
return make_unique<BinaryOperatorNode>(oper.second, std::move(left), std::move(right));
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "StaticGameData.hh"
|
||||
#include "TeamIndex.hh"
|
||||
|
||||
class QuestAvailabilityExpression {
|
||||
class IntegralExpression {
|
||||
public:
|
||||
struct Env {
|
||||
const QuestFlagsForDifficulty* flags;
|
||||
@@ -24,12 +24,12 @@ public:
|
||||
bool v1_present;
|
||||
};
|
||||
|
||||
QuestAvailabilityExpression(const std::string& text);
|
||||
~QuestAvailabilityExpression() = default;
|
||||
inline bool operator==(const QuestAvailabilityExpression& other) const {
|
||||
IntegralExpression(const std::string& text);
|
||||
~IntegralExpression() = default;
|
||||
inline bool operator==(const IntegralExpression& other) const {
|
||||
return this->root->operator==(*other.root);
|
||||
}
|
||||
inline bool operator!=(const QuestAvailabilityExpression& other) const {
|
||||
inline bool operator!=(const IntegralExpression& other) const {
|
||||
return !this->operator==(other);
|
||||
}
|
||||
inline int64_t evaluate(const Env& env) const {
|
||||
+6
-6
@@ -205,8 +205,8 @@ VersionedQuest::VersionedQuest(
|
||||
std::shared_ptr<const std::string> pvr_contents,
|
||||
std::shared_ptr<const BattleRules> battle_rules,
|
||||
ssize_t challenge_template_index,
|
||||
std::shared_ptr<const QuestAvailabilityExpression> available_expression,
|
||||
std::shared_ptr<const QuestAvailabilityExpression> enabled_expression)
|
||||
std::shared_ptr<const IntegralExpression> available_expression,
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression)
|
||||
: quest_number(quest_number),
|
||||
category_id(category_id),
|
||||
episode(Episode::NONE),
|
||||
@@ -673,8 +673,8 @@ QuestIndex::QuestIndex(
|
||||
const FileData* json_filedata = nullptr;
|
||||
shared_ptr<BattleRules> battle_rules;
|
||||
ssize_t challenge_template_index = -1;
|
||||
shared_ptr<const QuestAvailabilityExpression> available_expression;
|
||||
shared_ptr<const QuestAvailabilityExpression> enabled_expression;
|
||||
shared_ptr<const IntegralExpression> available_expression;
|
||||
shared_ptr<const IntegralExpression> enabled_expression;
|
||||
try {
|
||||
json_filedata = &json_files.at(basename);
|
||||
} catch (const out_of_range&) {
|
||||
@@ -698,11 +698,11 @@ QuestIndex::QuestIndex(
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
try {
|
||||
available_expression = make_shared<QuestAvailabilityExpression>(metadata_json.get_string("AvailableIf"));
|
||||
available_expression = make_shared<IntegralExpression>(metadata_json.get_string("AvailableIf"));
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
try {
|
||||
enabled_expression = make_shared<QuestAvailabilityExpression>(metadata_json.get_string("EnabledIf"));
|
||||
enabled_expression = make_shared<IntegralExpression>(metadata_json.get_string("EnabledIf"));
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -8,8 +8,8 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "IntegralExpression.hh"
|
||||
#include "PlayerSubordinates.hh"
|
||||
#include "QuestAvailabilityExpression.hh"
|
||||
#include "QuestScript.hh"
|
||||
#include "StaticGameData.hh"
|
||||
#include "TeamIndex.hh"
|
||||
@@ -76,8 +76,8 @@ struct VersionedQuest {
|
||||
std::shared_ptr<const std::string> pvr_contents;
|
||||
std::shared_ptr<const BattleRules> battle_rules;
|
||||
ssize_t challenge_template_index;
|
||||
std::shared_ptr<const QuestAvailabilityExpression> available_expression;
|
||||
std::shared_ptr<const QuestAvailabilityExpression> enabled_expression;
|
||||
std::shared_ptr<const IntegralExpression> available_expression;
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression;
|
||||
|
||||
VersionedQuest(
|
||||
uint32_t quest_number,
|
||||
@@ -89,8 +89,8 @@ struct VersionedQuest {
|
||||
std::shared_ptr<const std::string> pvr_contents,
|
||||
std::shared_ptr<const BattleRules> battle_rules = nullptr,
|
||||
ssize_t challenge_template_index = -1,
|
||||
std::shared_ptr<const QuestAvailabilityExpression> available_expression = nullptr,
|
||||
std::shared_ptr<const QuestAvailabilityExpression> enabled_expression = nullptr);
|
||||
std::shared_ptr<const IntegralExpression> available_expression = nullptr,
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression = nullptr);
|
||||
|
||||
std::string bin_filename() const;
|
||||
std::string dat_filename() const;
|
||||
@@ -124,8 +124,8 @@ public:
|
||||
std::string name;
|
||||
std::shared_ptr<const BattleRules> battle_rules;
|
||||
ssize_t challenge_template_index;
|
||||
std::shared_ptr<const QuestAvailabilityExpression> available_expression;
|
||||
std::shared_ptr<const QuestAvailabilityExpression> enabled_expression;
|
||||
std::shared_ptr<const IntegralExpression> available_expression;
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression;
|
||||
std::map<uint32_t, std::shared_ptr<const VersionedQuest>> versions;
|
||||
};
|
||||
|
||||
|
||||
+25
-29
@@ -4219,6 +4219,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
game->base_exp_multiplier = s->bb_global_exp_multiplier;
|
||||
}
|
||||
|
||||
const unordered_map<uint16_t, IntegralExpression>* quest_flag_rewrites;
|
||||
switch (game->base_version) {
|
||||
case Version::DC_NTE:
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
@@ -4226,6 +4227,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
case Version::DC_V2:
|
||||
case Version::PC_NTE:
|
||||
case Version::PC_V2:
|
||||
quest_flag_rewrites = &s->quest_flag_rewrites_v1_v2;
|
||||
if (game->mode == GameMode::BATTLE) {
|
||||
game->set_drop_mode(s->default_drop_mode_v1_v2_battle);
|
||||
game->allowed_drop_modes = s->allowed_drop_modes_v1_v2_battle;
|
||||
@@ -4240,6 +4242,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
case Version::GC_NTE:
|
||||
case Version::GC_V3:
|
||||
case Version::XB_V3:
|
||||
quest_flag_rewrites = &s->quest_flag_rewrites_v3;
|
||||
if (game->mode == GameMode::BATTLE) {
|
||||
game->set_drop_mode(s->default_drop_mode_v3_battle);
|
||||
game->allowed_drop_modes = s->allowed_drop_modes_v3_battle;
|
||||
@@ -4253,10 +4256,12 @@ shared_ptr<Lobby> create_game_generic(
|
||||
break;
|
||||
case Version::GC_EP3_NTE:
|
||||
case Version::GC_EP3:
|
||||
quest_flag_rewrites = nullptr;
|
||||
game->set_drop_mode(Lobby::DropMode::DISABLED);
|
||||
game->allowed_drop_modes = (1 << static_cast<size_t>(game->drop_mode));
|
||||
break;
|
||||
case Version::BB_V4:
|
||||
quest_flag_rewrites = &s->quest_flag_rewrites_v4;
|
||||
if (game->mode == GameMode::BATTLE) {
|
||||
game->set_drop_mode(s->default_drop_mode_v4_battle);
|
||||
game->allowed_drop_modes = s->allowed_drop_modes_v4_battle;
|
||||
@@ -4323,37 +4328,28 @@ shared_ptr<Lobby> create_game_generic(
|
||||
game->quest_flags_known = make_unique<QuestFlags>();
|
||||
}
|
||||
|
||||
if (s->unlock_all_areas) {
|
||||
static const vector<uint16_t> flags_ep1_v123 = {0x0017, 0x0020, 0x002A};
|
||||
static const vector<uint16_t> flags_ep1_v4 = {0x01F9, 0x0201, 0x0207};
|
||||
static const vector<uint16_t> flags_ep2_v123 = {0x004C, 0x004F, 0x0052};
|
||||
static const vector<uint16_t> flags_ep2_v4 = {0x021B, 0x0225, 0x022F};
|
||||
static const vector<uint16_t> flags_ep4_v4 = {0x02BD, 0x02BE, 0x02BF, 0x02C0, 0x02C1};
|
||||
|
||||
const vector<uint16_t>* flags_to_enable;
|
||||
switch (game->episode) {
|
||||
case Episode::EP1:
|
||||
flags_to_enable = is_v4(game->base_version) ? &flags_ep1_v4 : &flags_ep1_v123;
|
||||
break;
|
||||
case Episode::EP2:
|
||||
flags_to_enable = is_v4(game->base_version) ? &flags_ep2_v4 : &flags_ep2_v123;
|
||||
break;
|
||||
case Episode::EP4:
|
||||
flags_to_enable = &flags_ep1_v4;
|
||||
break;
|
||||
default:
|
||||
flags_to_enable = nullptr;
|
||||
}
|
||||
|
||||
if (flags_to_enable) {
|
||||
for (uint16_t flag_num : *flags_to_enable) {
|
||||
game->quest_flag_values->set(game->difficulty, flag_num);
|
||||
if (game->quest_flags_known) {
|
||||
game->quest_flags_known->set(game->difficulty, flag_num);
|
||||
}
|
||||
if (quest_flag_rewrites && !quest_flag_rewrites->empty()) {
|
||||
IntegralExpression::Env env = {
|
||||
.flags = &p->quest_flags.data.at(difficulty),
|
||||
.challenge_records = &p->challenge_records,
|
||||
.team = c->team(),
|
||||
.num_players = 1,
|
||||
.event = game->event,
|
||||
.v1_present = is_v1(game->base_version),
|
||||
};
|
||||
for (const auto& it : *quest_flag_rewrites) {
|
||||
bool should_set = it.second.evaluate(env);
|
||||
game->log.info("Overriding quest flag %04hX = %s", it.first, should_set ? "true" : "false");
|
||||
if (should_set) {
|
||||
game->quest_flag_values->set(game->difficulty, it.first);
|
||||
} else {
|
||||
game->quest_flag_values->clear(game->difficulty, it.first);
|
||||
}
|
||||
if (game->quest_flags_known) {
|
||||
game->quest_flags_known->set(game->difficulty, it.first);
|
||||
}
|
||||
c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_FLAG_STATE);
|
||||
}
|
||||
c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_FLAG_STATE);
|
||||
}
|
||||
|
||||
game->switch_flags = make_unique<SwitchFlags>();
|
||||
|
||||
@@ -2717,16 +2717,12 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
|
||||
auto s = c->require_server_state();
|
||||
// TODO: Should we allow overlays here?
|
||||
auto p = c->character(true, false);
|
||||
if (s->quest_flag_persist_mask.get(flag_num)) {
|
||||
if (should_set) {
|
||||
c->log.info("Setting quest flag %s:%03hX", name_for_difficulty(difficulty), flag_num);
|
||||
p->quest_flags.set(difficulty, flag_num);
|
||||
} else {
|
||||
c->log.info("Clearing quest flag %s:%03hX", name_for_difficulty(difficulty), flag_num);
|
||||
p->quest_flags.clear(difficulty, flag_num);
|
||||
}
|
||||
if (should_set) {
|
||||
c->log.info("Setting quest flag %s:%03hX", name_for_difficulty(difficulty), flag_num);
|
||||
p->quest_flags.set(difficulty, flag_num);
|
||||
} else {
|
||||
c->log.info("Quest flag %s:%03hX cannot be modified", name_for_difficulty(difficulty), flag_num);
|
||||
c->log.info("Clearing quest flag %s:%03hX", name_for_difficulty(difficulty), flag_num);
|
||||
p->quest_flags.clear(difficulty, flag_num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-7
@@ -718,13 +718,27 @@ void ServerState::load_config_early() {
|
||||
throw runtime_error("CLIENT drop mode cannot be allowed in V4");
|
||||
}
|
||||
|
||||
this->quest_flag_persist_mask.update_all(true);
|
||||
try {
|
||||
for (const auto& flag_id_json : this->config_json->get_list("PreventPersistQuestFlags")) {
|
||||
this->quest_flag_persist_mask.clear(flag_id_json->as_int());
|
||||
auto parse_quest_flag_rewrites = [&json = this->config_json](const char* key) -> std::unordered_map<uint16_t, IntegralExpression> {
|
||||
std::unordered_map<uint16_t, IntegralExpression> ret;
|
||||
try {
|
||||
for (const auto& it : json->get_dict(key)) {
|
||||
if (!it.first.starts_with("F_")) {
|
||||
throw runtime_error("invalid flag reference: " + it.first);
|
||||
}
|
||||
uint16_t flag = stoul(it.first.substr(2), nullptr, 16);
|
||||
if (it.second->is_bool()) {
|
||||
ret.emplace(flag, it.second->as_bool() ? "true" : "false");
|
||||
} else {
|
||||
ret.emplace(flag, it.second->as_string());
|
||||
}
|
||||
}
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
this->quest_flag_rewrites_v1_v2 = parse_quest_flag_rewrites("QuestFlagRewritesV1V2");
|
||||
this->quest_flag_rewrites_v3 = parse_quest_flag_rewrites("QuestFlagRewritesV3");
|
||||
this->quest_flag_rewrites_v4 = parse_quest_flag_rewrites("QuestFlagRewritesV4");
|
||||
|
||||
this->persistent_game_idle_timeout_usecs = this->config_json->get_int("PersistentGameIdleTimeout", 0);
|
||||
this->cheat_mode_behavior = parse_behavior_switch("CheatModeBehavior", BehaviorSwitch::OFF_BY_DEFAULT);
|
||||
@@ -890,7 +904,6 @@ void ServerState::load_config_early() {
|
||||
this->allow_dc_pc_games = this->config_json->get_bool("AllowDCPCGames", true);
|
||||
this->allow_gc_xb_games = this->config_json->get_bool("AllowGCXBGames", true);
|
||||
this->enable_chat_commands = this->config_json->get_bool("EnableChatCommands", true);
|
||||
this->unlock_all_areas = this->config_json->get_bool("UnlockAllAreas", false);
|
||||
|
||||
this->version_name_colors.reset();
|
||||
try {
|
||||
|
||||
+3
-2
@@ -91,7 +91,6 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool allow_dc_pc_games = true;
|
||||
bool allow_gc_xb_games = true;
|
||||
bool enable_chat_commands = true;
|
||||
bool unlock_all_areas = false;
|
||||
std::unique_ptr<std::array<uint32_t, NUM_NON_PATCH_VERSIONS>> version_name_colors;
|
||||
uint8_t allowed_drop_modes_v1_v2_normal = 0x1F;
|
||||
uint8_t allowed_drop_modes_v1_v2_battle = 0x07;
|
||||
@@ -111,7 +110,9 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
Lobby::DropMode default_drop_mode_v4_normal = Lobby::DropMode::SERVER_SHARED;
|
||||
Lobby::DropMode default_drop_mode_v4_battle = Lobby::DropMode::SERVER_SHARED;
|
||||
Lobby::DropMode default_drop_mode_v4_challenge = Lobby::DropMode::SERVER_SHARED;
|
||||
QuestFlagsForDifficulty quest_flag_persist_mask;
|
||||
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v1_v2;
|
||||
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v3;
|
||||
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v4;
|
||||
uint64_t persistent_game_idle_timeout_usecs = 0;
|
||||
bool ep3_send_function_call_enabled = false;
|
||||
bool enable_v3_v4_protected_subcommands = false;
|
||||
|
||||
+49
-14
@@ -958,14 +958,6 @@
|
||||
// available on the proxy server.
|
||||
"CheatModeBehavior": "OnByDefault",
|
||||
|
||||
// Whether to unlock all areas by default in Ep1/2/4 games. If this is on,
|
||||
// the Ragol warp in Pioneer 2 will allow access to all base areas (Forest 1,
|
||||
// Cave 1, Mine 1, and Ruins 1 in Episode 1, for example) even if the player
|
||||
// who created the game does not yet have access to those areas.
|
||||
// Note that some late PSOBB client versions (for example, the Tethealla
|
||||
// client) open all areas by default, so this setting has no effect for them.
|
||||
"UnlockAllAreas": false,
|
||||
|
||||
// Whether to enable rare drop notifications by default. Players can toggle
|
||||
// this behavior for themselves with the $itemnotifs command.
|
||||
"RareNotificationsEnabledByDefaultV1V2": false,
|
||||
@@ -1107,12 +1099,55 @@
|
||||
"Episode2": [1, 30, 60, 100],
|
||||
"Episode4": [1, 40, 70, 110],
|
||||
},
|
||||
// Some quest flags should not be written to the character file when they're
|
||||
// updated during a quest. Specify the flag IDs here to prevent those flags
|
||||
// from being updated in saved BB character data. The default value here
|
||||
// prevents the door locks in CCA from being deactivated in free-roam by
|
||||
// loading a quest.
|
||||
"PreventPersistQuestFlags": [0x0046, 0x0047, 0x0048],
|
||||
|
||||
// Some quest flags should be changed when a game is started in order to fix
|
||||
// certain in-game issues (noted in the comments in the default values below).
|
||||
// These can be true, false, or expressions to make values conditional on
|
||||
// other flags' values. For non-BB versions, you should generally only use
|
||||
// true and false here, since the server doesn't have direct access to the
|
||||
// client's quest flags from their save file.
|
||||
// If you use an expression, the format is the same as the AvailableIf and
|
||||
// EnabledIf fields in quest JSON files (see system/quests/battle/b88001.json
|
||||
// for details). Note that the expression is only evaluated at the time the
|
||||
// game is created, and the player-specific tokens like C_EpX_YY refer to the
|
||||
// player who created the game.
|
||||
// The UnlockAllAreas option is now gone; if you want the same behavior as if
|
||||
// it were enabled, uncomment all the "area unlocks" lines below. Note that
|
||||
// some late PSOBB client versions (for example, the Tethealla client) open
|
||||
// all areas by default, so the area unlock flags have no effect for them.
|
||||
"QuestFlagRewritesV1V2": {
|
||||
// "F_0017": true, // Ep1 area unlocks
|
||||
// "F_0020": true, // Ep1 area unlocks
|
||||
// "F_002A": true, // Ep1 area unlocks
|
||||
},
|
||||
"QuestFlagRewritesV3": {
|
||||
// "F_0017": true, // Ep1 area unlocks
|
||||
// "F_0020": true, // Ep1 area unlocks
|
||||
// "F_002A": true, // Ep1 area unlocks
|
||||
// "F_004C": true, // Ep2 area unlocks
|
||||
// "F_004F": true, // Ep2 area unlocks
|
||||
// "F_0052": true, // Ep2 area unlocks
|
||||
},
|
||||
"QuestFlagRewritesV4": {
|
||||
// "F_01F9": true, // Ep1 area unlocks
|
||||
// "F_0201": true, // Ep1 area unlocks
|
||||
// "F_0207": true, // Ep1 area unlocks
|
||||
// "F_021B": true, // Ep2 area unlocks
|
||||
// "F_0225": true, // Ep2 area unlocks
|
||||
// "F_022F": true, // Ep2 area unlocks
|
||||
// "F_02BD": true, // Ep4 area unlocks
|
||||
// "F_02BE": true, // Ep4 area unlocks
|
||||
// "F_02BF": true, // Ep4 area unlocks
|
||||
// "F_02C0": true, // Ep4 area unlocks
|
||||
// "F_02C1": true, // Ep4 area unlocks
|
||||
"F_0046": false, // Ep2 CCA door lock fix
|
||||
"F_0047": false, // Ep2 CCA door lock fix
|
||||
"F_0048": false, // Ep2 CCA door lock fix
|
||||
"F_002C": "F_01F7", // Ep1 Forest monument state = 1-2 cleared
|
||||
"F_002D": "F_01FD", // Ep1 Cave monument state = 2-2 cleared
|
||||
"F_002E": "F_0209", // Ep1 Mine monument state = 4-1 cleared
|
||||
"F_002F": "F_01F7 && F_01FD && F_0209", // All monuments state
|
||||
},
|
||||
|
||||
// Whether to enable certain exception handling. Disabling this causes
|
||||
// newserv to abort when any client causes an exception, which is generally
|
||||
|
||||
+34
-2
@@ -30,7 +30,6 @@
|
||||
"DefaultDropModeV4Battle": "SERVER_SHARED",
|
||||
"DefaultDropModeV4Challenge": "SERVER_SHARED",
|
||||
"CheatModeBehavior": "OnByDefault",
|
||||
"UnlockAllAreas": false,
|
||||
"RareNotificationsEnabledByDefault": false,
|
||||
"NotifyGameForItemPrimaryIdentifiers": [],
|
||||
"NotifyServerForItemPrimaryIdentifiers": [],
|
||||
@@ -407,5 +406,38 @@
|
||||
"Episode2": [1, 30, 60, 100],
|
||||
"Episode4": [1, 40, 70, 110],
|
||||
},
|
||||
"PreventPersistQuestFlags": [0x0046, 0x0047, 0x0048],
|
||||
|
||||
"QuestFlagRewritesV1V2": {
|
||||
// "F_0017": true, // Ep1 area unlocks
|
||||
// "F_0020": true, // Ep1 area unlocks
|
||||
// "F_002A": true, // Ep1 area unlocks
|
||||
},
|
||||
"QuestFlagRewritesV3": {
|
||||
// "F_0017": true, // Ep1 area unlocks
|
||||
// "F_0020": true, // Ep1 area unlocks
|
||||
// "F_002A": true, // Ep1 area unlocks
|
||||
// "F_004C": true, // Ep2 area unlocks
|
||||
// "F_004F": true, // Ep2 area unlocks
|
||||
// "F_0052": true, // Ep2 area unlocks
|
||||
},
|
||||
"QuestFlagRewritesV4": {
|
||||
// "F_01F9": true, // Ep1 area unlocks
|
||||
// "F_0201": true, // Ep1 area unlocks
|
||||
// "F_0207": true, // Ep1 area unlocks
|
||||
// "F_021B": true, // Ep2 area unlocks
|
||||
// "F_0225": true, // Ep2 area unlocks
|
||||
// "F_022F": true, // Ep2 area unlocks
|
||||
// "F_02BD": true, // Ep4 area unlocks
|
||||
// "F_02BE": true, // Ep4 area unlocks
|
||||
// "F_02BF": true, // Ep4 area unlocks
|
||||
// "F_02C0": true, // Ep4 area unlocks
|
||||
// "F_02C1": true, // Ep4 area unlocks
|
||||
"F_0046": false, // Ep2 CCA door lock fix
|
||||
"F_0047": false, // Ep2 CCA door lock fix
|
||||
"F_0048": false, // Ep2 CCA door lock fix
|
||||
"F_002C": "F_01F7", // Ep1 Forest monument state = 1-2 cleared
|
||||
"F_002D": "F_01FD", // Ep1 Cave monument state = 2-2 cleared
|
||||
"F_002E": "F_0209", // Ep1 Mine monument state = 4-1 cleared
|
||||
"F_002F": "F_01F7 && F_01FD && F_0209", // All monuments state
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user