From 66f584d4752b24cf65aeaa7e6aa6bd5d100aa0e9 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Tue, 5 Sep 2023 20:41:39 -0700 Subject: [PATCH] fix condition apply using incorrect criterion for non-item checks --- src/Episode3/CardSpecial.cc | 57 +++++++++++++++++++++++++++++++++---- src/Episode3/CardSpecial.hh | 2 ++ src/Episode3/DataIndexes.cc | 6 ++-- src/Episode3/PlayerState.cc | 4 +-- src/Episode3/RulerServer.cc | 13 ++++++++- src/Episode3/Server.cc | 4 +-- 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/Episode3/CardSpecial.cc b/src/Episode3/CardSpecial.cc index edd17dcd..d2d9a172 100644 --- a/src/Episode3/CardSpecial.cc +++ b/src/Episode3/CardSpecial.cc @@ -6,6 +6,25 @@ using namespace std; namespace Episode3 { +static uint16_t ref_for_card(shared_ptr card) { + if (card) { + return card->get_card_ref(); + } else { + return 0xFFFF; + } +} + +static string refs_str_for_cards_vector(const vector>& cards) { + string ret; + for (const auto& card : cards) { + if (!ret.empty()) { + ret += ", "; + } + ret += string_printf("%04hX", ref_for_card(card)); + } + return ret; +} + CardSpecial::DiceRoll::DiceRoll() { this->clear(); } @@ -89,6 +108,14 @@ shared_ptr CardSpecial::server() const { return s; } +ATTR_PRINTF(2, 3) +void CardSpecial::debug_log(const char* fmt, ...) const { + va_list va; + va_start(va, fmt); + this->server()->base()->log.debug_v(fmt, va); + va_end(va); +} + void CardSpecial::adjust_attack_damage_due_to_conditions( shared_ptr target_card, int16_t* inout_damage, uint16_t attacker_card_ref) { shared_ptr attacker_card = this->server()->card_for_set_card_ref(attacker_card_ref); @@ -252,8 +279,8 @@ bool CardSpecial::apply_defense_condition( if ((when == 2) && (defender_cond->type == ConditionType::GUOM) && (flags & 4)) { CardShortStatus stat = defender_card->get_short_status(); if (stat.card_flags & 4) { - this->server()->base()->log.debug("(when=2) @%04hX clearing GUOM from @%04hX", - attacker_card_ref, defender_card->get_card_ref()); + this->debug_log("(when=2) @%04hX clearing GUOM from @%04hX", + attacker_card_ref, ref_for_card(defender_card)); G_ApplyConditionEffect_GC_Ep3_6xB4x06 cmd; cmd.effect.flags = 0x04; cmd.effect.attacker_card_ref = this->send_6xB4x06_if_card_ref_invalid(attacker_card_ref, 0x0E); @@ -273,8 +300,7 @@ bool CardSpecial::apply_defense_condition( (defender_cond->type == ConditionType::ACID)) { int16_t hp = defender_card->get_current_hp(); if (hp > 0) { - this->server()->base()->log.debug("(when=2) @%04hX has ACID; removing 1 HP", - defender_cond->card_ref.load()); + this->debug_log("(when=2) @%04hX has ACID; removing 1 HP", defender_cond->card_ref.load()); this->send_6xB4x06_for_stat_delta( defender_card, defender_cond->card_ref, 0x20, -1, 0, 1); defender_card->set_current_hp(hp - 1); @@ -2464,6 +2490,8 @@ vector> CardSpecial::get_targeted_cards_for_condition( const ActionState& as, int16_t p_target_type, bool apply_usability_filters) const { + this->debug_log("get_targeted_cards_for_condition(card_ref=%04hX, def_effect_index=%02hhX, setter_card_ref=%04hX, as, p_target_type=%hd, apply_usability_filters=%s)", card_ref, def_effect_index, setter_card_ref, p_target_type, apply_usability_filters ? "true" : "false"); + vector> ret; uint8_t client_id = client_id_for_card_ref(card_ref); @@ -2471,10 +2499,12 @@ vector> CardSpecial::get_targeted_cards_for_condition( if (!card1) { card1 = this->server()->card_for_set_card_ref(setter_card_ref); } + this->debug_log("get_targeted_cards_for_condition: card1=%04hX", ref_for_card(card1)); auto card2 = this->server()->card_for_set_card_ref((as.attacker_card_ref == 0xFFFF) ? as.original_attacker_card_ref : as.attacker_card_ref); + this->debug_log("get_targeted_cards_for_condition: card2=%04hX", ref_for_card(card2)); Location card1_loc; if (!card1) { @@ -2483,11 +2513,15 @@ vector> CardSpecial::get_targeted_cards_for_condition( card1_loc.direction = Direction::RIGHT; } else { this->get_card1_loc_with_card2_opposite_direction(&card1_loc, card1, card2); + + string card1_loc_str = card1_loc.str(); + this->debug_log("get_targeted_cards_for_condition: card1_loc=%s", card1_loc_str.c_str()); } AttackMedium attack_medium = card2 ? card2->action_chain.chain.attack_medium : AttackMedium::UNKNOWN; + this->debug_log("get_targeted_cards_for_condition: attack_medium=%s", name_for_attack_medium(attack_medium)); auto add_card_refs = [&](const vector& result_card_refs) -> void { for (uint16_t result_card_ref : result_card_refs) { @@ -2503,7 +2537,10 @@ vector> CardSpecial::get_targeted_cards_for_condition( case 5: { auto result_card = this->server()->card_for_set_card_ref(setter_card_ref); if (result_card) { + this->debug_log("get_targeted_cards_for_condition: (p01/p05) result_card=%04hX", ref_for_card(result_card)); ret.emplace_back(result_card); + } else { + this->debug_log("get_targeted_cards_for_condition: (p01/p05) result_card=null"); } break; } @@ -2595,10 +2632,15 @@ vector> CardSpecial::get_targeted_cards_for_condition( ret = this->get_all_set_cards(); ret = this->filter_cards_by_range(ret, card1, card1_loc, card2); break; - case 16: + case 16: { ret = this->find_cards_in_hp_range(8, 1000); + string range_refs_str = refs_str_for_cards_vector(ret); + this->debug_log("get_targeted_cards_for_condition: (p16) candidate cards = [%s]", range_refs_str.c_str()); ret = this->filter_cards_by_range(ret, card1, card1_loc, card2); + range_refs_str = refs_str_for_cards_vector(ret); + this->debug_log("get_targeted_cards_for_condition: (p16) filtered cards = [%s]", range_refs_str.c_str()); break; + } case 17: { auto result_card = this->server()->card_for_set_card_ref(card_ref); if (result_card) { @@ -3002,6 +3044,9 @@ vector> CardSpecial::get_targeted_cards_for_condition( if (this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( card_ref, setter_card_ref, c->get_card_ref(), def_effect_index, attack_medium)) { filtered_ret.emplace_back(c); + this->debug_log("get_targeted_cards_for_condition: usability filter: kept card %04hX", ref_for_card(c)); + } else { + this->debug_log("get_targeted_cards_for_condition: usability filter: removed card %04hX", ref_for_card(c)); } } return filtered_ret; @@ -3517,7 +3562,7 @@ void CardSpecial::unknown_8024C2B0( { string as_s = as.str(); string eff_s = card_effect.str(); - this->server()->base()->log.debug("(when=%" PRIu32 ") set=@%04hX sc=@%04hX as=%s att=@%04hX eff=%s", + this->debug_log("(when=%" PRIu32 ") set=@%04hX sc=@%04hX as=%s att=@%04hX eff=%s", when, set_card_ref, sc_card_ref, as_s.c_str(), as_attacker_card_ref, eff_s.c_str()); } diff --git a/src/Episode3/CardSpecial.hh b/src/Episode3/CardSpecial.hh index 9c4e2faa..4742d0f7 100644 --- a/src/Episode3/CardSpecial.hh +++ b/src/Episode3/CardSpecial.hh @@ -95,6 +95,8 @@ public: std::shared_ptr server(); std::shared_ptr server() const; + void debug_log(const char* fmt, ...) const ATTR_PRINTF(2, 3); + void adjust_attack_damage_due_to_conditions( std::shared_ptr target_card, int16_t* inout_damage, uint16_t attacker_card_ref); void adjust_dice_boost_if_team_has_condition_52( diff --git a/src/Episode3/DataIndexes.cc b/src/Episode3/DataIndexes.cc index 257ebd86..5c6f4f6e 100644 --- a/src/Episode3/DataIndexes.cc +++ b/src/Episode3/DataIndexes.cc @@ -418,9 +418,9 @@ static const vector description_for_p_target({ /* p11 */ "All ally FCs", // TODO: how is this different from p10? /* p12 */ "All non-aerial FCs on both teams", /* p13 */ "All FCs on both teams that are Frozen", - /* p14 */ "All FCs on both teams that have <= 3 HP", - /* p15 */ "All FCs except SCs", // TODO: used during attacks only? - /* p16 */ "All FCs except SCs", // TODO: used during attacks only? how is this different from p15? + /* p14 */ "All FCs on both teams with <= 3 HP", + /* p15 */ "All FCs on both teams", + /* p16 */ "All FCs on both teams with >= 8 HP", /* p17 */ "This card", /* p18 */ "SC who equipped this card", /* p19 */ "Unknown: p19", // Unused diff --git a/src/Episode3/PlayerState.cc b/src/Episode3/PlayerState.cc index 7a195aa2..b21dea14 100644 --- a/src/Episode3/PlayerState.cc +++ b/src/Episode3/PlayerState.cc @@ -1777,11 +1777,11 @@ void PlayerState::roll_main_dice() { if (!should_exchange) { this->atk_points = (short)(char)this->dice_results[0]; this->def_points = (short)(char)this->dice_results[1]; - this->assist_flags = this->assist_flags & 0xFFFFFFFD; + this->assist_flags &= 0xFFFFFFFD; } else { this->atk_points = (short)(char)this->dice_results[1]; this->def_points = (short)(char)this->dice_results[0]; - this->assist_flags = this->assist_flags | 2; + this->assist_flags |= 2; } this->atk_points = this->atk_points + this->server()->card_special->client_has_atk_dice_boost_condition(this->client_id); diff --git a/src/Episode3/RulerServer.cc b/src/Episode3/RulerServer.cc index dd9abb30..762b94f1 100644 --- a/src/Episode3/RulerServer.cc +++ b/src/Episode3/RulerServer.cc @@ -922,25 +922,31 @@ bool RulerServer::check_usability_or_condition_apply( attack_medium = AttackMedium::UNKNOWN; } + this->server()->base()->log.debug("check_usability_or_condition_apply(client_id1=%02hhX, card_id1=%04hX, client_id2=%02hhX, card_id2=%04hX, card_id3=%04hX, def_effect_index=%02hhX, is_item_usability_check=%s, attack_medium=%s)", client_id1, card_id1, client_id2, card_id2, card_id3, def_effect_index, is_item_usability_check ? "true" : "false", name_for_attack_medium(attack_medium)); + auto ce1 = this->definition_for_card_id(card_id1); auto ce2 = this->definition_for_card_id(card_id2); auto ce3 = this->definition_for_card_id(card_id3); if (!ce1) { + this->server()->base()->log.debug("check_usability_or_condition_apply: ce1 missing"); return false; } if ((ce1->def.type == CardType::ITEM) && this->card_id_is_boss_sc(card_id2)) { + this->server()->base()->log.debug("check_usability_or_condition_apply: ce1 is item and card_id2 is boss sc"); return false; } CriterionCode criterion_code; - if (def_effect_index & 0xFF) { + if (def_effect_index == 0xFF) { criterion_code = ce1->def.usable_criterion; } else { if (def_effect_index > 2) { + this->server()->base()->log.debug("check_usability_or_condition_apply: invalid def_effect_index"); return false; } criterion_code = ce1->def.effects[def_effect_index].apply_criterion; } + this->server()->base()->log.debug("check_usability_or_condition_apply: criterion_code=%s", name_for_criterion_code(criterion_code)); // For item usability checks, prevent criteria that depend on player // positioning/team setup @@ -951,6 +957,7 @@ bool RulerServer::check_usability_or_condition_apply( (criterion_code == CriterionCode::UNKNOWN_07) || (criterion_code == CriterionCode::NOT_SC) || (criterion_code == CriterionCode::SC))) { + this->server()->base()->log.debug("check_usability_or_condition_apply: criterion is forbidden"); criterion_code = CriterionCode::NONE; } @@ -1000,9 +1007,12 @@ bool RulerServer::check_usability_or_condition_apply( break; case CriterionCode::UNKNOWN_07: // Like NOT_SC, but for ce3 instead of ce2 + this->server()->base()->log.debug("check_usability_or_condition_apply: UNKNOWN_07: ce3 type is %s", ce3 ? name_for_card_type(ce3->def.type) : "missing"); if (ce3 && (ce3->def.type != CardType::HUNTERS_SC) && (ce3->def.type != CardType::ARKZ_SC)) { + this->server()->base()->log.debug("check_usability_or_condition_apply: UNKNOWN_07: returned %s", ret ? "true" : "false"); return ret; } + this->server()->base()->log.debug("check_usability_or_condition_apply: UNKNOWN_07: did not pass"); break; case CriterionCode::NOT_SC: if (ce2 && (ce2->def.type != CardType::HUNTERS_SC) && (ce2->def.type != CardType::ARKZ_SC)) { @@ -1323,6 +1333,7 @@ bool RulerServer::check_usability_or_condition_apply( } } + this->server()->base()->log.debug("check_usability_or_condition_apply: default return (false)"); return false; } diff --git a/src/Episode3/Server.cc b/src/Episode3/Server.cc index a00956d0..0ecceb78 100644 --- a/src/Episode3/Server.cc +++ b/src/Episode3/Server.cc @@ -560,7 +560,7 @@ void Server::clear_player_flags_after_dice_phase() { for (size_t z = 0; z < 4; z++) { auto ps = this->player_states[z]; if (ps) { - ps->assist_flags = ps->assist_flags & 0xFFFFDFFE; + ps->assist_flags &= 0xFFFFDFFE; ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed(); } } @@ -1203,7 +1203,7 @@ void Server::set_client_id_ready_to_advance_phase(uint8_t client_id) { break; } } - ps->assist_flags = ps->assist_flags & 0xFFFF7FFF; + ps->assist_flags &= 0xFFFF7FFF; } ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed(); }