Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc58cc5a12 | ||
|
|
a274fb174f | ||
|
|
3622a5ec58 | ||
|
|
8a5f8a4d74 | ||
|
|
c1d341eecd | ||
|
|
3176e6775d | ||
|
|
34dcd0a235 | ||
|
|
cbda7dfbc9 | ||
|
|
d039e2cfe6 | ||
|
|
c02bd06ec0 | ||
|
|
efa63771e6 | ||
|
|
9f6d27fb3c | ||
|
|
1a61ae6f77 | ||
|
|
83c816fd0e | ||
|
|
adbaa92dd5 | ||
|
|
580df9f233 | ||
|
|
d5d17560f9 | ||
|
|
cd05ab97b5 | ||
|
|
4eecbd692b | ||
|
|
72beed7177 | ||
|
|
e0595de71a | ||
|
|
6687008d1a | ||
|
|
5b9f1b8f51 | ||
|
|
c570de7f41 | ||
|
|
d0138a6c23 | ||
|
|
29aa25e866 | ||
|
|
ef28be93db | ||
|
|
0d7be6a94e | ||
|
|
4fe78c4a63 | ||
|
|
b52edb2746 | ||
|
|
79d5412fe6 | ||
|
|
fcec2cd1dc | ||
|
|
2038ce15a7 | ||
|
|
08557011cb | ||
|
|
3e87bfd6cc | ||
|
|
ef86dd3ecf | ||
|
|
c887bcf7b9 | ||
|
|
709f2459e4 | ||
|
|
cdf8686c64 | ||
|
|
2fdf74f6ee | ||
|
|
e689679aac | ||
|
|
f70f65d1c3 | ||
|
|
d9b316270d | ||
|
|
e2668b330e | ||
|
|
90b5d1430f | ||
|
|
7c47c43655 | ||
|
|
9e45219706 | ||
|
|
d098800c88 | ||
|
|
3a40076958 | ||
|
|
06108df3d4 | ||
|
|
a442cf5a4d | ||
|
|
6dee29d213 | ||
|
|
7711c644a0 | ||
|
|
aab0a56349 | ||
|
|
13245bbc98 | ||
|
|
c25166d35a | ||
|
|
fc09693c93 | ||
|
|
b71c72db8b | ||
|
|
66591e32b5 | ||
|
|
fba05fa0fb | ||
|
|
11357d4fb5 | ||
|
|
674eb237e0 | ||
|
|
939269b060 | ||
|
|
f54200a7dd | ||
|
|
9ae2357493 | ||
|
|
da525cd111 | ||
|
|
c3f07c0ef5 | ||
|
|
2e7643aa2a | ||
|
|
aca9baf585 | ||
|
|
b4371ba3e0 | ||
|
|
4e118dd8e9 | ||
|
|
9279e21b84 | ||
|
|
8d9bb4a2c9 | ||
|
|
1040c61863 | ||
|
|
e86bdf52fe | ||
|
|
53b3f0af9c | ||
|
|
09f48d08b9 | ||
|
|
4eb592b740 | ||
|
|
c603e8f006 | ||
|
|
f334a2ad56 | ||
|
|
a39f287a88 | ||
|
|
758b3e4704 | ||
|
|
aa70dcbdc2 | ||
|
|
3667d53eae | ||
|
|
01df337ccc | ||
|
|
ad182d68ec | ||
|
|
f7dcc8f57c | ||
|
|
f73f738459 | ||
|
|
bf74a3c7d4 | ||
|
|
e8fb50659d | ||
|
|
00df0899fa | ||
|
|
ae5ba67fc8 | ||
|
|
bc929988b2 | ||
|
|
2346040d46 | ||
|
|
2eb6b3e0b4 | ||
|
|
2edcd89780 | ||
|
|
a63e5c5b55 | ||
|
|
af21e10e97 | ||
|
|
1b97527120 | ||
|
|
8074e2a82e | ||
|
|
db1afb6477 | ||
|
|
45311408d6 | ||
|
|
1141fca63a | ||
|
|
7b70def11f | ||
|
|
aac0c3813b | ||
|
|
49786842f0 | ||
|
|
9f9dfe03a6 | ||
|
|
792da2ce4b | ||
|
|
85ee724754 |
17
.cell/cell.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
sdl_video = "main"
|
||||||
|
[dependencies]
|
||||||
|
extramath = "https://gitea.pockle.world/john/extramath@master"
|
||||||
|
[system]
|
||||||
|
ar_timer = 60
|
||||||
|
actor_memory = 0
|
||||||
|
net_service = 0.1
|
||||||
|
reply_timeout = 60
|
||||||
|
actor_max = "10_000"
|
||||||
|
stack_max = 0
|
||||||
|
[actors]
|
||||||
|
[actors.prosperon/sdl_video]
|
||||||
|
main = true
|
||||||
|
[actors.prosperon/prosperon]
|
||||||
|
main = true
|
||||||
|
[actors.prosperon]
|
||||||
|
main = true
|
||||||
6
.cell/lock.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[modules]
|
||||||
|
[modules.extramath]
|
||||||
|
hash = "MCLZT3JABTAENS4WVXKGWJ7JPBLZER4YQ5VN2PE7ZD2Z4WYGTIMA===="
|
||||||
|
url = "https://gitea.pockle.world/john/extramath@master"
|
||||||
|
downloaded = "Monday June 2 12:07:20.42 PM -5 2025 AD"
|
||||||
|
commit = "84d81a19a8455bcf8dc494739e9e6d545df6ff2c"
|
||||||
13
.gitignore
vendored
@@ -6,27 +6,18 @@ build/
|
|||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
*.d
|
*.d
|
||||||
tags
|
|
||||||
Jenkinsfile
|
|
||||||
*~
|
*~
|
||||||
*.log
|
*.log
|
||||||
*.gz
|
*.gz
|
||||||
*.tar
|
*.tar
|
||||||
.nova/
|
.nova/
|
||||||
packer*
|
|
||||||
primum
|
|
||||||
sokol-shdc*
|
|
||||||
source/shaders/*.h
|
source/shaders/*.h
|
||||||
core.cdb
|
|
||||||
primum.exe
|
|
||||||
core.cdb.h
|
|
||||||
jsc
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.html
|
*.html
|
||||||
.vscode
|
.vscode
|
||||||
*.icns
|
*.icns
|
||||||
game.zip
|
|
||||||
icon.ico
|
icon.ico
|
||||||
steam/
|
steam/
|
||||||
subprojects/*/
|
subprojects/*/
|
||||||
build_dbg/
|
build_dbg/
|
||||||
|
modules/
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ This is a game engine developed using a QuickJS fork as its scripting language.
|
|||||||
|
|
||||||
## Coding Practices
|
## Coding Practices
|
||||||
- Use K&R style C
|
- Use K&R style C
|
||||||
- Use as little whitespace as possible
|
|
||||||
- Javascript style prefers objects and prototypical inheritence over ES6 classes, liberal use of closures, and var everywhere
|
- Javascript style prefers objects and prototypical inheritence over ES6 classes, liberal use of closures, and var everywhere
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|||||||
21
CLAUDE.md
@@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
## Build Commands
|
## Build Commands
|
||||||
|
|
||||||
### Build variants
|
### Build variants
|
||||||
- `make debug` - Build debug version (uses meson debug configuration)
|
- `make` - Make and install debug version. Usually all that's needed.
|
||||||
- `make fast` - Build optimized version
|
- `make fast` - Build optimized version
|
||||||
- `make release` - Build release version with LTO and optimizations
|
- `make release` - Build release version with LTO and optimizations
|
||||||
- `make small` - Build minimal size version
|
- `make small` - Build minimal size version
|
||||||
@@ -13,10 +13,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
- `make crosswin` - Cross-compile for Windows using mingw32
|
- `make crosswin` - Cross-compile for Windows using mingw32
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
- `meson test -C build_dbg` - Run all tests in debug build
|
After install with 'make', just run 'cell' and point it at the actor you want to launch. "cell tests/toml" runs the actor "tests/toml.js"
|
||||||
- `meson test -C build_<variant>` - Run tests in specific build variant
|
|
||||||
- `./build_dbg/prosperon tests/<testname>.js` - Run specific test
|
## Scripting language
|
||||||
- Available tests: `spawn_actor`, `empty`, `nota`, `wota`, `portalspawner`, `overling`, `send`, `delay`
|
This is called "cell", but it is is a variant of javascript and extremely similar.
|
||||||
|
|
||||||
### Common development commands
|
### Common development commands
|
||||||
- `meson setup build_<variant>` - Configure build directory
|
- `meson setup build_<variant>` - Configure build directory
|
||||||
@@ -126,7 +126,7 @@ meson test -C build_dbg
|
|||||||
### Debugging
|
### Debugging
|
||||||
- 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 `log.console()`, `log.error()`, etc.
|
||||||
- Log files written to `.prosperon/log.txt`
|
- Log files written to `.prosperon/log.txt`
|
||||||
|
|
||||||
# Project Structure Notes
|
# Project Structure Notes
|
||||||
@@ -202,6 +202,11 @@ meson test -C build_dbg
|
|||||||
|
|
||||||
### Utility Modules
|
### Utility Modules
|
||||||
- `time` - Time management and delays
|
- `time` - Time management and delays
|
||||||
|
- **Must be imported with `use('time')`**
|
||||||
|
- No `time.now()` function - use:
|
||||||
|
- `time.number()` - Number representation of current time
|
||||||
|
- `time.record()` - Struct representation of current time
|
||||||
|
- `time.text()` - Text representation of current time
|
||||||
- `io` - File I/O operations
|
- `io` - File I/O operations
|
||||||
- `json` - JSON parsing and serialization
|
- `json` - JSON parsing and serialization
|
||||||
- `util` - General utilities
|
- `util` - General utilities
|
||||||
@@ -234,7 +239,7 @@ When sending a message with a callback, respond by sending to the message itself
|
|||||||
```javascript
|
```javascript
|
||||||
// Sender side:
|
// Sender side:
|
||||||
send(actor, {type: 'status'}, response => {
|
send(actor, {type: 'status'}, response => {
|
||||||
console.log(response); // Handle the response
|
log.console(response); // Handle the response
|
||||||
});
|
});
|
||||||
|
|
||||||
// Receiver side:
|
// Receiver side:
|
||||||
@@ -279,7 +284,7 @@ $_.receiver(msg => {
|
|||||||
- Custom formats: Aseprite animations, etc.
|
- Custom formats: Aseprite animations, etc.
|
||||||
|
|
||||||
### Developer Tools
|
### Developer Tools
|
||||||
- Built-in documentation system with `prosperon.DOC`
|
- Built-in documentation system with `cell.DOC`
|
||||||
- Tracy profiler integration for performance monitoring
|
- Tracy profiler integration for performance monitoring
|
||||||
- Imgui debugging tools
|
- Imgui debugging tools
|
||||||
- Console logging with various severity levels
|
- Console logging with various severity levels
|
||||||
|
|||||||
10
Makefile
@@ -1,22 +1,22 @@
|
|||||||
debug: FORCE
|
debug: FORCE
|
||||||
meson setup build_dbg -Dbuildtype=debug
|
meson setup build_dbg -Dbuildtype=debug
|
||||||
meson compile -C build_dbg
|
meson install --only-changed -C build_dbg
|
||||||
|
|
||||||
fast: FORCE
|
fast: FORCE
|
||||||
meson setup build_fast
|
meson setup build_fast
|
||||||
meson compile -C build_fast
|
meson install -C build_fast
|
||||||
|
|
||||||
release: FORCE
|
release: FORCE
|
||||||
meson setup -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true build_release
|
meson setup -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true build_release
|
||||||
meson compile -C build_release
|
meson install -C build_release
|
||||||
|
|
||||||
sanitize: FORCE
|
sanitize: FORCE
|
||||||
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
|
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
|
||||||
meson compile -C build_sani
|
meson install -C build_sani
|
||||||
|
|
||||||
small: FORCE
|
small: FORCE
|
||||||
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small
|
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small
|
||||||
meson compile -C build_small
|
meson install -C build_small
|
||||||
|
|
||||||
web: FORCE
|
web: FORCE
|
||||||
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web
|
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web
|
||||||
|
|||||||
@@ -49,28 +49,28 @@ function getStats(arr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pretty print results
|
// Pretty print results
|
||||||
console.log("\n=== Performance Test Results (100 iterations) ===");
|
log.console("\n=== Performance Test Results (100 iterations) ===");
|
||||||
console.log("\nJSON Decoding (ms):");
|
log.console("\nJSON Decoding (ms):");
|
||||||
const jsonDecStats = getStats(jsonDecodeTimes);
|
const jsonDecStats = getStats(jsonDecodeTimes);
|
||||||
console.log(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
|
log.console(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
|
||||||
console.log(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
|
log.console(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
|
||||||
console.log(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
|
log.console(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
console.log("\nJSON Encoding (ms):");
|
log.console("\nJSON Encoding (ms):");
|
||||||
const jsonEncStats = getStats(jsonEncodeTimes);
|
const jsonEncStats = getStats(jsonEncodeTimes);
|
||||||
console.log(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
|
log.console(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
|
||||||
console.log(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
|
log.console(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
|
||||||
console.log(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
|
log.console(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
console.log("\nNOTA Encoding (ms):");
|
log.console("\nNOTA Encoding (ms):");
|
||||||
const notaEncStats = getStats(notaEncodeTimes);
|
const notaEncStats = getStats(notaEncodeTimes);
|
||||||
console.log(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
|
log.console(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
|
||||||
console.log(`Min: ${notaEncStats.min.toFixed(2)} ms`);
|
log.console(`Min: ${notaEncStats.min.toFixed(2)} ms`);
|
||||||
console.log(`Max: ${notaEncStats.max.toFixed(2)} ms`);
|
log.console(`Max: ${notaEncStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
console.log("\nNOTA Decoding (ms):");
|
log.console("\nNOTA Decoding (ms):");
|
||||||
const notaDecStats = getStats(notaDecodeTimes);
|
const notaDecStats = getStats(notaDecodeTimes);
|
||||||
console.log(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
|
log.console(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
|
||||||
console.log(`Min: ${notaDecStats.min.toFixed(2)} ms`);
|
log.console(`Min: ${notaDecStats.min.toFixed(2)} ms`);
|
||||||
console.log(`Max: ${notaDecStats.max.toFixed(2)} ms`);
|
log.console(`Max: ${notaDecStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
@@ -75,8 +75,8 @@ const benchmarks = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Print a header
|
// Print a header
|
||||||
console.log("Wota Encode/Decode Benchmark");
|
log.console("Wota Encode/Decode Benchmark");
|
||||||
console.log("============================\n");
|
log.console("============================\n");
|
||||||
|
|
||||||
// We'll run each benchmark scenario in turn.
|
// We'll run each benchmark scenario in turn.
|
||||||
for (let bench of benchmarks) {
|
for (let bench of benchmarks) {
|
||||||
@@ -96,11 +96,11 @@ for (let bench of benchmarks) {
|
|||||||
let elapsedSec = measureTime(runAllData, bench.iterations);
|
let elapsedSec = measureTime(runAllData, bench.iterations);
|
||||||
let opsPerSec = (totalIterations / elapsedSec).toFixed(1);
|
let opsPerSec = (totalIterations / elapsedSec).toFixed(1);
|
||||||
|
|
||||||
console.log(`${bench.name}:`);
|
log.console(`${bench.name}:`);
|
||||||
console.log(` Iterations: ${bench.iterations} × ${bench.data.length} data items = ${totalIterations}`);
|
log.console(` Iterations: ${bench.iterations} × ${bench.data.length} data items = ${totalIterations}`);
|
||||||
console.log(` Elapsed: ${elapsedSec.toFixed(3)} s`);
|
log.console(` Elapsed: ${elapsedSec.toFixed(3)} s`);
|
||||||
console.log(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
|
log.console(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All done
|
// All done
|
||||||
console.log("Benchmark completed.\n");
|
log.console("Benchmark completed.\n");
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
var wota = use('wota');
|
var wota = use('wota');
|
||||||
var nota = use('nota');
|
var nota = use('nota');
|
||||||
var json = use('json');
|
var json = use('json');
|
||||||
|
var jswota = use('jswota')
|
||||||
var os = use('os');
|
var os = use('os');
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ const libraries = [
|
|||||||
decode: wota.decode,
|
decode: wota.decode,
|
||||||
// Wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
// Wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
||||||
getSize(encoded) {
|
getSize(encoded) {
|
||||||
return encoded.byteLength;
|
return encoded.length;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,7 +32,7 @@ const libraries = [
|
|||||||
decode: nota.decode,
|
decode: nota.decode,
|
||||||
// Nota also produces an ArrayBuffer:
|
// Nota also produces an ArrayBuffer:
|
||||||
getSize(encoded) {
|
getSize(encoded) {
|
||||||
return encoded.byteLength;
|
return encoded.length;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -53,6 +54,11 @@ const libraries = [
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const benchmarks = [
|
const benchmarks = [
|
||||||
|
{
|
||||||
|
name: "Empty object",
|
||||||
|
data: [{}, {}, {}, {}],
|
||||||
|
iterations: 10000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Small Integers",
|
name: "Small Integers",
|
||||||
data: [0, 42, -1, 2023],
|
data: [0, 42, -1, 2023],
|
||||||
@@ -86,11 +92,6 @@ const benchmarks = [
|
|||||||
data: [ Array.from({length:1000}, (_, i) => i) ],
|
data: [ Array.from({length:1000}, (_, i) => i) ],
|
||||||
iterations: 1000
|
iterations: 1000
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Large Binary Blob (256KB)",
|
|
||||||
data: [ new Uint8Array(256 * 1024).buffer ],
|
|
||||||
iterations: 200
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -125,8 +126,8 @@ function runBenchmarkForLibrary(lib, bench) {
|
|||||||
let encodeTime = measureTime(() => {
|
let encodeTime = measureTime(() => {
|
||||||
for (let i = 0; i < bench.iterations; i++) {
|
for (let i = 0; i < bench.iterations; i++) {
|
||||||
// For each data item, encode it
|
// For each data item, encode it
|
||||||
for (let d of bench.data) {
|
for (let j = 0; j < bench.data.length; j++) {
|
||||||
let e = lib.encode(d);
|
let e = lib.encode(bench.data[j]);
|
||||||
// store only in the very first iteration, so we can decode them later
|
// store only in the very first iteration, so we can decode them later
|
||||||
// but do not store them every iteration or we blow up memory.
|
// but do not store them every iteration or we blow up memory.
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
@@ -155,12 +156,12 @@ function runBenchmarkForLibrary(lib, bench) {
|
|||||||
// 5. Main driver: run across all benchmarks, for each library.
|
// 5. Main driver: run across all benchmarks, for each library.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
console.log("Benchmark: Wota vs Nota vs JSON");
|
log.console("Benchmark: Wota vs Nota vs JSON");
|
||||||
console.log("================================\n");
|
log.console("================================\n");
|
||||||
|
|
||||||
for (let bench of benchmarks) {
|
for (let bench of benchmarks) {
|
||||||
console.log(`SCENARIO: ${bench.name}`);
|
log.console(`SCENARIO: ${bench.name}`);
|
||||||
console.log(` Data length: ${bench.data.length} | Iterations: ${bench.iterations}\n`);
|
log.console(` Data length: ${bench.data.length} | Iterations: ${bench.iterations}\n`);
|
||||||
|
|
||||||
for (let lib of libraries) {
|
for (let lib of libraries) {
|
||||||
let { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
let { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
||||||
@@ -170,13 +171,15 @@ for (let bench of benchmarks) {
|
|||||||
let encOpsPerSec = (totalOps / encodeTime).toFixed(1);
|
let encOpsPerSec = (totalOps / encodeTime).toFixed(1);
|
||||||
let decOpsPerSec = (totalOps / decodeTime).toFixed(1);
|
let decOpsPerSec = (totalOps / decodeTime).toFixed(1);
|
||||||
|
|
||||||
console.log(` ${lib.name}:`);
|
log.console(` ${lib.name}:`);
|
||||||
console.log(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec`);
|
log.console(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec [${(encodeTime/bench.iterations)*1000000000} ns/try]`);
|
||||||
console.log(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec`);
|
log.console(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec [${(decodeTime/bench.iterations)*1000000000}/try]`);
|
||||||
console.log(` Total size: ${totalSize} bytes (or code units for JSON)`);
|
log.console(` Total size: ${totalSize} bytes (or code units for JSON)`);
|
||||||
console.log("");
|
log.console("");
|
||||||
}
|
}
|
||||||
console.log("---------------------------------------------------------\n");
|
log.console("---------------------------------------------------------\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Benchmark complete.\n");
|
log.console("Benchmark complete.\n");
|
||||||
|
|
||||||
|
os.exit()
|
||||||
@@ -47,7 +47,7 @@ Certain functions are intrinsic to the program and cannot be overridden. They’
|
|||||||
- **Example**:
|
- **Example**:
|
||||||
```js
|
```js
|
||||||
this.delay(_ => {
|
this.delay(_ => {
|
||||||
console.log("3 seconds later!")
|
log.console("3 seconds later!")
|
||||||
}, 3)
|
}, 3)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
Provides a consistent way to create documentation for prosperon elements. Objects are documented by adding docstrings directly to object-like things (functions, objects, ...), or to an object's own "doc object".
|
Provides a consistent way to create documentation for prosperon elements. Objects are documented by adding docstrings directly to object-like things (functions, objects, ...), or to an object's own "doc object".
|
||||||
|
|
||||||
Docstrings are set to the symbol `prosperon.DOC`
|
Docstrings are set to the symbol `cell.DOC`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Suppose we have a module that returns a function
|
// Suppose we have a module that returns a function
|
||||||
function greet(name) { console.log("Hello, " + name) }
|
function greet(name) { log.console("Hello, " + name) }
|
||||||
|
|
||||||
// We can attach a docstring
|
// We can attach a docstring
|
||||||
greet.doc = `
|
greet.doc = `
|
||||||
@@ -21,12 +21,12 @@ return greet
|
|||||||
```js
|
```js
|
||||||
// Another way is to add a docstring object to an object
|
// Another way is to add a docstring object to an object
|
||||||
var greet = {
|
var greet = {
|
||||||
hello() { console.log('hello!') }
|
hello() { log.console('hello!') }
|
||||||
}
|
}
|
||||||
|
|
||||||
greet[prosperon.DOC] = {}
|
greet[cell.DOC] = {}
|
||||||
greet[prosperon.DOC][prosperon.DOC] = 'An object full of different greeter functions'
|
greet[cell.DOC][cell.DOC] = 'An object full of different greeter functions'
|
||||||
greet[prosperon.DOC].hello = 'A greeter that says, "hello!"'
|
greet[cell.DOC].hello = 'A greeter that says, "hello!"'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ return {
|
|||||||
This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
|
This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
console.log("Hello world")
|
log.console("Hello world")
|
||||||
|
|
||||||
this.delay(_ => {
|
this.delay(_ => {
|
||||||
this.kill();
|
this.kill();
|
||||||
@@ -62,6 +62,6 @@ this.delay(_ => {
|
|||||||
The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
|
The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
|
||||||
|
|
||||||
## Getting help
|
## Getting help
|
||||||
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `prosperon.doc(prosperon)` in your `main.js`.
|
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `cell.DOC(prosperon)` in your `main.js`.
|
||||||
|
|
||||||
Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.
|
Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.
|
||||||
@@ -101,7 +101,7 @@ $_.receiver(function(msg) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'status':
|
case 'status':
|
||||||
console.log(`got status request. current is ${json.encode(get_status())}`)
|
log.console(`got status request. current is ${json.encode(get_status())}`)
|
||||||
send(msg, {
|
send(msg, {
|
||||||
type: 'status_response',
|
type: 'status_response',
|
||||||
...get_status()
|
...get_status()
|
||||||
@@ -8,13 +8,13 @@ var waiting_client = null;
|
|||||||
var match_id = 0;
|
var match_id = 0;
|
||||||
|
|
||||||
$_.portal(e => {
|
$_.portal(e => {
|
||||||
console.log("NAT server: received connection request");
|
log.console("NAT server: received connection request");
|
||||||
|
|
||||||
if (!is_actor(e.actor))
|
if (!is_actor(e.actor))
|
||||||
send(e, {reason: "Must provide the actor you want to connect."});
|
send(e, {reason: "Must provide the actor you want to connect."});
|
||||||
|
|
||||||
if (waiting_client) {
|
if (waiting_client) {
|
||||||
console.log(`sending out messages! to ${json.encode(e.actor)} and ${json.encode(waiting_client.actor)}`)
|
log.console(`sending out messages! to ${json.encode(e.actor)} and ${json.encode(waiting_client.actor)}`)
|
||||||
send(waiting_client, e.actor)
|
send(waiting_client, e.actor)
|
||||||
send(e, waiting_client.actor)
|
send(e, waiting_client.actor)
|
||||||
|
|
||||||
@@ -25,5 +25,5 @@ $_.portal(e => {
|
|||||||
|
|
||||||
waiting_client = e
|
waiting_client = e
|
||||||
|
|
||||||
console.log(`actor ${json.encode(e.actor)} is waiting ...`)
|
log.console(`actor ${json.encode(e.actor)} is waiting ...`)
|
||||||
}, 4000);
|
}, 4000);
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
console.log(`nat client starting`)
|
log.console(`nat client starting`)
|
||||||
|
|
||||||
$_.contact((actor, reason) => {
|
$_.contact((actor, reason) => {
|
||||||
if (actor) {
|
if (actor) {
|
||||||
console.log(`trying to message ${json.encode(actor)}`)
|
log.console(`trying to message ${json.encode(actor)}`)
|
||||||
send(actor, {type:"greet"})
|
send(actor, {type:"greet"})
|
||||||
} else {
|
} else {
|
||||||
console.log(json.encode(reason))
|
log.console(json.encode(reason))
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
address: "108.210.60.32", // NAT server's public IP
|
address: "108.210.60.32", // NAT server's public IP
|
||||||
@@ -16,7 +16,7 @@ $_.contact((actor, reason) => {
|
|||||||
$_.receiver(e => {
|
$_.receiver(e => {
|
||||||
switch(e.type) {
|
switch(e.type) {
|
||||||
case 'greet':
|
case 'greet':
|
||||||
console.log(`hello!`)
|
log.console(`hello!`)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
262
meson.build
@@ -1,4 +1,4 @@
|
|||||||
project('prosperon', ['c', 'cpp'],
|
project('cell', ['c', 'cpp'],
|
||||||
version: '0.9.3',
|
version: '0.9.3',
|
||||||
meson_version: '>=1.4',
|
meson_version: '>=1.4',
|
||||||
default_options : [ 'cpp_std=c++11'])
|
default_options : [ 'cpp_std=c++11'])
|
||||||
@@ -13,21 +13,21 @@ fs = import('fs')
|
|||||||
add_project_arguments('-pedantic', language: ['c'])
|
add_project_arguments('-pedantic', language: ['c'])
|
||||||
|
|
||||||
git_tag_cmd = run_command('git', 'describe', '--tags', '--abbrev=0', check: false)
|
git_tag_cmd = run_command('git', 'describe', '--tags', '--abbrev=0', check: false)
|
||||||
prosperon_version = 'unknown'
|
cell_version = 'unknown'
|
||||||
if git_tag_cmd.returncode() == 0
|
if git_tag_cmd.returncode() == 0
|
||||||
prosperon_version = git_tag_cmd.stdout().strip()
|
cell_version = git_tag_cmd.stdout().strip()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
git_commit_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false)
|
git_commit_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false)
|
||||||
prosperon_commit = 'unknown'
|
cell_commit = 'unknown'
|
||||||
if git_commit_cmd.returncode() == 0
|
if git_commit_cmd.returncode() == 0
|
||||||
prosperon_commit = git_commit_cmd.stdout().strip()
|
cell_commit = git_commit_cmd.stdout().strip()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Important: pass the definitions without double-escaping quotes
|
# Important: pass the definitions without double-escaping quotes
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
'-DPROSPERON_VERSION="' + prosperon_version + '"',
|
'-DCELL_VERSION="' + cell_version + '"',
|
||||||
'-DPROSPERON_COMMIT="' + prosperon_commit + '"',
|
'-DCELL_COMMIT="' + cell_commit + '"',
|
||||||
language : 'c'
|
language : 'c'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,21 +67,31 @@ endif
|
|||||||
|
|
||||||
cmake = import('cmake')
|
cmake = import('cmake')
|
||||||
|
|
||||||
mbedtls_opts = cmake.subproject_options()
|
# Try to find system-installed mbedtls first
|
||||||
mbedtls_opts.add_cmake_defines({
|
mbedtls_dep = dependency('mbedtls', static: true, required: false)
|
||||||
'ENABLE_PROGRAMS': 'OFF', # Disable Mbed TLS programs
|
mbedx509_dep = dependency('mbedx509', static: true, required: false)
|
||||||
'ENABLE_TESTING': 'OFF', # Disable Mbed TLS tests
|
mbedcrypto_dep = dependency('mbedcrypto', static: true, required: false)
|
||||||
'CMAKE_BUILD_TYPE': 'Release', # Optimize for release
|
|
||||||
'MBEDTLS_FATAL_WARNINGS': 'ON', # Treat warnings as errors
|
if not mbedtls_dep.found() or not mbedx509_dep.found() or not mbedcrypto_dep.found()
|
||||||
'USE_STATIC_MBEDTLS_LIBRARY': 'ON',# Build static libraries
|
message('⚙ System mbedtls not found, building subproject...')
|
||||||
'USE_SHARED_MBEDTLS_LIBRARY': 'OFF'# Disable shared libraries
|
mbedtls_opts = cmake.subproject_options()
|
||||||
})
|
mbedtls_opts.add_cmake_defines({
|
||||||
mbedtls_proj = cmake.subproject('mbedtls', options: mbedtls_opts)
|
'ENABLE_PROGRAMS': 'OFF', # Disable Mbed TLS programs
|
||||||
deps += [
|
'ENABLE_TESTING': 'OFF', # Disable Mbed TLS tests
|
||||||
mbedtls_proj.dependency('mbedtls'),
|
'CMAKE_BUILD_TYPE': 'Release', # Optimize for release
|
||||||
mbedtls_proj.dependency('mbedx509'),
|
'MBEDTLS_FATAL_WARNINGS': 'ON', # Treat warnings as errors
|
||||||
mbedtls_proj.dependency('mbedcrypto')
|
'USE_STATIC_MBEDTLS_LIBRARY': 'ON',# Build static libraries
|
||||||
]
|
'USE_SHARED_MBEDTLS_LIBRARY': 'OFF'# Disable shared libraries
|
||||||
|
})
|
||||||
|
mbedtls_proj = cmake.subproject('mbedtls', options: mbedtls_opts)
|
||||||
|
deps += [
|
||||||
|
mbedtls_proj.dependency('mbedtls'),
|
||||||
|
mbedtls_proj.dependency('mbedx509'),
|
||||||
|
mbedtls_proj.dependency('mbedcrypto')
|
||||||
|
]
|
||||||
|
else
|
||||||
|
deps += [mbedtls_dep, mbedx509_dep, mbedcrypto_dep]
|
||||||
|
endif
|
||||||
|
|
||||||
sdl3_opts = cmake.subproject_options()
|
sdl3_opts = cmake.subproject_options()
|
||||||
sdl3_opts.add_cmake_defines({
|
sdl3_opts.add_cmake_defines({
|
||||||
@@ -130,34 +140,106 @@ if host_machine.system() == 'emscripten'
|
|||||||
method : 'pkg-config', # or 'cmake' if you prefer
|
method : 'pkg-config', # or 'cmake' if you prefer
|
||||||
required : true)
|
required : true)
|
||||||
else
|
else
|
||||||
sdl3_proj = cmake.subproject('sdl3', options : sdl3_opts)
|
# Try to find system-installed SDL3 first
|
||||||
deps += sdl3_proj.dependency('SDL3-static')
|
sdl3_dep = dependency('sdl3', static: true, required: false)
|
||||||
|
|
||||||
|
if not sdl3_dep.found()
|
||||||
|
message('⚙ System SDL3 not found, building subproject...')
|
||||||
|
sdl3_proj = cmake.subproject('sdl3', options : sdl3_opts)
|
||||||
|
deps += sdl3_proj.dependency('SDL3-static')
|
||||||
|
else
|
||||||
|
deps += sdl3_dep
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
quickjs_opts = []
|
# Try to find system-installed qjs-layout first
|
||||||
|
qjs_layout_dep = dependency('qjs-layout', static: true, required: false)
|
||||||
|
if not qjs_layout_dep.found()
|
||||||
|
message('⚙ System qjs-layout not found, building subproject...')
|
||||||
|
deps += dependency('qjs-layout', static:true)
|
||||||
|
else
|
||||||
|
deps += qjs_layout_dep
|
||||||
|
endif
|
||||||
|
|
||||||
|
miniz_dep = dependency('miniz', static: true, required: false)
|
||||||
|
if not miniz_dep.found()
|
||||||
|
message('⚙ System miniz not found, building subproject...')
|
||||||
|
deps += dependency('miniz', static:true)
|
||||||
|
else
|
||||||
|
deps += miniz_dep
|
||||||
|
endif
|
||||||
|
|
||||||
|
libuv_dep = dependency('libuv', static: true, required: false)
|
||||||
|
if not libuv_dep.found()
|
||||||
|
message('⚙ System libuv not found, building subproject...')
|
||||||
|
deps += dependency('libuv', static:true, fallback: ['libuv', 'libuv_dep'])
|
||||||
|
else
|
||||||
|
deps += libuv_dep
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Try to find system-installed physfs first
|
||||||
|
physfs_dep = dependency('physfs', static: true, required: false)
|
||||||
|
if not physfs_dep.found()
|
||||||
|
message('⚙ System physfs not found, building subproject...')
|
||||||
|
deps += dependency('physfs', static:true)
|
||||||
|
else
|
||||||
|
deps += physfs_dep
|
||||||
|
endif
|
||||||
|
|
||||||
quickjs_opts += 'default_library=static'
|
|
||||||
deps += dependency('quickjs', static:true, default_options:quickjs_opts)
|
|
||||||
deps += dependency('qjs-layout', static:true)
|
|
||||||
deps += dependency('qjs-miniz', static:true)
|
|
||||||
deps += dependency('physfs', static:true)
|
|
||||||
deps += dependency('threads')
|
deps += dependency('threads')
|
||||||
deps += dependency('chipmunk', static:true)
|
|
||||||
|
# Try to find system-installed chipmunk first
|
||||||
|
chipmunk_dep = dependency('chipmunk', static: true, required: false)
|
||||||
|
if not chipmunk_dep.found()
|
||||||
|
message('⚙ System chipmunk not found, building subproject...')
|
||||||
|
deps += dependency('chipmunk', static:true)
|
||||||
|
else
|
||||||
|
deps += chipmunk_dep
|
||||||
|
endif
|
||||||
|
|
||||||
if host_machine.system() != 'emscripten'
|
if host_machine.system() != 'emscripten'
|
||||||
deps += dependency('enet', static:true)
|
# Try to find system-installed enet first
|
||||||
|
enet_dep = dependency('enet', static: true, required: false)
|
||||||
|
if not enet_dep.found()
|
||||||
|
message('⚙ System enet not found, building subproject...')
|
||||||
|
deps += dependency('enet', static:true)
|
||||||
|
else
|
||||||
|
deps += enet_dep
|
||||||
|
endif
|
||||||
src += 'qjs_enet.c'
|
src += 'qjs_enet.c'
|
||||||
|
|
||||||
src += 'qjs_tracy.c'
|
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true']
|
||||||
tracy_opts = ['fibers=true', 'on_demand=true']
|
|
||||||
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
|
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
|
||||||
deps += dependency('tracy', static:true, default_options:tracy_opts)
|
|
||||||
|
# Try to find system-installed tracy first
|
||||||
|
tracy_dep = dependency('tracy', static: true, required: false)
|
||||||
|
if not tracy_dep.found()
|
||||||
|
message('⚙ System tracy not found, building subproject...')
|
||||||
|
deps += dependency('tracy', static:true, default_options:tracy_opts)
|
||||||
|
else
|
||||||
|
deps += tracy_dep
|
||||||
|
endif
|
||||||
|
|
||||||
src += 'qjs_dmon.c'
|
src += 'qjs_dmon.c'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
deps += dependency('soloud', static:true)
|
# Try to find system-installed soloud first
|
||||||
deps += dependency('libqrencode', static:true)
|
soloud_dep = dependency('soloud', static: true, required: false)
|
||||||
|
if not soloud_dep.found()
|
||||||
|
message('⚙ System soloud not found, building subproject...')
|
||||||
|
deps += dependency('soloud', static:true)
|
||||||
|
else
|
||||||
|
deps += soloud_dep
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Try to find system-installed qrencode first
|
||||||
|
qr_dep = dependency('qrencode', static: true, required: false)
|
||||||
|
if not qr_dep.found()
|
||||||
|
message('⚙ System qrencode not found, building subproject...')
|
||||||
|
deps += dependency('libqrencode', static:true)
|
||||||
|
else
|
||||||
|
deps += qr_dep
|
||||||
|
endif
|
||||||
|
|
||||||
# Storefront SDK support
|
# Storefront SDK support
|
||||||
storefront = get_option('storefront')
|
storefront = get_option('storefront')
|
||||||
@@ -194,10 +276,14 @@ link_args = link
|
|||||||
sources = []
|
sources = []
|
||||||
src += [
|
src += [
|
||||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||||
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
|
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
|
||||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c', 'qjs_actor.c',
|
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
|
||||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c'
|
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# js src
|
||||||
|
src += [ 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c', 'quickjs.c' ]
|
||||||
|
|
||||||
# quirc src
|
# quirc src
|
||||||
src += [
|
src += [
|
||||||
'thirdparty/quirc/quirc.c', 'thirdparty/quirc/decode.c',
|
'thirdparty/quirc/quirc.c', 'thirdparty/quirc/decode.c',
|
||||||
@@ -223,99 +309,33 @@ foreach file : src
|
|||||||
sources += files(full_path)
|
sources += files(full_path)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
if get_option('editor')
|
|
||||||
# sources += 'source/qjs_imgui.cpp'
|
|
||||||
# foreach imgui : imsrc
|
|
||||||
# sources += tp / 'imgui' / imgui
|
|
||||||
# endforeach
|
|
||||||
endif
|
|
||||||
|
|
||||||
includers = []
|
includers = []
|
||||||
foreach inc : includes
|
foreach inc : includes
|
||||||
includers += include_directories(inc)
|
includers += include_directories(inc)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
zip_folders = ['scripts', 'fonts', 'icons', 'shaders']
|
|
||||||
zip_paths = []
|
|
||||||
foreach folder: zip_folders
|
|
||||||
zip_paths += meson.project_source_root() / folder
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
# Produce core.zip
|
|
||||||
core = custom_target('core.zip',
|
|
||||||
output : 'core.zip',
|
|
||||||
command : ['sh', '-c',
|
|
||||||
'cd ' + meson.project_source_root() +
|
|
||||||
' && echo "Rebuilding core.zip" && rm -f ' + meson.current_build_dir() + '/core.zip && ' +
|
|
||||||
'zip -r ' + meson.current_build_dir() + '/core.zip scripts fonts icons shaders'
|
|
||||||
],
|
|
||||||
build_always_stale: true,
|
|
||||||
build_by_default: true
|
|
||||||
)
|
|
||||||
|
|
||||||
prosperon_raw = executable('prosperon_raw', sources,
|
|
||||||
dependencies: deps,
|
|
||||||
include_directories: includers,
|
|
||||||
link_args: link,
|
|
||||||
build_rpath: '$ORIGIN',
|
|
||||||
install:false
|
|
||||||
)
|
|
||||||
|
|
||||||
strip_enabled = ['release', 'minsize'].contains(get_option('buildtype'))
|
|
||||||
|
|
||||||
if strip_enabled
|
|
||||||
prosperon_raw_stripped = custom_target('prosperon_raw_stripped',
|
|
||||||
input: prosperon_raw,
|
|
||||||
output: 'prosperon_raw_stripped',
|
|
||||||
command: [
|
|
||||||
'sh', '-c',
|
|
||||||
'strip "$1" && cp "$1" "$2"',
|
|
||||||
'stripper',
|
|
||||||
'@INPUT@',
|
|
||||||
'@OUTPUT@'
|
|
||||||
],
|
|
||||||
build_by_default: true
|
|
||||||
)
|
|
||||||
exe_for_concat = prosperon_raw_stripped
|
|
||||||
else
|
|
||||||
exe_for_concat = prosperon_raw
|
|
||||||
endif
|
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
exe_ext = '.exe'
|
exe_ext = '.exe'
|
||||||
else
|
else
|
||||||
exe_ext = ''
|
exe_ext = ''
|
||||||
endif
|
endif
|
||||||
|
|
||||||
prosperon = custom_target('prosperon',
|
strip_enabled = ['release', 'minsize'].contains(get_option('buildtype'))
|
||||||
output: 'prosperon' + exe_ext,
|
|
||||||
input: [exe_for_concat, core],
|
if strip_enabled
|
||||||
command: [
|
add_project_link_arguments('-s', language: ['c', 'cpp'])
|
||||||
'sh', '-c',
|
endif
|
||||||
'cat "$1" "$2" > "$3" && chmod +x "$3" >/dev/null 2>&1',
|
|
||||||
'concat',
|
cell = executable('cell', sources,
|
||||||
'@INPUT0@',
|
dependencies: deps,
|
||||||
'@INPUT1@',
|
include_directories: includers,
|
||||||
'@OUTPUT@'
|
link_args: link,
|
||||||
],
|
build_rpath: '$ORIGIN',
|
||||||
build_always_stale: true,
|
install: true
|
||||||
build_by_default: true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
prosperon_dep = declare_dependency(
|
cell_dep = declare_dependency(
|
||||||
link_with: prosperon
|
link_with: cell
|
||||||
)
|
|
||||||
|
|
||||||
copy_tests = custom_target(
|
|
||||||
'copy_tests',
|
|
||||||
output: 'tests',
|
|
||||||
command: [
|
|
||||||
'cp', '-rf',
|
|
||||||
join_paths(meson.project_source_root(), 'tests'),
|
|
||||||
meson.project_build_root()
|
|
||||||
],
|
|
||||||
build_always_stale: true,
|
|
||||||
build_by_default: true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
@@ -330,5 +350,5 @@ tests = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
foreach file : tests
|
foreach file : tests
|
||||||
test(file, prosperon_raw, args:['tests/' + file + '.js'], depends:copy_tests)
|
test(file, cell, args:['tests/' + file])
|
||||||
endforeach
|
endforeach
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
var cam = {}
|
var cam = {}
|
||||||
|
|
||||||
var os = use('os')
|
|
||||||
var transform = use('transform')
|
var transform = use('transform')
|
||||||
|
|
||||||
var basecam = {}
|
var basecam = {}
|
||||||
216
prosperon/color.cm
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
function tohex(n) {
|
||||||
|
var s = Math.floor(n).toString(16);
|
||||||
|
if (s.length === 1) s = "0" + s;
|
||||||
|
return s.toUpperCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
var Color = {
|
||||||
|
white: [1, 1, 1],
|
||||||
|
black: [0, 0, 0],
|
||||||
|
blue: [0, 0, 1],
|
||||||
|
green: [0, 1, 0],
|
||||||
|
yellow: [1, 1, 0],
|
||||||
|
red: [1, 0, 0],
|
||||||
|
gray: [0.71, 0.71, 0.71],
|
||||||
|
cyan: [0, 1, 1],
|
||||||
|
purple: [0.635, 0.365, 0.89],
|
||||||
|
orange: [1, 0.565, 0.251],
|
||||||
|
magenta: [1, 0, 1],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.editor = {};
|
||||||
|
Color.editor.ur = Color.green;
|
||||||
|
|
||||||
|
Color.tohtml = function (v) {
|
||||||
|
var html = v.map(function (n) {
|
||||||
|
return tohex(n * 255);
|
||||||
|
});
|
||||||
|
return "#" + html.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
var esc = {};
|
||||||
|
esc.reset = "\x1b[0";
|
||||||
|
esc.color = function (v) {
|
||||||
|
var c = v.map(function (n) {
|
||||||
|
return Math.floor(n * 255);
|
||||||
|
});
|
||||||
|
var truecolor = "\x1b[38;2;" + c.join(";") + ";";
|
||||||
|
return truecolor;
|
||||||
|
};
|
||||||
|
|
||||||
|
esc.doc = "Functions and constants for ANSI escape sequences.";
|
||||||
|
|
||||||
|
Color.Arkanoid = {
|
||||||
|
orange: [1, 0.561, 0],
|
||||||
|
teal: [0, 1, 1],
|
||||||
|
green: [0, 1, 0],
|
||||||
|
red: [1, 0, 0],
|
||||||
|
blue: [0, 0.439, 1],
|
||||||
|
purple: [1, 0, 1],
|
||||||
|
yellow: [1, 1, 0],
|
||||||
|
silver: [0.616, 0.616, 0.616],
|
||||||
|
gold: [0.737, 0.682, 0],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Arkanoid.Powerups = {
|
||||||
|
red: [0.682, 0, 0] /* laser */,
|
||||||
|
blue: [0, 0, 0.682] /* enlarge */,
|
||||||
|
green: [0, 0.682, 0] /* catch */,
|
||||||
|
orange: [0.878, 0.561, 0] /* slow */,
|
||||||
|
purple: [0.824, 0, 0.824] /* break */,
|
||||||
|
cyan: [0, 0.682, 1] /* disruption */,
|
||||||
|
gray: [0.561, 0.561, 0.561] /* 1up */,
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Gameboy = {
|
||||||
|
darkest: [0.898, 0.42, 0.102],
|
||||||
|
dark: [0.898, 0.741, 0.102],
|
||||||
|
light: [0.741, 0.898, 0.102],
|
||||||
|
lightest: [0.42, 0.898, 0.102],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Apple = {
|
||||||
|
green: [0.369, 0.741, 0.243],
|
||||||
|
yellow: [1, 0.725, 0],
|
||||||
|
orange: [0.969, 0.51, 0],
|
||||||
|
red: [0.886, 0.22, 0.22],
|
||||||
|
purple: [0.592, 0.224, 0.6],
|
||||||
|
blue: [0, 0.612, 0.875],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Debug = {
|
||||||
|
boundingbox: Color.white,
|
||||||
|
names: [0.329, 0.431, 1],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Editor = {
|
||||||
|
grid: [0.388, 1, 0.502],
|
||||||
|
select: [1, 1, 0.216],
|
||||||
|
newgroup: [0.471, 1, 0.039],
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Detects the format of all colors and munges them into a floating point format */
|
||||||
|
Color.normalize = function (c) {
|
||||||
|
var add_a = function (a) {
|
||||||
|
var n = this.slice();
|
||||||
|
n[3] = a;
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var p of Object.keys(c)) {
|
||||||
|
if (typeof c[p] !== "object") continue;
|
||||||
|
if (!Array.isArray(c[p])) {
|
||||||
|
Color.normalize(c[p]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add alpha channel if not present
|
||||||
|
if (c[p].length === 3) {
|
||||||
|
c[p][3] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any values are > 1 (meaning they're in 0-255 format)
|
||||||
|
var needs_conversion = false;
|
||||||
|
for (var color of c[p]) {
|
||||||
|
if (color > 1) {
|
||||||
|
needs_conversion = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from 0-255 to 0-1 if needed
|
||||||
|
if (needs_conversion) {
|
||||||
|
c[p] = c[p].map(function (x) {
|
||||||
|
return x / 255;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
c[p].alpha = add_a;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.normalize(Color);
|
||||||
|
|
||||||
|
var ColorMap = {};
|
||||||
|
ColorMap.makemap = function (map) {
|
||||||
|
var newmap = Object.create(ColorMap);
|
||||||
|
Object.assign(newmap, map);
|
||||||
|
return newmap;
|
||||||
|
};
|
||||||
|
ColorMap.Jet = ColorMap.makemap({
|
||||||
|
0: [0, 0, 0.514],
|
||||||
|
0.125: [0, 0.235, 0.667],
|
||||||
|
0.375: [0.02, 1, 1],
|
||||||
|
0.625: [1, 1, 0],
|
||||||
|
0.875: [0.98, 0, 0],
|
||||||
|
1: [0.502, 0, 0],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.BlueRed = ColorMap.makemap({
|
||||||
|
0: [0, 0, 1],
|
||||||
|
1: [1, 0, 0],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.Inferno = ColorMap.makemap({
|
||||||
|
0: [0, 0, 0.016],
|
||||||
|
0.13: [0.122, 0.047, 0.282],
|
||||||
|
0.25: [0.333, 0.059, 0.427],
|
||||||
|
0.38: [0.533, 0.133, 0.416],
|
||||||
|
0.5: [0.729, 0.212, 0.333],
|
||||||
|
0.63: [0.89, 0.349, 0.2],
|
||||||
|
0.75: [0.976, 0.549, 0.039],
|
||||||
|
0.88: [0.976, 0.788, 0.196],
|
||||||
|
1: [0.988, 1, 0.643],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.Bathymetry = ColorMap.makemap({
|
||||||
|
0: [0.157, 0.102, 0.173],
|
||||||
|
0.13: [0.233, 0.192, 0.353],
|
||||||
|
0.25: [0.251, 0.298, 0.545],
|
||||||
|
0.38: [0.247, 0.431, 0.592],
|
||||||
|
0.5: [0.282, 0.557, 0.62],
|
||||||
|
0.63: [0.333, 0.682, 0.639],
|
||||||
|
0.75: [0.471, 0.808, 0.639],
|
||||||
|
0.88: [0.733, 0.902, 0.675],
|
||||||
|
1: [0.992, 0.996, 0.8],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.Viridis = ColorMap.makemap({
|
||||||
|
0: [0.267, 0.004, 0.329],
|
||||||
|
0.13: [0.278, 0.173, 0.478],
|
||||||
|
0.25: [0.231, 0.318, 0.545],
|
||||||
|
0.38: [0.173, 0.443, 0.557],
|
||||||
|
0.5: [0.129, 0.565, 0.553],
|
||||||
|
0.63: [0.153, 0.678, 0.506],
|
||||||
|
0.75: [0.361, 0.784, 0.388],
|
||||||
|
0.88: [0.667, 0.863, 0.196],
|
||||||
|
1: [0.992, 0.906, 0.145],
|
||||||
|
});
|
||||||
|
|
||||||
|
Color.normalize(ColorMap);
|
||||||
|
|
||||||
|
ColorMap.sample = function (t, map = this) {
|
||||||
|
if (t < 0) return map[0];
|
||||||
|
if (t > 1) return map[1];
|
||||||
|
|
||||||
|
var lastkey = 0;
|
||||||
|
for (var key of Object.keys(map).sort()) {
|
||||||
|
if (t < key) {
|
||||||
|
var b = map[key];
|
||||||
|
var a = map[lastkey];
|
||||||
|
var tt = (key - lastkey) * t;
|
||||||
|
return a.lerp(b, tt);
|
||||||
|
}
|
||||||
|
lastkey = key;
|
||||||
|
}
|
||||||
|
return map[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
ColorMap.doc = {
|
||||||
|
sample: "Sample a given colormap at the given percentage (0 to 1).",
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.maps = ColorMap
|
||||||
|
Color.utils = esc
|
||||||
|
|
||||||
|
return Color
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
var input = use('input')
|
var input = use('input')
|
||||||
var util = use('util')
|
|
||||||
|
|
||||||
var downkeys = {};
|
var downkeys = {};
|
||||||
|
|
||||||
@@ -295,7 +294,7 @@ var Player = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
print_pawns() {
|
print_pawns() {
|
||||||
[...this.pawns].reverse().forEach(x => console.log(x))
|
[...this.pawns].reverse().forEach(x => log.console(x))
|
||||||
},
|
},
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
221
prosperon/draw2d.cm
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
var math = use('math')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
|
var draw = {}
|
||||||
|
draw[cell.DOC] = `
|
||||||
|
A collection of 2D drawing functions that create drawing command lists.
|
||||||
|
These are pure functions that return plain JavaScript objects representing
|
||||||
|
drawing operations. No rendering or actor communication happens here.
|
||||||
|
`
|
||||||
|
|
||||||
|
// Create a new command list
|
||||||
|
draw.list = function() {
|
||||||
|
var commands = []
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Add a command to this list
|
||||||
|
push: function(cmd) {
|
||||||
|
commands.push(cmd)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get all commands
|
||||||
|
get: function() {
|
||||||
|
return commands
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clear all commands
|
||||||
|
clear: function() {
|
||||||
|
commands = []
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get command count
|
||||||
|
length: function() {
|
||||||
|
return commands.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default command list for convenience
|
||||||
|
var current_list = draw.list()
|
||||||
|
|
||||||
|
// Set the current list
|
||||||
|
draw.set_list = function(list) {
|
||||||
|
current_list = list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current list
|
||||||
|
draw.get_list = function() {
|
||||||
|
return current_list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear current list
|
||||||
|
draw.clear = function() {
|
||||||
|
current_list.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get commands from current list
|
||||||
|
draw.get_commands = function() {
|
||||||
|
return current_list.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to add a command
|
||||||
|
function add_command(type, data) {
|
||||||
|
var cmd = {cmd: type}
|
||||||
|
Object.assign(cmd, data)
|
||||||
|
current_list.push(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default geometry definitions
|
||||||
|
var ellipse_def = {
|
||||||
|
start: 0,
|
||||||
|
end: 1,
|
||||||
|
mode: 'fill',
|
||||||
|
thickness: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
var line_def = {
|
||||||
|
thickness: 1,
|
||||||
|
cap:"butt",
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect_def = {
|
||||||
|
thickness:1,
|
||||||
|
radius: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var slice9_info = {
|
||||||
|
tile_top:true,
|
||||||
|
tile_bottom:true,
|
||||||
|
tile_left:true,
|
||||||
|
tile_right:true,
|
||||||
|
tile_center_x:true,
|
||||||
|
tile_center_right:true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var image_info = {
|
||||||
|
tile_x: false,
|
||||||
|
tile_y: false,
|
||||||
|
flip_x: false,
|
||||||
|
flip_y: false,
|
||||||
|
mode: 'linear'
|
||||||
|
}
|
||||||
|
|
||||||
|
var circle_def = {
|
||||||
|
inner_radius:1, // percentage: 1 means filled circle
|
||||||
|
start:0,
|
||||||
|
end: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawing functions
|
||||||
|
draw.point = function(pos, size, opt = {}, material) {
|
||||||
|
add_command("draw_point", {
|
||||||
|
pos: pos,
|
||||||
|
size: size,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
draw.point[cell.DOC] = `
|
||||||
|
:param pos: A 2D position ([x, y]) where the point should be drawn.
|
||||||
|
:param size: The size of the point.
|
||||||
|
:param opt: Optional geometry properties.
|
||||||
|
:param material: Material/styling information (color, shaders, etc.)
|
||||||
|
:return: None
|
||||||
|
`
|
||||||
|
|
||||||
|
draw.ellipse = function(pos, radii, def, material) {
|
||||||
|
var opt = def ? {...ellipse_def, ...def} : ellipse_def
|
||||||
|
if (opt.thickness <= 0) opt.thickness = Math.max(radii[0], radii[1])
|
||||||
|
|
||||||
|
add_command("draw_ellipse", {
|
||||||
|
pos: pos,
|
||||||
|
radii: radii,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.line = function(points, def, material)
|
||||||
|
{
|
||||||
|
var opt = def ? {...line_def, ...def} : line_def
|
||||||
|
|
||||||
|
add_command("draw_line", {
|
||||||
|
points: points,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.cross = function render_cross(pos, size, def, material) {
|
||||||
|
var a = [pos.add([0, size]), pos.add([0, -size])]
|
||||||
|
var b = [pos.add([size, 0]), pos.add([-size, 0])]
|
||||||
|
draw.line(a, def, material)
|
||||||
|
draw.line(b, def, material)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, def, material) {
|
||||||
|
var dir = math.norm(end.sub(start))
|
||||||
|
var wing1 = [math.rotate(dir, wingangle).scale(wingspan).add(end), end]
|
||||||
|
var wing2 = [math.rotate(dir, -wingangle).scale(wingspan).add(end), end]
|
||||||
|
draw.line([start, end], def, material)
|
||||||
|
draw.line(wing1, def, material)
|
||||||
|
draw.line(wing2, def, material)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.rectangle = function render_rectangle(rect, def, material) {
|
||||||
|
var opt = def ? {...rect_def, ...def} : rect_def
|
||||||
|
|
||||||
|
add_command("draw_rect", {
|
||||||
|
rect: rect,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info, material) {
|
||||||
|
if (!image) throw Error('Need an image to render.')
|
||||||
|
|
||||||
|
add_command("draw_slice9", {
|
||||||
|
image: image,
|
||||||
|
rect: rect,
|
||||||
|
slice: slice,
|
||||||
|
info: info,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.image = function image(image, rect, rotation = 0, anchor = [0,0], shear = [0,0], info = {}, material) {
|
||||||
|
if (!rect) throw Error('Need rectangle to render image.')
|
||||||
|
if (!image) throw Error('Need an image to render.')
|
||||||
|
|
||||||
|
if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.')
|
||||||
|
|
||||||
|
info = Object.assign({}, image_info, info);
|
||||||
|
|
||||||
|
add_command("draw_image", {
|
||||||
|
image: image,
|
||||||
|
rect: rect,
|
||||||
|
rotation: rotation,
|
||||||
|
anchor: anchor,
|
||||||
|
shear: shear,
|
||||||
|
info: info,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.circle = function render_circle(pos, radius, def, material) {
|
||||||
|
draw.ellipse(pos, [radius,radius], def, material)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.text = function text(text, pos, font = 'fonts/c64.ttf', size = 8, color = color.white, wrap = 0) {
|
||||||
|
add_command("draw_text", {
|
||||||
|
text,
|
||||||
|
pos,
|
||||||
|
font,
|
||||||
|
size,
|
||||||
|
wrap,
|
||||||
|
material: {color}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return draw
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
var Color = use('color')
|
var color = use('color')
|
||||||
var os = use('os')
|
|
||||||
var graphics = use('graphics')
|
var graphics = use('graphics')
|
||||||
var transform = use('transform')
|
var transform = use('transform')
|
||||||
|
|
||||||
@@ -15,7 +14,7 @@ ex.garbage = function()
|
|||||||
ex.update = function(dt)
|
ex.update = function(dt)
|
||||||
{
|
{
|
||||||
for (var e of ex.emitters)
|
for (var e of ex.emitters)
|
||||||
try { e.step(dt) } catch(e) { console.error(e) }
|
try { e.step(dt) } catch(e) { log.error(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
ex.step_hook = function(p)
|
ex.step_hook = function(p)
|
||||||
@@ -104,7 +103,7 @@ ex.scale = 1
|
|||||||
ex.grow_for = 0
|
ex.grow_for = 0
|
||||||
ex.spawn_timer = 0
|
ex.spawn_timer = 0
|
||||||
ex.pps = 0
|
ex.pps = 0
|
||||||
ex.color = Color.white
|
ex.color = color.white
|
||||||
|
|
||||||
ex.draw = function()
|
ex.draw = function()
|
||||||
{
|
{
|
||||||
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
@@ -5,6 +5,7 @@ var sprite = use('sprite')
|
|||||||
var geom = use('geometry')
|
var geom = use('geometry')
|
||||||
var input = use('controller')
|
var input = use('controller')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
var bunnyTex = graphics.texture("bunny")
|
var bunnyTex = graphics.texture("bunny")
|
||||||
|
|
||||||
@@ -65,5 +66,5 @@ this.hud = function() {
|
|||||||
draw.images(bunnyTex, bunnies)
|
draw.images(bunnyTex, bunnies)
|
||||||
|
|
||||||
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length
|
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length
|
||||||
draw.text(msg, {x:0, y:0, width:config.width, height:40}, undefined, 0, Color.white, 0)
|
draw.text(msg, {x:0, y:0, width:config.width, height:40}, undefined, 0, color.white, 0)
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 398 B After Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 379 B |
@@ -1,16 +1,9 @@
|
|||||||
/* main.js – runs the demo with your prototype-based grid */
|
/* main.js – runs the demo with your prototype-based grid */
|
||||||
|
|
||||||
var moth = use('moth', $_.delay)
|
|
||||||
var json = use('json')
|
var json = use('json')
|
||||||
|
var draw2d = use('prosperon/draw2d')
|
||||||
|
|
||||||
var res = 160
|
var blob = use('blob')
|
||||||
var internal_res = 480
|
|
||||||
|
|
||||||
moth.initialize({ width:res, height:res, resolution_x:internal_res, resolution_y:internal_res, mode:'letterbox' });
|
|
||||||
|
|
||||||
var os = use('os');
|
|
||||||
var draw2d = use('draw2d');
|
|
||||||
var gfx = use('graphics');
|
|
||||||
|
|
||||||
/*──── import our pieces + systems ───────────────────────────────────*/
|
/*──── import our pieces + systems ───────────────────────────────────*/
|
||||||
var Grid = use('grid'); // your new ctor
|
var Grid = use('grid'); // your new ctor
|
||||||
@@ -55,7 +48,7 @@ function updateTitle() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
prosperon.window.title = title
|
log.console(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize title
|
// Initialize title
|
||||||
@@ -70,7 +63,7 @@ var opponentMousePos = null;
|
|||||||
var opponentHoldingPiece = false;
|
var opponentHoldingPiece = false;
|
||||||
var opponentSelectPos = null;
|
var opponentSelectPos = null;
|
||||||
|
|
||||||
prosperon.on('mouse_button_down', function(e) {
|
function handleMouseButtonDown(e) {
|
||||||
if (e.which !== 0) return;
|
if (e.which !== 0) return;
|
||||||
|
|
||||||
// Don't allow piece selection unless we have an opponent
|
// Don't allow piece selection unless we have an opponent
|
||||||
@@ -96,9 +89,9 @@ prosperon.on('mouse_button_down', function(e) {
|
|||||||
} else {
|
} else {
|
||||||
selectPos = null;
|
selectPos = null;
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
prosperon.on('mouse_button_up', function(e) {
|
function handleMouseButtonUp(e) {
|
||||||
if (e.which !== 0 || !holdingPiece || !selectPos) return;
|
if (e.which !== 0 || !holdingPiece || !selectPos) return;
|
||||||
|
|
||||||
// Don't allow moves unless we have an opponent and it's our turn
|
// Don't allow moves unless we have an opponent and it's our turn
|
||||||
@@ -117,16 +110,16 @@ prosperon.on('mouse_button_up', function(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
||||||
console.log("Made move from", selectPos, "to", c);
|
log.console("Made move from", selectPos, "to", c);
|
||||||
// Send move to opponent
|
// Send move to opponent
|
||||||
console.log("Sending move to opponent:", opponent);
|
log.console("Sending move to opponent:", opponent);
|
||||||
send(opponent, {
|
send(opponent, {
|
||||||
type: 'move',
|
type: 'move',
|
||||||
from: selectPos,
|
from: selectPos,
|
||||||
to: c
|
to: c
|
||||||
});
|
});
|
||||||
isMyTurn = false; // It's now opponent's turn
|
isMyTurn = false; // It's now opponent's turn
|
||||||
console.log("Move sent, now opponent's turn");
|
log.console("Move sent, now opponent's turn");
|
||||||
selectPos = null;
|
selectPos = null;
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
@@ -139,9 +132,9 @@ prosperon.on('mouse_button_up', function(e) {
|
|||||||
type: 'piece_drop'
|
type: 'piece_drop'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
prosperon.on('mouse_motion', function(e) {
|
function handleMouseMotion(e) {
|
||||||
var mx = e.pos.x;
|
var mx = e.pos.x;
|
||||||
var my = e.pos.y;
|
var my = e.pos.y;
|
||||||
|
|
||||||
@@ -162,7 +155,18 @@ prosperon.on('mouse_motion', function(e) {
|
|||||||
selectPos: selectPos
|
selectPos: selectPos
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
function handleKeyDown(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*──── drawing helpers ───────────────────────────────────────────────*/
|
/*──── drawing helpers ───────────────────────────────────────────────*/
|
||||||
/* ── constants ─────────────────────────────────────────────────── */
|
/* ── constants ─────────────────────────────────────────────────── */
|
||||||
@@ -193,7 +197,8 @@ function drawBoard() {
|
|||||||
|
|
||||||
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: color }
|
{ thickness: 0 },
|
||||||
|
{ color: color }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,7 +240,7 @@ function drawPieces() {
|
|||||||
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Draw the held piece at the mouse position if we're holding one
|
// Draw the held piece at the mouse position if we're holding one
|
||||||
@@ -245,7 +250,7 @@ function drawPieces() {
|
|||||||
var r = { x: hoverPos[0]*S, y: hoverPos[1]*S,
|
var r = { x: hoverPos[0]*S, y: hoverPos[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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,29 +262,23 @@ function drawPieces() {
|
|||||||
width:S, height:S };
|
width:S, height:S };
|
||||||
|
|
||||||
// Draw with slight transparency to show it's the opponent's piece
|
// Draw with slight transparency to show it's the opponent's piece
|
||||||
draw2d.image(opponentPiece.sprite, r, 0, [0,0], [0,0], {mode:"nearest", color: [1, 1, 1, 0.7]});
|
draw2d.image(opponentPiece.sprite, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var graphics = use('graphics')
|
function update(dt)
|
||||||
|
{
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
prosperon.on('draw', function() {
|
function draw()
|
||||||
|
{
|
||||||
|
draw2d.clear()
|
||||||
drawBoard()
|
drawBoard()
|
||||||
drawPieces()
|
drawPieces()
|
||||||
draw2d.text("HELL", [100,100])
|
return draw2d.get_commands()
|
||||||
})
|
}
|
||||||
|
|
||||||
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() {
|
function startServer() {
|
||||||
gameState = 'server_waiting';
|
gameState = 'server_waiting';
|
||||||
@@ -289,11 +288,11 @@ function startServer() {
|
|||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
$_.portal(e => {
|
$_.portal(e => {
|
||||||
console.log("Portal received contact message");
|
log.console("Portal received contact message");
|
||||||
// Reply with this actor to establish connection
|
// Reply with this actor to establish connection
|
||||||
console.log (json.encode($_))
|
log.console (json.encode($_))
|
||||||
send(e, $_);
|
send(e, $_);
|
||||||
console.log("Portal replied with server actor");
|
log.console("Portal replied with server actor");
|
||||||
}, 5678);
|
}, 5678);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,10 +301,10 @@ function joinServer() {
|
|||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
function contact_fn(actor, reason) {
|
function contact_fn(actor, reason) {
|
||||||
console.log("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
log.console("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
||||||
if (actor) {
|
if (actor) {
|
||||||
opponent = actor;
|
opponent = actor;
|
||||||
console.log("Connection established with server, sending join request");
|
log.console("Connection established with server, sending join request");
|
||||||
|
|
||||||
// Send a greet message with our actor object
|
// Send a greet message with our actor object
|
||||||
send(opponent, {
|
send(opponent, {
|
||||||
@@ -313,7 +312,7 @@ function joinServer() {
|
|||||||
client_actor: $_
|
client_actor: $_
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log(`Failed to connect: ${json.encode(reason)}`);
|
log.console(`Failed to connect: ${json.encode(reason)}`);
|
||||||
gameState = 'waiting';
|
gameState = 'waiting';
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
@@ -325,51 +324,38 @@ function joinServer() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var os = use('os')
|
|
||||||
var actor = use('actor')
|
|
||||||
for (var i in actor) console.log(i)
|
|
||||||
|
|
||||||
// Set up IO actor subscription
|
|
||||||
var ioguy = {
|
|
||||||
__ACTORDATA__: {
|
|
||||||
id: actor.ioactor()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
send(ioguy, {
|
|
||||||
type: "subscribe",
|
|
||||||
actor: $_
|
|
||||||
});
|
|
||||||
|
|
||||||
$_.receiver(e => {
|
$_.receiver(e => {
|
||||||
if (e.type === 'game_start' || e.type === 'move' || e.type === 'greet')
|
if (e.kind == 'update')
|
||||||
console.log("Receiver got message:", e.type, e);
|
send(e, update(e.dt))
|
||||||
if (e.type === 'quit') os.exit()
|
else if (e.kind == 'draw')
|
||||||
|
send(e, draw())
|
||||||
|
else if (e.type === 'game_start' || e.type === 'move' || e.type === 'greet')
|
||||||
|
log.console("Receiver got message:", e.type, e);
|
||||||
|
|
||||||
if (e.type === 'greet') {
|
if (e.type === 'greet') {
|
||||||
console.log("Server received greet from client");
|
log.console("Server received greet from client");
|
||||||
// Store the client's actor object for ongoing communication
|
// Store the client's actor object for ongoing communication
|
||||||
opponent = e.client_actor;
|
opponent = e.client_actor;
|
||||||
console.log("Stored client actor:", json.encode(opponent));
|
log.console("Stored client actor:", json.encode(opponent));
|
||||||
gameState = 'connected';
|
gameState = 'connected';
|
||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
// Send game_start to the client
|
// Send game_start to the client
|
||||||
console.log("Sending game_start to client");
|
log.console("Sending game_start to client");
|
||||||
send(opponent, {
|
send(opponent, {
|
||||||
type: 'game_start',
|
type: 'game_start',
|
||||||
your_color: 'black'
|
your_color: 'black'
|
||||||
});
|
});
|
||||||
console.log("game_start message sent to client");
|
log.console("game_start message sent to client");
|
||||||
}
|
}
|
||||||
else if (e.type === 'game_start') {
|
else if (e.type === 'game_start') {
|
||||||
console.log("Game starting, I am:", e.your_color);
|
log.console("Game starting, I am:", e.your_color);
|
||||||
myColor = e.your_color;
|
myColor = e.your_color;
|
||||||
isMyTurn = (myColor === 'white');
|
isMyTurn = (myColor === 'white');
|
||||||
gameState = 'connected';
|
gameState = 'connected';
|
||||||
updateTitle();
|
updateTitle();
|
||||||
} else if (e.type === 'move') {
|
} else if (e.type === 'move') {
|
||||||
console.log("Received move from opponent:", e.from, "to", e.to);
|
log.console("Received move from opponent:", e.from, "to", e.to);
|
||||||
// Apply opponent's move
|
// Apply opponent's move
|
||||||
var fromCell = grid.at(e.from);
|
var fromCell = grid.at(e.from);
|
||||||
if (fromCell.length) {
|
if (fromCell.length) {
|
||||||
@@ -377,12 +363,12 @@ $_.receiver(e => {
|
|||||||
if (mover.tryMove(piece, e.to)) {
|
if (mover.tryMove(piece, e.to)) {
|
||||||
isMyTurn = true; // It's now our turn
|
isMyTurn = true; // It's now our turn
|
||||||
updateTitle();
|
updateTitle();
|
||||||
console.log("Applied opponent move, now my turn");
|
log.console("Applied opponent move, now my turn");
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to apply opponent move");
|
log.console("Failed to apply opponent move");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("No piece found at from position");
|
log.console("No piece found at from position");
|
||||||
}
|
}
|
||||||
} else if (e.type === 'mouse_move') {
|
} else if (e.type === 'mouse_move') {
|
||||||
// Update opponent's mouse position
|
// Update opponent's mouse position
|
||||||
@@ -397,7 +383,13 @@ $_.receiver(e => {
|
|||||||
// Opponent dropped their piece
|
// Opponent dropped their piece
|
||||||
opponentHoldingPiece = false;
|
opponentHoldingPiece = false;
|
||||||
opponentSelectPos = null;
|
opponentSelectPos = null;
|
||||||
|
} else if (e.type === 'mouse_button_down') {
|
||||||
|
handleMouseButtonDown(e)
|
||||||
|
} else if (e.type === 'mouse_button_up') {
|
||||||
|
handleMouseButtonUp(e)
|
||||||
|
} else if (e.type === 'mouse_motion') {
|
||||||
|
handleMouseMotion(e)
|
||||||
|
} else if (e.type === 'key_down') {
|
||||||
|
handleKeyDown(e)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
prosperon.dispatch(e.type, e)
|
|
||||||
})
|
|
||||||
|
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
|
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
@@ -2,6 +2,7 @@
|
|||||||
var draw = use('draw2d')
|
var draw = use('draw2d')
|
||||||
var input = use('controller')
|
var input = use('controller')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
prosperon.camera.transform.pos = [0,0]
|
prosperon.camera.transform.pos = [0,0]
|
||||||
|
|
||||||
@@ -73,13 +74,13 @@ this.hud = function() {
|
|||||||
draw.rectangle({x:0, y:0, width:config.width, height:config.height}, [0,0,0,1])
|
draw.rectangle({x:0, y:0, width:config.width, height:config.height}, [0,0,0,1])
|
||||||
|
|
||||||
// Draw paddles
|
// Draw paddles
|
||||||
draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white)
|
draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
|
||||||
draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white)
|
draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
|
||||||
|
|
||||||
// Draw ball
|
// Draw ball
|
||||||
draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, Color.white)
|
draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, color.white)
|
||||||
|
|
||||||
// Simple score display
|
// Simple score display
|
||||||
var msg = score1 + " " + score2
|
var msg = score1 + " " + score2
|
||||||
draw.text(msg, {x:0, y:10, width:config.width, height:40}, undefined, 0, Color.white, 0)
|
draw.text(msg, {x:0, y:10, width:config.width, height:40}, undefined, 0, color.white, 0)
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ var render = use('render')
|
|||||||
var graphics = use('graphics')
|
var graphics = use('graphics')
|
||||||
var input = use('input')
|
var input = use('input')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
prosperon.camera.transform.pos = [0,0]
|
prosperon.camera.transform.pos = [0,0]
|
||||||
|
|
||||||
@@ -83,15 +84,15 @@ this.hud = function() {
|
|||||||
// Draw snake
|
// Draw snake
|
||||||
for (var i=0; i<snake.length; i++) {
|
for (var i=0; i<snake.length; i++) {
|
||||||
var s = snake[i]
|
var s = snake[i]
|
||||||
draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, Color.green)
|
draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, color.green)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw apple
|
// Draw apple
|
||||||
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, Color.red)
|
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, color.red)
|
||||||
|
|
||||||
if (gameState === "gameover") {
|
if (gameState === "gameover") {
|
||||||
var msg = "GAME OVER! Press SPACE to restart."
|
var msg = "GAME OVER! Press SPACE to restart."
|
||||||
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, undefined, 0, Color.white)
|
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, undefined, 0, color.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,23 +23,23 @@ var stats_loaded = false;
|
|||||||
// Initialize Steam
|
// Initialize Steam
|
||||||
function init_steam() {
|
function init_steam() {
|
||||||
if (!steam) {
|
if (!steam) {
|
||||||
console.log("Steam module not available");
|
log.console("Steam module not available");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Initializing Steam...");
|
log.console("Initializing Steam...");
|
||||||
steam_available = steam.steam_init();
|
steam_available = steam.steam_init();
|
||||||
|
|
||||||
if (steam_available) {
|
if (steam_available) {
|
||||||
console.log("Steam initialized successfully");
|
log.console("Steam initialized successfully");
|
||||||
|
|
||||||
// Request current stats/achievements
|
// Request current stats/achievements
|
||||||
if (steam.stats.stats_request()) {
|
if (steam.stats.stats_request()) {
|
||||||
console.log("Stats requested");
|
log.console("Stats requested");
|
||||||
stats_loaded = true;
|
stats_loaded = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to initialize Steam");
|
log.console("Failed to initialize Steam");
|
||||||
}
|
}
|
||||||
|
|
||||||
return steam_available;
|
return steam_available;
|
||||||
@@ -59,13 +59,13 @@ function unlock_achievement(achievement_name) {
|
|||||||
// Check if already unlocked
|
// Check if already unlocked
|
||||||
var unlocked = steam.achievement.achievement_get(achievement_name);
|
var unlocked = steam.achievement.achievement_get(achievement_name);
|
||||||
if (unlocked) {
|
if (unlocked) {
|
||||||
console.log("Achievement already unlocked:", achievement_name);
|
log.console("Achievement already unlocked:", achievement_name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock it
|
// Unlock it
|
||||||
if (steam.achievement.achievement_set(achievement_name)) {
|
if (steam.achievement.achievement_set(achievement_name)) {
|
||||||
console.log("Achievement unlocked:", achievement_name);
|
log.console("Achievement unlocked:", achievement_name);
|
||||||
|
|
||||||
// Store stats to make it permanent
|
// Store stats to make it permanent
|
||||||
steam.stats.stats_store();
|
steam.stats.stats_store();
|
||||||
@@ -87,7 +87,7 @@ function update_stat(stat_name, value, is_float) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log("Stat updated:", stat_name, "=", value);
|
log.console("Stat updated:", stat_name, "=", value);
|
||||||
steam.stats.stats_store();
|
steam.stats.stats_store();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ function start_game() {
|
|||||||
total_score = get_stat(STATS.TOTAL_SCORE, false);
|
total_score = get_stat(STATS.TOTAL_SCORE, false);
|
||||||
current_score = 0;
|
current_score = 0;
|
||||||
|
|
||||||
console.log("Starting game #" + (games_played + 1));
|
log.console("Starting game #" + (games_played + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function end_game(score) {
|
function end_game(score) {
|
||||||
@@ -167,7 +167,7 @@ function load_from_cloud() {
|
|||||||
function cleanup_steam() {
|
function cleanup_steam() {
|
||||||
if (steam_available) {
|
if (steam_available) {
|
||||||
steam.steam_shutdown();
|
steam.steam_shutdown();
|
||||||
console.log("Steam shut down");
|
log.console("Steam shut down");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
var draw = use('draw2d')
|
var draw = use('draw2d')
|
||||||
var input = use('input')
|
var input = use('input')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
prosperon.camera.transform.pos = [0,0]
|
prosperon.camera.transform.pos = [0,0]
|
||||||
|
|
||||||
@@ -248,7 +249,7 @@ this.hud = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next piece window
|
// Next piece window
|
||||||
draw.text("Next", {x:70, y:5, width:50, height:10}, undefined, 0, Color.white)
|
draw.text("Next", {x:70, y:5, width:50, height:10}, undefined, 0, color.white)
|
||||||
if (nextPiece) {
|
if (nextPiece) {
|
||||||
for (var i=0; i<nextPiece.blocks.length; i++) {
|
for (var i=0; i<nextPiece.blocks.length; i++) {
|
||||||
var nx = nextPiece.blocks[i][0]
|
var nx = nextPiece.blocks[i][0]
|
||||||
@@ -261,10 +262,10 @@ this.hud = function() {
|
|||||||
|
|
||||||
// Score & Level
|
// Score & Level
|
||||||
var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level
|
var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level
|
||||||
draw.text(info, {x:70, y:30, width:90, height:50}, undefined, 0, Color.white)
|
draw.text(info, {x:70, y:30, width:90, height:50}, undefined, 0, color.white)
|
||||||
|
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, undefined, 0, Color.red)
|
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, undefined, 0, color.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
var geometry = this
|
var geometry = this
|
||||||
geometry[prosperon.DOC] = `
|
geometry[cell.DOC] = `
|
||||||
A collection of geometry-related functions for circles, spheres, boxes, polygons,
|
A collection of geometry-related functions for circles, spheres, boxes, polygons,
|
||||||
and rectangle utilities. Some functionality is implemented in C and exposed here.
|
and rectangle utilities. Some functionality is implemented in C and exposed here.
|
||||||
`
|
`
|
||||||
@@ -7,7 +7,7 @@ and rectangle utilities. Some functionality is implemented in C and exposed here
|
|||||||
var math = use('math')
|
var math = use('math')
|
||||||
|
|
||||||
geometry.box = {}
|
geometry.box = {}
|
||||||
geometry.box[prosperon.DOC] = `
|
geometry.box[cell.DOC] = `
|
||||||
An object for box-related operations. Overridden later by a function definition, so
|
An object for box-related operations. Overridden later by a function definition, so
|
||||||
its direct usage is overshadowed. Contains:
|
its direct usage is overshadowed. Contains:
|
||||||
- points(ll, ur): Return an array of four 2D points for a box from ll (lower-left) to ur (upper-right).
|
- points(ll, ur): Return an array of four 2D points for a box from ll (lower-left) to ur (upper-right).
|
||||||
@@ -16,7 +16,7 @@ its direct usage is overshadowed. Contains:
|
|||||||
geometry.box.points = function (ll, ur) {
|
geometry.box.points = function (ll, ur) {
|
||||||
return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])]
|
return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])]
|
||||||
}
|
}
|
||||||
geometry.box.points[prosperon.DOC] = `
|
geometry.box.points[cell.DOC] = `
|
||||||
:param ll: Lower-left coordinate as a 2D vector (x,y).
|
:param ll: Lower-left coordinate as a 2D vector (x,y).
|
||||||
:param ur: Upper-right coordinate as a 2D vector (x,y).
|
:param ur: Upper-right coordinate as a 2D vector (x,y).
|
||||||
:return: An array of four points forming the corners of the box in order [ll, lower-right, ur, upper-left].
|
:return: An array of four points forming the corners of the box in order [ll, lower-right, ur, upper-left].
|
||||||
@@ -24,14 +24,14 @@ Compute the four corners of a box given lower-left and upper-right corners.
|
|||||||
`
|
`
|
||||||
|
|
||||||
geometry.sphere = {}
|
geometry.sphere = {}
|
||||||
geometry.sphere[prosperon.DOC] = `
|
geometry.sphere[cell.DOC] = `
|
||||||
Sphere-related geometry functions:
|
Sphere-related geometry functions:
|
||||||
- volume(r): Return the volume of a sphere with radius r.
|
- volume(r): Return the volume of a sphere with radius r.
|
||||||
- random(r, theta, phi): Return a random point on or inside a sphere.
|
- random(r, theta, phi): Return a random point on or inside a sphere.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.circle = {}
|
geometry.circle = {}
|
||||||
geometry.circle[prosperon.DOC] = `
|
geometry.circle[cell.DOC] = `
|
||||||
Circle-related geometry functions:
|
Circle-related geometry functions:
|
||||||
- area(r): Return the area of a circle with radius r.
|
- area(r): Return the area of a circle with radius r.
|
||||||
- random(r, theta): Return a random 2D point on a circle; uses sphere.random internally and extracts x,z.
|
- random(r, theta): Return a random 2D point on a circle; uses sphere.random internally and extracts x,z.
|
||||||
@@ -40,7 +40,7 @@ Circle-related geometry functions:
|
|||||||
geometry.sphere.volume = function (r) {
|
geometry.sphere.volume = function (r) {
|
||||||
return (Math.pi * r * r * r * 4) / 3
|
return (Math.pi * r * r * r * 4) / 3
|
||||||
}
|
}
|
||||||
geometry.sphere.volume[prosperon.DOC] = `
|
geometry.sphere.volume[cell.DOC] = `
|
||||||
:param r: The sphere radius.
|
:param r: The sphere radius.
|
||||||
:return: The volume of the sphere, calculated as (4/3) * pi * r^3.
|
:return: The volume of the sphere, calculated as (4/3) * pi * r^3.
|
||||||
`
|
`
|
||||||
@@ -55,7 +55,7 @@ geometry.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) {
|
|||||||
var pa = Math.turn2rad(Math.random_range(phi[0], phi[1]))
|
var pa = Math.turn2rad(Math.random_range(phi[0], phi[1]))
|
||||||
return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)]
|
return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)]
|
||||||
}
|
}
|
||||||
geometry.sphere.random[prosperon.DOC] = `
|
geometry.sphere.random[cell.DOC] = `
|
||||||
:param r: A single number (radius) or a 2-element array [minRadius, maxRadius].
|
:param r: A single number (radius) or a 2-element array [minRadius, maxRadius].
|
||||||
:param theta: A single number or 2-element array defining the range in turns for the theta angle, default [0,1].
|
:param theta: A single number or 2-element array defining the range in turns for the theta angle, default [0,1].
|
||||||
:param phi: A single number or 2-element array defining the range in turns for the phi angle, default [-0.5,0.5].
|
:param phi: A single number or 2-element array defining the range in turns for the phi angle, default [-0.5,0.5].
|
||||||
@@ -66,7 +66,7 @@ Generate a random point inside a sphere of variable radius, distributing angles
|
|||||||
geometry.circle.area = function (r) {
|
geometry.circle.area = function (r) {
|
||||||
return Math.pi * r * r
|
return Math.pi * r * r
|
||||||
}
|
}
|
||||||
geometry.circle.area[prosperon.DOC] = `
|
geometry.circle.area[cell.DOC] = `
|
||||||
:param r: Radius of the circle.
|
:param r: Radius of the circle.
|
||||||
:return: The area, pi * r^2.
|
:return: The area, pi * r^2.
|
||||||
`
|
`
|
||||||
@@ -74,7 +74,7 @@ geometry.circle.area[prosperon.DOC] = `
|
|||||||
geometry.circle.random = function (r, theta) {
|
geometry.circle.random = function (r, theta) {
|
||||||
return geometry.sphere.random(r, theta).xz
|
return geometry.sphere.random(r, theta).xz
|
||||||
}
|
}
|
||||||
geometry.circle.random[prosperon.DOC] = `
|
geometry.circle.random[cell.DOC] = `
|
||||||
:param r: A radius or [minRadius, maxRadius].
|
:param r: A radius or [minRadius, maxRadius].
|
||||||
:param theta: Angle range in turns (single number or [min,max]).
|
:param theta: Angle range in turns (single number or [min,max]).
|
||||||
:return: A 2D point (x,z) in the circle, using the sphere random generator and ignoring y.
|
:return: A 2D point (x,z) in the circle, using the sphere random generator and ignoring y.
|
||||||
@@ -91,7 +91,7 @@ geometry.box = function (w, h) {
|
|||||||
]
|
]
|
||||||
return points
|
return points
|
||||||
}
|
}
|
||||||
geometry.box[prosperon.DOC] = `
|
geometry.box[cell.DOC] = `
|
||||||
:param w: The width of the box.
|
:param w: The width of the box.
|
||||||
:param h: The height of the box.
|
:param h: The height of the box.
|
||||||
:return: An array of four 2D points representing the corners of a rectangle centered at [0,0].
|
:return: An array of four 2D points representing the corners of a rectangle centered at [0,0].
|
||||||
@@ -101,7 +101,7 @@ Construct a box centered at the origin with the given width and height. This ove
|
|||||||
geometry.ngon = function (radius, n) {
|
geometry.ngon = function (radius, n) {
|
||||||
return geometry.arc(radius, 360, n)
|
return geometry.arc(radius, 360, n)
|
||||||
}
|
}
|
||||||
geometry.ngon[prosperon.DOC] = `
|
geometry.ngon[cell.DOC] = `
|
||||||
:param radius: The radius of the n-gon from center to each vertex.
|
:param radius: The radius of the n-gon from center to each vertex.
|
||||||
:param n: Number of sides/vertices.
|
:param n: Number of sides/vertices.
|
||||||
:return: An array of 2D points forming a regular n-gon.
|
:return: An array of 2D points forming a regular n-gon.
|
||||||
@@ -118,7 +118,7 @@ geometry.arc = function (radius, angle, n, start = 0) {
|
|||||||
for (var i = 0; i < n; i++) points.push(math.rotate([radius, 0], start + arclen * i))
|
for (var i = 0; i < n; i++) points.push(math.rotate([radius, 0], start + arclen * i))
|
||||||
return points
|
return points
|
||||||
}
|
}
|
||||||
geometry.arc[prosperon.DOC] = `
|
geometry.arc[cell.DOC] = `
|
||||||
:param radius: The distance from center to the arc points.
|
:param radius: The distance from center to the arc points.
|
||||||
:param angle: The total angle (in degrees) over which points are generated, capped at 360.
|
:param angle: The total angle (in degrees) over which points are generated, capped at 360.
|
||||||
:param n: Number of segments (if <=1, empty array is returned).
|
:param n: Number of segments (if <=1, empty array is returned).
|
||||||
@@ -131,7 +131,7 @@ geometry.circle.points = function (radius, n) {
|
|||||||
if (n <= 1) return []
|
if (n <= 1) return []
|
||||||
return geometry.arc(radius, 360, n)
|
return geometry.arc(radius, 360, n)
|
||||||
}
|
}
|
||||||
geometry.circle.points[prosperon.DOC] = `
|
geometry.circle.points[cell.DOC] = `
|
||||||
:param radius: The circle's radius.
|
:param radius: The circle's radius.
|
||||||
:param n: Number of points around the circle.
|
:param n: Number of points around the circle.
|
||||||
:return: An array of 2D points equally spaced around a full 360-degree circle.
|
:return: An array of 2D points equally spaced around a full 360-degree circle.
|
||||||
@@ -141,7 +141,7 @@ Shortcut for geometry.arc(radius, 360, n).
|
|||||||
geometry.corners2points = function (ll, ur) {
|
geometry.corners2points = function (ll, ur) {
|
||||||
return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])]
|
return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])]
|
||||||
}
|
}
|
||||||
geometry.corners2points[prosperon.DOC] = `
|
geometry.corners2points[cell.DOC] = `
|
||||||
:param ll: Lower-left 2D coordinate.
|
:param ll: Lower-left 2D coordinate.
|
||||||
:param ur: Upper-right 2D coordinate (relative offset in x,y).
|
:param ur: Upper-right 2D coordinate (relative offset in x,y).
|
||||||
:return: A four-point array of corners [ll, lower-right, upper-right, upper-left].
|
:return: A four-point array of corners [ll, lower-right, upper-right, upper-left].
|
||||||
@@ -158,7 +158,7 @@ geometry.sortpointsccw = function (points) {
|
|||||||
})
|
})
|
||||||
return ccw.map(function (x) { return x.add(cm) })
|
return ccw.map(function (x) { return x.add(cm) })
|
||||||
}
|
}
|
||||||
geometry.sortpointsccw[prosperon.DOC] = `
|
geometry.sortpointsccw[cell.DOC] = `
|
||||||
:param points: An array of 2D points.
|
:param points: An array of 2D points.
|
||||||
:return: A new array of the same points, sorted counterclockwise around their centroid.
|
:return: A new array of the same points, sorted counterclockwise around their centroid.
|
||||||
Sort an array of points in CCW order based on their angles from the centroid.
|
Sort an array of points in CCW order based on their angles from the centroid.
|
||||||
@@ -185,61 +185,61 @@ geometry.points2cm = function(points) {
|
|||||||
})
|
})
|
||||||
return [x / n, y / n]
|
return [x / n, y / n]
|
||||||
}
|
}
|
||||||
geometry.points2cm[prosperon.DOC] = `
|
geometry.points2cm[cell.DOC] = `
|
||||||
:param points: An array of 2D points.
|
:param points: An array of 2D points.
|
||||||
:return: The centroid (average x,y) of the given points.
|
:return: The centroid (average x,y) of the given points.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_intersection[prosperon.DOC] = `
|
geometry.rect_intersection[cell.DOC] = `
|
||||||
:param a: The first rectangle as {x, y, w, h}.
|
:param a: The first rectangle as {x, y, w, h}.
|
||||||
:param b: The second rectangle as {x, y, w, h}.
|
:param b: The second rectangle as {x, y, w, h}.
|
||||||
:return: A rectangle that is the intersection of the two. May have zero width/height if no overlap.
|
:return: A rectangle that is the intersection of the two. May have zero width/height if no overlap.
|
||||||
Return the intersection of two rectangles. The result may be empty if no intersection.
|
Return the intersection of two rectangles. The result may be empty if no intersection.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_intersects[prosperon.DOC] = `
|
geometry.rect_intersects[cell.DOC] = `
|
||||||
:param a: Rectangle {x,y,w,h}.
|
:param a: Rectangle {x,y,w,h}.
|
||||||
:param b: Rectangle {x,y,w,h}.
|
:param b: Rectangle {x,y,w,h}.
|
||||||
:return: A boolean indicating whether the two rectangles overlap.
|
:return: A boolean indicating whether the two rectangles overlap.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_expand[prosperon.DOC] = `
|
geometry.rect_expand[cell.DOC] = `
|
||||||
:param a: Rectangle {x,y,w,h}.
|
:param a: Rectangle {x,y,w,h}.
|
||||||
:param b: Rectangle {x,y,w,h}.
|
:param b: Rectangle {x,y,w,h}.
|
||||||
:return: A new rectangle that covers the bounds of both input rectangles.
|
:return: A new rectangle that covers the bounds of both input rectangles.
|
||||||
Merge or combine two rectangles, returning their bounding rectangle.
|
Merge or combine two rectangles, returning their bounding rectangle.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_inside[prosperon.DOC] = `
|
geometry.rect_inside[cell.DOC] = `
|
||||||
:param inner: A rectangle to test.
|
:param inner: A rectangle to test.
|
||||||
:param outer: A rectangle that may contain 'inner'.
|
:param outer: A rectangle that may contain 'inner'.
|
||||||
:return: True if 'inner' is completely inside 'outer', otherwise false.
|
:return: True if 'inner' is completely inside 'outer', otherwise false.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_random[prosperon.DOC] = `
|
geometry.rect_random[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:return: A random point within the rectangle (uniform distribution).
|
:return: A random point within the rectangle (uniform distribution).
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.cwh2rect[prosperon.DOC] = `
|
geometry.cwh2rect[cell.DOC] = `
|
||||||
:param center: A 2D point [cx, cy].
|
:param center: A 2D point [cx, cy].
|
||||||
:param wh: A 2D size [width, height].
|
:param wh: A 2D size [width, height].
|
||||||
:return: A rectangle {x, y, w, h} with x,y set to center and w,h set to the given size.
|
:return: A rectangle {x, y, w, h} with x,y set to center and w,h set to the given size.
|
||||||
Helper: convert a center point and width/height vector to a rect object.
|
Helper: convert a center point and width/height vector to a rect object.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_point_inside[prosperon.DOC] = `
|
geometry.rect_point_inside[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:param point: A 2D point [px, py].
|
:param point: A 2D point [px, py].
|
||||||
:return: True if the point lies inside the rectangle, otherwise false.
|
:return: True if the point lies inside the rectangle, otherwise false.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_pos[prosperon.DOC] = `
|
geometry.rect_pos[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:return: A 2D vector [x,y] giving the rectangle's position.
|
:return: A 2D vector [x,y] giving the rectangle's position.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_move[prosperon.DOC] = `
|
geometry.rect_move[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:param offset: A 2D vector to add to the rectangle's position.
|
:param offset: A 2D vector to add to the rectangle's position.
|
||||||
:return: A new rectangle with updated x,y offset.
|
:return: A new rectangle with updated x,y offset.
|
||||||
@@ -1,59 +1,103 @@
|
|||||||
var graphics = this
|
var graphics = this
|
||||||
|
|
||||||
graphics[prosperon.DOC] = `
|
graphics[cell.DOC] = `
|
||||||
Provides functionality for loading and managing images, fonts, textures, and sprite meshes.
|
Provides functionality for loading and managing images, fonts, textures, and sprite meshes.
|
||||||
Includes both JavaScript and C-implemented routines for creating geometry buffers, performing
|
Includes both JavaScript and C-implemented routines for creating geometry buffers, performing
|
||||||
rectangle packing, etc.
|
rectangle packing, etc.
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var renderer_actor = arg[0]
|
||||||
|
|
||||||
var io = use('io')
|
var io = use('io')
|
||||||
var os = use('os')
|
var time = use('time')
|
||||||
var res = use('resources')
|
var res = use('resources')
|
||||||
var render = use('render')
|
var json = use('json')
|
||||||
|
|
||||||
var GPU = Symbol()
|
var GPU = Symbol()
|
||||||
var CPU = Symbol()
|
var CPU = Symbol()
|
||||||
var LASTUSE = Symbol()
|
var LASTUSE = Symbol()
|
||||||
|
var LOADING = Symbol()
|
||||||
|
|
||||||
var cache = new Map()
|
var cache = new Map()
|
||||||
|
|
||||||
// When creating an image, do an Object.create(graphics.Image)
|
// Image constructor function
|
||||||
graphics.Image = {
|
graphics.Image = function(surfaceData) {
|
||||||
get gpu() {
|
// Initialize private properties
|
||||||
this[LASTUSE] = os.now();
|
this[CPU] = surfaceData || undefined;
|
||||||
if (!this[GPU]) {
|
this[GPU] = undefined;
|
||||||
this[GPU] = render.load_texture(this[CPU]);
|
this[LOADING] = false;
|
||||||
decorate_rect_px(this);
|
this[LASTUSE] = time.number();
|
||||||
}
|
this.rect = {x:0, y:0, width:1, height:1};
|
||||||
|
}
|
||||||
return this[GPU]
|
|
||||||
},
|
|
||||||
|
|
||||||
get texture() { return this.gpu },
|
|
||||||
|
|
||||||
get cpu() {
|
// Define getters and methods on the prototype
|
||||||
this[LASTUSE] = os.now();
|
Object.defineProperties(graphics.Image.prototype, {
|
||||||
if (!this[CPU]) this[CPU] = render.read_texture(this[GPU])
|
gpu: {
|
||||||
return this[CPU]
|
get: function() {
|
||||||
|
this[LASTUSE] = time.number();
|
||||||
|
if (!this[GPU] && !this[LOADING]) {
|
||||||
|
this[LOADING] = true;
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Send message to load texture
|
||||||
|
send(renderer_actor, {
|
||||||
|
kind: "renderer",
|
||||||
|
op: "loadTexture",
|
||||||
|
data: this[CPU]
|
||||||
|
}, function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
log.error("Failed to load texture:")
|
||||||
|
log.error(response.error)
|
||||||
|
self[LOADING] = false;
|
||||||
|
} else {
|
||||||
|
self[GPU] = response;
|
||||||
|
decorate_rect_px(self);
|
||||||
|
self[LOADING] = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this[GPU]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get surface() { return this.cpu },
|
texture: {
|
||||||
|
get: function() { return this.gpu }
|
||||||
get width() {
|
},
|
||||||
return this[GPU].width
|
|
||||||
|
cpu: {
|
||||||
|
get: function() {
|
||||||
|
this[LASTUSE] = time.number();
|
||||||
|
// Note: Reading texture back from GPU requires async operation
|
||||||
|
// For now, return the CPU data if available
|
||||||
|
return this[CPU]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get height() {
|
surface: {
|
||||||
return this[GPU].height
|
get: function() { return this.cpu }
|
||||||
},
|
},
|
||||||
|
|
||||||
unload_gpu() {
|
width: {
|
||||||
this[GPU] = undefined
|
get: function() {
|
||||||
|
return this[CPU].width
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unload_cpu() {
|
height: {
|
||||||
this[CPU] = undefined
|
get: function() {
|
||||||
},
|
return this[CPU].height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add methods to prototype
|
||||||
|
graphics.Image.prototype.unload_gpu = function() {
|
||||||
|
this[GPU] = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.Image.prototype.unload_cpu = function() {
|
||||||
|
this[CPU] = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function calc_image_size(img) {
|
function calc_image_size(img) {
|
||||||
@@ -79,17 +123,7 @@ function decorate_rect_px(img) {
|
|||||||
|
|
||||||
function make_handle(obj)
|
function make_handle(obj)
|
||||||
{
|
{
|
||||||
var image = Object.create(graphics.Image);
|
return new graphics.Image(obj);
|
||||||
|
|
||||||
if (obj.surface) {
|
|
||||||
im
|
|
||||||
}
|
|
||||||
return Object.assign(Object.create(graphics.Image), {
|
|
||||||
rect:{x:0,y:0,width:1,height:1},
|
|
||||||
[CPU]:obj,
|
|
||||||
[GPU]:undefined,
|
|
||||||
[LASTUSE]:os.now()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapSurface(surf, maybeRect){
|
function wrapSurface(surf, maybeRect){
|
||||||
@@ -121,11 +155,14 @@ function decode_image(bytes, ext)
|
|||||||
function create_image(path){
|
function create_image(path){
|
||||||
try{
|
try{
|
||||||
const bytes = io.slurpbytes(path);
|
const bytes = io.slurpbytes(path);
|
||||||
let raw = decode_image(bytes, path.ext());
|
|
||||||
|
|
||||||
|
let raw = decode_image(bytes, path.ext());
|
||||||
|
|
||||||
/* ── Case A: static image ─────────────────────────────────── */
|
/* ── Case A: static image ─────────────────────────────────── */
|
||||||
if(raw.surface)
|
if(raw.surface) {
|
||||||
return make_handle(raw.surface);
|
var gg = new graphics.Image(raw.surface)
|
||||||
|
return gg
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Case B: GIF helpers returned array [surf, …] ─────────── */
|
/* ── Case B: GIF helpers returned array [surf, …] ─────────── */
|
||||||
if(Array.isArray(raw))
|
if(Array.isArray(raw))
|
||||||
@@ -149,7 +186,7 @@ function create_image(path){
|
|||||||
throw new Error('Unsupported image structure from decoder');
|
throw new Error('Unsupported image structure from decoder');
|
||||||
|
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error(`Error loading image ${path}: ${e.message}`);
|
log.error(`Error loading image ${path}: ${e.message}`);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +195,7 @@ var image = {}
|
|||||||
image.dimensions = function() {
|
image.dimensions = function() {
|
||||||
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]])
|
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]])
|
||||||
}
|
}
|
||||||
image.dimensions[prosperon.DOC] = `
|
image.dimensions[cell.DOC] = `
|
||||||
:return: A 2D array [width, height] that is the scaled size of this image (texture size * rect size).
|
:return: A 2D array [width, height] that is the scaled size of this image (texture size * rect size).
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -179,7 +216,7 @@ function pack_into_sheet(images) {
|
|||||||
graphics.is_image = function(obj) {
|
graphics.is_image = function(obj) {
|
||||||
if (obj.texture && obj.rect) return true
|
if (obj.texture && obj.rect) return true
|
||||||
}
|
}
|
||||||
graphics.is_image[prosperon.DOC] = `
|
graphics.is_image[cell.DOC] = `
|
||||||
:param obj: An object to check.
|
:param obj: An object to check.
|
||||||
:return: True if 'obj' has a .texture and a .rect property, indicating it's an image object.
|
:return: True if 'obj' has a .texture and a .rect property, indicating it's an image object.
|
||||||
`
|
`
|
||||||
@@ -188,19 +225,17 @@ graphics.texture_from_data = function(data)
|
|||||||
{
|
{
|
||||||
if (!(data instanceof ArrayBuffer)) return undefined
|
if (!(data instanceof ArrayBuffer)) return undefined
|
||||||
|
|
||||||
var img = {
|
var image = graphics.make_texture(data);
|
||||||
surface: graphics.make_texture(data)
|
var img = make_handle(image)
|
||||||
}
|
|
||||||
render.load_texture(img)
|
img.gpu;
|
||||||
decorate_rect_px(img)
|
|
||||||
|
|
||||||
return img
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.from_surface = function(id, surf)
|
graphics.from_surface = function(id, surf)
|
||||||
{
|
{
|
||||||
return make_handle(surf)
|
return make_handle(surf)
|
||||||
var img = { surface: surf }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.from = function(id, data)
|
graphics.from = function(id, data)
|
||||||
@@ -213,7 +248,7 @@ graphics.from = function(id, data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
graphics.texture = function texture(path) {
|
graphics.texture = function texture(path) {
|
||||||
if (path.__proto__ === graphics.Image) return path
|
if (path instanceof graphics.Image) return path
|
||||||
|
|
||||||
if (typeof path !== 'string')
|
if (typeof path !== 'string')
|
||||||
throw new Error('need a string for graphics.texture')
|
throw new Error('need a string for graphics.texture')
|
||||||
@@ -222,13 +257,14 @@ graphics.texture = function texture(path) {
|
|||||||
if (cache.has(id)) return cache.get(id)
|
if (cache.has(id)) return cache.get(id)
|
||||||
|
|
||||||
var ipath = res.find_image(id)
|
var ipath = res.find_image(id)
|
||||||
if (!ipath) throw new Error(`unknown image ${id}`)
|
if (!ipath)
|
||||||
|
throw new Error(`unknown image ${id}`)
|
||||||
|
|
||||||
var image = create_image(ipath)
|
var image = create_image(ipath)
|
||||||
cache.set(id, image)
|
cache.set(id, image)
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
graphics.texture[prosperon.DOC] = `
|
graphics.texture[cell.DOC] = `
|
||||||
:param path: A string path to an image file or an already-loaded image object.
|
:param path: A string path to an image file or an already-loaded image object.
|
||||||
:return: An image object with {surface, texture, frames?, etc.} depending on the format.
|
:return: An image object with {surface, texture, frames?, etc.} depending on the format.
|
||||||
Load or retrieve a cached image, converting it into a GPU texture. If 'path' is already an object, it’s returned directly.
|
Load or retrieve a cached image, converting it into a GPU texture. If 'path' is already an object, it’s returned directly.
|
||||||
@@ -242,7 +278,7 @@ graphics.texture.total_size = function() {
|
|||||||
// Not yet implemented, presumably sum of (texture.width * texture.height * 4) for images in RAM
|
// Not yet implemented, presumably sum of (texture.width * texture.height * 4) for images in RAM
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
graphics.texture.total_size[prosperon.DOC] = `
|
graphics.texture.total_size[cell.DOC] = `
|
||||||
:return: The total estimated memory size of all cached textures in RAM, in bytes. (Not yet implemented.)
|
:return: The total estimated memory size of all cached textures in RAM, in bytes. (Not yet implemented.)
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -251,23 +287,23 @@ graphics.texture.total_vram = function() {
|
|||||||
// Not yet implemented, presumably sum of GPU memory usage
|
// Not yet implemented, presumably sum of GPU memory usage
|
||||||
return vram
|
return vram
|
||||||
}
|
}
|
||||||
graphics.texture.total_vram[prosperon.DOC] = `
|
graphics.texture.total_vram[cell.DOC] = `
|
||||||
:return: The total estimated GPU memory usage of all cached textures, in bytes. (Not yet implemented.)
|
:return: The total estimated GPU memory usage of all cached textures, in bytes. (Not yet implemented.)
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.tex_hotreload = function tex_hotreload(file) {
|
graphics.tex_hotreload = function tex_hotreload(file) {
|
||||||
console.log(`hot reloading ${file}`)
|
log.console(`hot reloading ${file}`)
|
||||||
if (!(file in graphics.texture.cache)) return
|
if (!(file in graphics.texture.cache)) return
|
||||||
console.log('really doing it')
|
log.console('really doing it')
|
||||||
|
|
||||||
var img = create_image(file)
|
var img = create_image(file)
|
||||||
var oldimg = graphics.texture.cache[file]
|
var oldimg = graphics.texture.cache[file]
|
||||||
console.log(`new image:${json.encode(img)}`)
|
log.console(`new image:${json.encode(img)}`)
|
||||||
console.log(`old image: ${json.encode(oldimg)}`)
|
log.console(`old image: ${json.encode(oldimg)}`)
|
||||||
|
|
||||||
merge_objects(oldimg, img, ['surface', 'texture', 'loop', 'time'])
|
merge_objects(oldimg, img, ['surface', 'texture', 'loop', 'time'])
|
||||||
}
|
}
|
||||||
graphics.tex_hotreload[prosperon.DOC] = `
|
graphics.tex_hotreload[cell.DOC] = `
|
||||||
:param file: The file path that was changed on disk.
|
:param file: The file path that was changed on disk.
|
||||||
:return: None
|
:return: None
|
||||||
Reload the image for the given file, updating the cached copy in memory and GPU.
|
Reload the image for the given file, updating the cached copy in memory and GPU.
|
||||||
@@ -307,16 +343,25 @@ graphics.get_font = function get_font(path, size) {
|
|||||||
|
|
||||||
var data = io.slurpbytes(fullpath)
|
var data = io.slurpbytes(fullpath)
|
||||||
var font = graphics.make_font(data,size)
|
var font = graphics.make_font(data,size)
|
||||||
font.texture = render.load_texture(font.surface)
|
|
||||||
|
|
||||||
console.log('loaded font texture')
|
// Load font texture via renderer actor (async)
|
||||||
console.log(json.encode(font.texture))
|
send(renderer_actor, {
|
||||||
|
kind: "renderer",
|
||||||
|
op: "loadTexture",
|
||||||
|
data: font.surface
|
||||||
|
}, function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
log.error("Failed to load font texture:", response.error);
|
||||||
|
} else {
|
||||||
|
font.texture = response;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
fontcache[fontstr] = font
|
fontcache[fontstr] = font
|
||||||
|
|
||||||
return font
|
return font
|
||||||
}
|
}
|
||||||
graphics.get_font[prosperon.DOC] = `
|
graphics.get_font[cell.DOC] = `
|
||||||
:param path: A string path to a font file, optionally with ".size" appended.
|
:param path: A string path to a font file, optionally with ".size" appended.
|
||||||
:param size: Pixel size of the font, if not included in 'path'.
|
:param size: Pixel size of the font, if not included in 'path'.
|
||||||
:return: A font object with .surface and .texture for rendering text.
|
:return: A font object with .surface and .texture for rendering text.
|
||||||
@@ -334,31 +379,14 @@ graphics.queue_sprite_mesh = function(queue) {
|
|||||||
}
|
}
|
||||||
return [mesh.pos, mesh.uv, mesh.color, mesh.indices]
|
return [mesh.pos, mesh.uv, mesh.color, mesh.indices]
|
||||||
}
|
}
|
||||||
graphics.queue_sprite_mesh[prosperon.DOC] = `
|
graphics.queue_sprite_mesh[cell.DOC] = `
|
||||||
:param queue: An array of draw commands, some of which are {type:'sprite'} objects.
|
:param queue: An array of draw commands, some of which are {type:'sprite'} objects.
|
||||||
:return: An array of references to GPU buffers [pos,uv,color,indices].
|
:return: An array of references to GPU buffers [pos,uv,color,indices].
|
||||||
Builds a single geometry mesh for all sprite-type commands in the queue, storing first_index/num_indices
|
Builds a single geometry mesh for all sprite-type commands in the queue, storing first_index/num_indices
|
||||||
so they can be rendered in one draw call.
|
so they can be rendered in one draw call.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_sprite_mesh[prosperon.DOC] = `
|
graphics.make_text_buffer[cell.DOC] = `
|
||||||
:param sprites: An array of sprite objects, each containing .rect (or transform), .src (UV region), .color, etc.
|
|
||||||
:param oldMesh (optional): An existing mesh object to reuse/resize if possible.
|
|
||||||
:return: A GPU mesh object with pos, uv, color, and indices buffers for all sprites.
|
|
||||||
Given an array of sprites, build a single geometry mesh for rendering them.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_sprite_queue[prosperon.DOC] = `
|
|
||||||
:param sprites: An array of sprite objects.
|
|
||||||
:param camera: (unused in the C code example) Typically a camera or transform for sorting?
|
|
||||||
:param pipeline: A pipeline object for rendering.
|
|
||||||
:param sort: An integer or boolean for whether to sort sprites; if truthy, sorts by layer & texture.
|
|
||||||
:return: An array of pipeline commands: geometry with mesh references, grouped by image.
|
|
||||||
Given an array of sprites, optionally sort them, then build a queue of pipeline commands.
|
|
||||||
Each group with a shared image becomes one command.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_text_buffer[prosperon.DOC] = `
|
|
||||||
:param text: The string to render.
|
:param text: The string to render.
|
||||||
:param rect: A rectangle specifying position and possibly wrapping.
|
:param rect: A rectangle specifying position and possibly wrapping.
|
||||||
:param angle: Rotation angle (unused or optional).
|
:param angle: Rotation angle (unused or optional).
|
||||||
@@ -369,7 +397,7 @@ graphics.make_text_buffer[prosperon.DOC] = `
|
|||||||
Generate a GPU buffer mesh of text quads for rendering with a font, etc.
|
Generate a GPU buffer mesh of text quads for rendering with a font, etc.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.rectpack[prosperon.DOC] = `
|
graphics.rectpack[cell.DOC] = `
|
||||||
:param width: The width of the area to pack into.
|
:param width: The width of the area to pack into.
|
||||||
:param height: The height of the area to pack into.
|
:param height: The height of the area to pack into.
|
||||||
:param sizes: An array of [w,h] pairs for the rectangles to pack.
|
:param sizes: An array of [w,h] pairs for the rectangles to pack.
|
||||||
@@ -377,50 +405,39 @@ graphics.rectpack[prosperon.DOC] = `
|
|||||||
Perform a rectangle packing using the stbrp library. Return positions for each rect.
|
Perform a rectangle packing using the stbrp library. Return positions for each rect.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_texture[prosperon.DOC] = `
|
graphics.make_texture[cell.DOC] = `
|
||||||
:param data: Raw image bytes (PNG, JPG, etc.) as an ArrayBuffer.
|
:param data: Raw image bytes (PNG, JPG, etc.) as an ArrayBuffer.
|
||||||
:return: An SDL_Surface object representing the decoded image in RAM, for use with GPU or software rendering.
|
:return: An SDL_Surface object representing the decoded image in RAM, for use with GPU or software rendering.
|
||||||
Convert raw image bytes into an SDL_Surface object.
|
Convert raw image bytes into an SDL_Surface object.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_gif[prosperon.DOC] = `
|
graphics.make_gif[cell.DOC] = `
|
||||||
:param data: An ArrayBuffer containing GIF data.
|
:param data: An ArrayBuffer containing GIF data.
|
||||||
:return: An object with frames[], each frame having its own .surface. Some also have a .texture for GPU use.
|
:return: An object with frames[], each frame having its own .surface. Some also have a .texture for GPU use.
|
||||||
Load a GIF, returning its frames. If it's a single-frame GIF, the result may have .surface only.
|
Load a GIF, returning its frames. If it's a single-frame GIF, the result may have .surface only.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_aseprite[prosperon.DOC] = `
|
graphics.make_aseprite[cell.DOC] = `
|
||||||
:param data: An ArrayBuffer containing Aseprite (ASE) file data.
|
:param data: An ArrayBuffer containing Aseprite (ASE) file data.
|
||||||
:return: An object containing frames or animations, each with .surface. May also have top-level .surface for a single-layer case.
|
:return: An object containing frames or animations, each with .surface. May also have top-level .surface for a single-layer case.
|
||||||
Load an Aseprite/ASE file from an array of bytes, returning frames or animations.
|
Load an Aseprite/ASE file from an array of bytes, returning frames or animations.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.cull_sprites[prosperon.DOC] = `
|
graphics.cull_sprites[cell.DOC] = `
|
||||||
:param sprites: An array of sprite objects (each has rect or transform).
|
:param sprites: An array of sprite objects (each has rect or transform).
|
||||||
:param camera: A camera or bounding rectangle defining the view area.
|
:param camera: A camera or bounding rectangle defining the view area.
|
||||||
:return: A new array of sprites that are visible in the camera's view.
|
:return: A new array of sprites that are visible in the camera's view.
|
||||||
Filter an array of sprites to only those visible in the provided camera’s view.
|
Filter an array of sprites to only those visible in the provided camera’s view.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_surface[prosperon.DOC] = `
|
graphics.make_font[cell.DOC] = `
|
||||||
:param dimensions: The size object {width, height}, or an array [w,h].
|
|
||||||
:return: A blank RGBA surface with the given dimensions, typically for software rendering or icons.
|
|
||||||
Create a blank surface in RAM.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_cursor[prosperon.DOC] = `
|
|
||||||
:param opts: An object with {surface, hotx, hoty} or similar.
|
|
||||||
:return: An SDL_Cursor object referencing the given surface for a custom mouse cursor.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_font[prosperon.DOC] = `
|
|
||||||
:param data: TTF/OTF file data as an ArrayBuffer.
|
:param data: TTF/OTF file data as an ArrayBuffer.
|
||||||
:param size: Pixel size for rendering glyphs.
|
:param size: Pixel size for rendering glyphs.
|
||||||
:return: A font object with surface, texture, and glyph data, for text rendering with make_text_buffer.
|
:return: A font object with surface, texture, and glyph data, for text rendering with make_text_buffer.
|
||||||
Load a font from TTF/OTF data at the given size.
|
Load a font from TTF/OTF data at the given size.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_line_prim[prosperon.DOC] = `
|
graphics.make_line_prim[cell.DOC] = `
|
||||||
:param points: An array of [x,y] points forming the line.
|
:param points: An array of [x,y] points forming the line.
|
||||||
:param thickness: The thickness (width) of the polyline.
|
:param thickness: The thickness (width) of the polyline.
|
||||||
:param startCap: (Unused) Possibly the type of cap for the start.
|
:param startCap: (Unused) Possibly the type of cap for the start.
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 867 B After Width: | Height: | Size: 867 B |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |