Skip to content

Commit

Permalink
Hitting self in confusion is no longer a normal move.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidstone committed Feb 4, 2024
1 parent 2746937 commit 9028b9f
Show file tree
Hide file tree
Showing 31 changed files with 354 additions and 166 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ target_sources(tm_common PUBLIC
source/tm/move/executed_move.cpp
source/tm/move/future_selection.cpp
source/tm/move/healing_move_fails_in_generation_1.cpp
source/tm/move/hit_self.cpp
source/tm/move/initial_move.cpp
source/tm/move/irrelevant_action.cpp
source/tm/move/is_blocked_by_gravity.cpp
Expand Down Expand Up @@ -137,6 +138,8 @@ target_sources(tm_common PUBLIC
source/tm/pokemon/active_pokemon.cpp
source/tm/pokemon/active_status.cpp
source/tm/pokemon/any_pokemon.cpp
source/tm/pokemon/applied_damage.cpp
source/tm/pokemon/can_be_koed.cpp
source/tm/pokemon/change_hp.cpp
source/tm/pokemon/confusion.cpp
source/tm/pokemon/disable.cpp
Expand Down
1 change: 0 additions & 1 deletion source/tm/ability_blocks_move.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export constexpr auto ability_blocks_move(Generation const generation, Ability c
return generation >= Generation::five;
case MoveName::Fire_Fang:
return generation != Generation::four;
case MoveName::Hit_Self:
case MoveName::Struggle:
return false;
default:
Expand Down
43 changes: 20 additions & 23 deletions source/tm/battle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import tm.move.call_move;
import tm.move.causes_recoil;
import tm.move.do_switch;
import tm.move.future_selection;
import tm.move.hit_self;
import tm.move.irrelevant_action;
import tm.move.known_move;
import tm.move.move;
Expand Down Expand Up @@ -62,24 +63,6 @@ namespace technicalmachine {
using namespace bounded::literal;
using namespace std::string_view_literals;

constexpr auto get_actual_damage(
bool const ai_is_user,
MoveName const executed,
Damage const damage,
auto const user_pokemon,
auto const other_pokemon
) -> ActualDamage {
auto const damaged_is_user = executed == MoveName::Hit_Self;
auto const damaged_is_ai = !ai_is_user xor damaged_is_user;
auto const old_hp = damaged_is_user ? user_pokemon.hp() : other_pokemon.hp();
return visible_damage_to_actual_damage(
damage,
damaged_is_ai,
old_hp,
other_pokemon.substitute()
);
}

constexpr auto move_should_have_recoil(
Generation const generation,
Used const move,
Expand Down Expand Up @@ -276,12 +259,11 @@ struct Battle {
user_pokemon.set_base_ability(*recoil_ability);
}

auto const damage = get_actual_damage(
ai_is_user,
move.executed,
auto const damage = visible_damage_to_actual_damage(
move.damage,
user_pokemon,
other_pokemon
!ai_is_user,
other_pokemon.hp(),
other_pokemon.substitute()
);
call_move(
user_team,
Expand All @@ -302,6 +284,21 @@ struct Battle {
});
}

auto hit_self_in_confusion(bool const ai_is_user, VisibleHP const damage) & -> void {
apply_to_teams(ai_is_user, [&](auto & user_team, auto const &) {
auto const actual_damage = visible_damage_to_actual_damage(
damage,
ai_is_user,
user_team.pokemon().hp()
);
hit_self(
user_team,
actual_damage.value,
environment()
);
});
}

auto end_turn(bool const ai_went_first, EndOfTurnFlags const first_flags, EndOfTurnFlags const last_flags) & -> void {
apply_to_teams(ai_went_first, [&](auto & first, auto & last) {
end_of_turn(first, first_flags, last, last_flags, m_environment);
Expand Down
4 changes: 0 additions & 4 deletions source/tm/can_execute_move.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ export constexpr auto can_attempt_move_execution(any_active_pokemon auto const u
}

export constexpr auto can_execute_move(any_active_pokemon auto const user, Move const move, Environment const environment, bool const is_recharging, bool const is_fully_paralyzed) -> bool {
if (move.name() == MoveName::Hit_Self) {
BOUNDED_ASSERT(!is_recharging);
return true;
}
return !user.flinched() and !blocks_selection_and_execution(user, move.name(), environment) and !is_fully_paralyzed and !is_recharging;
}

Expand Down
1 change: 1 addition & 0 deletions source/tm/clients/client_battle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export struct ClientBattle {
virtual auto end_turn(bool ai_went_first, EndOfTurnFlags first_flags, EndOfTurnFlags last_flags) & -> void = 0;
virtual auto use_move(bool ai_is_user, MoveResult, bool user_status_was_cleared) & -> void = 0;
virtual auto use_switch(bool ai_is_user, Switch) & -> void = 0;
virtual auto hit_self_in_confusion(bool ai_is_user, VisibleHP const damage) & -> void = 0;

// TODO: Delete this function
virtual auto cures_target_status(bool is_ai, MoveName) const -> bool = 0;
Expand Down
4 changes: 4 additions & 0 deletions source/tm/clients/make_client_battle_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ struct ClientBattleImpl final : ClientBattle {
m_battle.use_switch(ai_is_user, switch_);
}

auto hit_self_in_confusion(bool ai_is_user, VisibleHP const damage) & -> void final {
m_battle.hit_self_in_confusion(ai_is_user, damage);
}

auto end_turn(bool const ai_went_first, EndOfTurnFlags const first_flags, EndOfTurnFlags const last_flags) & -> void final {
m_battle.end_turn(ai_went_first, first_flags, last_flags);
}
Expand Down
13 changes: 10 additions & 3 deletions source/tm/clients/pokemon_online/conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1887,14 +1887,21 @@ export constexpr auto item_to_id(Item const item) -> ItemID {
}
}

export using MoveID = bounded::integer<1, bounded::normalize<bounded::constant<numeric_traits::max_value<MoveName>> - bounded::integer(MoveName::Regular_Begin) + 1_bi>>;
export using MoveID = bounded::integer<
1,
bounded::normalize<
bounded::constant<numeric_traits::max_value<MoveName>> -
bounded::constant<numeric_traits::min_value<MoveName>> +
1_bi
>
>;

export constexpr auto id_to_move(MoveID const id) -> MoveName {
return static_cast<MoveName>(id + bounded::integer(MoveName::Regular_Begin) - 1_bi);
return static_cast<MoveName>(id + bounded::constant<numeric_traits::min_value<MoveName>> - 1_bi);
}

export constexpr auto move_to_id(MoveName const move) -> MoveID {
auto const move_id = bounded::integer(move) - bounded::integer(MoveName::Regular_Begin) + 1_bi;
auto const move_id = bounded::integer(move) - bounded::constant<numeric_traits::min_value<MoveName>> + 1_bi;
BOUNDED_ASSERT(move_id > 0_bi);
return ::bounded::assume_in_range<MoveID>(move_id);
}
Expand Down
7 changes: 7 additions & 0 deletions source/tm/clients/pokemon_showdown/battle_message_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ struct SwitchState {
VisibleHP hp;
};

struct HitSelf {
Party party;
StatusName status;
VisibleHP hp;
};

export struct BattleMessageHandler {
BattleMessageHandler(Party party, GenerationGeneric<Teams> teams);

Expand All @@ -61,6 +67,7 @@ export struct BattleMessageHandler {
private:
auto use_move(MoveState) -> void;
auto use_switch(SwitchState) -> void;
auto hit_self_in_confusion(HitSelf) -> void;
auto handle_switch_message(SwitchMessage) -> TeamIndex;

auto handle_end_of_turn(EndOfTurnState) -> void;
Expand Down
105 changes: 57 additions & 48 deletions source/tm/clients/pokemon_showdown/battle_message_handler_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,24 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
using ActionBuilder = tv::variant<
Nothing,
MoveStateBuilder,
Switches
Switches,
HitSelf
>;
auto action_builder = ActionBuilder(Nothing());
auto end_of_turn_state = EndOfTurnStateBuilder();

auto set_value_on_pokemon = [&](Party const party, auto const value) -> void {
auto const for_ai = party == m_party;
auto const index = tv::visit(action_builder, tv::overload(
[](Nothing) -> tv::optional<TeamIndex> {
return tv::none;
},
[](MoveStateBuilder const & move_builder) -> tv::optional<TeamIndex> {
return move_builder.phaze_index();
},
[=](Switches const switches) -> tv::optional<TeamIndex> {
auto const ptr = find_switch(switches, party);
return BOUNDED_CONDITIONAL(ptr, ptr->index, tv::none);
},
[](auto) -> tv::optional<TeamIndex> {
return tv::none;
}
));
if (index) {
Expand All @@ -96,23 +97,25 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
}
};

auto use_previous_move = [&](MoveStateBuilder & builder) -> void {
if (auto const move_state = builder.complete()) {
use_move(*move_state);
}
};
auto use_previous_switches = [&](Switches const switches) {
// TODO: Timing for both sides replacing a fainting Pokemon
for (auto const switch_ : containers::reversed(switches)) {
use_switch(switch_);
auto const do_action = tv::overload(
[](Nothing) {},
[&](MoveStateBuilder & builder) -> void {
if (auto const move_state = builder.complete()) {
use_move(*move_state);
}
},
[&](Switches const switches) {
// TODO: Timing for both sides replacing a fainting Pokemon
for (auto const switch_ : containers::reversed(switches)) {
use_switch(switch_);
}
},
[&](HitSelf const hit_self) {
hit_self_in_confusion(hit_self);
}
};
);
auto use_previous_action = [&] -> void {
tv::visit(action_builder, tv::overload(
[](Nothing) {},
use_previous_move,
use_previous_switches
));
tv::visit(action_builder, do_action);
action_builder = Nothing();
};

Expand Down Expand Up @@ -226,9 +229,6 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
}
};
tv::visit(action_builder, tv::overload(
[&](Nothing) {
natural_status_recovery();
},
[&](MoveStateBuilder & builder) {
auto const move_name = builder.executed_move();
auto const move_cured_status = move_name and (
Expand All @@ -238,12 +238,12 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
if (move_cured_status) {
builder.status_from_move(party, StatusName::clear);
} else {
use_previous_move(builder);
do_action(builder);
natural_status_recovery();
}
},
[&](Switches & switches) {
use_previous_switches(switches);
[&](auto const previous) {
do_action(previous);
natural_status_recovery();
}
));
Expand All @@ -267,17 +267,16 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
},
[&](HitSelfMessage const message) {
use_previous_action();
auto const party = message.party;
// TODO: You cannot select Hit Self, you just execute it. This
// matters for things like priority or determining whether
// Sucker Punch succeeds. As a workaround for now, say the user
// selected Struggle.
auto & builder = make_move_builder();
builder.use_move(party, MoveName::Struggle);
builder.use_move(party, MoveName::Hit_Self);
builder.damage(party, message.hp);
builder.set_expected(party, message.status);
builder.set_expected(party, message.hp);
tv::visit(action_builder, tv::overload(
[&](Nothing) {
action_builder.emplace([&] {
return HitSelf(message.party, message.status, message.hp);
});
},
[](auto) {
throw std::runtime_error("Tried to hit self and take another action");
}
));
},
[&](RecoilMessage const message) {
tv::visit(action_builder, tv::overload(
Expand Down Expand Up @@ -315,6 +314,9 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
}
ptr->status = message.status;
ptr->hp = message.hp;
},
[](HitSelf) {
throw std::runtime_error("Got multiple HP messages for hitting self in confusion");
}
));
},
Expand All @@ -330,18 +332,15 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
},
[&](SwitchMessage const message) {
auto & switches = tv::visit(action_builder, tv::overload(
[&](Nothing) -> Switches & {
return action_builder.emplace(bounded::construct<Switches>);
},
[&](MoveStateBuilder & move_builder) -> Switches & {
use_previous_move(move_builder);
return action_builder.emplace(bounded::construct<Switches>);
},
[&](Switches & s) -> Switches & {
if (find_switch(s, message.party)) {
throw std::runtime_error("Tried to switch in the same Pokemon twice");
}
return s;
},
[&](auto & previous) -> Switches & {
do_action(previous);
return action_builder.emplace(bounded::construct<Switches>);
}
));
auto const index = handle_switch_message(message);
Expand Down Expand Up @@ -373,6 +372,7 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
[&](StartConfusionMessage const message) {
tv::visit(action_builder, tv::overload(
[&](MoveStateBuilder & builder) {
// TODO: Rampage moves?
builder.confuse(other(message.party));
},
[](auto) {
Expand All @@ -382,9 +382,6 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
},
[&](MoveStatus const message) {
tv::visit(action_builder, tv::overload(
[](Nothing) {
throw std::runtime_error("Move status without a move");
},
[&](MoveStateBuilder & builder) {
builder.status_from_move(message.party, message.status);
},
Expand All @@ -400,6 +397,9 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
throw std::runtime_error("Toxic poison changed to poison from switching in later generations");
}
ptr->status = message.status;
},
[](auto) {
throw std::runtime_error("Move status without a move");
}
));
},
Expand Down Expand Up @@ -428,15 +428,18 @@ auto BattleMessageHandler::handle_message(std::span<ParsedMessage const> const b
throw std::runtime_error("End of turn did not complete");
}
},
[&](MoveStateBuilder) {
[](MoveStateBuilder) {
throw std::runtime_error("Should not have a move state builder at the start of a turn");
},
[&](Switches const switches) {
use_previous_switches(switches);
do_action(switches);
if (m_client_battle->is_end_of_turn()) {
throw std::runtime_error("Should not have pending switches before we handle the end of turn");
}
action_builder = Nothing();
},
[](HitSelf) {
throw std::runtime_error("Should not be hitting self at the start of a turn");
}
));
action_builder = Nothing();
Expand Down Expand Up @@ -494,6 +497,12 @@ auto BattleMessageHandler::use_switch(SwitchState const data) -> void {
try_correct_hp_and_status(data_is_for_ai, data.hp, data.status);
}

auto BattleMessageHandler::hit_self_in_confusion(HitSelf const data) -> void {
auto const data_is_for_ai = data.party == m_party;
m_client_battle->hit_self_in_confusion(data_is_for_ai, data.hp);
try_correct_hp_and_status(data_is_for_ai, data.hp, data.status);
}

auto BattleMessageHandler::handle_switch_message(SwitchMessage const message) -> TeamIndex {
if (message.party == m_party) {
auto const index = m_client_battle->ai_has(
Expand Down
Loading

0 comments on commit 9028b9f

Please sign in to comment.