Compare commits
No commits in common. "master" and "delete_node_fish" have entirely different histories.
master
...
delete_nod
174 changed files with 1235 additions and 10545 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -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/**
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[gd_resource type="Theme" format=3 uid="uid://btgbiqdc4kf15"]
|
||||
|
||||
[resource]
|
||||
default_font_size = 49
|
||||
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"version": "0.0.1",
|
||||
"title": "ChessBuilder",
|
||||
"developer": "Comet Studios",
|
||||
"release_date": "2025-01-15"
|
||||
}
|
||||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
uid://eqadab7yd0qq
|
||||
|
|
@ -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)
|
||||
|
||||
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 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)
|
||||
|
||||
# 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)
|
||||
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)
|
||||
|
||||
# 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
|
||||
cardPanel.gui_input.connect(func(event): _on_card_clicked(event, card))
|
||||
|
||||
# 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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://dgsj76xih6r3y
|
||||
|
|
@ -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
|
||||
|
||||
# Update and show the moves remaining label
|
||||
movesRemainingLabel.text = "Moves remaining this turn: " + str(moves)
|
||||
movesRemainingLabel.visible = true
|
||||
var vbox = previewPanel.get_child(0)
|
||||
var movesLabel = Label.new()
|
||||
movesLabel.text = "Moves remaining this turn: " + str(moves)
|
||||
movesLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
|
||||
# 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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://p4ahefeutov0
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://2xcreiq6lhe2
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://b1d17jfxagdc7
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://b5888fqblsco5
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
uid://o16wgudelf8x
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bayyvtfwpshsh
|
||||
|
|
@ -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,9 +104,7 @@ 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
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
uid://64bdg86r6t4o
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://cpgq5itsb2l8j
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://ca4j0d6hrcm4x
|
||||
|
|
@ -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()
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://b8f17yg2854vs
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://5w8amumsdpto
|
||||
|
|
@ -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()
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://nw1oesh7pr48
|
||||
|
|
@ -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()
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://ms7wi6dgoqnr
|
||||
|
|
@ -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()
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://8vpac2cx4e0u
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
uid://cvldhayf5ket0
|
||||
|
|
@ -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"
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://dm6uv77n0roen
|
||||
|
|
@ -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):
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
uid://bdwsolgdmo51y
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://boryt6vnd0icx
|
||||
|
|
@ -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"])
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
uid://dae6g5472sh26
|
||||
|
|
@ -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
|
||||
|
|
@ -224,7 +173,6 @@ func generateMove(think_time_ms: int = 1000) -> void:
|
|||
|
||||
|
||||
|
||||
|
||||
func getGeneratedMove() -> Dictionary:
|
||||
var move = generated_move.duplicate()
|
||||
generated_move.clear()
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
uid://bnxxex87ax7hc
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bdbap6f4c4d5w
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1 +0,0 @@
|
|||
uid://cbcu68o863pfp
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://vxufsih5pgeu
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://cbaoxhgtk4td8
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bfjmon81nckns
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://j0m4rwr86oi6
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://b7b2xlfvhgipb
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://dkk0eu4fj7q5j
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://nrd5mq0tfmur
|
||||
|
|
@ -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()
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://6cmhvsug8nbv
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bs0u5jjxm2c18
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://ckpv3snpg6g34
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bmi14xk8tn7xc
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://tsgxcwb1u8uc
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bf5ljae05pvla
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://dbm5dv81lbdod
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://c47i2m0ll101x
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bulvkisinp2ur
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://dbohdt174pual
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://b5xl0bxh21vbi
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://ccy6bx6kiejjy
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://rqkucvk8au68
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue