From c55b19dbc0d328098825470c5c6126fd36716a8b Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 11 Feb 2024 10:50:34 -0800 Subject: [PATCH] fix $dicerange --- src/ChatCommands.cc | 8 +- src/Episode3/DataIndexes.cc | 326 +++++++++++++++++------------------- src/Episode3/DataIndexes.hh | 18 +- src/Episode3/PlayerState.cc | 41 +---- src/Episode3/Server.cc | 20 ++- src/Episode3/Server.hh | 1 + src/ServerShell.cc | 64 +++++-- 7 files changed, 242 insertions(+), 236 deletions(-) diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index a88da976..680dc285 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -1873,11 +1873,11 @@ static void server_command_ep3_set_dice_range(shared_ptr c, const std::s } auto& rules = l->ep3_server->map_and_rules->rules; - rules.def_dice_range = def_dice_range; - rules.atk_dice_range_2v1 = atk_dice_range_2v1; - rules.def_dice_range_2v1 = def_dice_range_2v1; + rules.def_dice_value_range = def_dice_range; + rules.atk_dice_value_range_2v1 = atk_dice_range_2v1; + rules.def_dice_value_range_2v1 = def_dice_range_2v1; - if (!def_dice_range || !atk_dice_range_2v1 || !def_dice_range_2v1) { + if (!def_dice_range && !atk_dice_range_2v1 && !def_dice_range_2v1) { send_text_message_printf(l, "$C7Dice ranges reset\nto defaults"); } else { send_text_message_printf(l, "$C7Dice ranges changed:"); diff --git a/src/Episode3/DataIndexes.cc b/src/Episode3/DataIndexes.cc index f6a4f845..4536a284 100644 --- a/src/Episode3/DataIndexes.cc +++ b/src/Episode3/DataIndexes.cc @@ -1263,8 +1263,8 @@ Rules::Rules(const JSON& json) { this->overall_time_limit = json.get_int("overall_time_limit", this->overall_time_limit); this->phase_time_limit = json.get_int("phase_time_limit", this->phase_time_limit); this->allowed_cards = json.get_enum("allowed_cards", this->allowed_cards); - this->min_dice = json.get_int("min_dice", this->min_dice); - this->max_dice = json.get_int("max_dice", this->max_dice); + this->min_dice_value = json.get_int("min_dice", this->min_dice_value); + this->max_dice_value = json.get_int("max_dice", this->max_dice_value); this->disable_deck_shuffle = json.get_bool("disable_deck_shuffle", this->disable_deck_shuffle); this->disable_deck_loop = json.get_bool("disable_deck_loop", this->disable_deck_loop); this->char_hp = json.get_int("char_hp", this->char_hp); @@ -1273,15 +1273,15 @@ Rules::Rules(const JSON& json) { this->disable_dialogue = json.get_bool("disable_dialogue", this->disable_dialogue); this->dice_exchange_mode = json.get_enum("dice_exchange_mode", this->dice_exchange_mode); this->disable_dice_boost = json.get_bool("disable_dice_boost", this->disable_dice_boost); - uint8_t min_dice = json.get_int("min_def_dice", (this->def_dice_range >> 4) & 0x0F); - uint8_t max_dice = json.get_int("max_def_dice", this->def_dice_range & 0x0F); - this->def_dice_range = ((min_dice << 4) & 0xF0) | (max_dice & 0x0F); - min_dice = json.get_int("min_atk_dice_2v1", (this->atk_dice_range_2v1 >> 4) & 0x0F); - max_dice = json.get_int("max_atk_dice_2v1", this->atk_dice_range_2v1 & 0x0F); - this->atk_dice_range_2v1 = ((min_dice << 4) & 0xF0) | (max_dice & 0x0F); - min_dice = json.get_int("min_def_dice_2v1", (this->def_dice_range_2v1 >> 4) & 0x0F); - max_dice = json.get_int("max_def_dice_2v1", this->def_dice_range_2v1 & 0x0F); - this->def_dice_range_2v1 = ((min_dice << 4) & 0xF0) | (max_dice & 0x0F); + uint8_t min_dice = json.get_int("min_def_dice", (this->def_dice_value_range >> 4) & 0x0F); + uint8_t max_dice = json.get_int("max_def_dice", this->def_dice_value_range & 0x0F); + this->def_dice_value_range = ((min_dice << 4) & 0xF0) | (max_dice & 0x0F); + min_dice = json.get_int("min_atk_dice_2v1", (this->atk_dice_value_range_2v1 >> 4) & 0x0F); + max_dice = json.get_int("max_atk_dice_2v1", this->atk_dice_value_range_2v1 & 0x0F); + this->atk_dice_value_range_2v1 = ((min_dice << 4) & 0xF0) | (max_dice & 0x0F); + min_dice = json.get_int("min_def_dice_2v1", (this->def_dice_value_range_2v1 >> 4) & 0x0F); + max_dice = json.get_int("max_def_dice_2v1", this->def_dice_value_range_2v1 & 0x0F); + this->def_dice_value_range_2v1 = ((min_dice << 4) & 0xF0) | (max_dice & 0x0F); } JSON Rules::json() const { @@ -1289,8 +1289,8 @@ JSON Rules::json() const { {"overall_time_limit", this->overall_time_limit}, {"phase_time_limit", this->phase_time_limit}, {"allowed_cards", name_for_enum(this->allowed_cards)}, - {"min_dice", this->min_dice}, - {"max_dice", this->max_dice}, + {"min_dice", this->min_dice_value}, + {"max_dice", this->max_dice_value}, {"disable_deck_shuffle", static_cast(this->disable_deck_shuffle)}, {"disable_deck_loop", static_cast(this->disable_deck_loop)}, {"char_hp", this->char_hp}, @@ -1299,12 +1299,12 @@ JSON Rules::json() const { {"disable_dialogue", static_cast(this->disable_dialogue)}, {"dice_exchange_mode", name_for_enum(this->dice_exchange_mode)}, {"disable_dice_boost", static_cast(this->disable_dice_boost)}, - {"min_def_dice", ((this->def_dice_range >> 4) & 0x0F)}, - {"max_def_dice", (this->def_dice_range & 0x0F)}, - {"min_atk_dice_2v1", ((this->atk_dice_range_2v1 >> 4) & 0x0F)}, - {"max_atk_dice_2v1", (this->atk_dice_range_2v1 & 0x0F)}, - {"min_def_dice_2v1", ((this->def_dice_range_2v1 >> 4) & 0x0F)}, - {"max_def_dice_2v1", (this->def_dice_range_2v1 & 0x0F)}, + {"min_def_dice", ((this->def_dice_value_range >> 4) & 0x0F)}, + {"max_def_dice", (this->def_dice_value_range & 0x0F)}, + {"min_atk_dice_2v1", ((this->atk_dice_value_range_2v1 >> 4) & 0x0F)}, + {"max_atk_dice_2v1", (this->atk_dice_value_range_2v1 & 0x0F)}, + {"min_def_dice_2v1", ((this->def_dice_value_range_2v1 >> 4) & 0x0F)}, + {"max_def_dice_2v1", (this->def_dice_value_range_2v1 & 0x0F)}, }); } @@ -1312,8 +1312,8 @@ void Rules::set_defaults() { this->clear(); this->overall_time_limit = 24; // 2 hours this->phase_time_limit = 30; - this->min_dice = 1; - this->max_dice = 6; + this->min_dice_value = 1; + this->max_dice_value = 6; this->char_hp = 15; } @@ -1321,8 +1321,8 @@ void Rules::clear() { this->overall_time_limit = 0; this->phase_time_limit = 0; this->allowed_cards = AllowedCards::ALL; - this->min_dice = 0; - this->max_dice = 0; + this->min_dice_value = 0; + this->max_dice_value = 0; this->disable_deck_shuffle = 0; this->disable_deck_loop = 0; this->char_hp = 0; @@ -1331,10 +1331,52 @@ void Rules::clear() { this->disable_dialogue = 0; this->dice_exchange_mode = DiceExchangeMode::HIGH_ATK; this->disable_dice_boost = 0; - this->def_dice_range = 0; + this->def_dice_value_range = 0; + this->atk_dice_value_range_2v1 = 0; + this->def_dice_value_range_2v1 = 0; this->unused.clear(0); } +pair Rules::atk_dice_range(bool is_1p_2v1) const { + pair ret; + if (is_1p_2v1 && this->atk_dice_value_range_2v1 && (this->atk_dice_value_range_2v1 != 0xFF)) { + ret = make_pair((this->atk_dice_value_range_2v1 >> 4) & 0x0F, this->atk_dice_value_range_2v1 & 0x0F); + } else { + ret = make_pair(this->min_dice_value, this->max_dice_value); + } + if (ret.first == 0) { + ret.first = 1; + } + if (ret.second == 0) { + ret.second = 6; + } + if (ret.first > ret.second) { + ret = make_pair(ret.second, ret.first); + } + return ret; +} + +pair Rules::def_dice_range(bool is_1p_2v1) const { + pair ret; + if (is_1p_2v1 && this->def_dice_value_range_2v1 && (this->def_dice_value_range_2v1 != 0xFF)) { + ret = make_pair((this->def_dice_value_range_2v1 >> 4) & 0x0F, this->def_dice_value_range_2v1 & 0x0F); + } else if (this->def_dice_value_range && (this->def_dice_value_range != 0xFF)) { + ret = make_pair((this->def_dice_value_range >> 4) & 0x0F, this->def_dice_value_range & 0x0F); + } else { + ret = make_pair(this->min_dice_value, this->max_dice_value); + } + if (ret.first == 0) { + ret.first = 1; + } + if (ret.second == 0) { + ret.second = 6; + } + if (ret.first > ret.second) { + ret = make_pair(ret.second, ret.first); + } + return ret; +} + string Rules::str() const { vector tokens; @@ -1364,52 +1406,33 @@ string Rules::str() const { break; } - if (this->min_dice == 0xFF) { - tokens.emplace_back("min_dice=(open)"); - } else if (this->min_dice == 0x00) { - tokens.emplace_back("min_dice=1 (default)"); - } else { - tokens.emplace_back(string_printf("min_dice=%hhu", this->min_dice)); + auto format_dice_range = +[](std::pair range) -> string { + string s = "["; + if (range.first == 0xFF) { + s += "min=(open), "; + } else if (range.first == 0x00) { + s += "min=(default), "; + } else { + s += string_printf("min=%hhu, ", range.first); + } + if (range.second == 0xFF) { + s += "max=(open)]"; + } else if (range.second == 0x00) { + s += "max=(default)]"; + } else { + s += string_printf("max=%hhu]", range.second); + } + return s; + }; + tokens.emplace_back("dice_range=" + format_dice_range(make_pair(this->min_dice_value, this->max_dice_value))); + if (this->def_dice_value_range) { + tokens.emplace_back("def_dice_range=" + format_dice_range(this->def_dice_range(false))); } - if (this->max_dice == 0xFF) { - tokens.emplace_back("max_dice=(open)"); - } else if (this->max_dice == 0x00) { - tokens.emplace_back("max_dice=6 (default)"); - } else { - tokens.emplace_back(string_printf("max_dice=%hhu", this->max_dice)); + if (this->atk_dice_value_range_2v1) { + tokens.emplace_back("atk_dice_range_2v1=" + format_dice_range(this->atk_dice_range(true))); } - - if (this->min_def_dice() == 0) { - tokens.emplace_back("min_def_dice=(default)"); - } else { - tokens.emplace_back(string_printf("min_def_dice=%hhu", this->min_def_dice())); - } - if (this->max_def_dice() == 0) { - tokens.emplace_back("max_def_dice=(default)"); - } else { - tokens.emplace_back(string_printf("max_def_dice=%hhu", this->max_def_dice())); - } - - if (this->min_atk_dice_2v1() == 0) { - tokens.emplace_back("min_atk_dice_2v1=(default)"); - } else { - tokens.emplace_back(string_printf("min_atk_dice_2v1=%hhu", this->min_atk_dice_2v1())); - } - if (this->max_atk_dice_2v1() == 0) { - tokens.emplace_back("max_atk_dice_2v1=(default)"); - } else { - tokens.emplace_back(string_printf("max_atk_dice_2v1=%hhu", this->max_atk_dice_2v1())); - } - - if (this->min_def_dice_2v1() == 0) { - tokens.emplace_back("min_def_dice_2v1=(default)"); - } else { - tokens.emplace_back(string_printf("min_def_dice_2v1=%hhu", this->min_def_dice_2v1())); - } - if (this->max_def_dice_2v1() == 0) { - tokens.emplace_back("max_def_dice_2v1=(default)"); - } else { - tokens.emplace_back(string_printf("max_def_dice_2v1=%hhu", this->max_def_dice_2v1())); + if (this->def_dice_value_range_2v1) { + tokens.emplace_back("def_dice_range_2v1=" + format_dice_range(this->def_dice_range(true))); } switch (this->dice_exchange_mode) { @@ -1497,15 +1520,30 @@ RulesTrial::RulesTrial(const Rules& r) : overall_time_limit(r.overall_time_limit), phase_time_limit(r.phase_time_limit), allowed_cards(r.allowed_cards), - atk_die_behavior((r.max_dice == r.min_dice) ? r.max_dice : 0), - def_die_behavior(r.def_dice_range == 0xFF ? 0xFF : ((r.min_def_dice() == r.max_def_dice()) ? r.max_def_dice() : 0)), + // ATK/DEF behaviors set below disable_deck_shuffle(r.disable_deck_shuffle), disable_deck_loop(r.disable_deck_loop), char_hp(r.char_hp), hp_type(r.hp_type), no_assist_cards(r.no_assist_cards), disable_dialogue(r.disable_dialogue), - dice_exchange_mode(r.dice_exchange_mode) {} + dice_exchange_mode(r.dice_exchange_mode) { + if (r.max_dice_value == r.min_dice_value) { + this->atk_die_behavior = r.max_dice_value; + } else { + this->atk_die_behavior = 0; // Random + } + if (r.def_dice_value_range == 0xFF) { + this->atk_die_behavior = 0xFF; + } else { + auto def_range = r.def_dice_range(false); + if (def_range.first == def_range.second) { + this->def_die_behavior = def_range.first; + } else { + this->def_die_behavior = 0; + } + } +} RulesTrial::operator Rules() const { Rules ret; @@ -1513,11 +1551,11 @@ RulesTrial::operator Rules() const { ret.phase_time_limit = this->phase_time_limit; ret.allowed_cards = this->allowed_cards; if (this->atk_die_behavior) { - ret.min_dice = this->atk_die_behavior; - ret.max_dice = this->atk_die_behavior; + ret.min_dice_value = this->atk_die_behavior; + ret.max_dice_value = this->atk_die_behavior; } else { - ret.min_dice = 1; - ret.max_dice = 6; + ret.min_dice_value = 1; + ret.max_dice_value = 6; } ret.disable_deck_shuffle = this->disable_deck_shuffle; ret.disable_deck_loop = this->disable_deck_loop; @@ -1528,10 +1566,12 @@ RulesTrial::operator Rules() const { ret.dice_exchange_mode = this->dice_exchange_mode; ret.disable_dice_boost = 0; if (this->def_die_behavior) { - ret.def_dice_range = (this->def_die_behavior << 4) | this->def_die_behavior; + ret.def_dice_value_range = (this->def_die_behavior << 4) | this->def_die_behavior; } else { - ret.def_dice_range = 0x16; + ret.def_dice_value_range = 0x16; } + ret.atk_dice_value_range_2v1 = 0x00; + ret.def_dice_value_range_2v1 = 0x00; ret.unused.clear(0); return ret; } @@ -2079,82 +2119,48 @@ bool Rules::check_and_reset_invalid_fields() { this->allowed_cards = AllowedCards::ALL; ret = true; } - if (this->min_dice > 9) { - this->min_dice = 0; + if (this->min_dice_value > 9) { + this->min_dice_value = 0; ret = true; } - if (this->max_dice > 9) { - this->max_dice = 0; + if (this->max_dice_value > 9) { + this->max_dice_value = 0; ret = true; } - if ((this->min_dice != 0) && (this->max_dice != 0) && (this->max_dice < this->min_dice)) { - uint8_t t = this->min_dice; - this->min_dice = this->max_dice; - this->max_dice = t; + if ((this->min_dice_value != 0) && (this->max_dice_value != 0) && (this->max_dice_value < this->min_dice_value)) { + uint8_t t = this->min_dice_value; + this->min_dice_value = this->max_dice_value; + this->max_dice_value = t; ret = true; } - uint8_t min_def_dice = this->min_def_dice(); - uint8_t max_def_dice = this->max_def_dice(); - if (min_def_dice > 9) { - min_def_dice = 0; - ret = true; - } - if (max_def_dice > 9) { - max_def_dice = 0; - ret = true; - } - if ((min_def_dice != 0) && (max_def_dice != 0) && (max_def_dice < min_def_dice)) { - uint8_t t = min_def_dice; - min_def_dice = max_def_dice; - max_def_dice = t; - ret = true; - } - this->def_dice_range = ((min_def_dice << 4) & 0xF0) | (max_def_dice & 0x0F); - if (this->disable_deck_shuffle > 1) { - this->disable_deck_shuffle = 0; - ret = true; - } + // These ranges are a newserv-specific extension and are not part of the + // original implementation. + auto check_compressed_dice_range = +[](uint8_t* range) -> bool { + bool ret = false; + uint8_t min_dice = ((*range) >> 4) & 0x0F; + uint8_t max_dice = (*range) & 0x0F; + if (min_dice > 9) { + min_dice = 0; + ret = true; + } + if (max_dice > 9) { + max_dice = 0; + ret = true; + } + if ((min_dice != 0) && (max_dice != 0) && (max_dice < min_dice)) { + uint8_t t = min_dice; + min_dice = max_dice; + max_dice = t; + ret = true; + } + *range = ((min_dice << 4) & 0xF0) | (max_dice & 0x0F); + return ret; + }; + ret |= check_compressed_dice_range(&this->def_dice_value_range); + ret |= check_compressed_dice_range(&this->atk_dice_value_range_2v1); + ret |= check_compressed_dice_range(&this->def_dice_value_range_2v1); - uint8_t min_atk_dice_2v1 = this->min_atk_dice_2v1(); - uint8_t max_atk_dice_2v1 = this->max_atk_dice_2v1(); - if (min_atk_dice_2v1 > 9) { - min_atk_dice_2v1 = 0; - ret = true; - } - if (max_atk_dice_2v1 > 9) { - max_atk_dice_2v1 = 0; - ret = true; - } - if ((min_atk_dice_2v1 != 0) && (max_atk_dice_2v1 != 0) && (max_atk_dice_2v1 < min_atk_dice_2v1)) { - uint8_t t = min_atk_dice_2v1; - min_atk_dice_2v1 = max_atk_dice_2v1; - max_atk_dice_2v1 = t; - ret = true; - } - this->def_dice_range = ((min_atk_dice_2v1 << 4) & 0xF0) | (max_atk_dice_2v1 & 0x0F); - if (this->disable_deck_shuffle > 1) { - this->disable_deck_shuffle = 0; - ret = true; - } - - uint8_t min_def_dice_2v1 = this->min_def_dice_2v1(); - uint8_t max_def_dice_2v1 = this->max_def_dice_2v1(); - if (min_def_dice_2v1 > 9) { - min_def_dice_2v1 = 0; - ret = true; - } - if (max_def_dice_2v1 > 9) { - max_def_dice_2v1 = 0; - ret = true; - } - if ((min_def_dice_2v1 != 0) && (max_def_dice_2v1 != 0) && (max_def_dice_2v1 < min_def_dice_2v1)) { - uint8_t t = min_def_dice_2v1; - min_def_dice_2v1 = max_def_dice_2v1; - max_def_dice_2v1 = t; - ret = true; - } - this->def_dice_range = ((min_def_dice_2v1 << 4) & 0xF0) | (max_def_dice_2v1 & 0x0F); if (this->disable_deck_shuffle > 1) { this->disable_deck_shuffle = 0; ret = true; @@ -2184,32 +2190,16 @@ bool Rules::check_and_reset_invalid_fields() { this->disable_dice_boost = 0; ret = true; } - if ((this->max_dice != 0) && (this->max_dice < 3)) { - this->disable_dice_boost = 1; - ret = true; - } + // Due to newserv's custom range overrides, it doesn't make sense to disable + // Dice Boost for everyone based on the Rules struct. Instead, we skip setting + // the flag at roll time. + // if ((this->max_dice_value != 0) && (this->max_dice_value < 3)) { + // this->disable_dice_boost = 1; + // ret = true; + // } return ret; } -uint8_t Rules::min_def_dice() const { - return (this->def_dice_range >> 4) & 0x0F; -} -uint8_t Rules::max_def_dice() const { - return this->def_dice_range & 0x0F; -} -uint8_t Rules::min_atk_dice_2v1() const { - return (this->atk_dice_range_2v1 >> 4) & 0x0F; -} -uint8_t Rules::max_atk_dice_2v1() const { - return this->atk_dice_range_2v1 & 0x0F; -} -uint8_t Rules::min_def_dice_2v1() const { - return (this->def_dice_range_2v1 >> 4) & 0x0F; -} -uint8_t Rules::max_def_dice_2v1() const { - return this->def_dice_range_2v1 & 0x0F; -} - CardIndex::CardIndex( const string& filename, const string& decompressed_filename, diff --git a/src/Episode3/DataIndexes.hh b/src/Episode3/DataIndexes.hh index f6c2ceaa..3616740e 100644 --- a/src/Episode3/DataIndexes.hh +++ b/src/Episode3/DataIndexes.hh @@ -940,8 +940,8 @@ struct Rules { /* 00 */ uint8_t overall_time_limit = 0; /* 01 */ uint8_t phase_time_limit = 0; // In seconds; 0 = unlimited /* 02 */ AllowedCards allowed_cards = AllowedCards::ALL; - /* 03 */ uint8_t min_dice = 1; // 0 = default (1) - /* 04 */ uint8_t max_dice = 6; // 0 = default (6) + /* 03 */ uint8_t min_dice_value = 1; // 0 = default (1) + /* 04 */ uint8_t max_dice_value = 6; // 0 = default (6) /* 05 */ uint8_t disable_deck_shuffle = 0; // 0 = shuffle on, 1 = off /* 06 */ uint8_t disable_deck_loop = 0; // 0 = loop on, 1 = off /* 07 */ uint8_t char_hp = 15; @@ -952,10 +952,10 @@ struct Rules { /* 0C */ uint8_t disable_dice_boost = 0; // 0 = dice boost on, 1 = off // NOTE: The following fields are unused in PSO's implementation, but newserv // uses them to implement extended rules. - /* 0D */ uint8_t def_dice_range = 0; // High 4 bits = min, low 4 = max + /* 0D */ uint8_t def_dice_value_range = 0; // High 4 bits = min, low 4 = max // These fields specify override dice ranges for the 1-player team in 2v1 - /* 0E */ uint8_t atk_dice_range_2v1 = 0; // High 4 bits = min, low 4 = max - /* 0F */ uint8_t def_dice_range_2v1 = 0; // High 4 bits = min, low 4 = max + /* 0E */ uint8_t atk_dice_value_range_2v1 = 0; // High 4 bits = min, low 4 = max + /* 0F */ uint8_t def_dice_value_range_2v1 = 0; // High 4 bits = min, low 4 = max /* 10 */ parray unused; /* 14 */ @@ -976,12 +976,8 @@ struct Rules { bool check_invalid_fields() const; bool check_and_reset_invalid_fields(); - uint8_t min_def_dice() const; - uint8_t max_def_dice() const; - uint8_t min_atk_dice_2v1() const; - uint8_t max_atk_dice_2v1() const; - uint8_t min_def_dice_2v1() const; - uint8_t max_def_dice_2v1() const; + std::pair atk_dice_range(bool is_1p_2v1) const; + std::pair def_dice_range(bool is_1p_2v1) const; std::string str() const; } __attribute__((packed)); diff --git a/src/Episode3/PlayerState.cc b/src/Episode3/PlayerState.cc index e9226210..315f8c96 100644 --- a/src/Episode3/PlayerState.cc +++ b/src/Episode3/PlayerState.cc @@ -1987,44 +1987,21 @@ void PlayerState::roll_main_dice_or_apply_after_effects() { bool is_1p_2v1 = (s->team_client_count.at(this->get_team_id()) < s->team_client_count[this->get_team_id() ^ 1]); - uint8_t min_atk_dice = (is_1p_2v1 && rules.min_atk_dice_2v1()) ? rules.min_atk_dice_2v1() : rules.min_dice; - uint8_t max_atk_dice = (is_1p_2v1 && rules.max_atk_dice_2v1()) ? rules.max_atk_dice_2v1() : rules.max_dice; - if (min_atk_dice == 0) { - min_atk_dice = 1; - } - if (max_atk_dice == 0) { - max_atk_dice = 6; - } - if (max_atk_dice < min_atk_dice) { - uint8_t t = max_atk_dice; - max_atk_dice = min_atk_dice; - min_atk_dice = t; - } - uint8_t atk_dice_range_width = (max_atk_dice - min_atk_dice) + 1; + auto atk_range = rules.atk_dice_range(is_1p_2v1); + auto def_range = rules.def_dice_range(is_1p_2v1); + + uint8_t atk_dice_range_width = (atk_range.second - atk_range.first) + 1; if (atk_dice_range_width < 2) { - this->dice_results[0] = min_atk_dice; + this->dice_results[0] = atk_range.first; } else { - this->dice_results[0] = min_atk_dice + s->get_random(atk_dice_range_width); + this->dice_results[0] = atk_range.first + s->get_random(atk_dice_range_width); } - uint8_t min_def_dice = (is_1p_2v1 && rules.min_def_dice_2v1()) ? rules.min_def_dice_2v1() : (rules.min_def_dice() ? rules.min_def_dice() : rules.min_dice); - uint8_t max_def_dice = (is_1p_2v1 && rules.max_def_dice_2v1()) ? rules.max_def_dice_2v1() : (rules.max_def_dice() ? rules.max_def_dice() : rules.max_dice); - if (min_def_dice == 0) { - min_def_dice = 1; - } - if (max_def_dice == 0) { - max_def_dice = 6; - } - if (max_def_dice < min_def_dice) { - uint8_t t = max_def_dice; - max_def_dice = min_def_dice; - min_def_dice = t; - } - uint8_t def_dice_range_width = (max_def_dice - min_def_dice) + 1; + uint8_t def_dice_range_width = (def_range.second - def_range.first) + 1; if (def_dice_range_width < 2) { - this->dice_results[1] = min_def_dice; + this->dice_results[1] = def_range.first; } else { - this->dice_results[1] = min_def_dice + s->get_random(def_dice_range_width); + this->dice_results[1] = def_range.first + s->get_random(def_dice_range_width); } bool should_exchange = false; diff --git a/src/Episode3/Server.cc b/src/Episode3/Server.cc index a70d656c..75e2de5a 100644 --- a/src/Episode3/Server.cc +++ b/src/Episode3/Server.cc @@ -1363,6 +1363,13 @@ void Server::set_battle_started() { this->send_6xB4x05(); } +bool Server::player_can_receive_dice_boost(uint8_t client_id) const { + auto ps = this->player_states[client_id]; + bool is_1p_2v1 = (this->team_client_count.at(ps->get_team_id()) < this->team_client_count[ps->get_team_id() ^ 1]); + const auto& rules = this->map_and_rules->rules; + return (rules.atk_dice_range(is_1p_2v1).second >= 3) || (rules.def_dice_range(is_1p_2v1).second >= 3); +} + void Server::set_client_id_ready_to_advance_phase(uint8_t client_id, BattlePhase battle_phase) { if (client_id >= 4) { return; @@ -1377,7 +1384,10 @@ void Server::set_client_id_ready_to_advance_phase(uint8_t client_id, BattlePhase ps->assist_flags |= AssistFlag::READY_TO_END_PHASE; ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed(); if (this->battle_phase == BattlePhase::DICE) { - if (is_nte || !(ps->assist_flags & AssistFlag::ELIGIBLE_FOR_DICE_BOOST) || this->map_and_rules->rules.disable_dice_boost) { + if (is_nte || + !(ps->assist_flags & AssistFlag::ELIGIBLE_FOR_DICE_BOOST) || + this->map_and_rules->rules.disable_dice_boost || + !this->player_can_receive_dice_boost(client_id)) { ps->assist_flags &= (~AssistFlag::ELIGIBLE_FOR_DICE_BOOST); ps->roll_main_dice_or_apply_after_effects(); if (!is_nte && (ps->get_atk_points() < 3) && (ps->get_def_points() < 3)) { @@ -2149,9 +2159,13 @@ void Server::handle_CAx13_update_map_during_setup_t(shared_ptr, const st // newserv's extended rules are stored in unused parts of the Rules struct, // and clients will probably overwrite them with zeroes if we allow them to. // So, we preserve the extended rules manually here. - uint8_t def_dice_range = this->map_and_rules->rules.def_dice_range; + uint8_t def_dice_value_range = this->map_and_rules->rules.def_dice_value_range; + uint8_t atk_dice_value_range_2v1 = this->map_and_rules->rules.atk_dice_value_range_2v1; + uint8_t def_dice_value_range_2v1 = this->map_and_rules->rules.def_dice_value_range_2v1; *this->map_and_rules = in_cmd.map_and_rules_state; - this->map_and_rules->rules.def_dice_range = def_dice_range; + this->map_and_rules->rules.def_dice_value_range = def_dice_value_range; + this->map_and_rules->rules.atk_dice_value_range_2v1 = atk_dice_value_range_2v1; + this->map_and_rules->rules.def_dice_value_range_2v1 = def_dice_value_range_2v1; // If this match is part of a tournament, ignore the rules sent by the // client and use the tournament rules instead. diff --git a/src/Episode3/Server.hh b/src/Episode3/Server.hh index 78ae15d8..41701368 100644 --- a/src/Episode3/Server.hh +++ b/src/Episode3/Server.hh @@ -205,6 +205,7 @@ public: void send_set_card_updates_and_6xB4x04_if_needed(); void set_battle_ended(); void set_battle_started(); + bool player_can_receive_dice_boost(uint8_t client_id) const; void set_client_id_ready_to_advance_phase(uint8_t client_id, BattlePhase battle_phase); void set_phase_after(); void move_phase_before(); diff --git a/src/ServerShell.cc b/src/ServerShell.cc index d32b2a12..2c78d803 100644 --- a/src/ServerShell.cc +++ b/src/ServerShell.cc @@ -204,9 +204,13 @@ Server commands:\n\ shuffle: Shuffle entries when starting the tournament\n\ resize: If the tournament is less than half full when it starts, reduce\n\ the number of rounds to fit the existing entries\n\ - dice=MIN-MAX: Set minimum and maximum dice rolls\n\ - dice=MIN-MAX:MIN-MAX: Set minimum and maximum dice rolls for ATK and DEF\n\ - dice separately\n\ + dice=A-B: Set minimum and maximum dice rolls\n\ + dice=A-B:C-D: Set minimum and maximum dice rolls for ATK dice (A-B) and\n\ + DEF dice (C-D) separately\n\ + dice=A-B:C-D:E-F: Set minimum and maximum dice rolls for ATK dice, DEF\n\ + dice, and solo vs. 2P ATK and DEF dice (E-F) separately\n\ + dice=A-B:C-D:E-F:G-H: Set minimum and maximum dice rolls for ATK dice,\n\ + DEF dice, solo vs. 2P ATK (E-F) and DEF (G-H) dice separately\n\ overall-time-limit=N: Set battle time limit (in multiples of 5 minutes)\n\ phase-time-limit=N: Set phase time limit (in seconds)\n\ allowed-cards=ALL/N/NR/NRS: Set ranks of allowed cards\n\ @@ -490,24 +494,48 @@ Proxy session commands:\n\ } else if (token == "resize") { flags |= Episode3::Tournament::Flag::RESIZE_ON_START; } else if (starts_with(token, "dice=")) { - auto subtokens = split(token.substr(5), ':'); - if (subtokens.size() == 1) { - rules.def_dice_range = 0x00; - } else if (subtokens.size() == 2) { - auto subsubtokens = split(subtokens[1], '-'); - if (subsubtokens.size() != 2) { - throw runtime_error("dice option must be of the form dice=A-B or dice=A-B:C-D"); + auto parse_range_c = +[](const string& s) -> uint8_t { + auto tokens = split(s, '-'); + if (tokens.size() != 2) { + throw runtime_error("dice spec must be of the form MIN-MAX"); + } + return (stoul(tokens[0]) << 4) | (stoul(tokens[1]) & 0x0F); + }; + auto parse_range_p = +[](const string& s) -> pair { + auto tokens = split(s, '-'); + if (tokens.size() != 2) { + throw runtime_error("dice spec must be of the form MIN-MAX"); + } + return make_pair(stoul(tokens[0]), stoul(tokens[1])); + }; + + auto subtokens = split(token.substr(5), ':'); + if (subtokens.size() < 1) { + throw runtime_error("no dice ranges specified in dice= option"); + } + auto atk_range = parse_range_p(tokens[0]); + rules.min_dice_value = atk_range.first; + rules.max_dice_value = atk_range.second; + if (subtokens.size() >= 2) { + rules.def_dice_value_range = parse_range_c(tokens[1]); + if (subtokens.size() >= 3) { + rules.atk_dice_value_range_2v1 = parse_range_c(tokens[2]); + if (subtokens.size() == 3) { + rules.def_dice_value_range_2v1 = rules.atk_dice_value_range_2v1; + } else if (subtokens.size() == 4) { + rules.def_dice_value_range_2v1 = parse_range_c(tokens[3]); + } else { + throw runtime_error("too many range specs given"); + } + } else { + rules.atk_dice_value_range_2v1 = 0; + rules.def_dice_value_range_2v1 = 0; } - rules.def_dice_range = ((stoul(subsubtokens[0]) << 4) & 0xF0) | (stoul(subsubtokens[1]) & 0x0F); } else { - throw runtime_error("dice option must be of the form dice=A-B or dice=A-B:C-D"); + rules.def_dice_value_range = 0; + rules.atk_dice_value_range_2v1 = 0; + rules.def_dice_value_range_2v1 = 0; } - auto subsubtokens = split(subtokens[0], '-'); - if (subsubtokens.size() != 2) { - throw runtime_error("dice option must be of the form dice=A-B or dice=A-B:C-D"); - } - rules.min_dice = stoul(subsubtokens[0]); - rules.max_dice = stoul(subsubtokens[1]); } else if (starts_with(token, "overall-time-limit=")) { uint32_t limit = stoul(token.substr(19)); if (limit > 600) {