Files
jass-learner/addons/card-framework/json_card_factory.gd
Aspergerli ade3d0fb01 init
2026-03-09 19:18:47 +01:00

218 lines
7.5 KiB
GDScript

@tool
## JSON-based card factory implementation with asset management and caching.
##
## JsonCardFactory extends CardFactory to provide JSON-based card creation with
## sophisticated asset loading, data caching, and error handling. It manages
## card definitions stored as JSON files and automatically loads corresponding
## image assets from specified directories.
##
## Key Features:
## - JSON-based card data definition with flexible schema
## - Automatic asset loading and texture management
## - Performance-optimized data caching for rapid card creation
## - Comprehensive error handling with detailed logging
## - Directory scanning for bulk card data preloading
## - Configurable asset and data directory paths
##
## File Structure Requirements:
## [codeblock]
## project/
## ├── card_assets/ # card_asset_dir
## │ ├── ace_spades.png
## │ └── king_hearts.png
## ├── card_data/ # card_info_dir
## │ ├── ace_spades.json # Matches asset filename
## │ └── king_hearts.json
## [/codeblock]
##
## JSON Schema Example:
## [codeblock]
## {
## "name": "ace_spades",
## "front_image": "ace_spades.png",
## "suit": "spades",
## "value": "ace"
## }
## [/codeblock]
class_name JsonCardFactory
extends CardFactory
@export_group("card_scenes")
## Base card scene to instantiate for each card (must inherit from Card class)
@export var default_card_scene: PackedScene
@export_group("asset_paths")
## Directory path containing card image assets (PNG, JPG, etc.)
@export var card_asset_dir: String
## Directory path containing card information JSON files
@export var card_info_dir: String
@export_group("default_textures")
## Common back face texture used for all cards when face-down
@export var back_image: Texture2D
## Validates configuration and default card scene on initialization.
## Ensures default_card_scene references a valid Card-inherited node.
func _ready() -> void:
if default_card_scene == null:
push_error("default_card_scene is not assigned!")
return
# Validate that default_card_scene produces Card instances
var temp_instance = default_card_scene.instantiate()
if not (temp_instance is Card):
push_error("Invalid node type! default_card_scene must reference a Card.")
default_card_scene = null
temp_instance.queue_free()
## Creates a new card instance with JSON data and adds it to the target container.
## Uses cached data if available, otherwise loads from JSON and asset files.
## @param card_name: Identifier matching JSON filename (without .json extension)
## @param target: CardContainer to receive the new card
## @returns: Created Card instance or null if creation failed
func create_card(card_name: String, target: CardContainer) -> Card:
# Use cached data for optimal performance
if preloaded_cards.has(card_name):
var card_info = preloaded_cards[card_name]["info"]
var front_image = preloaded_cards[card_name]["texture"]
return _create_card_node(card_info.name, front_image, target, card_info)
else:
# Load card data on-demand (slower but supports dynamic loading)
var card_info = _load_card_info(card_name)
if card_info == null or card_info == {}:
push_error("Card info not found for card: %s" % card_name)
return null
# Validate required JSON fields
if not card_info.has("front_image"):
push_error("Card info does not contain 'front_image' key for card: %s" % card_name)
return null
# Load corresponding image asset
var front_image_path = card_asset_dir + "/" + card_info["front_image"]
var front_image = _load_image(front_image_path)
if front_image == null:
push_error("Card image not found: %s" % front_image_path)
return null
return _create_card_node(card_info.name, front_image, target, card_info)
## Scans card info directory and preloads all JSON data and textures into cache.
## Significantly improves card creation performance by eliminating file I/O during gameplay.
## Should be called during game initialization or loading screens.
func preload_card_data() -> void:
var dir = DirAccess.open(card_info_dir)
if dir == null:
push_error("Failed to open directory: %s" % card_info_dir)
return
# Scan directory for all JSON files
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
# Skip non-JSON files
if !file_name.ends_with(".json"):
file_name = dir.get_next()
continue
# Extract card name from filename (without .json extension)
var card_name = file_name.get_basename()
var card_info = _load_card_info(card_name)
if card_info == null:
push_error("Failed to load card info for %s" % card_name)
continue
# Load corresponding texture asset
var front_image_path = card_asset_dir + "/" + card_info.get("front_image", "")
var front_image_texture = _load_image(front_image_path)
if front_image_texture == null:
push_error("Failed to load card image: %s" % front_image_path)
continue
# Cache both JSON data and texture for fast access
preloaded_cards[card_name] = {
"info": card_info,
"texture": front_image_texture
}
print("Preloaded card data:", preloaded_cards[card_name])
file_name = dir.get_next()
## Loads and parses JSON card data from file system.
## @param card_name: Card identifier (filename without .json extension)
## @returns: Dictionary containing card data or empty dict if loading failed
func _load_card_info(card_name: String) -> Dictionary:
var json_path = card_info_dir + "/" + card_name + ".json"
if !FileAccess.file_exists(json_path):
return {}
# Read JSON file content
var file = FileAccess.open(json_path, FileAccess.READ)
var json_string = file.get_as_text()
file.close()
# Parse JSON with error handling
var json = JSON.new()
var error = json.parse(json_string)
if error != OK:
push_error("Failed to parse JSON: %s" % json_path)
return {}
return json.data
## Loads image texture from file path with error handling.
## @param image_path: Full path to image file
## @returns: Loaded Texture2D or null if loading failed
func _load_image(image_path: String) -> Texture2D:
var texture = load(image_path) as Texture2D
if texture == null:
push_error("Failed to load image resource: %s" % image_path)
return null
return texture
## Creates and configures a card node with textures and adds it to target container.
## @param card_name: Card identifier for naming and reference
## @param front_image: Texture for card front face
## @param target: CardContainer to receive the card
## @param card_info: Dictionary of card data from JSON
## @returns: Configured Card instance or null if addition failed
func _create_card_node(card_name: String, front_image: Texture2D, target: CardContainer, card_info: Dictionary) -> Card:
var card = _generate_card(card_info)
# Validate container can accept this card
if !target._card_can_be_added([card]):
print("Card cannot be added: %s" % card_name)
card.queue_free()
return null
# Configure card properties
card.card_info = card_info
card.card_size = card_size
# Add to scene tree and container
var cards_node = target.get_node("Cards")
cards_node.add_child(card)
target.add_card(card)
# Set card identity and textures
card.card_name = card_name
card.set_faces(front_image, back_image)
return card
## Instantiates a new card from the default card scene.
## @param _card_info: Card data dictionary (reserved for future customization)
## @returns: New Card instance or null if scene is invalid
func _generate_card(_card_info: Dictionary) -> Card:
if default_card_scene == null:
push_error("default_card_scene is not assigned!")
return null
return default_card_scene.instantiate()