Skip to content

Commit

Permalink
Implement event-hooked enchantment
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Shih committed Apr 17, 2017
1 parent b1e2108 commit 9e905e2
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 16 deletions.
22 changes: 19 additions & 3 deletions include/Cards/Expert1/Neutral.h
Original file line number Diff line number Diff line change
Expand Up @@ -1071,8 +1071,21 @@ namespace Cards
});
}
};
struct Card_DREAM_05e : public Enchantment<Card_DREAM_05e, Attack<5>, MaxHP<5>> {
// TODO: destroy at the start of the caster's turn
struct Card_DREAM_05e : public EventHookedEnchantment<Card_DREAM_05e, Attack<5>, MaxHP<5>> {
static void RegisterEvent(FlowControl::Manipulate & manipulate, state::CardRef card_ref,
FlowControl::enchantment::Enchantments::IdentifierType id,
FlowControl::enchantment::Enchantments::EventHookedEnchantment::AuxData & aux_data)
{
manipulate.AddEvent<state::Events::EventTypes::OnTurnStart>([card_ref, id, aux_data](state::Events::EventTypes::OnTurnStart::Context context) {
if (context.manipulate_.GetCard(card_ref).GetZone() != state::kCardZonePlay) return false;
if (!context.manipulate_.GetCard(card_ref).GetEnchantmentHandler().Exists(
FlowControl::enchantment::TieredEnchantments::IdentifierType{ Card_DREAM_05e::tier, id })) return false;

if (context.manipulate_.Board().GetCurrentPlayerId() != aux_data.player) return true;
context.manipulate_.OnBoardMinion(card_ref).Destroy();
return true;
});
}
};
struct Card_DREAM_05 : public SpellCardBase<Card_DREAM_05> {
Card_DREAM_05() {
Expand All @@ -1081,7 +1094,10 @@ namespace Cards
return TargetsGenerator(context.player_).Minion().SpellTargetable().GetInfo();
});
onplay_handler.SetOnPlayCallback([](FlowControl::onplay::context::OnPlay const& context) {
context.manipulate_.OnBoardMinion(context.GetTarget()).Enchant().Add<Card_DREAM_05e>();
FlowControl::enchantment::Enchantments::EventHookedEnchantment::AuxData aux_data;
aux_data.player = context.player_;
context.manipulate_.OnBoardMinion(context.GetTarget()).Enchant().AddEventHooked(
Card_DREAM_05e(), aux_data);
});
}
};
Expand Down
33 changes: 32 additions & 1 deletion include/Cards/framework/EnchantmentCardBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,39 @@ namespace Cards
template <typename... Ts>
struct EnchantmentForThisTurn : public Enchantment<Ts...>
{
EnchantmentForThisTurn() {
EnchantmentForThisTurn() : Enchantment() {
valid_this_turn = true;
}
};

template <
typename T,
typename Enchant1 = NullEnchant,
typename Enchant2 = NullEnchant,
typename Enchant3 = NullEnchant,
typename Enchant4 = NullEnchant,
typename Enchant5 = NullEnchant
>
struct EventHookedEnchantment
{
static constexpr EnchantmentTiers tier = EnchantmentTiers::kEnchantmentTier1;

EventHookedEnchantment() {
// TODO: use SFINAE to make sure caller correctly pass itself as T
// T::required_tier must NOT exist

apply_functor = [](state::Cards::EnchantableStates & stats) {
Enchant1::Apply(stats);
Enchant2::Apply(stats);
Enchant3::Apply(stats);
Enchant4::Apply(stats);
Enchant5::Apply(stats);
};

register_functor = T::RegisterEvent;
}

FlowControl::enchantment::Enchantments::ApplyFunctor apply_functor;
FlowControl::enchantment::Enchantments::EventHookedEnchantment::RegisterEventFunctor register_functor;
};
}
6 changes: 6 additions & 0 deletions include/FlowControl/Manipulators/Helpers/EnchantmentHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ namespace FlowControl
return state_.GetMutableCard(card_ref_).GetMutableEnchantmentHandler().PushBackNormalEnchantment(state_, T<20>());
}

template <typename EnchantmentType>
auto AddEventHooked(EnchantmentType&& enchant, enchantment::Enchantments::EventHookedEnchantment::AuxData const& aux_data) {
return state_.GetMutableCard(card_ref_).GetMutableEnchantmentHandler().PushBackEventHookedEnchantment(
FlowControl::Manipulate(state_, flow_context_), card_ref_, std::forward<EnchantmentType>(enchant), aux_data);
}

void SetHealthToMaxHP();

template <typename T> auto AddAuraEnchantment(T&& enchantment) {
Expand Down
21 changes: 13 additions & 8 deletions include/FlowControl/enchantment/Enchantments.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,22 @@ namespace FlowControl
bool Apply(state::State const& state, state::Cards::EnchantableStates & stats) const;
};
struct EventHookedEnchantment {
bool Apply(state::State const& state, state::Cards::EnchantableStates & stats) const { apply_functor(stats); return true; }
ApplyFunctor apply_functor;

struct AuxData {
AuxData() : valid(false) {}
bool valid;
} aux_data;
state::PlayerIdentifier player;
};

