Ep3 NTE battles checkpoint 2

This commit is contained in:
Martin Michelsen
2024-02-04 16:06:11 -08:00
parent 611193610b
commit c6e930b994
12 changed files with 861 additions and 603 deletions
+52 -46
View File
@@ -6230,16 +6230,16 @@ struct G_UpdateActionChainAndMetadata_Ep3_6xB4x0A {
// 6xB3x0B / CAx0B: Redraw initial hand (immediately before battle)
struct G_RedrawInitialHand_Ep3_6xB3x0B_CAx0B {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_RedrawInitialHand_Ep3_6xB3x0B_CAx0B) / 4, 0, 0x0B, 0, 0, 0, 0, 0};
struct G_RedrawInitialHand_Ep3_CAx0B {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_RedrawInitialHand_Ep3_CAx0B) / 4, 0, 0x0B, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
parray<uint8_t, 2> unused2;
} __packed__;
// 6xB3x0C / CAx0C: End initial redraw phase
struct G_EndInitialRedrawPhase_Ep3_6xB3x0C_CAx0C {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndInitialRedrawPhase_Ep3_6xB3x0C_CAx0C) / 4, 0, 0x0C, 0, 0, 0, 0, 0};
struct G_EndInitialRedrawPhase_Ep3_CAx0C {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndInitialRedrawPhase_Ep3_CAx0C) / 4, 0, 0x0C, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
parray<uint8_t, 2> unused2;
} __packed__;
@@ -6249,24 +6249,24 @@ struct G_EndInitialRedrawPhase_Ep3_6xB3x0C_CAx0C {
// current phase. This command isn't used for ending the attack or defense
// phases; for those phases, CAx12 and CAx28 are used instead.
struct G_EndNonAttackPhase_Ep3_6xB3x0D_CAx0D {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndNonAttackPhase_Ep3_6xB3x0D_CAx0D) / 4, 0, 0x0D, 0, 0, 0, 0, 0};
struct G_EndNonAttackPhase_Ep3_CAx0D {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndNonAttackPhase_Ep3_CAx0D) / 4, 0, 0x0D, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
parray<le_uint16_t, 5> unused2;
} __packed__;
// 6xB3x0E / CAx0E: Discard card from hand
struct G_DiscardCardFromHand_Ep3_6xB3x0E_CAx0E {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_DiscardCardFromHand_Ep3_6xB3x0E_CAx0E) / 4, 0, 0x0E, 0, 0, 0, 0, 0};
struct G_DiscardCardFromHand_Ep3_CAx0E {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_DiscardCardFromHand_Ep3_CAx0E) / 4, 0, 0x0E, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
le_uint16_t card_ref = 0xFFFF;
} __packed__;
// 6xB3x0F / CAx0F: Set card from hand
struct G_SetCardFromHand_Ep3_6xB3x0F_CAx0F {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetCardFromHand_Ep3_6xB3x0F_CAx0F) / 4, 0, 0x0F, 0, 0, 0, 0, 0};
struct G_SetCardFromHand_Ep3_CAx0F {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetCardFromHand_Ep3_CAx0F) / 4, 0, 0x0F, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
le_uint16_t card_ref = 0xFFFF;
le_uint16_t set_index = 0;
@@ -6276,8 +6276,8 @@ struct G_SetCardFromHand_Ep3_6xB3x0F_CAx0F {
// 6xB3x10 / CAx10: Move field character
struct G_MoveFieldCharacter_Ep3_6xB3x10_CAx10 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_MoveFieldCharacter_Ep3_6xB3x10_CAx10) / 4, 0, 0x10, 0, 0, 0, 0, 0};
struct G_MoveFieldCharacter_Ep3_CAx10 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_MoveFieldCharacter_Ep3_CAx10) / 4, 0, 0x10, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
le_uint16_t set_index = 0;
Episode3::Location loc;
@@ -6289,8 +6289,8 @@ struct G_MoveFieldCharacter_Ep3_6xB3x10_CAx10 {
// sent once for each attack (even if it includes multiple cards); in the
// defense case, this command is sent once for each defense card.
struct G_EnqueueAttackOrDefense_Ep3_6xB3x11_CAx11 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EnqueueAttackOrDefense_Ep3_6xB3x11_CAx11) / 4, 0, 0x11, 0, 0, 0, 0, 0};
struct G_EnqueueAttackOrDefense_Ep3_CAx11 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EnqueueAttackOrDefense_Ep3_CAx11) / 4, 0, 0x11, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
parray<uint8_t, 2> unused2;
Episode3::ActionState entry;
@@ -6300,24 +6300,30 @@ struct G_EnqueueAttackOrDefense_Ep3_6xB3x11_CAx11 {
// This command informs the server that the client is done playing attacks in
// the current round. (In the defense phase, CAx28 is used instead.)
struct G_EndAttackList_Ep3_6xB3x12_CAx12 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndAttackList_Ep3_6xB3x12_CAx12) / 4, 0, 0x12, 0, 0, 0, 0, 0};
struct G_EndAttackList_Ep3_CAx12 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndAttackList_Ep3_CAx12) / 4, 0, 0x12, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
parray<uint8_t, 2> unused2;
} __packed__;
// 6xB3x13 / CAx13: Set map state during setup
struct G_SetMapState_Ep3_6xB3x13_CAx13 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetMapState_Ep3_6xB3x13_CAx13) / 4, 0, 0x13, 0, 0, 0, 0, 0};
struct G_SetMapState_Ep3NTE_CAx13 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetMapState_Ep3NTE_CAx13) / 4, 0, 0x13, 0, 0, 0, 0, 0};
Episode3::MapAndRulesStateTrial map_and_rules_state;
Episode3::OverlayState overlay_state;
} __packed__;
struct G_SetMapState_Ep3_CAx13 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetMapState_Ep3_CAx13) / 4, 0, 0x13, 0, 0, 0, 0, 0};
Episode3::MapAndRulesState map_and_rules_state;
Episode3::OverlayState overlay_state;
} __packed__;
// 6xB3x14 / CAx14: Set player deck during setup
struct G_SetPlayerDeck_Ep3_6xB3x14_CAx14 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetPlayerDeck_Ep3_6xB3x14_CAx14) / 4, 0, 0x14, 0, 0, 0, 0, 0};
struct G_SetPlayerDeck_Ep3_CAx14 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetPlayerDeck_Ep3_CAx14) / 4, 0, 0x14, 0, 0, 0, 0, 0};
le_uint16_t client_id = 0;
uint8_t is_cpu_player = 0;
uint8_t unused2 = 0;
@@ -6327,8 +6333,8 @@ struct G_SetPlayerDeck_Ep3_6xB3x14_CAx14 {
// 6xB3x15 / CAx15: Hard-reset server state
// This command appears to be completely unused; the client never sends it.
struct G_HardResetServerState_Ep3_6xB3x15_CAx15 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_HardResetServerState_Ep3_6xB3x15_CAx15) / 4, 0, 0x15, 0, 0, 0, 0, 0};
struct G_HardResetServerState_Ep3_CAx15 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_HardResetServerState_Ep3_CAx15) / 4, 0, 0x15, 0, 0, 0, 0, 0};
// No arguments
} __packed__;
@@ -6356,8 +6362,8 @@ struct G_ForceDisconnect_Ep3_6xB5x1A {
// Curiously, this command can be used during a non-setup phase; the server
// should ignore the command's contents but still send a 6xB4x1C in response.
struct G_SetPlayerName_Ep3_6xB3x1B_CAx1B {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetPlayerName_Ep3_6xB3x1B_CAx1B) / 4, 0, 0x1B, 0, 0, 0, 0, 0};
struct G_SetPlayerName_Ep3_CAx1B {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_SetPlayerName_Ep3_CAx1B) / 4, 0, 0x1B, 0, 0, 0, 0, 0};
Episode3::NameEntry entry;
} __packed__;
@@ -6373,8 +6379,8 @@ struct G_SetPlayerNames_Ep3_6xB4x1C {
// response to this command) that includes RegistrationPhase::BATTLE_STARTED and
// a SetupPhase value other than REGISTRATION.
struct G_StartBattle_Ep3_6xB3x1D_CAx1D {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_StartBattle_Ep3_6xB3x1D_CAx1D) / 4, 0, 0x1D, 0, 0, 0, 0, 0};
struct G_StartBattle_Ep3_CAx1D {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_StartBattle_Ep3_CAx1D) / 4, 0, 0x1D, 0, 0, 0, 0, 0};
} __packed__;
// 6xB4x1E: Action result
@@ -6412,8 +6418,8 @@ struct G_Unknown_Ep3_6xB5x20 {
// 6xB3x21 / CAx21: End battle
struct G_EndBattle_Ep3_6xB3x21_CAx21 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndBattle_Ep3_6xB3x21_CAx21) / 4, 0, 0x21, 0, 0, 0, 0, 0};
struct G_EndBattle_Ep3_CAx21 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndBattle_Ep3_CAx21) / 4, 0, 0x21, 0, 0, 0, 0, 0};
le_uint32_t unused2 = 0;
} __packed__;
@@ -6455,8 +6461,8 @@ struct G_Unknown_Ep3_6xB5x27 {
// This command informs the server that the client is done playing defense
// cards. (In the attack phase, CAx12 is used instead.)
struct G_EndDefenseList_Ep3_6xB3x28_CAx28 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndDefenseList_Ep3_6xB3x28_CAx28) / 4, 0, 0x28, 0, 0, 0, 0, 0};
struct G_EndDefenseList_Ep3_CAx28 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndDefenseList_Ep3_CAx28) / 4, 0, 0x28, 0, 0, 0, 0, 0};
uint8_t unused1 = 0;
uint8_t client_id = 0;
parray<uint8_t, 2> unused2;
@@ -6488,8 +6494,8 @@ struct G_Unknown_Ep3_6xB4x2A {
// It seems Sega's servers completely ignored this command. The command name is
// based on a debug message found nearby.
struct G_ExecLegacyCard_Ep3_6xB3x2B_CAx2B {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_ExecLegacyCard_Ep3_6xB3x2B_CAx2B) / 4, 0, 0x2B, 0, 0, 0, 0, 0};
struct G_ExecLegacyCard_Ep3_CAx2B {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_ExecLegacyCard_Ep3_CAx2B) / 4, 0, 0x2B, 0, 0, 0, 0, 0};
le_uint16_t unused2 = 0;
parray<uint8_t, 2> unused3;
} __packed__;
@@ -6592,8 +6598,8 @@ struct G_SubtractAllyATKPoints_Ep3_6xB4x33 {
// 6xB3x34 / CAx34: Photon blast request
struct G_PhotonBlastRequest_Ep3_6xB3x34_CAx34 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_PhotonBlastRequest_Ep3_6xB3x34_CAx34) / 4, 0, 0x34, 0, 0, 0, 0, 0};
struct G_PhotonBlastRequest_Ep3_CAx34 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_PhotonBlastRequest_Ep3_CAx34) / 4, 0, 0x34, 0, 0, 0, 0, 0};
uint8_t ally_client_id = 0;
uint8_t reason = 0;
le_uint16_t card_ref = 0xFFFF;
@@ -6624,8 +6630,8 @@ struct G_RecreatePlayer_Ep3_6xB5x36 {
// 6xB3x37 / CAx37: Ready to advance from starting rolls phase
struct G_AdvanceFromStartingRollsPhase_Ep3_6xB3x37_CAx37 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_AdvanceFromStartingRollsPhase_Ep3_6xB3x37_CAx37) / 4, 0, 0x37, 0, 0, 0, 0, 0};
struct G_AdvanceFromStartingRollsPhase_Ep3_CAx37 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_AdvanceFromStartingRollsPhase_Ep3_CAx37) / 4, 0, 0x37, 0, 0, 0, 0, 0};
uint8_t client_id = 0;
parray<uint8_t, 3> unused2;
} __packed__;
@@ -6661,8 +6667,8 @@ struct G_UpdateAllPlayerStatistics_Ep3_6xB4x39 {
// It seems Sega's servers completely ignored this command and used server-side
// timing instead. newserv does the same.
struct G_OverallTimeLimitExpired_Ep3_6xB3x3A_CAx3A {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_OverallTimeLimitExpired_Ep3_6xB3x3A_CAx3A) / 4, 0, 0x3A, 0, 0, 0, 0, 0};
struct G_OverallTimeLimitExpired_Ep3_CAx3A {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_OverallTimeLimitExpired_Ep3_CAx3A) / 4, 0, 0x3A, 0, 0, 0, 0, 0};
} __packed__;
// 6xB4x3B: Load current environment
@@ -6748,16 +6754,16 @@ struct G_OpenBlockingMenu_Ep3_6xB5x3F {
// 6xB3x40 / CAx40: Request map list
// The server should respond with a 6xB6x40 command.
struct G_MapListRequest_Ep3_6xB3x40_CAx40 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_MapListRequest_Ep3_6xB3x40_CAx40) / 4, 0, 0x40, 0, 0, 0, 0, 0};
struct G_MapListRequest_Ep3_CAx40 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_MapListRequest_Ep3_CAx40) / 4, 0, 0x40, 0, 0, 0, 0, 0};
} __packed__;
// 6xB3x41 / CAx41: Request map data
// The server should respond with a 6xB6x41 command containing the definition of
// the specified map.
struct G_MapDataRequest_Ep3_6xB3x41_CAx41 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_MapDataRequest_Ep3_6xB3x41_CAx41) / 4, 0, 0x41, 0, 0, 0, 0, 0};
struct G_MapDataRequest_Ep3_CAx41 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_MapDataRequest_Ep3_CAx41) / 4, 0, 0x41, 0, 0, 0, 0, 0};
le_uint32_t map_number = 0;
} __packed__;
@@ -6865,8 +6871,8 @@ struct G_SetSpectatorCARDLevel_Ep3_6xB5x47 {
// 6xB3x48 / CAx48: End turn
struct G_EndTurn_Ep3_6xB3x48_CAx48 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndTurn_Ep3_6xB3x48_CAx48) / 4, 0, 0x48, 0, 0, 0, 0, 0};
struct G_EndTurn_Ep3_CAx48 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_EndTurn_Ep3_CAx48) / 4, 0, 0x48, 0, 0, 0, 0, 0};
uint8_t client_id = 0;
parray<uint8_t, 3> unused2;
} __packed__;
@@ -6883,8 +6889,8 @@ struct G_EndTurn_Ep3_6xB3x48_CAx48 {
// that callsite to implement one of the deck validity checks.
// Episode 3 Trial Edition does not send this command.
struct G_CardCounts_Ep3_6xB3x49_CAx49 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_CardCounts_Ep3_6xB3x49_CAx49) / 4, 0, 0x49, 0, 0, 0, 0, 0};
struct G_CardCounts_Ep3_CAx49 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_CardCounts_Ep3_CAx49) / 4, 0, 0x49, 0, 0, 0, 0, 0};
uint8_t basis = 0;
parray<uint8_t, 3> unused;
// This is encrypted with the trivial algorithm (see decrypt_trivial_gci_data)
+122 -86
View File
@@ -122,14 +122,16 @@ 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));
auto s = this->server();
auto log = s->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));
bool is_trial = s->options.is_trial();
ssize_t existing_cond_index;
for (size_t z = 0; z < this->action_chain.conditions.size(); z++) {
const auto& cond = this->action_chain.conditions[z];
if (cond.type == eff.type) {
existing_cond_index = z;
if (eff.type == ConditionType::MV_BONUS ||
if ((!is_trial && eff.type == ConditionType::MV_BONUS) ||
((cond.card_definition_effect_index == def_effect_index) &&
(cond.card_ref == target_card_ref))) {
break;
@@ -165,7 +167,7 @@ ssize_t Card::apply_abnormal_condition(
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());
s->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;
@@ -205,14 +207,15 @@ ssize_t Card::apply_abnormal_condition(
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;
if (!is_trial) {
s->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());
}
string cond_str = cond.str();
log.debug("sorted conditions: [%zu] => %s", z, cond_str.c_str());
}
return cond_index;
@@ -368,25 +371,27 @@ int16_t Card::compute_defense_power_for_attacker_card(
}
void Card::destroy_set_card(shared_ptr<Card> attacker_card) {
auto s = this->server();
auto ps = this->player_state();
this->current_hp = 0;
if (!(this->card_flags & 2)) {
if (!this->server()->ruler_server->card_ref_or_any_set_card_has_condition_46(this->card_ref)) {
this->server()->card_special->on_card_destroyed(
if (!s->ruler_server->card_ref_or_any_set_card_has_condition_46(this->card_ref)) {
s->card_special->on_card_destroyed(
attacker_card, this->shared_from_this());
this->card_flags = this->card_flags | 2;
this->update_stats_on_destruction();
this->player_state()->stats.num_owned_cards_destroyed++;
ps->stats.num_owned_cards_destroyed++;
if (attacker_card && (attacker_card->team_id != this->team_id)) {
attacker_card->player_state()->stats.num_opponent_cards_destroyed++;
this->server()->add_team_exp(this->team_id ^ 1, 3);
s->add_team_exp(this->team_id ^ 1, 3);
}
if ((this->sc_card_type == CardType::HUNTERS_SC) && (this->def_entry->def.type == CardType::ITEM)) {
auto sc_card = this->player_state()->get_sc_card();
if (!(sc_card->card_flags & 2) &&
!sc_card->get_attack_condition_value(ConditionType::ELUDE, 0xFFFF, 0xFF, 0xFFFF, nullptr)) {
auto sc_card = ps->get_sc_card();
if (!(sc_card->card_flags & 2) && !sc_card->get_condition_value(ConditionType::ELUDE)) {
int16_t hp = sc_card->get_current_hp();
sc_card->set_current_hp(hp - 1);
sc_card->player_state()->stats.sc_damage_taken++;
@@ -396,7 +401,7 @@ void Card::destroy_set_card(shared_ptr<Card> attacker_card) {
cmd.effect.attacker_card_ref = attacker_card->card_ref;
cmd.effect.target_card_ref = sc_card->card_ref;
cmd.effect.value = 1;
this->server()->send(cmd);
s->send(cmd);
}
if (sc_card->get_current_hp() < 1) {
sc_card->destroy_set_card(attacker_card);
@@ -404,10 +409,10 @@ void Card::destroy_set_card(shared_ptr<Card> attacker_card) {
}
}
if ((this->server()->map_and_rules->rules.hp_type == HPType::DEFEAT_TEAM) &&
(this->player_state()->get_sc_card().get() == this)) {
if ((s->map_and_rules->rules.hp_type == HPType::DEFEAT_TEAM) &&
(ps->get_sc_card().get() == this)) {
for (size_t set_index = 0; set_index < 8; set_index++) {
auto card = this->player_state()->get_set_card(set_index);
auto card = ps->get_set_card(set_index);
if (card) {
card->card_flags |= 2;
}
@@ -415,27 +420,27 @@ void Card::destroy_set_card(shared_ptr<Card> attacker_card) {
}
for (size_t client_id = 0; client_id < 4; client_id++) {
if (!this->server()->player_states[client_id]) {
if (!s->player_states[client_id]) {
continue;
}
size_t num_assists = this->server()->assist_server->compute_num_assist_effects_for_client(client_id);
size_t num_assists = s->assist_server->compute_num_assist_effects_for_client(client_id);
for (size_t z = 0; z < num_assists; z++) {
auto eff = this->server()->assist_server->get_active_assist_by_index(z);
auto eff = s->assist_server->get_active_assist_by_index(z);
if (eff == AssistEffect::HOMESICK) {
if (client_id == this->client_id) {
this->player_state()->return_set_card_to_hand2(this->card_ref);
ps->return_set_card_to_hand2(this->card_ref);
}
} else if (eff == AssistEffect::INHERITANCE) {
uint8_t other_team_id = this->server()->player_states[client_id]->get_team_id();
uint8_t this_team_id = this->player_state()->get_team_id();
uint8_t other_team_id = s->player_states[client_id]->get_team_id();
uint8_t this_team_id = ps->get_team_id();
if (this_team_id == other_team_id) {
this->server()->add_team_exp(team_id, this->max_hp);
s->add_team_exp(team_id, this->max_hp);
}
}
}
}
} else if (this->w_destroyer_sc_card.expired() && attacker_card) {
} else if (!this->w_destroyer_sc_card.lock() && attacker_card) {
this->w_destroyer_sc_card = attacker_card->player_state()->get_sc_card();
}
}
@@ -560,14 +565,27 @@ void Card::execute_attack(shared_ptr<Card> attacker_card) {
}
}
bool Card::get_attack_condition_value(
bool Card::get_condition_value(
ConditionType cond_type,
uint16_t card_ref,
uint8_t def_effect_index,
uint16_t value,
uint16_t* out_value) const {
return this->action_chain.get_condition_value(
cond_type, card_ref, def_effect_index, value, out_value);
return this->action_chain.get_condition_value(cond_type, card_ref, def_effect_index, value, out_value);
}
Condition* Card::find_condition(ConditionType cond_type) {
for (size_t z = 0; z < this->action_chain.conditions.size(); z++) {
auto& cond = this->action_chain.conditions[z];
if (cond.type == cond_type) {
return &cond;
}
}
return nullptr;
}
const Condition* Card::find_condition(ConditionType cond_type) const {
return const_cast<Card*>(this)->find_condition(cond_type);
}
shared_ptr<const CardIndex::CardEntry> Card::get_definition() const {
@@ -808,10 +826,12 @@ void Card::clear_action_chain_and_metadata_and_most_flags() {
this->action_metadata.card_ref = this->card_ref;
}
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());
void Card::compute_action_chain_results(bool apply_action_conditions, bool ignore_this_card_ap_tp) {
auto s = this->server();
auto log = s->log_stack(string_printf("compute_action_chain_results(@%04hX #%04hX): ", this->get_card_ref(), this->get_card_id()));
bool is_trial = s->options.is_trial();
this->action_chain.compute_attack_medium(s);
this->action_chain.chain.strike_count = 1;
this->action_chain.chain.ap_effect_bonus = 0;
this->action_chain.chain.tp_effect_bonus = 0;
@@ -824,16 +844,19 @@ void Card::compute_action_chain_results(
int16_t card_ap;
int16_t card_tp;
auto stat_swap_type = this->server()->card_special->compute_stat_swap_type(this->shared_from_this());
auto stat_swap_type = is_trial ? StatSwapType::NONE : s->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);
s->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_ap = card_ap;
int16_t effective_tp = card_tp;
int16_t effective_ap = this->ap;
int16_t effective_tp = this->tp;
// This option doesn't exist in NTE
ignore_this_card_ap_tp &= !is_trial;
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]);
auto ce = s->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;
@@ -842,11 +865,12 @@ void Card::compute_action_chain_results(
}
// Add AP/TP from MAG items to SC's AP/TP
auto ps = this->player_state();
if (this->def_entry->def.is_sc()) {
for (size_t set_index = 0; set_index < 8; set_index++) {
auto card = this->player_state()->get_set_card(set_index);
auto card = ps->get_set_card(set_index);
if ((card && (card->def_entry->def.card_class() == CardClass::MAG_ITEM)) && !(card->card_flags & 2)) {
this->server()->card_special->get_effective_ap_tp(
s->card_special->get_effective_ap_tp(
stat_swap_type, &card_ap, &card_tp, card->get_current_hp(), card->ap, card->tp);
effective_ap += card_ap;
effective_tp += card_tp;
@@ -857,7 +881,7 @@ void Card::compute_action_chain_results(
}
if ((this->def_entry->def.type == CardType::ITEM) && this->sc_def_entry) {
auto sc_card = this->player_state()->get_sc_card();
auto sc_card = ps->get_sc_card();
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;
@@ -866,50 +890,58 @@ void Card::compute_action_chain_results(
}
if (!this->action_chain.check_flag(0x10)) {
this->action_chain.chain.effective_ap = min<int16_t>(effective_ap, 99);
this->action_chain.chain.effective_ap = is_trial ? 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);
this->action_chain.chain.effective_tp = is_trial ? 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);
auto this_sh = this->shared_from_this();
s->card_special->apply_action_conditions(3, this_sh, this_sh, 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);
size_t num_assists = s->assist_server->compute_num_assist_effects_for_client(this->client_id);
for (size_t z = 0; z < num_assists; z++) {
switch (this->server()->assist_server->get_active_assist_by_index(z)) {
switch (s->assist_server->get_active_assist_by_index(z)) {
case AssistEffect::POWERLESS_RAIN:
if (this->card_type_is_sc_or_creature() &&
if (!is_trial &&
this->card_type_is_sc_or_creature() &&
(this->action_chain.chain.attack_medium == AttackMedium::PHYSICAL)) {
this->action_chain.chain.ap_effect_bonus -= 2;
}
break;
case AssistEffect::BRAVE_WIND:
if (this->card_type_is_sc_or_creature() &&
if (!is_trial &&
this->card_type_is_sc_or_creature() &&
(this->action_chain.chain.attack_medium == AttackMedium::PHYSICAL)) {
this->action_chain.chain.ap_effect_bonus += 2;
}
break;
case AssistEffect::INFLUENCE:
if (this->card_type_is_sc_or_creature()) {
int16_t count = this->player_state()->count_set_refs();
if (!is_trial &&
this->card_type_is_sc_or_creature()) {
int16_t count = ps->count_set_refs();
this->action_chain.chain.ap_effect_bonus += (count >> 1);
}
break;
case AssistEffect::AP_ABSORPTION:
if (this->action_chain.chain.attack_medium == AttackMedium::TECH) {
if (!is_trial && (this->action_chain.chain.attack_medium == AttackMedium::TECH)) {
this->action_chain.chain.tp_effect_bonus += 2;
}
break;
case AssistEffect::FIX:
if (is_trial && !this->def_entry->def.is_sc()) {
this->action_chain.chain.ap_effect_bonus = 2 - this->action_chain.chain.card_ap;
}
break;
case AssistEffect::TECH_FIELD:
if (this->card_type_is_sc_or_creature()) {
if (is_trial ? this->def_entry->def.is_sc() : this->card_type_is_sc_or_creature()) {
this->action_chain.chain.tp_effect_bonus += 2;
}
break;
@@ -962,7 +994,7 @@ void Card::compute_action_chain_results(
if (this->def_entry->def.is_sc()) {
size_t num_scs_in_range = 0;
for (size_t client_id = 0; client_id < 4; client_id++) {
auto other_ps = this->server()->get_player_state(client_id);
auto other_ps = s->get_player_state(client_id);
if (!other_ps || (client_id == this->client_id) || (other_ps->get_team_id() != this->team_id)) {
continue;
}
@@ -980,8 +1012,8 @@ void Card::compute_action_chain_results(
break;
case AssistEffect::VENGEANCE:
if (!this->def_entry->def.is_sc()) {
this->action_chain.chain.ap_effect_bonus +=
(this->server()->team_num_ally_fcs_destroyed[this->team_id] / 3);
size_t denom = is_trial ? 2 : 3;
this->action_chain.chain.ap_effect_bonus += (s->team_num_ally_fcs_destroyed[this->team_id] / denom);
}
break;
default:
@@ -999,42 +1031,46 @@ void Card::compute_action_chain_results(
} else {
log.debug("(unknown attack medium) damage = 0");
}
this->action_chain.chain.damage = min<int16_t>(
damage * this->action_chain.chain.damage_multiplier, 99);
this->action_chain.chain.damage = is_trial
? (damage * this->action_chain.chain.damage_multiplier)
: 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(
0x03, this->shared_from_this(), this->shared_from_this(), 2, nullptr);
auto this_sh = this->shared_from_this();
s->card_special->apply_action_conditions(0x03, this_sh, this_sh, 2, nullptr);
log.debug("applied action conditions (2)");
if (this->action_chain.check_flag(0x100)) {
if (!is_trial && 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)");
log.debug("skipped applying action conditions (2)");
}
num_assists = this->server()->assist_server->compute_num_assist_effects_for_client(this->get_client_id());
for (size_t z = 0; z < num_assists; z++) {
switch (this->server()->assist_server->get_active_assist_by_index(z)) {
case AssistEffect::AP_ABSORPTION:
if (this->action_chain.chain.attack_medium == AttackMedium::PHYSICAL) {
this->action_chain.chain.damage = 0;
}
break;
case AssistEffect::SILENT_COLOSSEUM:
if (this->action_chain.chain.damage >= 7) {
this->action_chain.chain.damage = 0;
}
break;
case AssistEffect::FIX:
if (!this->def_entry->def.is_sc()) {
this->action_chain.chain.damage = 2;
}
break;
default:
break;
if (!is_trial) {
num_assists = s->assist_server->compute_num_assist_effects_for_client(this->get_client_id());
for (size_t z = 0; z < num_assists; z++) {
switch (s->assist_server->get_active_assist_by_index(z)) {
case AssistEffect::AP_ABSORPTION:
if (this->action_chain.chain.attack_medium == AttackMedium::PHYSICAL) {
this->action_chain.chain.damage = 0;
}
break;
case AssistEffect::SILENT_COLOSSEUM:
if (this->action_chain.chain.damage >= 7) {
this->action_chain.chain.damage = 0;
}
break;
case AssistEffect::FIX:
if (!this->def_entry->def.is_sc()) {
this->action_chain.chain.damage = 2;
}
break;
default:
break;
}
}
}
}
+7 -5
View File
@@ -51,12 +51,14 @@ public:
void destroy_set_card(std::shared_ptr<Card> attacker_card);
int32_t error_code_for_move_to_location(const Location& loc) const;
void execute_attack(std::shared_ptr<Card> attacker_card);
bool get_attack_condition_value(
bool get_condition_value(
ConditionType cond_type,
uint16_t card_ref,
uint8_t def_effect_index,
uint16_t value,
uint16_t* out_value) const;
uint16_t card_ref = 0xFFFF,
uint8_t def_effect_index = 0xFF,
uint16_t value = 0xFFFF,
uint16_t* out_value = nullptr) const;
Condition* find_condition(ConditionType cond_type);
const Condition* find_condition(ConditionType cond_type) const;
std::shared_ptr<const CardIndex::CardEntry> get_definition() const;
uint16_t get_card_ref() const;
uint16_t get_card_id() const;
File diff suppressed because it is too large Load Diff
+18 -7
View File
@@ -341,10 +341,15 @@ const char* name_for_direction(Direction d) {
}
}
bool card_class_is_tech_like(CardClass cc) {
return (cc == CardClass::TECH) ||
(cc == CardClass::PHOTON_BLAST) ||
(cc == CardClass::BOSS_TECH);
bool card_class_is_tech_like(CardClass cc, bool is_trial) {
// NTE does not consider BOSS_TECH to be a tech-like card class, but that's
// probably because that card class just doesn't exist on NTE. Still, we
// handle
if (is_trial) {
return (cc == CardClass::TECH) || (cc == CardClass::PHOTON_BLAST);
} else {
return (cc == CardClass::TECH) || (cc == CardClass::PHOTON_BLAST) || (cc == CardClass::BOSS_TECH);
}
}
static const unordered_map<string, const char*> description_for_expr_token({
@@ -745,8 +750,14 @@ string CardDefinition::Effect::str_for_arg(const string& arg) {
} catch (const out_of_range&) {
return arg + " (Req. condition: unknown)";
}
case 'o':
return arg + " (Req. prev effect conditions passed)";
case 'o': {
const char* suffix = ((value / 10) == 1) ? " on opponent card" : " on self";
if (value == 0) {
return string_printf("%s (Req. any previous effect%s)", arg.c_str(), suffix);
} else {
return string_printf("%s (Req. effect %zu passed%s)", arg.c_str(), static_cast<size_t>(value % 10), suffix);
}
}
case 'p':
try {
return string_printf("%s (Target: %s)", arg.c_str(), description_for_p_target.at(value));
@@ -1456,7 +1467,7 @@ RulesTrial::RulesTrial(const Rules& r)
phase_time_limit(r.phase_time_limit),
allowed_cards(r.allowed_cards),
atk_dice_max(r.max_dice),
def_dice_max(r.max_dice),
def_dice_max(r.def_dice_range ? (r.def_dice_range & 0x0F) : r.max_dice),
disable_deck_shuffle(r.disable_deck_shuffle),
disable_deck_loop(r.disable_deck_loop),
char_hp(r.char_hp),
+3 -3
View File
@@ -164,7 +164,7 @@ enum class CardClass : uint16_t {
};
const char* name_for_card_class(CardClass cc);
bool card_class_is_tech_like(CardClass cc);
bool card_class_is_tech_like(CardClass cc, bool is_trial);
enum class TargetMode : uint8_t {
NONE = 0x00, // Used for defense cards, mags, shields, etc.
@@ -300,7 +300,7 @@ enum class ConditionType : uint8_t {
UNKNOWN_75 = 0x75,
REFLECT = 0x76, // Generate reverse attack
UNKNOWN_77 = 0x77,
ANY = 0x78, // Not a real condition; used as a wildcard in search functions
ANY = 0x78, // Not a real condition; used as a wildcard in search functions. Has value 0x64 on NTE
UNKNOWN_79 = 0x79,
UNKNOWN_7A = 0x7A,
UNKNOWN_7B = 0x7B,
@@ -984,7 +984,7 @@ struct RulesTrial {
/* 00 */ uint8_t overall_time_limit = 0;
/* 01 */ uint8_t phase_time_limit = 0;
/* 02 */ AllowedCards allowed_cards = AllowedCards::ALL;
/* 03 */ uint8_t atk_dice_max = 1; // TODO: Are these actually maxes? Look at the dice roll function
/* 03 */ uint8_t atk_dice_max = 6;
/* 04 */ uint8_t def_dice_max = 6;
/* 05 */ uint8_t disable_deck_shuffle = 0;
/* 06 */ uint8_t disable_deck_loop = 0;
+47 -11
View File
@@ -93,7 +93,7 @@ void PlayerState::init() {
this->draw_initial_hand();
if (s->options.is_trial()) {
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
// TODO: NTE calls 80243310(1) here
this->send_set_card_updates(true);
}
s->assist_server->hand_and_equip_states[this->client_id] = this->hand_and_equip;
@@ -1167,25 +1167,29 @@ uint8_t PlayerState::roll_dice_with_effects(size_t num_dice) {
void PlayerState::send_set_card_updates(bool always_send) {
auto s = this->server();
bool is_trial = s->options.is_trial();
uint16_t mask;
if (!this->sc_card) {
uint16_t mask = 0;
if (this->sc_card) {
this->sc_card->send_6xB4x4E_4C_4D_if_needed(always_send);
} else if (is_trial) {
this->send_6xB4x0A_for_set_card(0);
} else {
this->set_card_action_chains->at(0).clear();
this->set_card_action_metadatas->at(0).clear();
mask = 1;
} else {
this->sc_card->send_6xB4x4E_4C_4D_if_needed(always_send);
mask = 0;
mask |= 1;
}
for (size_t set_index = 0; set_index < 8; set_index++) {
auto card = this->set_cards[set_index];
if (!card) {
if (card) {
card->send_6xB4x4E_4C_4D_if_needed(always_send);
} else if (is_trial) {
this->send_6xB4x0A_for_set_card(set_index + 1);
} else {
mask |= 1 << (set_index + 1);
this->set_card_action_chains->at(set_index + 1).clear();
this->set_card_action_metadatas->at(set_index + 1).clear();
} else {
card->send_6xB4x4E_4C_4D_if_needed(always_send);
}
}
@@ -1773,7 +1777,7 @@ void PlayerState::unknown_8023C174() {
AssistFlag::ELIGIBLE_FOR_DICE_BOOST);
this->set_assist_flags_from_assist_effects();
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed(0);
this->send_set_card_updates(0);
this->send_set_card_updates();
}
void PlayerState::handle_homesick_assist_effect_from_bomb(shared_ptr<Card> card) {
@@ -1960,4 +1964,36 @@ void PlayerState::compute_team_dice_bonus_after_draw_phase() {
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
}
void PlayerState::send_6xB4x0A_for_set_card(size_t set_index) {
if (set_index >= 9) {
return;
}
auto s = this->server();
// The original code (in NTE) calls memcmp here, but then ignores the results
// and always copies the chain and metadata.
// this->set_card_action_chains->at(set_index) == this->unknown_a12;
// this->set_card_action_metadatas->at(set_index) == this->unknown_a13;
this->set_card_action_chains->at(set_index) = this->unknown_a12;
this->set_card_action_metadatas->at(set_index) = this->unknown_a13;
if (s->options.is_trial()) {
G_UpdateActionChainAndMetadata_Ep3NTE_6xB4x0A cmd;
cmd.client_id = this->client_id;
cmd.index = set_index;
cmd.chain = this->unknown_a12;
cmd.metadata = this->unknown_a13;
s->send(cmd);
} else {
G_UpdateActionChainAndMetadata_Ep3_6xB4x0A cmd;
cmd.client_id = this->client_id;
cmd.index = set_index;
cmd.chain = this->unknown_a12;
cmd.metadata = this->unknown_a13;
s->send(cmd);
}
}
} // namespace Episode3
+1
View File
@@ -138,6 +138,7 @@ public:
void roll_main_dice();
void unknown_8023C110();
void compute_team_dice_bonus_after_draw_phase();
void send_6xB4x0A_for_set_card(size_t set_index);
private:
std::weak_ptr<Server> w_server;
+1 -1
View File
@@ -439,7 +439,7 @@ void ActionChainWithConds::compute_attack_medium(shared_ptr<Server> server) {
if (!ce) {
continue;
}
if (card_class_is_tech_like(ce->def.card_class())) {
if (card_class_is_tech_like(ce->def.card_class(), server->options.is_trial())) {
this->chain.attack_medium = AttackMedium::TECH;
}
}
+14 -9
View File
@@ -636,9 +636,11 @@ bool RulerServer::card_ref_has_free_maneuver(uint16_t card_ref) const {
}
bool RulerServer::card_ref_is_aerial(uint16_t card_ref) const {
const auto* stat = this->short_status_for_card_ref(card_ref);
if (!stat || !this->card_exists_by_status(*stat)) {
return false;
if (!this->server()->options.is_trial()) {
const auto* stat = this->short_status_for_card_ref(card_ref);
if (!stat || !this->card_exists_by_status(*stat)) {
return false;
}
}
uint8_t client_id = client_id_for_card_ref(card_ref);
@@ -905,7 +907,8 @@ 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_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)));
auto s = this->server();
auto log = s->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;
@@ -918,7 +921,7 @@ bool RulerServer::check_usability_or_condition_apply(
log.debug("ce1 missing");
return false;
}
if ((ce1->def.type == CardType::ITEM) && this->card_id_is_boss_sc(card_id2)) {
if (!s->options.is_trial() && (ce1->def.type == CardType::ITEM) && this->card_id_is_boss_sc(card_id2)) {
log.debug("ce1 is item and card_id2 is boss sc");
return false;
}
@@ -954,8 +957,8 @@ bool RulerServer::check_usability_or_condition_apply(
// creature card is usable, the two client IDs should be the same or the
// second should not be given, so we'd return true if the criterion passes. If
// neither of these cases apply, we should return false as a failsafe even if
// the criterion passes.
bool ret = (!(def_effect_index & 0x80) || (client_id1 == client_id2)) || (client_id2 == 0xFF);
// the criterion passes. NTE did not have such a check.
bool ret = s->options.is_trial() || (!(def_effect_index & 0x80) || (client_id1 == client_id2)) || (client_id2 == 0xFF);
switch (criterion_code) {
case CriterionCode::NONE:
return ret;
@@ -1371,13 +1374,14 @@ uint16_t RulerServer::compute_attack_or_defense_costs(
tech_cost_bias = -1;
}
auto s = this->server();
for (size_t z = 0; pa.action_card_refs[z] != 0xFFFF; z++) {
auto ce = this->definition_for_card_ref(pa.action_card_refs[z]);
if (has_mighty_knuckle || !ce || (ce->def.type != CardType::ACTION)) {
return 99;
}
total_cost += (ce->def.self_cost + cost_bias);
if (card_class_is_tech_like(ce->def.card_class())) {
if (card_class_is_tech_like(ce->def.card_class(), s->options.is_trial())) {
total_cost += tech_cost_bias;
}
total_ally_cost += ce->def.ally_cost;
@@ -2520,13 +2524,14 @@ void RulerServer::replace_D1_D2_rank_cards_with_Attack(
}
AttackMedium RulerServer::get_attack_medium(const ActionState& pa) const {
bool is_trial = this->server()->options.is_trial();
for (size_t z = 0; z < 8; z++) {
uint16_t card_ref = pa.action_card_refs[z];
if (card_ref == 0xFFFF) {
return AttackMedium::PHYSICAL;
}
auto ce = this->definition_for_card_ref(card_ref);
if (ce && card_class_is_tech_like(ce->def.card_class())) {
if (ce && card_class_is_tech_like(ce->def.card_class(), is_trial)) {
return AttackMedium::TECH;
}
}
+34 -26
View File
@@ -822,7 +822,6 @@ void Server::draw_phase_after() {
}
if (no_winner_specified) {
if (this->options.is_trial()) {
// TODO: This looks like an incomplete version of compute_losing_team_id_and_add_winner_flags; reconcile this
throw runtime_error("unimplemented NTE condition");
} else {
this->compute_losing_team_id_and_add_winner_flags(0);
@@ -1705,7 +1704,7 @@ void Server::on_server_data_input(shared_ptr<Client> sender_c, const string& dat
}
void Server::handle_CAx0B_mulligan_hand(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_RedrawInitialHand_Ep3_6xB3x0B_CAx0B>(data);
const auto& in_cmd = check_size_t<G_RedrawInitialHand_Ep3_CAx0B>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "REDRAW");
if (in_cmd.client_id >= 4) {
@@ -1738,7 +1737,7 @@ void Server::handle_CAx0B_mulligan_hand(shared_ptr<Client>, const string& data)
}
void Server::handle_CAx0C_end_mulligan_phase(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_EndInitialRedrawPhase_Ep3_6xB3x0C_CAx0C>(data);
const auto& in_cmd = check_size_t<G_EndInitialRedrawPhase_Ep3_CAx0C>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "SETUP ADV 2");
if (in_cmd.client_id >= 4) {
@@ -1796,7 +1795,7 @@ void Server::handle_CAx0C_end_mulligan_phase(shared_ptr<Client>, const string& d
}
void Server::handle_CAx0D_end_non_action_phase(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_EndNonAttackPhase_Ep3_6xB3x0D_CAx0D>(data);
const auto& in_cmd = check_size_t<G_EndNonAttackPhase_Ep3_CAx0D>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "END PHASE");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
@@ -1816,7 +1815,7 @@ void Server::handle_CAx0D_end_non_action_phase(shared_ptr<Client>, const string&
}
void Server::handle_CAx0E_discard_card_from_hand(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_DiscardCardFromHand_Ep3_6xB3x0E_CAx0E>(data);
const auto& in_cmd = check_size_t<G_DiscardCardFromHand_Ep3_CAx0E>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "DISCARD");
if (in_cmd.client_id >= 4) {
@@ -1856,7 +1855,7 @@ void Server::handle_CAx0E_discard_card_from_hand(shared_ptr<Client>, const strin
}
void Server::handle_CAx0F_set_card_from_hand(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_SetCardFromHand_Ep3_6xB3x0F_CAx0F>(data);
const auto& in_cmd = check_size_t<G_SetCardFromHand_Ep3_CAx0F>(data);
this->send_debug_command_received_message(in_cmd.client_id, in_cmd.header.subsubcommand, "SET FC");
if (in_cmd.client_id >= 4) {
throw runtime_error("invalid client ID");
@@ -1898,7 +1897,7 @@ void Server::handle_CAx0F_set_card_from_hand(shared_ptr<Client>, const string& d
}
void Server::handle_CAx10_move_fc_to_location(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_MoveFieldCharacter_Ep3_6xB3x10_CAx10>(data);
const auto& in_cmd = check_size_t<G_MoveFieldCharacter_Ep3_CAx10>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "MOVE");
if (in_cmd.client_id >= 4) {
@@ -1936,7 +1935,7 @@ void Server::handle_CAx10_move_fc_to_location(shared_ptr<Client>, const string&
}
void Server::handle_CAx11_enqueue_attack_or_defense(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_EnqueueAttackOrDefense_Ep3_6xB3x11_CAx11>(data);
const auto& in_cmd = check_size_t<G_EnqueueAttackOrDefense_Ep3_CAx11>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "ENQUEUE ACT");
if (in_cmd.client_id >= 4) {
@@ -1972,7 +1971,7 @@ void Server::handle_CAx11_enqueue_attack_or_defense(shared_ptr<Client>, const st
}
void Server::handle_CAx12_end_attack_list(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_EndAttackList_Ep3_6xB3x12_CAx12>(data);
const auto& in_cmd = check_size_t<G_EndAttackList_Ep3_CAx12>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "END ATK LIST");
if (in_cmd.client_id >= 4) {
@@ -1994,8 +1993,9 @@ void Server::handle_CAx12_end_attack_list(shared_ptr<Client>, const string& data
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, error_code);
}
void Server::handle_CAx13_update_map_during_setup(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_SetMapState_Ep3_6xB3x13_CAx13>(data);
template <typename CmdT>
void Server::handle_CAx13_update_map_during_setup_t(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<CmdT>(data);
this->send_debug_command_received_message(
in_cmd.header.subsubcommand, "UPDATE MAP");
@@ -2036,8 +2036,16 @@ void Server::handle_CAx13_update_map_during_setup(shared_ptr<Client>, const stri
}
}
void Server::handle_CAx13_update_map_during_setup(shared_ptr<Client> c, const string& data) {
if (this->options.is_trial()) {
this->handle_CAx13_update_map_during_setup_t<G_SetMapState_Ep3NTE_CAx13>(c, data);
} else {
this->handle_CAx13_update_map_during_setup_t<G_SetMapState_Ep3_CAx13>(c, data);
}
}
void Server::handle_CAx14_update_deck_during_setup(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_SetPlayerDeck_Ep3_6xB3x14_CAx14>(data);
const auto& in_cmd = check_size_t<G_SetPlayerDeck_Ep3_CAx14>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "UPDATE DECK");
@@ -2062,7 +2070,7 @@ void Server::handle_CAx14_update_deck_during_setup(shared_ptr<Client>, const str
if (verify_error) {
throw runtime_error(string_printf("invalid deck: -0x%" PRIX32, verify_error));
}
if (!(this->options.behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
if (!this->options.is_trial() && !(this->options.behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
this->ruler_server->replace_D1_D2_rank_cards_with_Attack(entry.card_ids);
}
*this->deck_entries[in_cmd.client_id] = in_cmd.entry;
@@ -2084,7 +2092,7 @@ void Server::handle_CAx14_update_deck_during_setup(shared_ptr<Client>, const str
}
void Server::handle_CAx15_unused_hard_reset_server_state(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_HardResetServerState_Ep3_6xB3x15_CAx15>(data);
const auto& in_cmd = check_size_t<G_HardResetServerState_Ep3_CAx15>(data);
this->send_debug_command_received_message(
in_cmd.header.subsubcommand, "HARD RESET");
@@ -2102,7 +2110,7 @@ void Server::handle_CAx15_unused_hard_reset_server_state(shared_ptr<Client>, con
}
void Server::handle_CAx1B_update_player_name(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_SetPlayerName_Ep3_6xB3x1B_CAx1B>(data);
const auto& in_cmd = check_size_t<G_SetPlayerName_Ep3_CAx1B>(data);
this->send_debug_command_received_message(
in_cmd.entry.client_id, in_cmd.header.subsubcommand, "UPDATE NAME");
@@ -2134,7 +2142,7 @@ void Server::handle_CAx1B_update_player_name(shared_ptr<Client>, const string& d
}
void Server::handle_CAx1D_start_battle(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_StartBattle_Ep3_6xB3x1D_CAx1D>(data);
const auto& in_cmd = check_size_t<G_StartBattle_Ep3_CAx1D>(data);
this->send_debug_command_received_message(
in_cmd.header.subsubcommand, "START BATTLE");
@@ -2171,7 +2179,7 @@ void Server::handle_CAx1D_start_battle(shared_ptr<Client>, const string& data) {
}
void Server::handle_CAx21_end_battle(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_EndBattle_Ep3_6xB3x21_CAx21>(data);
const auto& in_cmd = check_size_t<G_EndBattle_Ep3_CAx21>(data);
this->send_debug_command_received_message(
in_cmd.header.subsubcommand, "END BATTLE");
if (this->setup_phase == SetupPhase::BATTLE_ENDED) {
@@ -2186,7 +2194,7 @@ void Server::handle_CAx21_end_battle(shared_ptr<Client>, const string& data) {
}
void Server::handle_CAx28_end_defense_list(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_EndDefenseList_Ep3_6xB3x28_CAx28>(data);
const auto& in_cmd = check_size_t<G_EndDefenseList_Ep3_CAx28>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "END DEF LIST");
if (in_cmd.client_id >= 4) {
@@ -2239,13 +2247,13 @@ void Server::handle_CAx28_end_defense_list(shared_ptr<Client>, const string& dat
}
void Server::handle_CAx2B_legacy_set_card(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_ExecLegacyCard_Ep3_6xB3x2B_CAx2B>(data);
const auto& in_cmd = check_size_t<G_ExecLegacyCard_Ep3_CAx2B>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "EXEC LEGACY");
// Sega's original implementation does nothing here, so we do nothing as well.
}
void Server::handle_CAx34_subtract_ally_atk_points(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_PhotonBlastRequest_Ep3_6xB3x34_CAx34>(data);
const auto& in_cmd = check_size_t<G_PhotonBlastRequest_Ep3_CAx34>(data);
uint8_t card_ref_client_id = client_id_for_card_ref(in_cmd.card_ref);
this->send_debug_command_received_message(
@@ -2322,7 +2330,7 @@ void Server::handle_CAx34_subtract_ally_atk_points(shared_ptr<Client>, const str
}
void Server::handle_CAx37_client_ready_to_advance_from_starter_roll_phase(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_AdvanceFromStartingRollsPhase_Ep3_6xB3x37_CAx37>(data);
const auto& in_cmd = check_size_t<G_AdvanceFromStartingRollsPhase_Ep3_CAx37>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "SETUP ADV 1");
if (in_cmd.client_id >= 4) {
@@ -2352,14 +2360,14 @@ void Server::handle_CAx37_client_ready_to_advance_from_starter_roll_phase(shared
}
void Server::handle_CAx3A_time_limit_expired(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_OverallTimeLimitExpired_Ep3_6xB3x3A_CAx3A>(data);
const auto& in_cmd = check_size_t<G_OverallTimeLimitExpired_Ep3_CAx3A>(data);
this->send_debug_command_received_message(in_cmd.header.subsubcommand, "TIME EXPIRED");
// We don't need to do anything here because the overall time limit is tracked
// server-side instead.
}
void Server::handle_CAx40_map_list_request(shared_ptr<Client> sender_c, const string& data) {
const auto& in_cmd = check_size_t<G_MapListRequest_Ep3_6xB3x40_CAx40>(data);
const auto& in_cmd = check_size_t<G_MapListRequest_Ep3_CAx40>(data);
this->send_debug_command_received_message(
in_cmd.header.subsubcommand, "MAP LIST");
@@ -2431,7 +2439,7 @@ void Server::send_6xB6x41_to_all_clients() const {
}
void Server::handle_CAx41_map_request(shared_ptr<Client>, const string& data) {
const auto& cmd = check_size_t<G_MapDataRequest_Ep3_6xB3x41_CAx41>(data);
const auto& cmd = check_size_t<G_MapDataRequest_Ep3_CAx41>(data);
this->send_debug_command_received_message(
cmd.header.subsubcommand, "MAP DATA");
@@ -2440,7 +2448,7 @@ void Server::handle_CAx41_map_request(shared_ptr<Client>, const string& data) {
}
void Server::handle_CAx48_end_turn(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_EndTurn_Ep3_6xB3x48_CAx48>(data);
const auto& in_cmd = check_size_t<G_EndTurn_Ep3_CAx48>(data);
this->send_debug_command_received_message(
in_cmd.client_id, in_cmd.header.subsubcommand, "END TURN");
if (in_cmd.client_id >= 4) {
@@ -2458,7 +2466,7 @@ void Server::handle_CAx48_end_turn(shared_ptr<Client>, const string& data) {
}
void Server::handle_CAx49_card_counts(shared_ptr<Client>, const string& data) {
const auto& in_cmd = check_size_t<G_CardCounts_Ep3_6xB3x49_CAx49>(data);
const auto& in_cmd = check_size_t<G_CardCounts_Ep3_CAx49>(data);
this->send_debug_command_received_message(
in_cmd.header.sender_client_id, in_cmd.header.subsubcommand, "CARD COUNTS");
+2
View File
@@ -195,6 +195,8 @@ public:
void handle_CAx10_move_fc_to_location(std::shared_ptr<Client> sender_c, const std::string& data);
void handle_CAx11_enqueue_attack_or_defense(std::shared_ptr<Client> sender_c, const std::string& data);
void handle_CAx12_end_attack_list(std::shared_ptr<Client> sender_c, const std::string& data);
template <typename CmdT>
void handle_CAx13_update_map_during_setup_t(std::shared_ptr<Client> sender_c, const std::string& data);
void handle_CAx13_update_map_during_setup(std::shared_ptr<Client> sender_c, const std::string& data);
void handle_CAx14_update_deck_during_setup(std::shared_ptr<Client> sender_c, const std::string& data);
void handle_CAx15_unused_hard_reset_server_state(std::shared_ptr<Client> sender_c, const std::string& data);