Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
thedivergentai avatar

Godot Genre Card Game

  • 196 installs
  • 454 repo stars
  • Updated July 28, 2026
  • thedivergentai/gd-agentic-skills

Use godot-genre-card-game for development tasks

About

godot-genre-card-game: A skill for development. This provides functionality for development workflows.

  • godot-genre-card-game

Godot Genre Card Game by the numbers

  • 196 all-time installs (skills.sh)
  • +11 installs in the week ending Jul 27, 2026 (Skillselion tracking)
  • Ranked #2,033 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-genre-card-game

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs196
repo stars454
Last updatedJuly 28, 2026
Repositorythedivergentai/gd-agentic-skills

What it does

Use godot-genre-card-game for development tasks

Files

SKILL.mdMarkdownGitHub ↗

Genre: Card Game

Expert blueprint for digital card games with data-driven design and juicy UI.

NEVER Do (Expert Anti-Patterns)

Logic & Architecture

  • NEVER hardcode card logic inside UI scripts; strictly encapsulate gameplay effects in `Callable` objects or Command resources pushed to a LIFO stack.
  • NEVER perform board-state calculations (Power/Toughness) in _process(); strictly use Signal-driven triggers or a centralized EffectStack resolver.
  • NEVER forget LIFO Stack Resolution; strictly use `Array.push_back()` and `Array.pop_back()` to resolve reactions from top-to-bottom.

UX & Animation

  • NEVER skip Z-Index management during drag-and-drop; strictly raise the card to the front on click to prevent it sliding under other cards.
  • NEVER allow instant card "teleportation" between piles; strictly use Tween animations (0.2s+) to give cards a tactile, physical feel.
  • NEVER use global_position for cards in hand; strictly position them using a `Curve2D` (Bezier) layout with `sample_baked()` for smooth, non-circular arcs.
  • NEVER allow instant card "teleportation" between piles; strictly use `create_tween()` and `tween_property` chainings (0.2s+) for juicy card-feel.

Deck & State Management

  • NEVER forget to handle Empty Deck scenarios; strictly implement auto-reshuffle of the discard pile to prevent soft-locks.
  • NEVER use floating point numbers for discrete card stats; strictly use int for Costs, Attack, and Health to avoid precision drift.
  • NEVER use standard Control nodes for mass tokens/battlefields; strictly use `_draw()` custom drawing to bypass SceneTree overhead when rendering 100+ cards or map icons.
  • NEVER rely on SceneTree order for hand logic; strictly manage logical order in an Array and update visuals via `queue_redraw()`.
  • NEVER erase array elements during a standard for loop; strictly iterate in reverse or use filter() to avoid indexing errors.
  • NEVER forget to provide parameterless constructors in _init(); otherwise, Resources will fail to load in the Inspector.

---

🛠 Expert Components (scripts/)

Original Expert Patterns

  • card_effect_resolution.gd - Stack-based effect resolver (LIFO/FIFO) handling nested triggers and counter-play.

Modular Components

  • card_data_resource.gd - Data-driven card definitions allowing Inspector-based design.
  • deck_shuffle_bag.gd - Secure randomization patterns for uniform card distribution.
  • turn_state_machine.gd - Managing rigid phases (Draw, Play, Combat) via state matching.
  • card_drag_drop.gd - Implementation of native _get_drag_data() for Control nodes.
  • board_query_filter.gd - Functional filter() patterns for querying board metadata.
  • card_tween_manager.gd - Managing interruptible card juice and board transitions.
  • reactive_card_ui.gd - Resource-signal driven UI for automatic visual state updates.
  • board_state_dictionary.gd - Grid-based tracking (Vector2i) decoupled from Node order.
  • match_state_resetter.gd - Clean-up pattern for in-match temporary Resource modifications.
  • deck_builder_validator.gd - Backend logic for deck-building constraints and mana curves.

---

Core Loop

1. Draw: Player draws cards from a deck into their hand. 2. Evaluate: Player assesses board state, mana/energy, and card options. 3. Play: Player plays cards to trigger effects (damage, buff, summon). 4. Resolve: Effects occur immediately or go onto a stack. 5. Discard/End: Unused cards are discarded (roguelike) or kept (TCG), turn ends.

Skill Chain

PhaseSkillsPurpose
1. Dataresources, custom-resourcesDefining Card properties (Cost, Type, Effect)
2. UIcontrol-nodes, layout-containersHand layout, card positioning, tooltips
3. Inputdrag-and-drop, state-machinesDragging cards to targets, hovering
4. Logiccommand-pattern, signalsExecuting card effects, turn phases
5. Polishgodot-tweening, shadersDraw animations, holographic foils

Architecture Overview

1. Card Data (Resource-based)

Godot Resources are perfect for card data.

# card_data.gd
extends Resource
class_name CardData

enum Type { ATTACK, SKILL, POWER }
enum Target { ENEMY, SELF, ALL_ENEMIES }

@export var id: String
@export var name: String
@export_multiline var description: String
@export var cost: int
@export var type: Type
@export var target_type: Target
@export var icon: Texture2D
@export var effect_script: Script # Custom logic per card

2. Deck Manager

Handles the piles: Draw Pile, Hand, Discard Pile, Exhaust Pile.

