Conversation
Batch 1 - Poison effects: - apply_poison (Deadly Poison, Poisoned Stab) - double_poison (Catalyst - 2x/3x multiplier) - apply_poison_all, apply_weak_2_all (Crippling Poison) - apply_poison_random_3_times (Bouncing Flask) - apply_corpse_explosion (Corpse Explosion on death AoE) - apply_poison_all_each_turn (Noxious Fumes power) Batch 2 - Shiv mechanics: - add_shivs_to_hand (Blade Dance, Cloak and Dagger) - add_shiv_each_turn (Infinite Blades power) - shivs_deal_more_damage (Accuracy power) - add_shivs_equal_to_discarded (Storm of Steel) Batch 3 - Discard triggers: - discard_1, discard_x, discard_random_1 (selection-based) - discard_hand, discard_hand_draw_same (Calculated Gamble) - discard_non_attacks (Unload) - when_discarded_draw (Reflex) - when_discarded_gain_energy (Tactician) - cost_reduces_per_discard (Eviscerate) - refund_2_energy_if_discarded_this_turn (Sneaky Strike) Batch 4 - Special effects: - gain_intangible, lose_1_dexterity_each_turn (Wraith Form) - no_draw_this_turn, cards_cost_0_this_turn (Bullet Time) - double_damage_next_turn (Phantasmal Killer) - double_next_skills (Burst) - block_not_removed_next_turn (Blur) - gain_1_block_per_card_played (After Image) - deal_damage_per_card_played (A Thousand Cuts) X-cost effects: - damage_x_times_energy (Skewer) - apply_weak_x, apply_strength_down_x (Malaise) - draw_x_next_turn, gain_x_energy_next_turn (Doppelganger) Power triggers added to powers.py: - ToolsOfTheTrade, NextTurnDraw, NextTurnEnergy (start of turn) - ThousandCuts, Burst, Accuracy (on card play) - Reflex, Tactician on manual discard - WellLaidPlans, NoDraw, ZeroCostCards (end of turn) - CorpseExplosion (on enemy death) - PhantasmalKiller, Blur (damage/block modifiers) Tests: 96 new tests in test_silent_cards.py covering all card stats and effect registrations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
PR #4 Code Review: Silent Cards - Java Parity AuditCritical Issues1. Wraith Form - Incorrect Dexterity Loss
2. Burst Power - Missing End of Turn Removal
Medium Issues
Verified Correct
Review by Claude Opus 4.5 |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 5 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
This PR is being reviewed by Cursor Bugbot
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
| """Refund 2 energy if a card was discarded this turn (Sneaky Strike).""" | ||
| discarded_this_turn = ctx.extra_data.get("discarded_this_turn", 0) | ||
| if discarded_this_turn > 0: | ||
| ctx.gain_energy(2) |
There was a problem hiding this comment.
Sneaky Strike energy refund checks wrong data location
High Severity
The refund_2_energy_if_discarded_this_turn effect reads from ctx.extra_data.get("discarded_this_turn", 0), but the SneakyStrike power trigger writes the discard count to ctx.state.discarded_this_turn. Since these are different storage locations, the effect will always see 0 discards and never trigger the energy refund for Sneaky Strike/Underhanded Strike.
Additional Locations (1)
| if card_id in ALL_CARDS: | ||
| card = ALL_CARDS[card_id] | ||
| energy_amount = card.magic_number if card.magic_number > 0 else 1 | ||
| ctx.gain_energy(energy_amount) |
There was a problem hiding this comment.
Reflex/Tactician discard triggers never fire
High Severity
The power triggers reflex_on_discard and tactician_on_discard are registered with power="Reflex" and power="Tactician", but these are card IDs not player statuses. The power trigger system only invokes handlers for powers that exist in owner.statuses.keys(). Since no code adds "Reflex" or "Tactician" as player statuses, these triggers will never execute. The corresponding card effects (when_discarded_draw, when_discarded_gain_energy) are also empty no-ops, meaning Reflex and Tactician's discard abilities are completely non-functional.
Additional Locations (1)
| if current > 1: | ||
| ctx.player.statuses["Burst"] = current - 1 | ||
| else: | ||
| del ctx.player.statuses["Burst"] |
There was a problem hiding this comment.
Burst power trigger consumes itself on play
High Severity
The burst_on_use power trigger fires after the Burst card's effects are applied, meaning the "Burst" status is already in the player's statuses when onUseCard triggers. Since Burst itself is a Skill card, the trigger checks pass and it immediately consumes the Burst status on the card that just created it. The trigger sets play_again = True for Burst (which is meaningless) and decrements the Burst counter before any subsequent skill can benefit from it. A check is needed to skip the card that created the Burst status.
|
|
||
| import pytest | ||
| import sys | ||
| sys.path.insert(0, '/Users/jackswitzer/Desktop/SlayTheSpireRL') |
| def get_silent_card_effects(card_id: str) -> List[str]: | ||
| """Get the effect names for a Silent card.""" | ||
| base_id = card_id.rstrip("+") | ||
| return SILENT_CARD_EFFECTS.get(base_id, []) |
1. Wraith Form - Dexterity Loss Implementation (CRITICAL) - Changed from direct subtraction to using apply_power_to_player - Now properly respects Artifact (can block negative dex) - Updated apply_power to treat negative Strength/Dexterity as debuffs 2. Burst Power - Missing End of Turn Removal (CRITICAL) - Added atEndOfTurn handler for Burst power - Burst now properly removes at end of turn even if unused - Matches Java BurstPower.atEndOfTurn() behavior 3. Thousand Cuts - Trigger Timing (MEDIUM) - Added documentation noting timing difference - Java uses onAfterCardPlayed, we use onUseCard - Removed duplicate handler definition 4. Bouncing Flask - RNG Stream (MEDIUM-HIGH) - Changed from random.choice() to deterministic RNG - Uses card_rng_state from combat state for reproducibility - Ensures same seed produces same targeting Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>


Summary
Test Results
328 tests passing
Files Changed
🤖 Generated with Claude Code
Note
Medium Risk
Large addition of combat/effect logic that changes core simulation behavior (new card effects and power triggers), though scoped to Silent mechanics and covered by new tests.
Overview
Adds a full set of Silent card effect implementations and wiring, including Poison, Shiv generation, discard/draw/energy/block conditionals, and X-cost/special behaviors via
SILENT_CARD_EFFECTS+get_silent_card_effects()inpackages/engine/effects/cards.py.Extends the power trigger registry (
packages/engine/registry/powers.py) with Silent-specific start/end-of-turn, on-card-play, discard-trigger, damage modifier, and on-death handlers (e.g.,ToolsOfTheTrade,Burst,Accuracy,Reflex/Tactician,CorpseExplosion).Adds a new
tests/test_silent_cards.pysuite asserting Silent card stats/effects and key mechanics interactions, and expands attack-card identification to include Silent attacks for playability/discard logic.Written by Cursor Bugbot for commit b620e9d. This will update automatically on new commits. Configure here.