Compare commits
2 commits
8040d708e7
...
d778f08a95
| Author | SHA1 | Date | |
|---|---|---|---|
| d778f08a95 | |||
| 0f74382fde |
12 changed files with 827 additions and 24 deletions
|
|
@ -199,3 +199,5 @@ func calculateCardCost(card: Card) -> int:
|
|||
func upgradeCard(card: Card) -> bool:
|
||||
# Implement card upgrading logic
|
||||
return false
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ enum RoomType {
|
|||
EVENT
|
||||
}
|
||||
|
||||
var min_levels = 6
|
||||
var max_levels = 15
|
||||
var min_nodes_per_level = 2
|
||||
var max_nodes_per_level = 6
|
||||
var min_levels = 10
|
||||
var max_levels = 20
|
||||
var max_connections_per_node = 4
|
||||
|
||||
var min_nodes_per_level = 1
|
||||
var max_nodes_per_level = 4
|
||||
var positions_per_level = 6
|
||||
var starting_elo = 1000
|
||||
var final_elo = 2100
|
||||
|
|
@ -62,7 +64,8 @@ func generate_map():
|
|||
for level in range(1, num_levels - 1):
|
||||
var level_nodes = []
|
||||
var level_elo = starting_elo + (elo_step * level)
|
||||
var num_nodes = _rng.randi_range(min_nodes_per_level, max_nodes_per_level)
|
||||
# Change this so that its more weighted towards the lower end with rare occurances of max_nodes_per_level rather than an even split
|
||||
var num_nodes = get_weighted_node_count(min_nodes_per_level, max_nodes_per_level)
|
||||
var available_positions = []
|
||||
for pos in range(positions_per_level):
|
||||
available_positions.append(pos)
|
||||
|
|
@ -114,9 +117,9 @@ func generate_map():
|
|||
|
||||
# Connect to 1-2 nodes in next level if not the final level
|
||||
if level < num_levels - 2:
|
||||
var num_next_connections = _rng.randi_range(1, 2)
|
||||
var num_next_connections = _rng.randi_range(1, max_connections_per_node)
|
||||
num_next_connections = min(num_next_connections, next_level_nodes.size())
|
||||
|
||||
var shouldTrim = _rng.randi_range(1, 15) > 3 || num_next_connections > 3
|
||||
for i in range(num_next_connections):
|
||||
if i < next_level_nodes.size():
|
||||
connections.append({
|
||||
|
|
@ -124,10 +127,13 @@ func generate_map():
|
|||
"to": next_level_nodes[i].id
|
||||
})
|
||||
|
||||
# Remove the selected nodes so they aren't picked again
|
||||
for i in range(num_next_connections):
|
||||
if next_level_nodes.size() > 0:
|
||||
next_level_nodes.pop_front()
|
||||
# # Remove the selected nodes so they aren't picked again
|
||||
# if lots of ocnnection earlier and deeper than lvl 3
|
||||
# if num_next_connections >= 2 && level > 3:
|
||||
if shouldTrim:
|
||||
for i in range(num_next_connections):
|
||||
if next_level_nodes.size() > 0:
|
||||
next_level_nodes.pop_front()
|
||||
|
||||
# Connect to final boss if at the level before
|
||||
elif level == num_levels - 2:
|
||||
|
|
@ -192,3 +198,22 @@ func _get_next_id():
|
|||
var id = _next_id
|
||||
_next_id += 1
|
||||
return id
|
||||
|
||||
|
||||
|
||||
func get_weighted_node_count(min_count, max_count):
|
||||
# Use exponential distribution to weight toward lower values
|
||||
var range_size = max_count - min_count + 1
|
||||
|
||||
var rand_val = _rng.randf()
|
||||
|
||||
# Apply exponential weighting (higher exponent = more weight toward minimum)
|
||||
var weight_exponent = 2 # Adjust this to control the curve
|
||||
var weighted_val = pow(rand_val, weight_exponent)
|
||||
|
||||
var node_count = min_count + floor(weighted_val * range_size)
|
||||
|
||||
if rand_val > 0.70:
|
||||
node_count = max_count
|
||||
|
||||
return node_count
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ const LINE_COLOR = Color(0.2, 0.2, 0.2)
|
|||
const LINE_COLOR_SELECTED = Color(0.2, 0.6, 0.2)
|
||||
const LINE_COLOR_ACCESSIBLE = Color(0.6, 0.6, 0.2)
|
||||
|
||||
const NODE_COLOR = Color(0.1, 0.1, 0.1, 0.5)
|
||||
const NODE_LEAF = Color(0.2, 0.6, 0.2)
|
||||
# Node symbols and colors
|
||||
const NODE_SYMBOLS = {
|
||||
RoomType.STARTING: "O", # Circle
|
||||
|
|
@ -49,12 +51,11 @@ var node_buttons = {}
|
|||
var connection_lines = []
|
||||
var traversed_map = []
|
||||
var current_node = null
|
||||
var levels = 5
|
||||
|
||||
const SCROLL_PADDING_TOP = 80
|
||||
const SCROLL_PADDING_BOTTOM = 80
|
||||
const SCROLL_PADDING_LEFT = 60
|
||||
const SCROLL_PADDING_RIGHT = 60
|
||||
const SCROLL_PADDING_RIGHT = 100
|
||||
|
||||
|
||||
@onready var map_container = $MapScrollContainer/MapContainer
|
||||
|
|
@ -69,11 +70,7 @@ func _ready():
|
|||
|
||||
# Create legend
|
||||
create_legend()
|
||||
|
||||
# Generate default map
|
||||
generate_map()
|
||||
|
||||
# Display the map
|
||||
display_map()
|
||||
|
||||
func create_legend():
|
||||
|
|
@ -82,7 +79,10 @@ func create_legend():
|
|||
{"type": RoomType.BOSS, "text": "Boss"},
|
||||
{"type": RoomType.STARTING, "text": "Starting Room"},
|
||||
{"type": RoomType.FINAL, "text": "Final Boss"},
|
||||
{"type": RoomType.NORMAL, "text": "Normal Room"}
|
||||
{"type": RoomType.NORMAL, "text": "Normal Room"},
|
||||
{"type": RoomType.SHOP, "text": "Shop"},
|
||||
{"type": RoomType.EVENT, "text": "Event"},
|
||||
{"type": RoomType.NORMAL, "text": "Escape Room"}
|
||||
]
|
||||
|
||||
for item in legend_items:
|
||||
|
|
@ -91,7 +91,10 @@ func create_legend():
|
|||
|
||||
var symbol = Label.new()
|
||||
symbol.text = NODE_SYMBOLS[item.type]
|
||||
symbol.add_theme_color_override("font_color", NODE_COLORS[item.type])
|
||||
if item.text != "Escape Room":
|
||||
symbol.add_theme_color_override("font_color", NODE_COLORS[item.type])
|
||||
else:
|
||||
symbol.add_theme_color_override("font_color", NODE_LEAF)
|
||||
symbol.add_theme_font_size_override("font_size", 24)
|
||||
|
||||
var text = Label.new()
|
||||
|
|
@ -106,7 +109,6 @@ func generate_map():
|
|||
map_nodes.clear()
|
||||
map_connections.clear()
|
||||
var mapGen = MapGenerator.new().generate_map()
|
||||
print("MapGen ", mapGen)
|
||||
# Create starting node
|
||||
# var start_node = {
|
||||
# "id": 0,
|
||||
|
|
@ -228,6 +230,14 @@ func get_node_by_id(id):
|
|||
return null
|
||||
|
||||
func draw_node(node_data):
|
||||
var isLeaf = true;
|
||||
for connection in map_connections:
|
||||
# if theres outgoing connection we arent at a dead end
|
||||
# dont change final node colour
|
||||
if connection.from == node_data.id || node_data.id == 1:
|
||||
isLeaf = false
|
||||
break;
|
||||
|
||||
var button = Button.new()
|
||||
button.text = NODE_SYMBOLS[node_data.type]
|
||||
button.custom_minimum_size = NODE_SIZE
|
||||
|
|
@ -236,7 +246,7 @@ func draw_node(node_data):
|
|||
# Add some styling
|
||||
var font = FontFile.new()
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.1, 0.1, 0.1, 0.5)
|
||||
style.bg_color = NODE_COLOR
|
||||
style.corner_radius_top_left = 30
|
||||
style.corner_radius_top_right = 30
|
||||
style.corner_radius_bottom_left = 30
|
||||
|
|
@ -245,10 +255,17 @@ func draw_node(node_data):
|
|||
style.border_width_top = 2
|
||||
style.border_width_right = 2
|
||||
style.border_width_bottom = 2
|
||||
if isLeaf:
|
||||
style.bg_color = NODE_LEAF
|
||||
|
||||
style.border_color = NODE_COLORS[node_data.type]
|
||||
|
||||
button.add_theme_font_size_override("font_size", 28)
|
||||
button.add_theme_color_override("font_color", NODE_COLORS[node_data.type])
|
||||
if isLeaf:
|
||||
button.add_theme_color_override("font_color", NODE_LEAF)
|
||||
# style.bg_color = NODE_LEAF
|
||||
else:
|
||||
button.add_theme_color_override("font_color", NODE_COLORS[node_data.type])
|
||||
button.add_theme_stylebox_override("normal", style)
|
||||
|
||||
# Position the node with top and left padding adjustments
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ signal new_game_requested(options)
|
|||
signal shop_open_requested(options)
|
||||
signal deckmanager_open_requested(options)
|
||||
signal map_open_requested(options)
|
||||
signal shop_closed
|
||||
signal card_purchased(card, price)
|
||||
@onready var newGameButton = $HBoxContainer/VBoxContainer/MenuOptions/NewGameText
|
||||
@onready var continueButton = $HBoxContainer/VBoxContainer/MenuOptions/Continue
|
||||
@onready var optionsButton = $HBoxContainer/VBoxContainer/MenuOptions/Options
|
||||
|
|
@ -18,6 +20,10 @@ signal map_open_requested(options)
|
|||
@onready var deckManagerScreen = get_node("/root/Board/DeckManagerScreen")
|
||||
@onready var mapScreen = get_node("/root/Board/MapScreen")
|
||||
@onready var stateMachine = get_node("/root/Board/StateMachine")
|
||||
@onready var shopScreen = get_node("/root/Board/ShopScreen")
|
||||
|
||||
var player_gold = 1000
|
||||
|
||||
func _ready():
|
||||
# Connect menu option signals
|
||||
_connect_button(newGameButton, "_on_new_game_pressed")
|
||||
|
|
@ -39,6 +45,11 @@ func _ready():
|
|||
mapScreen.connect("back_pressed", Callable(self, "_on_map_back_pressed"))
|
||||
mapScreen.connect("node_selected", Callable(self, "_on_map_node_selected"))
|
||||
mapScreen.visible = false
|
||||
|
||||
if shopScreen:
|
||||
shopScreen.connect("back_pressed", Callable(self, "_on_shop_back_pressed"))
|
||||
shopScreen.connect("card_purchased", Callable(self, "_on_shop_card_purchased"))
|
||||
shopScreen.visible = false
|
||||
load_version()
|
||||
|
||||
|
||||
|
|
@ -105,8 +116,41 @@ func _on_options_pressed():
|
|||
# Game Menu Screen Signals
|
||||
func _on_shop_open_requested(options):
|
||||
print("Shop requested with options:", options)
|
||||
if gameMenuScreen:
|
||||
gameMenuScreen.visible = false
|
||||
|
||||
if options == null:
|
||||
options = {}
|
||||
options["gold"] = player_gold
|
||||
if shopScreen:
|
||||
shopScreen.visible = true
|
||||
shopScreen.initialize(options)
|
||||
emit_signal("shop_open_requested", options)
|
||||
|
||||
func _on_shop_back_pressed():
|
||||
if shopScreen:
|
||||
shopScreen.visible = false
|
||||
player_gold = shopScreen.player_gold
|
||||
|
||||
# Show game menu again
|
||||
if gameMenuScreen:
|
||||
gameMenuScreen.visible = true
|
||||
|
||||
# Emit signal that shop was closed
|
||||
emit_signal("shop_closed")
|
||||
|
||||
func _on_shop_card_purchased(card, price):
|
||||
player_gold -= price
|
||||
# Forward the signal
|
||||
emit_signal("card_purchased", card, price)
|
||||
|
||||
# Add the card to the player's bank
|
||||
var board = get_node_or_null("/root/Board") as ChessGame
|
||||
if board and "deckManager" in board:
|
||||
var deck_manager = board.deckManager
|
||||
deck_manager.bank.append(card)
|
||||
print("Added purchased card to bank:", card.cardName)
|
||||
|
||||
func _on_deckmanager_open_requested(options):
|
||||
print("Deck Manager requested with options:", options)
|
||||
|
||||
|
|
|
|||
137
Systems/Game/Shop/CardVisual.gd
Normal file
137
Systems/Game/Shop/CardVisual.gd
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# Add or modify this in your CardVisual.gd script
|
||||
|
||||
extends Control
|
||||
class_name CardVisual
|
||||
|
||||
signal pressed
|
||||
|
||||
# Card data
|
||||
var current_card = null
|
||||
var is_selected = false
|
||||
var price = 50
|
||||
|
||||
# Node references
|
||||
@onready var card_container = $CardContainer
|
||||
@onready var name_label = $CardContainer/VBoxContainer/NameLabel
|
||||
@onready var rank_label = $CardContainer/VBoxContainer/RankLabel
|
||||
@onready var price_label = $CardContainer/VBoxContainer/PriceLabel
|
||||
|
||||
# Card rank colors
|
||||
var rank_colors = {
|
||||
Card.Rank.RANK_0: Color(0.8, 0.2, 0.2, 1.0), # Red
|
||||
Card.Rank.RANK_1: Color(0.95, 0.6, 0.1, 1.0), # Orange
|
||||
Card.Rank.RANK_2: Color(0.2, 0.8, 0.2, 1.0), # Green
|
||||
Card.Rank.RANK_3: Color(0.2, 0.7, 0.95, 1.0) # Blue
|
||||
}
|
||||
|
||||
func _ready():
|
||||
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
gui_input.connect(_on_gui_input)
|
||||
if card_container:
|
||||
card_container.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
for child in get_children():
|
||||
if child is Control:
|
||||
child.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
|
||||
connect("mouse_entered", Callable(self, "_on_mouse_entered"))
|
||||
connect("mouse_exited", Callable(self, "_on_mouse_exited"))
|
||||
# gui_input.connect(_on_gui_input)
|
||||
|
||||
|
||||
|
||||
func set_card(card):
|
||||
current_card = card
|
||||
if card == null:
|
||||
card_container.visible = false
|
||||
return
|
||||
|
||||
card_container.visible = true
|
||||
|
||||
name_label.text = card.cardName
|
||||
|
||||
var rank_text = ""
|
||||
match card.rank:
|
||||
Card.Rank.RANK_0:
|
||||
rank_text = "Rank 0"
|
||||
Card.Rank.RANK_1:
|
||||
rank_text = "Rank 1"
|
||||
Card.Rank.RANK_2:
|
||||
rank_text = "Rank 2"
|
||||
Card.Rank.RANK_3:
|
||||
rank_text = "Rank 3"
|
||||
|
||||
rank_label.text = rank_text
|
||||
|
||||
# Set color based on rank
|
||||
if card.rank in rank_colors:
|
||||
var color = rank_colors[card.rank]
|
||||
name_label.add_theme_color_override("font_color", color)
|
||||
rank_label.add_theme_color_override("font_color", color)
|
||||
|
||||
var panel_style = card_container.get_theme_stylebox("panel").duplicate()
|
||||
var bg_color = Color(panel_style.bg_color)
|
||||
bg_color = bg_color.lerp(color, 0.05) # Very subtle tint
|
||||
panel_style.bg_color = bg_color
|
||||
card_container.add_theme_stylebox_override("panel", panel_style)
|
||||
|
||||
update_selected_state()
|
||||
|
||||
func set_price(new_price):
|
||||
price = new_price
|
||||
if price_label:
|
||||
price_label.text = str(price) + " gold"
|
||||
|
||||
func set_selected(selected):
|
||||
is_selected = selected
|
||||
update_selected_state()
|
||||
|
||||
func update_selected_state():
|
||||
if !card_container:
|
||||
return
|
||||
|
||||
if is_selected:
|
||||
card_container.modulate = Color(1.2, 1.2, 1.2) # Slightly brighter
|
||||
card_container.scale = Vector2(1.1, 1.1) # Slightly larger
|
||||
|
||||
# Yellow border
|
||||
var panel_style = card_container.get_theme_stylebox("panel").duplicate()
|
||||
panel_style.border_width_left = 3
|
||||
panel_style.border_width_top = 3
|
||||
panel_style.border_width_right = 3
|
||||
panel_style.border_width_bottom = 3
|
||||
panel_style.border_color = Color(1.0, 0.8, 0.0, 1.0) # Gold border
|
||||
card_container.add_theme_stylebox_override("panel", panel_style)
|
||||
else:
|
||||
# Normal appearance when not selected
|
||||
card_container.modulate = Color(1.0, 1.0, 1.0)
|
||||
card_container.scale = Vector2(1.0, 1.0)
|
||||
|
||||
# Remove border
|
||||
var panel_style = card_container.get_theme_stylebox("panel").duplicate()
|
||||
panel_style.border_width_left = 0
|
||||
panel_style.border_width_top = 0
|
||||
panel_style.border_width_right = 0
|
||||
panel_style.border_width_bottom = 0
|
||||
card_container.add_theme_stylebox_override("panel", panel_style)
|
||||
|
||||
func _on_gui_input(event):
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||
emit_signal("pressed")
|
||||
|
||||
var tween = create_tween()
|
||||
tween.tween_property(card_container, "scale", Vector2(1.05, 1.05), 0.1)
|
||||
tween.tween_property(card_container, "scale", Vector2(1.1 if is_selected else 1.0, 1.1 if is_selected else 1.0), 0.1)
|
||||
|
||||
func _on_mouse_entered():
|
||||
|
||||
if !is_selected:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(card_container, "scale", Vector2(1.05, 1.05), 0.1)
|
||||
|
||||
func _on_mouse_exited():
|
||||
|
||||
if !is_selected:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(card_container, "scale", Vector2(1.0, 1.0), 0.1)
|
||||
1
Systems/Game/Shop/CardVisual.gd.uid
Normal file
1
Systems/Game/Shop/CardVisual.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b5xl0bxh21vbi
|
||||
348
Systems/Game/Shop/ShopScreen.gd
Normal file
348
Systems/Game/Shop/ShopScreen.gd
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
extends Control
|
||||
class_name ShopScreen
|
||||
|
||||
signal back_pressed
|
||||
signal card_purchased(card, price)
|
||||
|
||||
# Node references
|
||||
@onready var card_carousel = $MainContainer/CardCarouselContainer/CardCarousel
|
||||
@onready var gold_label = $TopBar/GoldContainer/GoldLabel
|
||||
@onready var buy_button = $BuyButton
|
||||
@onready var back_button = $BackButton
|
||||
@onready var card_preview = $CardPreviewPanel
|
||||
@onready var left_button = $MainContainer/CardCarouselContainer/LeftButton
|
||||
@onready var right_button = $MainContainer/CardCarouselContainer/RightButton
|
||||
|
||||
# Shop data
|
||||
var available_cards = [] # Cards available in the shop
|
||||
var player_gold = 1000
|
||||
var selected_card = null
|
||||
var selected_index = 0
|
||||
var is_escape = false # Is this shop an escape option
|
||||
var carousel_page = 0
|
||||
var cards_per_page = 3
|
||||
var card_instance_map = {}
|
||||
var hovering_card_index = -1
|
||||
var mouse_over_any_card = false
|
||||
# Card prices by rank
|
||||
var card_prices = {
|
||||
Card.Rank.RANK_0: 200, # Legendary (Rank 0)
|
||||
Card.Rank.RANK_1: 100, # Rare (Rank 1)
|
||||
Card.Rank.RANK_2: 50, # Uncommon (Rank 2)
|
||||
Card.Rank.RANK_3: 25 # Common (Rank 3)
|
||||
}
|
||||
|
||||
func _ready():
|
||||
if back_button:
|
||||
if back_button.is_connected("pressed", Callable(self, "_on_back_button_pressed")):
|
||||
back_button.disconnect("pressed", Callable(self, "_on_back_button_pressed"))
|
||||
back_button.connect("pressed", Callable(self, "_on_back_button_pressed"))
|
||||
|
||||
if buy_button:
|
||||
if buy_button.is_connected("pressed", Callable(self, "_on_buy_button_pressed")):
|
||||
buy_button.disconnect("pressed", Callable(self, "_on_buy_button_pressed"))
|
||||
buy_button.connect("pressed", Callable(self, "_on_buy_button_pressed"))
|
||||
|
||||
# Use direct connection for navigation buttons
|
||||
# This doesnt work for some reason use input below
|
||||
if left_button:
|
||||
if left_button.is_connected("pressed", Callable(self, "_on_left_button_pressed")):
|
||||
left_button.disconnect("pressed", Callable(self, "_on_left_button_pressed"))
|
||||
left_button.pressed.connect(_on_left_button_pressed)
|
||||
|
||||
if right_button:
|
||||
if right_button.is_connected("pressed", Callable(self, "_on_right_button_pressed")):
|
||||
right_button.disconnect("pressed", Callable(self, "_on_right_button_pressed"))
|
||||
right_button.pressed.connect(_on_right_button_pressed)
|
||||
|
||||
# Initialize with empty shop
|
||||
if card_preview:
|
||||
card_preview.visible = false
|
||||
|
||||
update_gold_display()
|
||||
update_buy_button()
|
||||
update_navigation_buttons()
|
||||
|
||||
func initialize(options = null):
|
||||
carousel_page = 0
|
||||
selected_index = 0
|
||||
|
||||
# Process options if provided
|
||||
if options:
|
||||
if options.has("gold") and options.gold is int:
|
||||
player_gold = options.gold
|
||||
if options.has("cards") and options.cards is Array:
|
||||
available_cards = options.cards.duplicate()
|
||||
if options.has("is_escape") and options.is_escape is bool:
|
||||
is_escape = options.is_escape
|
||||
# Apply escape markup (higher prices)
|
||||
if is_escape:
|
||||
for rank in card_prices:
|
||||
card_prices[rank] = int(card_prices[rank] * 1.5)
|
||||
|
||||
# Find the DeckManager instance if cards not provided in options
|
||||
if available_cards.is_empty():
|
||||
var board = get_node_or_null("/root/Board") as ChessGame
|
||||
if board and "deckManager" in board:
|
||||
var deck_manager = board.deckManager
|
||||
available_cards = generate_shop_cards(deck_manager)
|
||||
|
||||
# Update display
|
||||
update_gold_display()
|
||||
populate_carousel()
|
||||
update_navigation_buttons()
|
||||
|
||||
if not available_cards.is_empty():
|
||||
select_card(0) # Select the first card by default
|
||||
|
||||
func generate_shop_cards(deck_manager):
|
||||
var shop_cards = []
|
||||
|
||||
var all_cards = []
|
||||
var card_classes = [
|
||||
HopscotchCard,
|
||||
FieryCapeCard,
|
||||
FieryTrailCard,
|
||||
ExplosiveBootsCard,
|
||||
DoubleTimeCard,
|
||||
DrunkDrivingCard,
|
||||
SupernovaCard
|
||||
]
|
||||
|
||||
for card_class in card_classes:
|
||||
var card = card_class.new()
|
||||
all_cards.append(card)
|
||||
|
||||
all_cards.shuffle()
|
||||
|
||||
var num_shop_cards = min(randi_range(5, 7), all_cards.size())
|
||||
|
||||
for i in range(num_shop_cards):
|
||||
shop_cards.append(all_cards[i % card_classes.size()])
|
||||
|
||||
return shop_cards
|
||||
|
||||
func populate_carousel():
|
||||
if card_carousel:
|
||||
for child in card_carousel.get_children():
|
||||
child.queue_free()
|
||||
|
||||
card_instance_map.clear()
|
||||
|
||||
var start_index = carousel_page * cards_per_page
|
||||
var end_index = min(start_index + cards_per_page, available_cards.size())
|
||||
|
||||
card_carousel.add_theme_constant_override("separation", 20)
|
||||
|
||||
for i in range(start_index, end_index):
|
||||
var card = available_cards[i]
|
||||
var card_visual = preload("res://card_visual.tscn").instantiate()
|
||||
|
||||
card_carousel.add_child(card_visual)
|
||||
card_visual.set_card(card)
|
||||
|
||||
var price = get_card_price(card)
|
||||
card_visual.set_price(price)
|
||||
|
||||
var instance_id = card_visual.get_instance_id()
|
||||
|
||||
card_instance_map[instance_id] = {
|
||||
"card_index": i,
|
||||
"card": card
|
||||
}
|
||||
|
||||
update_navigation_buttons()
|
||||
|
||||
|
||||
func update_navigation_buttons():
|
||||
if left_button:
|
||||
left_button.disabled = (carousel_page <= 0)
|
||||
|
||||
if right_button:
|
||||
var max_page = ceil(float(available_cards.size()) / cards_per_page) - 1
|
||||
right_button.disabled = (carousel_page >= max_page or available_cards.size() <= cards_per_page)
|
||||
|
||||
|
||||
func select_card(index):
|
||||
if index < 0 or index >= available_cards.size():
|
||||
return
|
||||
|
||||
var page_for_index = int(index / cards_per_page)
|
||||
if page_for_index != carousel_page:
|
||||
carousel_page = page_for_index
|
||||
populate_carousel()
|
||||
|
||||
selected_index = index
|
||||
selected_card = available_cards[index]
|
||||
|
||||
var visual_index = index % cards_per_page
|
||||
|
||||
for i in range(card_carousel.get_child_count()):
|
||||
var card_visual = card_carousel.get_child(i)
|
||||
print(i ," vi ", visual_index, " Ind ", index, " cp ", cards_per_page)
|
||||
card_visual.set_selected(i == visual_index)
|
||||
|
||||
if card_preview and selected_card:
|
||||
card_preview.preview_card(selected_card)
|
||||
card_preview.visible = true
|
||||
|
||||
update_buy_button()
|
||||
|
||||
func update_buy_button():
|
||||
if not buy_button or not selected_card:
|
||||
return
|
||||
|
||||
var price = get_card_price(selected_card)
|
||||
buy_button.text = "BUY (" + str(price) + " gold)"
|
||||
|
||||
buy_button.disabled = price > player_gold
|
||||
|
||||
func get_card_price(card):
|
||||
if not card:
|
||||
return 0
|
||||
if card.rank in card_prices:
|
||||
return card_prices[card.rank]
|
||||
return 50
|
||||
|
||||
func update_gold_display():
|
||||
if gold_label:
|
||||
gold_label.text = str(player_gold) + " GOLD"
|
||||
|
||||
func purchase_selected_card():
|
||||
if not selected_card:
|
||||
return false
|
||||
|
||||
var price = get_card_price(selected_card)
|
||||
|
||||
if player_gold < price:
|
||||
# Show not enough gold message
|
||||
print("Not enough gold")
|
||||
return false
|
||||
|
||||
player_gold -= price
|
||||
var purchased_card = selected_card.duplicate()
|
||||
var old_page = carousel_page
|
||||
available_cards.remove_at(selected_index)
|
||||
emit_signal("card_purchased", purchased_card, price)
|
||||
|
||||
update_gold_display()
|
||||
|
||||
var max_page = max(0, ceil(float(available_cards.size()) / cards_per_page) - 1)
|
||||
|
||||
if carousel_page > max_page:
|
||||
carousel_page = max_page
|
||||
|
||||
populate_carousel()
|
||||
|
||||
if not available_cards.is_empty():
|
||||
var new_index = min(selected_index, available_cards.size() - 1)
|
||||
select_card(new_index)
|
||||
else:
|
||||
selected_card = null
|
||||
selected_index = -1
|
||||
if card_preview:
|
||||
card_preview.hide_preview()
|
||||
|
||||
buy_button.disabled = true
|
||||
|
||||
update_buy_button()
|
||||
return true
|
||||
|
||||
# Signal handlers
|
||||
func _on_back_button_pressed():
|
||||
emit_signal("back_pressed")
|
||||
visible = false
|
||||
|
||||
func _on_buy_button_pressed():
|
||||
purchase_selected_card()
|
||||
|
||||
func _input(event):
|
||||
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
# Check if click is within buttons
|
||||
if left_button and left_button.get_global_rect().has_point(event.position) and not left_button.disabled:
|
||||
# print("Left button clicked via _input")
|
||||
_on_left_button_pressed()
|
||||
get_viewport().set_input_as_handled()
|
||||
elif right_button and right_button.get_global_rect().has_point(event.position) and not right_button.disabled:
|
||||
# print("Right button clicked via _input")
|
||||
_on_right_button_pressed()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _on_left_button_pressed():
|
||||
if carousel_page > 0:
|
||||
carousel_page -= 1
|
||||
var new_index = carousel_page * cards_per_page
|
||||
populate_carousel()
|
||||
select_card(new_index)
|
||||
|
||||
|
||||
func _on_right_button_pressed():
|
||||
var max_page = ceil(float(available_cards.size()) / cards_per_page) - 1
|
||||
if carousel_page < max_page:
|
||||
carousel_page += 1
|
||||
var new_index = carousel_page * cards_per_page
|
||||
populate_carousel()
|
||||
select_card(new_index)
|
||||
|
||||
|
||||
func _on_card_visual_pressed(index):
|
||||
select_card(index)
|
||||
|
||||
func _on_card_visual_hover(card, index):
|
||||
if card_preview and card:
|
||||
card_preview.preview_card(card)
|
||||
card_preview.visible = true
|
||||
select_card(index)
|
||||
|
||||
func _on_card_visual_exit():
|
||||
|
||||
if card_preview and selected_card:
|
||||
card_preview.preview_card(selected_card)
|
||||
elif card_preview:
|
||||
card_preview.hide_preview()
|
||||
|
||||
func _process(delta):
|
||||
if not visible:
|
||||
return
|
||||
|
||||
var mouse_pos = get_viewport().get_mouse_position()
|
||||
|
||||
# Reset tracking
|
||||
var old_hovering_index = hovering_card_index
|
||||
hovering_card_index = -1
|
||||
mouse_over_any_card = false
|
||||
var mouse_clicked = Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)
|
||||
|
||||
for i in range(card_carousel.get_child_count()):
|
||||
var card_visual = card_carousel.get_child(i)
|
||||
var card_rect = card_visual.get_global_rect()
|
||||
|
||||
if card_rect.has_point(mouse_pos) and mouse_clicked:
|
||||
# Get data from instance map
|
||||
var instance_id = card_visual.get_instance_id()
|
||||
if card_instance_map.has(instance_id):
|
||||
var data = card_instance_map[instance_id]
|
||||
hovering_card_index = data.card_index
|
||||
mouse_over_any_card = true
|
||||
|
||||
# If we just started hovering this card
|
||||
if old_hovering_index != hovering_card_index:
|
||||
_on_card_visual_hover(data.card, data.card_index)
|
||||
|
||||
# Also trigger the card's own hover effect
|
||||
card_visual._on_mouse_entered()
|
||||
|
||||
break
|
||||
|
||||
# If we were hovering a card before but not now, trigger exit
|
||||
if old_hovering_index != -1 and hovering_card_index == -1:
|
||||
_on_card_visual_exit()
|
||||
|
||||
# Find the previously hovered card by index
|
||||
for i in range(card_carousel.get_child_count()):
|
||||
var card_visual = card_carousel.get_child(i)
|
||||
var instance_id = card_visual.get_instance_id()
|
||||
if card_instance_map.has(instance_id) and card_instance_map[instance_id].card_index == old_hovering_index:
|
||||
card_visual._on_mouse_exited()
|
||||
break
|
||||
1
Systems/Game/Shop/ShopScreen.gd.uid
Normal file
1
Systems/Game/Shop/ShopScreen.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://rqkucvk8au68
|
||||
12
board.tscn
12
board.tscn
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=30 format=3 uid="uid://d0qyk6v20uief"]
|
||||
[gd_scene load_steps=31 format=3 uid="uid://d0qyk6v20uief"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cbcu68o863pfp" path="res://Systems/Game/ChessGame.gd" id="1_fkb2r"]
|
||||
[ext_resource type="Script" uid="uid://d2bfw6edgkhfa" path="res://Systems/StateMachine/GameStates/WhiteTurn.gd" id="3_276ip"]
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
[ext_resource type="Script" uid="uid://bfjmon81nckns" path="res://Systems/Game/GameMenuButton.gd" id="26_t2e38"]
|
||||
[ext_resource type="PackedScene" uid="uid://c7uqbcxdjoais" path="res://deck_manager_screen.tscn" id="28_4nyv8"]
|
||||
[ext_resource type="PackedScene" uid="uid://dxiw67f3rrwue" path="res://map_screen.tscn" id="29_y7cv2"]
|
||||
[ext_resource type="PackedScene" uid="uid://djw7jhwtnycxq" path="res://shop_screen.tscn" id="30_5rfmq"]
|
||||
|
||||
[node name="Board" type="Control"]
|
||||
layout_mode = 3
|
||||
|
|
@ -393,3 +394,12 @@ layout_mode = 1
|
|||
[node name="MapScreen" parent="." instance=ExtResource("29_y7cv2")]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
|
||||
[node name="ShopScreen" parent="." instance=ExtResource("30_5rfmq")]
|
||||
layout_mode = 1
|
||||
|
||||
[node name="MapContainer" type="Control" parent="ShopScreen"]
|
||||
clip_contents = true
|
||||
custom_minimum_size = Vector2(800, 600)
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
|
|
|
|||
67
card_visual.tscn
Normal file
67
card_visual.tscn
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://bpfuysambx702"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b5xl0bxh21vbi" path="res://Systems/Game/Shop/CardVisual.gd" id="1_nbt2g"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mkr1p"]
|
||||
bg_color = Color(0.14902, 0.14902, 0.188235, 1)
|
||||
border_color = Color(1, 0.8, 0, 1)
|
||||
corner_radius_top_left = 10
|
||||
corner_radius_top_right = 10
|
||||
corner_radius_bottom_right = 10
|
||||
corner_radius_bottom_left = 10
|
||||
|
||||
[node name="CardVisual" type="Control"]
|
||||
custom_minimum_size = Vector2(150, 250)
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
script = ExtResource("1_nbt2g")
|
||||
|
||||
[node name="CardContainer" type="Panel" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
pivot_offset = Vector2(75, 125)
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_mkr1p")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CardContainer"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 10.0
|
||||
offset_top = 10.0
|
||||
offset_right = -10.0
|
||||
offset_bottom = -10.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="NameLabel" type="Label" parent="CardContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "Card Name"
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="CardContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RankLabel" type="Label" parent="CardContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Rank 3"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Spacer" type="Control" parent="CardContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="PriceLabel" type="Label" parent="CardContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "25 gold"
|
||||
horizontal_alignment = 1
|
||||
|
|
@ -63,7 +63,6 @@ custom_minimum_size = Vector2(800, 600)
|
|||
layout_mode = 2
|
||||
|
||||
[node name="LegendContainer" type="VBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
|
|
|
|||
152
shop_screen.tscn
Normal file
152
shop_screen.tscn
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://djw7jhwtnycxq"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://rqkucvk8au68" path="res://Systems/Game/Shop/ShopScreen.gd" id="1_aoepi"]
|
||||
[ext_resource type="Script" uid="uid://bfjmon81nckns" path="res://Systems/Game/GameMenuButton.gd" id="2_mkl0p"]
|
||||
[ext_resource type="PackedScene" uid="uid://xxxxxxxx" path="res://card_preview_panel.tscn" id="3_yh3xu"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jk2fb"]
|
||||
bg_color = Color(0.14902, 0.14902, 0.2, 1)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[node name="ShopScreen" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_aoepi")
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.08, 0.08, 0.12, 1)
|
||||
|
||||
[node name="TopBar" type="HBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_top = 20.0
|
||||
offset_bottom = 80.0
|
||||
grow_horizontal = 2
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="TopBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_font_sizes/font_size = 36
|
||||
text = "SHOP"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="GoldContainer" type="HBoxContainer" parent="TopBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
alignment = 2
|
||||
|
||||
[node name="GoldLabel" type="Label" parent="TopBar/GoldContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "100 GOLD"
|
||||
|
||||
[node name="Spacer" type="Control" parent="TopBar/GoldContainer"]
|
||||
custom_minimum_size = Vector2(20, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CardPreviewPanel" parent="." instance=ExtResource("3_yh3xu")]
|
||||
layout_mode = 1
|
||||
anchors_preset = 4
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 20.0
|
||||
offset_top = -200.0
|
||||
offset_right = 320.0
|
||||
offset_bottom = 200.0
|
||||
grow_horizontal = 1
|
||||
|
||||
[node name="MainContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 340.0
|
||||
offset_top = 100.0
|
||||
offset_right = -20.0
|
||||
offset_bottom = -100.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="CardCarouselContainer" type="HBoxContainer" parent="MainContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
alignment = 1
|
||||
|
||||
[node name="LeftButton" type="Button" parent="MainContainer/CardCarouselContainer"]
|
||||
custom_minimum_size = Vector2(40, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "←"
|
||||
|
||||
[node name="CardCarousel" type="HBoxContainer" parent="MainContainer/CardCarouselContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 20
|
||||
alignment = 1
|
||||
|
||||
[node name="RightButton" type="Button" parent="MainContainer/CardCarouselContainer"]
|
||||
custom_minimum_size = Vector2(40, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "→"
|
||||
|
||||
[node name="BackButton" type="RichTextLabel" parent="."]
|
||||
custom_minimum_size = Vector2(100, 40)
|
||||
layout_mode = 2
|
||||
offset_top = 578.0
|
||||
offset_right = 100.0
|
||||
offset_bottom = 618.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 4
|
||||
text = "BACK"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 2
|
||||
script = ExtResource("2_mkl0p")
|
||||
|
||||
[node name="BuyButton" type="Button" parent="."]
|
||||
custom_minimum_size = Vector2(200, 50)
|
||||
layout_mode = 2
|
||||
offset_left = 952.0
|
||||
offset_top = 573.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 623.0
|
||||
size_flags_horizontal = 8
|
||||
size_flags_vertical = 4
|
||||
theme_override_font_sizes/font_size = 18
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_jk2fb")
|
||||
text = "BUY (50 gold)"
|
||||
|
||||
[node name="BottomBar" type="HBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -80.0
|
||||
offset_bottom = -20.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
alignment = 1
|
||||
|
||||
[node name="Spacer" type="Control" parent="BottomBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
Loading…
Reference in a new issue