Compare commits

..

No commits in common. "master" and "delete_node_fish" have entirely different histories.

174 changed files with 1235 additions and 10545 deletions

1
.gitignore vendored
View file

@ -7,7 +7,6 @@
# Chess engine binaries and their subdirectories
Assets/ChessEngines/stockfish/*
Assets/ChessEngines/Fairy-Stockfish/*
Assets/ChessEngines/quack/*
# Assets/ChessEngines/stockfish/**
# Assets/ChessEngines/Fairy-Stockfish/**

View file

@ -29,22 +29,6 @@ class ChessEngine extends EventEmitter {
this.engine.on('close', (code) => {
console.log('Engine process closed with code:', code);
this.isReady = false;
// console.log('Attempting to restart engine...');
// // Add a small delay before restarting to avoid rapid restart loops
// setTimeout(() => {
// this.start()
// .then(() => {
// // Restore previous state if needed
// if (this.currentFen) {
// this.setPosition(this.currentFen);
// }
// this.emit('restarted', { code });
// })
// .catch(err => {
// console.error('Failed to restart engine:', err);
// this.emit('restart-failed', { error: err });
// });
// }, 1000);
});
// Initialize engine
@ -124,15 +108,7 @@ class ChessEngine extends EventEmitter {
this.engine.stdin.write(cmd + '\n');
}
}
async startPos() {
if (!this.isReady) throw new Error('Engine not ready');
this.sendCommand(`position startpos`);
// Ensure engine is ready after position set
this.sendCommand('isready');
await new Promise(resolve => this.once('ready', resolve));
}
async setBoardPosition(fen) {
if (!this.isReady) throw new Error('Engine not ready');
@ -144,16 +120,6 @@ class ChessEngine extends EventEmitter {
await new Promise(resolve => this.once('ready', resolve));
}
async setNewGame() {
// if (!this.isReady) throw new Error('Engine not ready');
this.sendCommand(`ucinewgame`);
// Ensure engine is ready after position set
this.sendCommand('isready');
await new Promise(resolve => this.once('ready', resolve));
}
async getBestMove(options = {}) {
if (!this.isReady) throw new Error('Engine not ready');
if (!this.currentFen) throw new Error('Position not set');
@ -186,11 +152,6 @@ class ChessEngine extends EventEmitter {
return { bestMove };
}
async createNewGame(){
await this.setNewGame();
return true
}
quit() {
if (this.engine) {
this.sendCommand('quit');

View file

@ -65,10 +65,11 @@ try {
const app = express();
const port = 27531;
let board = null;
let engine = null;
let isReady = false;
let lastResponse = null
const SERVER_WAIT_THRESHOLD = 1 * 60 * 1000;
const SERVER_WAIT_THRESHOLD = 2 * 60 * 1000;
const CHECK_INTERVAL = 5000;
// Initialize ffish and engine
@ -93,7 +94,6 @@ ffish.onRuntimeInitialized = async () => {
engine.sendCommand('setoption name Hash value 128');
engine.sendCommand('setoption name MultiPV value 1');
engine.sendCommand('setoption name UCI_LimitStrength value true');
engine.sendCommand('uci');
}
} catch (error) {
console.log('Initialization error:', error);
@ -166,12 +166,12 @@ app.post('/validate', (req, res) => {
}
try {
// Create temporary board to validate FEN
const tempBoard = new ffish.Board(variant);
const isValid = tempBoard.setFen(fen);
tempBoard.delete();
res.json({
status: 'ok',
isValid,
startingFen: ffish.startingFen(variant)
});
@ -181,15 +181,25 @@ app.post('/validate', (req, res) => {
});
// New game endpoint
app.post('/new', async (req, res) => {
app.post('/new', (req, res) => {
lastResponse = new Date().getTime()
const { variant = 'chess' } = req.body;
try {
if (board) {
board.delete();
}
board = new ffish.Board(variant);
const engineAnalysis = await engine.createNewGame();
res.json({
status: 'ok',
fen: board.fen(),
legalMoves: board.legalMoves().split(' '),
legalMovesSan: board.legalMovesSan().split(' '),
isCheck: board.isCheck(),
turn: board.turn(),
moveStack: board.moveStack()
});
} catch (error) {
res.status(500).json({ error: error.message });
@ -197,28 +207,44 @@ app.post('/new', async (req, res) => {
});
// Set position endpoint
app.post('/position', async(req, res) => {
app.post('/position', (req, res) => {
lastResponse = new Date().getTime()
const { fen, variant = 'chess', start } = req.body;
const { fen, variant = 'chess' } = req.body;
if (!fen) {
return res.status(400).json({ error: 'FEN string required' });
}
try {
//we have a lot of funky rules lets not validate
// const isValid = ffish.validateFen(fen) == 1
if(start){
await engine.startPos()
engine.sendCommand('d');
}else if(fen){
await engine.setBoardPosition(fen)
}
// if (!isValid) {
// return res.status(400).json({ error: 'Invalid FEN string',
// fen,
// variant,
// boardExists: !!board, reqBody: JSON.stringify(req.body) });
// }else {
// // if (board) {
// // board.delete();
// // }
// board.setFen(fen);
// }
board.setFen(fen);
res.json({
status: 'ok',
msg: "Set Position",
legalMoves: board.legalMoves().split(' '),
legalMovesSan: board.legalMovesSan().split(' '),
isCheck: board.isCheck(),
isGameOver: board.isGameOver(),
result: board.result(),
turn: board.turn(),
moveStack: board.moveStack()
});
} catch (error) {
res.status(500).json({ error: error.message, poserr: true });
res.status(500).json({ error: error.message });
}
});
@ -227,16 +253,49 @@ app.post('/move', (req, res) => {
lastResponse = new Date().getTime()
const { move, notation = 'uci', variant = 'chess' } = req.body;
if (!board) {
return res.status(400).json({ error: 'No active board' });
}
if (!move) {
return res.status(400).json({ error: 'Move required' });
}
if (board) {
board.delete();
}
board = new ffish.Board(variant);
try {
if (notation === 'san') {
board.pushSan(move);
} else {
board.push(move);
}
const response = {
status: 'ok',
fen: board.fen(),
legalMoves: board.legalMoves().split(' '),
legalMovesSan: board.legalMovesSan().split(' '),
isCheck: board.isCheck(),
isGameOver: board.isGameOver(),
result: board.result(),
turn: board.turn(),
moveStack: board.moveStack(),
fullmoveNumber: board.fullmoveNumber(),
halfmoveClock: board.halfmoveClock()
};
if (board.isGameOver()) {
response.hasInsufficientMaterial = {
white: board.hasInsufficientMaterial(true), // true for white
black: board.hasInsufficientMaterial(false) // false for black
};
response.gameResult = board.result();
}
res.json(response);
} catch (error) {
res.status(500).json({ error: error.message });
@ -246,54 +305,58 @@ app.post('/move', (req, res) => {
// State endpoint
app.get('/state', (req, res) => {
lastResponse = new Date().getTime()
if (!board) {
return res.status(400).json({ error: 'No active board' });
}
try {
res.json({
status: 'ok',
fen: board.fen(),
legalMoves: board.legalMoves().split(' '),
legalMovesSan: board.legalMovesSan().split(' '),
isCheck: board.isCheck(),
isGameOver: board.isGameOver(),
result: board.result(),
turn: board.turn(),
moveStack: board.moveStack(),
fullmoveNumber: board.fullmoveNumber(),
halfmoveClock: board.halfmoveClock(),
variant: board.variant(),
is960: board.is960()
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
/*
createNewGame
*/
app.post('/newgame', async (req, res) => {
lastResponse = new Date().getTime()
try {
const response = {
status: 'ok',
};
// If engine is available, get engine analysis
if (engine && engine.isReady) {
const engineAnalysis = await engine.createNewGame();
}
res.json(response);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Analysis endpoint
app.post('/analyze', async (req, res) => {
lastResponse = new Date().getTime()
if (!board) {
return res.status(400).json({ error: 'No active board' });
}
try {
const { depth = 15, movetime = 1000 } = req.body;
// Get basic position analysis
const positionAnalysis = {
status: 'ok',
fen: board.fen(),
isCheck: board.isCheck(),
isGameOver: board.isGameOver(),
result: board.result(),
hasInsufficientMaterial: {
white: board.hasInsufficientMaterial(true), // true for white
black: board.hasInsufficientMaterial(false) // false for black
},
legalMoves: board.legalMoves().split(' '),
legalMovesSan: board.legalMovesSan().split(' '),
moveStack: board.moveStack()
};
// If engine is available, get engine analysis
if (engine && engine.isReady) {
const engineAnalysis = await engine.getAnalysis("", {
const engineAnalysis = await engine.getAnalysis(board.fen(), {
depth,
movetime
});
@ -309,6 +372,9 @@ app.post('/analyze', async (req, res) => {
// Engine move endpoint
app.post('/enginemove', async (req, res) => {
lastResponse = new Date().getTime()
if (!board) {
return res.status(400).json({ error: 'No active board' });
}
if (!engine || !engine.isReady) {
return res.status(503).json({
@ -325,17 +391,26 @@ app.post('/enginemove', async (req, res) => {
} = req.body;
try {
// const fen = board.fen();
const analysis = await engine.getAnalysis(fen, {
depth,
movetime,
nodes
});
if (analysis.bestMove) {
board.push(analysis.bestMove);
}
res.json({
status: 'ok',
move: analysis.bestMove,
analysis: analysis,
fen: board.fen(),
legalMoves: board.legalMoves().split(' '),
isCheck: board.isCheck(),
isGameOver: board.isGameOver(),
turn: board.turn()
});
} catch (error) {
res.status(500).json({
@ -403,6 +478,7 @@ function startIdleMonitor() {
}
function closeServer(){
if (board) board.delete();
if (engine) engine.quit();
process.exit(0);
}

View file

@ -1,87 +0,0 @@
import fs from 'fs'
const pieceSetups = [
"nk", // 2 cells
"nkr", // 3 cells
"nkbr", // 4 cells
"rnkbr", // 5 cells
"rbnkqr", // 6 cells
"rnbkqnr", // 7 cells
"rnbqkbnr", // 8 cells (standard chess)
"rbnqknbnr", // 9 cells
"rnbnqknbnr", // 10 cells
"rnbnqknbnnr", // 11 cells
"rnnbnqknbnnr" // 12 cells
];
const files = "abcdefghijkl";
function generateVariant(width, height) {
if (width < 2 || width > 12) {
console.error("Width must be between 2 and 12");
return "";
}
if (height < 6 || height > 12) {
console.error("Height must be between 6 and 12");
return "";
}
const pieceSetup = pieceSetups[width - 2];
const emptyRanks = height - 4;
const whitePieces = pieceSetup.toUpperCase();
const blackPieces = pieceSetup.toLowerCase();
const whitePawns = "P".repeat(width);
const blackPawns = "p".repeat(width);
const emptyRank = width.toString();
let fenParts = [blackPieces, blackPawns];
for (let i = 0; i < emptyRanks; i++) {
fenParts.push(emptyRank);
}
fenParts.push(whitePawns, whitePieces);
const fen = fenParts.join("/") + " w - - 0 1";
const maxFile = files[width - 1];
return `[chessbuilder${width}x${height}:chess]
pieceToCharTable = PNBRQK..*@...........pnbrqk..*@...........
maxRank = ${height}
maxFile = ${maxFile}
startFen = ${fen}
walltype = * # Duck wall
walltype = @ # Stone wall
wallingRule = static
wallOrMove = false
mobilityRegion = *:@
prohibitedMove = *@
`;
}
function generateAllVariants() {
let allVariants = "";
let count = 0;
for (let height = 6; height <= 12; height++) {
for (let width = 2; width <= 12; width++) {
const variant = generateVariant(width, height);
allVariants += variant + "\n";
count++;
}
}
fs.writeFileSync('chessbuilder_variants.ini', allVariants);
console.log(`Generated ${count} variants and saved to chessbuilder_variants.ini`);
return allVariants;
}
generateAllVariants();

View file

@ -1,4 +0,0 @@
[gd_resource type="Theme" format=3 uid="uid://btgbiqdc4kf15"]
[resource]
default_font_size = 49

View file

@ -1,4 +0,0 @@
[gd_resource type="Theme" format=3 uid="uid://cuq0xndnachqb"]
[resource]
default_font_size = 80

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bexpni52h8527"
path="res://.godot/imported/characters.png-aa4b087f4ba916c3768cf8d9d020e5a0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/main_menu/characters.png"
dest_files=["res://.godot/imported/characters.png-aa4b087f4ba916c3768cf8d9d020e5a0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bdb3grojgjtaq"
path="res://.godot/imported/icon.png-a1f010172d24ada42d71428b39fa8e05.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/main_menu/icon.png"
dest_files=["res://.godot/imported/icon.png-a1f010172d24ada42d71428b39fa8e05.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bn0offg4w11w4"
path="res://.godot/imported/label_continue.png-73ecf71c03ce72f5729846d86d279818.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/main_menu/label_continue.png"
dest_files=["res://.godot/imported/label_continue.png-73ecf71c03ce72f5729846d86d279818.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b8khh5b1iwic1"
path="res://.godot/imported/label_options.png-19b15ef319e033f5146c10542dd5f109.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/main_menu/label_options.png"
dest_files=["res://.godot/imported/label_options.png-19b15ef319e033f5146c10542dd5f109.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -1,6 +0,0 @@
{
"version": "0.0.1",
"title": "ChessBuilder",
"developer": "Comet Studios",
"release_date": "2025-01-15"
}

View file

@ -21,13 +21,11 @@ var unitWhitelist: Array[String] = [] # List of piece types this card can be at
var id: String = Utils.generate_guid()
var stored_board_flow = null
var stored_game_state = null
var is_default: bool = false
func _init():
remaining_turns = duration
# print(id)
func reset():
remaining_turns = duration
func can_attach_to_piece(piece: Pawn) -> bool:
# print(unitWhitelist, " | ", piece.name , " | ", unitWhitelist.has(piece.name))
if unitWhitelist.is_empty():

View file

@ -1 +0,0 @@
uid://eqadab7yd0qq

View file

@ -1,98 +1,65 @@
class_name CardDisplay extends Control
# Base card dimensions
const BASE_CARD_WIDTH = 150
const BASE_CARD_HEIGHT = 250
const CARD_WIDTH = 150
const CARD_HEIGHT = 250
const CARD_MARGIN = 10
const MAX_HAND_WIDTH_RATIO = 0.7 # Maximum portion of screen width to use for hand
var cardDisplays = [] # Array of {panel: Node, card: Card}
var cardDisplays = []
var selectedCard = null
var container: HBoxContainer
var cardPreviewScene = preload("res://card_preview_panel.tscn")
var currently_hovered_card = null
# Preview card panel instance for hovering
var hoveredPreview: CardPreviewPanel = null
var isPreviewVisible = false
func _ready():
# Create the container for the hand
# Create the container first
container = HBoxContainer.new()
container.name = "Hand"
container.position = Vector2(10, 500)
container.size_flags_horizontal = SIZE_EXPAND_FILL
add_child(container)
# Create a hover preview panel that will follow the mouse
hoveredPreview = cardPreviewScene.instantiate()
hoveredPreview.visible = false
add_child(hoveredPreview)
func update_hand(hand: Array):
clear_cards()
# Calculate card sizes based on hand size and screen width
var card_size = calculate_card_size(hand.size())
for card in hand:
add_card_display(card, card_size)
add_card_display(card)
func calculate_card_size(hand_size: int) -> Vector2:
# Get screen dimensions
var screen_width = get_viewport_rect().size.x
var screen_height = get_viewport_rect().size.y
func add_card_display(card):
var cardPanel = PanelContainer.new()
cardPanel.custom_minimum_size = Vector2(CARD_WIDTH, CARD_HEIGHT)
var max_hand_width = screen_width * MAX_HAND_WIDTH_RATIO
var width_per_card = max_hand_width / max(hand_size, 1)
var vbox = VBoxContainer.new()
vbox.set_meta("card_id", card.id)
cardPanel.add_child(vbox)
# Ensure minimum size and preserve aspect ratio
var card_width = clamp(width_per_card - CARD_MARGIN, 80, BASE_CARD_WIDTH)
var card_height = card_width * (BASE_CARD_HEIGHT / BASE_CARD_WIDTH)
var nameLabel = Label.new()
nameLabel.text = card.cardName
nameLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vbox.add_child(nameLabel)
# Ensure cards don't extend off the bottom of the screen
var max_height = screen_height * 0.4 # Use at most 40% of screen height
if card_height > max_height:
card_height = max_height
card_width = card_height * (BASE_CARD_WIDTH / BASE_CARD_HEIGHT)
var rankLabel = Label.new()
rankLabel.text = "Rank " + str(card.rank)
rankLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vbox.add_child(rankLabel)
return Vector2(card_width, card_height)
var descLabel = Label.new()
descLabel.text = card.description
descLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
descLabel.custom_minimum_size = Vector2(CARD_WIDTH - 10, 0)
vbox.add_child(descLabel)
var unitWhitelistLabel = Label.new()
unitWhitelistLabel.text = ", ".join(card.unitWhitelist)
unitWhitelistLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
unitWhitelistLabel.custom_minimum_size = Vector2(CARD_WIDTH - 10, 0)
vbox.add_child(unitWhitelistLabel)
func add_card_display(card, card_size: Vector2 = Vector2(BASE_CARD_WIDTH, BASE_CARD_HEIGHT)):
# Create a container for the card
var cardContainer = PanelContainer.new()
cardContainer.custom_minimum_size = card_size
var idLabel = Label.new()
idLabel.text = card.id.substr(card.id.length() - 8, -1)
idLabel.autowrap_mode = true
idLabel.custom_minimum_size = Vector2(CARD_WIDTH - 10, 0)
vbox.add_child(idLabel)
cardPanel.gui_input.connect(func(event): _on_card_clicked(event, card))
# Create a stylebox for selection highlighting
var style = StyleBoxFlat.new()
style.bg_color = Utils.DARK_CELL
style.corner_radius_top_left = 5
style.corner_radius_top_right = 5
style.corner_radius_bottom_left = 5
style.corner_radius_bottom_right = 5
cardContainer.add_theme_stylebox_override("panel", style)
# Create compact card display
var compact_card = CompactCardDisplay.new()
compact_card.set_anchors_preset(Control.PRESET_FULL_RECT)
compact_card.mouse_filter = Control.MOUSE_FILTER_PASS
# Add to container
cardContainer.add_child(compact_card)
compact_card.set_card(card)
# Connect signals
cardContainer.gui_input.connect(func(event): _on_card_clicked(event, card))
cardContainer.mouse_entered.connect(func(): _on_card_mouse_entered(card))
cardContainer.mouse_exited.connect(func(): _on_card_mouse_exited())
# Add to hand container
container.add_child(cardContainer)
# Add to display list
cardDisplays.append({
"panel": cardContainer,
"card": card
})
cardDisplays.append(cardPanel)
container.add_child(cardPanel)
func _on_card_clicked(event: InputEvent, card: Card):
if event is InputEventMouseButton and event.pressed:
@ -103,70 +70,29 @@ func _on_card_clicked(event: InputEvent, card: Card):
selectedCard = null
highlight_selectedCard(selectedCard)
func _on_card_mouse_entered( card: Card):
# Show card preview when hovering
# print("_on_card_mouse_entered")
currently_hovered_card = card
hoveredPreview.preview_card(card)
hoveredPreview.visible = true
# Position it above the hand area
var mouse_pos = get_viewport().get_mouse_position()
hoveredPreview.position = Vector2(mouse_pos.x - hoveredPreview.size.x/2, mouse_pos.y - hoveredPreview.size.y - 20)
# Make sure it's visible within screen bounds
hoveredPreview.position.x = clamp(hoveredPreview.position.x, 10, get_viewport_rect().size.x - hoveredPreview.size.x - 10)
hoveredPreview.position.y = clamp(hoveredPreview.position.y, 10, get_viewport_rect().size.y - hoveredPreview.size.y - 10)
isPreviewVisible = true
func _on_card_mouse_exited():
# Only hide the preview after a delay and if we're not hovering over a new card
var exited_card = currently_hovered_card
currently_hovered_card = null
# Wait a bit before hiding
await get_tree().create_timer(0.2).timeout
# Only hide if we're not hovering over a new card
if exited_card == currently_hovered_card or currently_hovered_card == null:
hoveredPreview.visible = false
isPreviewVisible = false
func _process(delta):
# If preview is visible, update its position to follow the mouse
if isPreviewVisible:
var mouse_pos = get_viewport().get_mouse_position()
hoveredPreview.position = Vector2(mouse_pos.x - hoveredPreview.size.x/2, mouse_pos.y - hoveredPreview.size.y - 20)
hoveredPreview.position.x = clamp(hoveredPreview.position.x, 10, get_viewport_rect().size.x - hoveredPreview.size.x - 10)
hoveredPreview.position.y = clamp(hoveredPreview.position.y, 10, get_viewport_rect().size.y - hoveredPreview.size.y - 10)
func highlight_selectedCard(selected: Card) -> void:
for card_data in cardDisplays:
var panel = card_data.panel
var compact_card = panel.get_child(0) as CompactCardDisplay
for display in cardDisplays:
var style = StyleBoxFlat.new()
style.bg_color = Utils.DARK_CELL # Default color
if selected && card_data.card.id == selected.id:
var vbox = display.get_child(0)
if selected && vbox.get_meta("card_id") == selected.id:
style.bg_color = Color(0.4, 0.6, 0.4, 1) # Selected color
# Apply highlighting to the CompactCardDisplay
if compact_card:
compact_card.set_selected(true)
else:
# Reset highlighting
if compact_card:
compact_card.set_selected(false)
display.add_theme_stylebox_override("panel", style)
func get_card_by_uuid(uuid: String) -> Card:
for card_data in cardDisplays:
if card_data.card.id == uuid:
return card_data.card
for display in cardDisplays:
var vbox = display.get_child(0)
if vbox.get_meta("card_id") == uuid:
return selectedCard
return null
func getSelectedCard() -> Card:
return selectedCard
func clear_cards():
for card_data in cardDisplays:
card_data.panel.queue_free()
for display in cardDisplays:
display.queue_free()
cardDisplays.clear()
selectedCard = null
selectedCard = null

View file

@ -1 +0,0 @@
uid://dgsj76xih6r3y

View file

@ -6,55 +6,88 @@ const PREVIEW_HEIGHT = 200
const SCREEN_MARGIN = 20
var currentCard: Card = null
var previewPanel: CardPreviewPanel
var previewPanel: PanelContainer
var movesRemainingLabel: Label
func _init() -> void:
previewPanel = preload("res://card_preview_panel.tscn").instantiate()
previewPanel = PanelContainer.new()
previewPanel.custom_minimum_size = Vector2(PREVIEW_WIDTH, PREVIEW_HEIGHT)
position = Vector2(0, 0)
var vbox = VBoxContainer.new()
previewPanel.add_child(vbox)
var style = StyleBoxFlat.new()
style.bg_color = Color(0.2, 0.2, 0.2, 0.9)
style.corner_radius_top_left = 10
style.corner_radius_top_right = 10
style.corner_radius_bottom_left = 10
style.corner_radius_bottom_right = 10
previewPanel.add_theme_stylebox_override("panel", style)
previewPanel.hide()
# Add the preview panel as a child
add_child(previewPanel)
# Add a custom label for moves remaining that we'll show/hide as needed
movesRemainingLabel = Label.new()
movesRemainingLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
movesRemainingLabel.add_theme_font_size_override("font_size", 14)
movesRemainingLabel.visible = false
# Add the label to the preview panel's content
var content = previewPanel.get_node("CardContent")
if content:
content.add_child(movesRemainingLabel)
func show_card_preview(card: Card) -> void:
if card == null:
hide_preview()
previewPanel.hide()
currentCard = null
return
currentCard = card
var vbox = previewPanel.get_child(0)
# Use the preview_card method from CardPreviewPanel
previewPanel.preview_card(card)
for child in vbox.get_children():
child.queue_free()
# Since we're showing a new card, hide the moves remaining label
movesRemainingLabel.visible = false
var nameLabel = Label.new()
nameLabel.text = card.cardName
nameLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
nameLabel.add_theme_font_size_override("font_size", 18)
vbox.add_child(nameLabel)
var rankLabel = Label.new()
rankLabel.text = "Rank " + str(card.rank)
rankLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vbox.add_child(rankLabel)
var idLabel = Label.new()
idLabel.text = "ID: " + card.id.substr(card.id.length() - 8, -1)
idLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vbox.add_child(idLabel)
var durationLabel = Label.new()
durationLabel.text = str(card.remaining_turns) + " turns remaining"
durationLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
vbox.add_child(durationLabel)
var descLabel = Label.new()
descLabel.text = card.description
descLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
descLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
descLabel.custom_minimum_size = Vector2(PREVIEW_WIDTH - 20, 0)
vbox.add_child(descLabel)
previewPanel.show()
func hide_preview() -> void:
previewPanel.hide_preview()
previewPanel.hide()
currentCard = null
movesRemainingLabel.visible = false
func update_moves_remaining(moves: int) -> void:
if currentCard == null:
return
var vbox = previewPanel.get_child(0)
var movesLabel = Label.new()
movesLabel.text = "Moves remaining this turn: " + str(moves)
movesLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
# Update and show the moves remaining label
movesRemainingLabel.text = "Moves remaining this turn: " + str(moves)
movesRemainingLabel.visible = true
# Remove old moves label if it exists
for child in vbox.get_children():
if child.text.begins_with("Moves remaining this turn:"):
child.queue_free()
vbox.add_child(movesLabel)

View file

@ -1 +0,0 @@
uid://p4ahefeutov0

View file

@ -1,84 +0,0 @@
extends Panel
class_name CardPreviewPanel
# Node references
@onready var card_name_label = $CardContent/CardNameLabel
@onready var rank_label = $CardContent/RankLabel
@onready var description_label = $CardContent/DescriptionLabel
@onready var unit_list_label = $CardContent/UnitListLabel
@onready var effect_type_label = $CardContent/EffectTypeLabel
@onready var duration_label = $CardContent/DurationLabel
# Card rank colors (same as other card components)
var rank_colors = {
Card.Rank.RANK_0: Color(0.9, 0.1, 0.1, 1.0), # Red
Card.Rank.RANK_1: Color(0.9, 0.6, 0.1, 1.0), # Orange
Card.Rank.RANK_2: Color(0.1, 0.7, 0.1, 1.0), # Green
Card.Rank.RANK_3: Color(0.1, 0.7, 0.9, 1.0) # Blue
}
# Effect type descriptions
var effect_type_names = {
Card.EffectType.MOVEMENT_MODIFIER: "Movement Modifier",
Card.EffectType.BOARD_EFFECT: "Board Effect",
Card.EffectType.PIECE_EFFECT: "Piece Effect",
Card.EffectType.SPECIAL_ACTION: "Special Action"
}
func _ready():
# Hide the preview initially
visible = false
func preview_card(card: Card):
if !card:
hide_preview()
return
# if !is_inside_tree():
# await ready
# Update all card information
card_name_label.text = card.cardName
# Set rank information
var rank_text = "Rank " + str(card.rank)
match card.rank:
Card.Rank.RANK_0:
rank_text += " (One-time use, removed from deck)"
Card.Rank.RANK_1:
rank_text += " (Once per match)"
Card.Rank.RANK_2:
rank_text += " (Discarded after use)"
Card.Rank.RANK_3:
rank_text += " (Reused)"
rank_label.text = rank_text
# Set rank color
if card.rank in rank_colors:
rank_label.add_theme_color_override("font_color", rank_colors[card.rank])
card_name_label.add_theme_color_override("font_color", rank_colors[card.rank])
# Set description
description_label.text = card.description
# Set unit whitelist
if card.unitWhitelist.is_empty():
unit_list_label.text = "Can be applied to: Any piece"
else:
unit_list_label.text = "Can be applied to: " + ", ".join(card.unitWhitelist)
# Set effect type
if card.effectType in effect_type_names:
effect_type_label.text = "Effect Type: " + effect_type_names[card.effectType]
else:
effect_type_label.text = "Effect Type: Unknown"
# Set duration
if card.duration > 0:
duration_label.text = "Duration: " + str(card.duration) + " turns"
else:
duration_label.text = "Duration: Instant"
# Show the preview
visible = true
func hide_preview():
visible = false

View file

@ -1 +0,0 @@
uid://2xcreiq6lhe2

View file

@ -1,54 +0,0 @@
extends Control
class_name CardBankItem
signal card_selected(card_item, card)
var current_card = null
@onready var rank_label = $RankLabel
@onready var card_name_label = $CardNameLabel
@onready var card_frame = $CardFrame
# Card rank colors (matching CardSlot colors)
var rank_colors = {
Card.Rank.RANK_0: Color(0.9, 0.1, 0.1, 1.0), # Red
Card.Rank.RANK_1: Color(0.9, 0.6, 0.1, 1.0), # Orange
Card.Rank.RANK_2: Color(0.1, 0.7, 0.1, 1.0), # Green
Card.Rank.RANK_3: Color(0.1, 0.7, 0.9, 1.0) # Blue
}
func _ready():
# Connect signals for interaction
custom_minimum_size = Vector2(0, 40) # Ensure it has enough height
size_flags_horizontal = SIZE_EXPAND_FILL
mouse_filter = Control.MOUSE_FILTER_STOP
if card_frame:
card_frame.mouse_filter = Control.MOUSE_FILTER_PASS
if rank_label:
rank_label.mouse_filter = Control.MOUSE_FILTER_PASS
if card_name_label:
card_name_label.mouse_filter = Control.MOUSE_FILTER_PASS
# Connect GUI input signal
gui_input.connect(_on_gui_input)
func set_card(card):
current_card = card
update_appearance()
func update_appearance():
if current_card:
# Update card details
card_name_label.text = current_card.cardName
rank_label.text = str(current_card.rank)
# Set color based on rank
if current_card.rank in rank_colors:
rank_label.modulate = rank_colors[current_card.rank]
card_frame.modulate = rank_colors[current_card.rank]
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
if current_card:
emit_signal("card_selected", self, current_card)

View file

@ -1 +0,0 @@
uid://b1d17jfxagdc7

View file

@ -1,93 +0,0 @@
extends Panel
class_name CardSlot
signal card_selected(card_slot, card)
var current_card = null
var dragging = false
var drag_offset = Vector2()
@onready var card_name_label = $CardNameLabel
@onready var card_rank_label = $RankLabel
@onready var card_border = $CardBorder
@onready var card_bg = $CardBackground
# Card rank colors
var rank_colors = {
Card.Rank.RANK_0: Color(0.9, 0.1, 0.1, 1.0), # Red
Card.Rank.RANK_1: Color(0.9, 0.6, 0.1, 1.0), # Orange
Card.Rank.RANK_2: Color(0.1, 0.7, 0.1, 1.0), # Green
Card.Rank.RANK_3: Color(0.1, 0.7, 0.9, 1.0) # Blue
}
func _ready():
# Set up card appearance
update_appearance()
# Connect signals for interaction
mouse_filter = Control.MOUSE_FILTER_STOP
gui_input.connect(_on_gui_input)
if card_border:
card_border.mouse_filter = Control.MOUSE_FILTER_PASS
for child in get_children():
if child is Control:
child.mouse_filter = Control.MOUSE_FILTER_PASS
func set_card(card):
current_card = card
update_appearance()
func clear():
current_card = null
update_appearance()
func has_card():
return current_card != null
func update_appearance():
if current_card:
# Show card details
card_name_label.text = current_card.cardName
card_name_label.visible = true
# Set rank display
card_rank_label.text = str(current_card.rank)
card_rank_label.visible = true
# Set color based on rank
if current_card.rank in rank_colors:
card_rank_label.add_theme_color_override("font_color", rank_colors[current_card.rank])
card_border.modulate = rank_colors[current_card.rank]
# Show card background
card_bg.modulate = Color(0.2, 0.2, 0.25, 1.0)
else:
# Empty slot
card_name_label.visible = false
card_rank_label.visible = false
card_border.modulate = Color(0.4, 0.4, 0.4, 0.5)
card_bg.modulate = Color(0.15, 0.15, 0.15, 0.8)
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
# Start drag
if current_card:
emit_signal("card_selected", self, current_card)
# dragging = true
# drag_offset = get_global_mouse_position() - global_position
# else:
# # End drag
# if dragging:
# dragging = false
# emit_signal("card_selected", self, current_card)
# elif current_card:
# # Just a click, still select the card
# emit_signal("card_selected", self, current_card)
func _process(delta):
if dragging:
# Update position while dragging
global_position = get_global_mouse_position() - drag_offset

View file

@ -1 +0,0 @@
uid://b5888fqblsco5

View file

@ -4,12 +4,12 @@ func _init():
super._init()
# id = Utils.generate_guid()
cardName = "Double Time"
rank = Rank.RANK_3
rank = Rank.RANK_2
effectType = EffectType.MOVEMENT_MODIFIER
description = "Piece can move twice in one turn"
duration = 2 # Lasts for 2 turns
unitWhitelist = []
is_default = true
func modify_moves() -> Dictionary:
return {
"extra_moves": 1,

View file

@ -1 +0,0 @@
uid://o16wgudelf8x

View file

@ -22,7 +22,7 @@ func apply_effect(target_piece = null, board_flow = null, game_state = null):
var current_y = int(current_pos[1])
# Calculate target position
var target_y = game_state.boardYSize - 1 if target_piece.Item_Color == 1 else 0
var target_y = 7 if target_piece.Item_Color == 1 else 0
var y_direction = 1 if target_y > current_y else -1
# Generate tiles to check
@ -32,12 +32,6 @@ func apply_effect(target_piece = null, board_flow = null, game_state = null):
tiles_to_check.append(str(current_x) + "-" + str(y))
y += y_direction
if game_state.isPlayerTurn():
print("")
else:
print("REVERSING TILES TO CHECK")
tiles_to_check.reverse()
# Find final position (stop before any impassable tile)
var final_y = target_y
for tile_name in tiles_to_check:
@ -47,14 +41,10 @@ func apply_effect(target_piece = null, board_flow = null, game_state = null):
var tile_y = int(tile_name.split("-")[1])
final_y = tile_y - y_direction
break
print("CHECKING TILES ", tiles_to_check)
# Process pieces in path
for tile_name in tiles_to_check:
var tile = game_state.tileManager.get_tile(tile_name)
if tile && !tile.passable:
# print("CHECKING TILES ", tile_name, " passable ", tile.passable)
break
# elif !tile:
# print("CHECKING TILES ", tile_name, " no tile")
var tile_y = int(tile_name.split("-")[1])
# Only process tiles up to where we're stopping
var container = board_flow.get_node(tile_name) as PieceContainer
@ -65,7 +55,7 @@ func apply_effect(target_piece = null, board_flow = null, game_state = null):
# Move piece to final position
print("FINAL TILES ", str(current_x) + "-" + str(final_y))
var final_container = board_flow.get_node(str(current_x) + "-" + str(final_y)) as PieceContainer
# print("FINAL TILES ", final_container.get_piece().Item_Color)
print("FINAL TILES ", final_container.get_piece().Item_Color)
# Important: Need to remove the piece from its current container first
# AND keep a reference to it
@ -76,8 +66,6 @@ func apply_effect(target_piece = null, board_flow = null, game_state = null):
burned = true
game_state.resolveMoveEffects()
game_state.clearSelection()
game_state.stateMachine.transitionToNextState(Constants.POST_MOVE)
# Breaks Stockfish for some reason
return true
return true

View file

@ -1 +0,0 @@
uid://bayyvtfwpshsh

View file

@ -6,19 +6,13 @@ func _init():
duration = 1
super._init()
cardName = "Explosive Boots"
rank = Rank.RANK_3
rank = Rank.RANK_2
effectType = EffectType.PIECE_EFFECT
description = "All enemy units within 1 radius of the moving peice at the end of the turn are captured"
unitWhitelist = ["Pawn", "Knight", "King"]
remaining_turns = duration
is_default = true
func reset():
remaining_turns = duration
valid = false
func apply_effect(target_piece = null, board_flow = null, game_state = null):
print("EXPLOSIVE apply_effect", target_piece)
attached_piece = target_piece
stored_board_flow = board_flow
stored_game_state = game_state
@ -28,7 +22,6 @@ func apply_effect(target_piece = null, board_flow = null, game_state = null):
setup_persistent_effect(game_state)
# dont apply on card attachment
if !valid:
print("EXPLOSIVE apply_effect INVALID", )
return true
var piece_pos = target_piece.get_parent().name.split("-")
var piece_x = int(piece_pos[0])
@ -111,10 +104,8 @@ func setup_persistent_effect(game_state):
game_state.connect("turn_changed", on_turn_changed)
func on_turn_changed():
print("TURN CHANGED ==================", stored_game_state.isPlayerTurn() )
if stored_game_state.stateMachine.disabled:
return
# print("TURN CHANGED ==================", attached_piece, " | ", remaining_turns, )
if stored_game_state.isPlayerTurn() and attached_piece and remaining_turns > 0:
var piece = attached_piece
var flow = stored_board_flow

View file

@ -1 +0,0 @@
uid://64bdg86r6t4o

View file

@ -1 +0,0 @@
uid://cpgq5itsb2l8j

View file

@ -1 +0,0 @@
uid://ca4j0d6hrcm4x

View file

@ -1,143 +0,0 @@
class_name HalitosisCard extends Card
const EFFECT_RADIUS = 1
var valid = false;
func _init():
duration = 3
super._init()
cardName = "Halitosis"
rank = Rank.RANK_2
effectType = EffectType.PIECE_EFFECT
description = "All enemy units within 1 radius of the moving piece at the end of the turn are pushed back"
unitWhitelist = ["Pawn", "Knight", "King"]
remaining_turns = duration
is_default = false
func reset():
remaining_turns = duration
valid = false
func apply_effect(target_piece = null, board_flow = null, game_state = null):
print("HALITOSIS apply_effect", target_piece)
attached_piece = target_piece
stored_board_flow = board_flow
stored_game_state = game_state
if !target_piece or !board_flow or !game_state:
print(cardName, " missing input param ")
return false
setup_persistent_effect(game_state)
# dont apply on card attachment
if !valid:
print("HALITOSIS apply_effect INVALID", )
return true
var piece_pos = target_piece.get_parent().name.split("-")
var piece_x = int(piece_pos[0])
var piece_y = int(piece_pos[1])
var tiles_to_check = []
for dx in range(-EFFECT_RADIUS, EFFECT_RADIUS + 1):
for dy in range(-EFFECT_RADIUS, EFFECT_RADIUS + 1):
if max(abs(dx), abs(dy)) > EFFECT_RADIUS:
continue
var target_x = piece_x + dx
var target_y = piece_y + dy
if target_x < 0 or target_x >= game_state.boardXSize or target_y < 0 or target_y >= game_state.boardYSize:
continue
if dx == 0 and dy == 0:
continue
tiles_to_check.append(str(target_x) + "-" + str(target_y))
# Process tiles and add overlay
for tile_name in tiles_to_check:
var container = board_flow.get_node(tile_name) as PieceContainer
# Handle overlay through container's overlay management
container.remove_overlay("HalitosisOverlay")
var overlay = ColorRect.new()
overlay.name = "HalitosisOverlay"
overlay.color = Color(0.5, 0.7, 1, 0.3) # Blue-ish
overlay.size = container.size
overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
container.add_overlay(overlay)
overlay.show()
# Check for pieces to affect
var piece = container.get_piece()
if piece != null and piece.Item_Color != target_piece.Item_Color:
# Calculate the direction to push
var push_dir_x = piece_x - int(tile_name.split("-")[0])
var push_dir_y = piece_y - int(tile_name.split("-")[1])
# Normalize the direction vector
var length = sqrt(push_dir_x * push_dir_x + push_dir_y * push_dir_y)
if length > 0:
push_dir_x = round(push_dir_x / length)
push_dir_y = round(push_dir_y / length)
# Calculate target cell to push to
var push_target_x = int(int(tile_name.split("-")[0]) - push_dir_x)
var push_target_y = int(int(tile_name.split("-")[1]) - push_dir_y)
var push_target = str(push_target_x) + "-" + str(push_target_y)
print("HALITSOSES ", tile_name, " ", push_target)
# Check if target is valid and empty
if push_target_x >= 0 and push_target_x < game_state.boardXSize and push_target_y >= 0 and push_target_y < game_state.boardYSize:
var target_container = board_flow.get_node(push_target) as PieceContainer
if target_container and !target_container.has_piece():
# Animate the push
target_container.animate_movement(container, piece)
else:
# If we can't push (blocked), capture the piece instead
# game_state.updatePointsAndCapture(piece)
print("Blocked should we capture?")
else:
print("Off Board should we capture?")
# If push would be off the board, capture the piece
# game_state.updatePointsAndCapture(piece)
# Setup timer to remove overlays
var timer = Timer.new()
board_flow.add_child(timer)
timer.wait_time = 1
timer.one_shot = true
timer.timeout.connect(func():
for tile_name in tiles_to_check:
var tile = board_flow.get_node(tile_name)
var overlay = tile.get_node_or_null("HalitosisOverlay")
if overlay:
overlay.queue_free()
timer.queue_free()
)
timer.start()
return true
func setup_persistent_effect(game_state):
# Connect to the turn change signal if not already connected
if !game_state.is_connected("turn_changed", on_turn_changed):
game_state.connect("turn_changed", on_turn_changed)
func on_turn_changed():
print("TURN CHANGED ==================", stored_game_state.isPlayerTurn())
if stored_game_state.stateMachine.disabled:
return
if stored_game_state.isPlayerTurn() and attached_piece and remaining_turns > 0:
var piece = attached_piece
var flow = stored_board_flow
var state = stored_game_state
# Now try apply_effect with these values
valid = true
apply_effect(piece, flow, state)
func remove_effect():
if attached_piece:
var game_state = stored_game_state
if game_state.is_connected("turn_changed", on_turn_changed):
game_state.disconnect("turn_changed", on_turn_changed)
super.remove_effect()

View file

@ -1 +0,0 @@
uid://b8f17yg2854vs

View file

@ -1 +0,0 @@
uid://5w8amumsdpto

View file

@ -1,28 +0,0 @@
class_name HorseCostumeCard extends Card
func _init():
super._init()
# id = Utils.generate_guid()
cardName = "Horse Costume"
rank = Rank.RANK_2
effectType = EffectType.MOVEMENT_MODIFIER
description = "Attached Unit can move and capture in any direction"
duration = 3
unitWhitelist = ["Pawn", "Bishop", "Queen", "Rook", "King"]
is_default = false
# current_movement_string = "mWmFcfF"
func apply_effect(target_piece = null, board_flow = null, game_state = null):
if !super.apply_effect(target_piece, board_flow, game_state):
return false
attached_piece = target_piece
attached_piece.current_movement_string = "N"
return true
func reset():
super.reset()
remaining_turns = duration
attached_piece.reset_current_movement_string()

View file

@ -1 +0,0 @@
uid://nw1oesh7pr48

View file

@ -1,28 +0,0 @@
class_name KingsSquireCard extends Card
func _init():
super._init()
# id = Utils.generate_guid()
cardName = "Kings' Squire"
rank = Rank.RANK_2
effectType = EffectType.MOVEMENT_MODIFIER
description = "Attached Pawn can move and capture in any direction"
duration = 3
unitWhitelist = ["Pawn"]
is_default = false
# current_movement_string = "mWmFcfF"
func apply_effect(target_piece = null, board_flow = null, game_state = null):
if !super.apply_effect(target_piece, board_flow, game_state):
return false
attached_piece = target_piece
attached_piece.current_movement_string = "WF"
return true
func reset():
super.reset()
remaining_turns = duration
attached_piece.reset_current_movement_string()

View file

@ -1 +0,0 @@
uid://ms7wi6dgoqnr

View file

@ -1,28 +0,0 @@
class_name QueensSquireCard extends Card
func _init():
super._init()
# id = Utils.generate_guid()
cardName = "Queens' Squire"
rank = Rank.RANK_3
effectType = EffectType.MOVEMENT_MODIFIER
description = "Attached Pawn can move in any direction, can only capture diagonally forward"
duration = 4
unitWhitelist = ["Pawn"]
is_default = true
# current_movement_string = "mWmFcfF"
func apply_effect(target_piece = null, board_flow = null, game_state = null):
if !super.apply_effect(target_piece, board_flow, game_state):
return false
attached_piece = target_piece
attached_piece.current_movement_string = "mWmFcfF"
return true
func reset():
super.reset()
remaining_turns = duration
attached_piece.reset_current_movement_string()

View file

@ -1 +0,0 @@
uid://8vpac2cx4e0u

View file

@ -8,15 +8,11 @@ func _init():
cardName = "Supernova"
rank = Rank.RANK_0
effectType = EffectType.PIECE_EFFECT
duration = 1
duration = 5
description = "All enemy units within 4 radius are captured"
unitWhitelist = ["King"]
remaining_turns = duration
func reset():
remaining_turns = duration
valid = false
func apply_effect(target_piece = null, board_flow = null, game_state = null):
attached_piece = target_piece
stored_board_flow = board_flow

View file

@ -1 +0,0 @@
uid://cvldhayf5ket0

View file

@ -1,109 +0,0 @@
extends Panel
class_name CompactCardDisplay
# Node references (will be created programmatically)
var card_name_label: Label
var rank_label: Label
var description_label: Label
var effect_type_label: Label
# Card rank colors (same as other card components)
var rank_colors = {
Card.Rank.RANK_0: Color(0.9, 0.1, 0.1, 1.0), # Red
Card.Rank.RANK_1: Color(0.9, 0.6, 0.1, 1.0), # Orange
Card.Rank.RANK_2: Color(0.1, 0.7, 0.1, 1.0), # Green
Card.Rank.RANK_3: Color(0.1, 0.7, 0.9, 1.0) # Blue
}
# Effect type descriptions
var effect_type_names = {
Card.EffectType.MOVEMENT_MODIFIER: "Movement Modifier",
Card.EffectType.BOARD_EFFECT: "Board Effect",
Card.EffectType.PIECE_EFFECT: "Piece Effect",
Card.EffectType.SPECIAL_ACTION: "Special Action"
}
var current_card = null
func _ready():
# Create a stylish background
var style = StyleBoxFlat.new()
style.bg_color = Color(0.15, 0.15, 0.15, 1.0)
style.corner_radius_top_left = 5
style.corner_radius_top_right = 5
style.corner_radius_bottom_left = 5
style.corner_radius_bottom_right = 5
style.border_width_left = 2
style.border_width_top = 2
style.border_width_right = 2
style.border_width_bottom = 2
style.border_color = Color(0.3, 0.3, 0.3)
add_theme_stylebox_override("panel", style)
# Create a content container
var content = VBoxContainer.new()
content.set_anchors_preset(Control.PRESET_FULL_RECT)
content.add_theme_constant_override("separation", 2) # Tight spacing
add_child(content)
card_name_label = Label.new()
card_name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
card_name_label.add_theme_font_size_override("font_size", 12)
card_name_label.clip_text = true # Prevent overflow
content.add_child(card_name_label)
rank_label = Label.new()
rank_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
rank_label.add_theme_font_size_override("font_size", 10)
content.add_child(rank_label)
# Create description label (limited to 2-3 lines)
description_label = Label.new()
description_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
description_label.add_theme_font_size_override("font_size", 9)
description_label.autowrap_mode = TextServer.AUTOWRAP_WORD
description_label.max_lines_visible = 3
description_label.size_flags_vertical = SIZE_EXPAND_FILL
content.add_child(description_label)
effect_type_label = Label.new()
effect_type_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
effect_type_label.add_theme_font_size_override("font_size", 8)
effect_type_label.clip_text = true
content.add_child(effect_type_label)
func set_selected(is_selected: bool) -> void:
var style = get_theme_stylebox("panel").duplicate()
if is_selected:
style.bg_color = Utils.GREEN_CELL # Selected color
else:
style.bg_color = Utils.DARK_CELL
add_theme_stylebox_override("panel", style)
func set_card(card: Card):
if !card:
return
if !is_inside_tree():
await ready
current_card = card
card_name_label.text = card.cardName
rank_label.text = "Rank " + str(card.rank)
if card.rank in rank_colors:
rank_label.add_theme_color_override("font_color", rank_colors[card.rank])
var style = get_theme_stylebox("panel").duplicate()
style.border_color = rank_colors[card.rank]
add_theme_stylebox_override("panel", style)
description_label.text = card.description
if card.effectType in effect_type_names:
effect_type_label.text = "Effect: " + effect_type_names[card.effectType]
else:
effect_type_label.text = "Effect: Unknown"

View file

@ -1 +0,0 @@
uid://dm6uv77n0roen

View file

@ -3,70 +3,40 @@ class_name DeckManager
var deck: Array = []
var hand: Array = []
var bank: Array = []
var discard: Array = []
var attached_cards: Dictionary = {} # piece_id: card
var attached_effects: Dictionary = {} # piece_id: [card]
var hand_size: int = 5
var preloaded_hand_cards: Array = [] # array of string ids
# Card costs for shop
const CARD_BASE_COSTS = {
Card.Rank.RANK_0: 100,
Card.Rank.RANK_1: 75,
Card.Rank.RANK_2: 50,
Card.Rank.RANK_3: 25
}
func _init():
print("************************DECK INIT*****************")
initializeStartingDeck()
func preload_cards(card_array = []):
var cnt = 0
preloaded_hand_cards.clear()
for card in card_array:
preloaded_hand_cards.append(card.id)
func set_hand_size(size: int):
hand_size = size
func initializeStartingDeck(starting_cards: Array = []):
func initializeStartingDeck():
deck.clear();
# addCardToDeck(ExplosiveBootsCard.new())
# addCardToDeck(DoubleTimeCard.new())
# # addCardToDeck(DrunkDrivingCard.new())
# addCardToDeck(SupernovaCard.new())
print("Initializing deck with " + str(starting_cards.size()) + " cards")
for template_card in starting_cards:
# Create a new instance of the same card type
var new_card = create_new_card_instance(template_card)
if new_card:
addCardToDeck(new_card)
else:
print("Failed to create card: " + template_card.cardName)
func initializeStartingBank():
bank.append(HopscotchCard.new())
bank.append(FieryCapeCard.new())
bank.append(FieryTrailCard.new())
bank.append(ExplosiveBootsCard.new())
bank.append(DoubleTimeCard.new())
bank.append(DrunkDrivingCard.new())
bank.append(SupernovaCard.new())
for i in range(3):
bank.append(HopscotchCard.new())
bank.append(FieryCapeCard.new())
# for i in range(2):
# deck.append(DoubleTimeCard.new())
deck.append(FieryCapeCard.new())
deck.append(FieryTrailCard.new())
deck.append(ExplosiveBootsCard.new())
deck.append(DoubleTimeCard.new())
deck.append(DrunkDrivingCard.new())
deck.append(HopscotchCard.new())
deck.append(SupernovaCard.new())
shuffleDeck()
drawStartingHand()
func shuffleDeck():
deck.shuffle()
func drawStartingHand(skip_preload: bool = false):
if !skip_preload:
for id in preloaded_hand_cards:
var matchCard = func matchCardId(card):
return "id" in card and card.id == id
var cardIndex = deck.find_custom(matchCard.bind())
if cardIndex != -1:
hand.append(deck.pop_at(cardIndex))
func drawStartingHand():
for i in range(min(hand_size, deck.size())):
drawCard()
@ -78,15 +48,13 @@ func drawCard():
if !deck.is_empty() && hand.size() < hand_size:
hand.append(deck.pop_back())
emit_signal("hand_updated", hand)
signal hand_updated
func addCardToDeck(card: Card):
deck.insert(0, card)
func playCard(card: Card, target_piece: Pawn, board_flow = null, game_state = null, avoidHand = false):
if !avoidHand and !hand.has(card):
@ -100,7 +68,7 @@ func playCard(card: Card, target_piece: Pawn, board_flow = null, game_state = nu
attached_cards[target_piece.get_instance_id()] = card
hand.erase(card)
emit_signal("hand_updated", hand)
return true
# print("Failed Play Card 2")
@ -127,12 +95,6 @@ func playEffect(card: Card, target_piece: Pawn, board_flow = null, game_state =
# print("Failed Play Card 2")
return false
func cleanup():
if hand.size() > 0:
for card in hand:
deck.push_back(card)
hand.clear()
func updateEffectDurations():
var expired_entries = [] # Store [piece_id, card_id] pairs to remove
@ -172,7 +134,7 @@ func updateCardDurations():
if card.remaining_turns <= 0:
expired_cards.append(piece_id)
card.reset()
match card.rank:
Card.Rank.RANK_0:
print("Rank 3 Burned permanently")
@ -185,7 +147,7 @@ func updateCardDurations():
discard.append(card)
pass
Card.Rank.RANK_3:
addCardToDeck(card)
deck.append(card)
print("Rank 3 add to Deck")
pass
else:
@ -213,33 +175,10 @@ func getShopCards(num_cards: int = 3) -> Array:
return shop_cards
func calculateCardCost(card: Card) -> int:
var base_cost = Utils.CardPrices[card.rank]
var base_cost = CARD_BASE_COSTS[card.rank]
# Add modifiers based on card effects
return base_cost
func upgradeCard(card: Card) -> bool:
# Implement card upgrading logic
return false
func create_new_card_instance(template_card: Card) -> Card:
var new_card = null
var script = template_card.get_script()
if script:
new_card = script.new()
else:
print("Warning: Could not get script from card: " + template_card.cardName)
var class_list = ProjectSettings.get_global_class_list()
var card_class_name = template_card.get_class()
for class_info in class_list:
if class_info["class"] == card_class_name:
var card_script = load(class_info["path"])
if card_script:
new_card = card_script.new()
break
return new_card

View file

@ -1 +0,0 @@
uid://bdwsolgdmo51y

View file

@ -1,286 +0,0 @@
extends Node
class_name FairyStockfishVariantGenerator
const STANDARD_PIECE_CHARS = "PNBRQK"
const files = "abcdefghijkl"
const VARIANT_CHARS = "ACDEFGHIJLMOSTVWXYZ"
func generate_variant_string(chess_game: ChessGame) -> String:
var width = chess_game.boardXSize
var height = chess_game.boardYSize
var prefix = "chessbuilder" + str(width) + "x" + str(height)
var piece_data = gather_piece_data(chess_game)
var piece_definitions = piece_data.definitions
var piece_mappings = piece_data.mappings
var used_variant_chars = piece_data.used_chars
var variant_string = "[" + prefix + ":chess]\n"
# Build the pieceToCharTable with all used variant characters
var char_table = build_piece_char_table(used_variant_chars)
variant_string += "pieceToCharTable = " + char_table + "\n"
variant_string += "maxRank = " + str(height) + "\n"
variant_string += "maxFile = " + files[width - 1] + "\n"
var current_fen = chess_game.getCurrentFen()
variant_string += "startFen = " + current_fen + "\n\n"
# Add wall types
variant_string += "walltype = * # Duck wall\n"
variant_string += "walltype = @ # Stone wall\n\n"
# Add walling rules
variant_string += "wallingRule = static\n"
variant_string += "wallOrMove = false\n"
variant_string += "mobilityRegion = *:@\n"
variant_string += "prohibitedMove = *@\n\n"
# Add the piece definitions
variant_string += "# Custom piece movement definitions\n"
for piece_key in piece_definitions:
variant_string += "piece " + piece_key + " = " + piece_definitions[piece_key] + "\n"
# Add pieceDrops if needed for capturing
variant_string += "\n# Allow capturing with custom pieces\n"
variant_string += "pieceDrops = true\n"
return variant_string
# Build a complete pieceToCharTable that includes all variant characters
func build_piece_char_table(used_variant_chars: Array) -> String:
var char_table = STANDARD_PIECE_CHARS
for variant_char in used_variant_chars:
char_table += variant_char.to_upper()
char_table += "..*@..........."
# Add lowercase versions
char_table += STANDARD_PIECE_CHARS.to_lower()
for variant_char in used_variant_chars:
char_table += variant_char.to_lower()
char_table += "..*@..........."
return char_table
# Gather all piece data from the current game state
func gather_piece_data(chess_game: ChessGame) -> Dictionary:
# Dictionary to track unique movement patterns
# Key format: "piece_char:color:movement_string"
var unique_movements = {}
var modified_piece_types = {}
# First pass: collect all unique movement patterns
for y in range(chess_game.boardYSize):
for x in range(chess_game.boardXSize):
var container = chess_game.boardContainer.get_node(str(x) + "-" + str(y)) as PieceContainer
if container && container.has_piece():
var piece = container.get_piece() as Pawn
if piece.current_movement_string != piece.original_movement_string:
var piece_char = get_piece_char(piece)
var color_key = "white" if piece.Item_Color == 0 else "black"
var piece_key = piece_char + ":" + color_key
if !modified_piece_types.has(piece_key):
modified_piece_types[piece_key] = true
var movement_key = piece_key + ":" + piece.current_movement_string
if !unique_movements.has(movement_key):
unique_movements[movement_key] = {
"piece_char": piece_char,
"color": color_key,
"betza_notation": piece.current_movement_string,
"fairy_notation": convert_betza_to_fairy_stockfish(piece.current_movement_string),
"positions": []
}
unique_movements[movement_key].positions.append(str(x) + "-" + str(y))
var variant_char_index = 0
var piece_definitions = {}
var piece_mappings = {}
var used_variant_chars = []
for movement_key in unique_movements:
var movement_data = unique_movements[movement_key]
var piece_char = movement_data.piece_char
var color = movement_data.color
var fs_notation = movement_data.fairy_notation
var variant_char
if variant_char_index < VARIANT_CHARS.length():
variant_char = VARIANT_CHARS[variant_char_index]
used_variant_chars.append(variant_char)
variant_char_index += 1
else:
# If we run out of variant chars, we'll need to add more or implement another solution
print("Warning: Ran out of variant characters for piece definitions")
break
# Store the piece definition
var definition_key = variant_char.to_upper() if color == "white" else variant_char.to_lower()
piece_definitions[definition_key] = fs_notation
for pos in movement_data.positions:
piece_mappings[pos] = definition_key
return {
"definitions": piece_definitions,
"mappings": piece_mappings,
"used_chars": used_variant_chars
}
func get_piece_char(piece: Pawn) -> String:
match piece.name:
"Pawn": return "P"
"Knight": return "N"
"Bishop": return "B"
"Rook": return "R"
"Queen": return "Q"
"King": return "K"
_: return "P" # Default to pawn
func convert_betza_to_fairy_stockfish(betza_notation: String) -> String:
var atoms = parse_betza_notation(betza_notation)
var fairy_stockfish_moves = []
for atom in atoms:
var move_type = atom.type
var modifiers = atom.modifiers
var range_limit = atom.range
var move_only = "m" in modifiers
var capture_only = "c" in modifiers
# Process direction modifiers
var direction_modifiers = []
for modifier in modifiers:
if modifier.length() > 1 || ["f", "b", "l", "r"].has(modifier):
for char in modifier:
if ["f", "b", "l", "r"].has(char):
direction_modifiers.append(char)
var fs_move = ""
match move_type:
"W": fs_move += "W" # Wazir (orthogonal step)
"F": fs_move += "F" # Ferz (diagonal step)
"R": fs_move += "R" # Rook (orthogonal slider)
"B": fs_move += "B" # Bishop (diagonal slider)
"N": fs_move += "N" # Knight
"K": fs_move += "K" # King (one step in any direction)
"Q": fs_move += "Q" # Queen (combination of R and B)
if range_limit > 0:
fs_move += str(range_limit)
if direction_modifiers.size() > 0:
fs_move += "/"
for dir in direction_modifiers:
match dir:
"f": fs_move += "f"
"b": fs_move += "b"
"l": fs_move += "l"
"r": fs_move += "r"
if move_only:
fs_move += "m"
elif capture_only:
fs_move += "c"
fairy_stockfish_moves.append(fs_move)
var ret_str = ""
for moves in fairy_stockfish_moves:
ret_str += str(moves) + " "
return ret_str
# Parse Betza notation into atoms
func parse_betza_notation(notation: String) -> Array:
var atoms = []
var i = 0
while i < notation.length():
var atom = {
"type": "",
"modifiers": [],
"range": -1 # -1 means unlimited
}
while i < notation.length() && ["m", "c"].has(notation[i]):
atom.modifiers.append(notation[i])
i += 1
var directions = ""
while i < notation.length() && ["f", "b", "l", "r"].has(notation[i]):
directions += notation[i]
i += 1
if directions:
atom.modifiers.append(directions)
# Get the atom type
if i < notation.length():
atom.type = notation[i]
i += 1
# Check for range specification
var range_str = ""
while i < notation.length() && notation[i].is_valid_int():
range_str += notation[i]
i += 1
if range_str:
atom.range = int(range_str)
atoms.append(atom)
return atoms
# Generate a modified FEN string with custom piece characters
func generate_modified_fen(chess_game: ChessGame, piece_mappings: Dictionary) -> String:
var fen_parts = chess_game.getCurrentFen().split(" ")
var board_part = fen_parts[0]
var rows = board_part.split("/")
var new_rows = []
for y in range(rows.size()):
var new_row = ""
var x = 0
for c in rows[y]:
if c.is_valid_int():
# Handle empty squares
new_row += c
x += int(c)
else:
var pos = str(x) + "-" + str(y)
if piece_mappings.has(pos):
new_row += piece_mappings[pos]
else:
new_row += c
x += 1
new_rows.append(new_row)
fen_parts[0] = new_rows.join("/")
return fen_parts.join(" ")
# Save the variant definition to a file
func save_variant_to_file(variant_string: String) -> bool:
print("VARIANT ", variant_string)
ServerManager.write_variant(variant_string)
return true
# Helper function to get a complete variant definition for the current game state
func generate_and_save_variant(chess_game: ChessGame) -> String:
var variant_string = generate_variant_string(chess_game)
save_variant_to_file(variant_string)
return variant_string

View file

@ -1 +0,0 @@
uid://boryt6vnd0icx

View file

@ -3,7 +3,6 @@ extends Node
var server_path: String = ""
var log_dir: String = ""
var variant_dir : String = ""
var log_string: String = ""
var running := false
var server_process_id: int = -50
@ -13,29 +12,6 @@ var ping_http: HTTPRequest
var server_url = "http://localhost:27531"
func write_variant(variant_string: String):
var file = FileAccess.open(variant_dir, FileAccess.WRITE_READ)
var error = FileAccess.get_open_error()
if error != OK:
# print("Failed to open log file. Error code: ", error)
return
if file == null:
# print("File handle is null")
return
file.store_string(variant_string)
var write_error = FileAccess.get_open_error()
if write_error != OK:
print("Failed to write to log file. Error code: ", write_error)
file.close()
func write_log(message: String):
# First check if path is valid
# print("Attempting to write to: ", log_dir)
@ -65,7 +41,6 @@ func write_log(message: String):
func _ready():
server_path = extract_server_files() + "/ChessEngines/fairy-chess-server"
setup_logging()
setup_variant()
check_server_files(server_path)
start_server()
get_tree().set_auto_accept_quit(false)
@ -142,14 +117,6 @@ func setup_logging():
log_dir = l_dir.path_join("godot-chess.log")
write_log("ServerManager initialized")
func setup_variant():
var l_dir = get_globalDir() + "/variant"
# Create directory if it doesn't exist
DirAccess.make_dir_recursive_absolute(l_dir)
variant_dir = l_dir.path_join("custom_variant.ini")
write_log("ServerManager Variants initialized")
func _exit_tree():
stop_server()
@ -187,6 +154,7 @@ func _on_request_timeout():
# Clean up timer
request_timer.queue_free()
request_timer = null
# Call your secondary processing function here
write_log("HTTP Request failed, starting server process")
write_log(server_path + "/index.js")
server_process_id = OS.create_process("node", [server_path + "/index.js"])

View file

@ -1 +0,0 @@
uid://dae6g5472sh26

View file

@ -34,7 +34,7 @@ func connect_to_engine(_path: String, g: ChessGame) -> bool:
if ServerManager.is_server_running():
print("**************SERVER RUNNING ****************")
running = true
# start_game(2100)
start_game(1350)
return true
await get_tree().create_timer(delay).timeout
@ -74,7 +74,7 @@ func load_fen(fen: String):
http_request.request(server_url + "/position", headers, HTTPClient.METHOD_POST, body)
await http_request.request_completed
func start_board(elo: int, variant: String = "8x8"):
func start_board(elo: int):
if not running:
return
var headers = ["Content-Type: application/json"]
@ -85,26 +85,15 @@ func start_board(elo: int, variant: String = "8x8"):
print(body)
http_request.request(server_url + "/new", headers, HTTPClient.METHOD_POST, body)
await http_request.request_completed
setElo(elo, variant)
setElo(elo)
func clear_game( ):
if not running:
return
var headers = ["Content-Type: application/json"]
var body = JSON.new().stringify({
"variant": 'chess'
})
print(server_url + "/new")
print(body)
var request: HTTPRequest = HTTPRequest.new()
request.request(server_url + "/new", headers, HTTPClient.METHOD_POST, body)
await request.request_completed
func _exit_tree():
ServerManager.stop_server()
disconnect_engine();
func update_position(fen: String):
load_fen(fen)
@ -118,40 +107,7 @@ func get_globalDir() -> String:
return OS.get_environment("HOME").path_join("Library/ChessBuilder")
func setVariant(variant: String = "8x8"):
if not running:
return
print("####################################")
print("####################################")
print("chessbuilder" + variant)
print("####################################")
print("####################################")
print("####################################")
var headers = ["Content-Type: application/json"]
var data = {
"options": [
{
"name": "VariantPath",
"value": get_globalDir() + "/variant" + "/custom_variant.ini"
},
{
"name": "UCI_Variant",
"value": "chessbuilder" + variant
},
]
}
var body = JSON.new().stringify(data)
# Request engine move
http_request.request(
server_url + "/setoptions",
headers,
HTTPClient.METHOD_POST,
body
)
func setElo(elo: int = 1350, variant: String = "8x8") -> void:
func setElo(elo: int = 1350) -> void:
if not running:
return
var headers = ["Content-Type: application/json"]
@ -159,7 +115,7 @@ func setElo(elo: int = 1350, variant: String = "8x8") -> void:
"options": [
{
"name": "VariantPath",
"value": get_globalDir() + "/variant" + "/custom_variant.ini"
"value": get_globalDir() + "/Assets" + "/ChessEngines/Fairy-Stockfish/src/variants.ini"
},
{
"name": "UCI_LimitStrength",
@ -171,7 +127,7 @@ func setElo(elo: int = 1350, variant: String = "8x8") -> void:
},
{
"name": "UCI_Variant",
"value": "chessbuilder" + variant
"value": "chessbuilder" # Convert int to string
},
]
}
@ -188,18 +144,11 @@ func setElo(elo: int = 1350, variant: String = "8x8") -> void:
HTTPClient.METHOD_POST,
body
)
await elo_req.request_completed
http_request.request(
server_url + "/position",
headers,
HTTPClient.METHOD_POST,
JSON.new().stringify({"start": true})
)
func generateMove(think_time_ms: int = 1000) -> void:
if not running:
return
print("&&&&&&&&&&&&&&&GENERATING MOVE&&&&&&&&&&&&&&&&&&&&&&", str(game.generate_variant_aware_fen()))
print("&&&&&&&&&&&&&&&GENERATING MOVE&&&&&&&&&&&&&&&&&&&&&&")
move_time = think_time_ms
@ -207,7 +156,7 @@ func generateMove(think_time_ms: int = 1000) -> void:
var body = JSON.stringify({
"movetime": think_time_ms,
"depth": 15,
"fen": str(game.generate_variant_aware_fen())
"fen": str(game.getCurrentFen())
})
# Request engine move
@ -221,7 +170,6 @@ func generateMove(think_time_ms: int = 1000) -> void:
body
)
@ -301,6 +249,6 @@ func _on_request_completed(result: int, response_code: int, headers: PackedStrin
print("HTTP Request failed")
return
if response.status != "ok":
if response.status != "ok":
print("Server error:", response_code, json.parse(body.get_string_from_utf8()),)
return

View file

@ -1 +0,0 @@
uid://bnxxex87ax7hc

View file

@ -1,84 +0,0 @@
class_name CameraController
extends Node2D
signal zoom_changed(zoom_level)
# Camera movement properties
@export var min_zoom: float = 0.5
@export var max_zoom: float = 2.0
@export var zoom_step: float = 0.1
@export var pan_speed: float = 1.0
var board_container: Node
var initial_board_position: Vector2
var initial_board_size: Vector2
var current_zoom: float = 1.0
var dragging: bool = false
var drag_start_position: Vector2
func _init(target_board: Node) -> void:
board_container = target_board
func _ready() -> void:
initial_board_position = board_container.position
if board_container.has_method("get_combined_minimum_size"):
initial_board_size = board_container.get_combined_minimum_size()
else:
initial_board_size = board_container.size
current_zoom = 1.0
board_container.scale = Vector2(current_zoom, current_zoom)
adjust_pivot()
func adjust_pivot() -> void:
var center = initial_board_size / 2
board_container.pivot_offset = center
func _input(event: InputEvent) -> void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
zoom_in(event.position)
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
zoom_out(event.position)
elif event.button_index == MOUSE_BUTTON_MIDDLE:
if event.pressed:
start_drag(event.position)
else:
end_drag()
if event is InputEventMouseMotion and dragging:
update_drag(event.position)
func zoom_in(mouse_pos: Vector2) -> void:
if current_zoom < max_zoom:
apply_zoom(current_zoom + zoom_step)
func zoom_out(mouse_pos: Vector2) -> void:
if current_zoom > min_zoom:
apply_zoom(current_zoom - zoom_step)
func apply_zoom(new_zoom: float) -> void:
current_zoom = new_zoom
board_container.scale = Vector2(current_zoom, current_zoom)
emit_signal("zoom_changed", current_zoom)
func start_drag(position: Vector2) -> void:
dragging = true
drag_start_position = position
func end_drag() -> void:
dragging = false
func update_drag(position: Vector2) -> void:
if dragging:
var delta = position - drag_start_position
board_container.position += delta * pan_speed
drag_start_position = position
func reset_view() -> void:
current_zoom = 1.0
board_container.scale = Vector2(current_zoom, current_zoom)
board_container.position = initial_board_position

View file

@ -1 +0,0 @@
uid://bdbap6f4c4d5w

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
uid://cbcu68o863pfp

View file

@ -1,226 +0,0 @@
extends Control
class_name DeckManagerScreen
signal back_pressed
signal deck_manager_visibility_changed(isvisible)
# Node references
@onready var deckGrid = $MainContainer/GridScrollContainer/GridContainer
@onready var bankContainer = $MainContainer/BankContainer/ScrollContainer/VBoxContainer
@onready var backButton = $BackButton
@onready var card_preview = $CardPreviewPanel
# Default deck size (can be overridden via options)
var maxDeckSize = 40
var deckManager = null
var bankCards = [] # Cards available but not in deck
# The current deck
var currentDeck = []
var current_preview_card = null
func _ready():
# Connect back button
if backButton:
backButton.connect("pressed", Callable(self, "_on_backButton_pressed"))
if card_preview:
card_preview.visible = false
func initialize(options = null):
# Process options if provided
if options:
if options.has("max_deck_size") and options.max_deck_size is int:
maxDeckSize = options.max_deck_size
# Find the DeckManager instance
var board = get_node_or_null("/root/Board") as ChessGame
if board and "deckManager" in board:
deckManager = board.deckManager
print("Found deck manager:", deckManager)
# if(!deckManager.deck):
# deckManager.initializeStartingDeck()
else:
print("DeckManager not found on Board node")
print("DECK MANAGER")
# Load cards from deck and bank
loadCards()
# Set up the grid with empty card containers
setupDeckGrid()
# Populate the bank with available cards
populateBank()
func loadCards():
var board = get_node_or_null("/root/Board") as ChessGame
if board and "deckManager" in board:
deckManager = board.deckManager
print("Found deck manager:", deckManager)
if deckManager:
# Clone the deck to work with
currentDeck = deckManager.deck.duplicate()
bankCards = deckManager.bank.duplicate()
else:
# Fallback with empty collections if deck manager not found
currentDeck = []
bankCards = []
print("Warning: DeckManager not found")
func setupDeckGrid():
# Clear existing children
for child in deckGrid.get_children():
child.queue_free()
# Calculate grid dimensions
var cols = 4 #check screen to deteremine
var rows = maxDeckSize / cols
deckGrid.columns = cols
# Create card slots
for i in range(maxDeckSize):
var card_slot = preload("res://card_slot.tscn").instantiate()
deckGrid.add_child(card_slot)
# Connect signals
card_slot.connect("card_selected", Callable(self, "_on_deck_card_selected"))
_connect_hover_signals(card_slot)
# Set card if available
if i < currentDeck.size():
card_slot.set_card(currentDeck[i])
else:
card_slot.clear()
func _connect_hover_signals(node):
# Add hover signals for preview functionality
if node.is_class("Control"):
node.mouse_entered.connect(Callable(self, "_on_card_mouse_entered").bind(node))
# node.mouse_exited.connect(Callable(self, "_on_card_mouse_exited").bind(node))
func populateBank():
# Clear existing children
for child in bankContainer.get_children():
child.queue_free()
# Add each bank card to the list
for card in bankCards:
var card_item = preload("res://card_bank_item.tscn").instantiate()
bankContainer.add_child(card_item)
card_item.set_card(card)
card_item.connect("card_selected", Callable(self, "_on_bank_card_selected"))
_connect_hover_signals(card_item)
func _on_deck_card_selected(card_slot, card):
if card:
# Remove card from deck
var index = currentDeck.find(card)
if index >= 0:
currentDeck.remove_at(index)
# Add to bank
bankCards.append(card)
# Update UI
card_slot.clear()
populateBank()
if current_preview_card == card:
hide_card_preview()
func _on_bank_card_selected(card_item, card):
print("_on_bank_card_selected")
# Find first empty slot in deck
var empty_slot_index = -1
for i in range(deckGrid.get_child_count()):
var slot = deckGrid.get_child(i)
if !slot.has_card():
empty_slot_index = i
break
if empty_slot_index >= 0 and currentDeck.size() < maxDeckSize:
# Remove from bank
var bank_index = bankCards.find(card)
if bank_index >= 0:
bankCards.remove_at(bank_index)
# Add to deck
currentDeck.append(card)
# Update UI
deckGrid.get_child(empty_slot_index).set_card(card)
populateBank()
func _on_card_mouse_entered(node):
var card = null
# Get the card from the node
if node.has_method("has_card") and node.has_card():
# This is a CardSlot
card = node.current_card
elif node.has_method("set_card") and node.current_card:
# This is a CardBankItem
card = node.current_card
if card:
show_card_preview(card)
# func _on_card_mouse_exited(node):
# # Add a short delay before hiding the preview
# # This prevents flickering when moving between cards
# await get_tree().create_timer(0.1).timeout
# # Only hide if we're not hovering over another card that shows the same preview
# if current_preview_card:
# hide_card_preview()
func show_card_preview(card):
if card_preview and card:
current_preview_card = card
card_preview.preview_card(card)
func hide_card_preview():
if card_preview:
current_preview_card = null
card_preview.hide_preview()
func saveDeck():
if deckManager:
# Save the current deck to the deck manager
deckManager.deck = currentDeck.duplicate()
deckManager.bank = bankCards.duplicate()
print("Deck saved with ", currentDeck.size(), " cards")
func _on_backButton_pressed():
# Save changes before returning
saveDeck()
# Emit signal to go back
emit_signal("back_pressed")
# Hide this screen
visible = false
# Notification
func _notification(what):
if what == NOTIFICATION_VISIBILITY_CHANGED:
_on_visibility_changed(visible)
func _on_visibility_changed(is_visible):
print("DeckManager visibility changed to: ", is_visible)
if is_visible:
loadCards()
setupDeckGrid()
else:
print("DeckManager is now invisible")
emit_signal("deck_manager_visibility_changed", is_visible)

View file

@ -1 +0,0 @@
uid://vxufsih5pgeu

View file

@ -1,45 +0,0 @@
class_name Game extends Node
@onready var menuContainer = $MenuContainer
@onready var chessGame = $ChessGame
@onready var stateMachine = $StateMachine
func _ready():
if menuContainer:
menuContainer.visible = true
if chessGame:
chessGame.visible = false
if menuContainer and menuContainer.has_signal("new_game_requested"):
menuContainer.connect("new_game_requested", Callable(self, "_on_new_game_started"))
func _on_new_game_started():
print("Starting new game...")
if chessGame:
chessGame.visible = true
if !chessGame.is_inside_tree():
await chessGame.ready
_start_game_logic()
func _start_game_logic():
if stateMachine and stateMachine.has_method("transitionToNextState"):
stateMachine.transitionToNextState(Constants.WHITE_TURN)
func _unhandled_input(event):
if event is InputEventKey:
if event.pressed and event.keycode == KEY_ESCAPE:
_toggle_menu()
func _toggle_menu():
if menuContainer:
menuContainer.visible = !menuContainer.visible
if menuContainer.visible and chessGame:
pass

View file

@ -1 +0,0 @@
uid://cbaoxhgtk4td8

View file

@ -1,66 +0,0 @@
extends RichTextLabel
class_name GameMenuButton
signal pressed
# Style properties
var normal_color: Color = Color(1, 1, 1, 1) # White
var hover_color: Color = Color(1, 1, 0, 1) # Yellow
var font_size: int = 28
func _ready():
# Make this label clickable
mouse_filter = Control.MOUSE_FILTER_STOP
# Prevent text wrapping
autowrap_mode = TextServer.AUTOWRAP_OFF
fit_content = true
# Set size flags to expand horizontally
size_flags_horizontal = SIZE_EXPAND_FILL
# Remove default padding/margin
add_theme_constant_override("margin_top", 0)
add_theme_constant_override("margin_bottom", 0)
add_theme_constant_override("margin_left", 0)
add_theme_constant_override("margin_right", 0)
# Remove line spacing
add_theme_constant_override("line_separation", 0)
# Set up base styling
add_theme_font_size_override("normal_font_size", font_size)
add_theme_color_override("default_color", normal_color)
# Make text bold
bbcode_enabled = true
text = "[b]" + text + "[/b]"
# Connect the gui_input signal to our own handler
connect("gui_input", Callable(self, "_on_gui_input"))
# Connect hover signals for better user experience
connect("mouse_entered", Callable(self, "_on_mouse_entered"))
connect("mouse_exited", Callable(self, "_on_mouse_exited"))
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
emit_signal("pressed")
get_viewport().set_input_as_handled()
func _on_mouse_entered():
# Change appearance when mouse hovers
add_theme_color_override("default_color", hover_color)
# Scale up slightly on hover (optional)
var tween = create_tween()
tween.tween_property(self, "scale", Vector2(1.05, 1.05), 0.1)
func _on_mouse_exited():
# Restore original appearance
add_theme_color_override("default_color", normal_color)
# Scale back to normal (optional)
var tween = create_tween()
tween.tween_property(self, "scale", Vector2(1.0, 1.0), 0.1)

View file

@ -1 +0,0 @@
uid://bfjmon81nckns

View file

@ -1,100 +0,0 @@
extends Control
class_name GameMenuScreen
# Signals with optional parameters
signal shop_open_requested(options)
signal deckmanager_open_requested(options)
signal map_open_requested(options)
signal new_game_requested(options)
signal lobby_open_requested(options)
# @onready var newGameButton = $HBoxContainer/VBoxContainer/MenuOptions/NewGameText
# @onready var continueButton = $HBoxContainer/VBoxContainer/MenuOptions/Continue
# @onready var optionsButton = $HBoxContainer/VBoxContainer/MenuOptions/Options
# @onready var versionText = $HBoxContainer/VBoxContainer/VersionText
# @onready var titleText = $HBoxContainer/VBoxContainer/TitleText
# @onready var developerText = $HBoxContainer/VBoxContainer/DeveloperText
# Node references
@onready var shopButton = $HBoxContainer/VBoxContainer/GameOptions/ShopText
@onready var lobbyButton = $HBoxContainer/VBoxContainer/GameOptions/LobbyText
@onready var mapButton = $HBoxContainer/VBoxContainer/GameOptions/MapText
@onready var startButton = $HBoxContainer/VBoxContainer/GameOptions/StartText
@onready var backButton = $HBoxContainer/VBoxContainer/GameOptions/BackText
# Reference to main menu container
var main_menu_container = null
func _ready():
# Setup button signals
if shopButton:
shopButton.visible = false;
shopButton.connect("pressed", Callable(self, "_on_shop_button_pressed"))
if lobbyButton:
lobbyButton.connect("pressed", Callable(self, "_on_lobby_button_pressed"))
if mapButton:
mapButton.visible = false;
mapButton.connect("pressed", Callable(self, "_on_map_button_pressed"))
if startButton:
startButton.connect("pressed", Callable(self, "_on_start_button_pressed"))
startButton.visible = false;
if backButton:
backButton.connect("pressed", Callable(self, "_on_back_button_pressed"))
connect("map_open_requested", Callable(self, "_on_map_button_pressed"))
func setup(menu_container):
main_menu_container = menu_container
func _on_shop_button_pressed():
print("Shop button pressed")
# Emit signal with null options
emit_signal("shop_open_requested", null)
func _on_deck_button_pressed():
print("Manage Deck button pressed")
# Emit signal with null options
emit_signal("deckmanager_open_requested", null)
func _on_map_button_pressed():
print("Map button pressed")
# ignore error, keeping the null prevents infinit recursion
emit_signal("map_open_requested", null)
visible = false
func _on_start_button_pressed():
print("Start button pressed")
# Emit signal with empty options dictionary
emit_signal("new_game_requested", {})
# emit_signal("new_game_requested")
# Hide this menu
self.visible = false
func _on_back_button_pressed():
print("Back button pressed")
# Return to main menu
if main_menu_container:
main_menu_container.visible = true
# Hide this menu
self.visible = false
func show_menu():
self.visible = true
func _on_lobby_open_requested(options):
print("Lobby button pressed")
emit_signal("lobby_open_requested", options)
visible = false
func _on_lobby_button_pressed():
print("Lobby button pressed")
emit_signal("lobby_open_requested", null)
visible = false

View file

@ -1 +0,0 @@
uid://j0m4rwr86oi6

View file

@ -1,258 +0,0 @@
extends Control
class_name HandPreloadScreen
signal back_pressed
signal preload_visibility_changed(isvisible)
# Node references
@onready var deckGrid = $MainContainer/GridScrollContainer/GridContainer
@onready var bankContainer = $MainContainer/BankContainer/ScrollContainer/VBoxContainer
@onready var backButton = $BackButton
@onready var card_preview = $CardPreviewPanel
@onready var token_label = $TokenLabel
@onready var cost_label = $CostLabel
var handSize = 2
var deckManager = null
var player = null
var bankCards = [] # Cards available
var tokens = 0
var usedSlots = []
var currentHand = []
var current_preview_card = null
func _ready():
# Connect back button
if backButton:
backButton.connect("pressed", Callable(self, "_on_backButton_pressed"))
if card_preview:
card_preview.visible = false
if cost_label:
cost_label.visible = false
func initialize(options = null):
# Find the DeckManager instance
var board = get_node_or_null("/root/Board") as ChessGame
if board and "deckManager" in board:
deckManager = board.deckManager
print("Found deck manager:", deckManager)
else:
print("DeckManager not found on Board node")
if board and "player" in board:
player = board.player
handSize = player.hand_size
tokens = player.tokens
updateTokenLabel()
else:
print("Player not found on Board node")
# Load cards from deck and bank
loadCards()
# Set up the grid with empty card containers
setupDeckGrid()
# Populate the bank with available cards
populateBank()
func updateTokenLabel():
if token_label:
token_label.text = str(tokens) + " TOKENS"
func loadCards():
if deckManager:
currentHand.clear()
usedSlots.clear()
# Clone the deck to work with
bankCards = deckManager.deck.duplicate()
for id in deckManager.preloaded_hand_cards:
var matchCard = func matchCardId(card):
return "id" in card and card.id == id
var cardIndex = bankCards.find_custom(matchCard.bind())
if cardIndex != -1:
currentHand.append(bankCards[cardIndex].id)
usedSlots.append(true)
else:
# Fallback with empty collections if deck manager not found
currentHand = []
bankCards = []
print("Warning: DeckManager not found")
func setupDeckGrid():
# Clear existing children
for child in deckGrid.get_children():
child.queue_free()
# Calculate grid dimensions
var cols = 4 #check screen to deteremine
var rows = handSize / cols
deckGrid.columns = cols
# Create card slots
for i in range(handSize):
var card_slot = preload("res://card_slot.tscn").instantiate()
deckGrid.add_child(card_slot)
# Connect signals
card_slot.connect("card_selected", Callable(self, "_on_hand_card_selected"))
_connect_hover_signals(card_slot)
# Set card if available
if i < currentHand.size():
var matchCard = func matchCardId(card):
return "id" in card and card.id == currentHand[i]
var cardIndex = bankCards.find_custom(matchCard.bind())
if cardIndex != -1:
card_slot.set_card(bankCards[cardIndex])
else:
card_slot.clear()
func _connect_hover_signals(node):
# Add hover signals for preview functionality
if node.is_class("Control"):
node.mouse_entered.connect(Callable(self, "_on_card_mouse_entered").bind(node))
# node.mouse_exited.connect(Callable(self, "_on_card_mouse_exited").bind(node))
func populateBank():
for child in bankContainer.get_children():
child.queue_free()
# Add each bank card to the list
for card in bankCards:
var card_item = preload("res://card_bank_item.tscn").instantiate()
bankContainer.add_child(card_item)
card_item.set_card(card)
card_item.connect("card_selected", Callable(self, "_on_bank_card_selected"))
_connect_hover_signals(card_item)
static var TokenCosts = {
Card.Rank.RANK_0: 15, # Most expensive (one-time use)
Card.Rank.RANK_1: 10, # Expensive (once per match)
Card.Rank.RANK_2: 5, # Medium (multiple uses)
Card.Rank.RANK_3: 3 # Cheapest (basic cards)
}
func _on_hand_card_selected(card_slot, card):
if card:
# Remove card from deck
var index = currentHand.find(card.id)
if index >= 0:
currentHand.remove_at(index)
if index < usedSlots.size() and usedSlots[index] != true:
tokens += Utils.TokenCosts[card.rank]
usedSlots.remove_at(index)
updateTokenLabel()
# Update UI
card_slot.clear()
populateBank()
if current_preview_card == card:
hide_card_preview()
func _on_bank_card_selected(card_item, card):
print("_on_bank_card_selected ", card.id)
# Find first empty slot in deck
var empty_slot_index = -1
if card and (Utils.HandRankWhiteList.has(card.rank)):
for i in range(deckGrid.get_child_count()):
var slot = deckGrid.get_child(i)
if !slot.has_card():
empty_slot_index = i
break
# print("_on_bank_card_selected ", currentHand, " ", handSize, " ", empty_slot_index, )
# print("_on_bank_card_selected ", empty_slot_index >= 0, " ", currentHand.size() < handSize, " ", currentHand.find(card.id))
if empty_slot_index >= 0 and currentHand.size() < handSize and currentHand.find(card.id) == -1 and tokens - Utils.TokenCosts[card.rank] > 0:
# print("currentHand append ", card.id)
tokens -= Utils.TokenCosts[card.rank]
updateTokenLabel()
currentHand.append(card.id)
usedSlots.append(false)
deckGrid.get_child(empty_slot_index).set_card(card)
populateBank()
func _on_card_mouse_entered(node):
var card = null
# Get the card from the node
if node.has_method("has_card") and node.has_card():
# This is a CardSlot
card = node.current_card
elif node.has_method("set_card") and node.current_card:
# This is a CardBankItem
card = node.current_card
if card:
show_card_preview(card)
# func _on_card_mouse_exited(node):
# # Add a short delay before hiding the preview
# # This prevents flickering when moving between cards
# await get_tree().create_timer(0.1).timeout
# # Only hide if we're not hovering over another card that shows the same preview
# if current_preview_card:
# hide_card_preview()
func show_card_preview(card):
if card_preview and card:
current_preview_card = card
card_preview.preview_card(card)
if cost_label:
cost_label.visible = true
cost_label.text = "Cost: " + str(Utils.TokenCosts[card.rank]) + " tokens"
func hide_card_preview():
if cost_label:
cost_label.visible = false
if card_preview:
current_preview_card = null
card_preview.hide_preview()
func savePreloadCards():
if deckManager:
# Save the current deck to the deck manager
deckManager.preloaded_hand_cards = currentHand.duplicate()
print("Preloads saved with ", currentHand.size(), " cards")
player.tokens = tokens
func _on_backButton_pressed():
# Save changes before returning
savePreloadCards()
# Emit signal to go back
emit_signal("back_pressed")
# Hide this screen
visible = false
# Notification
func _notification(what):
if what == NOTIFICATION_VISIBILITY_CHANGED:
_on_visibility_changed(visible)
func _on_visibility_changed(is_visible):
print("Preload Screen visibility changed to: ", is_visible)
if is_visible:
loadCards()
setupDeckGrid()
else:
print("Preload Screen is now invisible")
emit_signal("preload_visibility_changed", is_visible)

View file

@ -1 +0,0 @@
uid://b7b2xlfvhgipb

View file

@ -1,101 +0,0 @@
extends Control
class_name LobbyScreen
signal vanilla_selected
signal shop_selected(options)
signal preload_selected(options)
signal deeper_selected
signal back_pressed
signal lobby_screen_visibility_changed(isvisible)
# Node references
@onready var vanilla_button = $BottomContainer/VanillaButton
@onready var shop_button = $CenterContainer/ShopButton
@onready var deeper_button = $BottomContainer/DeeperButton
@onready var back_button = $BackButton
@onready var preload_button = $PreloadButton
@onready var run_count_label = $TopBar/RunCountLabel
@onready var token_label = $TopBar/TokenContainer/TokenLabel
var game: ChessGame
var player: Player
func _ready():
# Connect button signals
if vanilla_button:
vanilla_button.connect("pressed", Callable(self, "_on_vanilla_button_pressed"))
if shop_button:
shop_button.connect("pressed", Callable(self, "_on_shop_button_pressed"))
if deeper_button:
deeper_button.connect("pressed", Callable(self, "_on_deeper_button_pressed"))
if back_button:
back_button.connect("pressed", Callable(self, "_on_back_button_pressed"))
if preload_button:
preload_button.connect("pressed", Callable(self, "_on_preload_button_pressed"))
func initialize(options = null):
game = get_node_or_null("/root/Board") as ChessGame
if game and "player" in game:
player = game.player
update_display()
if deeper_button:
deeper_button.disabled = player.get_run_count() == 0
func update_display():
if player:
if run_count_label:
run_count_label.text = "RUN #" + str(player.get_run_count() + 1)
if deeper_button:
deeper_button.disabled = player.run_count == 0
if token_label:
token_label.text = str(player.tokens) + " TOKENS"
func _on_preload_button_pressed():
emit_signal("preload_selected")
self.visible = false
func _on_vanilla_button_pressed():
emit_signal("vanilla_selected")
self.visible = false
func _on_shop_button_pressed():
emit_signal("shop_selected", {})
self.visible = false
func _on_deeper_button_pressed():
emit_signal("deeper_selected")
self.visible = false
func _on_back_button_pressed():
emit_signal("back_pressed")
# self.visible = false
func show_screen():
initialize()
self.visible = true
# Notification
func _notification(what):
if what == NOTIFICATION_VISIBILITY_CHANGED:
_on_visibility_changed(visible)
func _on_visibility_changed(is_visible):
print("Lobby Screen visibility changed to: ", is_visible)
if is_visible:
update_display()
else:
print("Lobby Screen is now invisible")
emit_signal("lobby_screen_visibility_changed", is_visible)

View file

@ -1 +0,0 @@
uid://dkk0eu4fj7q5j

View file

@ -1,387 +0,0 @@
extends Control
class_name LobbyShopScreen
signal back_pressed
signal card_unlocked(card, token_cost)
signal lobby_shop_visibility_changed(isvisible)
signal hand_size_increased
# Node references
@onready var card_carousel = $MainContainer/CardCarouselContainer/CardCarousel
@onready var token_label = $TopBar/TokenContainer/TokenLabel
@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
@onready var hand_size_label = $HandSizeContainer/HandSizeLabel
@onready var hand_size_cost_label = $HandSizeContainer/CostLabel
@onready var increase_hand_size_button = $HandSizeContainer/IncreaseButton
var available_cards = []
var player_tokens = 0
var selected_card = null
var selected_index = 0
var carousel_page = 0
var cards_per_page = 3
var card_instance_map = {}
var hovering_card_index = -1
var mouse_over_any_card = false
var game: ChessGame
var player: Player
var card_costs = Utils.TokenCosts
var fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
func _ready():
if back_button:
back_button.connect("pressed", Callable(self, "_on_back_button_pressed"))
if buy_button:
buy_button.connect("pressed", Callable(self, "_on_buy_button_pressed"))
if increase_hand_size_button:
increase_hand_size_button.connect("pressed", Callable(self, "_on_increase_hand_size_pressed"))
if left_button:
left_button.pressed.connect(_on_left_button_pressed)
if right_button:
right_button.pressed.connect(_on_right_button_pressed)
# Initialize with empty shop
if card_preview:
card_preview.visible = false
update_token_display()
update_buy_button()
update_navigation_buttons()
update_hand_size_display()
func initialize(options = null):
carousel_page = 0
selected_index = 0
game = get_node_or_null("/root/Board") as ChessGame
if game and "player" in game:
player = game.player
player_tokens = player.tokens
# Generate shop cards if not provided
if available_cards.is_empty() and game and "deckManager" in game:
var deck_manager = game.deckManager
available_cards = generate_shop_cards(deck_manager)
# Update display
update_token_display()
update_hand_size_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_available_cards = []
for rank in player.cards_by_rank:
for card in player.cards_by_rank[rank]:
var is_unlocked = false
for unlocked_card in player.unlocked_cards:
if unlocked_card.cardName == card.cardName:
is_unlocked = true
break
if not is_unlocked:
var card_instance = card.duplicate()
all_available_cards.append(card_instance)
if all_available_cards.is_empty():
print("No new cards available for the shop")
return []
all_available_cards.shuffle()
var num_shop_cards = min(randi_range(5, 7), all_available_cards.size())
for i in range(num_shop_cards):
shop_cards.append(all_available_cards[i])
print("Generated shop with " + str(shop_cards.size()) + " cards")
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 token_cost = get_card_cost(card)
card_visual.set_price(token_cost)
card_visual.set_string("tokens")
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)
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 cost = get_card_cost(selected_card)
buy_button.text = "BUY (" + str(cost) + " tokens)"
buy_button.disabled = cost > player_tokens
func get_card_cost(card):
if not card:
return 0
if card.rank in card_costs:
return card_costs[card.rank]
return 10
func update_token_display():
if token_label:
token_label.text = str(player_tokens) + " TOKENS"
func update_hand_size_display():
if not player or not hand_size_label or not hand_size_cost_label:
return
hand_size_label.text = "HAND SIZE: " + str(player.hand_size)
# Calculate cost using Fibonacci sequence
var fib_index = max(player.hand_size - 2, 1) + 2 # Start at index 2 (which is 1)
if fib_index >= fibonacci.size():
fib_index = fibonacci.size() - 1
var cost = fibonacci[fib_index]
hand_size_cost_label.text = "Cost: " + str(cost) + " tokens"
if increase_hand_size_button:
increase_hand_size_button.disabled = cost > player_tokens
func purchase_selected_card():
if not selected_card or not player:
return false
var cost = get_card_cost(selected_card)
if player_tokens < cost:
print("Not enough tokens")
return false
player_tokens -= cost
player.tokens = player_tokens
var purchased_card = selected_card.duplicate()
available_cards.remove_at(selected_index)
emit_signal("card_unlocked", purchased_card, cost)
update_token_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
func increase_hand_size():
if not player:
return false
# Calculate cost using Fibonacci sequence
var fib_index = max(player.hand_size - 2, 1) + 2
if fib_index >= fibonacci.size():
fib_index = fibonacci.size() - 1
var cost = fibonacci[fib_index]
if player_tokens < cost:
print("Not enough tokens for hand size increase")
return false
if player.hand_size >= player.MAX_HAND_SIZE:
print("Hand reached max size")
return false
player_tokens -= cost
player.tokens = player_tokens
player.set_hand_size(player.hand_size + 1)
update_token_display()
update_hand_size_display()
update_buy_button()
emit_signal("hand_size_increased")
return true
# Signal handlers
func _on_back_button_pressed():
emit_signal("back_pressed")
visible = false
func _on_buy_button_pressed():
purchase_selected_card()
func _on_increase_hand_size_pressed():
increase_hand_size()
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
# Notification
func _notification(what):
if what == NOTIFICATION_VISIBILITY_CHANGED:
_on_visibility_changed(visible)
func _on_visibility_changed(is_visible):
print("LobbyShop visibility changed to: ", is_visible)
if is_visible:
update_token_display()
update_hand_size_display()
update_buy_button()
else:
print("LobbyShop is now invisible")
emit_signal("lobby_shop_visibility_changed", is_visible)

View file

@ -1 +0,0 @@
uid://nrd5mq0tfmur

View file

@ -1,108 +0,0 @@
extends Control
class_name DotPatternGenerator
# Dot pattern settings
const DOT_SPACING = 30
const DOT_SIZE = 2
const DOT_COLOR = Color(0.3, 0.3, 0.3, 0.5)
const PATH_DOT_COLOR = Color(0.4, 0.4, 0.6, 0.8)
const PATH_DOT_SIZE = 3
const HIGHLIGHT_RADIUS = 150
# For path highlights
var map_screen = null
var highlight_dots = false
func _init(highlight: bool = false):
highlight_dots = highlight
# Don't process input
mouse_filter = Control.MOUSE_FILTER_IGNORE
func _ready():
# Apply the pattern
generate_dots()
# Find map screen if we're highlighting paths
if highlight_dots:
map_screen = find_map_screen()
func find_map_screen():
var parent = get_parent()
while parent != null:
if parent is MapScreen:
return parent
parent = parent.get_parent()
return null
func generate_dots():
# Clear existing dots
for child in get_children():
child.queue_free()
# Get the size of this control
var control_size = size
# Calculate how many dots we need
var cols = int(control_size.x / DOT_SPACING) + 1
var rows = int(control_size.y / DOT_SPACING) + 1
# Create dots
for i in range(rows):
for j in range(cols):
var dot_position = Vector2(j * DOT_SPACING, i * DOT_SPACING)
var on_path = false
# If highlighting, check if dot is near a path
if highlight_dots and map_screen != null:
on_path = is_near_path(dot_position)
# Create the dot with appropriate style
var dot = ColorRect.new()
dot.size = Vector2(DOT_SIZE, DOT_SIZE) if not on_path else Vector2(PATH_DOT_SIZE, PATH_DOT_SIZE)
dot.color = DOT_COLOR if not on_path else PATH_DOT_COLOR
dot.position = dot_position - (dot.size / 2)
add_child(dot)
func is_near_path(position: Vector2) -> bool:
# If no map screen, can't check paths
if map_screen == null:
return false
# Get connections from map screen
for line in map_screen.connection_lines:
if line.get_point_count() < 2:
continue
# Check distance to line segment
var start = line.get_point_position(0)
var end = line.get_point_position(1)
# Adjust for potential offset in the map container
start += map_screen.map_container.position
end += map_screen.map_container.position
# Check if point is near the line
var distance = distance_to_line_segment(position, start, end)
if distance < HIGHLIGHT_RADIUS:
return true
return false
# Calculate distance from point to line segment
func distance_to_line_segment(point: Vector2, line_start: Vector2, line_end: Vector2) -> float:
var line_vec = line_end - line_start
var point_vec = point - line_start
var line_length_squared = line_vec.length_squared()
if line_length_squared == 0:
return point_vec.length() # Line segment is a point
# Calculate projection of point onto line
var t = max(0, min(1, point_vec.dot(line_vec) / line_length_squared))
var projection = line_start + t * line_vec
# Return distance from point to projection
return (point - projection).length()
func update_dots():
if highlight_dots and map_screen != null:
generate_dots()

View file

@ -1 +0,0 @@
uid://6cmhvsug8nbv

View file

@ -1,795 +0,0 @@
extends RefCounted
class_name ChessMapGenerator
var min_levels = 5
var max_levels = 6
var max_connections_per_node = 4
var min_nodes_per_level = 1
var max_nodes_per_level = 4
var positions_per_level = 4
var starting_elo = 1000
var final_elo = 2100
var current_max_level = 0;
# var level_unit_distribution = ["", "", "", "nkr", "rnkr", "rnkbr", "rnqkbr", "rnqkbnr", "rnbqkbnr", "rbnqknbnr", "rnbnqknbnr", "rnbnqknbnbr", "rbnbnqknbnbr"]
# var level_unit_distribution = ["", "nk", "nkr", "rnkr", "rnkr", "rnkbr", "rnqkbr", "rnqkbnr", "rnbqkbnr"]
var level_unit_distribution = ["", "nk", "nkr", "rnkr", "rnkbr", "rnqkbr", "rnqkbnr", "rnbqkbnr"]
# ["", "nk", "nkr", "rnkr", "rnkbr", "rnqkbr", "rnqkbnr", "rnbqkbnr", "rbnqknbnr", "rnbnqknbnr", "rnbnqknbnnr", "rnnbnqknbnnr"]
# ["", "", "nkr", "rnkr", "rnkr", "rnkbr", "rnqkbr", "rnqkbnr", "rnbqkbnr", "rbnqknbnr", "rnbnqknbnr", "rnbnqknbnbr", "rbnbnqknbnbr"]
var _rng = RandomNumberGenerator.new()
var _next_id = 0
func _init(seed_value = null):
# Set seed for reproducible maps
if seed_value != null:
_rng.seed = seed_value
else:
_rng.randomize()
func change_preset_mode(mode, player):
# var game = get_node_or_null("/root/Board") as ChessGame
# var player = game.player
var run_count = player.run_count
match mode:
"vanilla":
min_levels = 5
max_levels = 6
var elo_step = Utils.VANILLA_ELO_STEP
var base_elo = Utils.MIN_ELO + run_count * elo_step
starting_elo = clamp(base_elo, Utils.MIN_ELO, Utils.MAX_ELO)
final_elo = starting_elo + elo_step
"deeper":
min_levels = 10
max_levels = 16
var elo_step = Utils.DEEPER_ELO_STEP
var base_elo = Utils.MIN_ELO + run_count * elo_step
starting_elo = clamp(base_elo, Utils.MIN_ELO, Utils.MAX_ELO)
final_elo = starting_elo + elo_step
_:
min_levels = 5
max_levels = 6
var elo_step = Utils.VANILLA_ELO_STEP
var base_elo = Utils.MIN_ELO + run_count * elo_step
starting_elo = clamp(base_elo, Utils.MIN_ELO, Utils.MAX_ELO)
final_elo = starting_elo + elo_step
func generate_map(mode, player):
var isVanilla = mode == "vanilla"
change_preset_mode(mode, player)
var nodes = []
var connections = []
_next_id = 0
var num_levels = _rng.randi_range(min_levels, max_levels)
current_max_level = num_levels;
var elo_step = float(final_elo - starting_elo) / (num_levels - 1)
var start_node = {
"id": _get_next_id(),
"type": Utils.RoomType.STARTING,
"level": 0,
"position": Vector2(3, 0),
"metadata": {
"is_escape": false,
},
"elo": starting_elo
}
nodes.append(start_node)
# Create final boss node
var final_node = {
"id": _get_next_id(),
"type": Utils.RoomType.FINAL,
"level": num_levels - 1,
"position": Vector2(3, num_levels - 1),
"metadata": {},
"elo": final_elo
}
# final_node.metadata = generate_final_data(final_node)
final_node = generate_node_data(final_node, player)
# print("final_node ====", final_node)
nodes.append(final_node)
var levels_nodes = {0: [start_node], (num_levels - 1): [final_node]}
for level in range(1, num_levels - 1):
var level_nodes = []
var level_elo = starting_elo + (elo_step * 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)
available_positions.shuffle()
for i in range(num_nodes):
var node_type = _get_random_room_type(level, num_levels)
var node = {
"id": _get_next_id(),
"type": node_type,
"level": level,
"position": Vector2(available_positions[i], level),
"elo": level_elo,
"metadata": {}
}
node = generate_node_data(node, player)
nodes.append(node)
level_nodes.append(node)
levels_nodes[level] = level_nodes
# Connect nodes between levels
# First connect starting node to level 1
if levels_nodes.has(1) and levels_nodes[1].size() > 0:
var num_connections = min(_rng.randi_range(2, 3), levels_nodes[1].size())
var targets = levels_nodes[1].duplicate()
targets.shuffle()
for i in range(num_connections):
connections.append({
"from": start_node.id,
"to": targets[i].id
})
# Keep track of which nodes are connected
var connected_nodes = [start_node.id]
for level in range(1, num_levels - 1):
if not levels_nodes.has(level) or not levels_nodes.has(level + 1):
continue
var current_level_nodes = levels_nodes[level]
var next_level_nodes = levels_nodes[level + 1].duplicate()
next_level_nodes.shuffle()
# For each node in current level that is connected from previous level
for node in current_level_nodes:
if _is_node_connected_to(node.id, connections):
# Add to connected nodes
connected_nodes.append(node.id)
# 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, 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({
"from": node.id,
"to": next_level_nodes[i].id
})
# # 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:
connections.append({
"from": node.id,
"to": final_node.id
})
# Remove any nodes that aren't connected
var valid_nodes = []
for node in nodes:
if connected_nodes.has(node.id) or node.id == final_node.id:
valid_nodes.append(node)
# Clean up connections to removed nodes
var valid_connections = []
for conn in connections:
var from_valid = false
var to_valid = false
for node in valid_nodes:
if node.id == conn.from:
from_valid = true
if node.id == conn.to:
to_valid = true
if from_valid and to_valid:
valid_connections.append(conn)
var index = 0
for valid_node in valid_nodes:
var isLeaf = true;
for connection in valid_connections:
# if theres outgoing connection we arent at a dead end
if connection.from == valid_node.id:
isLeaf = false
break;
valid_node.metadata.is_escape = isLeaf
valid_nodes[index] = valid_node;
index += 1
return {
"nodes": valid_nodes,
"connections": valid_connections,
"levels": num_levels,
"seed": _rng.seed
}
func _get_random_room_type(level, total_levels):
# return Utils.RoomType.BOSS
var boss_chance = 0.1 + (level / float(total_levels) * 0.1)
var shop_chance = 0.1 + (level / float(total_levels) * 0.05)
var event_chance = 0.05
if level == total_levels - 2:
boss_chance += 0.3
var roll = _rng.randf()
if roll < boss_chance:
return Utils.RoomType.BOSS
elif roll < boss_chance + shop_chance:
return Utils.RoomType.SHOP
elif roll < boss_chance + shop_chance + event_chance:
return Utils.RoomType.EVENT
else:
return Utils.RoomType.NORMAL
func _is_node_connected_to(node_id, connections):
for conn in connections:
if conn.to == node_id:
return true
return false
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
func generate_node_data(node, player):
var data = {
"id": node.id,
"type": node.type,
"level": node.level,
"position": node.position,
"elo": node.elo,
"metadata": node.metadata
}
match data.type:
Utils.RoomType.STARTING:
data.metadata = generate_starting_data(data, player)
Utils.RoomType.NORMAL:
data.metadata = generate_chess_data(data, player)
Utils.RoomType.BOSS:
data.metadata = generate_boss_data(data, player)
Utils.RoomType.FINAL:
data.metadata = generate_final_data(data, player)
Utils.RoomType.SHOP:
data.metadata = generate_shop_data(data, player)
Utils.RoomType.EVENT:
data.metadata = generate_event_data(data, player)
_:
data.metadata = {}
return data
func generate_boss_data(node, player):
# level_unit_distribution
# current_max_level
var rng = float(node.level) / int(current_max_level)
var game_type = Utils.BossType.ZERG
var height = 6;
# (current_value, min_value, max_value, min_index, max_index)
var index = map_to_array_index(node.level, 2, current_max_level - 2, 1, level_unit_distribution.size() - 1);
var unit_string = level_unit_distribution[index]
var special_unit = ""
var pawn_string = ""
var enemy_unit_depth = 3
if game_type == Utils.BossType.ZERG:
index = 7
height = 7
# if index > 7:
unit_string = level_unit_distribution[index]
elif game_type == Utils.BossType.DOUBLETROUBLE:
index = 9
height = 9
elif game_type == Utils.BossType.WARLARD:
index = 10
height = 10
unit_string = "1rnbqkbnr1"
special_unit = "rnnnqkbbbr"
# if game_type == "zerg":
# index = map_to_array_index(node.level, 5, current_max_level - 2, 1, level_unit_distribution.size() - 1);
for x in unit_string.length():
pawn_string += "p"
var fen = "";
# "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
if node.level >= 7 and node.level <= 10:
# height = node.level
enemy_unit_depth = 3
elif node.level > 10:
# height = 10
enemy_unit_depth = 4
for x in height - enemy_unit_depth:
if x == 0:
fen += unit_string + "/"
elif x == 1:
fen += pawn_string + "/"
# elif x == height - 3:
# fen += "u"
# # for y in unit_string.length() - 1:
# # fen += "*"
# fen += "u/"
else:
fen += str(unit_string.length()) + "/"
if game_type == Utils.BossType.ZERG:
for x in enemy_unit_depth - 1:
fen += pawn_string.to_upper() + "/"
fen += pawn_string.to_upper()
elif game_type == Utils.BossType.WARLARD:
fen += pawn_string.to_upper() + "/" + special_unit.to_upper()
else:
fen += pawn_string.to_upper() + "/" + unit_string.to_upper()
var fen_ending = " w KQkq - 0 1"
# print("generate_chess_data ", fen + fen_ending)
# change condition
var win_condition = Utils.WinConditionType.TURN_NUMBER
var win_target_turn = null
var loss_target_unit = null
var win_target_unit = null
var boss_turn_additional = null
var loss_condition = Utils.LossConditionType.UNIT_LOST
if game_type == Utils.BossType.ZERG:
win_condition = Utils.WinConditionType.TURN_NUMBER
# smallest board = 2 x 6 = 12
# largest board = 10 x 12 = 120
# smallest turn target 10
# largest turn target 50
var roomSize = height * unit_string.length()
var clamped_value = clamp(roomSize, 12, 120)
var percentage = (clamped_value - 12) / (120 - 12)
win_target_turn = 10 + percentage * (50 - 10)
loss_target_unit = "King"
loss_condition = Utils.LossConditionType.UNIT_LOST
elif game_type == Utils.BossType.DOUBLETROUBLE:
boss_turn_additional = 2
win_condition = Utils.WinConditionType.CAPTURE_UNIT
win_target_unit = "King"
loss_target_unit = "King"
loss_condition = Utils.LossConditionType.UNIT_LOST
return {
"is_escape": node.metadata.is_escape if node.metadata.has("is_escape") else false,
"fen": fen + fen_ending,
"game_type": "chess",
"boss_type": game_type,
"has_opponent": true,
"boss_turn_additional": boss_turn_additional,
"win_condition": win_condition,
"win_target_turn": win_target_turn,
"win_target_unit": win_target_unit,
"loss_target_unit": loss_target_unit,
"loss_condition": loss_condition,
"reward": {
"gold": 50 * node.level,
"cards": [],
"selection": generate_shop_cards(3, player),
"selection_limit": 2
},
"elo": node.elo,
}
func generate_shop_data(node, player):
var num_shop_cards = min(randi_range(5, 7), player.unlocked_cards.size())
return {
"is_escape": node.metadata.is_escape if node.metadata.has("is_escape") else false,
"cards": generate_shop_cards(num_shop_cards, player),
}
func generate_shop_cards(num_shop_cards, player):
var shop_cards = []
var all_cards = []
for card_class in player.unlocked_cards:
var card = create_new_card_instance(card_class)
all_cards.append(card)
all_cards.shuffle()
for i in range(num_shop_cards):
shop_cards.append(all_cards[i % player.unlocked_cards.size()])
return shop_cards
func generate_starting_data(node, player):
return {
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"elo": node.elo,
}
func generate_event_data(node, player):
var index = map_to_array_index(node.level, 2, current_max_level - 2, 1, level_unit_distribution.size() - 1);
var unit_string = level_unit_distribution[index]
var height = 6
var width = unit_string.length()
if node.level > 7 and node.level <= 10:
height = node.level
elif node.level > 10:
height = 10
# var mazedata = generate_maze(width, height)
var mazedata = generate_maze(12, 12)
# print("**************************")
# print("**************************")
# print("**************************")
# print(mazedata)
# print("**************************")
# print("**************************")
# print("**************************")
return {
"is_escape": node.metadata.is_escape if node.metadata.has("is_escape") else false,
"fen": mazedata.fen + " w KQkq - 0 1",
"game_type": "maze",
"has_opponent": false,
"win_condition": Utils.WinConditionType.TILE_REACHED,
"win_target": [mazedata.end],
"win_target_unit": "King",
"elo": node.elo,
"reward": {
"gold": 150 * node.level,
"cards": [],
"selection": generate_shop_cards(5, player),
"selection_limit": 1
},
}
# "rnbqkbnr1/pppppppp1/9/9/9/9/9/PPPPPPPP1/RNBQKBNR1 w KQkq - 0 1"
func generate_final_data(node, player):
# level_unit_distribution
# current_max_level
var rng = float(node.level) / int(current_max_level)
# print(node.level, " ", 2, " ", current_max_level - 2, " ", 1, " ", level_unit_distribution.size() - 1)
var index = map_to_array_index(node.level, 2, current_max_level - 2, 1, level_unit_distribution.size() - 1);
# print("generate_chess_data ", index)
var unit_string = level_unit_distribution[index]
var pawn_string = ""
for x in unit_string.length():
pawn_string += "p"
var height = 6;
# "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
var fen = "";
if node.level > 8 and node.level <= 10:
height = node.level
elif node.level > 10:
height = 10
for x in height - 2:
if x == 0:
fen += unit_string + "/"
elif x == 1:
fen += pawn_string + "/"
# elif x == height - 3:
# fen += "u"
# # for y in unit_string.length() - 1:
# # fen += "*"
# fen += "u/"
else:
fen += str(unit_string.length()) + "/"
fen += pawn_string.to_upper() + "/" + unit_string.to_upper()
var fen_ending = " w KQkq - 0 1"
# print("generate_chess_data ", fen + fen_ending)
var win_condition = Utils.WinConditionType.CAPTURE_UNIT # Default
var loss_condition = Utils.LossConditionType.UNIT_LOST # Default
# Randomly select a win condition with increasing variety at higher levels
var rng_val = _rng.randf()
var conditions_pool = [Utils.WinConditionType.CAPTURE_UNIT] # Default pool
var additional_metadata = {}
var result = {
"is_escape": true,
"fen": fen + fen_ending,
"game_type": "chess",
"win_condition": win_condition,
"loss_condition": loss_condition,
"has_opponent": true,
"elo": node.elo,
"reward": {
"gold": 50 * node.level
}
}
# Merge additional metadata
for key in additional_metadata:
result[key] = additional_metadata[key]
# print("final_node ", result)
return result
func generate_chess_data(node, player):
# level_unit_distribution
# current_max_level
var rng = float(node.level) / int(current_max_level)
# print(node.level, " ", 2, " ", current_max_level - 2, " ", 1, " ", level_unit_distribution.size() - 1)
var index = map_to_array_index(node.level, 2, current_max_level - 2, 1, level_unit_distribution.size() - 1);
# print("generate_chess_data ", index)
var unit_string = level_unit_distribution[index]
var pawn_string = ""
var height = 6;
# unit_string = level_unit_distribution[level_unit_distribution.size() - 1]
# height = 8
for x in unit_string.length():
pawn_string += "p"
# "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
var fen = "";
if node.level > 8 and node.level <= 10:
height = node.level
elif node.level > 10:
height = 10
for x in height - 2:
if x == 0:
fen += unit_string + "/"
elif x == 1:
fen += pawn_string + "/"
# elif x == height - 3:
# fen += "u"
# # for y in unit_string.length() - 1:
# # fen += "*"
# fen += "u/"
else:
fen += str(unit_string.length()) + "/"
fen += pawn_string.to_upper() + "/" + unit_string.to_upper()
var fen_ending = " w KQkq - 0 1"
# print("generate_chess_data ", fen + fen_ending)
var win_condition = Utils.WinConditionType.CAPTURE_UNIT # Default
var loss_condition = Utils.LossConditionType.UNIT_LOST # Default
# Randomly select a win condition with increasing variety at higher levels
var rng_val = _rng.randf()
var conditions_pool = [Utils.WinConditionType.CAPTURE_UNIT] # Default pool
# # Add more varied win conditions at higher levels
# if node.level >= 3:
# conditions_pool.append(Utils.WinConditionType.BOARD_CLEARED)
# if node.level >= 5:
# conditions_pool.append(Utils.WinConditionType.TILE_REACHED)
# if node.level >= 7:
# conditions_pool.append(Utils.WinConditionType.TURN_NUMBER)
# # For certain special nodes, always use specific conditions
# if node.type == Utils.RoomType.EVENT:
# win_condition = Utils.WinConditionType.TILE_REACHED
# elif node.type == Utils.RoomType.BOSS and node.level > 5:
# # Bosses at higher levels have more varied win conditions
# win_condition = conditions_pool[_rng.randi() % conditions_pool.size()]
# else:
# # Regular nodes have weighted randomness
# if rng_val < 0.7: # 70% chance of default capture king
# win_condition = Utils.WinConditionType.CAPTURE_UNIT
# else:
# # Pick randomly from the conditions pool
# win_condition = conditions_pool[_rng.randi() % conditions_pool.size()]
# Create additional metadata for specific win conditions
var additional_metadata = {}
# match win_condition:
# Utils.WinConditionType.TILE_REACHED:
# # Generate target tiles for the Tile win condition
# var target_tiles = []
# var target_count = 1 # Default to 1 target tile
# # Determine target unit (default is any piece)
# var target_unit = ""
# if _rng.randf() < 0.5: # 50% chance of specific piece
# var pieces = ["Pawn", "Knight", "Bishop", "Rook", "Queen"]
# target_unit = pieces[_rng.randi() % pieces.size()]
# # Create target tiles at the enemy's back rank
# var target_x = _rng.randi() % unit_string.length()
# target_tiles.append(str(target_x) + "-0") # Top row
# additional_metadata["target_tiles"] = target_tiles
# additional_metadata["target_unit"] = target_unit
# Utils.WinConditionType.TURN_NUMBER:
# # Set a target turn number
# additional_metadata["target_turn"] = (node.level * 2) + _rng.randi_range(5, 10)
# # Also add a turn limit for loss condition
# loss_condition = Utils.LossConditionType.TURN_NUMBER
# additional_metadata["turn_limit"] = additional_metadata["target_turn"] + _rng.randi_range(5, 15)
# Build the result metadata
var result = {
"is_escape": node.metadata.is_escape if node.metadata.has("is_escape") else false,
"fen": fen + fen_ending,
"game_type": "chess",
"win_condition": win_condition,
"loss_condition": loss_condition,
"has_opponent": true,
"elo": node.elo,
"reward": {
"gold": 50 * node.level
}
}
# Merge additional metadata
for key in additional_metadata:
result[key] = additional_metadata[key]
return result
func map_to_array_index(current_value, min_value, max_value, min_index, max_index):
# Ensure the current value is within bounds
var clamped_value = clamp(current_value, min_value, max_value)
# Calculate how far along the input range we are (0.0 to 1.0)
var normalized_position = float(clamped_value - min_value) / float(max_value - min_value)
# Map this to our target index range
var index_range = max_index - min_index
var mapped_index = min_index + round(normalized_position * index_range)
# Ensure we're returning an integer within the valid array index range
return int(clamp(mapped_index, min_index, max_index))
func generate_maze(width: int, height: int) -> Dictionary:
# Ensure dimensions are odd to have proper walls
if width % 2 == 0:
width += 1
if height % 2 == 0:
height += 1
# Initialize the maze with all walls
var maze = []
for y in range(height):
var row = []
for x in range(width):
row.append("*") # * represents wall
maze.append(row)
# Use a recursive backtracking algorithm to generate the maze
var rng = RandomNumberGenerator.new()
rng.randomize()
# Start at a random odd position
var start_x = rng.randi_range(0, width/2-1) * 2 + 1
var start_y = rng.randi_range(0, height/2-1) * 2 + 1
# Carve the maze recursively
_carve_maze(maze, start_x, start_y, width, height, rng)
# Pick a random end point (far from start point)
var end_x = 0
var end_y = 0
var max_distance = 0
for y in range(1, height, 2):
for x in range(1, width, 2):
if maze[y][x] == " ": # Only consider path cells
var distance = abs(x - start_x) + abs(y - start_y)
if distance > max_distance:
max_distance = distance
end_x = x
end_y = y
maze[start_y][start_x] = "k"
# Mark the end position with a space (keep it as a path)
# It's already a space, but we ensure it here
maze[end_y][end_x] = " "
# Convert the maze to a FEN-like string
var fen_string = ""
for y in range(height):
var empty_count = 0
for x in range(width):
if maze[y][x] == " ": # Empty space
empty_count += 1
else:
if empty_count > 0:
fen_string += str(empty_count)
empty_count = 0
fen_string += maze[y][x]
# Add any remaining empty count
if empty_count > 0:
fen_string += str(empty_count)
# Add row separator (except for the last row)
if y < height - 1:
fen_string += "/"
return {
"fen": fen_string,
"start": str(start_x) + "-"+ str(start_y),
"end": str(end_x) + "-"+ str(end_y)
}
# Recursive function to carve out the maze
func _carve_maze(maze, x, y, width, height, rng):
# Mark the current cell as a path
maze[y][x] = " "
# Define the four possible directions (up, right, down, left)
var directions = [[0, -2], [2, 0], [0, 2], [-2, 0]]
# Shuffle directions for randomness
directions.shuffle()
# Try each direction
for dir in directions:
var nx = x + dir[0]
var ny = y + dir[1]
# Check if the new position is valid and unvisited
if nx > 0 and nx < width-1 and ny > 0 and ny < height-1 and maze[ny][nx] == "*":
# Carve a path between the current cell and the new cell
maze[y + dir[1]/2][x + dir[0]/2] = " "
# Recursively carve from the new cell
_carve_maze(maze, nx, ny, width, height, rng)
func create_new_card_instance(template_card: Card) -> Card:
var new_card = null
var script = template_card.get_script()
if script:
new_card = script.new()
else:
print("Warning: Could not get script from card: " + template_card.cardName)
var class_list = ProjectSettings.get_global_class_list()
var card_class_name = template_card.get_class()
for class_info in class_list:
if class_info["class"] == card_class_name:
var card_script = load(class_info["path"])
if card_script:
new_card = card_script.new()
break
return new_card

View file

@ -1 +0,0 @@
uid://bs0u5jjxm2c18

View file

@ -1,556 +0,0 @@
extends Control
class_name MapScreen
signal back_pressed
signal deckmanager_open_requested(options)
signal node_selected(node_data)
signal map_visibility_changed(is_visible)
const MapGenerator = preload("res://Systems/Game/Map/MapGenerator.gd")
# Room type constants
# Node config
const NODE_SIZE = Vector2(60, 60)
const NODE_SPACING_X = 150
const NODE_SPACING_Y = 120
const LINE_WIDTH = 3
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 = {
Utils.RoomType.STARTING: "O", # Circle
Utils.RoomType.NORMAL: "", # Square
Utils.RoomType.BOSS: "", # Star
Utils.RoomType.FINAL: "", # Burst
Utils.RoomType.SHOP: "", # Star
Utils.RoomType.EVENT: "?" # Burst
}
const NODE_COLORS = {
Utils.RoomType.STARTING: Color(1, 1, 1), # White
Utils.RoomType.NORMAL: Color(0.2, 0.2, 0.2), # Dark gray
Utils.RoomType.BOSS: Color(0.9, 0.2, 0.2), # Red
Utils.RoomType.FINAL: Color(0.9, 0.9, 0.2), # Yellow
Utils.RoomType.SHOP: Color(0.2, 0.7, 0.9), # Yellow
Utils.RoomType.EVENT: Color(0.8, 0.4, 0.9) # Yellow
}
var map_nodes = []
var map_connections = []
var node_buttons = {}
var connection_lines = []
var traversed_map = []
var current_node = null
const SCROLL_PADDING_TOP = 80
const SCROLL_PADDING_BOTTOM = 80
const SCROLL_PADDING_LEFT = 60
const SCROLL_PADDING_RIGHT = 100
var node_popup_scene = preload("res://node_map_popup.tscn")
var selected_node_panel: NodePopup = null
@onready var map_container = $MapScrollContainer/MapContainer
@onready var back_button = $BackButton
@onready var deck_manager_button = $DeckManagerButton
@onready var title_label = $TitleLabel
@onready var legend_container = $LegendContainer
@onready var gold_label = $GoldLabel
func _ready():
# Connect back button
if back_button:
back_button.visible = false;
back_button.connect("pressed", Callable(self, "_on_back_button_pressed"))
if deck_manager_button:
deck_manager_button.connect("pressed", Callable(self, "_on_deck_button_pressed"))
# Create legend
create_legend()
# generate_map()
# display_map()
selected_node_panel = node_popup_scene.instantiate()
selected_node_panel.visible = false
add_child(selected_node_panel)
if selected_node_panel:
selected_node_panel.pressed.connect(on_node_panel_pressed)
func create_legend():
# Create legend for room types
var legend_items = [
{"type": Utils.RoomType.BOSS, "text": "Boss"},
{"type": Utils.RoomType.STARTING, "text": "Starting Room"},
{"type": Utils.RoomType.FINAL, "text": "Final Boss"},
{"type": Utils.RoomType.NORMAL, "text": "Normal Room"},
{"type": Utils.RoomType.SHOP, "text": "Shop"},
{"type": Utils.RoomType.EVENT, "text": "Event"},
{"type": Utils.RoomType.NORMAL, "text": "Escape Room"}
]
for item in legend_items:
var hbox = HBoxContainer.new()
hbox.add_theme_constant_override("separation", 10)
var symbol = Label.new()
symbol.text = NODE_SYMBOLS[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()
text.text = item.text
hbox.add_child(symbol)
hbox.add_child(text)
legend_container.add_child(hbox)
func generate_map(options):
# {"mode": options.mode}
# Clear existing map
var game = get_node_or_null("/root/Board") as ChessGame
var player = game.player
map_nodes.clear()
map_connections.clear()
connection_lines.clear()
traversed_map.clear()
var mapGen = MapGenerator.new().generate_map(options.mode, player)
# Create starting node
# var start_node = {
# "id": 0,
# "type": Utils.RoomType.STARTING,
# "level": 0,
# "position": Vector2(3, 4) # Column, Row
# }
# map_nodes.append(start_node)
# current_node = start_node
# # Create final boss node
# var final_node = {
# "id": 1,
# "type": Utils.RoomType.FINAL,
# "level": levels - 1,
# "position": Vector2(3, 0) # Column, Row
# }
# map_nodes.append(final_node)
for node_data in mapGen.nodes:
add_node(
node_data.id,
node_data.type,
node_data.level,
node_data.position,
node_data.elo,
node_data.metadata
)
# Store elo rating if needed
map_nodes[map_nodes.size() - 1].elo = node_data.elo
# Set current node to starting node (id 0)
if node_data.id == 0:
current_node = map_nodes[map_nodes.size() - 1]
# Process connections
for connection in mapGen.connections:
add_connection(connection.from, connection.to)
func add_node(id, type, level, position, elo, metadata):
map_nodes.append({
"id": id,
"type": type,
"level": level,
"position": position,
"elo": elo,
"metadata": metadata,
})
func add_connection(from_id, to_id):
map_connections.append({
"from": from_id,
"to": to_id
})
func display_map():
# Clear previous display
for child in map_container.get_children():
child.queue_free()
node_buttons.clear()
connection_lines = []
# Calculate map dimensions for proper padding
var min_y = 9999
var max_y = 0
var min_x = 9999
var max_x = 0
# Find boundaries of the map
for node_data in map_nodes:
var x_pos = node_data.position.x * NODE_SPACING_X
var y_pos = node_data.position.y * NODE_SPACING_Y
min_x = min(min_x, x_pos)
max_x = max(max_x, x_pos)
min_y = min(min_y, y_pos)
max_y = max(max_y, y_pos)
var map_width = max_x - min_x + NODE_SIZE.x * 2 + SCROLL_PADDING_LEFT + SCROLL_PADDING_RIGHT
var map_height = max_y - min_y + NODE_SIZE.y * 2 + SCROLL_PADDING_TOP + SCROLL_PADDING_BOTTOM
map_container.custom_minimum_size = Vector2(map_width, map_height)
var top_padding = Control.new()
top_padding.custom_minimum_size = Vector2(map_width, SCROLL_PADDING_TOP)
top_padding.position = Vector2(0, 0)
map_container.add_child(top_padding)
var bottom_padding = Control.new()
bottom_padding.custom_minimum_size = Vector2(map_width, SCROLL_PADDING_BOTTOM)
bottom_padding.position = Vector2(0, map_height - SCROLL_PADDING_BOTTOM)
map_container.add_child(bottom_padding)
var left_padding = Control.new()
left_padding.custom_minimum_size = Vector2(SCROLL_PADDING_LEFT, map_height)
left_padding.position = Vector2(0, 0)
map_container.add_child(left_padding)
var right_padding = Control.new()
right_padding.custom_minimum_size = Vector2(SCROLL_PADDING_RIGHT, map_height)
right_padding.position = Vector2(map_width - SCROLL_PADDING_RIGHT, 0)
map_container.add_child(right_padding)
# Draw connections first (so they're behind nodes)
for connection in map_connections:
var from_node = get_node_by_id(connection.from)
var to_node = get_node_by_id(connection.to)
if from_node and to_node:
draw_curved_connection(from_node, to_node)
# Draw nodes
for node_data in map_nodes:
draw_node(node_data)
func get_node_by_id(id):
for node in map_nodes:
if node.id == id:
return node
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
button.flat = true
# Add some styling
var font = FontFile.new()
var style = StyleBoxFlat.new()
style.bg_color = NODE_COLOR
style.corner_radius_top_left = 30
style.corner_radius_top_right = 30
style.corner_radius_bottom_left = 30
style.corner_radius_bottom_right = 30
style.border_width_left = 2
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)
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
var x_pos = node_data.position.x * NODE_SPACING_X + SCROLL_PADDING_LEFT
var y_pos = node_data.position.y * NODE_SPACING_Y + SCROLL_PADDING_TOP
button.position = Vector2(x_pos, y_pos) - NODE_SIZE/2
# Store data
button.set_meta("node_data", node_data)
# Connect signal
button.pressed.connect(func(): _on_node_pressed(node_data))
# Add to map
map_container.add_child(button)
node_buttons[node_data.id] = button
# Highlight current node
if current_node and node_data.id == current_node.id:
highlight_current_node(button)
func on_node_panel_pressed(id):
# print("on_node_panel_pressed ", id )
if id != null:
current_node = null
for node in map_nodes:
if node.id == id:
current_node = node
break
traversed_node(current_node)
# Refresh display to update highlights
display_map()
# Emit signal with selected node data
emit_signal("node_selected", current_node)
selected_node_panel.visible = false;
func draw_connection(from_node, to_node):
var line = Line2D.new()
line.width = LINE_WIDTH
line.default_color = LINE_COLOR
if from_node.id == current_node.id:
line.default_color = LINE_COLOR_ACCESSIBLE
elif traversed_map.has(to_node.id) and (traversed_map.has(from_node.id) || from_node.id == 0):
line.default_color = LINE_COLOR_SELECTED
# Calculate start and end positions with top and left padding adjustments
var start_pos = from_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
var end_pos = to_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
# Add points
line.add_point(start_pos)
line.add_point(end_pos)
# Add to map
map_container.add_child(line)
connection_lines.append(line)
func highlight_current_node(button):
var style = button.get_theme_stylebox("normal").duplicate()
style.bg_color = Color(0.3, 0.3, 0.3, 0.7)
style.border_width_left = 4
style.border_width_top = 4
style.border_width_right = 4
style.border_width_bottom = 4
button.add_theme_stylebox_override("normal", style)
func _on_node_pressed(node_data):
print("SELECTED NODE", node_data)
if is_node_accessible(node_data) || is_node_path_accessible(node_data):
# Get node type description
var node_name = get_node_type_name(node_data.type)
var node_desc = get_node_description(node_data)
# Position the popup near the clicked node
var button_pos = node_buttons[node_data.id].global_position
var popup_pos = button_pos + Vector2(NODE_SIZE.x/2, NODE_SIZE.y/2)
selected_node_panel.global_position = popup_pos
# Set popup data and make it visible
selected_node_panel.set_card(node_data.id, node_name, node_desc)
if traversed_map.has(node_data.id):
selected_node_panel._disable_enter()
else:
selected_node_panel._enable_enter()
selected_node_panel.visible = true
else:
print("Node not accessible from current position", node_data)
func traversed_node(node_data):
traversed_map.append(node_data.id)
func is_node_path_accessible(node_data):
if current_node == null:
return node_data.type == Utils.RoomType.STARTING
# Check if there's a direct connection from current node to the path travelled
for id in traversed_map:
var cur = get_node_by_id(id)
for connection in map_connections:
if connection.from == cur.id and connection.to == node_data.id:
return true
return false
func is_node_accessible(node_data):
if current_node == null:
return node_data.type == Utils.RoomType.STARTING
# Check if there's a direct connection from current node to target node
for connection in map_connections:
if connection.from == current_node.id and connection.to == node_data.id:
return true
return false
func _on_back_button_pressed():
emit_signal("back_pressed")
visible = false
func _on_deck_button_pressed():
emit_signal("deckmanager_open_requested", {})
visible = false
func draw_curved_connection(from_node, to_node):
var line = Line2D.new()
line.width = LINE_WIDTH
line.default_color = LINE_COLOR
if from_node.id == current_node.id:
line.default_color = LINE_COLOR_ACCESSIBLE
elif traversed_map.has(to_node.id) and (traversed_map.has(from_node.id) || from_node.id == 0):
line.default_color = LINE_COLOR_SELECTED
var start_pos = from_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
var end_pos = to_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
# Create a bezier curve path
var points = []
# If nodes are on different levels (y positions)
if from_node.position.y != to_node.position.y:
var mid_y = (start_pos.y + end_pos.y) / 2
var control_offset = NODE_SPACING_Y * 0.5 * (1 + abs(from_node.position.x - to_node.position.x) * 0.2)
var control1 = Vector2(start_pos.x, start_pos.y + control_offset)
var control2 = Vector2(end_pos.x, end_pos.y - control_offset)
# Add more points for a smoother curve
for i in range(11): # 0.0, 0.1, 0.2, ..., 1.0
var t = i / 10.0
var point = cubic_bezier(start_pos, control1, control2, end_pos, t)
points.append(point)
else:
# For nodes on the same level, use a simple curved path
var mid_x = (start_pos.x + end_pos.x) / 2
var mid_y = start_pos.y
var curve_height = NODE_SPACING_Y * 0.3 * (1 + abs(from_node.position.x - to_node.position.x) * 0.1)
if to_node.position.x > from_node.position.x:
mid_y -= curve_height
else:
mid_y += curve_height
var mid_point = Vector2(mid_x, mid_y)
# Create a quadratic bezier curve with just 3 points
for i in range(11):
var t = i / 10.0
var point = quadratic_bezier(start_pos, mid_point, end_pos, t)
points.append(point)
# Add the points to the line
for point in points:
line.add_point(point)
# Add to map
map_container.add_child(line)
connection_lines.append(line)
func cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float) -> Vector2:
var q0 = p0.lerp(p1, t)
var q1 = p1.lerp(p2, t)
var q2 = p2.lerp(p3, t)
var r0 = q0.lerp(q1, t)
var r1 = q1.lerp(q2, t)
return r0.lerp(r1, t)
func quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float) -> Vector2:
var q0 = p0.lerp(p1, t)
var q1 = p1.lerp(p2, t)
return q0.lerp(q1, t)
func get_node_type_name(type):
match type:
Utils.RoomType.STARTING:
return "Starting Room"
Utils.RoomType.NORMAL:
return "Chess Match"
Utils.RoomType.BOSS:
return "Boss Battle"
Utils.RoomType.FINAL:
return "Final Boss"
Utils.RoomType.SHOP:
return "Card Shop"
Utils.RoomType.EVENT:
return "Random Event"
_:
return "Unknown Room"
func get_node_description(node_data):
var desc = ""
match node_data.type:
Utils.RoomType.STARTING:
desc = "Begin the dungeon"
Utils.RoomType.NORMAL:
desc = "A chess match \n\n ELO: " + str(node_data.elo)
Utils.RoomType.BOSS:
desc = "Maybe add some Boss lore? \n\n ELO: " + str(node_data.elo)
Utils.RoomType.FINAL:
desc = "BHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA"
Utils.RoomType.SHOP:
desc = "Purchase new cards"
Utils.RoomType.EVENT:
desc = "A random event from pawn zerg to slot machine"
_:
desc = "An unknown room type."
# Add additional info if this is a leaf node (dead end)
var isLeaf = true
for connection in map_connections:
if connection.from == node_data.id || node_data.id == 1:
isLeaf = false
break
if isLeaf && node_data.type != Utils.RoomType.FINAL:
desc += "\n\nWarning: This is an escape node"
return desc
func update_gold_display():
var game = get_node_or_null("/root/Board") as ChessGame
if gold_label:
gold_label.text = str(game.player.get_gold()) + " GOLD"
# Notification
func _notification(what):
if what == NOTIFICATION_VISIBILITY_CHANGED:
_on_visibility_changed(visible)
func _on_visibility_changed(is_visible):
print("MapScreen visibility changed to: ", is_visible)
if is_visible:
update_gold_display()
else:
print("MapScreen is now invisible")
emit_signal("map_visibility_changed", is_visible)

View file

@ -1 +0,0 @@
uid://ckpv3snpg6g34

View file

@ -1,147 +0,0 @@
extends RefCounted
class_name NodeLayoutHelper
# Configuration
var map_width: int = 6 # Available horizontal positions (0-5)
var level_height: float = 1.0 # Vertical spacing between levels
var node_spread: float = 0.7 # How much to spread nodes horizontally (0-1)
var path_smoothness: float = 0.3 # How much to align nodes with their connections (0-1)
# Internal
var _rng = RandomNumberGenerator.new()
func _init(seed_value: int = 0):
if seed_value != 0:
_rng.seed = seed_value
else:
_rng.randomize()
# Organize node positions for better visual layout
func organize_layout(nodes: Array, connections: Array) -> Array:
# Clone the nodes so we don't modify the original array
var updated_nodes = []
for node in nodes:
updated_nodes.append(node.duplicate())
# Group nodes by level
var nodes_by_level = {}
for node in updated_nodes:
var level = node.level
if not nodes_by_level.has(level):
nodes_by_level[level] = []
nodes_by_level[level].append(node)
# Process each level
for level in nodes_by_level:
var level_nodes = nodes_by_level[level]
# Skip levels with fixed positions
if level == 0 or level == nodes_by_level.size() - 1:
continue
# Calculate optimal positions based on connections
for node in level_nodes:
_adjust_node_position(node, updated_nodes, connections, nodes_by_level)
# Resolve overlaps after initial placement
for level in nodes_by_level:
var level_nodes = nodes_by_level[level]
if level_nodes.size() > 1:
_resolve_horizontal_overlaps(level_nodes)
return updated_nodes
# Adjust node position based on its connections
func _adjust_node_position(node, all_nodes, connections, nodes_by_level):
# Find all connections to/from this node
var connected_from = [] # Nodes in the previous level connecting to this node
var connected_to = [] # Nodes in the next level this node connects to
for conn in connections:
if conn.to == node.id:
var from_node = _find_node_by_id(all_nodes, conn.from)
if from_node and from_node.level == node.level - 1:
connected_from.append(from_node)
if conn.from == node.id:
var to_node = _find_node_by_id(all_nodes, conn.to)
if to_node and to_node.level == node.level + 1:
connected_to.append(to_node)
# Calculate optimal x position based on connected nodes
var optimal_x = node.position.x # Start with current position
if connected_from.size() > 0 or connected_to.size() > 0:
var avg_x = 0.0
var total_connected = 0
# Consider positions of connected nodes
for from_node in connected_from:
avg_x += from_node.position.x
total_connected += 1
for to_node in connected_to:
avg_x += to_node.position.x
total_connected += 1
if total_connected > 0:
avg_x /= total_connected
# Blend between current position and optimal position
optimal_x = lerp(node.position.x, avg_x, path_smoothness)
# Add some random variation to prevent everything aligning too perfectly
optimal_x += (_rng.randf() - 0.5) * node_spread
# Keep within bounds
optimal_x = clamp(optimal_x, 0, map_width)
# Update node position
node.position.x = optimal_x
# Y position is determined by level
node.position.y = node.level * level_height
# Resolve horizontal overlaps between nodes on the same level
func _resolve_horizontal_overlaps(level_nodes):
# Sort nodes by x position
level_nodes.sort_custom(func(a, b): return a.position.x < b.position.x)
# Minimum distance between nodes
var min_distance = 1.0
# Check for overlaps and adjust
for i in range(1, level_nodes.size()):
var prev_node = level_nodes[i-1]
var curr_node = level_nodes[i]
if curr_node.position.x - prev_node.position.x < min_distance:
# If too close, move them apart
var midpoint = (prev_node.position.x + curr_node.position.x) / 2
var half_distance = min_distance / 2
# Try to maintain relative positions
prev_node.position.x = midpoint - half_distance
curr_node.position.x = midpoint + half_distance
# Ensure we stay within bounds
if prev_node.position.x < 0:
var shift = -prev_node.position.x
prev_node.position.x += shift
curr_node.position.x += shift
if curr_node.position.x > map_width:
var shift = curr_node.position.x - map_width
prev_node.position.x -= shift
curr_node.position.x -= shift
# Final boundary check for previous node
prev_node.position.x = max(0, prev_node.position.x)
# Find a node by its ID
func _find_node_by_id(nodes, id):
for node in nodes:
if node.id == id:
return node
return null

View file

@ -1 +0,0 @@
uid://bmi14xk8tn7xc

View file

@ -1,132 +0,0 @@
# Add or modify this in your CardVisual.gd script
extends Control
class_name NodePopup
signal pressed(id)
# Node references
@onready var card_container = $CardContainer
@onready var name_label = $CardContainer/VBoxContainer/NameLabel
@onready var desc_label = $CardContainer/VBoxContainer/DescriptionLabel
@onready var enter_btn = $CardContainer/VBoxContainer/HBoxContainer/EnterButton
@onready var close_btn = $CardContainer/VBoxContainer/HBoxContainer/CloseButton
var node_id = null;
var animation_tween
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
if enter_btn:
enter_btn.connect("pressed", Callable(self, "_on_enter_button_pressed"))
if close_btn:
close_btn.connect("pressed", Callable(self, "_on_close_button_pressed"))
card_container.visible = false
# Setup initial scale for animation
card_container.scale = Vector2(0.8, 0.8)
set_process_input(true)
func set_card(id:int, name: String, desc: String):
if id == null:
card_container.visible = false
return
node_id = id
card_container.visible = true
name_label.text = name
desc_label.text = desc
# Animate popup appearance
if animation_tween:
animation_tween.kill()
animation_tween = create_tween()
animation_tween.tween_property(card_container, "scale", Vector2(1.0, 1.0), 0.2).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
# Make sure popup is fully visible on screen
call_deferred("ensure_on_screen")
func ensure_on_screen():
await get_tree().process_frame
# Get viewport size
var viewport_size = get_viewport_rect().size
# Get popup global position and size
var popup_rect = Rect2(global_position, card_container.size)
# Adjust if off-screen
if popup_rect.position.x < 0:
global_position.x = 10
elif popup_rect.position.x + popup_rect.size.x > viewport_size.x:
global_position.x = viewport_size.x - popup_rect.size.x - 10
if popup_rect.position.y < 0:
global_position.y = 10
elif popup_rect.position.y + popup_rect.size.y > viewport_size.y:
global_position.y = viewport_size.y - popup_rect.size.y - 10
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.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.0, 1.0), 0.1)
# Handle dragging the popup
var dragging = false
var drag_start_pos = Vector2()
func _input(event):
if not visible or not card_container.visible:
return
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
# Check if mouse is over the card container
var local_mouse_pos = get_local_mouse_position()
if card_container.get_rect().has_point(local_mouse_pos):
dragging = true
drag_start_pos = local_mouse_pos - card_container.position
else:
dragging = false
elif event is InputEventMouseMotion and dragging:
card_container.position = get_local_mouse_position() - drag_start_pos
func _on_enter_button_pressed():
# Add a confirmation animation before closing
var tween = create_tween()
tween.tween_property(card_container, "scale", Vector2(1.1, 1.1), 0.1)
tween.tween_property(card_container, "scale", Vector2(0.8, 0.8), 0.2)
tween.tween_callback(func(): _confirm_selection())
func _disable_enter():
enter_btn.disabled = true;
func _enable_enter():
enter_btn.disabled = false;
func _confirm_selection():
emit_signal("pressed", node_id)
card_container.visible = false
visible = false
func _on_close_button_pressed():
# Add a closing animation
var tween = create_tween()
tween.tween_property(card_container, "scale", Vector2(0.8, 0.8), 0.2)
tween.tween_callback(func(): _cancel_selection())
func _cancel_selection():
emit_signal("pressed", null)
card_container.visible = false
visible = false

View file

@ -1 +0,0 @@
uid://tsgxcwb1u8uc

View file

@ -1,491 +0,0 @@
extends Control
class_name MenuContainer
const VERSION_FILE_PATH = "res://Game.json"
# Signal to notify parent when a new game is requested
# signal new_game_requested
signal reward_closed(node)
signal new_game_requested(options)
signal shop_open_requested(options)
signal reward_open_requested(options)
signal deckmanager_open_requested(options)
signal map_open_requested(options)
signal node_completed(options)
signal shop_closed
signal card_purchased(card, price)
signal card_unlocked(card, price)
signal lobby_open_requested(options)
signal lobby_shop_open_requested(options)
signal vanilla_mode_selected
signal deeper_mode_selected
@onready var newGameButton = $HBoxContainer/VBoxContainer/MenuOptions/NewGameText
@onready var continueButton = $HBoxContainer/VBoxContainer/MenuOptions/Continue
@onready var optionsButton = $HBoxContainer/VBoxContainer/MenuOptions/Options
@onready var versionText = $HBoxContainer/VBoxContainer/VersionText
@onready var titleText = $HBoxContainer/VBoxContainer/TitleText
@onready var developerText = $HBoxContainer/VBoxContainer/DeveloperText
@onready var gameMenuScreen = get_node("/root/Board/GameMenuScreen")
@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")
@onready var progressionScreen = get_node("/root/Board/ProgressionScreen")
@onready var rewardScreen = get_node("/root/Board/RewardScreen")
@onready var game = get_node_or_null("/root/Board") as ChessGame
@onready var lobbyScreen = get_node_or_null("/root/Board/LobbyScreen")
@onready var lobbyShopScreen = get_node_or_null("/root/Board/LobbyShopScreen")
@onready var preloadhandScreen = get_node_or_null("/root/Board/HandPreloadScreen")
var player_gold = 10
var back_to_map = false
var winConditionManager: WinConditionManager
var currentWinCondition = Utils.WinConditionType.CAPTURE_UNIT
var currentLossCondition = Utils.LossConditionType.UNIT_LOST
func _ready():
# Connect menu option signals
_connect_button(newGameButton, "_on_new_game_pressed")
_connect_button(continueButton, "_on_continue_pressed")
_connect_button(optionsButton, "_on_options_pressed")
if gameMenuScreen:
gameMenuScreen.setup(self)
gameMenuScreen.connect("shop_open_requested", Callable(self, "_on_shop_open_requested"))
gameMenuScreen.connect("deckmanager_open_requested", Callable(self, "_on_deckmanager_open_requested"))
gameMenuScreen.connect("map_open_requested", Callable(self, "_on_map_open_requested"))
gameMenuScreen.connect("new_game_requested", Callable(self, "_on_start_game_pressed"))
gameMenuScreen.connect("reward_open_requested", Callable(self, "_on_reward_open_requested"))
gameMenuScreen.connect("lobby_open_requested", Callable(self, "_on_lobby_open_requested"))
gameMenuScreen.visible = false
else:
connect("map_open_requested", Callable(self, "_on_map_open_requested"))
connect("reward_closed", Callable(self, "_on_reward_completed"))
game.connect("node_completed", Callable(self, "_on_node_completed"))
if deckManagerScreen:
deckManagerScreen.connect("back_pressed", Callable(self, "_on_deck_manager_back_pressed"))
deckManagerScreen.visible = false
if preloadhandScreen:
preloadhandScreen.connect("back_pressed", Callable(self, "_on_preload_back_pressed"))
preloadhandScreen.visible = false
if progressionScreen:
progressionScreen.connect("save_pressed", Callable(self, "_on_progression_save_pressed"))
progressionScreen.visible = false
if mapScreen:
mapScreen.connect("back_pressed", Callable(self, "_on_map_back_pressed"))
mapScreen.connect("deckmanager_open_requested", Callable(self, "_on_deckmanager_open_requested_from_map"))
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
if rewardScreen:
rewardScreen.connect("back_pressed", Callable(self, "_on_reward_back_pressed"))
rewardScreen.connect("card_purchased", Callable(self, "_on_reward_card"))
rewardScreen.visible = false
if lobbyScreen:
lobbyScreen.connect("vanilla_selected", Callable(self, "_on_vanilla_selected"))
lobbyScreen.connect("preload_selected", Callable(self, "_on_preload_selected"))
lobbyScreen.connect("shop_selected", Callable(self, "_on_lobby_shop_open_requested"))
lobbyScreen.connect("deeper_selected", Callable(self, "_on_deeper_selected"))
lobbyScreen.connect("back_pressed", Callable(self, "_on_lobby_back_pressed"))
# lobbyScreen.connect("map_open_requested", Callable(self, "_on_map_open_requested"))
lobbyScreen.visible = false
if lobbyShopScreen:
lobbyShopScreen.connect("back_pressed", Callable(self, "_on_lobby_shop_back_pressed"))
lobbyShopScreen.connect("card_unlocked", Callable(self, "_on_lobby_shop_card_unlocked"))
lobbyShopScreen.connect("hand_size_increased", Callable(self, "_on_hand_size_increased"))
lobbyShopScreen.visible = false
load_version()
func load_version():
var version = "v.0.0.0"
var title = "ChessBuilder"
var developer = ""
var file = FileAccess.open(VERSION_FILE_PATH, FileAccess.READ)
if file:
var json_text = file.get_as_text()
file.close()
# Parse the JSON
var json = JSON.new()
var error = json.parse(json_text)
if error == OK:
var data = json.get_data()
if data and typeof(data) == TYPE_DICTIONARY and data.has("version"):
version = "v " + str(data.version)
if data and typeof(data) == TYPE_DICTIONARY and data.has("title"):
title = str(data.title)
if data and typeof(data) == TYPE_DICTIONARY and data.has("developer"):
developer = str(data.developer)
else:
print("JSON Parse Error: ", json.get_error_message())
if versionText:
versionText.text = version
if titleText:
titleText.text = title
if developerText:
developerText.text = developer
# Connect a button signal to a method
func _connect_button(button, method_name):
if button and button.has_signal("pressed"):
if !button.is_connected("pressed", Callable(self, method_name)):
button.connect("pressed", Callable(self, method_name))
func _on_preload_back_pressed():
if preloadhandScreen:
preloadhandScreen.visible = false
if lobbyScreen:
lobbyScreen.visible = true
# Handle New Game button press
func _on_new_game_pressed():
print("New Game pressed, showing game menu")
self.visible = false
if gameMenuScreen:
gameMenuScreen.visible = true
# _on_map_open_requested({})
# emit_signal("new_game_requested")
func _on_lobby_open_requested(options):
print("Lobby requested with options:", options)
if gameMenuScreen:
gameMenuScreen.visible = false
if lobbyScreen:
lobbyScreen.visible = true
lobbyScreen.initialize(options)
lobbyScreen.show_screen()
print("_on_lobby_open_requested" )
func _on_lobby_back_pressed():
if lobbyScreen:
lobbyScreen.visible = false
self.visible = true
func _on_preload_selected():
print("Preload mode selected")
preloadhandScreen.initialize(null)
preloadhandScreen.visible = true
func _on_vanilla_selected():
print("Vanilla mode selected")
if gameMenuScreen:
gameMenuScreen.visible = true
game.mode = "vanilla"
_on_map_open_requested({"mode": "vanilla", "hand_size": 2, })
# emit_signal("vanilla_mode_selected")
func _on_deeper_selected():
print("Deeper mode selected")
if gameMenuScreen:
gameMenuScreen.visible = true
game.mode = "deeper"
_on_map_open_requested({"mode": "deeper", "hand_size": game.player.hand_size,})
func _on_lobby_shop_open_requested(options):
print("Lobby Shop requested with options:", options)
if lobbyScreen:
lobbyScreen.visible = false
if lobbyShopScreen:
lobbyShopScreen.visible = true
lobbyShopScreen.initialize(options)
emit_signal("lobby_shop_open_requested", options)
func _on_lobby_shop_back_pressed():
if lobbyShopScreen:
lobbyShopScreen.visible = false
if lobbyScreen:
lobbyScreen.visible = true
func _on_lobby_shop_card_unlocked(card, token_cost):
# Add the card to the player's bank
if game and "deckManager" in game:
var deck_manager = game.deckManager
game.player.unlock_card(card)
print("Added unlocked card to whitelist: ", card.cardName)
func _on_hand_size_increased():
print("Hand size increased")
# Handle Continue button press
func _on_continue_pressed():
print("Continue pressed")
# Implement continue game logic here
# self.visible = false
# You can add logic to load a saved game if needed
# Handle Options button press
func _on_options_pressed():
print("Options pressed")
# Implement options menu logic here
# You could show an options panel or switch to an options menu
# 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 = {}
if "gold" in options:
options["gold"] = game.player.get_gold()
if mapScreen:
mapScreen.visible = false
if shopScreen:
shopScreen.visible = true
shopScreen.initialize(options)
emit_signal("shop_open_requested", options)
func _on_shop_back_pressed():
if shopScreen:
shopScreen.visible = false
game.player.set_gold(shopScreen.player_gold)
# Show game menu again
if not back_to_map and gameMenuScreen:
gameMenuScreen.visible = true
if back_to_map:
mapScreen.visible = true
# Emit signal that shop was closed
emit_signal("shop_closed")
func _on_shop_card_purchased(card, price):
game.player.remove_gold(price)
# Forward the signal
emit_signal("card_purchased", card, price)
# Add the card to the player's bank
if game and "deckManager" in game:
var deck_manager = game.deckManager
deck_manager.bank.append(card)
print("Added purchased card to bank:", card.cardName)
func _on_reward_open_requested(options):
print("Reward requested with options:", options)
if shopScreen:
shopScreen.visible = false
if options == null:
options = {}
if "gold" in options.metadata:
options.metadata["gold"] = game.player.get_gold()
if rewardScreen:
rewardScreen.visible = true
rewardScreen.initialize(options)
emit_signal("reward_open_requested", options)
func _on_reward_back_pressed(completed_node):
if rewardScreen:
rewardScreen.visible = false
if not back_to_map and gameMenuScreen:
gameMenuScreen.visible = true
if back_to_map:
mapScreen.visible = true
print("DONE: ", completed_node)
var is_escape = completed_node.metadata.is_escape
emit_signal("reward_closed", completed_node)
func _on_reward_card(card, price):
# Forward the signal
emit_signal("card_purchased", card, price)
# Add the card to the player's bank
if game and "deckManager" in game:
var deck_manager = game.deckManager
deck_manager.bank.append(card)
print("Added reward card to bank:", card.cardName)
func _on_reward_completed(completed_node):
var is_escape = completed_node.metadata.is_escape
if progressionScreen and is_escape:
progressionScreen.initialize({"max_deck_size": 10, "node": completed_node})
if completed_node.type == Utils.RoomType.FINAL:
game.player.add_tokens(1)
progressionScreen.visible = true
func _on_progression_save_pressed():
print("_on_progression_save_pressed")
progressionScreen.visible = false
gameMenuScreen.visible = false
_on_lobby_open_requested(null)
func _on_deckmanager_open_requested_from_map(options):
print("Deck Manager requested with options:", options)
# Hide game menu
gameMenuScreen.visible = false
# Show and initialize deck manager
if deckManagerScreen:
deckManagerScreen.visible = true
deckManagerScreen.initialize(options)
back_to_map = true
# Also emit signal for other systems that might need to know
emit_signal("deckmanager_open_requested", options)
func _on_deckmanager_open_requested(options):
print("Deck Manager requested with options:", options)
# Hide game menu
gameMenuScreen.visible = false
# Show and initialize deck manager
if deckManagerScreen:
deckManagerScreen.visible = true
deckManagerScreen.initialize(options)
# Also emit signal for other systems that might need to know
emit_signal("deckmanager_open_requested", options)
func _on_deck_manager_back_pressed():
# Return to game menu screen
deckManagerScreen.visible = false
if not back_to_map:
gameMenuScreen.visible = true
else:
mapScreen.visible = true
func _on_map_open_requested(options):
print("Map requested with options:", options)
gameMenuScreen.visible = false
if "hand_size" in options:
game.player.update_deck_hand_size(options.hand_size)
if mapScreen:
mapScreen.generate_map({"mode": options.mode})
mapScreen.display_map()
mapScreen.visible = true
emit_signal("map_open_requested", options)
func _on_start_game_pressed(options):
print("Starting game with options:", options)
emit_signal("new_game_requested", options)
func _on_map_back_pressed():
mapScreen.visible = false
gameMenuScreen.visible = true
# Map node selection
func _on_map_node_selected(node_data):
print("Selected map node: ", node_data)
if node_data.type == Utils.RoomType.SHOP:
print("Utils.RoomType.SHOP")
back_to_map = true
_on_shop_open_requested(node_data.metadata)
elif node_data.type == Utils.RoomType.FINAL:
print("Utils.RoomType.FINAL")
if gameMenuScreen:
gameMenuScreen.visible = false
if deckManagerScreen:
deckManagerScreen.visible = false
if mapScreen:
mapScreen.visible = false
back_to_map = true
_on_start_game_pressed(node_data)
elif node_data.type == Utils.RoomType.NORMAL:
print("Utils.RoomType.NORMAL")
if gameMenuScreen:
gameMenuScreen.visible = false
if deckManagerScreen:
deckManagerScreen.visible = false
if mapScreen:
mapScreen.visible = false
back_to_map = true
_on_start_game_pressed(node_data)
elif node_data.type == Utils.RoomType.BOSS:
print("Utils.RoomType.BOSS")
if gameMenuScreen:
gameMenuScreen.visible = false
if deckManagerScreen:
deckManagerScreen.visible = false
if mapScreen:
mapScreen.visible = false
back_to_map = true
_on_start_game_pressed(node_data)
elif node_data.type == Utils.RoomType.EVENT:
print("Utils.RoomType.EVENT")
if gameMenuScreen:
gameMenuScreen.visible = false
if deckManagerScreen:
deckManagerScreen.visible = false
if mapScreen:
mapScreen.visible = false
back_to_map = true
_on_start_game_pressed(node_data)
func _on_node_completed(options):
var node_data = options.node
var completed = options.completed
var success = options.success
if success:
_on_reward_open_requested(node_data)
print("**********************************")
print("**********************************")
print("**********************************")
print("**********************************")
print("**************MATCH***************")
print("**************OVER****************")
print("**********************************")
print("**********************************")
print("**********************************")
print("**********************************")
else:
print("**********************************")
print("**********************************")
print("**********************************")
print("**********************************")
print("**************GAME****************")
print("**************OVER****************")
print("*************GITGUD***************")
print("**********************************")
print("**********************************")
print("**********************************")
# Public method to show the menu
func show_menu():
self.visible = true
if gameMenuScreen:
gameMenuScreen.visible = false
if deckManagerScreen:
deckManagerScreen.visible = false
if mapScreen:
mapScreen.visible = false

View file

@ -1 +0,0 @@
uid://bf5ljae05pvla

View file

@ -1,39 +0,0 @@
extends TextureRect
class_name MenuOption
signal pressed
# Original modulate to return to after hover
var _original_modulate: Color
func _ready():
# Make this texture rect clickable
mouse_filter = Control.MOUSE_FILTER_STOP
# Save original modulate color
_original_modulate = modulate
# Connect the gui_input signal to our own handler
connect("gui_input", Callable(self, "_on_gui_input"))
# Connect hover signals for better user experience
connect("mouse_entered", Callable(self, "_on_mouse_entered"))
connect("mouse_exited", Callable(self, "_on_mouse_exited"))
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
emit_signal("pressed")
get_viewport().set_input_as_handled()
func _on_mouse_entered():
# Change appearance when mouse hovers
modulate = Color(1.2, 1.2, 0.8) # Slight yellowish tint and brightening
# Optional: Add a hover sound effect
# if has_node("/root/SoundManager"):
# get_node("/root/SoundManager").play_hover_sound()
func _on_mouse_exited():
# Restore original appearance
modulate = _original_modulate

View file

@ -1 +0,0 @@
uid://dbm5dv81lbdod

View file

@ -1,50 +0,0 @@
extends RichTextLabel
class_name MenuTextOption
signal pressed
# Style properties
var normal_color: Color = Color(1, 1, 1, 1) # White
var hover_color: Color = Color(1, 1, 0, 1) # Yellow
var font_size: int = 24
func _ready():
# Make this label clickable
mouse_filter = Control.MOUSE_FILTER_STOP
# Set up base styling
add_theme_font_size_override("normal_font_size", font_size)
add_theme_color_override("default_color", normal_color)
# Make text bold
bbcode_enabled = true
text = "[b]" + text + "[/b]"
# Connect the gui_input signal to our own handler
connect("gui_input", Callable(self, "_on_gui_input"))
# Connect hover signals for better user experience
connect("mouse_entered", Callable(self, "_on_mouse_entered"))
connect("mouse_exited", Callable(self, "_on_mouse_exited"))
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
emit_signal("pressed")
get_viewport().set_input_as_handled()
func _on_mouse_entered():
# Change appearance when mouse hovers
add_theme_color_override("default_color", hover_color)
# Scale up slightly on hover (optional)
var tween = create_tween()
tween.tween_property(self, "scale", Vector2(1.05, 1.05), 0.1)
func _on_mouse_exited():
# Restore original appearance
add_theme_color_override("default_color", normal_color)
# Scale back to normal (optional)
var tween = create_tween()
tween.tween_property(self, "scale", Vector2(1.0, 1.0), 0.1)

View file

@ -1 +0,0 @@
uid://c47i2m0ll101x

View file

@ -1,167 +0,0 @@
extends Node
class_name Player
var attached_cards: Dictionary = {}
var attached_effects: Dictionary = {}
var hand_size: int = 2
var gold: int = 70
var tokens: int = 1
var game: ChessGame
var run_count: int = 0
const MAX_HAND_SIZE = 5
var unlocked_cards = []
var cards_by_rank = {
Card.Rank.RANK_0: [], # Most powerful (one-time use)
Card.Rank.RANK_1: [], # Once per match
Card.Rank.RANK_2: [], # Discard pile cards
Card.Rank.RANK_3: [] # Basic cards
}
var cards_by_name = {}
func _init(g: int, size: int, tok: int, gm: ChessGame):
print("INIT PLAYER CHARACTER")
# var classList = ProjectSettings.get_global_class_list();
# for classType in classList:
# if classType.base == "Card":
# print("ALL CARDS ", classType)
gold = g
tokens = tok
game = gm
hand_size = size
update_deck_hand_size(size)
load_all_cards()
initialize_deck_from_defaults(unlocked_cards)
# Debug output
print_card_list()
func initialize_deck_from_defaults(unlocked_cards: Array = []):
if "deckManager" in game:
game.deckManager.initializeStartingDeck(unlocked_cards)
func update_deck_hand_size(size: int):
if "deckManager" in game:
game.deckManager.set_hand_size(hand_size)
func set_hand_size(size: int):
hand_size = size
func get_tokens() -> int:
return tokens
func set_tokens(amount: int) -> void:
tokens = amount
func remove_tokens(amount: int) -> void:
tokens = max(0, tokens - amount)
func add_tokens(t: int) -> void :
tokens += t
func increment_runs() -> void :
run_count += 1
func get_run_count() -> int :
return run_count
func get_gold() -> int :
return gold
func add_gold(g: int) -> void :
gold += g
func remove_gold(g: int) -> void :
gold -= g
func set_gold(g: int) -> void :
gold = g
func unlock_card(card_instance):
unlocked_cards.append(card_instance)
func load_all_cards():
var class_list = ProjectSettings.get_global_class_list()
print("Loading all card classes...")
for class_info in class_list:
if class_info.base == "Card":
var cname = class_info["class"]
print("Found card class: ", cname)
var script_path = class_info["path"]
var card_script = load(script_path)
if card_script == null:
print("Couldn't load script: ", script_path)
continue
var card_instance = card_script.new()
if card_instance.is_default:
unlocked_cards.append(card_instance)
cards_by_name[card_instance.cardName] = card_instance
if card_instance.rank in cards_by_rank:
cards_by_rank[card_instance.rank].append(card_instance)
else:
print("Warning: Card has unknown rank: ", card_instance.cardName, " (", card_instance.rank, ")")
print("Successfully loaded card: ", card_instance.cardName, " (Rank ", card_instance.rank, ")")
print("Card loading complete. Loaded ", cards_by_name.size(), " cards.")
func print_card_list():
print("\n--- CARD LIST BY RANK ---")
for rank in cards_by_rank:
var rank_name = ""
match rank:
Card.Rank.RANK_0: rank_name = "RANK 0 (One-time use)"
Card.Rank.RANK_1: rank_name = "RANK 1 (Once per match)"
Card.Rank.RANK_2: rank_name = "RANK 2 (Discard pile)"
Card.Rank.RANK_3: rank_name = "RANK 3 (Basic)"
_: rank_name = "UNKNOWN RANK"
print("\n" + rank_name + " (" + str(cards_by_rank[rank].size()) + " cards):")
for card in cards_by_rank[rank]:
print(" - " + card.cardName)
print("\n--- END CARD LIST ---\n")
print("\n--- UNLOCKED CARD LIST ---")
for card in unlocked_cards:
print(" - " + card.cardName)
print("\n--- END UNLOCKED CARD LIST ---\n")
func get_card(card_name: String) -> Card:
if card_name in cards_by_name:
return cards_by_name[card_name].duplicate()
return null
func get_random_card(rank: int) -> Card:
if rank in cards_by_rank and not cards_by_rank[rank].is_empty():
var random_index = randi() % cards_by_rank[rank].size()
return cards_by_rank[rank][random_index].duplicate()
return null
func get_cards_by_rank(rank: int) -> Array:
if rank in cards_by_rank:
var result = []
for card in cards_by_rank[rank]:
result.append(card.duplicate())
return result
return []
func get_all_cards() -> Array:
var result = []
for rank in cards_by_rank:
for card in cards_by_rank[rank]:
result.append(card.duplicate())
return result

View file

@ -1 +0,0 @@
uid://bulvkisinp2ur

View file

@ -1,250 +0,0 @@
extends Control
class_name ProgressionScreen
signal save_pressed
signal progression_screen_visibility_changed(isvisible)
@onready var deckGrid = $MainContainer/GridScrollContainer/GridContainer
@onready var bankContainer = $MainContainer/BankContainer/ScrollContainer/VBoxContainer
@onready var saveButton = $SaveButton
@onready var card_preview = $CardPreviewPanel
var maxDeckSize = 10
var deckManager = null
var game = null
var bankCards = [] # All Users card
# The current deck
var currentDeck = []
var current_preview_card = null
func _ready():
# Connect back button
if saveButton:
saveButton.connect("pressed", Callable(self, "_on_save_button_pressed"))
if card_preview:
card_preview.visible = false
func initialize(options = null):
if options:
if options.has("max_deck_size") and options.max_deck_size is int:
maxDeckSize = options.max_deck_size
# Find the DeckManager instance
var board = get_node_or_null("/root/Board") as ChessGame
game = board
if board and "deckManager" in board:
deckManager = board.deckManager
print("Found deck manager:", deckManager)
# if(!deckManager.deck):
# deckManager.initializeStartingDeck()
else:
print("DeckManager not found on Board node")
print("DECK MANAGER")
# Load cards from deck and bank
loadCards()
# Set up the grid with empty card containers
setupDeckGrid()
# Populate the bank with available cards
populateBank()
func loadCards():
var board = get_node_or_null("/root/Board") as ChessGame
if board and "deckManager" in board:
deckManager = board.deckManager
print("Found deck manager:", deckManager)
if deckManager:
# Clone the deck to work with
currentDeck = []
bankCards = deckManager.bank.duplicate()
bankCards.append_array(deckManager.deck.duplicate())
else:
# Fallback with empty collections if deck manager not found
currentDeck = []
bankCards = []
print("Warning: DeckManager not found")
func setupDeckGrid():
# Clear existing children
for child in deckGrid.get_children():
child.queue_free()
# Calculate grid dimensions
var cols = 4 #check screen to deteremine
var rows = maxDeckSize / cols
deckGrid.columns = cols
# Create card slots
for i in range(maxDeckSize):
var card_slot = preload("res://card_slot.tscn").instantiate()
deckGrid.add_child(card_slot)
# Connect signals
card_slot.connect("card_selected", Callable(self, "_on_deck_card_selected"))
_connect_hover_signals(card_slot)
# Set card if available
if i < currentDeck.size():
card_slot.set_card(currentDeck[i])
else:
card_slot.clear()
func _connect_hover_signals(node):
# Add hover signals for preview functionality
if node.is_class("Control"):
node.mouse_entered.connect(Callable(self, "_on_card_mouse_entered").bind(node))
# node.mouse_exited.connect(Callable(self, "_on_card_mouse_exited").bind(node))
func populateBank():
# Clear existing children
for child in bankContainer.get_children():
child.queue_free()
# Add each bank card to the list
for card in bankCards:
var card_item = preload("res://card_bank_item.tscn").instantiate()
bankContainer.add_child(card_item)
card_item.set_card(card)
card_item.connect("card_selected", Callable(self, "_on_bank_card_selected"))
_connect_hover_signals(card_item)
func _on_deck_card_selected(card_slot, card):
if card:
# Remove card from deck
var index = currentDeck.find(card)
if index >= 0:
currentDeck.remove_at(index)
# Add to bank
bankCards.append(card)
# Update UI
card_slot.clear()
populateBank()
if current_preview_card == card:
hide_card_preview()
func _on_bank_card_selected(card_item, card):
print("_on_bank_card_selected")
# Find first empty slot in deck
var empty_slot_index = -1
for i in range(deckGrid.get_child_count()):
var slot = deckGrid.get_child(i)
if !slot.has_card():
empty_slot_index = i
break
if empty_slot_index >= 0 and currentDeck.size() < maxDeckSize:
# Remove from bank
var bank_index = bankCards.find(card)
if bank_index >= 0:
bankCards.remove_at(bank_index)
# Add to deck
currentDeck.append(card)
# Update UI
deckGrid.get_child(empty_slot_index).set_card(card)
populateBank()
func _on_card_mouse_entered(node):
var card = null
# Get the card from the node
if node.has_method("has_card") and node.has_card():
# This is a CardSlot
card = node.current_card
elif node.has_method("set_card") and node.current_card:
# This is a CardBankItem
card = node.current_card
if card:
show_card_preview(card)
# func _on_card_mouse_exited(node):
# # Add a short delay before hiding the preview
# # This prevents flickering when moving between cards
# await get_tree().create_timer(0.1).timeout
# # Only hide if we're not hovering over another card that shows the same preview
# if current_preview_card:
# hide_card_preview()
func show_card_preview(card):
if card_preview and card:
current_preview_card = card
card_preview.preview_card(card)
func hide_card_preview():
if card_preview:
current_preview_card = null
card_preview.hide_preview()
func save_deck():
sell_remaining_cards()
if deckManager:
# Save the current deck to the deck manager
deckManager.deck = currentDeck.duplicate()
deckManager.bank = []
print("Deck saved with ", currentDeck.size(), " cards")
if game.player:
game.player.increment_runs()
func sell_remaining_cards():
for card in bankCards:
sell_selected_card(card)
bankCards.clear()
func get_card_price(card):
if not card:
return 0
if card.rank in Utils.CardPrices:
return Utils.CardPrices[card.rank]
return 50
func sell_selected_card(selected_card):
var price = get_card_price(selected_card)
game.player.add_gold(price)
return true
func _on_save_button_pressed():
# Save changes before returning
save_deck()
# Emit signal to go back
emit_signal("save_pressed")
# Hide this screen
visible = false
# Notification
func _notification(what):
if what == NOTIFICATION_VISIBILITY_CHANGED:
_on_visibility_changed(visible)
func _on_visibility_changed(is_visible):
print("Progression Screen visibility changed to: ", is_visible)
if is_visible:
loadCards()
setupDeckGrid()
else:
print("Progression Screen is now invisible")
emit_signal("progression_screen_visibility_changed", is_visible)

View file

@ -1 +0,0 @@
uid://dbohdt174pual

View file

@ -1,150 +0,0 @@
# 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
var descr_string = "gold"
# 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 hide_price():
if price_label:
price_label.visible = false
func set_string(str):
descr_string = str
if price_label:
price_label.visible = true
price_label.text = str(price) + " " +descr_string
func set_price(new_price):
price = new_price
if price_label:
price_label.visible = true
price_label.text = str(price) + " " +descr_string
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)

View file

@ -1 +0,0 @@
uid://b5xl0bxh21vbi

View file

@ -1,315 +0,0 @@
extends Control
class_name RewardScreen
signal back_pressed(node)
signal card_purchased(card, price)
# Node references
@onready var card_carousel = $MainContainer/CardCarouselContainer/CardCarousel
@onready var gold_label = $TopBar/GoldContainer/GoldLabel
@onready var select_button = $SelectButton
@onready var back_button = $BackButton
@onready var card_preview = $CardPreviewPanel
@onready var left_button = $MainContainer/CardCarouselContainer/LeftButton
@onready var right_button = $MainContainer/CardCarouselContainer/RightButton
@onready var screen_title = $TopBar/TitleLabel
# reward 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 reward event an escape option
var carousel_page = 0
var cards_per_page = 3
var card_selection_counter = 0
var card_selection_limit = 1
var card_instance_map = {}
var hovering_card_index = -1
var mouse_over_any_card = false
var deckManager: DeckManager
var selected_node = null
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 select_button:
if select_button.is_connected("pressed", Callable(self, "_on_select_button_pressed")):
select_button.disconnect("pressed", Callable(self, "_on_select_button_pressed"))
select_button.connect("pressed", Callable(self, "_on_select_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_select_button()
update_navigation_buttons()
func initialize(node_data = null):
print("RewardScreen ", node_data)
carousel_page = 0
selected_index = 0
var board = get_node_or_null("/root/Board") as ChessGame
if board and "deckManager" in board:
deckManager = board.deckManager
var options = node_data.metadata
selected_node = node_data
# Process options if provided
if options:
if options.has("gold") and options.gold is int:
player_gold = options.gold
if options.has("reward") and options.reward.has("selection") and options.reward.selection is Array:
available_cards = options.reward.selection.duplicate()
else:
available_cards = []
if options.has("is_escape") and options.is_escape is bool:
is_escape = options.is_escape
if options.has("reward") and options.reward.has("gold") and options.reward.gold is int:
board.player.add_gold(options.reward.gold)
player_gold = board.player.get_gold()
if options.has("reward") and options.reward.has("selection_limit") and options.reward.selection_limit is int:
card_selection_limit = options.reward.selection_limit
screen_title.text = "Select Reward (" + str(options.reward.selection_limit) + ")"
select_button.visible = true
select_button.disabled = false
else:
screen_title.text = "Reward :" + str(options.reward.gold) + " gold"
select_button.visible = false
card_selection_counter = 0
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()
player_gold = board.player.get_gold()
# Update display
update_gold_display()
populate_carousel()
update_navigation_buttons()
if not available_cards.is_empty():
select_card(0)
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)
card_visual.hide_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)
card_visual.set_selected(i == visual_index)
if card_preview and selected_card:
card_preview.preview_card(selected_card)
card_preview.visible = true
update_select_button()
func update_select_button():
if not select_button or not selected_card:
return
if card_selection_counter >= card_selection_limit:
select_button.disabled = true
func update_gold_display():
if gold_label:
gold_label.text = str(player_gold) + " GOLD"
func get_selected_card():
if not selected_card:
return false
var purchased_card = selected_card.duplicate()
var old_page = carousel_page
available_cards.remove_at(selected_index)
emit_signal("card_purchased", purchased_card, 0)
card_selection_counter += 1
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()
select_button.disabled = true
update_select_button()
return true
# Signal handlers
func _on_back_button_pressed():
emit_signal("back_pressed", selected_node)
visible = false
func _on_select_button_pressed():
get_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 old_hovering_index != hovering_card_index:
_on_card_visual_hover(data.card, data.card_index)
card_visual._on_mouse_entered()
break
if old_hovering_index != -1 and hovering_card_index == -1:
_on_card_visual_exit()
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

View file

@ -1 +0,0 @@
uid://ccy6bx6kiejjy

View file

@ -1,344 +0,0 @@
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 = Utils.CardPrices
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
else:
var board = get_node_or_null("/root/Board") as ChessGame
player_gold = board.player.get_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)
# 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)
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

View file

@ -1 +0,0 @@
uid://rqkucvk8au68

Some files were not shown because too many files have changed in this diff Show more