# deck_manager.gd
var draw_pile: Array[CardData] = []
var hand: Array[CardData] = []
var discard_pile: Array[CardData] = []

func draw_cards(amount: int) -> void:
    for i in amount:
        if draw_pile.is_empty():
            reshuffle_discard()
            
        if draw_pile.is_empty(): 
            break # No cards left
            
        var card = draw_pile.pop_back()
        hand.append(card)
        card_drawn.emit(card)

func reshuffle_discard() -> void:
    draw_pile.append_array(discard_pile)
    discard_pile.clear()
    draw_pile.shuffle()

3. Card Visual (UI)

The interactive node representing a card in hand.

# card_ui.gd
extends Control

var card_data: CardData
var start_pos: Vector2
var is_dragging: bool = false

func _gui_input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
        if event.pressed:
            start_drag()
        else:
            end_drag()

func _process(delta: float) -> void:
    if is_dragging:
        global_position = get_global_mouse_position() - size / 2
    else:
        # Hover effect or return to hand position
        pass

Key Mechanics Implementation

Effect Resolution (Command Pattern)

Decouple the "playing" of a card from its "effect".

func play_card(card: CardData, target: Node) -> void:
    if current_energy < card.cost:
        show_error("Not enough energy")
        return
        
    current_energy -= card.cost
    
    # Execute effect
    var effect = card.effect_script.new()
    effect.execute(target)
    
    move_to_discard(card)

Hand Layout (Arching)

Cards in hand usually form an arc. Use a math formula (Bezier or Circle) to position them based on index and total_cards.

func update_hand_visuals() -> void:
    var center_x = screen_width / 2
    var radius = 1000.0
    var angle_step = 5.0
    
    for i in hand_visuals.size():
        var card = hand_visuals[i]
        var angle = deg_to_rad((i - hand_visuals.size() / 2.0) * angle_step)
        var target_pos = Vector2(
            center_x + sin(angle) * radius,
            screen_height + cos(angle) * radius
        )
        card.target_rotation = angle
        card.target_position = target_pos

Common Pitfalls

1. Complexity Overload: Too many keywords. Fix: Stick to 3-5 core keywords (e.g., Taunt, Poison, Shield) and expand slowly. 2. Unreadable Text: Tiny fonts on cards. Fix: Use icons for common stats (Damage, Block) and keep text short. 3. Animation Lock: Waiting for slow animations to finish before playing the next card. Fix: Allow queueing actions or keep animations snappy (< 0.3s).

Godot-Specific Tips

  • MouseFilter: Getting drag/drop to work with overlapping UI requires careful setup of mouse_filter (Pass vs Stop).
  • Z-Index: Use z_index or CanvasLayer to ensure the dragged card is always on top of everything else.
  • Tweens: Essential! Tween position, rotation, and scale for that "juicy" Hearthstone/Slay the Spire feel.

---

🚀 Elite Technical Implementations (Batch 09)

1. Holographic Foil (Shader Script)

Add visual rarity and "juice" to cards using a holographic shader. This script uses iridescence based on TIME and UV coordinates to create a shifting rainbow effect.

shader_type canvas_item;

uniform float foil_speed : hint_range(0.1, 5.0) = 1.0;
uniform float foil_intensity : hint_range(0.0, 1.0) = 0.5;

void fragment() {
    vec4 base_color = texture(TEXTURE, UV);
    
    // Create shifting rainbow iridescence
    vec3 holo_color = vec3(
        0.5 + 0.5 * sin(TIME * foil_speed + UV.x * 10.0),
        0.5 + 0.5 * sin(TIME * foil_speed + UV.y * 10.0 + 2.0),
        0.5 + 0.5 * sin(TIME * foil_speed + (UV.x + UV.y) * 10.0 + 4.0)
    );
    
    // Blend with base texture alpha
    COLOR = vec4(mix(base_color.rgb, holo_color, foil_intensity * base_color.a), base_color.a);
}

2. Card-History Logging (Action Tracking)

Track card actions (Played, Drawn, Discarded) for a history panel using a custom Logger. This intercepts messages tagged with [CARD] and routes them to a turn history buffer.

class_name CardHistoryLogger extends Logger

signal history_updated(entry: String)
var turn_history: Array[String] = []

func _log_message(message: String, error: bool) -> void:
    if not error and message.begins_with("[CARD]"):
        turn_history.append(message)
        history_updated.emit(message)

# To register (in an Autoload):
# func _init(): OS.add_logger(CardHistoryLogger.new())

3. Hand-Limit Logic (Over-Draw Protection)

Encapsulate hand data and enforce a maximum size. Use signals to notify the UI when a card is successfully drawn or discarded due to being overdrawn.

class_name HandManager extends Node

signal card_drawn(card: Resource)
signal card_overdrawn(card: Resource)

@export var max_hand_size: int = 10
var _current_hand: Array[Resource] = []

func draw_card(new_card: Resource) -> void:
    if _current_hand.size() >= max_hand_size:
        # Hand is full; trigger overdraw
        card_overdrawn.emit(new_card)
    else:
        _current_hand.append(new_card)
        card_drawn.emit(new_card)
  • Master Skill: godot-master

Related skills

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.