diff --git a/src/Episode3/Card.cc b/src/Episode3/Card.cc index 84d7c207..a7e304d5 100644 --- a/src/Episode3/Card.cc +++ b/src/Episode3/Card.cc @@ -107,6 +107,7 @@ ssize_t Card::apply_abnormal_condition( int16_t value, int8_t dice_roll_value, int8_t random_percent) { + auto log = this->server()->log_stack(string_printf("apply_abnormal_condition(%02hhX, @%04X, @%04X, %hd, %hhd, %hhd): ", def_effect_index, target_card_ref, sc_card_ref, value, dice_roll_value, random_percent)); ssize_t existing_cond_index; for (size_t z = 0; z < this->action_chain.conditions.size(); z++) { @@ -132,9 +133,13 @@ ssize_t Card::apply_abnormal_condition( break; } } + log.debug("existing_cond_index < 0 (new condition) => cond_index = %zd", cond_index); + } else { + log.debug("existing_cond_index = %zd (existing condition)", existing_cond_index); } if (cond_index < 0) { + log.debug("no space for condition"); return -1; } @@ -142,10 +147,10 @@ ssize_t Card::apply_abnormal_condition( auto& cond = this->action_chain.conditions[cond_index]; if ((eff.type == ConditionType::MV_BONUS) && (cond.type == ConditionType::MV_BONUS)) { existing_cond_value = clamp(cond.value, -99, 99); + log.debug("MV_BONUS combines => existing_cond_value = %hd", existing_cond_value); } - this->server()->card_special->apply_stat_deltas_to_card_from_condition_and_clear_cond( - cond, this->shared_from_this()); + this->server()->card_special->apply_stat_deltas_to_card_from_condition_and_clear_cond(cond, this->shared_from_this()); cond.type = eff.type; cond.card_ref = target_card_ref; cond.condition_giver_card_ref = sc_card_ref; @@ -178,7 +183,19 @@ ssize_t Card::apply_abnormal_condition( cond.remaining_turns = atoi(&eff.arg1[1]); } + string cond_str = cond.str(); + log.debug("wrote condition %zd => %s", cond_index, cond_str.c_str()); + this->server()->card_special->update_condition_orders(this->shared_from_this()); + + for (size_t z = 0; z < this->action_chain.conditions.size(); z++) { + if (this->action_chain.conditions[z].type == ConditionType::NONE) { + continue; + } + string cond_str = cond.str(); + log.debug("sorted conditions: [%zu] => %s", z, cond_str.c_str()); + } + return cond_index; } @@ -227,9 +244,12 @@ void Card::commit_attack( G_ApplyConditionEffect_GC_Ep3_6xB4x06* cmd, size_t strike_number, int16_t* out_effective_damage) { + auto log = this->server()->log_stack(string_printf("commit_attack(@%04hX #%04hX, @%04hX #%04hX => %hd (str%zu)): ", this->get_card_ref(), this->get_card_id(), attacker_card->get_card_ref(), attacker_card->get_card_id(), damage, strike_number)); + int16_t effective_damage = damage; this->server()->card_special->adjust_attack_damage_due_to_conditions( this->shared_from_this(), &effective_damage, attacker_card->get_card_ref()); + log.debug("adjusted damage = %hd", effective_damage); size_t num_assists = this->server()->assist_server->compute_num_assist_effects_for_client(this->client_id); for (size_t z = 0; z < num_assists; z++) { @@ -247,29 +267,36 @@ void Card::commit_attack( } } } + log.debug("after assists = %hd", effective_damage); if (this->action_metadata.check_flag(0x10)) { effective_damage = 0; + log.debug("flag 0x10 => effective damage = %hd", effective_damage); } auto attacker_ps = attacker_card->player_state(); attacker_ps->stats.damage_given += effective_damage; this->player_state()->stats.damage_taken += effective_damage; + log.debug("updated stats"); - this->current_hp = clamp( - this->current_hp - effective_damage, 0, this->max_hp); + this->current_hp = clamp(this->current_hp - effective_damage, 0, this->max_hp); + log.debug("hp set to %hd", this->current_hp); if ((effective_damage > 0) && (attacker_ps->stats.max_attack_damage < effective_damage)) { attacker_ps->stats.max_attack_damage = effective_damage; + log.debug("attacker new max damage %hd", effective_damage); } this->last_attack_final_damage = effective_damage; + log.debug("last attack final damage = %hd", effective_damage); if (effective_damage > 0) { this->card_flags = this->card_flags | 4; + log.debug("set flag 4"); } if (this->current_hp < 1) { this->destroy_set_card(attacker_card); + log.debug("card destroyed"); } G_ApplyConditionEffect_GC_Ep3_6xB4x06 cmd_to_send; @@ -420,12 +447,18 @@ void Card::execute_attack(shared_ptr attacker_card) { return; } + auto log = this->server()->log_stack(string_printf("execute_attack(@%04X #%04X, @%04X #%04X): ", this->get_card_ref(), this->get_card_id(), attacker_card->get_card_ref(), attacker_card->get_card_id())); + this->card_flags = this->card_flags & 0xFFFFFFF3; int16_t attack_ap = this->action_metadata.attack_bonus; int16_t attack_tp = 0; int16_t defense_power = this->compute_defense_power_for_attacker_card(attacker_card); + log.debug("ap=%hd, tp=%hd, defense=%hd", attack_ap, attack_tp, defense_power); if ((attack_ap == 0) && !this->action_metadata.check_flag(0x20)) { + log.debug("ap == 0 and flag 0x20 not set"); return; + } else { + log.debug("ap != 0 or flag 0x20 set; continuing..."); } G_ApplyConditionEffect_GC_Ep3_6xB4x06 cmd; @@ -447,12 +480,15 @@ void Card::execute_attack(shared_ptr attacker_card) { uint16_t attacker_card_ref = attacker_card->get_card_ref(); this->server()->card_special->compute_attack_ap( this->shared_from_this(), &attack_ap, attacker_card_ref); + log.debug("computed ap %hd", attack_ap); this->apply_ap_adjust_assists_to_attack(attacker_card, &attack_ap, &defense_power); + log.debug("assist adjusts ap=%hd, defense=%hd", attack_ap, defense_power); int16_t raw_damage = attack_ap - defense_power; // Note: The original code uses attack_tp here, even though it is always // zero at this point int16_t preliminary_damage = max(raw_damage, 0) - attack_tp; this->last_attack_preliminary_damage = preliminary_damage; + log.debug("raw_damage=%hd, preliminary_damange=%hd", raw_damage, preliminary_damage); uint16_t targeted_card_ref = this->get_card_ref(); uint32_t unknown_a9 = 0; @@ -460,15 +496,20 @@ void Card::execute_attack(shared_ptr attacker_card) { targeted_card_ref, 1, 0, attacker_card_ref, 0xFFFF, 0, &unknown_a9, 0xFF, 0, 0xFFFF); if (!target) { target = this->shared_from_this(); + log.debug("target is not replaced"); + } else { + log.debug("target replaced with @%04hX #%04hX", target->get_card_ref(), target->get_card_id()); } if (unknown_a9 != 0) { preliminary_damage = 0; + log.debug("a9 nonzero; preliminary_damage = 0"); } if (!(this->card_flags & 2) && (!attacker_card || !(attacker_card->card_flags & 2))) { this->server()->card_special->check_for_defense_interference( attacker_card, this->shared_from_this(), &preliminary_damage); + log.debug("checked for defense interference"); } cmd.effect.current_hp = min(attack_ap, 99); @@ -476,6 +517,7 @@ void Card::execute_attack(shared_ptr attacker_card) { cmd.effect.tp = attack_tp; this->player_state()->stats.num_attacks_taken++; if (!(target->card_flags & 2)) { + log.debug("flag 2 not set"); for (size_t strike_num = 0; strike_num < attacker_card->action_chain.chain.strike_count; strike_num++) { int16_t final_effective_damage = 0; target->commit_attack( @@ -487,9 +529,11 @@ void Card::execute_attack(shared_ptr attacker_card) { 0, this->current_defense_power - final_effective_damage); } } else { + log.debug("flag 2 set; committing zero-damage attack"); target->commit_attack(0, attacker_card, &cmd, 0, nullptr); } if (this != target.get()) { + log.debug("target was replaced; committing zero-damage attack on original card"); this->commit_attack(0, attacker_card, &cmd, 0, nullptr); } @@ -515,6 +559,10 @@ uint16_t Card::get_card_ref() const { return this->card_ref; } +uint16_t Card::get_card_id() const { + return this->get_definition()->def.card_id; +} + uint8_t Card::get_client_id() const { return this->client_id; } @@ -724,24 +772,34 @@ void Card::clear_action_chain_and_metadata_and_most_flags() { void Card::compute_action_chain_results( bool apply_action_conditions, bool ignore_this_card_ap_tp) { + auto log = this->server()->log_stack(string_printf("compute_action_chain_results(@%04hX #%04hX): ", this->get_card_ref(), this->get_card_id())); this->action_chain.compute_attack_medium(this->server()); this->action_chain.chain.strike_count = 1; this->action_chain.chain.ap_effect_bonus = 0; this->action_chain.chain.tp_effect_bonus = 0; + log.debug("(initial) medium=%s, strike_count=%hhu, ap_effect_bonus=%hhd, tp_effect_bonus=%hhd", + name_for_attack_medium(this->action_chain.chain.attack_medium), + this->action_chain.chain.strike_count, + this->action_chain.chain.ap_effect_bonus, + this->action_chain.chain.tp_effect_bonus); + int16_t card_ap; int16_t card_tp; auto stat_swap_type = this->server()->card_special->compute_stat_swap_type(this->shared_from_this()); + log.debug("stat_swap_type = %zu (0=none, 1=a/t, 2=a/h)", static_cast(stat_swap_type)); this->server()->card_special->get_effective_ap_tp( stat_swap_type, &card_ap, &card_tp, this->get_current_hp(), this->ap, this->tp); + log.debug("card_ap = %hd, card_tp = %hd", card_ap, card_tp); - int16_t effective_tp = card_tp; int16_t effective_ap = card_ap; + int16_t effective_tp = card_tp; for (size_t z = 0; (!ignore_this_card_ap_tp && (z < 8) && (z < this->action_chain.chain.attack_action_card_ref_count)); z++) { auto ce = this->server()->definition_for_card_ref(this->action_chain.chain.attack_action_card_refs[z]); if (ce) { effective_ap += ce->def.ap.stat; effective_tp += ce->def.tp.stat; + log.debug("(action card @%04hX) updated effective_ap = %hd, effective_tp = %hd", this->action_chain.chain.attack_action_card_refs[z].load(), effective_ap, effective_tp); } } @@ -754,6 +812,8 @@ void Card::compute_action_chain_results( stat_swap_type, &card_ap, &card_tp, card->get_current_hp(), card->ap, card->tp); effective_ap += card_ap; effective_tp += card_tp; + log.debug("(mag card set_index %zu @%04hX) updated effective_ap = %hd, effective_tp = %hd", + set_index, card->get_card_ref(), effective_ap, effective_tp); } } } @@ -763,18 +823,25 @@ void Card::compute_action_chain_results( sc_card->compute_action_chain_results(apply_action_conditions, true); effective_ap += sc_card->action_chain.chain.effective_ap + sc_card->action_chain.chain.ap_effect_bonus; effective_tp += sc_card->action_chain.chain.effective_tp + sc_card->action_chain.chain.tp_effect_bonus; + log.debug("(item is attacking; adding SC stats) updated effective_ap = %hd, effective_tp = %hd", + effective_ap, effective_tp); } if (!this->action_chain.check_flag(0x10)) { this->action_chain.chain.effective_ap = min(effective_ap, 99); + log.debug("set chain effective_ap = %hd", this->action_chain.chain.effective_ap); } if (!this->action_chain.check_flag(0x20)) { this->action_chain.chain.effective_tp = min(effective_tp, 99); + log.debug("set chain effective_tp = %hd", this->action_chain.chain.effective_tp); } if (apply_action_conditions) { this->server()->card_special->apply_action_conditions( 3, this->shared_from_this(), this->shared_from_this(), 1, nullptr); + log.debug("applied action conditions (1)"); + } else { + log.debug("skipped applying action conditions (1)"); } size_t num_assists = this->server()->assist_server->compute_num_assist_effects_for_client(this->client_id); @@ -887,18 +954,27 @@ void Card::compute_action_chain_results( int16_t damage = 0; if (this->action_chain.chain.attack_medium == AttackMedium::TECH) { damage = this->action_chain.chain.effective_tp + this->action_chain.chain.tp_effect_bonus; + log.debug("(tech) damage = %hhd (eff) + %hhd (bonus) = %hd", this->action_chain.chain.effective_tp, this->action_chain.chain.tp_effect_bonus, damage); } else if (this->action_chain.chain.attack_medium == AttackMedium::PHYSICAL) { damage = this->action_chain.chain.effective_ap + this->action_chain.chain.ap_effect_bonus; + log.debug("(physical) damage = %hhd (eff) + %hhd (bonus) = %hd", this->action_chain.chain.effective_ap, this->action_chain.chain.ap_effect_bonus, damage); + } else { + log.debug("(unknown attack medium) damage = 0"); } this->action_chain.chain.damage = min( damage * this->action_chain.chain.damage_multiplier, 99); + log.debug("overall chain damage = %hd (base) * %hhd (mult) = %hhd", damage, this->action_chain.chain.damage_multiplier, this->action_chain.chain.damage); if (apply_action_conditions) { this->server()->card_special->apply_action_conditions( - 3, this->shared_from_this(), this->shared_from_this(), 2, nullptr); + 0x03, this->shared_from_this(), this->shared_from_this(), 2, nullptr); + log.debug("applied action conditions (2)"); if (this->action_chain.check_flag(0x100)) { this->action_chain.chain.damage = min(this->action_chain.chain.damage + 5, 99); + log.debug("(has flag 0x100) chain damage = %hhd", this->action_chain.chain.damage); } + } else { + log.debug("applied action conditions (2)"); } num_assists = this->server()->assist_server->compute_num_assist_effects_for_client(this->get_client_id()); @@ -983,6 +1059,7 @@ void Card::unknown_80235B10() { } void Card::unknown_80236374(shared_ptr other_card, const ActionState* as) { + auto log = this->server()->log_stack(string_printf("unknown_80236374(@%04hX #%04hX, @%04hX #%04hX): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id())); auto check_card = [&](shared_ptr card) -> void { if (card) { if (!card->unknown_80236554(other_card, as)) { @@ -1112,6 +1189,16 @@ bool Card::is_guard_item() const { } bool Card::unknown_80236554(shared_ptr other_card, const ActionState* as) { + auto log = this->server()->log_stack(other_card + ? string_printf("unknown_80236554(@%04hX #%04hX, @%04hX #%04hX): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id()) + : string_printf("unknown_80236554(@%04hX #%04hX, null): ", this->get_card_ref(), this->get_card_id())); + if (as) { + string as_str = as->str(); + log.debug("as = %s", as_str.c_str()); + } else { + log.debug("as = null"); + } + bool ret = false; int16_t attack_bonus = 0; @@ -1121,6 +1208,7 @@ bool Card::unknown_80236554(shared_ptr other_card, const ActionState* as) if (other_card->action_chain.chain.target_card_refs[z] == this->get_card_ref()) { attack_bonus = other_card->action_chain.chain.damage; ret = true; + log.debug("attack_bonus = %hd (matched other_card->action_chain.chain.target_card_refs)", attack_bonus); break; } } @@ -1128,6 +1216,7 @@ bool Card::unknown_80236554(shared_ptr other_card, const ActionState* as) for (size_t z = 0; (z < 4 * 9) && (as->target_card_refs[z] != 0xFFFF); z++) { if (as->target_card_refs[z] == this->get_card_ref()) { attack_bonus = other_card->action_chain.chain.damage; + log.debug("attack_bonus = %hd (matched as->target_card_refs)", attack_bonus); ret = true; break; } @@ -1136,23 +1225,22 @@ bool Card::unknown_80236554(shared_ptr other_card, const ActionState* as) } this->action_metadata.attack_bonus = max(attack_bonus, 0); + log.debug("attack_bonus = %hhd", this->action_metadata.attack_bonus); this->last_attack_preliminary_damage = 0; this->last_attack_final_damage = 0; + log.debug("last attack damage stats cleared"); if (other_card) { - this->server()->card_special->apply_action_conditions( - 3, other_card, this->shared_from_this(), 0x20, as); - this->server()->card_special->apply_action_conditions( - 0x17, other_card, this->shared_from_this(), 0x40, as); + this->server()->card_special->apply_action_conditions(0x03, other_card, this->shared_from_this(), 0x20, as); + this->server()->card_special->apply_action_conditions(0x17, other_card, this->shared_from_this(), 0x40, as); if (other_card->action_chain.check_flag(0x20000)) { this->action_metadata.attack_bonus = 0; return ret; } } - if (!(this->card_flags & 2)) { - return ret; + if (this->card_flags & 2) { + this->action_metadata.attack_bonus = 0; } - this->action_metadata.attack_bonus = 0; return ret; } @@ -1174,8 +1262,9 @@ void Card::unknown_802362D8(shared_ptr other_card) { } } -void Card::unknown_80237734() { - if (!this->action_chain.unknown_8024DEC4()) { +void Card::apply_attack_result() { + auto log = this->server()->log_stack(string_printf("apply_attack_result(@%04hX #%04hX): ", this->get_card_ref(), this->get_card_id())); + if (!this->action_chain.can_apply_attack()) { return; } diff --git a/src/Episode3/Card.hh b/src/Episode3/Card.hh index b899d4bf..3074546d 100644 --- a/src/Episode3/Card.hh +++ b/src/Episode3/Card.hh @@ -59,6 +59,7 @@ public: uint16_t* out_value) const; std::shared_ptr get_definition() const; uint16_t get_card_ref() const; + uint16_t get_card_id() const; uint8_t get_client_id() const; uint8_t get_current_hp() const; uint8_t get_max_hp() const; @@ -89,7 +90,7 @@ public: bool is_guard_item() const; bool unknown_80236554(std::shared_ptr other_card, const ActionState* as); void unknown_802362D8(std::shared_ptr other_card); - void unknown_80237734(); + void apply_attack_result(); private: std::weak_ptr w_server; diff --git a/src/Episode3/CardSpecial.cc b/src/Episode3/CardSpecial.cc index 4334d7bd..f97244cc 100644 --- a/src/Episode3/CardSpecial.cc +++ b/src/Episode3/CardSpecial.cc @@ -21,7 +21,7 @@ static string refs_str_for_cards_vector(const vector>& cards) { if (!ret.empty()) { ret += ", "; } - ret += string_printf("%04hX", ref_for_card(card)); + ret += string_printf("@%04hX", ref_for_card(card)); } return ret; } @@ -437,8 +437,11 @@ bool CardSpecial::apply_stat_deltas_to_all_cards_from_all_conditions_with_card_r return ret; } -bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond( - Condition& cond, shared_ptr card) { +bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond(Condition& cond, shared_ptr card) { + auto log = this->server()->log_stack(string_printf("apply_stat_deltas_to_card_from_condition_and_clear_cond(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id())); + string cond_str = cond.str(); + log.debug("cond: %s", cond_str.c_str()); + ConditionType cond_type = cond.type; int16_t cond_value = clamp(cond.value, -99, 99); uint8_t cond_flags = cond.flags; @@ -450,10 +453,13 @@ bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond( if (cond_flags & 2) { int16_t ap = clamp(card->ap, -99, 99); int16_t tp = clamp(card->tp, -99, 99); + log.debug("A_T_SWAP_0C: swapping AP (%hd) and TP (%hd)", ap, tp); this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0xA0, tp - ap, 0, 0); this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0x80, ap - tp, 0, 0); card->ap = tp; card->tp = ap; + } else { + log.debug("A_T_SWAP_0C: required flag is missing"); } break; case ConditionType::A_H_SWAP: @@ -461,12 +467,17 @@ bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond( int16_t ap = clamp(card->ap, -99, 99); int16_t hp = clamp(card->get_current_hp(), -99, 99); if (hp != ap) { + log.debug("A_H_SWAP: swapping AP (%hd) and HP (%hd)", ap, hp); this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0xA0, hp - ap, 0, 0); this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0x20, ap - hp, 0, 0); - card->set_current_hp(ap, 1, 1); + card->set_current_hp(ap, true, true); card->ap = hp; this->destroy_card_if_hp_zero(card, cond_card_ref); + } else { + log.debug("A_H_SWAP: AP (%hd) == HP (%hd)", ap, hp); } + } else { + log.debug("A_H_SWAP: required flag is missing"); } break; case ConditionType::AP_OVERRIDE: @@ -484,9 +495,12 @@ bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond( this->send_6xB4x06_for_stat_delta( card, cond_card_ref, 0xA0, -cond_value, 0, 0); card->ap = max(card->ap - cond_value, 0); + log.debug("AP_OVERRIDE: subtracting %hd from AP => %hd", cond_value, card->ap); } else { other_cond->value = clamp(other_cond->value + cond_value, -99, 99); } + } else { + log.debug("AP_OVERRIDE: required flag is missing"); } break; case ConditionType::TP_OVERRIDE: @@ -497,36 +511,52 @@ bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond( if (!other_cond) { this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0x80, -cond_value, 0, 0); card->tp = max(card->tp - cond_value, 0); + log.debug("TP_OVERRIDE: subtracting %hd from TP => %hd", cond_value, card->tp); } else { other_cond->value = clamp(other_cond->value + cond_value, -99, 99); } + } else { + log.debug("TP_OVERRIDE: required flag is missing"); } break; case ConditionType::MISC_AP_BONUSES: if (cond_flags & 2) { this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0xA0, -cond_value, 0, 0); card->ap = max(card->ap - cond_value, 0); + log.debug("MISC_AP_BONUSES: subtracting %hd from AP => %hd", cond_value, card->ap); + } else { + log.debug("MISC_AP_BONUSES: required flag is missing"); } break; case ConditionType::MISC_TP_BONUSES: if (cond_flags & 2) { this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0x80, -cond_value, 0, 0); card->tp = max(card->tp - cond_value, 0); + log.debug("MISC_TP_BONUSES: subtracting %hd from TP => %hd", cond_value, card->tp); + } else { + log.debug("MISC_TP_BONUSES: required flag is missing"); } break; case ConditionType::AP_SILENCE: if (cond_flags & 2) { this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0xA0, cond_value, 0, 0); card->ap = max(card->ap + cond_value, 0); + log.debug("AP_SILENCE: adding %hd to AP => %hd", cond_value, card->ap); + } else { + log.debug("AP_SILENCE: required flag is missing"); } break; case ConditionType::TP_SILENCE: if (cond_flags & 2) { this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0x80, cond_value, 0, 0); card->tp = max(card->tp + cond_value, 0); + log.debug("TP_SILENCE: adding %hd to TP => %hd", cond_value, card->tp); + } else { + log.debug("TP_SILENCE: required flag is missing"); } break; default: + log.debug("%s: no adjustments for condition type", name_for_condition_type(cond_type)); break; } @@ -539,8 +569,7 @@ bool CardSpecial::apply_stats_deltas_to_card_from_all_conditions_with_card_ref( for (ssize_t z = 8; z >= 0; z--) { auto& cond = card->action_chain.conditions[z]; if ((cond.type != ConditionType::NONE) && (cond.card_ref == card_ref)) { - ret |= this->apply_stat_deltas_to_card_from_condition_and_clear_cond( - cond, card); + ret |= this->apply_stat_deltas_to_card_from_condition_and_clear_cond(cond, card); } } return ret; @@ -637,15 +666,26 @@ CardSpecial::AttackEnvStats CardSpecial::compute_attack_env_stats( const DiceRoll& dice_roll, uint16_t target_card_ref, uint16_t condition_giver_card_ref) { + auto log = this->server()->log_stack("compute_attack_env_stats: "); + + string pa_str = pa.str(); + log.debug("pa=%s, card=@%04hX #%04hX, dice_roll=%hhu, target=@%04hX, condition_giver=@%04hX", pa_str.c_str(), card->get_card_ref(), card->get_card_id(), dice_roll.value, target_card_ref, condition_giver_card_ref); + this->action_state = pa; auto attacker_card = this->server()->card_for_set_card_ref(pa.attacker_card_ref); if (!attacker_card && (pa.original_attacker_card_ref != 0xFFFF)) { attacker_card = this->server()->card_for_set_card_ref(pa.original_attacker_card_ref); + log.debug("attacker=@%04hX #%04hX (from original)", attacker_card->get_card_ref(), attacker_card->get_card_id()); + } else if (attacker_card) { + log.debug("attacker=@%04hX #%04hX (from set)", attacker_card->get_card_ref(), attacker_card->get_card_id()); + } else { + log.debug("attacker=null (from set)"); } AttackEnvStats ast; auto ps = card->player_state(); + log.debug("base ps = %hhu", ps->client_id); ast.num_set_cards = ps->count_set_cards(); auto condition_giver_card = this->server()->card_for_set_card_ref(condition_giver_card_ref); auto target_card = this->server()->card_for_set_card_ref(target_card_ref); @@ -681,30 +721,14 @@ CardSpecial::AttackEnvStats CardSpecial::compute_attack_env_stats( ast.target_team_num_set_cards = target_team_num_set_cards; ast.condition_giver_team_num_set_cards = condition_giver_team_num_set_cards; - ast.num_native_creatures = this->get_all_set_cards_by_team_and_class( - CardClass::NATIVE_CREATURE, 0xFF, true) - .size(); - ast.num_a_beast_creatures = this->get_all_set_cards_by_team_and_class( - CardClass::A_BEAST_CREATURE, 0xFF, true) - .size(); - ast.num_machine_creatures = this->get_all_set_cards_by_team_and_class( - CardClass::MACHINE_CREATURE, 0xFF, true) - .size(); - ast.num_dark_creatures = this->get_all_set_cards_by_team_and_class( - CardClass::DARK_CREATURE, 0xFF, true) - .size(); - ast.num_sword_type_items = this->get_all_set_cards_by_team_and_class( - CardClass::SWORD_ITEM, 0xFF, true) - .size(); - ast.num_gun_type_items = this->get_all_set_cards_by_team_and_class( - CardClass::GUN_ITEM, 0xFF, true) - .size(); - ast.num_cane_type_items = this->get_all_set_cards_by_team_and_class( - CardClass::CANE_ITEM, 0xFF, true) - .size(); - ast.num_sword_type_items_on_team = card - ? this->get_all_set_cards_by_team_and_class(CardClass::SWORD_ITEM, card->get_team_id(), true).size() - : 0; + ast.num_native_creatures = this->get_all_set_cards_by_team_and_class(CardClass::NATIVE_CREATURE, 0xFF, true).size(); + ast.num_a_beast_creatures = this->get_all_set_cards_by_team_and_class(CardClass::A_BEAST_CREATURE, 0xFF, true).size(); + ast.num_machine_creatures = this->get_all_set_cards_by_team_and_class(CardClass::MACHINE_CREATURE, 0xFF, true).size(); + ast.num_dark_creatures = this->get_all_set_cards_by_team_and_class(CardClass::DARK_CREATURE, 0xFF, true).size(); + ast.num_sword_type_items = this->get_all_set_cards_by_team_and_class(CardClass::SWORD_ITEM, 0xFF, true).size(); + ast.num_gun_type_items = this->get_all_set_cards_by_team_and_class(CardClass::GUN_ITEM, 0xFF, true).size(); + ast.num_cane_type_items = this->get_all_set_cards_by_team_and_class(CardClass::CANE_ITEM, 0xFF, true).size(); + ast.num_sword_type_items_on_team = card ? this->get_all_set_cards_by_team_and_class(CardClass::SWORD_ITEM, card->get_team_id(), true).size() : 0; size_t num_item_or_creature_cards_in_hand = 0; for (size_t z = 0; z < 6; z++) { @@ -1113,7 +1137,9 @@ shared_ptr CardSpecial::compute_replaced_target_based_on_conditions( } StatSwapType CardSpecial::compute_stat_swap_type(shared_ptr card) const { + auto log = this->server()->log_stack(string_printf("compute_stat_swap_type(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id())); if (!card) { + log.debug("card is missing"); return StatSwapType::NONE; } @@ -1121,23 +1147,33 @@ StatSwapType CardSpecial::compute_stat_swap_type(shared_ptr card) co for (size_t cond_index = 0; cond_index < 9; cond_index++) { auto& cond = card->action_chain.conditions[cond_index]; if (cond.type != ConditionType::NONE) { + auto cond_log = log.sub(string_printf("(%zu) ", cond_index)); + string cond_str = cond.str(); + cond_log.debug("%s", cond_str.c_str()); if (!this->card_ref_has_ability_trap(cond)) { if (cond.type == ConditionType::UNKNOWN_75) { if (ret == StatSwapType::A_H_SWAP) { + log.debug("UNKNOWN_75: clearing"); ret = StatSwapType::NONE; } else { + log.debug("UNKNOWN_75: setting A_H_SWAP"); ret = StatSwapType::A_H_SWAP; } } else if (cond.type == ConditionType::A_T_SWAP) { if (ret == StatSwapType::A_T_SWAP) { + log.debug("A_T_SWAP: clearing"); ret = StatSwapType::NONE; } else { + log.debug("A_T_SWAP: setting A_T_SWAP"); ret = StatSwapType::A_T_SWAP; } } + } else { + log.debug("skipping due to ability trap"); } } } + log.debug("ret = %zu", static_cast(ret)); return ret; } @@ -1568,7 +1604,7 @@ int32_t CardSpecial::evaluate_effect_expr( const AttackEnvStats& ast, const char* expr, DiceRoll& dice_roll) const { - auto log = this->server()->log.sub("evaluate_effect_expr: "); + auto log = this->server()->log_stack("evaluate_effect_expr: "); if (log.min_level == LogLevel::DEBUG) { log.debug("ast, expr=\"%s\", dice_roll=(client_id=%02hhX, a2=%02hhX, value=%02hhX, value_used_in_expr=%s, a5=%04hX)", expr, dice_roll.client_id, dice_roll.unknown_a2, dice_roll.value, dice_roll.value_used_in_expr ? "true" : "false", dice_roll.unknown_a5); ast.print(stderr); @@ -1662,10 +1698,10 @@ bool CardSpecial::execute_effect( ConditionType cond_type, uint32_t unknown_p7, uint16_t attacker_card_ref) { - auto log = this->server()->log.sub("execute_effect: "); + auto log = this->server()->log_stack(string_printf("execute_effect(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id())); { string cond_str = cond.str(); - log.debug("cond=%s, card=%04hX, expr_value=%hd, unknown_p5=%hd, cond_type=%s, unknown_p7=%" PRIu32 ", attacker_card_ref=%04hX", cond_str.c_str(), ref_for_card(card), expr_value, unknown_p5, name_for_condition_type(cond_type), unknown_p7, attacker_card_ref); + log.debug("cond=%s, card=@%04hX, expr_value=%hd, unknown_p5=%hd, cond_type=%s, unknown_p7=%" PRIu32 ", attacker_card_ref=@%04hX", cond_str.c_str(), ref_for_card(card), expr_value, unknown_p5, name_for_condition_type(cond_type), unknown_p7, attacker_card_ref); } int16_t clamped_expr_value = clamp(expr_value, -99, 99); int16_t clamped_unknown_p5 = clamp(unknown_p5, -99, 99); @@ -1909,8 +1945,7 @@ bool CardSpecial::execute_effect( cmd.effect.operation = -static_cast(cond.type); cmd.effect.condition_index = z; this->server()->send(cmd); - this->apply_stat_deltas_to_card_from_condition_and_clear_cond( - cond, card); + this->apply_stat_deltas_to_card_from_condition_and_clear_cond(cond, card); card->send_6xB4x4E_4C_4D_if_needed(); } } @@ -2541,8 +2576,8 @@ vector> CardSpecial::get_targeted_cards_for_condition( const ActionState& as, int16_t p_target_type, bool apply_usability_filters) const { - auto log = this->server()->log.sub("get_targeted_cards_for_condition: "); - log.debug("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"); + auto log = this->server()->log_stack(string_printf("get_targeted_cards_for_condition(@%04hX, %hhu, @%04hX): ", card_ref, def_effect_index, setter_card_ref)); + log.debug("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; @@ -2551,12 +2586,12 @@ vector> CardSpecial::get_targeted_cards_for_condition( if (!card1) { card1 = this->server()->card_for_set_card_ref(setter_card_ref); } - log.debug("card1=%04hX", ref_for_card(card1)); + log.debug("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); - log.debug("card2=%04hX", ref_for_card(card2)); + log.debug("card2=@%04hX", ref_for_card(card2)); Location card1_loc; if (!card1) { @@ -2589,7 +2624,7 @@ 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) { - log.debug("(p01/p05) result_card=%04hX", ref_for_card(result_card)); + log.debug("(p01/p05) result_card=@%04hX", ref_for_card(result_card)); ret.emplace_back(result_card); } else { log.debug("(p01/p05) result_card=null"); @@ -2749,13 +2784,13 @@ vector> CardSpecial::get_targeted_cards_for_condition( if (def && ps) { // TODO: Again with the Gifoie hardcoding... uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2); - log23.debug("effective range card ID is %04hX", range_card_id); + log23.debug("effective range card ID is #%04hX", range_card_id); parray range; compute_effective_range(range, this->server()->card_index, range_card_id, card1_loc, this->server()->map_and_rules, &log23); auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF); log23.debug("%zu result card refs", result_card_refs.size()); for (uint16_t result_card_ref : result_card_refs) { - auto result_log = log23.subf("(result ref %04hX) ", result_card_ref); + auto result_log = log23.subf("(result @%04hX) ", result_card_ref); auto result_card = this->server()->card_for_set_card_ref(result_card_ref); if (!result_card) { result_log.debug("result card not found"); @@ -3109,9 +3144,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); - log.debug("usability filter: kept card %04hX", ref_for_card(c)); + log.debug("usability filter: kept card @%04hX", ref_for_card(c)); } else { - log.debug("usability filter: removed card %04hX", ref_for_card(c)); + log.debug("usability filter: removed card @%04hX", ref_for_card(c)); } } return filtered_ret; @@ -3173,7 +3208,7 @@ void CardSpecial::on_card_set(shared_ptr ps, uint16_t card_ref) { uint16_t sc_card_ref = sc_card ? sc_card->get_card_ref() : 0xFFFF; ActionState as; - this->unknown_8024C2B0(1, card_ref, as, sc_card_ref); + this->evaluate_and_apply_effects(0x01, card_ref, as, sc_card_ref); } const CardDefinition::Effect* CardSpecial::original_definition_for_condition( @@ -3372,9 +3407,12 @@ size_t CardSpecial::sum_last_attack_damage( vector>* out_cards, int32_t* out_damage_sum, size_t* out_damage_count) const { + auto log = this->server()->log_stack("sum_last_attack_damage: "); + size_t damage_count = 0; auto check_card = [&](shared_ptr c) -> void { if (c && (c->last_attack_final_damage > 0)) { + log.debug("check_card @%04hX #%04hX => %hd", c->get_card_ref(), c->get_card_id(), c->last_attack_final_damage); if (out_damage_sum) { *out_damage_sum += c->last_attack_final_damage; } @@ -3483,9 +3521,9 @@ void CardSpecial::unknown_80244AA8(shared_ptr card) { } this->apply_defense_conditions(as, 0x27, card, 4); - this->unknown_8024C2B0(0x27, card->get_card_ref(), as, 0xFFFF); + this->evaluate_and_apply_effects(0x27, card->get_card_ref(), as, 0xFFFF); this->apply_defense_conditions(as, 0x13, card, 4); - this->unknown_8024C2B0(0x13, card->get_card_ref(), as, 0xFFFF); + this->evaluate_and_apply_effects(0x13, card->get_card_ref(), as, 0xFFFF); } void CardSpecial::check_for_defense_interference( @@ -3568,17 +3606,17 @@ void CardSpecial::check_for_defense_interference( } } -void CardSpecial::unknown_8024C2B0( - uint32_t when, +void CardSpecial::evaluate_and_apply_effects( + uint8_t when, uint16_t set_card_ref, const ActionState& as, uint16_t sc_card_ref, bool apply_defense_condition_to_all_cards, uint16_t apply_defense_condition_to_card_ref) { - auto log = this->server()->log.sub("unknown_8024C2B0: "); + auto log = this->server()->log_stack(string_printf("evaluate_and_apply_effects(%02hhX, @%04hX, @%04hX): ", when, set_card_ref, sc_card_ref)); { string as_str = as.str(); - log.debug("when=%02" PRIX32 ", set_card_ref=%04hX, as=%s, sc_card_ref=%04hX, apply_defense_condition_to_all_cards=%s, apply_defense_condition_to_card_ref=%04hX", + log.debug("when=%02hhX, set_card_ref=@%04hX, as=%s, sc_card_ref=@%04hX, apply_defense_condition_to_all_cards=%s, apply_defense_condition_to_card_ref=@%04hX", when, set_card_ref, as_str.c_str(), sc_card_ref, apply_defense_condition_to_all_cards ? "true" : "false", apply_defense_condition_to_card_ref); } @@ -3604,7 +3642,7 @@ void CardSpecial::unknown_8024C2B0( break; } auto action_ce = this->server()->definition_for_card_ref(as.action_card_refs[z]); - if (action_ce && (action_ce->def.card_id == action_ce->def.card_id)) { + if (action_ce && (action_ce->def.card_id == ce->def.card_id)) { as_action_card_refs_contains_duplicate_of_set_card = true; } } @@ -3681,7 +3719,7 @@ void CardSpecial::unknown_8024C2B0( } for (size_t z = 0; z < targeted_cards.size(); z++) { - auto target_log = effect_log.sub(string_printf("(target:%04hX) ", targeted_cards[z]->get_card_ref())); + auto target_log = effect_log.sub(string_printf("(target:@%04hX) ", targeted_cards[z]->get_card_ref())); dice_roll.value_used_in_expr = false; string arg2_str = card_effect.arg2; target_log.debug("arg2_str = %s", arg2_str.c_str()); @@ -3709,9 +3747,9 @@ void CardSpecial::unknown_8024C2B0( sc_card_ref); if (!target_card) { target_card = targeted_cards[z]; - target_log.debug("target card (not replaced) = %04hX", target_card->get_card_ref()); + target_log.debug("target card (not replaced) = @%04hX", target_card->get_card_ref()); } else { - target_log.debug("target card (replaced) = %04hX", target_card->get_card_ref()); + target_log.debug("target card (replaced) = @%04hX", target_card->get_card_ref()); } ssize_t applied_cond_index = -1; @@ -4042,10 +4080,10 @@ void CardSpecial::on_card_destroyed( attacker_card, destroyed_card); uint16_t destroyed_card_ref = destroyed_card->get_card_ref(); - this->unknown_8024C2B0(5, destroyed_card_ref, defense_as, 0xFFFF); + this->evaluate_and_apply_effects(0x05, destroyed_card_ref, defense_as, 0xFFFF); for (size_t z = 0; (z < 8) && (defense_as.action_card_refs[z] != 0xFFFF); z++) { - this->unknown_8024C2B0( - 5, defense_as.action_card_refs[z], defense_as, destroyed_card->get_card_ref()); + this->evaluate_and_apply_effects( + 0x05, defense_as.action_card_refs[z], defense_as, destroyed_card->get_card_ref()); } if (attacker_card) { @@ -4287,6 +4325,9 @@ vector> CardSpecial::filter_cards_by_range( } void CardSpecial::unknown_8024AAB8(const ActionState& as) { + auto log = this->server()->log_stack("unknown_8024AAB8: "); + string as_str = as.str(); + log.debug("as=%s", as_str.c_str()); this->unknown_action_state_a1 = as; for (size_t z = 0; (z < 8) && (as.action_card_refs[z] != 0xFFFF); z++) { @@ -4297,21 +4338,21 @@ void CardSpecial::unknown_8024AAB8(const ActionState& as) { } if (this->send_6xB4x06_if_card_ref_invalid(as.original_attacker_card_ref, 0x1F) == 0xFFFF) { - this->unknown_8024C2B0( - 1, + this->evaluate_and_apply_effects( + 0x01, as.action_card_refs[z], as, this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x21)); - this->unknown_8024C2B0( - 0xb, + this->evaluate_and_apply_effects( + 0x0B, as.action_card_refs[z], as, this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x22)); } else { uint16_t card_ref = this->send_6xB4x06_if_card_ref_invalid(as.target_card_refs[0], 0x20); if (card_ref != 0xFFFF) { - this->unknown_8024C2B0(1, as.action_card_refs[z], as, card_ref); - this->unknown_8024C2B0(0x15, as.action_card_refs[z], as, card_ref); + this->evaluate_and_apply_effects(0x01, as.action_card_refs[z], as, card_ref); + this->evaluate_and_apply_effects(0x15, as.action_card_refs[z], as, card_ref); } } } @@ -4319,17 +4360,17 @@ void CardSpecial::unknown_8024AAB8(const ActionState& as) { if (as.original_attacker_card_ref == 0xffff) { uint16_t card_ref1 = this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x23); uint16_t card_ref2 = this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x25); - this->unknown_8024C2B0(0x33, card_ref2, as, card_ref1); + this->evaluate_and_apply_effects(0x33, card_ref2, as, card_ref1); card_ref1 = this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x24); card_ref2 = this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x26); - this->unknown_8024C2B0(0x34, card_ref2, as, card_ref1); + this->evaluate_and_apply_effects(0x34, card_ref2, as, card_ref1); for (size_t z = 0; (z < 4 * 9) && (as.target_card_refs[z] != 0xFFFF); z++) { uint16_t card_ref = this->send_6xB4x06_if_card_ref_invalid( as.action_card_refs[z], 0x27); if (card_ref == 0xFFFF) { break; } - this->unknown_8024C2B0(0x35, as.target_card_refs[z], as, as.attacker_card_ref); + this->evaluate_and_apply_effects(0x35, as.target_card_refs[z], as, as.attacker_card_ref); } } } @@ -4337,9 +4378,9 @@ void CardSpecial::unknown_8024AAB8(const ActionState& as) { void CardSpecial::unknown_80244BE4(shared_ptr card) { ActionState as = this->create_attack_state_from_card_action_chain(card); this->apply_defense_conditions(as, 9, card, 4); - this->unknown_8024C2B0(9, card->get_card_ref(), as, 0xFFFF); + this->evaluate_and_apply_effects(0x09, card->get_card_ref(), as, 0xFFFF); this->apply_defense_conditions(as, 0x27, card, 4); - this->unknown_8024C2B0(0x27, card->get_card_ref(), as, 0xFFFF); + this->evaluate_and_apply_effects(0x27, card->get_card_ref(), as, 0xFFFF); } void CardSpecial::unknown_80244CA8(shared_ptr card) { @@ -4358,16 +4399,17 @@ void CardSpecial::unknown_80244CA8(shared_ptr card) { } this->apply_defense_conditions(as, 0x46, card, 4); - this->unknown_8024C2B0(0x46, card->get_card_ref(), as, sc_card_ref); + this->evaluate_and_apply_effects(0x46, card->get_card_ref(), as, sc_card_ref); if (ps->is_team_turn()) { this->apply_defense_conditions(as, 4, card, 4); - this->unknown_8024C2B0(4, card->get_card_ref(), as, sc_card_ref); + this->evaluate_and_apply_effects(0x04, card->get_card_ref(), as, sc_card_ref); } } template -void CardSpecial::unknown1_t( - shared_ptr unknown_p2, const ActionState* existing_as) { +void CardSpecial::unknown1_t(shared_ptr unknown_p2, const ActionState* existing_as) { + auto log = this->server()->log_stack(string_printf("unknown1_t<%02hhX, %02hhX>(@%04hX #%04hX): ", When1, When2, unknown_p2->get_card_ref(), unknown_p2->get_card_id())); + ActionState as; if (!existing_as) { as = this->create_attack_state_from_card_action_chain(unknown_p2); @@ -4383,18 +4425,18 @@ void CardSpecial::unknown1_t( } } auto card = this->sc_card_for_card(unknown_p2); - this->unknown_8024C2B0(When1, unknown_p2->get_card_ref(), as, card ? card->get_card_ref() : 0xFFFF); + this->evaluate_and_apply_effects(When1, unknown_p2->get_card_ref(), as, card ? card->get_card_ref() : 0xFFFF); for (size_t z = 0; (z < 8) && (as.action_card_refs[z] != 0xFFFF); z++) { - this->unknown_8024C2B0(When1, as.action_card_refs[z], as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(When1, as.action_card_refs[z], as, unknown_p2->get_card_ref()); } for (size_t z = 0; (z < 4 * 9) && (as.target_card_refs[z] != 0xFFFF); z++) { auto card = this->server()->card_for_set_card_ref(as.target_card_refs[z]); if (card) { ActionState target_as = this->create_defense_state_for_card_pair_action_chains( unknown_p2, card); - this->unknown_8024C2B0(When2, as.target_card_refs[z], target_as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(When2, as.target_card_refs[z], target_as, unknown_p2->get_card_ref()); for (size_t w = 0; (w < 8) && (target_as.action_card_refs[w] != 0xFFFF); w++) { - this->unknown_8024C2B0(When1, target_as.action_card_refs[w], target_as, card->get_card_ref()); + this->evaluate_and_apply_effects(When1, target_as.action_card_refs[w], target_as, card->get_card_ref()); } } } @@ -4415,6 +4457,8 @@ void CardSpecial::unknown_8024945C(shared_ptr unknown_p2, const ActionStat } void CardSpecial::unknown_8024966C(shared_ptr unknown_p2, const ActionState* existing_as) { + auto log = this->server()->log_stack(string_printf("unknown_8024966C(@%04hX #%04hX): ", unknown_p2->get_card_ref(), unknown_p2->get_card_id())); + ActionState as; if (!existing_as) { as = this->create_attack_state_from_card_action_chain(unknown_p2); @@ -4443,23 +4487,23 @@ void CardSpecial::unknown_8024966C(shared_ptr unknown_p2, const ActionStat } } - this->unknown_8024C2B0(0x3D, unknown_p2->get_card_ref(), as, card_ref); - this->unknown_8024C2B0(0x3E, unknown_p2->get_card_ref(), as, card_ref); + this->evaluate_and_apply_effects(0x3D, unknown_p2->get_card_ref(), as, card_ref); + this->evaluate_and_apply_effects(0x3E, unknown_p2->get_card_ref(), as, card_ref); if (defender_card) { - this->unknown_8024C2B0(0x22, defender_card->get_card_ref(), as, card_ref); + this->evaluate_and_apply_effects(0x22, defender_card->get_card_ref(), as, card_ref); } for (size_t z = 0; (z < 8) && (as.action_card_refs[z] != 0xFFFF); z++) { - this->unknown_8024C2B0(0x3D, as.action_card_refs[z], as, unknown_p2->get_card_ref()); - this->unknown_8024C2B0(0x3E, as.action_card_refs[z], as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(0x3D, as.action_card_refs[z], as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(0x3E, as.action_card_refs[z], as, unknown_p2->get_card_ref()); } for (size_t z = 0; (z < 4 * 9) && (as.target_card_refs[z] != 0xFFFF); z++) { card = this->server()->card_for_set_card_ref(as.target_card_refs[z]); if (card) { ActionState defense_as = this->create_defense_state_for_card_pair_action_chains(unknown_p2, card); - this->unknown_8024C2B0(0x3D, card->get_card_ref(), defense_as, unknown_p2->get_card_ref()); - this->unknown_8024C2B0(0x3F, card->get_card_ref(), defense_as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(0x3D, card->get_card_ref(), defense_as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(0x3F, card->get_card_ref(), defense_as, unknown_p2->get_card_ref()); } } } @@ -4473,11 +4517,11 @@ void CardSpecial::unknown_8024A9D8(const ActionState& pa, uint16_t action_card_r for (size_t z = 0; (z < 8) && (pa.action_card_refs[z] != 0xFFFF); z++) { if ((action_card_ref == 0xFFFF) || (action_card_ref == pa.action_card_refs[z])) { if (pa.original_attacker_card_ref == 0xFFFF) { - this->unknown_8024C2B0(0x29, pa.action_card_refs[z], pa, pa.attacker_card_ref); - this->unknown_8024C2B0(0x2A, pa.action_card_refs[z], pa, pa.attacker_card_ref); + this->evaluate_and_apply_effects(0x29, pa.action_card_refs[z], pa, pa.attacker_card_ref); + this->evaluate_and_apply_effects(0x2A, pa.action_card_refs[z], pa, pa.attacker_card_ref); } else { - this->unknown_8024C2B0(0x29, pa.action_card_refs[z], pa, pa.target_card_refs[0]); - this->unknown_8024C2B0(0x2B, pa.action_card_refs[z], pa, pa.target_card_refs[0]); + this->evaluate_and_apply_effects(0x29, pa.action_card_refs[z], pa, pa.target_card_refs[0]); + this->evaluate_and_apply_effects(0x2B, pa.action_card_refs[z], pa, pa.target_card_refs[0]); } } } @@ -4560,6 +4604,8 @@ void CardSpecial::check_for_attack_interference(shared_ptr unknown_p2) { template void CardSpecial::unknown_t2(shared_ptr unknown_p2) { + auto log = this->server()->log_stack(string_printf("unknown_t2<%02hhX, %02hhX, %02hhX, %02hhX>(@%04hX #%04hX): ", When1, When2, When3, When4, unknown_p2->get_card_ref(), unknown_p2->get_card_id())); + ActionState as = this->create_attack_state_from_card_action_chain(unknown_p2); auto sc_card = this->sc_card_for_card(unknown_p2); @@ -4591,24 +4637,24 @@ void CardSpecial::unknown_t2(shared_ptr unknown_p2) { } } - this->unknown_8024C2B0(When1, unknown_p2->get_card_ref(), as, sc_card_ref); - this->unknown_8024C2B0(When2, unknown_p2->get_card_ref(), as, sc_card_ref); + this->evaluate_and_apply_effects(When1, unknown_p2->get_card_ref(), as, sc_card_ref); + this->evaluate_and_apply_effects(When2, unknown_p2->get_card_ref(), as, sc_card_ref); if (defender_card) { - this->unknown_8024C2B0(When3, defender_card->get_card_ref(), as, sc_card_ref); + this->evaluate_and_apply_effects(When3, defender_card->get_card_ref(), as, sc_card_ref); } for (size_t z = 0; (z < 8) && (as.action_card_refs[z] != 0xFFFF); z++) { - this->unknown_8024C2B0(When1, as.action_card_refs[z], as, unknown_p2->get_card_ref()); - this->unknown_8024C2B0(When2, as.action_card_refs[z], as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(When1, as.action_card_refs[z], as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(When2, as.action_card_refs[z], as, unknown_p2->get_card_ref()); } for (size_t z = 0; (z < 4 * 9) && (as.target_card_refs[z] != 0xFFFF); z++) { auto set_card = this->server()->card_for_set_card_ref(as.target_card_refs[z]); if (set_card) { ActionState target_as = this->create_defense_state_for_card_pair_action_chains(unknown_p2, set_card); - this->unknown_8024C2B0(When1, set_card->get_card_ref(), target_as, unknown_p2->get_card_ref()); - this->unknown_8024C2B0(When4, set_card->get_card_ref(), target_as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(When1, set_card->get_card_ref(), target_as, unknown_p2->get_card_ref()); + this->evaluate_and_apply_effects(When4, set_card->get_card_ref(), target_as, unknown_p2->get_card_ref()); for (size_t z = 0; (z < 8) && (target_as.action_card_refs[z] != 0xFFFF); z++) { - this->unknown_8024C2B0(When1, target_as.action_card_refs[z], target_as, set_card->get_card_ref()); - this->unknown_8024C2B0(When4, target_as.action_card_refs[z], target_as, set_card->get_card_ref()); + this->evaluate_and_apply_effects(When1, target_as.action_card_refs[z], target_as, set_card->get_card_ref()); + this->evaluate_and_apply_effects(When4, target_as.action_card_refs[z], target_as, set_card->get_card_ref()); } } } @@ -4654,8 +4700,8 @@ void CardSpecial::unknown_8024A6DC( ActionState as = this->create_defense_state_for_card_pair_action_chains( unknown_p2, unknown_p3); for (size_t z = 0; (z < 8) && (as.action_card_refs[z] != 0xFFFF); z++) { - this->unknown_8024C2B0(1, as.action_card_refs[z], as, unknown_p3->get_card_ref()); - this->unknown_8024C2B0(0x15, as.action_card_refs[z], as, unknown_p3->get_card_ref()); + this->evaluate_and_apply_effects(0x01, as.action_card_refs[z], as, unknown_p3->get_card_ref()); + this->evaluate_and_apply_effects(0x15, as.action_card_refs[z], as, unknown_p3->get_card_ref()); } } diff --git a/src/Episode3/CardSpecial.hh b/src/Episode3/CardSpecial.hh index 433c55b5..8d4a6e40 100644 --- a/src/Episode3/CardSpecial.hh +++ b/src/Episode3/CardSpecial.hh @@ -276,8 +276,8 @@ public: std::shared_ptr attacker_card, std::shared_ptr target_card, int16_t* inout_unknown_p4); - void unknown_8024C2B0( - uint32_t when, + void evaluate_and_apply_effects( + uint8_t when, uint16_t set_card_ref, const ActionState& as, uint16_t sc_card_ref, diff --git a/src/Episode3/DeckState.cc b/src/Episode3/DeckState.cc index 9bc707d6..a54ba754 100644 --- a/src/Episode3/DeckState.cc +++ b/src/Episode3/DeckState.cc @@ -293,7 +293,7 @@ static const char* name_for_card_state(DeckState::CardState st) { } void DeckState::print(FILE* stream, std::shared_ptr card_index) const { - fprintf(stream, "DeckState: client_id=%hhu draw_index=%hhu card_ref_base=%04hX shuffle=%s loop=%s\n", + fprintf(stream, "DeckState: client_id=%hhu draw_index=%hhu card_ref_base=@%04hX shuffle=%s loop=%s\n", this->client_id, this->draw_index, this->card_ref_base, this->shuffle_enabled ? "true" : "false", this->loop_enabled ? "true" : "false"); for (size_t z = 0; z < 31; z++) { const auto& e = this->entries[z]; @@ -306,10 +306,10 @@ void DeckState::print(FILE* stream, std::shared_ptr card_index) } if (ce) { string name = ce->def.en_name; - fprintf(stream, " (%02zu) index=%02hhX ref=%04hX card_id=%04hX \"%s\" %s\n", + fprintf(stream, " (%02zu) index=%02hhX ref=@%04hX card_id=#%04hX \"%s\" %s\n", z, e.deck_index, this->card_refs[z], e.card_id, name.c_str(), name_for_card_state(e.state)); } else { - fprintf(stream, " (%02zu) index=%02hhX ref=%04hX card_id=%04hX %s\n", + fprintf(stream, " (%02zu) index=%02hhX ref=@%04hX card_id=#%04hX %s\n", z, e.deck_index, this->card_refs[z], e.card_id, name_for_card_state(e.state)); } } diff --git a/src/Episode3/PlayerState.cc b/src/Episode3/PlayerState.cc index fff37191..5b77284f 100644 --- a/src/Episode3/PlayerState.cc +++ b/src/Episode3/PlayerState.cc @@ -778,7 +778,7 @@ vector PlayerState::get_all_cards_within_range( uint8_t target_team_id) const { auto s = this->server(); - auto log = s->log.sub("get_all_cards_within_range: "); + auto log = this->server()->log_stack("get_all_cards_within_range: "); string loc_str = loc.str(); log.debug("loc=%s, target_team_id=%02hhX", loc_str.c_str(), target_team_id); diff --git a/src/Episode3/PlayerStateSubordinates.cc b/src/Episode3/PlayerStateSubordinates.cc index 0d388747..10cf0d98 100644 --- a/src/Episode3/PlayerStateSubordinates.cc +++ b/src/Episode3/PlayerStateSubordinates.cc @@ -477,7 +477,7 @@ void ActionChainWithConds::set_action_subphase_from_card( this->chain.action_subphase = card->server()->get_current_action_subphase(); } -bool ActionChainWithConds::unknown_8024DEC4() const { +bool ActionChainWithConds::can_apply_attack() const { return this->check_flag(4) ? false : (this->chain.target_card_ref_count != 0); } @@ -609,7 +609,7 @@ std::string HandAndEquipState::str() const { "assist_flags=%08" PRIX32 ", hand_refs=%s, " "assist_ref=@%04hX, set_refs=%s, sc_ref=@%04hX, " "hand_refs2=%s, set_refs2=%s, assist_ref2=@%04hX, " - "assist_set_num=%hu, assist_card_id=%04hX, " + "assist_set_num=%hu, assist_card_id=#%04hX, " "assist_turns=%hhu, assit_dely=%hhu, atk_bonus=%hhu, " "def_bonus=%hhu, u2=[%hhu, %hhu]]", this->dice_results[0], @@ -808,24 +808,24 @@ vector get_card_refs_within_range( vector ret; if (is_card_within_range(range, loc, short_statuses[0], log)) { if (log) { - log->debug("get_card_refs_within_range: sc card %04hX within range", short_statuses[0].card_ref.load()); + log->debug("get_card_refs_within_range: sc card @%04hX within range", short_statuses[0].card_ref.load()); } ret.emplace_back(short_statuses[0].card_ref); } else { if (log) { - log->debug("get_card_refs_within_range: sc card %04hX not within range", short_statuses[0].card_ref.load()); + log->debug("get_card_refs_within_range: sc card @%04hX not within range", short_statuses[0].card_ref.load()); } } for (size_t card_index = 7; card_index < 15; card_index++) { const auto& ss = short_statuses[card_index]; if (is_card_within_range(range, loc, ss, log)) { if (log) { - log->debug("get_card_refs_within_range: card %04hX within range", ss.card_ref.load()); + log->debug("get_card_refs_within_range: card @%04hX within range", ss.card_ref.load()); } ret.emplace_back(ss.card_ref); } else { if (log) { - log->debug("get_card_refs_within_range: card %04hX not within range", ss.card_ref.load()); + log->debug("get_card_refs_within_range: card @%04hX not within range", ss.card_ref.load()); } } } diff --git a/src/Episode3/PlayerStateSubordinates.hh b/src/Episode3/PlayerStateSubordinates.hh index 342a5ee4..8d409108 100644 --- a/src/Episode3/PlayerStateSubordinates.hh +++ b/src/Episode3/PlayerStateSubordinates.hh @@ -159,7 +159,7 @@ struct ActionChainWithConds { uint16_t* out_value) const; void set_action_subphase_from_card(std::shared_ptr card); - bool unknown_8024DEC4() const; + bool can_apply_attack() const; std::string str() const; } __attribute__((packed)); diff --git a/src/Episode3/RulerServer.cc b/src/Episode3/RulerServer.cc index 693221c3..6b72cb4e 100644 --- a/src/Episode3/RulerServer.cc +++ b/src/Episode3/RulerServer.cc @@ -15,7 +15,7 @@ void compute_effective_range( PrefixedLogger* log) { if (log && log->should_log(LogLevel::DEBUG)) { string loc_str = loc.str(); - log->debug("compute_effective_range: card_id=%04hX, loc=%s", card_id, loc_str.c_str()); + log->debug("compute_effective_range: card_id=#%04hX, loc=%s", card_id, loc_str.c_str()); log->debug("compute_effective_range: map_and_rules->map:"); map_and_rules->map.print(stderr); } @@ -903,23 +903,21 @@ bool RulerServer::check_usability_or_condition_apply( uint8_t def_effect_index, bool is_item_usability_check, AttackMedium attack_medium) const { - auto log = this->server()->log.sub("check_usability_or_condition_apply: "); + auto log = this->server()->log_stack(string_printf("check_usability_or_condition_apply(%02hhX, #%04hX, %02hhX, #%04hX, #%04hX, %02hhX, %s, %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))); if (static_cast(attack_medium) & 0x80) { attack_medium = AttackMedium::UNKNOWN; } - 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) { - log.debug("check_usability_or_condition_apply: ce1 missing"); + log.debug("ce1 missing"); return false; } if ((ce1->def.type == CardType::ITEM) && this->card_id_is_boss_sc(card_id2)) { - log.debug("check_usability_or_condition_apply: ce1 is item and card_id2 is boss sc"); + log.debug("ce1 is item and card_id2 is boss sc"); return false; } @@ -928,12 +926,12 @@ bool RulerServer::check_usability_or_condition_apply( criterion_code = ce1->def.usable_criterion; } else { if (def_effect_index > 2) { - log.debug("check_usability_or_condition_apply: invalid def_effect_index"); + log.debug("invalid def_effect_index"); return false; } criterion_code = ce1->def.effects[def_effect_index].apply_criterion; } - log.debug("check_usability_or_condition_apply: criterion_code=%s", name_for_criterion_code(criterion_code)); + log.debug("criterion_code=%s", name_for_criterion_code(criterion_code)); // For item usability checks, prevent criteria that depend on player // positioning/team setup @@ -944,7 +942,7 @@ bool RulerServer::check_usability_or_condition_apply( (criterion_code == CriterionCode::FC) || (criterion_code == CriterionCode::NOT_SC) || (criterion_code == CriterionCode::SC))) { - log.debug("check_usability_or_condition_apply: criterion is forbidden"); + log.debug("criterion is forbidden"); criterion_code = CriterionCode::NONE; } @@ -1318,7 +1316,7 @@ bool RulerServer::check_usability_or_condition_apply( } } - log.debug("check_usability_or_condition_apply: default return (false)"); + log.debug("default return (false)"); return false; } diff --git a/src/Episode3/Server.cc b/src/Episode3/Server.cc index d8a0e04c..50303775 100644 --- a/src/Episode3/Server.cc +++ b/src/Episode3/Server.cc @@ -34,7 +34,6 @@ Server::Server(shared_ptr lobby, card_index(card_index), map_index(map_index), behavior_flags(behavior_flags), - log(lobby->log.prefix + "[Ep3::Server] ", lobby->log.min_level), random_crypt(random_crypt), last_chosen_map(map_if_tournament), is_tournament(!!map_if_tournament), @@ -73,7 +72,16 @@ Server::Server(shared_ptr lobby, chosen_trap_tile_index_of_type(0), has_done_pb(0), num_6xB4x06_commands_sent(0), - prev_num_6xB4x06_commands_sent(0) {} + prev_num_6xB4x06_commands_sent(0) { + new StackLogger(this, lobby->log.prefix + "[Ep3::Server] ", lobby->log.min_level); +} + +Server::~Server() noexcept(false) { + if (this->logger_stack.size() != 1) { + throw logic_error(string_printf("incorrect logger stack size: expected 1, received %zu", this->logger_stack.size())); + } + delete this->logger_stack.back(); +} void Server::init() { this->map_and_rules.reset(new MapAndRulesState()); @@ -107,6 +115,52 @@ void Server::init() { this->send_6xB4x46(); } +Server::StackLogger::StackLogger(const Server* s, const std::string& prefix) + : PrefixedLogger(s->logger_stack.back()->prefix + prefix, s->logger_stack.back()->min_level), + server(s) { + s->logger_stack.push_back(this); +} + +Server::StackLogger::StackLogger(const Server* s, const std::string& prefix, LogLevel min_level) + : PrefixedLogger(prefix, min_level), + server(s) { + s->logger_stack.push_back(this); +} + +Server::StackLogger::StackLogger(StackLogger&& other) + : PrefixedLogger(std::move(other)), + server(other.server) { + if (this->server->logger_stack.back() != &other) { + throw logic_error("cannot move StackLogger unless it is the last one"); + } + this->server->logger_stack.back() = this; +} + +Server::StackLogger& Server::StackLogger::operator=(StackLogger&& other) { + this->PrefixedLogger::operator=(std::move(other)); + this->server = other.server; + if (this->server->logger_stack.back() != &other) { + throw logic_error("cannot move StackLogger unless it is the last one"); + } + this->server->logger_stack.back() = this; + return *this; +} + +Server::StackLogger::~StackLogger() noexcept(false) { + if (this->server->logger_stack.back() != this) { + throw logic_error("incorrect logger stack unwind order"); + } + this->server->logger_stack.pop_back(); +} + +Server::StackLogger Server::log_stack(const std::string& prefix) const { + return StackLogger(this, prefix); +} + +const Server::StackLogger& Server::log() const { + return *this->logger_stack.back(); +} + int8_t Server::get_winner_team_id() const { // Note: This function is not part of the original implementation. @@ -251,12 +305,12 @@ __attribute__((format(printf, 2, 3))) void Server::send_info_message_printf(cons void Server::send_debug_command_received_message( uint8_t client_id, uint8_t subsubcommand, const char* description) const { - this->log.debug("%hhu/CAx%02hhX %s", client_id, subsubcommand, description); + this->log().debug("%hhu/CAx%02hhX %s", client_id, subsubcommand, description); this->send_debug_message_printf("$C5%hhu/CAx%02hhX %s", client_id, subsubcommand, description); } void Server::send_debug_command_received_message(uint8_t subsubcommand, const char* description) const { - this->log.debug("*/CAx%02hhX %s", subsubcommand, description); + this->log().debug("*/CAx%02hhX %s", subsubcommand, description); this->send_debug_message_printf("$C5*/CAx%02hhX %s", subsubcommand, description); } @@ -2372,7 +2426,9 @@ uint32_t Server::send_6xB4x06_if_card_ref_invalid( } void Server::unknown_8023EEF4() { + auto log = this->log_stack("unknown_8023EEF4: "); if (this->unknown_a14 >= 0x20) { + log.debug("unknown_a14 too large (0x%" PRIX32 ")", this->unknown_a14); return; } @@ -2380,15 +2436,26 @@ void Server::unknown_8023EEF4() { auto card = this->attack_cards[this->unknown_a14]; if (this->get_current_team_turn() == card->get_team_id()) { ActionState as = this->pending_attacks_with_cards[this->unknown_a14]; + log.debug("card @%04hX #%04hX can attack", card->get_card_ref(), card->get_card_id()); + string as_str = as.str(); + log.debug("as: %s", as_str.c_str()); this->replace_targets_due_to_destruction_or_conditions(&as); + as_str = as.str(); + log.debug("as after target replacement: %s", as_str.c_str()); if (this->any_target_exists_for_attack(as)) { + log.debug("as is valid"); break; + } else { + log.debug("as is not valid"); } + } else { + log.debug("card @%04hX #%04hX cannot attack (wrong turn)", card->get_card_ref(), card->get_card_id()); } this->unknown_a14++; } if (this->unknown_a14 < this->num_pending_attacks_with_cards) { + log.debug("a14 (%" PRIu32 ") < num_pending_attacks_with_cards (%" PRIu32 ")", this->unknown_a14, this->num_pending_attacks_with_cards); this->defense_list_ended_for_client.clear(false); G_SetActionState_GC_Ep3_6xB4x29 cmd; @@ -2619,8 +2686,10 @@ void Server::unknown_8023EE48() { } void Server::unknown_8023EE80() { + auto log = this->log_stack("unknown_8023EE80: "); if (this->unknown_a14 < this->num_pending_attacks_with_cards) { - this->attack_cards[this->unknown_a14]->unknown_80237734(); + log.debug("applying first attack result"); + this->attack_cards[this->unknown_a14]->apply_attack_result(); this->unknown_a14++; } this->check_for_battle_end(); @@ -2630,17 +2699,20 @@ void Server::unknown_8023EE80() { } void Server::unknown_802402F4() { + auto log = this->log_stack("unknown_802402F4: "); for (size_t client_id = 0; client_id < 4; client_id++) { auto ps = this->player_states[client_id]; if (ps && (this->current_team_turn2 == ps->get_team_id())) { auto card = ps->get_sc_card(); if (card) { - card->compute_action_chain_results(1, 0); + log.debug("SC card has action chain"); + card->compute_action_chain_results(true, false); } for (size_t set_index = 0; set_index < 8; set_index++) { card = ps->get_set_card(set_index); if (card) { - card->compute_action_chain_results(1, 0); + log.debug("set card %zu has action chain", set_index); + card->compute_action_chain_results(true, false); } } } diff --git a/src/Episode3/Server.hh b/src/Episode3/Server.hh index 346bed8d..4b95d6ea 100644 --- a/src/Episode3/Server.hh +++ b/src/Episode3/Server.hh @@ -64,8 +64,25 @@ public: uint32_t behavior_flags, std::shared_ptr random_crypt, std::shared_ptr map_if_tournament); + ~Server() noexcept(false); void init(); + class StackLogger : public PrefixedLogger { + public: + StackLogger(const Server* s, const std::string& prefix); + StackLogger(const Server* s, const std::string& prefix, LogLevel min_level); + StackLogger(const StackLogger&) = delete; + StackLogger(StackLogger&&); + StackLogger& operator=(const StackLogger&) = delete; + StackLogger& operator=(StackLogger&&); + ~StackLogger() noexcept(false); + + private: + const Server* server; + }; + StackLogger log_stack(const std::string& prefix) const; + const StackLogger& log() const; + int8_t get_winner_team_id() const; template @@ -212,12 +229,12 @@ public: std::shared_ptr card_index; std::shared_ptr map_index; uint32_t behavior_flags; - PrefixedLogger log; std::shared_ptr random_crypt; std::shared_ptr last_chosen_map; bool is_tournament; bool tournament_match_result_sent; uint8_t override_environment_number; + mutable std::deque logger_stack; // These fields were originally contained in the TCardServerBase object struct PresenceEntry {