Skip to main content

Furfur

The Liar

Unit #120

HPPWRCPSpeedRangeTier
12 (+6)3 (+1)3 (+2)FastAnyC

Abilities

Game Within a Game — 0 AP

q: Game Within a Game: 0 AP - b, 1x: Play the Question Familiar in Any Lane. Then, place either a Truth or Lie card facedown underneath Question. The unplayed card cannot be seen by your opponent.
Engine Implementation
def _furfur_game_within_a_game(
state: GameState, demon: DemonInstance, targets, choices, rng
) -> GameState:
"""#120 Furfur [0] — Game Within a Game.

Deploy the Question familiar to target lane, then place Truth or Lie card
facedown. If is_lie=True in choices, apply status "question_is_lie"=1 to
the Question familiar so the idx=1 trigger can detect it.
"""
import copy as _cp_gwig
from engine.status_effects import apply_status as _ap_st_gwig
from engine.operations import deploy_familiar as _deploy_fam_gwig

question_uid = "120_3"
target_lane = int(choices.get("lane", 0)) % 3 if choices else 0
is_lie = bool(choices.get("is_lie", False)) if choices else False

# Try to deploy Question familiar (requires it in owner's familiar_deck)
owner_player = state.players[demon.owner]
if question_uid in owner_player.familiar_deck:
state = _deploy_fam_gwig(state, demon.owner, question_uid, target_lane)

# Find the Question familiar on field (just deployed or already there)
if is_lie:
question_demon = next(
(d for d in state.demons
if d.unit_id == question_uid and d.owner == demon.owner
and not d.fatally_wounded),
None,
)
if question_demon is not None:
state = _ap_st_gwig(state, demon, question_demon, "question_is_lie", 1)

return state

register_ability("120", 0, _furfur_game_within_a_game)

Passive

d: At the end of your opponent's Main Phases, you may reveal the card under Question. Afterwards, if it was Lie, Furfur performs up to 2 actions with -2 AP Cost and ignoring a and b costs.
Engine Implementation
def _furfur_lie_payoff_trigger(
state: GameState, event: GameEvent, demon: DemonInstance, depth: int
) -> GameState | None:
"""#120 Furfur idx=1 — At end of opponent's Main Phase, reveal Question.
If Lie: Furfur performs up to 2 actions with -2 AP Cost ignoring exhaust/ready.

Fires on MAIN_PHASE_END. Only fires if event.side is the OPPONENT of
Furfur's owner (i.e., this phase ending is the opponent's phase).

Checks for Question familiar (#120_3) on field owned by Furfur's owner.
If Question has a "question_is_lie" status with value=1:
Apply Status: Furfur has "furfur_lie_actions" = 2 (consumes on use)
Apply Status: Furfur has "ap_cost" = -2 (discount on next 2 actions)

CRITICAL (confusion: Furfur Lie payoff is easily denied — confusion #15):
The opponent can deploy a readied demon to Challenge Question, denying the payoff.
This engine implementation does not model the Challenge response — it assumes
the payoff was not denied and applies the bonus when the Lie is revealed.
The deny mechanic is handled at the game loop level (player choice to Challenge).
"""
furfur = next((d for d in state.demons if d.instance_id == demon.instance_id), None)
if furfur is None or furfur.fatally_wounded:
return None

# Only fires at the END of the OPPONENT'S main phase
opponent = Side.PLAYER_2 if furfur.owner == Side.PLAYER_1 else Side.PLAYER_1
if event.side != opponent:
return None

# Find Question familiar (120_3) owned by Furfur's owner on the field
question = next(
(d for d in state.demons if d.unit_id == "120_3" and d.owner == furfur.owner),
None,
)
if question is None:
return None # No Question on field — no reveal

# Check if Question has a "question_is_lie" status with value=1
from engine.status_effects import get_active_effects_on
question_effects = get_active_effects_on(state, question)
lie_effects = [e for e in question_effects if e.stat == "question_is_lie" and e.value == 1]

if not lie_effects:
return None # Card under Question is Truth, or no card placed

# Lie revealed! Apply bonus to Furfur:
# - "furfur_lie_actions" = 2 (up to 2 free actions)
# - "ap_cost" = -2 (each action costs 2 less AP)
# Both expire at end of next main phase (i.e., Furfur's current main phase starts)
# Since this fires at MAIN_PHASE_END, Furfur's owner hasn't gone yet.
# The statuses should last through Furfur's next main phase.
new_state = _apply_status_furfur(state, furfur, furfur, "furfur_lie_actions", 2)
new_state = _apply_status_furfur(new_state, furfur, furfur, "ap_cost", -2)
return new_state

register_trigger("120", 1, _furfur_lie_payoff_trigger)
Furfur