typedef void(*RegisterEventFunctor)(FlowControl::Manipulate &, state::CardRef, IdentifierType, AuxData &);

bool Apply(state::State const& state, state::Cards::EnchantableStates & stats) const { apply_functor(stats); return true; }
void RegisterEvent(FlowControl::Manipulate & manipulate, state::CardRef card_ref, IdentifierType id, AuxData & aux_data) const {
register_functor(manipulate, card_ref, id, aux_data);
}

ApplyFunctor apply_functor;
RegisterEventFunctor register_functor;
AuxData aux_data;
};
using EnchantmentType = std::variant<NormalEnchantment, AuraEnchantment, EventHookedEnchantment>;
typedef Utils::CloneableContainers::RemovableVector<EnchantmentType> ContainerType;
Expand Down Expand Up @@ -85,15 +88,17 @@ namespace FlowControl
}

template <typename EnchantmentType>
typename IdentifierType PushBackEventHookedEnchantment(FlowControl::Manipulate & manipulate, state::CardRef card_ref)
typename IdentifierType PushBackEventHookedEnchantment(
FlowControl::Manipulate & manipulate, state::CardRef card_ref,
EnchantmentType&& item, enchantment::Enchantments::EventHookedEnchantment::AuxData const& aux_data)
{
EnchantmentType item;
need_update_ = true;
assert(item.apply_functor);
assert(item.register_functor);
IdentifierType id = enchantments_.PushBack(EventHookedEnchantment{ item.apply_functor, item.register_functor });
IdentifierType id = enchantments_.PushBack(EventHookedEnchantment{ item.apply_functor, item.register_functor, aux_data });
item.register_functor(manipulate, card_ref, id,
std::get<EventHookedEnchantment>(enchantments_.Get(id)).aux_data);
std::get<EventHookedEnchantment>(*enchantments_.Get(id)).aux_data);
return id;
}

void Remove(IdentifierType id)
Expand Down
8 changes: 6 additions & 2 deletions include/FlowControl/enchantment/Handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ namespace FlowControl
template <typename EnchantmentType> auto PushBackAuraEnchantment(state::State const& state, EnchantmentType&& enchantment) {
return enchantments.PushBackAuraEnchantment<EnchantmentType>(std::forward<EnchantmentType>(enchantment));
}
template <typename EnchantmentType> auto PushBackEventHookedEnchantment(FlowControl::Manipulate & manipulate, state::CardRef card_ref) {
return enchantments.PushBackEventHookedEnchantment<EnchantmentType>(manipulate, card_ref);
template <typename EnchantmentType> auto PushBackEventHookedEnchantment(
FlowControl::Manipulate & manipulate, state::CardRef card_ref,
EnchantmentType&& enchant, enchantment::Enchantments::EventHookedEnchantment::AuxData const& aux_data)
{
return enchantments.PushBackEventHookedEnchantment(
manipulate, card_ref, std::forward<EnchantmentType>(enchant), aux_data);
}

bool Exists(TieredEnchantments::IdentifierType id) const { return enchantments.Exists(id); }
Expand Down
8 changes: 6 additions & 2 deletions include/FlowControl/enchantment/TieredEnchantments.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ namespace FlowControl
}

template <typename EnchantmentType>
void PushBackEventHookedEnchantment(FlowControl::Manipulate & manipulate, state::CardRef card_ref)
typename IdentifierType PushBackEventHookedEnchantment(FlowControl::Manipulate & manipulate, state::CardRef card_ref,
EnchantmentType&& enchant, enchantment::Enchantments::EventHookedEnchantment::AuxData const& aux_data)
{
GetEnchantments<EnchantmentType::normal_tier>().PushBackEventHookedEnchantment<EnchantmentType>(manipulate, card_ref);
constexpr EnchantmentTiers tier = EnchantmentType::tier;
Enchantments::IdentifierType id = GetEnchantments<tier>()
.PushBackEventHookedEnchantment(manipulate, card_ref, std::forward<EnchantmentType>(enchant), aux_data);
return IdentifierType{ tier, id };
}

void Remove(IdentifierType id)
Expand Down
1 change: 1 addition & 0 deletions include/state/Cards/Card.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ namespace state

void SetJustPlayedFlag(bool v) { data_.just_played = v; }

auto const& GetEnchantmentHandler() const { return data_.enchantment_handler; }
auto& GetMutableEnchantmentHandler() { return data_.enchantment_handler; }
auto& GetMutableDeathrattleHandler() { return data_.deathrattle_handler; }

Expand Down
90 changes: 90 additions & 0 deletions vs_projects/flow_controller/test4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4561,4 +4561,94 @@ void test4()
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 1);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);
}();

