add more options in IntegralExpression
This commit is contained in:
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "Client.hh"
|
||||
|
||||
const std::vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
|
||||
const std::vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES{
|
||||
ChoiceSearchCategory{
|
||||
.id = 0x0001,
|
||||
.name = "Level",
|
||||
@@ -145,4 +145,4 @@ const std::vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
|
||||
return (choice_id == 0) || (target_choice_id == 0) || (choice_id == target_choice_id);
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
+3
-1
@@ -377,7 +377,9 @@ bool Client::evaluate_quest_availability_expression(
|
||||
}
|
||||
auto p = this->character_file();
|
||||
IntegralExpression::Env env = {
|
||||
.flags = &p->quest_flags.data.at(static_cast<size_t>(difficulty)),
|
||||
.section_id = p->disp.visual.sh.section_id,
|
||||
.difficulty = difficulty,
|
||||
.flags = &p->quest_flags,
|
||||
.challenge_records = &p->challenge_records,
|
||||
.team = this->team(),
|
||||
.num_players = num_players,
|
||||
|
||||
@@ -811,7 +811,7 @@ void DownloadSession::on_request_complete() {
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<DownloadSession::GameConfig> DownloadSession::game_configs({
|
||||
const std::vector<DownloadSession::GameConfig> DownloadSession::game_configs{
|
||||
{.mode = GameMode::NORMAL, .episode = Episode::EP1, .v1 = true, .v2 = true, .v3 = true},
|
||||
{.mode = GameMode::NORMAL, .episode = Episode::EP2, .v1 = false, .v2 = false, .v3 = true},
|
||||
{.mode = GameMode::NORMAL, .episode = Episode::EP4, .v1 = false, .v2 = false, .v3 = false},
|
||||
@@ -821,4 +821,4 @@ const std::vector<DownloadSession::GameConfig> DownloadSession::game_configs({
|
||||
{.mode = GameMode::SOLO, .episode = Episode::EP1, .v1 = false, .v2 = false, .v3 = false},
|
||||
{.mode = GameMode::SOLO, .episode = Episode::EP2, .v1 = false, .v2 = false, .v3 = false},
|
||||
{.mode = GameMode::SOLO, .episode = Episode::EP4, .v1 = false, .v2 = false, .v3 = false},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1738,7 +1738,7 @@ bool Server::update_registration_phase() {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers({
|
||||
const std::unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers{
|
||||
{0x0B, &Server::handle_CAx0B_redraw_initial_hand},
|
||||
{0x0C, &Server::handle_CAx0C_end_redraw_initial_hand_phase},
|
||||
{0x0D, &Server::handle_CAx0D_end_non_action_phase},
|
||||
@@ -1762,7 +1762,7 @@ const std::unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers
|
||||
{0x41, &Server::handle_CAx41_map_request},
|
||||
{0x48, &Server::handle_CAx48_end_turn},
|
||||
{0x49, &Server::handle_CAx49_card_counts},
|
||||
});
|
||||
};
|
||||
|
||||
void Server::on_server_data_input(std::shared_ptr<Client> sender_c, const std::string& data) {
|
||||
auto header = check_size_t<G_CardBattleCommandHeader>(data, 0xFFFF);
|
||||
|
||||
+39
-10
@@ -158,26 +158,42 @@ std::string IntegralExpression::UnaryOperatorNode::str() const {
|
||||
}
|
||||
}
|
||||
|
||||
IntegralExpression::FlagLookupNode::FlagLookupNode(uint16_t flag_index) : flag_index(flag_index) {}
|
||||
bool IntegralExpression::SectionIDLookupNode::operator==(const Node& other) const {
|
||||
return (dynamic_cast<const SectionIDLookupNode*>(&other) != nullptr);
|
||||
}
|
||||
|
||||
bool IntegralExpression::FlagLookupNode::operator==(const Node& other) const {
|
||||
int64_t IntegralExpression::SectionIDLookupNode::evaluate(const Env& env) const {
|
||||
return env.section_id;
|
||||
}
|
||||
|
||||
std::string IntegralExpression::SectionIDLookupNode::str() const {
|
||||
return "P_SID";
|
||||
}
|
||||
|
||||
IntegralExpression::QuestFlagLookupNode::QuestFlagLookupNode(Difficulty difficulty, uint16_t flag_index)
|
||||
: difficulty(difficulty), flag_index(flag_index) {}
|
||||
|
||||
bool IntegralExpression::QuestFlagLookupNode::operator==(const Node& other) const {
|
||||
try {
|
||||
const FlagLookupNode& other_flag = dynamic_cast<const FlagLookupNode&>(other);
|
||||
return other_flag.flag_index == this->flag_index;
|
||||
const QuestFlagLookupNode& other_flag = dynamic_cast<const QuestFlagLookupNode&>(other);
|
||||
return ((other_flag.difficulty == this->difficulty) && (other_flag.flag_index == this->flag_index));
|
||||
} catch (const std::bad_cast&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t IntegralExpression::FlagLookupNode::evaluate(const Env& env) const {
|
||||
int64_t IntegralExpression::QuestFlagLookupNode::evaluate(const Env& env) const {
|
||||
if (!env.flags) {
|
||||
throw std::runtime_error("quest flags not available");
|
||||
}
|
||||
return env.flags->get(this->flag_index) ? 1 : 0;
|
||||
Difficulty effective_difficulty = (this->difficulty == Difficulty::UNKNOWN) ? env.difficulty : this->difficulty;
|
||||
return env.flags->get(effective_difficulty, this->flag_index) ? 1 : 0;
|
||||
}
|
||||
|
||||
std::string IntegralExpression::FlagLookupNode::str() const {
|
||||
return std::format("F_{:04X}", this->flag_index);
|
||||
std::string IntegralExpression::QuestFlagLookupNode::str() const {
|
||||
return (this->difficulty == Difficulty::UNKNOWN)
|
||||
? std::format("F_{:04X}", this->flag_index)
|
||||
: std::format("F_{:c}_{:04X}", abbreviation_for_difficulty(this->difficulty), this->flag_index);
|
||||
}
|
||||
|
||||
IntegralExpression::ChallengeCompletionLookupNode::ChallengeCompletionLookupNode(Episode episode, uint8_t stage_index)
|
||||
@@ -380,16 +396,29 @@ std::unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(s
|
||||
}
|
||||
|
||||
// Check for env lookups
|
||||
if (text == "P_SID") {
|
||||
return std::make_unique<SectionIDLookupNode>();
|
||||
}
|
||||
if (text.starts_with("F_")) {
|
||||
Difficulty difficulty = Difficulty::UNKNOWN;
|
||||
if (text.starts_with("F_N_")) {
|
||||
difficulty = Difficulty::NORMAL;
|
||||
} else if (text.starts_with("F_H_")) {
|
||||
difficulty = Difficulty::HARD;
|
||||
} else if (text.starts_with("F_V_")) {
|
||||
difficulty = Difficulty::VERY_HARD;
|
||||
} else if (text.starts_with("F_U_")) {
|
||||
difficulty = Difficulty::ULTIMATE;
|
||||
}
|
||||
char* endptr = nullptr;
|
||||
uint64_t flag = strtoul(text.data() + 2, &endptr, 16);
|
||||
uint64_t flag = strtoul(text.data() + ((difficulty == Difficulty::UNKNOWN) ? 2 : 4), &endptr, 16);
|
||||
if (endptr != text.data() + text.size()) {
|
||||
throw std::runtime_error("invalid flag lookup token");
|
||||
}
|
||||
if (flag >= 0x400) {
|
||||
throw std::runtime_error("invalid flag index");
|
||||
}
|
||||
return std::make_unique<FlagLookupNode>(flag);
|
||||
return std::make_unique<QuestFlagLookupNode>(difficulty, flag);
|
||||
}
|
||||
if (text.starts_with("CC_")) {
|
||||
Episode episode;
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
class IntegralExpression {
|
||||
public:
|
||||
struct Env {
|
||||
const QuestFlagsForDifficulty* flags;
|
||||
uint8_t section_id;
|
||||
Difficulty difficulty;
|
||||
const QuestFlags* flags;
|
||||
const PlayerRecordsChallengeBB* challenge_records;
|
||||
std::shared_ptr<const TeamIndex::Team> team;
|
||||
size_t num_players;
|
||||
@@ -105,15 +107,25 @@ protected:
|
||||
std::unique_ptr<const Node> sub;
|
||||
};
|
||||
|
||||
class FlagLookupNode : public Node {
|
||||
class SectionIDLookupNode : public Node {
|
||||
public:
|
||||
FlagLookupNode(uint16_t flag_index);
|
||||
virtual ~FlagLookupNode() = default;
|
||||
SectionIDLookupNode() = default;
|
||||
virtual ~SectionIDLookupNode() = default;
|
||||
virtual bool operator==(const Node& other) const;
|
||||
virtual int64_t evaluate(const Env& env) const;
|
||||
virtual std::string str() const;
|
||||
};
|
||||
|
||||
class QuestFlagLookupNode : public Node {
|
||||
public:
|
||||
QuestFlagLookupNode(Difficulty difficulty, uint16_t flag_index);
|
||||
virtual ~QuestFlagLookupNode() = default;
|
||||
virtual bool operator==(const Node& other) const;
|
||||
virtual int64_t evaluate(const Env& env) const;
|
||||
virtual std::string str() const;
|
||||
|
||||
protected:
|
||||
Difficulty difficulty;
|
||||
uint16_t flag_index;
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -6,10 +6,10 @@
|
||||
#include "ItemParameterTable.hh"
|
||||
#include "StaticGameData.hh"
|
||||
|
||||
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_DC_NTE({10});
|
||||
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V1_V2({10, 10, 1, 10, 10, 10, 10, 10, 10, 1});
|
||||
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V3_V4(
|
||||
{10, 10, 1, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 99, 1});
|
||||
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_DC_NTE{10};
|
||||
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V1_V2{10, 10, 1, 10, 10, 10, 10, 10, 10, 1};
|
||||
const std::vector<uint8_t> ItemData::StackLimits::DEFAULT_TOOL_LIMITS_V3_V4{
|
||||
10, 10, 1, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 99, 1};
|
||||
|
||||
const ItemData::StackLimits ItemData::StackLimits::DEFAULT_STACK_LIMITS_DC_NTE(
|
||||
Version::DC_NTE, ItemData::StackLimits::DEFAULT_TOOL_LIMITS_DC_NTE, 999999);
|
||||
|
||||
@@ -4735,7 +4735,9 @@ std::shared_ptr<Lobby> create_game_generic(
|
||||
|
||||
if (quest_flag_rewrites && !quest_flag_rewrites->empty()) {
|
||||
IntegralExpression::Env env = {
|
||||
.flags = &p->quest_flags.array(difficulty),
|
||||
.section_id = game->effective_section_id(),
|
||||
.difficulty = game->difficulty,
|
||||
.flags = &p->quest_flags,
|
||||
.challenge_records = &p->challenge_records,
|
||||
.team = creator_c->team(),
|
||||
.num_players = 1,
|
||||
|
||||
+6
-6
@@ -33,7 +33,7 @@ inline uint8_t get_pre_v1_subcommand(Version v, uint8_t nte_subcommand, uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
const std::unordered_set<uint32_t> v2_crypt_initial_client_commands({
|
||||
const std::unordered_set<uint32_t> v2_crypt_initial_client_commands{
|
||||
0x00260088, // (17) DCNTE license check
|
||||
0x00B0008B, // (02) DCNTE login
|
||||
0x00B0018B, // (02) DCNTE login (UDP off)
|
||||
@@ -52,20 +52,20 @@ const std::unordered_set<uint32_t> v2_crypt_initial_client_commands({
|
||||
0x0130019D, // (02) DCv2/GCNTE extended login (UDP off)
|
||||
// Note: PSO PC initial commands are not listed here because we don't use a detector encryption for PSO PC
|
||||
// (instead, we use the split reconnect command to send PC to a different port).
|
||||
});
|
||||
const std::unordered_set<uint32_t> v3_crypt_initial_client_commands({
|
||||
};
|
||||
const std::unordered_set<uint32_t> v3_crypt_initial_client_commands{
|
||||
0x00E000DB, // (17) GC/XB license check
|
||||
0x00EC009E, // (02) GC login
|
||||
0x00EC019E, // (02) GC login (UDP off)
|
||||
0x0150009E, // (02) GC extended login
|
||||
0x0150019E, // (02) GC extended login (UDP off)
|
||||
});
|
||||
};
|
||||
|
||||
const std::unordered_set<std::string> bb_crypt_initial_client_commands({
|
||||
const std::unordered_set<std::string> bb_crypt_initial_client_commands{
|
||||
std::string("\xB4\x00\x93\x00\x00\x00\x00\x00", 8),
|
||||
std::string("\xAC\x00\x93\x00\x00\x00\x00\x00", 8),
|
||||
std::string("\xDC\x00\xDB\x00\x00\x00\x00\x00", 8),
|
||||
});
|
||||
};
|
||||
|
||||
void send_command(
|
||||
std::shared_ptr<Client> c,
|
||||
|
||||
@@ -515,7 +515,7 @@ struct WeaponRootT {
|
||||
U32T<BE> favored_grind_range_table; // {u32 min, u32 max}[6]
|
||||
} __packed_ws_be__(WeaponRootT, 0x20);
|
||||
|
||||
const std::array<std::pair<uint8_t, uint8_t>, 0x48> WeaponShopRandomSet::type_defs({
|
||||
const std::array<std::pair<uint8_t, uint8_t>, 0x48> WeaponShopRandomSet::type_defs{{
|
||||
/* 00 */ {0x01, 0x00}, // Saber
|
||||
/* 01 */ {0x01, 0x01}, // Brand
|
||||
/* 02 */ {0x01, 0x02}, // Buster
|
||||
@@ -588,9 +588,9 @@ const std::array<std::pair<uint8_t, uint8_t>, 0x48> WeaponShopRandomSet::type_de
|
||||
/* 45 */ {0x0A, 0x05}, // MACE OF ADAMAN
|
||||
/* 46 */ {0x0C, 0x05}, // ICE STAFF:DAGON
|
||||
/* 47 */ {0x0B, 0x05}, // BRAVE HAMMER
|
||||
});
|
||||
}};
|
||||
|
||||
const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs_39({
|
||||
const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs_39{{
|
||||
// Indexed by section_id
|
||||
{0x28, 0x00}, // HARISEN BATTLE FAN
|
||||
{0x2A, 0x00}, // AKIKO'S WOK
|
||||
@@ -602,9 +602,9 @@ const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs
|
||||
{0x59, 0x00}, // BROOM
|
||||
{0x8A, 0x00}, // SANGE
|
||||
{0x99, 0x00}, // ANGEL HARP
|
||||
});
|
||||
}};
|
||||
|
||||
const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs_3A({
|
||||
const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs_3A{{
|
||||
// Indexed by section_id
|
||||
{0x99, 0x00}, // ANGEL HARP
|
||||
{0x64, 0x00}, // CHAMELEON SCYTHE
|
||||
@@ -616,7 +616,7 @@ const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs
|
||||
{0x2A, 0x00}, // AKIKO'S WOK
|
||||
{0x48, 0x00}, // SAMBA MARACAS
|
||||
{0x35, 0x00}, // CRAZY TUNE
|
||||
});
|
||||
}};
|
||||
|
||||
const std::array<int8_t, 20> WeaponShopRandomSet::bonus_values{
|
||||
-50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
|
||||
|
||||
@@ -121,13 +121,13 @@ static const std::array<const char*, 10> section_id_to_name = {
|
||||
static const std::array<const char*, 10> section_id_to_abbreviation = {
|
||||
"Vir", "Grn", "Sky", "Blu", "Prp", "Pnk", "Red", "Orn", "Ylw", "Wht"};
|
||||
|
||||
const std::unordered_map<std::string, uint8_t> name_to_section_id({{"viridia", 0},
|
||||
const std::unordered_map<std::string, uint8_t> name_to_section_id{
|
||||
// Greennill is spelled Greenill in some places, so we accept both spellings
|
||||
{"greennill", 1}, {"greenill", 1}, {"skyly", 2}, {"bluefull", 3}, {"purplenum", 4}, {"pinkal", 5}, {"redria", 6},
|
||||
{"oran", 7}, {"yellowboze", 8}, {"whitill", 9},
|
||||
{"viridia", 0}, {"greennill", 1}, {"greenill", 1}, {"skyly", 2}, {"bluefull", 3}, {"purplenum", 4}, {"pinkal", 5},
|
||||
{"redria", 6}, {"oran", 7}, {"yellowboze", 8}, {"whitill", 9},
|
||||
|
||||
// Shortcuts for chat commands
|
||||
{"b", 3}, {"g", 1}, {"o", 7}, {"pi", 5}, {"pu", 4}, {"r", 6}, {"s", 2}, {"v", 0}, {"w", 9}, {"y", 8}});
|
||||
{"b", 3}, {"g", 1}, {"o", 7}, {"pi", 5}, {"pu", 4}, {"r", 6}, {"s", 2}, {"v", 0}, {"w", 9}, {"y", 8}};
|
||||
|
||||
const std::vector<std::string> lobby_event_to_name = {
|
||||
"none", "xmas", "none", "val", "easter", "hallo", "sonic", "newyear",
|
||||
@@ -673,10 +673,10 @@ char char_for_challenge_rank(uint8_t rank) {
|
||||
return "BAS"[rank];
|
||||
}
|
||||
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V123({0, 19, 39, 79});
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP1({0, 19, 39, 79});
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP2({0, 29, 49, 89});
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP4({0, 39, 79, 109});
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V123{{0, 19, 39, 79}};
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP1{{0, 19, 39, 79}};
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP2{{0, 29, 49, 89}};
|
||||
const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP4{{0, 39, 79, 109}};
|
||||
|
||||
const std::array<GameMode, 2> ALL_GAME_MODES_V1 = {GameMode::NORMAL, GameMode::BATTLE};
|
||||
const std::array<GameMode, 3> ALL_GAME_MODES_V23 = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE};
|
||||
|
||||
+22
-22
@@ -1338,35 +1338,35 @@
|
||||
// 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 JSONs (see
|
||||
// system/quests/retrieval/q058.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.
|
||||
// created, and the player-specific tokens like CC_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
|
||||
// "area unlock" 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
|
||||
// "F_0017": true, // Ep1 area unlock (Caves)
|
||||
// "F_0020": true, // Ep1 area unlock (Mines)
|
||||
// "F_002A": true, // Ep1 area unlock (Ruins)
|
||||
},
|
||||
"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
|
||||
// "F_0017": true, // Ep1 area unlock (Caves)
|
||||
// "F_0020": true, // Ep1 area unlock (Mines)
|
||||
// "F_002A": true, // Ep1 area unlock (Ruins)
|
||||
// "F_004C": true, // Ep2 area unlock (VR Spaceship)
|
||||
// "F_004F": true, // Ep2 area unlock (CCA)
|
||||
// "F_0052": true, // Ep2 area unlock (Seabed)
|
||||
},
|
||||
"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_01F9": true, // Ep1 area unlock (Caves)
|
||||
// "F_0201": true, // Ep1 area unlock (Mines)
|
||||
// "F_0207": true, // Ep1 area unlock (Ruins)
|
||||
// "F_021B": true, // Ep2 area unlock (VR Spaceship)
|
||||
// "F_0225": true, // Ep2 area unlock (CCA)
|
||||
// "F_022F": true, // Ep2 area unlock (Seabed)
|
||||
// "F_02BD": true, // Ep4 area unlock (Crater West)
|
||||
// "F_02BE": true, // Ep4 area unlock (Crater South)
|
||||
// "F_02BF": true, // Ep4 area unlock (Crater North)
|
||||
// "F_02C0": true, // Ep4 area unlock (Crater Interior)
|
||||
// "F_02C1": true, // Ep4 area unlock (Desert)
|
||||
"F_0046": false, // Ep2 CCA door lock fix
|
||||
"F_0047": false, // Ep2 CCA door lock fix
|
||||
"F_0048": false, // Ep2 CCA door lock fix
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
// Quests that are considered unavailable don't appear in the quest menu at all. To set a condition for a quest to be
|
||||
// available, you can set AvailableIf in the quest's JSON file. The value for AvailableIf should be an expression
|
||||
// that tests any of the following:
|
||||
// F_XXXX: Quest flag specified in hex (e.g. F_014D)
|
||||
// P_SID: Current effective section ID for the game; see name_to_section_id in StaticGameData.cc for values
|
||||
// F_XXXX: Quest flag specified in hex (e.g. F_014D) for the difficulty the player is currently playing in
|
||||
// F_N_XXXX, F_H_XXXX, F_V_XXXX, F_U_XXXX: Same as F_XXXX, but read from a specific difficulty regardless of which
|
||||
// difficulty the player is currently playing in
|
||||
// CC_EpX_Y: Whether or not Challenge stage X in Episode Y is complete (e.g. CC_Ep1_7)
|
||||
// T_ZZZ: Whether or not the player's BB team has reward with key ZZZ (as defined in TeamRewards in config.json)
|
||||
// V_NumPlayers: The number of players in the current game
|
||||
|
||||
Reference in New Issue
Block a user