Skip to content

Defect: Orb system + 65 card effects#2

Open
JackSwitzer wants to merge 1 commit intomainfrom
work/cards-defect
Open

Defect: Orb system + 65 card effects#2
JackSwitzer wants to merge 1 commit intomainfrom
work/cards-defect

Conversation

@JackSwitzer
Copy link
Owner

@JackSwitzer JackSwitzer commented Feb 4, 2026

Summary

  • Complete orb system (OrbManager, 4 orb types)
  • 29 Defect-specific orb effects
  • 65+ card effects for all Defect cards
  • Focus, channeling, evoking mechanics

Test Results

4270 passed, 6 skipped, 56 xfailed, 81 xpassed

Files Changed

  • packages/engine/effects/orbs.py (NEW)
  • packages/engine/effects/defect_cards.py (NEW)
  • tests/test_defect_cards.py (NEW, 77 tests)
  • packages/engine/state/combat.py (orb_manager field)

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new combat subsystem (OrbManager) and wires it into CombatState copying and effect registration, which can impact simulations if orb state or effect names are incorrect. Broad new effect coverage increases surface area for gameplay regressions, though changes are isolated to Defect mechanics.

Overview
Adds full Defect support: a new effects/orbs.py orb system (channel/evoke/passives, Focus, slot management, Lock-On/Electrodynamics/Loop behaviors) plus a large effects/defect_cards.py set of registered Defect card effect handlers.

Integrates Defect into the engine by including DEFECT_CARDS in ALL_CARDS, updating a few Defect card effect names to match existing handlers (e.g., draw_cards, gain_energy_magic), importing the new modules for effect registration in effects/__init__.py, and extending CombatState with a copyable orb_manager. Adds a comprehensive test_defect_cards.py suite and makes tests/conftest.py path setup repo-relative.

Written by Cursor Bugbot for commit d926ac9. This will update automatically on new commits. Configure here.

Add complete orb system:
- OrbManager class with channel, evoke, passive triggers
- 4 orb types: Lightning, Frost, Dark, Plasma
- Focus modifier support
- Loop power (extra passive triggers)
- Electrodynamics (lightning hits all)
- Lock-On debuff support

Implement all Defect card effects:
- Orb channeling: Zap, Ball Lightning, Coolheaded, Darkness, Fusion,
  Glacier, Chaos, Chill, Rainbow, Meteor Strike, Tempest, etc.
- Orb evoking: Dualcast, Multi-Cast, Recursion, Fission
- Focus: Defragment, Consume, Biased Cognition, Hyperbeam, Reprogram
- Orb counting: Barrage, Compile Driver, Blizzard, Thunder Strike
- Powers: Echo Form, Creative AI, Storm, Static Discharge, Loop,
  Heatsinks, Machine Learning, Buffer, Self Repair, Capacitor
- Card manipulation: All For One, Hologram, Seek, Reboot
- Special: Claw damage scaling, Streamline cost reduction, etc.

Add Defect cards to ALL_CARDS registry.
Update CombatState with orb_manager field.
Add 77 comprehensive tests for all Defect mechanics.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@JackSwitzer
Copy link
Owner Author

PR #2 Code Review: Defect Cards + Orb System - Java Parity Audit

Critical Issues

1. Loop Power - Wrong Orb Targeted

  • Python triggers rightmost orb (index -1)
  • Java triggers leftmost orb (index 0) - LoopPower.java:33-34
  • Fix: Change to trigger orb at index 0

2. Loop Power - Wrong Trigger Timing

  • Python: end of turn
  • Java: atStartOfTurn()
  • Fix: Move Loop triggers to start of turn

3. Electrodynamics - Wrong Lightning Count (MEDIUM)

  • Python channels 1 Lightning
  • Java channels magicNumber Lightning orbs (base: 2, upgraded: 3)
  • Fix: Use magic_number in channel_lightning

Verified Correct

  • Orb base values (Lightning: 3/8, Frost: 2/5, Dark: 6/0+acc, Plasma: 1/2)
  • Lock-On 50% multiplier
  • Channel-when-full evoke behavior
  • Electrodynamics lightning hit-all

Review by Claude Opus 4.5

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 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.

@effect_simple("channel_lightning")
def channel_lightning_effect(ctx: EffectContext) -> None:
"""Channel 1 Lightning orb (Zap, Ball Lightning)."""
channel_orb(ctx.state, "Lightning")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Electrodynamics channels only one Lightning instead of magic_number

Medium Severity

The channel_lightning effect always channels exactly 1 Lightning orb, ignoring ctx.magic_number. Electrodynamics has base_magic=2, upgrade_magic=1 meaning it expects to channel 2 (or 3 when upgraded) Lightning orbs, but only 1 is channeled. Compare with channel_random_orb_effect which correctly uses ctx.magic_number to determine how many orbs to channel.

Fix in Cursor Fix in Web

zero_cost_ids = {
"Claw", "Go for the Eyes", "Zap", "Turbo", "Steam", "Reboot",
"Seek", "Fission", "Rainbow"
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded zero-cost card list missing Beam Cell and FTL

Medium Severity

The all_for_one_effect uses a hardcoded zero_cost_ids set to identify 0-cost cards to return from discard. This list is missing "Beam Cell" and "FTL" which are both 0-cost Defect cards (defined with cost=0 in the card registry). Additionally, the list incorrectly includes "Zap" (cost=1) and "Rainbow" (cost=2), which will cause these cards to be incorrectly treated as 0-cost if they aren't in card_costs.

Fix in Cursor Fix in Web

"""Decrease max orb slots (minimum 0)."""
self.max_slots = max(0, self.max_slots - amount)
# If we have more orbs than slots, evoke leftmost
# (This shouldn't happen in normal gameplay, but handle it)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing orb slots doesn't evoke excess orbs

Medium Severity

The remove_orb_slot method reduces max_slots but doesn't evoke orbs when the current orb count exceeds the new capacity. The comment acknowledges this ("If we have more orbs than slots, evoke leftmost") but the implementation is missing. This affects the Consume card, which commonly reduces orb slots when orbs are already channeled, leaving the orb array in an inconsistent state with more orbs than slots.

Fix in Cursor Fix in Web

zero_cost_ids = {
"Claw", "Go for the Eyes", "Zap", "Turbo", "Steam", "Reboot",
"Seek", "Fission", "Rainbow", "FTL", "Beam Cell"
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scrape incorrectly treats Zap and Rainbow as zero-cost

Low Severity

The scrape_effect hardcoded zero_cost_ids set incorrectly includes "Zap" (cost=1) and "Rainbow" (cost=2). When Scrape draws these cards, they won't be discarded even though they cost more than 0. This gives the player an unintended advantage by keeping non-zero-cost cards in hand. The fallback to card_costs with default 1 won't catch these since they're in the hardcoded list.

Fix in Cursor Fix in Web

@JackSwitzer
Copy link
Owner Author

Correction: Loop Power Orb Targeting

Retracting Critical Issue #1 from my previous review.

After deeper investigation of Java source:

  • LoopPower.java uses orbs.get(0)
  • GoldPlatedCables also uses orbs.get(0) but description says "rightmost orb"
  • evokeOrb() uses orbs.get(0) and game tooltip says "Evoke: Consume your rightmost Orb"

Conclusion: In Java's orb list, index 0 = rightmost orb visually. The list is stored in reverse visual order (right-to-left).

Loop power using index 0 IS correct - it triggers the rightmost orb as expected.

The remaining issues (Loop timing at start of turn, Electrodynamics channel count) are still valid.

Correction by Claude Opus 4.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant