new features, play menu, deck builder, deck selection
This commit is contained in:
@@ -4,12 +4,14 @@ extends Node
|
||||
## Loads card definitions from JSON and provides lookup methods
|
||||
|
||||
const CARDS_PATH = "res://data/cards.json"
|
||||
const STARTER_DECKS_PATH = "res://data/starter_decks.json"
|
||||
|
||||
# Loaded card data
|
||||
var _cards: Dictionary = {} # id -> CardData
|
||||
var _cards_by_element: Dictionary = {} # Element -> Array[CardData]
|
||||
var _cards_by_type: Dictionary = {} # CardType -> Array[CardData]
|
||||
var _card_textures: Dictionary = {} # id -> Texture2D
|
||||
var _starter_decks: Array = [] # Array of StarterDeckData
|
||||
|
||||
# Signals
|
||||
signal database_loaded
|
||||
@@ -17,6 +19,7 @@ signal load_error(message: String)
|
||||
|
||||
func _ready() -> void:
|
||||
_load_database()
|
||||
_load_starter_decks()
|
||||
|
||||
func _load_database() -> void:
|
||||
var file = FileAccess.open(CARDS_PATH, FileAccess.READ)
|
||||
@@ -158,6 +161,137 @@ func get_cards_by_element(element: Enums.Element) -> Array:
|
||||
func get_cards_by_type(card_type: Enums.CardType) -> Array:
|
||||
return _cards_by_type.get(card_type, [])
|
||||
|
||||
|
||||
## Filter cards by multiple criteria
|
||||
## filters: Dictionary with optional keys:
|
||||
## - name: String (substring search, case-insensitive)
|
||||
## - elements: Array[Enums.Element] (OR logic - card has any of these)
|
||||
## - type: Enums.CardType (-1 or omit for any)
|
||||
## - cost_min: int
|
||||
## - cost_max: int
|
||||
## - job: String (exact match, case-insensitive)
|
||||
## - category: String (exact match)
|
||||
## - power_min: int
|
||||
## - power_max: int
|
||||
## - ex_burst_only: bool
|
||||
## - set: String (card ID prefix, e.g. "1-", "2-")
|
||||
func filter_cards(filters: Dictionary) -> Array[CardData]:
|
||||
var results: Array[CardData] = []
|
||||
|
||||
for card in _cards.values():
|
||||
if _matches_filters(card, filters):
|
||||
results.append(card)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
func _matches_filters(card: CardData, filters: Dictionary) -> bool:
|
||||
# Name search (case-insensitive substring)
|
||||
if filters.has("name") and not filters.name.is_empty():
|
||||
if not card.name.to_lower().contains(filters.name.to_lower()):
|
||||
return false
|
||||
|
||||
# Element filter (OR logic for multi-select)
|
||||
if filters.has("elements") and filters.elements.size() > 0:
|
||||
var has_element = false
|
||||
for elem in card.elements:
|
||||
if elem in filters.elements:
|
||||
has_element = true
|
||||
break
|
||||
if not has_element:
|
||||
return false
|
||||
|
||||
# Type filter
|
||||
if filters.has("type") and filters.type != -1:
|
||||
if card.type != filters.type:
|
||||
return false
|
||||
|
||||
# Cost filter (range)
|
||||
if filters.has("cost_min") and card.cost < filters.cost_min:
|
||||
return false
|
||||
if filters.has("cost_max") and card.cost > filters.cost_max:
|
||||
return false
|
||||
|
||||
# Job filter (case-insensitive)
|
||||
if filters.has("job") and not filters.job.is_empty():
|
||||
if card.job.to_lower() != filters.job.to_lower():
|
||||
return false
|
||||
|
||||
# Category filter
|
||||
if filters.has("category") and not filters.category.is_empty():
|
||||
if card.category != filters.category:
|
||||
return false
|
||||
|
||||
# Power range
|
||||
if filters.has("power_min") and card.power < filters.power_min:
|
||||
return false
|
||||
if filters.has("power_max") and card.power > filters.power_max:
|
||||
return false
|
||||
|
||||
# EX Burst filter
|
||||
if filters.has("ex_burst_only") and filters.ex_burst_only:
|
||||
if not card.has_ex_burst:
|
||||
return false
|
||||
|
||||
# Set/Opus filter (card ID prefix)
|
||||
if filters.has("set") and not filters.set.is_empty():
|
||||
if not card.id.begins_with(filters.set):
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
## Get all unique job values from loaded cards
|
||||
func get_unique_jobs() -> Array[String]:
|
||||
var jobs: Dictionary = {}
|
||||
for card in _cards.values():
|
||||
if not card.job.is_empty():
|
||||
jobs[card.job] = true
|
||||
var result: Array[String] = []
|
||||
for job in jobs.keys():
|
||||
result.append(job)
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
|
||||
## Get all unique category values from loaded cards
|
||||
func get_unique_categories() -> Array[String]:
|
||||
var categories: Dictionary = {}
|
||||
for card in _cards.values():
|
||||
if not card.category.is_empty():
|
||||
categories[card.category] = true
|
||||
var result: Array[String] = []
|
||||
for category in categories.keys():
|
||||
result.append(category)
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
|
||||
## Get all unique set/opus prefixes from card IDs
|
||||
func get_card_sets() -> Array[String]:
|
||||
var sets: Dictionary = {}
|
||||
for card in _cards.values():
|
||||
# Extract prefix before first dash (e.g., "1" from "1-001H")
|
||||
var dash_pos = card.id.find("-")
|
||||
if dash_pos > 0:
|
||||
var prefix = card.id.substr(0, dash_pos)
|
||||
sets[prefix] = true
|
||||
var result: Array[String] = []
|
||||
for set_id in sets.keys():
|
||||
result.append(set_id)
|
||||
# Sort numerically if possible
|
||||
result.sort_custom(func(a, b):
|
||||
var a_num = a.to_int() if a.is_valid_int() else 9999
|
||||
var b_num = b.to_int() if b.is_valid_int() else 9999
|
||||
return a_num < b_num
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
## Get card count
|
||||
func get_card_count() -> int:
|
||||
return _cards.size()
|
||||
|
||||
## Get or load a card texture
|
||||
func get_card_texture(card: CardData) -> Texture2D:
|
||||
if card.id in _card_textures:
|
||||
@@ -234,8 +368,82 @@ func create_test_deck(player_index: int) -> Array[String]:
|
||||
return deck
|
||||
|
||||
|
||||
## Starter Deck Methods
|
||||
|
||||
func _load_starter_decks() -> void:
|
||||
var file = FileAccess.open(STARTER_DECKS_PATH, FileAccess.READ)
|
||||
if not file:
|
||||
push_warning("Failed to open starter decks: " + STARTER_DECKS_PATH)
|
||||
return
|
||||
|
||||
var json_text = file.get_as_text()
|
||||
file.close()
|
||||
|
||||
var json = JSON.new()
|
||||
var error = json.parse(json_text)
|
||||
if error != OK:
|
||||
push_error("Failed to parse starter decks JSON: " + json.get_error_message())
|
||||
return
|
||||
|
||||
var data = json.get_data()
|
||||
if not data.has("starter_decks"):
|
||||
push_error("Starter decks file missing 'starter_decks' array")
|
||||
return
|
||||
|
||||
for deck_data in data["starter_decks"]:
|
||||
var deck = StarterDeckData.new()
|
||||
deck.id = deck_data.get("id", "")
|
||||
deck.name = deck_data.get("name", "")
|
||||
deck.opus = deck_data.get("opus", "")
|
||||
deck.description = deck_data.get("description", "")
|
||||
deck.elements = deck_data.get("elements", [])
|
||||
deck.cards = deck_data.get("cards", [])
|
||||
deck.image = deck_data.get("image", "")
|
||||
_starter_decks.append(deck)
|
||||
|
||||
print("CardDatabase: Loaded ", _starter_decks.size(), " starter decks")
|
||||
|
||||
|
||||
## Get all starter decks
|
||||
func get_starter_decks() -> Array:
|
||||
return _starter_decks
|
||||
|
||||
|
||||
## Get a starter deck by ID
|
||||
func get_starter_deck(deck_id: String) -> StarterDeckData:
|
||||
for deck in _starter_decks:
|
||||
if deck.id == deck_id:
|
||||
return deck
|
||||
return null
|
||||
|
||||
|
||||
## Get a random starter deck
|
||||
func get_random_starter_deck() -> StarterDeckData:
|
||||
if _starter_decks.is_empty():
|
||||
return null
|
||||
return _starter_decks[randi() % _starter_decks.size()]
|
||||
|
||||
|
||||
## Data Classes
|
||||
|
||||
class StarterDeckData:
|
||||
var id: String = ""
|
||||
var name: String = ""
|
||||
var opus: String = ""
|
||||
var description: String = ""
|
||||
var elements: Array = [] # Array of element name strings
|
||||
var cards: Array = [] # Array of card IDs
|
||||
var image: String = "" # Path to box art image
|
||||
|
||||
func get_texture() -> Texture2D:
|
||||
if image.is_empty():
|
||||
return null
|
||||
var texture_path = "res://assets/ui/" + image
|
||||
if ResourceLoader.exists(texture_path):
|
||||
return load(texture_path)
|
||||
return null
|
||||
|
||||
|
||||
class CardData:
|
||||
var id: String = ""
|
||||
var name: String = ""
|
||||
|
||||
@@ -52,7 +52,9 @@ func _on_database_loaded() -> void:
|
||||
print("GameManager: Ready")
|
||||
|
||||
## Start a new game
|
||||
func start_new_game() -> void:
|
||||
## deck1 and deck2 are optional arrays of card IDs
|
||||
## If empty, test decks will be created
|
||||
func start_new_game(deck1: Array = [], deck2: Array = []) -> void:
|
||||
if not is_initialized:
|
||||
push_error("GameManager not initialized")
|
||||
return
|
||||
@@ -68,12 +70,24 @@ func start_new_game() -> void:
|
||||
# Connect signals
|
||||
_connect_game_signals()
|
||||
|
||||
# Create test decks
|
||||
var deck1 = CardDatabase.create_test_deck(0)
|
||||
var deck2 = CardDatabase.create_test_deck(1)
|
||||
# Use provided decks or create test decks
|
||||
var player1_deck: Array[String] = []
|
||||
var player2_deck: Array[String] = []
|
||||
|
||||
if deck1.is_empty():
|
||||
player1_deck = CardDatabase.create_test_deck(0)
|
||||
else:
|
||||
for card_id in deck1:
|
||||
player1_deck.append(card_id)
|
||||
|
||||
if deck2.is_empty():
|
||||
player2_deck = CardDatabase.create_test_deck(1)
|
||||
else:
|
||||
for card_id in deck2:
|
||||
player2_deck.append(card_id)
|
||||
|
||||
# Setup and start
|
||||
game_state.setup_game(deck1, deck2)
|
||||
game_state.setup_game(player1_deck, player2_deck)
|
||||
game_state.start_game()
|
||||
|
||||
is_game_active = true
|
||||
|
||||
Reference in New Issue
Block a user