Ep3 NTE checkpoint 4
This commit is contained in:
+58
-15
@@ -446,6 +446,11 @@ void Card::destroy_set_card(shared_ptr<Card> attacker_card) {
|
||||
}
|
||||
|
||||
int32_t Card::error_code_for_move_to_location(const Location& loc) const {
|
||||
// TODO: NTE has different logic here, which appears to be similar enough to
|
||||
// the final logic that I didn't bother to reverse-engineer it completely.
|
||||
// Eventually, we should revisit this, but I suspect doing so would be a lot
|
||||
// of tedium for little to no benefit.
|
||||
|
||||
if (this->player_state()->assist_flags & AssistFlag::IS_SKIPPING_TURN) {
|
||||
return -0x76;
|
||||
}
|
||||
@@ -629,6 +634,8 @@ uint8_t Card::get_team_id() const {
|
||||
}
|
||||
|
||||
int32_t Card::move_to_location(const Location& loc) {
|
||||
auto s = this->server();
|
||||
|
||||
int32_t code = this->error_code_for_move_to_location(loc);
|
||||
if (code) {
|
||||
return code;
|
||||
@@ -636,7 +643,7 @@ int32_t Card::move_to_location(const Location& loc) {
|
||||
|
||||
uint32_t path_cost;
|
||||
uint32_t path_length;
|
||||
if (!this->server()->ruler_server->get_move_path_length_and_cost(
|
||||
if (!s->ruler_server->get_move_path_length_and_cost(
|
||||
this->client_id, this->card_ref, loc, &path_length, &path_cost)) {
|
||||
return -0x79;
|
||||
}
|
||||
@@ -646,20 +653,48 @@ int32_t Card::move_to_location(const Location& loc) {
|
||||
this->loc = loc;
|
||||
this->card_flags = this->card_flags | 0x80;
|
||||
|
||||
// On NTE, traps happen now, not after the Move phase
|
||||
if (s->options.is_trial() &&
|
||||
this->def_entry->def.is_sc() &&
|
||||
((s->overlay_state.tiles[loc.y][loc.x] & 0xF0) == 0x40)) {
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
auto other_ps = s->player_states[z];
|
||||
if (!other_ps) {
|
||||
continue;
|
||||
}
|
||||
auto other_sc = other_ps->get_sc_card();
|
||||
if (!other_sc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((abs(other_sc->loc.x - loc.x) < 2) && (abs(other_sc->loc.y - loc.y) < 2)) {
|
||||
uint8_t trap_type = s->overlay_state.tiles[loc.y][loc.x] & 0x0F;
|
||||
uint16_t trap_card_id = s->overlay_state.trap_card_ids_nte[trap_type];
|
||||
if (other_ps->replace_assist_card_by_id(trap_card_id)) {
|
||||
G_Unknown_Ep3_6xB4x2C cmd;
|
||||
cmd.change_type = 1;
|
||||
cmd.client_id = other_ps->client_id;
|
||||
cmd.unknown_a2[0] = trap_card_id;
|
||||
s->send(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t warp_type = 0; warp_type < 5; warp_type++) {
|
||||
for (size_t warp_end = 0; warp_end < 2; warp_end++) {
|
||||
if ((this->server()->warp_positions[warp_type][warp_end][0] == this->loc.x) &&
|
||||
(this->server()->warp_positions[warp_type][warp_end][1] == this->loc.y)) {
|
||||
if ((s->warp_positions[warp_type][warp_end][0] == this->loc.x) &&
|
||||
(s->warp_positions[warp_type][warp_end][1] == this->loc.y)) {
|
||||
G_Unknown_Ep3_6xB4x2C cmd;
|
||||
cmd.loc.x = this->loc.x;
|
||||
cmd.loc.y = this->loc.y;
|
||||
this->loc.x = this->server()->warp_positions[warp_type][warp_end ^ 1][0];
|
||||
this->loc.y = this->server()->warp_positions[warp_type][warp_end ^ 1][1];
|
||||
this->loc.x = s->warp_positions[warp_type][warp_end ^ 1][0];
|
||||
this->loc.y = s->warp_positions[warp_type][warp_end ^ 1][1];
|
||||
cmd.change_type = 0;
|
||||
cmd.card_refs.clear(0xFFFF);
|
||||
cmd.card_refs[0] = this->card_ref;
|
||||
cmd.unknown_a2.clear(0xFFFFFFFF);
|
||||
this->server()->send(cmd);
|
||||
s->send(cmd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1197,17 +1232,25 @@ void Card::unknown_80237A90(const ActionState& pa, uint16_t action_card_ref) {
|
||||
this->facing_direction = pa.facing_direction;
|
||||
this->action_chain.add_attack_action_card_ref(action_card_ref, s);
|
||||
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
if (s->ruler_server->count_rampage_targets_for_attack(pa, z) != 0) {
|
||||
this->action_chain.set_flags(0x200 << z);
|
||||
if (s->options.is_trial()) {
|
||||
if (s->ruler_server->count_targets_with_rampage_and_not_pierce_nte(pa)) {
|
||||
this->action_chain.set_flags(0x02);
|
||||
}
|
||||
if (s->ruler_server->attack_action_has_pierce_and_not_rampage(pa, z)) {
|
||||
this->action_chain.set_flags(0x2000 << z);
|
||||
if (s->ruler_server->count_targets_with_pierce_and_not_rampage_nte(pa)) {
|
||||
this->action_chain.set_flags(0x80);
|
||||
}
|
||||
} else {
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
if (s->ruler_server->count_rampage_targets_for_attack(pa, z) != 0) {
|
||||
this->action_chain.set_flags(0x200 << z);
|
||||
}
|
||||
if (s->ruler_server->attack_action_has_pierce_and_not_rampage(pa, z)) {
|
||||
this->action_chain.set_flags(0x2000 << z);
|
||||
}
|
||||
}
|
||||
if (s->ruler_server->any_attack_action_card_is_support_tech_or_support_pb(pa)) {
|
||||
this->action_chain.set_flags(0x20000);
|
||||
}
|
||||
}
|
||||
|
||||
if (s->ruler_server->any_attack_action_card_is_support_tech_or_support_pb(pa)) {
|
||||
this->action_chain.set_flags(0x20000);
|
||||
}
|
||||
|
||||
if (this->action_chain.chain.target_card_ref_count == 0) {
|
||||
|
||||
+18
-21
@@ -3810,25 +3810,28 @@ int16_t CardSpecial::max_all_attack_bonuses(size_t* out_count) const {
|
||||
void CardSpecial::unknown_80244AA8(shared_ptr<Card> card) {
|
||||
ActionState as = this->create_attack_state_from_card_action_chain(card);
|
||||
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto ps = this->server()->player_states[client_id];
|
||||
if (ps) {
|
||||
auto other_card = ps->get_sc_card();
|
||||
if (other_card) {
|
||||
this->clear_invalid_conditions_on_card(other_card, as);
|
||||
}
|
||||
for (size_t set_index = 0; set_index < 8; set_index++) {
|
||||
auto other_card = ps->get_set_card(set_index);
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
if (!is_trial) {
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto ps = this->server()->player_states[client_id];
|
||||
if (ps) {
|
||||
auto other_card = ps->get_sc_card();
|
||||
if (other_card) {
|
||||
this->clear_invalid_conditions_on_card(other_card, as);
|
||||
}
|
||||
for (size_t set_index = 0; set_index < 8; set_index++) {
|
||||
auto other_card = ps->get_set_card(set_index);
|
||||
if (other_card) {
|
||||
this->clear_invalid_conditions_on_card(other_card, as);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->apply_defense_conditions(as, 0x27, card, 0x04);
|
||||
this->evaluate_and_apply_effects(0x27, card->get_card_ref(), as, 0xFFFF);
|
||||
}
|
||||
|
||||
this->apply_defense_conditions(as, 0x27, card, 4);
|
||||
this->evaluate_and_apply_effects(0x27, card->get_card_ref(), as, 0xFFFF);
|
||||
this->apply_defense_conditions(as, 0x13, card, 4);
|
||||
this->apply_defense_conditions(as, 0x13, card, is_trial ? 0x1F : 0x04);
|
||||
this->evaluate_and_apply_effects(0x13, card->get_card_ref(), as, 0xFFFF);
|
||||
}
|
||||
|
||||
@@ -4661,15 +4664,9 @@ void CardSpecial::unknown_8024AAB8(const ActionState& as) {
|
||||
|
||||
if (this->send_6xB4x06_if_card_ref_invalid(as.original_attacker_card_ref, 0x1F) == 0xFFFF) {
|
||||
this->evaluate_and_apply_effects(
|
||||
0x01,
|
||||
as.action_card_refs[z],
|
||||
as,
|
||||
this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x21));
|
||||
0x01, as.action_card_refs[z], as, this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x21));
|
||||
this->evaluate_and_apply_effects(
|
||||
0x0B,
|
||||
as.action_card_refs[z],
|
||||
as,
|
||||
this->send_6xB4x06_if_card_ref_invalid(as.attacker_card_ref, 0x22));
|
||||
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) {
|
||||
@@ -4851,7 +4848,7 @@ shared_ptr<Card> CardSpecial::sc_card_for_card(shared_ptr<Card> unknown_p2) {
|
||||
|
||||
void CardSpecial::unknown_8024A9D8(const ActionState& pa, uint16_t action_card_ref) {
|
||||
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 (this->server()->options.is_trial() || (action_card_ref == 0xFFFF) || (action_card_ref == pa.action_card_refs[z])) {
|
||||
if (pa.original_attacker_card_ref == 0xFFFF) {
|
||||
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);
|
||||
|
||||
@@ -1466,8 +1466,8 @@ RulesTrial::RulesTrial(const Rules& r)
|
||||
: overall_time_limit(r.overall_time_limit),
|
||||
phase_time_limit(r.phase_time_limit),
|
||||
allowed_cards(r.allowed_cards),
|
||||
atk_dice_max(r.max_dice),
|
||||
def_dice_max(r.def_dice_range ? (r.def_dice_range & 0x0F) : r.max_dice),
|
||||
atk_die_behavior((r.max_dice == r.min_dice) ? r.max_dice : 0),
|
||||
def_die_behavior((r.min_def_dice() == r.max_def_dice()) ? r.max_def_dice() : 0),
|
||||
disable_deck_shuffle(r.disable_deck_shuffle),
|
||||
disable_deck_loop(r.disable_deck_loop),
|
||||
char_hp(r.char_hp),
|
||||
@@ -1481,8 +1481,13 @@ RulesTrial::operator Rules() const {
|
||||
ret.overall_time_limit = this->overall_time_limit;
|
||||
ret.phase_time_limit = this->phase_time_limit;
|
||||
ret.allowed_cards = this->allowed_cards;
|
||||
ret.min_dice = 1;
|
||||
ret.max_dice = this->atk_dice_max;
|
||||
if (this->atk_die_behavior) {
|
||||
ret.min_dice = this->atk_die_behavior;
|
||||
ret.max_dice = this->atk_die_behavior;
|
||||
} else {
|
||||
ret.min_dice = 1;
|
||||
ret.max_dice = 6;
|
||||
}
|
||||
ret.disable_deck_shuffle = this->disable_deck_shuffle;
|
||||
ret.disable_deck_loop = this->disable_deck_loop;
|
||||
ret.char_hp = this->char_hp;
|
||||
@@ -1491,7 +1496,11 @@ RulesTrial::operator Rules() const {
|
||||
ret.disable_dialogue = this->disable_dialogue;
|
||||
ret.dice_exchange_mode = this->dice_exchange_mode;
|
||||
ret.disable_dice_boost = 0;
|
||||
ret.def_dice_range = 0x10 | (this->def_dice_max ? this->def_dice_max : 0x06);
|
||||
if (this->def_die_behavior) {
|
||||
ret.def_dice_range = (this->def_die_behavior << 4) | this->def_die_behavior;
|
||||
} else {
|
||||
ret.def_dice_range = 0x16;
|
||||
}
|
||||
ret.unused.clear(0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -984,8 +984,11 @@ 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 = 6;
|
||||
/* 04 */ uint8_t def_dice_max = 6;
|
||||
// In NTE, the dice behave differently than in non-NTE. A zero in either of
|
||||
// these fields means the corresponding die is random in the range [1, 6];
|
||||
// any nonzero value means that die will always take that value.
|
||||
/* 03 */ uint8_t atk_die_behavior = 0;
|
||||
/* 04 */ uint8_t def_die_behavior = 0;
|
||||
/* 05 */ uint8_t disable_deck_shuffle = 0;
|
||||
/* 06 */ uint8_t disable_deck_loop = 0;
|
||||
/* 07 */ uint8_t char_hp = 15;
|
||||
@@ -1185,6 +1188,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
// 30-34 = teleporters (2 of each value may be present)
|
||||
// 40-44 = traps (one of each type is chosen at random to be a real trap at
|
||||
// battle start time)
|
||||
// 40-4F = traps on NTE
|
||||
// 50 = blocked by metal box (appears as improperly-z-buffered teal cube in
|
||||
// preview; behaves like 10 and 20 in game)
|
||||
// The assist cards that each trap type can contain are:
|
||||
|
||||
@@ -43,7 +43,7 @@ void MapAndRulesState::clear() {
|
||||
this->num_team0_players = 0;
|
||||
this->unused2 = 0;
|
||||
this->start_facing_directions = 0;
|
||||
this->unused3 = 0;
|
||||
this->unknown_a3 = 0;
|
||||
this->map_number = 0;
|
||||
this->unused4 = 0;
|
||||
this->rules.clear();
|
||||
@@ -77,7 +77,7 @@ MapAndRulesStateTrial::MapAndRulesStateTrial(const MapAndRulesState& state)
|
||||
num_team0_players(state.num_team0_players),
|
||||
unused2(state.unused2),
|
||||
unused5(state.start_facing_directions),
|
||||
unknown_a3(state.unused3),
|
||||
unknown_a3(state.unknown_a3),
|
||||
map_number(state.map_number),
|
||||
unused4(state.unused4),
|
||||
rules(state.rules) {}
|
||||
@@ -92,7 +92,7 @@ MapAndRulesStateTrial::operator MapAndRulesState() const {
|
||||
ret.num_team0_players = this->num_team0_players;
|
||||
ret.unused2 = this->unused2;
|
||||
ret.start_facing_directions = this->unused5;
|
||||
ret.unused3 = this->unknown_a3;
|
||||
ret.unknown_a3 = this->unknown_a3;
|
||||
ret.map_number = this->map_number;
|
||||
ret.unused4 = this->unused4;
|
||||
ret.rules = this->rules;
|
||||
@@ -109,7 +109,7 @@ void OverlayState::clear() {
|
||||
}
|
||||
this->unused1.clear(0);
|
||||
this->unused2.clear(0);
|
||||
this->unused3.clear(0);
|
||||
this->trap_card_ids_nte.clear(0);
|
||||
}
|
||||
|
||||
} // namespace Episode3
|
||||
|
||||
@@ -31,7 +31,7 @@ struct MapAndRulesState {
|
||||
/* 0114 */ uint8_t num_team0_players = 0;
|
||||
/* 0115 */ uint8_t unused2 = 0;
|
||||
/* 0116 */ le_uint16_t start_facing_directions = 0;
|
||||
/* 0118 */ be_uint32_t unused3 = 0;
|
||||
/* 0118 */ be_uint32_t unknown_a3 = 0;
|
||||
/* 011C */ le_uint32_t map_number = 0;
|
||||
/* 0120 */ be_uint32_t unused4 = 0;
|
||||
/* 0124 */ Rules rules;
|
||||
@@ -71,7 +71,7 @@ struct OverlayState {
|
||||
parray<parray<uint8_t, 0x10>, 0x10> tiles;
|
||||
parray<le_uint32_t, 5> unused1;
|
||||
parray<le_uint32_t, 0x10> unused2;
|
||||
parray<le_uint16_t, 0x10> unused3;
|
||||
parray<le_uint16_t, 0x10> trap_card_ids_nte; // Unused on non-NTE
|
||||
|
||||
OverlayState();
|
||||
void clear();
|
||||
|
||||
+198
-109
@@ -176,11 +176,17 @@ void PlayerState::apply_assist_card_effect_on_set(
|
||||
}
|
||||
|
||||
if (hand_index < 6) {
|
||||
for (size_t z = 0; z < 0x10; z++) {
|
||||
if (this->deck_state->draw_card_by_ref(this->discard_log_card_refs[z])) {
|
||||
this->card_refs[hand_index] = this->discard_log_card_refs[z];
|
||||
this->discard_log_card_refs[z] = 0xFFFF;
|
||||
break;
|
||||
if (s->options.is_trial()) {
|
||||
if (this->deck_state->draw_card_by_ref(this->discard_log_card_refs[0])) {
|
||||
this->pop_from_discard_log(0);
|
||||
}
|
||||
} else {
|
||||
for (size_t z = 0; z < 0x10; z++) {
|
||||
if (this->deck_state->draw_card_by_ref(this->discard_log_card_refs[z])) {
|
||||
this->card_refs[hand_index] = this->discard_log_card_refs[z];
|
||||
this->discard_log_card_refs[z] = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,7 +208,9 @@ void PlayerState::apply_assist_card_effect_on_set(
|
||||
|
||||
case AssistEffect::SKIP_SET:
|
||||
case AssistEffect::SKIP_ACT:
|
||||
this->assist_delay_turns = 2;
|
||||
if (!s->options.is_trial()) {
|
||||
this->assist_delay_turns = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case AssistEffect::NECROMANCER: {
|
||||
@@ -244,11 +252,16 @@ void PlayerState::apply_assist_card_effect_on_set(
|
||||
}
|
||||
}
|
||||
}
|
||||
this->on_cards_destroyed();
|
||||
|
||||
bool is_trial = s->options.is_trial();
|
||||
if (!is_trial) {
|
||||
this->on_cards_destroyed();
|
||||
}
|
||||
this->atk_points = min<uint8_t>(9, this->atk_points + (total_cost >> 1));
|
||||
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
s->send_6xB4x05();
|
||||
if (!is_trial) {
|
||||
s->send_6xB4x05();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -307,86 +320,104 @@ void PlayerState::apply_assist_card_effect_on_set(
|
||||
s->update_battle_state_flags_and_send_6xB4x03_if_needed();
|
||||
|
||||
this->num_destroyed_fcs = 0;
|
||||
s->team_num_cards_destroyed[this->team_id] = 0;
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
const auto other_ps = s->get_player_state(client_id);
|
||||
if (other_ps && (this->team_id == other_ps->get_team_id())) {
|
||||
auto card = other_ps->get_sc_card();
|
||||
if (card) {
|
||||
card->num_cards_destroyed_by_team_at_set_time = 0;
|
||||
card->num_destroyed_ally_fcs = 0;
|
||||
}
|
||||
for (size_t set_index = 0; set_index < 8; set_index++) {
|
||||
auto set_card = other_ps->get_set_card(set_index);
|
||||
if (set_card) {
|
||||
set_card->num_cards_destroyed_by_team_at_set_time = 0;
|
||||
set_card->num_destroyed_ally_fcs = 0;
|
||||
if (!s->options.is_trial()) {
|
||||
s->team_num_cards_destroyed[this->team_id] = 0;
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
const auto other_ps = s->get_player_state(client_id);
|
||||
if (other_ps && (this->team_id == other_ps->get_team_id())) {
|
||||
auto card = other_ps->get_sc_card();
|
||||
if (card) {
|
||||
card->num_cards_destroyed_by_team_at_set_time = 0;
|
||||
card->num_destroyed_ally_fcs = 0;
|
||||
}
|
||||
for (size_t set_index = 0; set_index < 8; set_index++) {
|
||||
auto set_card = other_ps->get_set_card(set_index);
|
||||
if (set_card) {
|
||||
set_card->num_cards_destroyed_by_team_at_set_time = 0;
|
||||
set_card->num_destroyed_ally_fcs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AssistEffect::SLOW_TIME:
|
||||
case AssistEffect::SLOW_TIME: {
|
||||
bool is_trial = s->options.is_trial();
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto other_ps = s->get_player_state(client_id);
|
||||
if (!other_ps) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (other_ps->assist_remaining_turns < 10) {
|
||||
if (is_trial
|
||||
? (other_ps->assist_remaining_turns != 90 && other_ps->assist_remaining_turns != 99)
|
||||
: (other_ps->assist_remaining_turns < 10)) {
|
||||
other_ps->assist_remaining_turns = min<uint8_t>(9, other_ps->assist_remaining_turns << 1);
|
||||
}
|
||||
|
||||
for (ssize_t set_index = -1; set_index < 8; set_index++) {
|
||||
for (ssize_t set_index = is_trial ? 0 : -1; set_index < 8; set_index++) {
|
||||
auto card = (set_index == -1)
|
||||
? other_ps->get_sc_card()
|
||||
: other_ps->get_set_card(set_index);
|
||||
if (card) {
|
||||
for (size_t cond_index = 0; cond_index < 9; cond_index++) {
|
||||
auto& cond = card->action_chain.conditions[cond_index];
|
||||
if ((cond.type != ConditionType::NONE) &&
|
||||
(cond.remaining_turns < 10)) {
|
||||
if (cond.type == ConditionType::NONE) {
|
||||
continue;
|
||||
}
|
||||
if (is_trial) {
|
||||
if (cond.remaining_turns < 49) {
|
||||
cond.remaining_turns <<= 1;
|
||||
}
|
||||
} else if (cond.remaining_turns < 10) {
|
||||
cond.remaining_turns = min<uint8_t>(9, cond.remaining_turns << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
other_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
other_ps->send_set_card_updates();
|
||||
if (!is_trial) {
|
||||
other_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
other_ps->send_set_card_updates();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AssistEffect::QUICK_TIME:
|
||||
case AssistEffect::QUICK_TIME: {
|
||||
bool is_trial = s->options.is_trial();
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto other_ps = s->get_player_state(client_id);
|
||||
if (!other_ps) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (other_ps->assist_remaining_turns < 10) {
|
||||
if (is_trial
|
||||
? (other_ps->assist_remaining_turns != 90 && other_ps->assist_remaining_turns != 99)
|
||||
: (other_ps->assist_remaining_turns < 10)) {
|
||||
other_ps->assist_remaining_turns = ((other_ps->assist_remaining_turns + 1) >> 1);
|
||||
}
|
||||
|
||||
for (ssize_t set_index = -1; set_index < 8; set_index++) {
|
||||
for (ssize_t set_index = is_trial ? 0 : -1; set_index < 8; set_index++) {
|
||||
auto card = (set_index == -1)
|
||||
? other_ps->get_sc_card()
|
||||
: other_ps->get_set_card(set_index);
|
||||
if (card) {
|
||||
for (size_t cond_index = 0; cond_index < 9; cond_index++) {
|
||||
auto& cond = card->action_chain.conditions[cond_index];
|
||||
if ((cond.type != ConditionType::NONE) &&
|
||||
(cond.remaining_turns < 10)) {
|
||||
if ((cond.type != ConditionType::NONE) && (cond.remaining_turns < (is_trial ? 99 : 10))) {
|
||||
cond.remaining_turns = (cond.remaining_turns + 1) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
other_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
other_ps->send_set_card_updates();
|
||||
if (!is_trial) {
|
||||
other_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
other_ps->send_set_card_updates();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AssistEffect::SQUEEZE:
|
||||
this->set_random_assist_card_from_hand_for_free();
|
||||
@@ -397,7 +428,7 @@ void PlayerState::apply_assist_card_effect_on_set(
|
||||
break;
|
||||
|
||||
case AssistEffect::SKIP_TURN:
|
||||
if (!setter_ps || (setter_ps->team_id == this->team_id)) {
|
||||
if (!s->options.is_trial() && (!setter_ps || (setter_ps->team_id == this->team_id))) {
|
||||
this->assist_delay_turns = 6;
|
||||
} else {
|
||||
this->assist_delay_turns = 5;
|
||||
@@ -819,6 +850,10 @@ uint8_t PlayerState::get_atk_points() const {
|
||||
return this->atk_points;
|
||||
}
|
||||
|
||||
uint8_t PlayerState::get_atk_points_nte() const {
|
||||
return min<uint8_t>(this->atk_points2_max, this->atk_points);
|
||||
}
|
||||
|
||||
void PlayerState::get_short_status_for_card_index_in_hand(
|
||||
size_t hand_index, CardShortStatus* stat) const {
|
||||
stat->card_ref = this->card_refs[hand_index - 1];
|
||||
@@ -913,7 +948,7 @@ bool PlayerState::is_team_turn() const {
|
||||
}
|
||||
|
||||
void PlayerState::log_discard(uint16_t card_ref, uint16_t reason) {
|
||||
for (size_t z = 15; z > 0; z--) {
|
||||
for (size_t z = this->discard_log_card_refs.size() - 1; z > 0; z--) {
|
||||
this->discard_log_card_refs[z] = this->discard_log_card_refs[z - 1];
|
||||
this->discard_log_reasons[z] = this->discard_log_reasons[z - 1];
|
||||
}
|
||||
@@ -921,6 +956,27 @@ void PlayerState::log_discard(uint16_t card_ref, uint16_t reason) {
|
||||
this->discard_log_reasons[0] = reason;
|
||||
}
|
||||
|
||||
uint16_t PlayerState::pop_from_discard_log(uint16_t) {
|
||||
// NTE appears to have a bug here (or some obviated code): it searches for an
|
||||
// entry with the given reason, then ignores the result of that search and
|
||||
// always returns the first entry instead.
|
||||
// size_t z;
|
||||
// for (size_t z = 0; z < this->discard_log_card_refs.size(); z++) {
|
||||
// if ((this->discard_log_card_refs[z] != 0xFFFF) && (this->discard_log_reasons[z] == reason)) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
uint16_t ret = this->discard_log_card_refs[0];
|
||||
for (size_t z = 0; z < this->discard_log_card_refs.size() - 1; z++) {
|
||||
this->discard_log_card_refs[z] = this->discard_log_card_refs[z + 1];
|
||||
this->discard_log_reasons[z] = this->discard_log_reasons[z + 1];
|
||||
}
|
||||
this->discard_log_card_refs[this->discard_log_card_refs.size() - 1] = 0xFFFF;
|
||||
this->discard_log_reasons[this->discard_log_reasons.size() - 1] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool PlayerState::move_card_to_location_by_card_index(size_t card_index, const Location& new_loc) {
|
||||
auto s = this->server();
|
||||
|
||||
@@ -1042,16 +1098,18 @@ void PlayerState::on_cards_destroyed() {
|
||||
void PlayerState::replace_all_set_assists_with_random_assists() {
|
||||
auto s = this->server();
|
||||
|
||||
const auto& assist_card_ids = all_assist_card_ids(s->options.is_trial());
|
||||
bool is_trial = s->options.is_trial();
|
||||
const auto& assist_card_ids = all_assist_card_ids(is_trial);
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto other_ps = s->get_player_state(client_id);
|
||||
if (other_ps &&
|
||||
((other_ps->card_refs[6] != 0xFFFF) || (other_ps->set_assist_card_id != 0xFFFF))) {
|
||||
((other_ps->card_refs[6] != 0xFFFF) || (!is_trial && (other_ps->set_assist_card_id != 0xFFFF)))) {
|
||||
uint16_t card_id = 0x0130;
|
||||
while (card_id == 0x0130) { // God Whim
|
||||
size_t index = s->get_random(assist_card_ids.size());
|
||||
card_id = assist_card_ids[index];
|
||||
if (!this->god_whim_can_use_hidden_cards) {
|
||||
// In NTE, God Whim can use ANY card, even unobtainable cards.
|
||||
if (!is_trial && !this->god_whim_can_use_hidden_cards) {
|
||||
auto ce = s->definition_for_card_id(card_id);
|
||||
if (!ce || ce->def.cannot_drop) {
|
||||
continue;
|
||||
@@ -1078,13 +1136,15 @@ bool PlayerState::replace_assist_card_by_id(uint16_t card_id) {
|
||||
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
s->assist_server->populate_effects();
|
||||
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto other_ps = s->get_player_state(client_id);
|
||||
if (other_ps) {
|
||||
uint32_t prev_assist_flags = other_ps->assist_flags;
|
||||
other_ps->set_assist_flags_from_assist_effects();
|
||||
if (prev_assist_flags != other_ps->assist_flags) {
|
||||
other_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
if (!s->options.is_trial()) {
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto other_ps = s->get_player_state(client_id);
|
||||
if (other_ps) {
|
||||
uint32_t prev_assist_flags = other_ps->assist_flags;
|
||||
other_ps->set_assist_flags_from_assist_effects();
|
||||
if (prev_assist_flags != other_ps->assist_flags) {
|
||||
other_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1283,12 +1343,15 @@ bool PlayerState::set_card_from_hand(
|
||||
|
||||
this->deck_state->set_card_ref_in_play(card_ref);
|
||||
|
||||
bool is_trial = s->options.is_trial();
|
||||
auto ce = s->definition_for_card_ref(card_ref);
|
||||
if (ce->def.type == CardType::ITEM || ce->def.type == CardType::CREATURE) {
|
||||
if ((card_index < 7) || (card_index >= 15)) {
|
||||
return 0;
|
||||
}
|
||||
this->card_refs[card_index + 1] = card_ref;
|
||||
// Note: NTE doesn't call the destructor on the existing card, if there is
|
||||
// one. Is that a bug?
|
||||
this->set_cards[card_index - 7] = make_shared<Card>(s->card_id_for_card_ref(card_ref), card_ref, this->client_id, s);
|
||||
auto new_card = this->set_cards[card_index - 7];
|
||||
new_card->init();
|
||||
@@ -1297,6 +1360,8 @@ bool PlayerState::set_card_from_hand(
|
||||
new_card->loc.x = loc->x;
|
||||
new_card->loc.y = loc->y;
|
||||
}
|
||||
// Note: NTE doesn't track this, but NTE can't use it anyway, so we don't
|
||||
// check for NTE here.
|
||||
this->stats.num_item_or_creature_cards_set++;
|
||||
|
||||
} else if (ce->def.type == CardType::ASSIST) {
|
||||
@@ -1316,7 +1381,9 @@ bool PlayerState::set_card_from_hand(
|
||||
target_ps->assist_card_set_number = s->next_assist_card_set_number++;
|
||||
|
||||
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
target_ps->apply_assist_card_effect_on_set(this->shared_from_this());
|
||||
if (!is_trial) {
|
||||
target_ps->apply_assist_card_effect_on_set(this->shared_from_this());
|
||||
}
|
||||
target_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
s->assist_server->populate_effects();
|
||||
|
||||
@@ -1331,26 +1398,33 @@ bool PlayerState::set_card_from_hand(
|
||||
other_ps->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
}
|
||||
}
|
||||
if (is_trial) {
|
||||
target_ps->apply_assist_card_effect_on_set(this->shared_from_this());
|
||||
}
|
||||
}
|
||||
// NTE doesn't track this, but NTE also doesn't have access to it.
|
||||
this->stats.num_assist_cards_set++;
|
||||
}
|
||||
// NTE doesn't track this, but NTE also doesn't have access to it.
|
||||
this->stats.num_cards_set++;
|
||||
|
||||
this->compute_total_set_cards_cost();
|
||||
s->card_special->on_card_set(this->shared_from_this(), card_ref);
|
||||
if (ce->def.type == CardType::ASSIST) {
|
||||
if (!is_trial && (ce->def.type == CardType::ASSIST)) {
|
||||
s->check_for_destroyed_cards_and_send_6xB4x05_6xB4x02();
|
||||
}
|
||||
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
|
||||
s->send_6xB4x05();
|
||||
|
||||
G_Unknown_Ep3_6xB4x4A cmd;
|
||||
cmd.card_refs.clear(0xFFFF);
|
||||
cmd.card_refs[0] = card_ref;
|
||||
cmd.client_id = this->client_id;
|
||||
cmd.entry_count = 1;
|
||||
cmd.round_num = s->get_round_num();
|
||||
s->send(cmd);
|
||||
if (!is_trial) {
|
||||
G_Unknown_Ep3_6xB4x4A cmd;
|
||||
cmd.card_refs.clear(0xFFFF);
|
||||
cmd.card_refs[0] = card_ref;
|
||||
cmd.client_id = this->client_id;
|
||||
cmd.entry_count = 1;
|
||||
cmd.round_num = s->get_round_num();
|
||||
s->send(cmd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1682,6 +1756,7 @@ int16_t PlayerState::get_assist_turns_remaining() {
|
||||
|
||||
bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
|
||||
auto s = this->server();
|
||||
bool is_trial = s->options.is_trial();
|
||||
|
||||
auto attacker_card = s->card_for_set_card_ref(pa.attacker_card_ref);
|
||||
if (attacker_card) {
|
||||
@@ -1689,67 +1764,80 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
|
||||
}
|
||||
|
||||
auto action_type = s->ruler_server->get_pending_action_type(pa);
|
||||
this->subtract_or_check_atk_or_def_points_for_action(pa, 1);
|
||||
if (!is_trial) {
|
||||
this->subtract_or_check_atk_or_def_points_for_action(pa, 1);
|
||||
}
|
||||
|
||||
if (action_type == ActionType::ATTACK) {
|
||||
G_Unknown_Ep3_6xB4x4A cmd;
|
||||
cmd.card_refs.clear(0xFFFF);
|
||||
cmd.client_id = this->client_id;
|
||||
cmd.round_num = s->get_round_num();
|
||||
cmd.entry_count = 0;
|
||||
auto card = s->card_for_set_card_ref(pa.attacker_card_ref);
|
||||
if (card) {
|
||||
card->loc.direction = pa.facing_direction;
|
||||
|
||||
G_Unknown_Ep3_6xB4x4A cmd;
|
||||
cmd.card_refs.clear(0xFFFF);
|
||||
cmd.client_id = this->client_id;
|
||||
cmd.round_num = s->get_round_num();
|
||||
cmd.entry_count = 0;
|
||||
size_t z = 0;
|
||||
do {
|
||||
card->unknown_80237A90(pa, pa.action_card_refs[z]);
|
||||
card->unknown_802379BC(pa.action_card_refs[z]);
|
||||
if (pa.action_card_refs[z] != 0xFFFF) {
|
||||
cmd.card_refs[z] = pa.action_card_refs[z];
|
||||
cmd.entry_count++;
|
||||
}
|
||||
auto ce = s->definition_for_card_ref(pa.action_card_refs[z]);
|
||||
if (ce) {
|
||||
auto card_class = ce->def.card_class();
|
||||
if ((card_class == CardClass::TECH) ||
|
||||
(card_class == CardClass::PHOTON_BLAST) ||
|
||||
(card_class == CardClass::BOSS_TECH)) {
|
||||
this->stats.num_tech_cards_set++;
|
||||
if (!is_trial) {
|
||||
if (pa.action_card_refs[z] != 0xFFFF) {
|
||||
cmd.card_refs[z] = pa.action_card_refs[z];
|
||||
cmd.entry_count++;
|
||||
}
|
||||
if ((card_class == CardClass::ATTACK_ACTION) ||
|
||||
(card_class == CardClass::CONNECT_ONLY_ATTACK_ACTION) ||
|
||||
(card_class == CardClass::BOSS_ATTACK_ACTION)) {
|
||||
this->stats.num_attack_actions_set++;
|
||||
auto ce = s->definition_for_card_ref(pa.action_card_refs[z]);
|
||||
if (ce) {
|
||||
auto card_class = ce->def.card_class();
|
||||
if ((card_class == CardClass::TECH) ||
|
||||
(card_class == CardClass::PHOTON_BLAST) ||
|
||||
(card_class == CardClass::BOSS_TECH)) {
|
||||
this->stats.num_tech_cards_set++;
|
||||
}
|
||||
if ((card_class == CardClass::ATTACK_ACTION) ||
|
||||
(card_class == CardClass::CONNECT_ONLY_ATTACK_ACTION) ||
|
||||
(card_class == CardClass::BOSS_ATTACK_ACTION)) {
|
||||
this->stats.num_attack_actions_set++;
|
||||
}
|
||||
this->stats.num_cards_set++;
|
||||
}
|
||||
this->stats.num_cards_set++;
|
||||
}
|
||||
z++;
|
||||
} while ((z < 8) && (pa.action_card_refs[z] != 0xFFFF));
|
||||
// Note: This is never sent on NTE because entry_count will always be zero
|
||||
if (cmd.entry_count > 0) {
|
||||
s->send(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (action_type == ActionType::DEFENSE) {
|
||||
G_Unknown_Ep3_6xB4x4A cmd;
|
||||
cmd.card_refs.clear(0xFFFF);
|
||||
cmd.client_id = this->client_id;
|
||||
cmd.round_num = s->get_round_num();
|
||||
for (size_t z = 0; (z < 4 * 9) && (pa.target_card_refs[z] != 0xFFFF); z++) {
|
||||
auto target_card = s->card_for_set_card_ref(pa.target_card_refs[z]);
|
||||
if (target_card) {
|
||||
target_card->unknown_802379DC(pa);
|
||||
if (this->client_id == target_card->get_client_id()) {
|
||||
this->stats.defense_actions_set_on_self++;
|
||||
} else {
|
||||
this->stats.defense_actions_set_on_ally++;
|
||||
if (!is_trial) {
|
||||
if (this->client_id == target_card->get_client_id()) {
|
||||
this->stats.defense_actions_set_on_self++;
|
||||
} else {
|
||||
this->stats.defense_actions_set_on_ally++;
|
||||
}
|
||||
this->stats.num_cards_set++;
|
||||
}
|
||||
this->stats.num_cards_set++;
|
||||
}
|
||||
}
|
||||
cmd.card_refs[0] = pa.defense_card_ref;
|
||||
cmd.entry_count = 1;
|
||||
s->send(cmd);
|
||||
if (!is_trial) {
|
||||
G_Unknown_Ep3_6xB4x4A cmd;
|
||||
cmd.card_refs.clear(0xFFFF);
|
||||
cmd.client_id = this->client_id;
|
||||
cmd.round_num = s->get_round_num();
|
||||
cmd.card_refs[0] = pa.defense_card_ref;
|
||||
cmd.entry_count = 1;
|
||||
s->send(cmd);
|
||||
}
|
||||
}
|
||||
if (is_trial) {
|
||||
this->subtract_or_check_atk_or_def_points_for_action(pa, 1);
|
||||
}
|
||||
for (size_t z = 0; (z < pa.action_card_refs.size()) && (pa.action_card_refs[z] != 0xFFFF); z++) {
|
||||
this->discard_ref_from_hand(pa.action_card_refs[z]);
|
||||
@@ -1869,6 +1957,13 @@ void PlayerState::roll_main_dice_or_apply_after_effects() {
|
||||
auto s = this->server();
|
||||
const auto& rules = s->map_and_rules->rules;
|
||||
|
||||
// In NTE, the dice behave differently - there is no minimum, and instead the
|
||||
// player can specify a fixed value for each die or a random value (1-6). The
|
||||
// implementation of this function is therefore quite different on NTE, but
|
||||
// since we already support custom ranges for ATK and DEF dice, we just use
|
||||
// the non-NTE logic and assign the dice ranges at battle start time to yield
|
||||
// the NTE behavior. (See RulesTrial in DataIndexes.cc for how this is done.)
|
||||
|
||||
uint8_t min_atk_dice = rules.min_dice;
|
||||
uint8_t max_atk_dice = rules.max_dice;
|
||||
if (min_atk_dice == 0) {
|
||||
@@ -1882,6 +1977,12 @@ void PlayerState::roll_main_dice_or_apply_after_effects() {
|
||||
max_atk_dice = min_atk_dice;
|
||||
min_atk_dice = t;
|
||||
}
|
||||
uint8_t atk_dice_range_width = (max_atk_dice - min_atk_dice) + 1;
|
||||
if (atk_dice_range_width < 2) {
|
||||
this->dice_results[0] = min_atk_dice;
|
||||
} else {
|
||||
this->dice_results[0] = min_atk_dice + s->get_random(atk_dice_range_width);
|
||||
}
|
||||
|
||||
uint8_t min_def_dice = rules.min_def_dice() ? rules.min_def_dice() : rules.min_dice;
|
||||
uint8_t max_def_dice = rules.max_def_dice() ? rules.max_def_dice() : rules.max_dice;
|
||||
@@ -1896,23 +1997,11 @@ void PlayerState::roll_main_dice_or_apply_after_effects() {
|
||||
max_def_dice = min_def_dice;
|
||||
min_def_dice = t;
|
||||
}
|
||||
|
||||
// In NTE, the dice aren't actually rolled here; they are instead rolled in
|
||||
// dice_phase_before, and only the after effects are processed here.
|
||||
if (!s->options.is_trial()) {
|
||||
uint8_t atk_dice_range_width = (max_atk_dice - min_atk_dice) + 1;
|
||||
if (atk_dice_range_width < 2) {
|
||||
this->dice_results[0] = min_atk_dice;
|
||||
} else {
|
||||
this->dice_results[0] = min_atk_dice + s->get_random(atk_dice_range_width);
|
||||
}
|
||||
|
||||
uint8_t def_dice_range_width = (max_def_dice - min_def_dice) + 1;
|
||||
if (def_dice_range_width < 2) {
|
||||
this->dice_results[1] = min_def_dice;
|
||||
} else {
|
||||
this->dice_results[1] = min_def_dice + s->get_random(def_dice_range_width);
|
||||
}
|
||||
uint8_t def_dice_range_width = (max_def_dice - min_def_dice) + 1;
|
||||
if (def_dice_range_width < 2) {
|
||||
this->dice_results[1] = min_def_dice;
|
||||
} else {
|
||||
this->dice_results[1] = min_def_dice + s->get_random(def_dice_range_width);
|
||||
}
|
||||
|
||||
bool should_exchange = false;
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
const Location& loc,
|
||||
uint8_t target_team_id) const;
|
||||
uint8_t get_atk_points() const;
|
||||
uint8_t get_atk_points_nte() const;
|
||||
void get_short_status_for_card_index_in_hand(size_t hand_index, CardShortStatus* stat) const;
|
||||
std::shared_ptr<DeckState> get_deck();
|
||||
uint8_t get_def_points() const;
|
||||
@@ -95,6 +96,7 @@ public:
|
||||
bool is_mulligan_allowed() const;
|
||||
bool is_team_turn() const;
|
||||
void log_discard(uint16_t card_ref, uint16_t reason);
|
||||
uint16_t pop_from_discard_log(uint16_t reason);
|
||||
bool move_card_to_location_by_card_index(size_t card_index, const Location& new_loc);
|
||||
void move_null_hand_refs_to_end();
|
||||
void on_cards_destroyed();
|
||||
|
||||
@@ -481,31 +481,33 @@ bool ActionChainWithConds::can_apply_attack() const {
|
||||
return this->check_flag(4) ? false : (this->chain.target_card_ref_count != 0);
|
||||
}
|
||||
|
||||
/* 0000 */ int8_t effective_ap;
|
||||
/* 0001 */ int8_t effective_tp;
|
||||
/* 0002 */ int8_t ap_effect_bonus;
|
||||
/* 0003 */ int8_t damage;
|
||||
/* 0004 */ le_uint16_t acting_card_ref;
|
||||
/* 0006 */ le_uint16_t unknown_card_ref_a3;
|
||||
/* 0008 */ parray<le_uint16_t, 8> attack_action_card_refs;
|
||||
/* 0018 */ uint8_t attack_action_card_ref_count;
|
||||
/* 0019 */ AttackMedium attack_medium;
|
||||
/* 001A */ uint8_t target_card_ref_count;
|
||||
/* 001B */ ActionSubphase action_subphase;
|
||||
/* 001C */ uint8_t strike_count;
|
||||
/* 001D */ int8_t damage_multiplier;
|
||||
/* 001E */ uint8_t attack_number;
|
||||
/* 001F */ int8_t tp_effect_bonus;
|
||||
/* 0020 */ uint8_t unused1;
|
||||
/* 0021 */ uint8_t unused2;
|
||||
/* 0022 */ int8_t card_ap;
|
||||
/* 0023 */ int8_t card_tp;
|
||||
/* 0024 */ le_uint32_t flags;
|
||||
// The only difference between this structure and ActionChainWithConds is that
|
||||
// these two fields have changed orders.
|
||||
/* 0028 */ parray<Condition, 9> conditions;
|
||||
/* 00B8 */ parray<le_uint16_t, 4 * 9> target_card_refs;
|
||||
/* 0100 */
|
||||
uint8_t ActionChainWithConds::get_adjusted_move_ability_nte(uint8_t ability) const {
|
||||
for (size_t z = 0; z < this->conditions.size(); z++) {
|
||||
const auto& cond = this->conditions[z];
|
||||
switch (cond.type) {
|
||||
case ConditionType::IMMOBILE:
|
||||
case ConditionType::FREEZE:
|
||||
ability = 0;
|
||||
break;
|
||||
case ConditionType::SET_MV_COST_TO_0:
|
||||
ability = 99;
|
||||
break;
|
||||
case ConditionType::ADD_1_TO_MV_COST:
|
||||
ability--;
|
||||
break;
|
||||
case ConditionType::SCALE_MV_COST:
|
||||
if (cond.value == 0) {
|
||||
ability = 99;
|
||||
} else {
|
||||
ability /= cond.value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ability;
|
||||
}
|
||||
|
||||
ActionChainWithCondsTrial::ActionChainWithCondsTrial(const ActionChainWithConds& src)
|
||||
: effective_ap(src.chain.effective_ap),
|
||||
|
||||
@@ -169,6 +169,8 @@ struct ActionChainWithConds {
|
||||
void set_action_subphase_from_card(std::shared_ptr<const Card> card);
|
||||
bool can_apply_attack() const;
|
||||
|
||||
uint8_t get_adjusted_move_ability_nte(uint8_t ability) const;
|
||||
|
||||
std::string str() const;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
+245
-138
@@ -237,19 +237,25 @@ bool RulerServer::card_has_pierce_or_rampage(
|
||||
uint16_t action_card_ref,
|
||||
uint8_t def_effect_index,
|
||||
AttackMedium attack_medium) const {
|
||||
auto short_statuses = (client_id != 0xFF) ? this->short_statuses[client_id] : nullptr;
|
||||
auto short_statuses = (client_id < 4) ? this->short_statuses[client_id] : nullptr;
|
||||
*out_has_rampage = false;
|
||||
|
||||
if (cond_type == ConditionType::NONE) {
|
||||
return false;
|
||||
bool ret;
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
if (is_trial) {
|
||||
ret = true;
|
||||
} else {
|
||||
if (cond_type == ConditionType::NONE) {
|
||||
return false;
|
||||
}
|
||||
ret = this->check_usability_or_apply_condition_for_card_refs(
|
||||
action_card_ref,
|
||||
attacker_card_ref,
|
||||
// Original code omitted this null check and presumably could crash here
|
||||
short_statuses ? short_statuses->at(0).card_ref.load() : 0xFFFF,
|
||||
def_effect_index,
|
||||
attack_medium);
|
||||
}
|
||||
bool ret = this->check_usability_or_apply_condition_for_card_refs(
|
||||
action_card_ref,
|
||||
attacker_card_ref,
|
||||
// Original code omitted this null check and presumably could crash here
|
||||
short_statuses ? short_statuses->at(0).card_ref.load() : 0xFFFF,
|
||||
def_effect_index,
|
||||
attack_medium);
|
||||
|
||||
switch (cond_type) {
|
||||
case ConditionType::RAMPAGE:
|
||||
@@ -282,7 +288,10 @@ bool RulerServer::card_has_pierce_or_rampage(
|
||||
if (short_statuses) {
|
||||
const auto& sc_status = short_statuses->at(0);
|
||||
auto ce = this->definition_for_card_ref(sc_status.card_ref);
|
||||
if (ce && (this->get_card_ref_max_hp(sc_status.card_ref) <= sc_status.current_hp * 2)) {
|
||||
// This appears to be an NTE bug: Major Pierce doesn't work on Arkz SCs.
|
||||
if (ce &&
|
||||
(!is_trial || (ce->def.type == CardType::HUNTERS_SC)) &&
|
||||
(this->get_card_ref_max_hp(sc_status.card_ref) <= sc_status.current_hp * 2)) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -292,8 +301,7 @@ bool RulerServer::card_has_pierce_or_rampage(
|
||||
}
|
||||
}
|
||||
|
||||
bool RulerServer::attack_action_has_rampage_and_not_pierce(
|
||||
const ActionState& pa, uint16_t card_ref) const {
|
||||
bool RulerServer::attack_action_has_rampage_and_not_pierce(const ActionState& pa, uint16_t card_ref) const {
|
||||
uint16_t orig_card_ref;
|
||||
uint16_t effective_range_card_id;
|
||||
TargetMode effective_target_mode;
|
||||
@@ -367,15 +375,15 @@ bool RulerServer::attack_action_has_rampage_and_not_pierce(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RulerServer::attack_action_has_pierce_and_not_rampage(
|
||||
const ActionState& pa, uint8_t client_id) {
|
||||
if ((client_id_for_card_ref(pa.attacker_card_ref) == 0xFF) || (client_id == 0xFF)) {
|
||||
bool RulerServer::attack_action_has_pierce_and_not_rampage(const ActionState& pa, uint8_t client_id) const {
|
||||
if ((client_id_for_card_ref(pa.attacker_card_ref) == 0xFF) || (client_id >= 4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
auto attack_medium = this->get_attack_medium(pa);
|
||||
auto stat = this->short_statuses[client_id];
|
||||
if (!stat || !this->card_exists_by_status(stat->at(0)) || (stat->at(0).card_ref == 0xFFFF)) {
|
||||
if (!stat || (!is_trial && !this->card_exists_by_status(stat->at(0))) || (stat->at(0).card_ref == 0xFFFF)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -394,6 +402,33 @@ bool RulerServer::attack_action_has_pierce_and_not_rampage(
|
||||
last_action_card_index = z;
|
||||
}
|
||||
|
||||
auto check_chain = [&]() -> optional<bool> {
|
||||
const auto* chain = this->action_chain_with_conds_for_card_ref(pa.attacker_card_ref);
|
||||
if (chain) {
|
||||
for (ssize_t cond_index = 8; cond_index >= 0; cond_index--) {
|
||||
bool has_rampage = false;
|
||||
if (this->card_has_pierce_or_rampage(
|
||||
client_id, chain->conditions[cond_index].type, &has_rampage,
|
||||
pa.attacker_card_ref, chain->conditions[cond_index].card_ref,
|
||||
chain->conditions[cond_index].card_definition_effect_index,
|
||||
attack_medium)) {
|
||||
return true;
|
||||
}
|
||||
if (has_rampage) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullopt;
|
||||
};
|
||||
|
||||
if (is_trial) {
|
||||
auto res = check_chain();
|
||||
if (res.has_value()) {
|
||||
return res.value();
|
||||
}
|
||||
}
|
||||
|
||||
for (; last_action_card_index >= 0; last_action_card_index--) {
|
||||
auto ce = this->definition_for_card_ref(
|
||||
pa.action_card_refs[last_action_card_index]);
|
||||
@@ -420,20 +455,10 @@ bool RulerServer::attack_action_has_pierce_and_not_rampage(
|
||||
}
|
||||
}
|
||||
|
||||
const auto* chain = this->action_chain_with_conds_for_card_ref(pa.attacker_card_ref);
|
||||
if (chain) {
|
||||
for (ssize_t cond_index = 8; cond_index >= 0; cond_index--) {
|
||||
bool has_rampage = false;
|
||||
if (this->card_has_pierce_or_rampage(
|
||||
client_id, chain->conditions[cond_index].type, &has_rampage,
|
||||
pa.attacker_card_ref, chain->conditions[cond_index].card_ref,
|
||||
chain->conditions[cond_index].card_definition_effect_index,
|
||||
attack_medium)) {
|
||||
return true;
|
||||
}
|
||||
if (has_rampage) {
|
||||
return false;
|
||||
}
|
||||
if (!is_trial) {
|
||||
auto res = check_chain();
|
||||
if (res.has_value()) {
|
||||
return res.value();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,10 +820,13 @@ bool RulerServer::check_pierce_and_rampage(
|
||||
uint16_t action_card_ref,
|
||||
uint8_t def_effect_index,
|
||||
AttackMedium attack_medium) const {
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
|
||||
// Note: NTE doesn't set this to zero; it apparently expects the caller to.
|
||||
*out_has_pierce = false;
|
||||
|
||||
const auto* card_short_status = this->short_status_for_card_ref(card_ref);
|
||||
if (cond_type == ConditionType::NONE) {
|
||||
if (!is_trial && (cond_type == ConditionType::NONE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -820,8 +848,9 @@ bool RulerServer::check_pierce_and_rampage(
|
||||
client_short_statuses = nullptr;
|
||||
}
|
||||
|
||||
bool apply_check_result = this->check_usability_or_apply_condition_for_card_refs(
|
||||
action_card_ref, attacker_card_ref, card_ref, def_effect_index, attack_medium);
|
||||
bool apply_check_result = (is_trial ||
|
||||
this->check_usability_or_apply_condition_for_card_refs(
|
||||
action_card_ref, attacker_card_ref, card_ref, def_effect_index, attack_medium));
|
||||
|
||||
switch (cond_type) {
|
||||
case ConditionType::PIERCE:
|
||||
@@ -1366,6 +1395,7 @@ uint16_t RulerServer::compute_attack_or_defense_costs(
|
||||
cost_bias++;
|
||||
}
|
||||
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
if (pa.action_card_refs[0] == 0xFFFF) {
|
||||
total_cost = cost_bias + 1;
|
||||
} else {
|
||||
@@ -1388,13 +1418,15 @@ uint16_t RulerServer::compute_attack_or_defense_costs(
|
||||
if (this->card_has_mighty_knuckle(pa.action_card_refs[z])) {
|
||||
has_mighty_knuckle = true;
|
||||
}
|
||||
size_t num_assists = this->assist_server->compute_num_assist_effects_for_client(pa.client_id);
|
||||
for (size_t w = 0; w < num_assists; w++) {
|
||||
auto assist_effect = this->assist_server->get_active_assist_by_index(w);
|
||||
if (assist_effect == AssistEffect::INFLATION) {
|
||||
assist_cost_bias++;
|
||||
} else if (assist_effect == AssistEffect::DEFLATION) {
|
||||
assist_cost_bias--;
|
||||
if (!is_trial) {
|
||||
size_t num_assists = this->assist_server->compute_num_assist_effects_for_client(pa.client_id);
|
||||
for (size_t w = 0; w < num_assists; w++) {
|
||||
auto assist_effect = this->assist_server->get_active_assist_by_index(w);
|
||||
if (assist_effect == AssistEffect::INFLATION) {
|
||||
assist_cost_bias++;
|
||||
} else if (assist_effect == AssistEffect::DEFLATION) {
|
||||
assist_cost_bias--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1403,7 +1435,11 @@ uint16_t RulerServer::compute_attack_or_defense_costs(
|
||||
size_t num_assists = this->assist_server->compute_num_assist_effects_for_client(pa.client_id);
|
||||
for (size_t w = 0; w < num_assists; w++) {
|
||||
auto assist_effect = this->assist_server->get_active_assist_by_index(w);
|
||||
if ((assist_effect == AssistEffect::BATTLE_ROYALE) &&
|
||||
if (!is_trial && (assist_effect == AssistEffect::INFLATION)) {
|
||||
assist_cost_bias++;
|
||||
} else if (!is_trial && (assist_effect == AssistEffect::DEFLATION)) {
|
||||
assist_cost_bias--;
|
||||
} else if ((assist_effect == AssistEffect::BATTLE_ROYALE) &&
|
||||
(pa.action_card_refs[0] == 0xFFFF)) {
|
||||
total_cost = 0;
|
||||
final_cost = 0;
|
||||
@@ -1412,7 +1448,9 @@ uint16_t RulerServer::compute_attack_or_defense_costs(
|
||||
|
||||
if (has_mighty_knuckle) {
|
||||
if (!allow_mighty_knuckle) {
|
||||
final_cost = 0;
|
||||
if (!is_trial) {
|
||||
final_cost = 0;
|
||||
}
|
||||
} else {
|
||||
final_cost = max<int16_t>(final_cost, this->hand_and_equip_states[pa.client_id]->atk_points);
|
||||
}
|
||||
@@ -1455,9 +1493,11 @@ bool RulerServer::compute_effective_range_and_target_mode_for_attack(
|
||||
auto target_mode = ce->def.target_mode;
|
||||
if (this->card_ref_or_sc_has_fixed_range(pa.attacker_card_ref)) {
|
||||
card_id = this->card_id_for_card_ref(pa.attacker_card_ref);
|
||||
auto sc_ce = this->definition_for_card_id(card_id);
|
||||
if (sc_ce && (static_cast<uint8_t>(target_mode) < 6)) {
|
||||
target_mode = sc_ce->def.target_mode;
|
||||
if (!this->server()->options.is_trial()) {
|
||||
auto sc_ce = this->definition_for_card_id(card_id);
|
||||
if (sc_ce && (static_cast<uint8_t>(target_mode) < 6)) {
|
||||
target_mode = sc_ce->def.target_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1480,8 +1520,7 @@ bool RulerServer::compute_effective_range_and_target_mode_for_attack(
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RulerServer::count_rampage_targets_for_attack(
|
||||
const ActionState& pa, uint8_t client_id) const {
|
||||
size_t RulerServer::count_rampage_targets_for_attack(const ActionState& pa, uint8_t client_id) const {
|
||||
if (client_id == 0xFF) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1638,7 +1677,8 @@ int32_t RulerServer::error_code_for_client_setting_card(
|
||||
return -0x76;
|
||||
}
|
||||
|
||||
if (!this->is_card_ref_in_hand(card_ref)) {
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
if (!is_trial && !this->is_card_ref_in_hand(card_ref)) {
|
||||
return -0x5E;
|
||||
}
|
||||
|
||||
@@ -1679,7 +1719,7 @@ int32_t RulerServer::error_code_for_client_setting_card(
|
||||
|
||||
// Check for assists that can only be set on yourself
|
||||
auto eff = assist_effect_number_for_card_id(ce->def.card_id, this->server()->options.is_trial());
|
||||
if (((eff == AssistEffect::LEGACY) || (eff == AssistEffect::EXCHANGE)) &&
|
||||
if (((eff == AssistEffect::LEGACY) || (!is_trial && (eff == AssistEffect::EXCHANGE))) &&
|
||||
(assist_target_client_id != 0xFF) &&
|
||||
(assist_target_client_id != client_id_for_card_ref(card_ref))) {
|
||||
return -0x75;
|
||||
@@ -1718,8 +1758,8 @@ int32_t RulerServer::error_code_for_client_setting_card(
|
||||
|
||||
if ((ce->def.type == CardType::ITEM) || (ce->def.type == CardType::CREATURE)) {
|
||||
int16_t existing_fcs_cost = 0;
|
||||
bool limit_summoning_by_count = this->find_condition_on_card_ref(
|
||||
short_statuses->at(0).card_ref, ConditionType::FC_LIMIT_BY_COUNT);
|
||||
bool limit_summoning_by_count = !is_trial &&
|
||||
this->find_condition_on_card_ref(short_statuses->at(0).card_ref, ConditionType::FC_LIMIT_BY_COUNT);
|
||||
for (size_t z = 7; z < 15; z++) {
|
||||
const auto& this_status = short_statuses->at(z);
|
||||
if ((this_status.card_ref != 0xFFFF) && this->card_exists_by_status(this_status)) {
|
||||
@@ -1758,71 +1798,91 @@ int32_t RulerServer::error_code_for_client_setting_card(
|
||||
return 0;
|
||||
}
|
||||
|
||||
Location summon_area_loc;
|
||||
uint8_t summon_area_size;
|
||||
if (!this->get_creature_summon_area(
|
||||
client_id, &summon_area_loc, &summon_area_size)) {
|
||||
if (team_id != 1) {
|
||||
if ((loc->x > 0) && (loc->x < this->map_and_rules->map.width - 1)) {
|
||||
if ((loc->y < this->map_and_rules->map.height - summon_cost - 1) &&
|
||||
(loc->y > 0)) {
|
||||
return 0;
|
||||
if (is_trial) {
|
||||
// It seems NTE assumes that teams always start on the same ends of the
|
||||
// map; non-NTE removes this restriction.
|
||||
if (team_id == 1) {
|
||||
if (((loc->x < 1) ||
|
||||
(loc->x >= this->map_and_rules->map.width - 1) ||
|
||||
(loc->y < summon_cost + 1) ||
|
||||
(loc->y >= this->map_and_rules->map.height - 1)) &&
|
||||
(loc->y != this->map_and_rules->map.height - 2)) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else if (((loc->x < 1) ||
|
||||
(loc->x >= this->map_and_rules->map.width - 1) ||
|
||||
(loc->y < 1) ||
|
||||
(loc->y >= this->map_and_rules->map.height - summon_cost - 1)) &&
|
||||
(loc->y != 1)) {
|
||||
return -0x7E;
|
||||
}
|
||||
|
||||
} else {
|
||||
Location summon_area_loc;
|
||||
uint8_t summon_area_size;
|
||||
if (!this->get_creature_summon_area(client_id, &summon_area_loc, &summon_area_size)) {
|
||||
if (team_id != 1) {
|
||||
if ((loc->x > 0) && (loc->x < this->map_and_rules->map.width - 1)) {
|
||||
if ((loc->y < this->map_and_rules->map.height - summon_cost - 1) &&
|
||||
(loc->y > 0)) {
|
||||
return 0;
|
||||
}
|
||||
if (loc->y == 1) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (loc->y == 1) {
|
||||
return 0;
|
||||
} else {
|
||||
if ((loc->x > 0) &&
|
||||
(loc->x < this->map_and_rules->map.width - 1)) {
|
||||
if ((summon_cost + 1 <= loc->y) && (loc->y < this->map_and_rules->map.height - 1)) {
|
||||
return 0;
|
||||
}
|
||||
if (loc->y == this->map_and_rules->map.height - 2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -0x7E;
|
||||
}
|
||||
|
||||
int32_t x_offset, y_offset;
|
||||
this->offsets_for_direction(summon_area_loc, &x_offset, &y_offset);
|
||||
if (x_offset == 0) {
|
||||
if ((loc->x < 1) && (loc->x >= this->map_and_rules->map.width - 1)) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else {
|
||||
if ((loc->x > 0) &&
|
||||
(loc->x < this->map_and_rules->map.width - 1)) {
|
||||
if ((summon_cost + 1 <= loc->y) && (loc->y < this->map_and_rules->map.height - 1)) {
|
||||
return 0;
|
||||
int16_t diff = max<int16_t>(summon_area_size - summon_cost, 0);
|
||||
if (x_offset > 0) {
|
||||
if (loc->x < summon_area_loc.x) {
|
||||
return -0x7E;
|
||||
}
|
||||
if (loc->y == this->map_and_rules->map.height - 2) {
|
||||
return 0;
|
||||
if (loc->x > summon_area_loc.x + diff) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else if (x_offset < 0) {
|
||||
if ((loc->x > summon_area_loc.x) || (loc->x < summon_area_loc.x - diff)) {
|
||||
return -0x7E;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -0x7E;
|
||||
}
|
||||
|
||||
int32_t x_offset, y_offset;
|
||||
this->offsets_for_direction(summon_area_loc, &x_offset, &y_offset);
|
||||
if (x_offset == 0) {
|
||||
if ((loc->x < 1) && (loc->x >= this->map_and_rules->map.width - 1)) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else {
|
||||
int16_t diff = max<int16_t>(summon_area_size - summon_cost, 0);
|
||||
if (x_offset > 0) {
|
||||
if (loc->x < summon_area_loc.x) {
|
||||
if (y_offset == 0) {
|
||||
if ((loc->y < 1) && (loc->y >= this->map_and_rules->map.height - 1)) {
|
||||
return -0x7E;
|
||||
}
|
||||
if (loc->x > summon_area_loc.x + diff) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else if (x_offset < 0) {
|
||||
if ((loc->x > summon_area_loc.x) || (loc->x < summon_area_loc.x - diff)) {
|
||||
return -0x7E;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (y_offset == 0) {
|
||||
if ((loc->y < 1) && (loc->y >= this->map_and_rules->map.height - 1)) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else {
|
||||
int16_t diff = max<int16_t>(summon_area_size - summon_cost, 0);
|
||||
if (y_offset > 0) {
|
||||
if (loc->y < summon_area_loc.y) {
|
||||
return -0x7E;
|
||||
}
|
||||
if (loc->y > summon_area_loc.y + diff) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else if (y_offset < 0) {
|
||||
if ((loc->y > summon_area_loc.y) || (loc->y < summon_area_loc.y - diff)) {
|
||||
return -0x7E;
|
||||
} else {
|
||||
int16_t diff = max<int16_t>(summon_area_size - summon_cost, 0);
|
||||
if (y_offset > 0) {
|
||||
if (loc->y < summon_area_loc.y) {
|
||||
return -0x7E;
|
||||
}
|
||||
if (loc->y > summon_area_loc.y + diff) {
|
||||
return -0x7E;
|
||||
}
|
||||
} else if (y_offset < 0) {
|
||||
if ((loc->y > summon_area_loc.y) || (loc->y < summon_area_loc.y - diff)) {
|
||||
return -0x7E;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2026,7 +2086,7 @@ uint8_t RulerServer::get_card_ref_max_hp(uint16_t card_ref) const {
|
||||
return 0;
|
||||
} else if (((ce->def.type == CardType::HUNTERS_SC) || (ce->def.type == CardType::ARKZ_SC)) &&
|
||||
(this->map_and_rules->rules.char_hp > 0) &&
|
||||
!this->card_ref_is_boss_sc(card_ref)) {
|
||||
(this->server()->options.is_trial() || !this->card_ref_is_boss_sc(card_ref))) {
|
||||
return this->map_and_rules->rules.char_hp;
|
||||
} else {
|
||||
return ce->def.hp.stat;
|
||||
@@ -2175,7 +2235,7 @@ bool RulerServer::is_attack_valid(const ActionState& pa) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attacker_card_status->card_flags & 2) {
|
||||
if (!this->server()->options.is_trial() && (attacker_card_status->card_flags & 2)) {
|
||||
this->error_code3 = -0x60;
|
||||
return false;
|
||||
}
|
||||
@@ -2291,7 +2351,9 @@ bool RulerServer::is_attack_or_defense_valid(const ActionState& pa) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t cost = this->compute_attack_or_defense_costs(pa, false, nullptr);
|
||||
// NTE apparently does not check the action's cost here
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
int16_t cost = is_trial ? 0 : this->compute_attack_or_defense_costs(pa, false, nullptr);
|
||||
|
||||
switch (this->get_pending_action_type(pa)) {
|
||||
case ActionType::ATTACK:
|
||||
@@ -2387,8 +2449,9 @@ bool RulerServer::is_defense_valid(const ActionState& pa) {
|
||||
}
|
||||
}
|
||||
|
||||
if (this->find_condition_on_card_ref(pa.target_card_refs[0], ConditionType::HOLD) ||
|
||||
this->find_condition_on_card_ref(pa.target_card_refs[0], ConditionType::CANNOT_DEFEND)) {
|
||||
if (!this->server()->options.is_trial() &&
|
||||
(this->find_condition_on_card_ref(pa.target_card_refs[0], ConditionType::HOLD) ||
|
||||
this->find_condition_on_card_ref(pa.target_card_refs[0], ConditionType::CANNOT_DEFEND))) {
|
||||
this->error_code3 = -0x63;
|
||||
return false;
|
||||
}
|
||||
@@ -2417,30 +2480,53 @@ size_t RulerServer::max_move_distance_for_card_ref(uint32_t card_ref) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t ret = ce->def.mv.stat;
|
||||
|
||||
Condition cond;
|
||||
if (this->find_condition_on_card_ref(card_ref, ConditionType::MV_BONUS, &cond, nullptr, true)) {
|
||||
ret += cond.value;
|
||||
}
|
||||
if (this->find_condition_on_card_ref(card_ref, ConditionType::SET_MV, &cond, nullptr, true)) {
|
||||
ret = cond.value;
|
||||
}
|
||||
ret = max<ssize_t>(0, ret);
|
||||
|
||||
size_t num_assists = this->assist_server->compute_num_assist_effects_for_client(client_id);
|
||||
bool has_stamina_effect = false;
|
||||
for (size_t z = 0; z < num_assists; z++) {
|
||||
auto eff = this->assist_server->get_active_assist_by_index(z);
|
||||
if (eff == AssistEffect::SNAIL_PACE) {
|
||||
return 1;
|
||||
if (this->server()->options.is_trial()) {
|
||||
if (ce->def.type == CardType::ITEM) {
|
||||
return ce->def.mv.stat;
|
||||
}
|
||||
if (eff == AssistEffect::STAMINA) {
|
||||
has_stamina_effect = true;
|
||||
}
|
||||
}
|
||||
|
||||
return (has_stamina_effect) ? 9 : min<ssize_t>(9, ret);
|
||||
Condition cond;
|
||||
if (this->find_condition_on_card_ref(card_ref, ConditionType::SET_MV, &cond)) {
|
||||
return cond.value;
|
||||
}
|
||||
|
||||
size_t num_assists = this->assist_server->compute_num_assist_effects_for_client(client_id);
|
||||
bool has_stamina_effect = false;
|
||||
for (size_t z = 0; z < num_assists; z = z + 1) {
|
||||
auto assist = this->assist_server->get_active_assist_by_index(z);
|
||||
if (assist == AssistEffect::SNAIL_PACE) {
|
||||
return 1;
|
||||
} else if (assist == AssistEffect::STAMINA) {
|
||||
has_stamina_effect = true;
|
||||
}
|
||||
}
|
||||
return has_stamina_effect ? 99 : ce->def.mv.stat;
|
||||
|
||||
} else {
|
||||
ssize_t ret = ce->def.mv.stat;
|
||||
Condition cond;
|
||||
if (this->find_condition_on_card_ref(card_ref, ConditionType::MV_BONUS, &cond, nullptr, true)) {
|
||||
ret += cond.value;
|
||||
}
|
||||
if (this->find_condition_on_card_ref(card_ref, ConditionType::SET_MV, &cond, nullptr, true)) {
|
||||
ret = cond.value;
|
||||
}
|
||||
ret = max<ssize_t>(0, ret);
|
||||
|
||||
size_t num_assists = this->assist_server->compute_num_assist_effects_for_client(client_id);
|
||||
bool has_stamina_effect = false;
|
||||
for (size_t z = 0; z < num_assists; z++) {
|
||||
auto eff = this->assist_server->get_active_assist_by_index(z);
|
||||
if (eff == AssistEffect::SNAIL_PACE) {
|
||||
return 1;
|
||||
}
|
||||
if (eff == AssistEffect::STAMINA) {
|
||||
has_stamina_effect = true;
|
||||
}
|
||||
}
|
||||
|
||||
return has_stamina_effect ? 9 : min<ssize_t>(9, ret);
|
||||
}
|
||||
}
|
||||
|
||||
RulerServer::MovePath::MovePath()
|
||||
@@ -2552,9 +2638,11 @@ int32_t RulerServer::set_cost_for_card(uint8_t client_id, uint16_t card_ref) con
|
||||
return -0x7D;
|
||||
}
|
||||
|
||||
bool is_trial = this->server()->options.is_trial();
|
||||
auto short_statuses = this->short_statuses[client_id];
|
||||
int32_t ret = ce->def.self_cost;
|
||||
if (short_statuses &&
|
||||
if (!is_trial &&
|
||||
short_statuses &&
|
||||
this->card_exists_by_status(short_statuses->at(0)) &&
|
||||
this->find_condition_on_card_ref(short_statuses->at(0).card_ref, ConditionType::UNKNOWN_69)) {
|
||||
ret = 0;
|
||||
@@ -2587,9 +2675,8 @@ int32_t RulerServer::set_cost_for_card(uint8_t client_id, uint16_t card_ref) con
|
||||
for (size_t z = 0; z < num_assists; z++) {
|
||||
auto eff = this->assist_server->get_active_assist_by_index(z);
|
||||
if (eff == AssistEffect::LAND_PRICE) {
|
||||
// Note: Original code had an extra addend (ret < 0 && (ret & 1) != 0),
|
||||
// but ret cannot be negatve here, so we omit it.
|
||||
ret += ret >> 1;
|
||||
// In NTE, Land Price is apparently 2x rather than 1.5x
|
||||
ret = is_trial ? (ret << 1) : (ret + (ret >> 1));
|
||||
} else if (eff == AssistEffect::DEFLATION) {
|
||||
ret = max<int32_t>(0, ret - 1);
|
||||
} else if (eff == AssistEffect::INFLATION) {
|
||||
@@ -2674,4 +2761,24 @@ int32_t RulerServer::verify_deck(
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t RulerServer::count_targets_with_rampage_and_not_pierce_nte(const ActionState& as) const {
|
||||
size_t ret = 0;
|
||||
for (size_t z = 0; (z < as.target_card_refs.size()) && (as.target_card_refs[z] != 0xFFFF); z++) {
|
||||
if (this->attack_action_has_rampage_and_not_pierce(as, as.target_card_refs[z])) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t RulerServer::count_targets_with_pierce_and_not_rampage_nte(const ActionState& as) const {
|
||||
size_t ret = 0;
|
||||
for (size_t z = 0; (z < as.target_card_refs.size()) && (as.target_card_refs[z] != 0xFFFF); z++) {
|
||||
if (this->attack_action_has_pierce_and_not_rampage(as, client_id_for_card_ref(as.target_card_refs[z]))) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Episode3
|
||||
|
||||
@@ -61,7 +61,9 @@ public:
|
||||
uint8_t def_effect_index,
|
||||
AttackMedium attack_medium) const;
|
||||
bool attack_action_has_rampage_and_not_pierce(const ActionState& pa, uint16_t card_ref) const;
|
||||
bool attack_action_has_pierce_and_not_rampage(const ActionState& pa, uint8_t client_id);
|
||||
bool attack_action_has_pierce_and_not_rampage(const ActionState& pa, uint8_t client_id) const;
|
||||
size_t count_targets_with_rampage_and_not_pierce_nte(const ActionState& as) const;
|
||||
size_t count_targets_with_pierce_and_not_rampage_nte(const ActionState& as) const;
|
||||
bool card_exists_by_status(const CardShortStatus& stat) const;
|
||||
bool card_has_mighty_knuckle(uint32_t card_ref) const;
|
||||
uint16_t card_id_for_card_ref(uint16_t card_ref) const;
|
||||
|
||||
+206
-90
@@ -938,9 +938,15 @@ void Server::end_action_phase() {
|
||||
// that this can only ever be 0 or 2, but we may have to delete the enum if
|
||||
// that turns out to be false.
|
||||
this->action_subphase = static_cast<ActionSubphase>(static_cast<uint8_t>(this->action_subphase) + 2);
|
||||
this->copy_player_states_to_prev_states();
|
||||
this->unknown_8023EEF4();
|
||||
this->send_set_card_updates_and_6xB4x04_if_needed();
|
||||
if (this->options.is_trial()) {
|
||||
this->unknown_8023EEF4();
|
||||
this->update_battle_state_flags_and_send_6xB4x03_if_needed(0);
|
||||
this->send_6xB4x02_for_all_players_if_needed();
|
||||
} else {
|
||||
this->copy_player_states_to_prev_states();
|
||||
this->unknown_8023EEF4();
|
||||
this->send_set_card_updates_and_6xB4x04_if_needed();
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::enqueue_attack_or_defense(uint8_t client_id, ActionState* pa) {
|
||||
@@ -982,8 +988,7 @@ bool Server::enqueue_attack_or_defense(uint8_t client_id, ActionState* pa) {
|
||||
size_t attack_index = this->num_pending_attacks++;
|
||||
this->pending_attacks[attack_index] = *pa;
|
||||
ps->set_action_cards_for_action_state(*pa);
|
||||
auto card = this->card_for_set_card_ref(this->send_6xB4x06_if_card_ref_invalid(
|
||||
pa->attacker_card_ref, 1));
|
||||
auto card = this->card_for_set_card_ref(this->send_6xB4x06_if_card_ref_invalid(pa->attacker_card_ref, 1));
|
||||
if (card) {
|
||||
card->card_flags |= 0x400;
|
||||
auto card_ps = card->player_state();
|
||||
@@ -1481,6 +1486,8 @@ void Server::set_player_deck_valid(uint8_t client_id) {
|
||||
}
|
||||
|
||||
void Server::setup_and_start_battle() {
|
||||
bool is_trial = this->options.is_trial();
|
||||
|
||||
this->setup_phase = SetupPhase::STARTER_ROLLS;
|
||||
|
||||
// Note: This is where original implementation re-seeds random_crypt (it uses
|
||||
@@ -1488,14 +1495,16 @@ void Server::setup_and_start_battle() {
|
||||
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
if (!this->check_presence_entry(z)) {
|
||||
this->name_entries[z].clear();
|
||||
if (!is_trial) {
|
||||
this->name_entries[z].clear();
|
||||
}
|
||||
} else {
|
||||
this->player_states[z] = make_shared<PlayerState>(z, this->shared_from_this());
|
||||
this->player_states[z]->init();
|
||||
}
|
||||
}
|
||||
|
||||
if (this->map_and_rules->rules.hp_type == HPType::COMMON_HP) {
|
||||
if (!is_trial && (this->map_and_rules->rules.hp_type == HPType::COMMON_HP)) {
|
||||
int16_t team_hp[2] = {99, 99};
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
auto ps = this->player_states[z];
|
||||
@@ -1542,13 +1551,22 @@ void Server::setup_and_start_battle() {
|
||||
}
|
||||
}
|
||||
|
||||
// this->__unused6__ = 0;
|
||||
// Non-NTE:
|
||||
// this->__unused6__ = 0;
|
||||
// NTE:
|
||||
// this->unknown_a1 = 0;
|
||||
// this->unknown_a2 = 0;
|
||||
|
||||
for (size_t warp_type = 0; warp_type < 5; warp_type++) {
|
||||
this->warp_positions[warp_type][0].clear(0xFF);
|
||||
this->warp_positions[warp_type][1].clear(0xFF);
|
||||
}
|
||||
|
||||
this->num_trap_tiles_nte = 0;
|
||||
for (size_t z = 0; z < 0x10; z++) {
|
||||
this->trap_tile_locs_nte[z].clear(0xFF);
|
||||
}
|
||||
|
||||
for (size_t y = 0; y < 0x10; y++) {
|
||||
for (size_t x = 0; x < 0x10; x++) {
|
||||
uint8_t tile_spec = this->overlay_state.tiles[y][x];
|
||||
@@ -1564,59 +1582,71 @@ void Server::setup_and_start_battle() {
|
||||
}
|
||||
} else if ((tile_type == 0x10) || (tile_type == 0x20) || (tile_type == 0x50)) {
|
||||
this->map_and_rules->map.tiles[y][x] = 0;
|
||||
} else if (is_trial && (tile_type == 0x40) && (this->num_trap_tiles_nte < 0x10)) {
|
||||
this->trap_tile_locs_nte[this->num_trap_tiles_nte][0] = x;
|
||||
this->trap_tile_locs_nte[this->num_trap_tiles_nte][1] = y;
|
||||
this->num_trap_tiles_nte++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t trap_type = 0; trap_type < 5; trap_type++) {
|
||||
this->chosen_trap_tile_index_of_type[trap_type] = 0xFF;
|
||||
if (!is_trial) {
|
||||
for (size_t trap_type = 0; trap_type < 5; trap_type++) {
|
||||
this->chosen_trap_tile_index_of_type[trap_type] = 0xFF;
|
||||
|
||||
size_t num_trap_tiles = 0;
|
||||
for (size_t y = 0; y < 0x10; y++) {
|
||||
for (size_t x = 0; x < 0x10; x++) {
|
||||
if ((this->overlay_state.tiles[y][x] == (trap_type | 0x40)) &&
|
||||
(num_trap_tiles < 8)) {
|
||||
this->trap_tile_locs[trap_type][num_trap_tiles][0] = x;
|
||||
this->trap_tile_locs[trap_type][num_trap_tiles][1] = y;
|
||||
num_trap_tiles++;
|
||||
size_t num_trap_tiles = 0;
|
||||
for (size_t y = 0; y < 0x10; y++) {
|
||||
for (size_t x = 0; x < 0x10; x++) {
|
||||
if ((this->overlay_state.tiles[y][x] == (trap_type | 0x40)) &&
|
||||
(num_trap_tiles < 8)) {
|
||||
this->trap_tile_locs[trap_type][num_trap_tiles][0] = x;
|
||||
this->trap_tile_locs[trap_type][num_trap_tiles][1] = y;
|
||||
num_trap_tiles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->num_trap_tiles_of_type[trap_type] = num_trap_tiles;
|
||||
this->num_trap_tiles_of_type[trap_type] = num_trap_tiles;
|
||||
|
||||
if (num_trap_tiles > 0) {
|
||||
this->chosen_trap_tile_index_of_type[trap_type] = this->get_random(num_trap_tiles);
|
||||
if (num_trap_tiles > 0) {
|
||||
this->chosen_trap_tile_index_of_type[trap_type] = this->get_random(num_trap_tiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->send_6xB4x02_for_all_players_if_needed(true);
|
||||
this->send_6xB4x05();
|
||||
if (is_trial) {
|
||||
this->send_all_state_updates();
|
||||
this->send_6xB4x02_for_all_players_if_needed();
|
||||
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
auto ps = this->player_states[z];
|
||||
if (ps) {
|
||||
ps->send_set_card_updates(true);
|
||||
}
|
||||
}
|
||||
|
||||
this->send_all_state_updates();
|
||||
this->send_6xB4x1C_names_update();
|
||||
this->registration_phase = RegistrationPhase::BATTLE_STARTED;
|
||||
this->update_battle_state_flags_and_send_6xB4x03_if_needed(true);
|
||||
this->send_6xB4x50_trap_tile_locations();
|
||||
|
||||
if (this->options.is_trial()) {
|
||||
G_UpdateMap_Ep3NTE_6xB4x05 cmd;
|
||||
cmd.state = *this->map_and_rules;
|
||||
cmd.start_battle = 1;
|
||||
this->send(cmd);
|
||||
|
||||
} else {
|
||||
this->send_6xB4x02_for_all_players_if_needed(true);
|
||||
this->send_6xB4x05();
|
||||
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
auto ps = this->player_states[z];
|
||||
if (ps) {
|
||||
ps->send_set_card_updates(true);
|
||||
}
|
||||
}
|
||||
|
||||
this->send_all_state_updates();
|
||||
this->send_6xB4x1C_names_update();
|
||||
}
|
||||
|
||||
this->registration_phase = RegistrationPhase::BATTLE_STARTED;
|
||||
this->update_battle_state_flags_and_send_6xB4x03_if_needed(true);
|
||||
|
||||
if (!is_trial) {
|
||||
this->send_6xB4x50_trap_tile_locations();
|
||||
G_UpdateMap_Ep3_6xB4x05 cmd;
|
||||
cmd.state = *this->map_and_rules;
|
||||
cmd.start_battle = 1;
|
||||
this->send(cmd);
|
||||
}
|
||||
|
||||
this->battle_start_usecs = now();
|
||||
|
||||
this->send_6xB4x46();
|
||||
@@ -1848,16 +1878,18 @@ void Server::handle_CAx0D_end_non_action_phase(shared_ptr<Client>, const string&
|
||||
throw runtime_error("invalid client ID");
|
||||
}
|
||||
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd_ack;
|
||||
out_cmd_ack.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd_ack.response_phase = 1;
|
||||
this->send(out_cmd_ack);
|
||||
if (!this->options.is_trial()) {
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd_ack;
|
||||
out_cmd_ack.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd_ack.response_phase = 1;
|
||||
this->send(out_cmd_ack);
|
||||
}
|
||||
|
||||
this->set_client_id_ready_to_advance_phase(in_cmd.client_id, static_cast<BattlePhase>(in_cmd.battle_phase.load()));
|
||||
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd_fin;
|
||||
out_cmd_fin.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd_fin.response_phase = 2;
|
||||
out_cmd_fin.response_phase = this->options.is_trial() ? 0 : 2;
|
||||
this->send(out_cmd_fin);
|
||||
}
|
||||
|
||||
@@ -1923,7 +1955,9 @@ void Server::handle_CAx0F_set_card_from_hand(shared_ptr<Client>, const string& d
|
||||
if (in_cmd.client_id >= 4) {
|
||||
error_code = -0x78;
|
||||
}
|
||||
if (error_code == 0) {
|
||||
|
||||
bool always_send_response = (error_code == 0);
|
||||
if (always_send_response) {
|
||||
this->ruler_server->error_code1 = 0;
|
||||
auto ps = this->player_states.at(in_cmd.client_id);
|
||||
if (!ps) {
|
||||
@@ -1935,12 +1969,14 @@ void Server::handle_CAx0F_set_card_from_hand(shared_ptr<Client>, const string& d
|
||||
this->ruler_server->error_code1 = error_code;
|
||||
}
|
||||
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd.error_code = this->options.is_trial() ? (was_set ? 0 : 1) : this->ruler_server->error_code1;
|
||||
this->send(out_cmd);
|
||||
if (!this->options.is_trial() || always_send_response) {
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd.error_code = this->options.is_trial() ? (was_set ? 0 : 1) : this->ruler_server->error_code1;
|
||||
this->send(out_cmd);
|
||||
}
|
||||
|
||||
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, out_cmd.error_code);
|
||||
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, this->ruler_server->error_code1);
|
||||
}
|
||||
|
||||
void Server::handle_CAx10_move_fc_to_location(shared_ptr<Client>, const string& data) {
|
||||
@@ -1961,6 +1997,7 @@ void Server::handle_CAx10_move_fc_to_location(shared_ptr<Client>, const string&
|
||||
if (in_cmd.client_id >= 4) {
|
||||
error_code = -0x78;
|
||||
}
|
||||
|
||||
if (error_code == 0) {
|
||||
auto ps = this->player_states.at(in_cmd.client_id);
|
||||
if (!ps) {
|
||||
@@ -1973,12 +2010,14 @@ void Server::handle_CAx10_move_fc_to_location(shared_ptr<Client>, const string&
|
||||
this->ruler_server->error_code2 = error_code;
|
||||
}
|
||||
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd.error_code = this->ruler_server->error_code2;
|
||||
this->send(out_cmd);
|
||||
if (!this->options.is_trial() || (this->ruler_server->error_code2 == 0)) {
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd.error_code = this->options.is_trial() ? 0 : this->ruler_server->error_code2;
|
||||
this->send(out_cmd);
|
||||
}
|
||||
|
||||
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, out_cmd.error_code);
|
||||
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, this->ruler_server->error_code2);
|
||||
}
|
||||
|
||||
void Server::handle_CAx11_enqueue_attack_or_defense(shared_ptr<Client>, const string& data) {
|
||||
@@ -2009,12 +2048,15 @@ void Server::handle_CAx11_enqueue_attack_or_defense(shared_ptr<Client>, const st
|
||||
this->ruler_server->error_code3 = error_code;
|
||||
}
|
||||
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd.error_code = this->ruler_server->error_code3;
|
||||
this->send(out_cmd);
|
||||
bool is_trial = this->options.is_trial();
|
||||
if (!is_trial || (error_code == 0)) {
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
out_cmd.error_code = is_trial ? !!this->ruler_server->error_code3 : this->ruler_server->error_code3;
|
||||
this->send(out_cmd);
|
||||
}
|
||||
|
||||
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, out_cmd.error_code);
|
||||
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, this->ruler_server->error_code3);
|
||||
}
|
||||
|
||||
void Server::handle_CAx12_end_attack_list(shared_ptr<Client>, const string& data) {
|
||||
@@ -2033,9 +2075,11 @@ void Server::handle_CAx12_end_attack_list(shared_ptr<Client>, const string& data
|
||||
this->end_attack_list_for_client(in_cmd.client_id);
|
||||
}
|
||||
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
this->send(out_cmd);
|
||||
if (!this->options.is_trial() || (error_code == 0)) {
|
||||
G_ActionResult_Ep3_6xB4x1E out_cmd;
|
||||
out_cmd.sequence_num = in_cmd.header.sequence_num;
|
||||
this->send(out_cmd);
|
||||
}
|
||||
|
||||
this->send_debug_message_if_error_code_nonzero(in_cmd.client_id, error_code);
|
||||
}
|
||||
@@ -2194,7 +2238,14 @@ void Server::handle_CAx1D_start_battle(shared_ptr<Client>, const string& data) {
|
||||
in_cmd.header.subsubcommand, "START BATTLE");
|
||||
|
||||
if (!this->battle_in_progress) {
|
||||
if (!this->update_registration_phase()) {
|
||||
bool is_trial = this->options.is_trial();
|
||||
|
||||
bool should_start = false;
|
||||
if (is_trial) {
|
||||
should_start = (this->registration_phase == RegistrationPhase::REGISTERED);
|
||||
} else if (this->update_registration_phase()) {
|
||||
should_start = true;
|
||||
} else {
|
||||
G_RejectBattleStartRequest_Ep3_6xB4x53 out_cmd;
|
||||
out_cmd.setup_phase = this->setup_phase;
|
||||
out_cmd.registration_phase = this->registration_phase;
|
||||
@@ -2206,7 +2257,9 @@ void Server::handle_CAx1D_start_battle(shared_ptr<Client>, const string& data) {
|
||||
this->presence_entries[z].clear();
|
||||
}
|
||||
this->battle_in_progress = false;
|
||||
} else {
|
||||
}
|
||||
|
||||
if (should_start) {
|
||||
auto l = this->lobby.lock();
|
||||
if (l) {
|
||||
if (l->battle_record) {
|
||||
@@ -2668,6 +2721,7 @@ void Server::unknown_8023EEF4() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_trial = this->options.is_trial();
|
||||
while (this->unknown_a14 < this->num_pending_attacks_with_cards) {
|
||||
auto card = this->attack_cards[this->unknown_a14];
|
||||
if (this->get_current_team_turn() == card->get_team_id()) {
|
||||
@@ -2675,7 +2729,11 @@ void Server::unknown_8023EEF4() {
|
||||
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);
|
||||
if (is_trial) {
|
||||
this->replace_targets_due_to_destruction_nte(&as);
|
||||
} else {
|
||||
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)) {
|
||||
@@ -2697,28 +2755,35 @@ void Server::unknown_8023EEF4() {
|
||||
G_SetActionState_Ep3_6xB4x29 cmd;
|
||||
cmd.unknown_a1 = this->unknown_a14;
|
||||
cmd.state = this->pending_attacks_with_cards[this->unknown_a14];
|
||||
this->replace_targets_due_to_destruction_or_conditions(&cmd.state);
|
||||
if (is_trial) {
|
||||
this->replace_targets_due_to_destruction_nte(&cmd.state);
|
||||
} else {
|
||||
this->replace_targets_due_to_destruction_or_conditions(&cmd.state);
|
||||
}
|
||||
ActionState as = cmd.state;
|
||||
this->send(cmd);
|
||||
|
||||
this->card_special->unknown_8024AAB8(as);
|
||||
this->attack_cards[this->unknown_a14]->compute_action_chain_results(1, 0);
|
||||
this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as);
|
||||
if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) {
|
||||
this->card_special->unknown_8024945C(this->attack_cards[this->unknown_a14], as);
|
||||
}
|
||||
this->attack_cards[this->unknown_a14]->compute_action_chain_results(1, 0);
|
||||
this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as);
|
||||
if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) {
|
||||
this->card_special->unknown_8024966C(this->attack_cards[this->unknown_a14], &as);
|
||||
}
|
||||
this->attack_cards[this->unknown_a14]->compute_action_chain_results(1, 0);
|
||||
this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as);
|
||||
this->attack_cards[this->unknown_a14]->send_6xB4x4E_4C_4D_if_needed();
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
auto ps = this->player_states[z];
|
||||
if (ps) {
|
||||
ps->send_set_card_updates();
|
||||
|
||||
if (!is_trial) {
|
||||
this->attack_cards[this->unknown_a14]->compute_action_chain_results(1, 0);
|
||||
this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as);
|
||||
if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) {
|
||||
this->card_special->unknown_8024945C(this->attack_cards[this->unknown_a14], as);
|
||||
}
|
||||
this->attack_cards[this->unknown_a14]->compute_action_chain_results(1, 0);
|
||||
this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as);
|
||||
if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) {
|
||||
this->card_special->unknown_8024966C(this->attack_cards[this->unknown_a14], &as);
|
||||
}
|
||||
this->attack_cards[this->unknown_a14]->compute_action_chain_results(1, 0);
|
||||
this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as);
|
||||
this->attack_cards[this->unknown_a14]->send_6xB4x4E_4C_4D_if_needed();
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
auto ps = this->player_states[z];
|
||||
if (ps) {
|
||||
ps->send_set_card_updates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2733,8 +2798,11 @@ void Server::unknown_8023EEF4() {
|
||||
this->update_battle_state_flags_and_send_6xB4x03_if_needed();
|
||||
this->send_6xB4x02_for_all_players_if_needed();
|
||||
}
|
||||
this->update_battle_state_flags_and_send_6xB4x03_if_needed();
|
||||
this->send_6xB4x02_for_all_players_if_needed();
|
||||
|
||||
if (!is_trial) {
|
||||
this->update_battle_state_flags_and_send_6xB4x03_if_needed();
|
||||
this->send_6xB4x02_for_all_players_if_needed();
|
||||
}
|
||||
}
|
||||
|
||||
void Server::execute_bomb_assist_effect() {
|
||||
@@ -2771,10 +2839,58 @@ void Server::execute_bomb_assist_effect() {
|
||||
}
|
||||
}
|
||||
|
||||
void Server::replace_targets_due_to_destruction_or_conditions(
|
||||
ActionState* as) {
|
||||
auto attacker_card = this->card_for_set_card_ref(
|
||||
this->send_6xB4x06_if_card_ref_invalid(as->attacker_card_ref, 3));
|
||||
void Server::replace_targets_due_to_destruction_nte(ActionState* as) {
|
||||
auto attacker_card = this->card_for_set_card_ref(this->send_6xB4x06_if_card_ref_invalid(as->attacker_card_ref, 3));
|
||||
|
||||
for (size_t z = 0; z < 0x24; z++) {
|
||||
auto target_card = this->card_for_set_card_ref(as->target_card_refs[z]);
|
||||
if (!target_card) {
|
||||
break;
|
||||
}
|
||||
if ((target_card->card_flags & 2) ||
|
||||
(target_card->get_definition()->def.type != CardType::ITEM) ||
|
||||
attacker_card->action_chain.check_flag(0x02)) {
|
||||
continue;
|
||||
}
|
||||
auto ps = target_card->player_state();
|
||||
shared_ptr<Card> found_guard_item;
|
||||
for (size_t z = 0; z < 8; z++) {
|
||||
auto set_card = ps->get_set_card(z);
|
||||
if (set_card &&
|
||||
(set_card != target_card) &&
|
||||
!(set_card->card_flags & 2) &&
|
||||
set_card->is_guard_item()) {
|
||||
found_guard_item = set_card;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto replaced_target = found_guard_item;
|
||||
if (!found_guard_item) {
|
||||
for (size_t z = 0; z < 8; z++) {
|
||||
auto set_card = ps->get_set_card(z);
|
||||
if (set_card && (set_card != target_card) && !(set_card->card_flags & 2)) {
|
||||
replaced_target = set_card;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
as->target_card_refs[z] = replaced_target ? replaced_target->get_card_ref() : ps->get_sc_card()->get_card_ref();
|
||||
}
|
||||
|
||||
size_t write_offset = 0;
|
||||
for (size_t z = 0; z < as->target_card_refs.size(); z++) {
|
||||
if (as->target_card_refs[z] != 0xFFFF) {
|
||||
if (z != write_offset) {
|
||||
as->target_card_refs[write_offset] = as->target_card_refs[z];
|
||||
}
|
||||
write_offset++;
|
||||
}
|
||||
}
|
||||
as->target_card_refs.clear_after(write_offset, 0xFFFF);
|
||||
}
|
||||
|
||||
void Server::replace_targets_due_to_destruction_or_conditions(ActionState* as) {
|
||||
auto attacker_card = this->card_for_set_card_ref(this->send_6xB4x06_if_card_ref_invalid(as->attacker_card_ref, 3));
|
||||
if (!attacker_card) {
|
||||
as->target_card_refs[0] = 0xFFFF;
|
||||
return;
|
||||
|
||||
@@ -217,6 +217,7 @@ public:
|
||||
uint32_t send_6xB4x06_if_card_ref_invalid(uint16_t card_ref, int16_t negative_value);
|
||||
void unknown_8023EEF4();
|
||||
void execute_bomb_assist_effect();
|
||||
void replace_targets_due_to_destruction_nte(ActionState* as);
|
||||
void replace_targets_due_to_destruction_or_conditions(ActionState* as);
|
||||
bool any_target_exists_for_attack(const ActionState& as);
|
||||
uint8_t get_current_team_turn2() const;
|
||||
@@ -312,6 +313,8 @@ public:
|
||||
parray<uint8_t, 5> num_trap_tiles_of_type;
|
||||
parray<uint8_t, 5> chosen_trap_tile_index_of_type;
|
||||
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];
|
||||
parray<uint8_t, 4> has_done_pb;
|
||||
parray<parray<uint8_t, 4>, 4> has_done_pb_with_client;
|
||||
|
||||
@@ -78,7 +78,7 @@ static shared_ptr<const Menu> proxy_options_menu_for_client(shared_ptr<const Cli
|
||||
add_flag_option(ProxyOptionsMenuItemID::EP3_INFINITE_TIME, Client::Flag::PROXY_EP3_INFINITE_TIME_ENABLED,
|
||||
"Infinite time", "Disable overall and\nper-phase time limits\nin battle");
|
||||
add_flag_option(ProxyOptionsMenuItemID::EP3_UNMASK_WHISPERS, Client::Flag::PROXY_EP3_UNMASK_WHISPERS,
|
||||
"Unmask whispers", "Show contents of\nwhisper messages even\nif they are not for\nyou");
|
||||
"Unmask whispers", "Show contents of\nwhisper messages\neven if they are not\nfor you");
|
||||
}
|
||||
}
|
||||
if (s->proxy_allow_save_files) {
|
||||
|
||||
Reference in New Issue
Block a user