networked chess
This commit is contained in:
148
CLAUDE.md
148
CLAUDE.md
@@ -36,9 +36,10 @@ Prosperon is an actor-based game engine inspired by Douglas Crockford's Misty sy
|
|||||||
|
|
||||||
### JavaScript Style Guide
|
### JavaScript Style Guide
|
||||||
- Use `use()` function for imports (Misty-style, not ES6 import/export)
|
- Use `use()` function for imports (Misty-style, not ES6 import/export)
|
||||||
- Prefer objects and closures over ES6 classes
|
- Prefer closures and javascript objects and prototypes over ES6 style classes
|
||||||
- Follow existing JavaScript patterns in the codebase
|
- Follow existing JavaScript patterns in the codebase
|
||||||
- Functions as first-class citizens
|
- Functions as first-class citizens
|
||||||
|
- Do not use const or let; only var
|
||||||
|
|
||||||
### Core Systems
|
### Core Systems
|
||||||
1. **Actor System** (scripts/core/engine.js)
|
1. **Actor System** (scripts/core/engine.js)
|
||||||
@@ -59,8 +60,11 @@ Prosperon is an actor-based game engine inspired by Douglas Crockford's Misty sy
|
|||||||
|
|
||||||
### Engine Entry Points
|
### Engine Entry Points
|
||||||
- `source/prosperon.c` - Main C entry point
|
- `source/prosperon.c` - Main C entry point
|
||||||
- `scripts/core/engine.js` - JavaScript engine initialization
|
- `scripts/core/engine.js` - JavaScript engine initialization for system
|
||||||
- `scripts/core/base.js` - Base prototypes and utilities
|
- `scripts/core/base.js` has modifications to this Javascript runtime (for example, additions to the base Array, String, etc)
|
||||||
|
|
||||||
|
### Subprojects
|
||||||
|
- C code has many subprojects, who's source and sometimes documentation can be found in subprojects. subprojects/quickjs/doc has documentation for quickjs
|
||||||
|
|
||||||
### Resource System
|
### Resource System
|
||||||
- Scripts are bundled into `core.zip` during build
|
- Scripts are bundled into `core.zip` during build
|
||||||
@@ -91,6 +95,12 @@ cd examples/chess
|
|||||||
./prosperon
|
./prosperon
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- Documentation is found in docs
|
||||||
|
- Documentation for the JS modules loaded with 'use' is docs/api/modules
|
||||||
|
- .md files directly in docs gives a high level overview
|
||||||
|
- docs/dull is what this specific Javascript system is (including alterations from quickjs/es6)
|
||||||
|
|
||||||
### Shader Development
|
### Shader Development
|
||||||
- Shaders are in `shaders/` directory as HLSL
|
- Shaders are in `shaders/` directory as HLSL
|
||||||
- Compile script: `shaders/compile.sh`
|
- Compile script: `shaders/compile.sh`
|
||||||
@@ -117,4 +127,134 @@ meson test -C build_dbg
|
|||||||
- Use debug build: `make debug`
|
- Use debug build: `make debug`
|
||||||
- Tracy profiler support when enabled
|
- Tracy profiler support when enabled
|
||||||
- Console logging available via `console.log()`, `console.error()`, etc.
|
- Console logging available via `console.log()`, `console.error()`, etc.
|
||||||
- Log files written to `.prosperon/log.txt`
|
- Log files written to `.prosperon/log.txt`
|
||||||
|
|
||||||
|
# Project Structure Notes
|
||||||
|
|
||||||
|
## Core JavaScript Modules
|
||||||
|
|
||||||
|
- JavaScript modules are defined using the MISTUSE macro in jsffi.c
|
||||||
|
- The `js_os_funcs`, `js_io_funcs`, etc. arrays define the available functions for each module
|
||||||
|
- New functions are added with MIST_FUNC_DEF(module, function, args_count)
|
||||||
|
|
||||||
|
## File I/O
|
||||||
|
|
||||||
|
- `io.slurp(path)` - Reads a file as text
|
||||||
|
- `io.slurpbytes(path)` - Reads a file as an ArrayBuffer
|
||||||
|
- `io.slurpwrite(path, data)` - Writes data (string or ArrayBuffer) to a file
|
||||||
|
- `io.exists(path)` - Checks if a file exists
|
||||||
|
|
||||||
|
## Script Loading
|
||||||
|
|
||||||
|
- The `use(path)` function in engine.js loads JavaScript modules
|
||||||
|
- Script loading happens in prosperon.c and the engine.js script
|
||||||
|
- jsffi.c contains the C hooks for the QuickJS JavaScript engine
|
||||||
|
- Added functionality for bytecode compilation and loading:
|
||||||
|
- `os.compile_bytecode(source, filename)` - Compiles JS to bytecode, returns ArrayBuffer
|
||||||
|
- `os.eval_bytecode(bytecode)` - Evaluates bytecode from an ArrayBuffer
|
||||||
|
- `compile(scriptPath)` - Compiles a JS file to a .jso bytecode file
|
||||||
|
- Modified `use()` to check for .jso files before loading .js files
|
||||||
|
|
||||||
|
## QuickJS Bytecode API
|
||||||
|
|
||||||
|
- `JS_Eval` with JS_EVAL_FLAG_COMPILE_ONLY - Compiles without executing
|
||||||
|
- `JS_WriteObject` with JS_WRITE_OBJ_BYTECODE - Serializes to bytecode
|
||||||
|
- `JS_ReadObject` with JS_READ_OBJ_BYTECODE - Deserializes and loads bytecode
|
||||||
|
- Bytecode files use .jso extension alongside .js files
|
||||||
|
|
||||||
|
## Available JavaScript APIs
|
||||||
|
|
||||||
|
### Core APIs
|
||||||
|
- `actor` - Base prototype for all actor objects
|
||||||
|
- `$_` - Special global for actor messaging
|
||||||
|
- `prosperon` - Global engine interface
|
||||||
|
- `console` - Logging and debugging interface
|
||||||
|
|
||||||
|
### Framework APIs
|
||||||
|
- `moth` - Higher-level game framework that simplifies Prosperon usage
|
||||||
|
- Handles window creation, game loop, and event dispatching
|
||||||
|
- Provides simple configuration via config.js
|
||||||
|
- Auto-initializes systems like rendering and input
|
||||||
|
- Manages camera, resolution, and FPS automatically
|
||||||
|
|
||||||
|
### Rendering
|
||||||
|
- `draw2d` - 2D drawing primitives
|
||||||
|
- `render` - Low-level rendering operations
|
||||||
|
- `graphics` - Higher-level graphics utilities
|
||||||
|
- `camera` - Camera controls and transformations
|
||||||
|
- `sprite` - Sprite rendering and management
|
||||||
|
|
||||||
|
### Physics and Math
|
||||||
|
- `math` - Mathematical utilities
|
||||||
|
- `geometry` - Geometric calculations and shapes
|
||||||
|
- `transform` - Object transformations
|
||||||
|
|
||||||
|
### Input and Events
|
||||||
|
- `input` - Mouse, keyboard, and touch handling
|
||||||
|
- `event` - Event management system
|
||||||
|
|
||||||
|
### Networking
|
||||||
|
- `enet` - Networking through ENet library
|
||||||
|
- `http` - HTTP client capabilities
|
||||||
|
|
||||||
|
### Audio
|
||||||
|
- `sound` - Audio playback using SoLoud
|
||||||
|
|
||||||
|
### Utility Modules
|
||||||
|
- `time` - Time management and delays
|
||||||
|
- `io` - File I/O operations
|
||||||
|
- `json` - JSON parsing and serialization
|
||||||
|
- `util` - General utilities
|
||||||
|
- `color` - Color manipulation
|
||||||
|
- `miniz` - Compression utilities
|
||||||
|
- `nota` - Structured data format
|
||||||
|
- `wota` - Serialization format
|
||||||
|
- `qr` - QR code generation/reading
|
||||||
|
- `tween` - Animation tweening
|
||||||
|
- `spline` - Spline calculations
|
||||||
|
- `imgui` - Immediate mode GUI
|
||||||
|
|
||||||
|
## Game Development Patterns
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
- Game config is typically in `config.js`
|
||||||
|
- Main entry point is `main.js`
|
||||||
|
- Resource loading through `resources.js`
|
||||||
|
|
||||||
|
### Actor Pattern Usage
|
||||||
|
- Create actors with `actor.spawn(script, config)`
|
||||||
|
- Manage actor hierarchy with overlings and underlings
|
||||||
|
- Schedule actor tasks with `delay()` method
|
||||||
|
- Clean up with `kill()` and `garbage()`
|
||||||
|
|
||||||
|
### Game Loop Registration
|
||||||
|
- Register functions like `update`, `draw`, `gui`, etc.
|
||||||
|
- Set function.layer property to control execution order
|
||||||
|
- Use `Register` system to manage callbacks
|
||||||
|
|
||||||
|
### Program vs Module Pattern
|
||||||
|
- Programs are actor scripts that don't return values, they execute top-to-bottom
|
||||||
|
- Modules are files that return single values (usually objects) that get frozen
|
||||||
|
- Programs can spawn other programs as underlings
|
||||||
|
- Programs have lifecycle hooks: awake, update, draw, garbage, etc.
|
||||||
|
|
||||||
|
## Technical Capabilities
|
||||||
|
|
||||||
|
### Graphics Pipeline
|
||||||
|
- Supports multiple render backends (Direct3D, Metal, Vulkan via SDL3)
|
||||||
|
- Custom shader system with cross-platform compilation
|
||||||
|
- Sprite batching for efficient 2D rendering
|
||||||
|
- Camera systems for both 2D and 3D
|
||||||
|
|
||||||
|
### Asset Support
|
||||||
|
- Images: PNG, JPG, QOI, etc.
|
||||||
|
- Audio: Various formats through SoLoud
|
||||||
|
- Models: Basic 3D model support
|
||||||
|
- Custom formats: Aseprite animations, etc.
|
||||||
|
|
||||||
|
### Developer Tools
|
||||||
|
- Built-in documentation system with `prosperon.DOC`
|
||||||
|
- Tracy profiler integration for performance monitoring
|
||||||
|
- Imgui debugging tools
|
||||||
|
- Console logging with various severity levels
|
||||||
|
```
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
/* main.js – runs the demo with your prototype-based grid */
|
/* main.js – runs the demo with your prototype-based grid */
|
||||||
|
|
||||||
|
var moth = use('moth')
|
||||||
|
var json = use('json')
|
||||||
|
|
||||||
var res = 160
|
var res = 160
|
||||||
var internal_res = 480
|
var internal_res = 480
|
||||||
var render = use('render');
|
|
||||||
render.initialize({ width:res, height:res, resolution_x:internal_res, resolution_y:internal_res, mode:'letterbox' });
|
moth.initialize({ width:res, height:res, resolution_x:internal_res, resolution_y:internal_res, mode:'letterbox' });
|
||||||
|
|
||||||
var os = use('os');
|
var os = use('os');
|
||||||
var draw2d = use('draw2d');
|
var draw2d = use('draw2d');
|
||||||
var gfx = use('graphics');
|
var gfx = use('graphics');
|
||||||
|
|
||||||
/* camera – unchanged */
|
|
||||||
var camera = { size:[internal_res,internal_res], transform:os.make_transform(), fov:50, near_z:0,
|
|
||||||
far_z:1000, surface:undefined, viewport:{x:0,y:0,width:1,height:1},
|
|
||||||
ortho:true, anchor:[0,0] };
|
|
||||||
|
|
||||||
/*──── import our pieces + systems ───────────────────────────────────*/
|
/*──── import our pieces + systems ───────────────────────────────────*/
|
||||||
var Grid = use('grid'); // your new ctor
|
var Grid = use('grid'); // your new ctor
|
||||||
var MovementSystem = use('movement').MovementSystem;
|
var MovementSystem = use('movement').MovementSystem;
|
||||||
@@ -21,97 +20,281 @@ var rules = use('rules');
|
|||||||
|
|
||||||
/*──── build board ───────────────────────────────────────────────────*/
|
/*──── build board ───────────────────────────────────────────────────*/
|
||||||
var grid = new Grid(8, 8);
|
var grid = new Grid(8, 8);
|
||||||
grid.width = 8; // (the ctor didn’t store them)
|
grid.width = 8; // (the ctor didn't store them)
|
||||||
grid.height = 8;
|
grid.height = 8;
|
||||||
|
|
||||||
var mover = new MovementSystem(grid, rules);
|
var mover = new MovementSystem(grid, rules);
|
||||||
startingPos(grid);
|
startingPos(grid);
|
||||||
|
|
||||||
|
/*──── networking and game state ─────────────────────────────────────*/
|
||||||
|
var gameState = 'waiting'; // 'waiting', 'searching', 'server_waiting', 'connected'
|
||||||
|
var isServer = false;
|
||||||
|
var opponent = null;
|
||||||
|
var myColor = null; // 'white' or 'black'
|
||||||
|
var isMyTurn = false;
|
||||||
|
|
||||||
|
function updateTitle() {
|
||||||
|
var title = "Misty Chess - ";
|
||||||
|
|
||||||
|
switch(gameState) {
|
||||||
|
case 'waiting':
|
||||||
|
title += "Press S to start server or J to join";
|
||||||
|
break;
|
||||||
|
case 'searching':
|
||||||
|
title += "Searching for server...";
|
||||||
|
break;
|
||||||
|
case 'server_waiting':
|
||||||
|
title += "Waiting for player to join...";
|
||||||
|
break;
|
||||||
|
case 'connected':
|
||||||
|
if (myColor) {
|
||||||
|
title += (mover.turn === myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
|
||||||
|
} else {
|
||||||
|
title += mover.turn + " turn";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prosperon.window.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize title
|
||||||
|
updateTitle();
|
||||||
|
|
||||||
/*──── mouse → click-to-move ─────────────────────────────────────────*/
|
/*──── mouse → click-to-move ─────────────────────────────────────────*/
|
||||||
var selectPos = null;
|
var selectPos = null;
|
||||||
prosperon.window.on_mousedown = function (btn, mx, my) {
|
var hoverPos = null;
|
||||||
if (btn !== 0) return;
|
var holdingPiece = false;
|
||||||
|
|
||||||
|
prosperon.on('mouse_button_down', function(e) {
|
||||||
|
if (e.which !== 0) return;
|
||||||
|
var mx = e.mouse.x;
|
||||||
|
var my = e.mouse.y;
|
||||||
|
|
||||||
var c = [Math.floor(mx / 60), Math.floor(my / 60)];
|
var c = [Math.floor(mx / 60), Math.floor(my / 60)];
|
||||||
if (!grid.inBounds(c)) return;
|
if (!grid.inBounds(c)) return;
|
||||||
|
|
||||||
var cell = grid.at(c);
|
var cell = grid.at(c);
|
||||||
if (selectPos && mover.tryMove(grid.at(selectPos)[0], c)) {
|
|
||||||
selectPos = null; // made a move
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cell.length && cell[0].colour === mover.turn) {
|
if (cell.length && cell[0].colour === mover.turn) {
|
||||||
selectPos = c; // pick up piece
|
selectPos = c;
|
||||||
|
holdingPiece = true;
|
||||||
} else {
|
} else {
|
||||||
selectPos = null;
|
selectPos = null;
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
|
||||||
|
prosperon.on('mouse_button_up', function(e) {
|
||||||
|
if (e.which !== 0 || !holdingPiece || !selectPos) return;
|
||||||
|
|
||||||
|
var mx = e.mouse.x;
|
||||||
|
var my = e.mouse.y;
|
||||||
|
|
||||||
|
var c = [Math.floor(mx / 60), Math.floor(my / 60)];
|
||||||
|
if (!grid.inBounds(c)) {
|
||||||
|
holdingPiece = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow moves if it's our turn or we're in local mode
|
||||||
|
if (gameState !== 'connected' || isMyTurn) {
|
||||||
|
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
||||||
|
// Send move to opponent if connected
|
||||||
|
if (gameState === 'connected' && opponent) {
|
||||||
|
$_.send(opponent, {
|
||||||
|
type: 'move',
|
||||||
|
from: selectPos,
|
||||||
|
to: c
|
||||||
|
});
|
||||||
|
isMyTurn = false; // It's now opponent's turn
|
||||||
|
}
|
||||||
|
selectPos = null;
|
||||||
|
updateTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holdingPiece = false;
|
||||||
|
})
|
||||||
|
|
||||||
|
prosperon.on('mouse_motion', function(e) {
|
||||||
|
var mx = e.pos.x;
|
||||||
|
var my = e.pos.y;
|
||||||
|
|
||||||
|
var c = [Math.floor(mx / 60), Math.floor(my / 60)];
|
||||||
|
if (!grid.inBounds(c)) {
|
||||||
|
hoverPos = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverPos = c;
|
||||||
|
})
|
||||||
|
|
||||||
/*──── drawing helpers ───────────────────────────────────────────────*/
|
/*──── drawing helpers ───────────────────────────────────────────────*/
|
||||||
/* ── constants ─────────────────────────────────────────────────── */
|
/* ── constants ─────────────────────────────────────────────────── */
|
||||||
var S = 60; // square size in px
|
var S = 60; // square size in px
|
||||||
var light = [0.93,0.93,0.93,1];
|
var light = [0.93,0.93,0.93,1];
|
||||||
var dark = [0.25,0.25,0.25,1];
|
var dark = [0.25,0.25,0.25,1];
|
||||||
|
var hoverColor = [0.8, 1.0, 0.8, 1.0];
|
||||||
|
var validMoveColor = [1.0, 0.8, 0.8, 1.0];
|
||||||
|
|
||||||
/* ── draw one 8×8 chess board ──────────────────────────────────── */
|
/* ── draw one 8×8 chess board ──────────────────────────────────── */
|
||||||
function drawBoard() {
|
function drawBoard() {
|
||||||
for (var y = 0; y < 8; ++y)
|
for (var y = 0; y < 8; ++y)
|
||||||
for (var x = 0; x < 8; ++x) {
|
for (var x = 0; x < 8; ++x) {
|
||||||
|
var isHovered = hoverPos && hoverPos[0] === x && hoverPos[1] === y;
|
||||||
|
var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]);
|
||||||
|
|
||||||
|
var color = ((x+y)&1) ? dark : light;
|
||||||
|
|
||||||
|
if (isHovered && !isValidMove) {
|
||||||
|
color = hoverColor;
|
||||||
|
} else if (isValidMove) {
|
||||||
|
color = validMoveColor;
|
||||||
|
}
|
||||||
|
|
||||||
draw2d.rectangle(
|
draw2d.rectangle(
|
||||||
{ x: x*S, y: y*S, width: S, height: S },
|
{ x: x*S, y: y*S, width: S, height: S },
|
||||||
{ thickness: 0, color: ((x+y)&1) ? dark : light }
|
{ thickness: 0, color: color }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidMoveForTurn(from, to) {
|
||||||
|
if (!grid.inBounds(to)) return false;
|
||||||
|
|
||||||
|
var piece = grid.at(from)[0];
|
||||||
|
if (!piece) return false;
|
||||||
|
|
||||||
|
// Check if the destination has a piece of the same color
|
||||||
|
var destCell = grid.at(to);
|
||||||
|
if (destCell.length && destCell[0].colour === piece.colour) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules.canMove(piece, from, to, grid);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── draw every live piece ─────────────────────────────────────── */
|
/* ── draw every live piece ─────────────────────────────────────── */
|
||||||
function drawPieces() {
|
function drawPieces() {
|
||||||
grid.each(function (piece) {
|
grid.each(function (piece) {
|
||||||
if (piece.captured) return;
|
if (piece.captured) return;
|
||||||
|
|
||||||
|
// Skip drawing the piece being held
|
||||||
|
if (holdingPiece && selectPos &&
|
||||||
|
piece.coord[0] === selectPos[0] &&
|
||||||
|
piece.coord[1] === selectPos[1]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var r = { x: piece.coord[0]*S, y: piece.coord[1]*S,
|
var r = { x: piece.coord[0]*S, y: piece.coord[1]*S,
|
||||||
width:S, height:S };
|
width:S, height:S };
|
||||||
|
|
||||||
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
// Draw the held piece at the mouse position if we're holding one
|
||||||
/*──── main loop ─────────────────────────────────────────────────────*/
|
if (holdingPiece && selectPos && hoverPos) {
|
||||||
var last = os.now(), fpsTimer = 0, fpsCount = 0;
|
var piece = grid.at(selectPos)[0];
|
||||||
|
if (piece) {
|
||||||
function loop() {
|
var r = { x: hoverPos[0]*S, y: hoverPos[1]*S,
|
||||||
var now = os.now(), dt = now - last; last = now;
|
width:S, height:S };
|
||||||
|
|
||||||
render.clear([22/255, 120/255, 194/255, 1]);
|
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
||||||
render.camera(camera);
|
}
|
||||||
drawBoard();
|
|
||||||
drawPieces();
|
|
||||||
render.present();
|
|
||||||
|
|
||||||
fpsTimer += dt; fpsCount++;
|
|
||||||
if (fpsTimer >= 0.5) {
|
|
||||||
prosperon.window.title =
|
|
||||||
'Chess demo – ' + mover.turn + '\'s move – FPS ' + (fpsCount / fpsTimer).toFixed(1);
|
|
||||||
fpsTimer = fpsCount = 0;
|
|
||||||
}
|
}
|
||||||
$_.delay(loop, Math.max(0, (1 / 60) - (os.now() - now)));
|
|
||||||
}
|
}
|
||||||
loop();
|
|
||||||
|
prosperon.on('draw', function() {
|
||||||
|
drawBoard()
|
||||||
|
drawPieces()
|
||||||
|
})
|
||||||
|
|
||||||
|
prosperon.on('key_down', function(e) {
|
||||||
|
// S key - start server
|
||||||
|
if (e.scancode === 22 && gameState === 'waiting') { // S key
|
||||||
|
startServer();
|
||||||
|
}
|
||||||
|
// J key - join server
|
||||||
|
else if (e.scancode === 13 && gameState === 'waiting') { // J key
|
||||||
|
joinServer();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function startServer() {
|
||||||
|
gameState = 'server_waiting';
|
||||||
|
isServer = true;
|
||||||
|
myColor = 'white';
|
||||||
|
isMyTurn = true;
|
||||||
|
updateTitle();
|
||||||
|
|
||||||
|
$_.portal(e => {
|
||||||
|
opponent = e;
|
||||||
|
gameState = 'connected';
|
||||||
|
updateTitle();
|
||||||
|
|
||||||
|
// Tell the joining player they are black
|
||||||
|
$_.send(opponent, {
|
||||||
|
type: 'game_start',
|
||||||
|
your_color: 'black'
|
||||||
|
});
|
||||||
|
}, 5678);
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinServer() {
|
||||||
|
gameState = 'searching';
|
||||||
|
updateTitle();
|
||||||
|
|
||||||
|
function contact_fn(actor, reason) {
|
||||||
|
console.log("CONTACTED!")
|
||||||
|
if (actor) {
|
||||||
|
opponent = actor;
|
||||||
|
gameState = 'connected';
|
||||||
|
myColor = 'black';
|
||||||
|
isMyTurn = false;
|
||||||
|
updateTitle();
|
||||||
|
} else {
|
||||||
|
console.log(`Failed to connect: ${json.encode(reason)}`);
|
||||||
|
gameState = 'waiting';
|
||||||
|
updateTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_.contact(contact_fn, {
|
||||||
|
address: "localhost",
|
||||||
|
port: 5678
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var os = use('os')
|
var os = use('os')
|
||||||
|
|
||||||
|
// Set up IO actor subscription
|
||||||
var ioguy = {
|
var ioguy = {
|
||||||
__ACTORDATA__: {
|
__ACTORDATA__: {
|
||||||
id: os.ioactor()
|
id: os.ioactor()
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
$_.send(ioguy, {
|
$_.send(ioguy, {
|
||||||
type: "subscribe",
|
type: "subscribe",
|
||||||
actor: $_
|
actor: $_
|
||||||
})
|
});
|
||||||
|
|
||||||
$_.receiver(e => {
|
$_.receiver(e => {
|
||||||
if (e.type === 'quit')
|
if (e.type === 'quit') os.exit()
|
||||||
os.exit()
|
if (e.type === 'game_start') {
|
||||||
else
|
myColor = e.your_color;
|
||||||
console.log(json.encode(e))
|
isMyTurn = (myColor === 'white');
|
||||||
})
|
updateTitle();
|
||||||
|
} else if (e.type === 'move') {
|
||||||
|
// Apply opponent's move
|
||||||
|
var fromCell = grid.at(e.from);
|
||||||
|
if (fromCell.length) {
|
||||||
|
var piece = fromCell[0];
|
||||||
|
if (mover.tryMove(piece, e.to)) {
|
||||||
|
isMyTurn = true; // It's now our turn
|
||||||
|
updateTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prosperon.dispatch(e.type, e)
|
||||||
|
})
|
||||||
@@ -1000,8 +1000,6 @@ function enet_check()
|
|||||||
$_.delay(enet_check, service_delay);
|
$_.delay(enet_check, service_delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`actor ${prosperon.id} online.`)
|
|
||||||
|
|
||||||
send_messages();
|
send_messages();
|
||||||
|
|
||||||
})()
|
})()
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ $_.unneeded(_ => {
|
|||||||
|
|
||||||
var subscribers = []
|
var subscribers = []
|
||||||
|
|
||||||
|
var windows = []
|
||||||
|
var renderers = []
|
||||||
|
|
||||||
|
var os = use('os')
|
||||||
|
|
||||||
$_.receiver(e => {
|
$_.receiver(e => {
|
||||||
if (e.type === "subscribe") {
|
if (e.type === "subscribe") {
|
||||||
if (!e.actor) throw Error('Got a subscribe message with no actor.');
|
if (!e.actor) throw Error('Got a subscribe message with no actor.');
|
||||||
@@ -11,6 +16,150 @@ $_.receiver(e => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.type === "window") {
|
||||||
|
var window = windows[e.id]
|
||||||
|
|
||||||
|
switch (e.fn) {
|
||||||
|
case "create":
|
||||||
|
window = os.engine_start(e.config)
|
||||||
|
windows[e.id] = window
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "fullscreen":
|
||||||
|
window.fullscreen()
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "make_renderer":
|
||||||
|
var renderer = window.make_renderer(e.config || {})
|
||||||
|
renderers[e.renderer_id] = renderer
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "keyboard_shown":
|
||||||
|
return window.keyboard_shown()
|
||||||
|
|
||||||
|
case "theme":
|
||||||
|
return window.theme()
|
||||||
|
|
||||||
|
case "safe_area":
|
||||||
|
return window.safe_area()
|
||||||
|
|
||||||
|
case "bordered":
|
||||||
|
window.bordered(e.value)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "set_icon":
|
||||||
|
window.set_icon(e.icon)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "set_title":
|
||||||
|
window.title = e.title
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "get_title":
|
||||||
|
return window.title
|
||||||
|
|
||||||
|
case "set_size":
|
||||||
|
window.size = e.size
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "get_size":
|
||||||
|
return window.size
|
||||||
|
|
||||||
|
case "mouse_grab":
|
||||||
|
window.mouse_grab(e.value)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.type === "render") {
|
||||||
|
var renderer = renderers[e.id]
|
||||||
|
|
||||||
|
switch (e.fn) {
|
||||||
|
case "draw_color":
|
||||||
|
renderer.draw_color(e.color)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "present":
|
||||||
|
renderer.present()
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "clear":
|
||||||
|
renderer.clear()
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "line":
|
||||||
|
renderer.line(e.config)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "point":
|
||||||
|
renderer.point(e.config)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "texture":
|
||||||
|
renderer.texture(e.texture, e.src_rect, e.dst_rect, e.angle, e.center)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "rects":
|
||||||
|
renderer.rects(e.rects)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "geometry":
|
||||||
|
renderer.geometry(e.vertices, e.indices)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "geometry2":
|
||||||
|
renderer.geometry2(e.vertices, e.indices)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "sprite":
|
||||||
|
renderer.sprite(e.config)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "load_texture":
|
||||||
|
renderer.load_texture(e.path)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "get_image":
|
||||||
|
renderer.get_image(e.config)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "scale":
|
||||||
|
renderer.scale(e.scale)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "logical_size":
|
||||||
|
renderer.logical_size(e.size)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "viewport":
|
||||||
|
renderer.viewport(e.viewport)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "clip":
|
||||||
|
renderer.clip(e.rect)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "vsync":
|
||||||
|
renderer.vsync(e.enable)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "coords":
|
||||||
|
renderer.coords(e.config)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "camera":
|
||||||
|
renderer.camera(e.cam, e.layer)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "screen2world":
|
||||||
|
return renderer.screen2world(e.point)
|
||||||
|
|
||||||
|
case "target":
|
||||||
|
renderer.target(e.target)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var a of subscribers)
|
for (var a of subscribers)
|
||||||
$_.send(a, e);
|
$_.send(a, e);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,20 +6,15 @@
|
|||||||
var os = use('os');
|
var os = use('os');
|
||||||
var io = use('io');
|
var io = use('io');
|
||||||
var render = use('render');
|
var render = use('render');
|
||||||
var draw2d = use('draw2d');
|
|
||||||
var actor = use('actor');
|
var actor = use('actor');
|
||||||
var gameConfig = {};
|
var gameConfig = {};
|
||||||
var gameDir = "";
|
var gameDir = "";
|
||||||
|
|
||||||
// Framework initialization
|
// Framework initialization
|
||||||
function initialize(dir) {
|
function initialize() {
|
||||||
gameDir = dir;
|
var configPath = `config.js`;
|
||||||
|
if (io.exists(configPath))
|
||||||
// Load configuration if it exists
|
|
||||||
var configPath = `${dir}/config.js`;
|
|
||||||
if (io.exists(configPath)) {
|
|
||||||
gameConfig = use(configPath);
|
gameConfig = use(configPath);
|
||||||
}
|
|
||||||
|
|
||||||
// Set up default config values
|
// Set up default config values
|
||||||
gameConfig.resolution = gameConfig.resolution || { width: 640, height: 480 };
|
gameConfig.resolution = gameConfig.resolution || { width: 640, height: 480 };
|
||||||
@@ -52,42 +47,8 @@ function initialize(dir) {
|
|||||||
|
|
||||||
// Set window title
|
// Set window title
|
||||||
prosperon.window.title = gameConfig.title;
|
prosperon.window.title = gameConfig.title;
|
||||||
|
|
||||||
// Set up IO actor subscription
|
|
||||||
var ioguy = {
|
|
||||||
__ACTORDATA__: {
|
|
||||||
id: os.ioactor()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$_.send(ioguy, {
|
|
||||||
type: "subscribe",
|
|
||||||
actor: $_
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set up automatic receiver for input events
|
|
||||||
$_.receiver(function(e) {
|
|
||||||
// Handle quit
|
|
||||||
if (e.type === 'quit') {
|
|
||||||
os.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forward all events to prosperon's dispatch system
|
|
||||||
prosperon.dispatch(e.type, e);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start the game loop
|
|
||||||
startGameLoop();
|
startGameLoop();
|
||||||
|
|
||||||
// Load the game's main.js
|
|
||||||
|
|
||||||
var mainPath = `${dir}/main.js`;
|
|
||||||
if (io.exists(mainPath)) {
|
|
||||||
// Spawn the main game actor
|
|
||||||
actor.spawn(mainPath);
|
|
||||||
} else {
|
|
||||||
console.error(`No main.js found in ${dir}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main game loop
|
// Main game loop
|
||||||
|
|||||||
Reference in New Issue
Block a user