fix using incorrect card object in 59:SLAYERS_ASSASSINS

This commit is contained in:
Martin Michelsen
2024-02-29 22:49:06 -08:00
parent af1c51b2b5
commit 11f49af6f9
8 changed files with 136 additions and 69 deletions
+7 -7
View File
@@ -39,17 +39,17 @@ private:
public:
parray<AssistEffect, 4> assist_effects;
std::shared_ptr<const CardIndex::CardEntry> assist_card_defs[4];
bcarray<std::shared_ptr<const CardIndex::CardEntry>, 4> assist_card_defs;
uint32_t num_assist_cards_set;
parray<uint8_t, 4> client_ids_with_assists;
parray<AssistEffect, 4> active_assist_effects;
std::shared_ptr<const CardIndex::CardEntry> active_assist_card_defs[4];
bcarray<std::shared_ptr<const CardIndex::CardEntry>, 4> active_assist_card_defs;
uint32_t num_active_assists;
std::shared_ptr<HandAndEquipState> hand_and_equip_states[4];
std::shared_ptr<parray<CardShortStatus, 0x10>> card_short_statuses[4];
std::shared_ptr<DeckEntry> deck_entries[4];
std::shared_ptr<parray<ActionChainWithConds, 9>> set_card_action_chains[4];
std::shared_ptr<parray<ActionMetadata, 9>> set_card_action_metadatas[4];
bcarray<std::shared_ptr<HandAndEquipState>, 4> hand_and_equip_states;
bcarray<std::shared_ptr<parray<CardShortStatus, 0x10>>, 4> card_short_statuses;
bcarray<std::shared_ptr<DeckEntry>, 4> deck_entries;
bcarray<std::shared_ptr<parray<ActionChainWithConds, 9>>, 4> set_card_action_chains;
bcarray<std::shared_ptr<parray<ActionMetadata, 9>>, 4> set_card_action_metadatas;
};
} // namespace Episode3
+1 -3
View File
@@ -96,9 +96,7 @@ private:
class BattleRecordPlayer {
public:
BattleRecordPlayer(
std::shared_ptr<const BattleRecord> rec,
std::shared_ptr<struct event_base> base);
BattleRecordPlayer(std::shared_ptr<const BattleRecord> rec, std::shared_ptr<struct event_base> base);
~BattleRecordPlayer() = default;
std::shared_ptr<const BattleRecord> get_record() const;
+12 -20
View File
@@ -393,9 +393,7 @@ bool CardSpecial::apply_defense_condition(
string expr = orig_eff->expr.decode();
int16_t expr_value = this->evaluate_effect_expr(astats, expr.c_str(), dice_roll);
this->execute_effect(
*defender_cond, defender_card, expr_value, defender_cond->value,
orig_eff->type, flags, attacker_card_ref);
this->execute_effect(*defender_cond, defender_card, expr_value, defender_cond->value, orig_eff->type, flags, attacker_card_ref);
if (flags & 4) {
if (is_nte || !(defender_card->card_flags & 2)) {
defender_card->compute_action_chain_results(true, false);
@@ -2461,13 +2459,13 @@ bool CardSpecial::execute_effect(
[[fallthrough]];
case ConditionType::SLAYERS_ASSASSINS:
if (is_nte) {
auto card = s->card_for_set_card_ref(attacker_card_ref);
auto set_card = s->card_for_set_card_ref(attacker_card_ref);
bool card_found = false;
if (!card) {
if (!set_card) {
card_found = false;
} else {
for (size_t z = 0; z < card->action_chain.chain.target_card_ref_count; z++) {
if (card->action_chain.chain.target_card_refs[z] == card->get_card_ref()) {
for (size_t z = 0; z < set_card->action_chain.chain.target_card_ref_count; z++) {
if (set_card->action_chain.chain.target_card_refs[z] == card->get_card_ref()) {
card_found = true;
break;
}
@@ -3511,8 +3509,7 @@ void CardSpecial::on_card_set(shared_ptr<PlayerState> ps, uint16_t card_ref) {
this->evaluate_and_apply_effects(0x01, card_ref, as, sc_card_ref);
}
const CardDefinition::Effect* CardSpecial::original_definition_for_condition(
const Condition& cond) const {
const CardDefinition::Effect* CardSpecial::original_definition_for_condition(const Condition& cond) const {
auto ce = this->server()->definition_for_card_ref(cond.card_ref);
if (!ce) {
return nullptr;
@@ -3526,8 +3523,7 @@ bool CardSpecial::card_ref_has_ability_trap(const Condition& cond) const {
if (!card) {
return false;
} else {
return this->card_has_condition_with_ref(
card, ConditionType::ABILITY_TRAP, 0xFFFF, 0xFFFF);
return this->card_has_condition_with_ref(card, ConditionType::ABILITY_TRAP, 0xFFFF, 0xFFFF);
}
}
@@ -3567,8 +3563,7 @@ void CardSpecial::send_6xB4x06_for_card_destroyed(
this->server()->send(cmd);
}
uint16_t CardSpecial::send_6xB4x06_if_card_ref_invalid(
uint16_t card_ref, int16_t value) const {
uint16_t CardSpecial::send_6xB4x06_if_card_ref_invalid(uint16_t card_ref, int16_t value) const {
auto s = this->server();
if (!s->options.is_nte() && !s->card_ref_is_empty_or_has_valid_card_id(card_ref)) {
if (value != 0) {
@@ -4115,7 +4110,7 @@ void CardSpecial::evaluate_and_apply_effects(
// bug probably does nothing in any reasonable scenario, since the
// target card refs array immediately precedes the conditions array,
// and the target card refs array is excessively long, so OR'ing a
// value that is amost certainly already 0xFFFF with 1 would do
// value that is almost certainly already 0xFFFF with 1 would do
// nothing. In our implementation, however, we bounds-check
// everything, so we've moved this check inside the relevant if block.
if (dice_roll.value_used_in_expr) {
@@ -4140,8 +4135,7 @@ void CardSpecial::evaluate_and_apply_effects(
if (any_expr_used_dice_roll) {
dice_cmd.effect.flags = 0x08;
dice_cmd.effect.attacker_card_ref = this->send_6xB4x06_if_card_ref_invalid(
as_attacker_card_ref, 0x15);
dice_cmd.effect.attacker_card_ref = this->send_6xB4x06_if_card_ref_invalid(as_attacker_card_ref, 0x15);
dice_cmd.effect.dice_roll_value = dice_roll.value;
s->send(dice_cmd);
}
@@ -4671,8 +4665,7 @@ void CardSpecial::unknown_8024AAB8(const ActionState& as) {
log.debug("as=%s", as_str.c_str());
for (size_t z = 0; (z < 8) && (as.action_card_refs[z] != 0xFFFF); z++) {
uint16_t card_ref = this->send_6xB4x06_if_card_ref_invalid(
as.action_card_refs[z], 0x1E);
uint16_t card_ref = this->send_6xB4x06_if_card_ref_invalid(as.action_card_refs[z], 0x1E);
if (card_ref == 0xFFFF) {
break;
}
@@ -4941,8 +4934,7 @@ void CardSpecial::check_for_attack_interference(shared_ptr<Card> unknown_p2) {
G_ApplyConditionEffect_Ep3_6xB4x06 cmd;
cmd.effect.flags = 0x04;
cmd.effect.attacker_card_ref = this->send_6xB4x06_if_card_ref_invalid(
unknown_p2->get_card_ref(), 0x11);
cmd.effect.attacker_card_ref = this->send_6xB4x06_if_card_ref_invalid(unknown_p2->get_card_ref(), 0x11);
cmd.effect.target_card_ref = unknown_p2->get_card_ref();
cmd.effect.value = 0;
cmd.effect.operation = 0x7D;
+1 -1
View File
@@ -148,7 +148,7 @@ private:
public:
std::shared_ptr<Card> sc_card;
std::shared_ptr<Card> set_cards[8];
bcarray<std::shared_ptr<Card>, 8> set_cards;
uint8_t client_id;
uint16_t num_mulligans_allowed;
CardType sc_card_type;
+5 -5
View File
@@ -198,11 +198,11 @@ private:
std::weak_ptr<Server> w_server;
public:
std::shared_ptr<HandAndEquipState> hand_and_equip_states[4];
std::shared_ptr<parray<CardShortStatus, 0x10>> short_statuses[4];
std::shared_ptr<DeckEntry> deck_entries[4];
std::shared_ptr<parray<ActionChainWithConds, 9>> set_card_action_chains[4];
std::shared_ptr<parray<ActionMetadata, 9>> set_card_action_metadatas[4];
bcarray<std::shared_ptr<HandAndEquipState>, 4> hand_and_equip_states;
bcarray<std::shared_ptr<parray<CardShortStatus, 0x10>>, 4> short_statuses;
bcarray<std::shared_ptr<DeckEntry>, 4> deck_entries;
bcarray<std::shared_ptr<parray<ActionChainWithConds, 9>>, 4> set_card_action_chains;
bcarray<std::shared_ptr<parray<ActionMetadata, 9>>, 4> set_card_action_metadatas;
std::shared_ptr<MapAndRulesState> map_and_rules;
std::shared_ptr<StateFlags> state_flags;
std::shared_ptr<AssistServer> assist_server;
+21 -28
View File
@@ -265,30 +265,26 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
void Server::send_6xB4x46() const {
// Note: This function is not part of the original implementation; it was
// factored out from its callsites in this file and the strings were changed.
if (this->options.is_nte()) {
G_ServerVersionStrings_Ep3NTE_6xB4x46 cmd;
cmd.version_signature.encode(VERSION_SIGNATURE_NTE, 1);
cmd.date_str1.encode(format_time(this->options.card_index->definitions_mtime() * 1000000), 1);
this->send(cmd);
// NTE doesn't have the date_str2 field, but we send it anyway to make
// debugging easier.
G_ServerVersionStrings_Ep3_6xB4x46 cmd;
cmd.version_signature.encode(this->options.is_nte() ? VERSION_SIGNATURE_NTE : VERSION_SIGNATURE, 1);
cmd.date_str1.encode(format_time(this->options.card_index->definitions_mtime() * 1000000), 1);
string date_str2;
if (this->options.opt_rand_crypt) {
date_str2 = string_printf(
"Random:%08" PRIX32 "+%08" PRIX32,
this->options.opt_rand_crypt->seed(),
this->options.opt_rand_crypt->absolute_offset());
} else {
G_ServerVersionStrings_Ep3_6xB4x46 cmd;
cmd.version_signature.encode(VERSION_SIGNATURE, 1);
cmd.date_str1.encode(format_time(this->options.card_index->definitions_mtime() * 1000000), 1);
string date_str2;
if (this->options.opt_rand_crypt) {
date_str2 = string_printf(
"Random:%08" PRIX32 "+%08" PRIX32,
this->options.opt_rand_crypt->seed(),
this->options.opt_rand_crypt->absolute_offset());
} else {
date_str2 = "Random:<SYS>";
}
if (this->last_chosen_map) {
date_str2 += string_printf(" Map:%08" PRIX32, this->last_chosen_map->map_number);
}
cmd.date_str2.encode(date_str2, 1);
this->send(cmd);
date_str2 = "Random:<SYS>";
}
if (this->last_chosen_map) {
date_str2 += string_printf(" Map:%08" PRIX32, this->last_chosen_map->map_number);
}
cmd.date_str2.encode(date_str2, 1);
this->send(cmd);
}
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::Map> map, uint8_t language, bool is_nte) {
@@ -2498,8 +2494,7 @@ void Server::handle_CAx34_subtract_ally_atk_points(shared_ptr<Client>, const str
attacker_card->card_flags |= 0x400;
attacker_card->player_state()->send_6xB4x04_if_needed();
}
uint16_t card_ref = this->send_6xB4x06_if_card_ref_invalid(
pa.original_attacker_card_ref, 9);
uint16_t card_ref = this->send_6xB4x06_if_card_ref_invalid(pa.original_attacker_card_ref, 9);
auto orig_attacker_card = this->card_for_set_card_ref(card_ref);
auto target_card = this->card_for_set_card_ref(pa.target_card_refs[0]);
if (orig_attacker_card && target_card) {
@@ -2800,11 +2795,9 @@ uint32_t Server::get_team_exp(uint8_t team_id) const {
return this->team_exp[team_id];
}
uint32_t Server::send_6xB4x06_if_card_ref_invalid(
uint16_t card_ref, int16_t negative_value) {
uint32_t Server::send_6xB4x06_if_card_ref_invalid(uint16_t card_ref, int16_t negative_value) {
if (this->card_special) {
return this->card_special->send_6xB4x06_if_card_ref_invalid(
card_ref, -negative_value);
return this->card_special->send_6xB4x06_if_card_ref_invalid(card_ref, -negative_value);
}
return card_ref;
}
+5 -5
View File
@@ -292,7 +292,7 @@ public:
void clear();
} __attribute__((packed));
std::shared_ptr<MapAndRulesState> map_and_rules;
std::shared_ptr<DeckEntry> deck_entries[4];
bcarray<std::shared_ptr<DeckEntry>, 4> deck_entries;
parray<PresenceEntry, 4> presence_entries;
uint8_t num_clients_present;
parray<NameEntry, 4> name_entries;
@@ -311,7 +311,7 @@ public:
RegistrationPhase registration_phase;
ActionSubphase action_subphase;
uint8_t current_team_turn2;
ActionState pending_attacks[0x20];
bcarray<ActionState, 0x20> pending_attacks;
uint32_t num_pending_attacks;
parray<uint8_t, 4> client_done_enqueuing_attacks;
parray<uint8_t, 4> player_ready_to_end_phase;
@@ -327,8 +327,8 @@ public:
std::array<std::shared_ptr<PlayerState>, 4> player_states;
parray<uint32_t, 4> clients_done_in_mulligan_phase;
uint32_t num_pending_attacks_with_cards;
std::shared_ptr<Card> attack_cards[0x20];
ActionState pending_attacks_with_cards[0x20];
bcarray<std::shared_ptr<Card>, 0x20> attack_cards;
bcarray<ActionState, 0x20> pending_attacks_with_cards;
uint32_t unknown_a14;
uint32_t unknown_a15;
parray<uint32_t, 4> defense_list_ended_for_client;
@@ -346,7 +346,7 @@ public:
parray<parray<parray<uint8_t, 2>, 8>, 5> trap_tile_locs;
parray<parray<uint8_t, 2>, 0x10> trap_tile_locs_nte;
size_t num_trap_tiles_nte;
ActionState pb_action_states[4];
bcarray<ActionState, 4> pb_action_states;
parray<uint8_t, 4> has_done_pb;
parray<parray<uint8_t, 4>, 4> has_done_pb_with_client;
mutable uint32_t num_6xB4x06_commands_sent;
+84
View File
@@ -247,6 +247,90 @@ struct parray {
}
} __attribute__((packed));
template <typename ItemT, size_t Count>
struct bcarray {
ItemT items[Count];
bcarray(ItemT v) {
this->clear(v);
}
bcarray(std::initializer_list<ItemT> init_items) {
for (size_t z = 0; z < init_items.size(); z++) {
this->items[z] = std::data(init_items)[z];
}
this->clear_after(init_items.size());
}
template <typename ArgT = ItemT>
requires(std::is_arithmetic_v<ArgT> || is_converted_endian_sc_v<ArgT>)
bcarray() {
this->clear(0);
}
template <typename ArgT = ItemT>
requires std::is_pointer_v<ArgT>
bcarray() {
this->clear(nullptr);
}
template <typename ArgT = ItemT>
requires(!std::is_arithmetic_v<ArgT> && !std::is_pointer_v<ArgT> && !is_converted_endian_sc_v<ArgT>)
bcarray() {}
bcarray(const bcarray& other) {
this->operator=(other);
}
bcarray(bcarray&& other) {
this->operator=(std::move(other));
}
constexpr static size_t size() {
return Count;
}
ItemT& operator[](size_t index) {
if (index >= Count) {
throw std::out_of_range("array index out of bounds");
}
return *&this->items[index];
}
const ItemT& operator[](size_t index) const {
if (index >= Count) {
throw std::out_of_range("array index out of bounds");
}
return *&this->items[index];
}
ItemT& at(size_t index) {
return this->operator[](index);
}
const ItemT& at(size_t index) const {
return this->operator[](index);
}
bcarray& operator=(const bcarray& s) {
for (size_t x = 0; x < Count; x++) {
this->items[x] = s.items[x];
}
return *this;
}
bcarray& operator=(bcarray&& s) {
for (size_t x = 0; x < Count; x++) {
this->items[x] = std::move(s.items[x]);
}
return *this;
}
bool operator==(const bcarray& s) const {
for (size_t x = 0; x < Count; x++) {
if (this->items[x] != s.items[x]) {
return false;
}
}
return true;
}
bool operator!=(const bcarray& s) const {
return !this->operator==(s);
}
} __attribute__((packed));
// Packed text objects for use in protocol structs
enum class TextEncoding {