[state, flow_context, &parameter_getter, &random]() mutable {
FlowControl::FlowController controller(state, flow_context);

AddHandCard(Cards::ID_EX1_572, flow_context, state, state::PlayerIdentifier::First());
parameter_getter.next_minion_put_location = 0;
parameter_getter.next_specified_target_count = 0;
parameter_getter.next_specified_target_idx = 0;
if (controller.PlayCard(1) != FlowControl::kResultNotDetermined) assert(false);
CheckHero(state, state::PlayerIdentifier::First(), 30, 0, 0);
CheckHero(state, state::PlayerIdentifier::Second(), 30, 0, 0);
CheckCrystals(state, state::PlayerIdentifier::First(), { 1, 10 });
CheckCrystals(state, state::PlayerIdentifier::Second(), { 10, 10 });
CheckMinions(state, state::PlayerIdentifier::First(), { { 4,12,12 } });
CheckMinions(state, state::PlayerIdentifier::Second(), {});
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 1);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);

random.called_times = 0;
random.next_rand = 4;
if (controller.EndTurn() != FlowControl::kResultNotDetermined) assert(false);
assert(random.called_times == 1);
CheckHero(state, state::PlayerIdentifier::First(), 30, 0, 0);
CheckHero(state, state::PlayerIdentifier::Second(), 29, 0, 0);
CheckCrystals(state, state::PlayerIdentifier::First(), { 1, 10 });
CheckCrystals(state, state::PlayerIdentifier::Second(), { 10, 10 });
CheckMinions(state, state::PlayerIdentifier::First(), { { 4,12,12 } });
CheckMinions(state, state::PlayerIdentifier::Second(), {});
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 2);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);

random.called_times = 0;
if (controller.EndTurn() != FlowControl::kResultNotDetermined) assert(false);
assert(random.called_times == 0);
CheckHero(state, state::PlayerIdentifier::First(), 29, 0, 0);
CheckHero(state, state::PlayerIdentifier::Second(), 29, 0, 0);
CheckCrystals(state, state::PlayerIdentifier::First(), { 10, 10 });
CheckCrystals(state, state::PlayerIdentifier::Second(), { 10, 10 });
CheckMinions(state, state::PlayerIdentifier::First(), { { 4,12,12 } });
CheckMinions(state, state::PlayerIdentifier::Second(), {});
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 2);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);

parameter_getter.next_specified_target_count = 1;
parameter_getter.next_specified_target_idx = 0;
if (controller.PlayCard(1) != FlowControl::kResultNotDetermined) assert(false);
CheckHero(state, state::PlayerIdentifier::First(), 29, 0, 0);
CheckHero(state, state::PlayerIdentifier::Second(), 29, 0, 0);
CheckCrystals(state, state::PlayerIdentifier::First(), { 10, 10 });
CheckCrystals(state, state::PlayerIdentifier::Second(), { 10, 10 });
CheckMinions(state, state::PlayerIdentifier::First(), { { 9,17,17 } });
CheckMinions(state, state::PlayerIdentifier::Second(), {});
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 1);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);

random.called_times = 0;
random.next_rand = 4;
if (controller.EndTurn() != FlowControl::kResultNotDetermined) assert(false);
assert(random.called_times == 1);
CheckHero(state, state::PlayerIdentifier::First(), 29, 0, 0);
CheckHero(state, state::PlayerIdentifier::Second(), 27, 0, 0);
CheckCrystals(state, state::PlayerIdentifier::First(), { 10, 10 });
CheckCrystals(state, state::PlayerIdentifier::Second(), { 10, 10 });
CheckMinions(state, state::PlayerIdentifier::First(), { { 9,17,17 } });
CheckMinions(state, state::PlayerIdentifier::Second(), {});
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 2);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);

if (controller.EndTurn() != FlowControl::kResultNotDetermined) assert(false);
CheckHero(state, state::PlayerIdentifier::First(), 27, 0, 0);
CheckHero(state, state::PlayerIdentifier::Second(), 27, 0, 0);
CheckCrystals(state, state::PlayerIdentifier::First(), { 10, 10 });
CheckCrystals(state, state::PlayerIdentifier::Second(), { 10, 10 });
CheckMinions(state, state::PlayerIdentifier::First(), { });
CheckMinions(state, state::PlayerIdentifier::Second(), {});
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 2);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);

random.called_times = 0;
if (controller.EndTurn() != FlowControl::kResultNotDetermined) assert(false);
assert(random.called_times == 0);
CheckHero(state, state::PlayerIdentifier::First(), 27, 0, 0);
CheckHero(state, state::PlayerIdentifier::Second(), 24, 0, 0);
CheckCrystals(state, state::PlayerIdentifier::First(), { 10, 10 });
CheckCrystals(state, state::PlayerIdentifier::Second(), { 10, 10 });
CheckMinions(state, state::PlayerIdentifier::First(), {});
CheckMinions(state, state::PlayerIdentifier::Second(), {});
assert(state.GetBoard().Get(state::PlayerIdentifier::First()).hand_.Size() == 2);
assert(state.GetBoard().Get(state::PlayerIdentifier::Second()).hand_.Size() == 1);
}();
}

0 comments on commit 9e905e2

Please sign in to comment.