fix chained action card conditions not applying

This commit is contained in:
Martin Michelsen
2023-09-12 10:30:33 -07:00
parent 7657d4f2fc
commit 15566f7143
11 changed files with 369 additions and 146 deletions
+104 -15
View File
@@ -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<int16_t>(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<int16_t>(
this->current_hp - effective_damage, 0, this->max_hp);
this->current_hp = clamp<int16_t>(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<Card> 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<Card> 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<int16_t>(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<Card> 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<int16_t>(attack_ap, 99);
@@ -476,6 +517,7 @@ void Card::execute_attack(shared_ptr<Card> 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<Card> 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<size_t>(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<int16_t>(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<int16_t>(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<int16_t>(
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<int16_t>(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<Card> 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> 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<Card> 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<Card> 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<Card> 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<Card> other_card, const ActionState* as)
}
this->action_metadata.attack_bonus = max<int16_t>(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<Card> 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;
}
+2 -1
View File
@@ -59,6 +59,7 @@ public:
uint16_t* out_value) const;
std::shared_ptr<const CardIndex::CardEntry> 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<Card> other_card, const ActionState* as);
void unknown_802362D8(std::shared_ptr<Card> other_card);
void unknown_80237734();
void apply_attack_result();
private:
std::weak_ptr<Server> w_server;
+145 -99
View File
@@ -21,7 +21,7 @@ static string refs_str_for_cards_vector(const vector<shared_ptr<T>>& 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> card) {
bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond(Condition& cond, shared_ptr<Card> 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<int16_t>(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<int16_t>(card->ap, -99, 99);
int16_t tp = clamp<int16_t>(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<int16_t>(card->ap, -99, 99);
int16_t hp = clamp<int16_t>(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<int16_t>(card->ap - cond_value, 0);
log.debug("AP_OVERRIDE: subtracting %hd from AP => %hd", cond_value, card->ap);
} else {
other_cond->value = clamp<int16_t>(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<int16_t>(card->tp - cond_value, 0);
log.debug("TP_OVERRIDE: subtracting %hd from TP => %hd", cond_value, card->tp);
} else {
other_cond->value = clamp<int16_t>(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<int16_t>(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<int16_t>(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<int16_t>(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<int16_t>(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<Card> CardSpecial::compute_replaced_target_based_on_conditions(
}
StatSwapType CardSpecial::compute_stat_swap_type(shared_ptr<const Card> 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<const Card> 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<size_t>(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<int16_t>(expr_value, -99, 99);
int16_t clamped_unknown_p5 = clamp<int16_t>(unknown_p5, -99, 99);
@@ -1909,8 +1945,7 @@ bool CardSpecial::execute_effect(
cmd.effect.operation = -static_cast<int8_t>(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<shared_ptr<const Card>> 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<shared_ptr<const Card>> ret;
@@ -2551,12 +2586,12 @@ vector<shared_ptr<const Card>> 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<shared_ptr<const Card>> 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<shared_ptr<const Card>> 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<uint8_t, 9 * 9> 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<shared_ptr<const Card>> 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<PlayerState> 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<shared_ptr<const Card>>* 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<const Card> 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> 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<shared_ptr<const Card>> 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> 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> card) {
@@ -4358,16 +4399,17 @@ void CardSpecial::unknown_80244CA8(shared_ptr<Card> 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 <uint8_t When1, uint8_t When2>
void CardSpecial::unknown1_t(
shared_ptr<Card> unknown_p2, const ActionState* existing_as) {
void CardSpecial::unknown1_t(shared_ptr<Card> 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<Card> unknown_p2, const ActionStat
}
void CardSpecial::unknown_8024966C(shared_ptr<Card> 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<Card> 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<Card> unknown_p2) {
template <uint8_t When1, uint8_t When2, uint8_t When3, uint8_t When4>
void CardSpecial::unknown_t2(shared_ptr<Card> 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<Card> 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());
}
}
+2 -2
View File
@@ -276,8 +276,8 @@ public:
std::shared_ptr<const Card> attacker_card,
std::shared_ptr<Card> 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,
+3 -3
View File
@@ -293,7 +293,7 @@ static const char* name_for_card_state(DeckState::CardState st) {
}
void DeckState::print(FILE* stream, std::shared_ptr<const CardIndex> 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<const CardIndex> 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));
}
}
+1 -1
View File
@@ -778,7 +778,7 @@ vector<uint16_t> 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);
+6 -6
View File
@@ -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<uint16_t> get_card_refs_within_range(
vector<uint16_t> 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());
}
}
}
+1 -1
View File
@@ -159,7 +159,7 @@ struct ActionChainWithConds {
uint16_t* out_value) const;
void set_action_subphase_from_card(std::shared_ptr<const Card> card);
bool unknown_8024DEC4() const;
bool can_apply_attack() const;
std::string str() const;
} __attribute__((packed));
+8 -10
View File
@@ -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<uint8_t>(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;
}
+79 -7
View File
@@ -34,7 +34,6 @@ Server::Server(shared_ptr<Lobby> 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> 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);
}
}
}
+18 -1
View File
@@ -64,8 +64,25 @@ public:
uint32_t behavior_flags,
std::shared_ptr<PSOLFGEncryption> random_crypt,
std::shared_ptr<const MapIndex::MapEntry> 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 <typename T>
@@ -212,12 +229,12 @@ public:
std::shared_ptr<const CardIndex> card_index;
std::shared_ptr<const MapIndex> map_index;
uint32_t behavior_flags;
PrefixedLogger log;
std::shared_ptr<PSOLFGEncryption> random_crypt;
std::shared_ptr<const MapIndex::MapEntry> last_chosen_map;
bool is_tournament;
bool tournament_match_result_sent;
uint8_t override_environment_number;
mutable std::deque<StackLogger*> logger_stack;
// These fields were originally contained in the TCardServerBase object
struct PresenceEntry {