@erik9994857/cag 1.0.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,644 @@
1
+ var path = require("path");
2
+ var fs = require("fs");
3
+ var childProcess = require("child_process");
4
+
5
+ function ElectronRunner(engine, apiSettings) {
6
+ this.engine = engine;
7
+ this.info = engine.getInfo();
8
+ this.projectRoot = engine.projectRoot;
9
+ this.tempDir = path.join(this.projectRoot, ".cag-run");
10
+ this.apiSettings = apiSettings || {};
11
+ }
12
+
13
+ ElectronRunner.prototype.run = function () {
14
+ this.ensureDirectories();
15
+ this.generateApp();
16
+ this.installAndLaunch();
17
+ return { success: true };
18
+ };
19
+
20
+ ElectronRunner.prototype.ensureDirectories = function () {
21
+ if (fs.existsSync(this.tempDir)) {
22
+ this.removeDirectory(this.tempDir);
23
+ }
24
+ fs.mkdirSync(this.tempDir, { recursive: true });
25
+ };
26
+
27
+ ElectronRunner.prototype.generateApp = function () {
28
+ var pkg = {
29
+ name: "cag-runner",
30
+ version: "1.0.0",
31
+ main: "main.js",
32
+ dependencies: {}
33
+ };
34
+ fs.writeFileSync(path.join(this.tempDir, "package.json"), JSON.stringify(pkg, null, 2));
35
+ fs.writeFileSync(path.join(this.tempDir, "main.js"), this.getMainProcessSource());
36
+ fs.writeFileSync(path.join(this.tempDir, "index.html"), this.getRendererSource());
37
+ };
38
+
39
+ ElectronRunner.prototype.installAndLaunch = function () {
40
+ childProcess.execSync("npm install electron@latest --save --no-audit --no-fund", {
41
+ cwd: this.tempDir,
42
+ stdio: "inherit"
43
+ });
44
+
45
+ var electronBin = path.join(this.tempDir, "node_modules", ".bin", "electron");
46
+ if (process.platform === "win32") {
47
+ electronBin = electronBin + ".cmd";
48
+ }
49
+
50
+ childProcess.execSync("\"" + electronBin + "\" .", {
51
+ cwd: this.tempDir,
52
+ stdio: "inherit",
53
+ shell: true
54
+ });
55
+ };
56
+
57
+ ElectronRunner.prototype.getMainProcessSource = function () {
58
+ var s = this.apiSettings;
59
+ var windowTitle = s.windowTitle || "Default Game";
60
+ var windowWidth = s.windowWidth || 1280;
61
+ var windowHeight = s.windowHeight || 720;
62
+ var bg = s.background || "#1a1a2e";
63
+
64
+ var lines = [];
65
+ lines.push("var electron = require('electron');");
66
+ lines.push("var app = electron.app;");
67
+ lines.push("var BrowserWindow = electron.BrowserWindow;");
68
+ lines.push("var path = require('path');");
69
+ lines.push("");
70
+ lines.push("var win = null;");
71
+ lines.push("");
72
+ lines.push("app.whenReady().then(function () {");
73
+ lines.push(" win = new BrowserWindow({");
74
+ lines.push(" width: " + windowWidth + ",");
75
+ lines.push(" height: " + windowHeight + ",");
76
+ lines.push(" minWidth: 800,");
77
+ lines.push(" minHeight: 600,");
78
+ lines.push(" title: " + JSON.stringify(windowTitle) + ",");
79
+ lines.push(" fullscreenable: true,");
80
+ lines.push(" webPreferences: { nodeIntegration: false, contextIsolation: true },");
81
+ lines.push(" backgroundColor: " + JSON.stringify(bg) + ",");
82
+ lines.push(" autoHideMenuBar: true,");
83
+ lines.push(" show: false");
84
+ lines.push(" });");
85
+ lines.push(" win.setMenuBarVisibility(false);");
86
+ lines.push(" win.loadFile('index.html');");
87
+ lines.push(" win.once('ready-to-show', function () { win.show(); });");
88
+ lines.push(" win.on('closed', function () { win = null; });");
89
+ lines.push(" win.webContents.on('before-input-event', function (event, input) {");
90
+ lines.push(" if (input.key === 'F11') win.setFullScreen(!win.isFullScreen());");
91
+ lines.push(" });");
92
+ lines.push("});");
93
+ lines.push("app.on('window-all-closed', function () { app.quit(); });");
94
+ return lines.join("\n");
95
+ };
96
+
97
+ ElectronRunner.prototype.getRendererSource = function () {
98
+ var s = this.apiSettings;
99
+ var chunkSize = s.chunkSize || 16;
100
+ var renderDist = s.renderDistance || 1;
101
+ var sky = s.skyColor || { r: 0.53, g: 0.81, b: 0.92 };
102
+ var fogC = s.fogColor || { r: 0.53, g: 0.81, b: 0.92 };
103
+ var fogNear = s.fogNear || 64;
104
+ var fogFar = s.fogFar || 128;
105
+ var sunD = s.sunDirection || { x: 0.5, y: 1.0, z: 0.3 };
106
+ var amb = s.ambientLight || { r: 0.3, g: 0.3, b: 0.4 };
107
+ var fov = s.fov || 70;
108
+ var pSpeed = s.playerSpeed || 0.08;
109
+ var jHeight = s.jumpHeight || 0.18;
110
+ var grav = s.gravity || 0.015;
111
+ var spX = s.spawnX || 8;
112
+ var spY = s.spawnY || 2;
113
+ var spZ = s.spawnZ || 8;
114
+ var windowTitle = s.windowTitle || "Default Game";
115
+ var infoID = this.info.ID;
116
+ var infoVer = this.info.version || "1.0.0";
117
+
118
+ var css = [
119
+ "* { margin: 0; padding: 0; box-sizing: border-box; }",
120
+ "body { background: #1a1a2e; overflow: hidden; font-family: monospace; }",
121
+ "#loading { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #1a1a2e; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 999; color: #fff; transition: opacity 0.5s; }",
122
+ "#loading.fade-out { opacity: 0; pointer-events: none; }",
123
+ "#loading h1 { font-size: 64px; color: #4ecca3; margin-bottom: 4px; letter-spacing: 6px; text-shadow: 0 0 30px rgba(78,204,163,0.3); }",
124
+ "#loading h2 { font-size: 14px; color: #666; margin-bottom: 30px; font-weight: normal; letter-spacing: 2px; }",
125
+ "#load-status { font-size: 12px; color: #555; margin-bottom: 12px; min-height: 18px; transition: color 0.3s; }",
126
+ "#bar-outer { width: 300px; height: 3px; background: #2a2a3e; border-radius: 2px; overflow: hidden; }",
127
+ "#bar-inner { width: 0%; height: 100%; background: linear-gradient(90deg, #4ecca3, #45b7d1); border-radius: 2px; transition: width 0.4s ease-out; }",
128
+ ".version-tag { font-size: 11px; color: #444; margin-top: 20px; }",
129
+ "#click-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.65); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 998; cursor: pointer; transition: opacity 0.3s; }",
130
+ "#click-overlay:hover { background: rgba(0,0,0,0.55); }",
131
+ ".play-icon { width: 80px; height: 80px; border: 3px solid #4ecca3; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 16px; transition: transform 0.2s, border-color 0.2s; }",
132
+ "#click-overlay:hover .play-icon { transform: scale(1.1); border-color: #45b7d1; }",
133
+ ".play-triangle { width: 0; height: 0; border-style: solid; border-width: 15px 0 15px 28px; border-color: transparent transparent transparent #4ecca3; margin-left: 6px; }",
134
+ "#click-overlay span { color: #888; font-size: 16px; letter-spacing: 3px; }",
135
+ "canvas { display: block; position: fixed; top: 0; left: 0; }",
136
+ "#hud { position: fixed; top: 10px; left: 10px; z-index: 100; pointer-events: none; }",
137
+ "#hud-box { background: rgba(0,0,0,0.7); padding: 10px 14px; border-left: 3px solid #4ecca3; backdrop-filter: blur(4px); border-radius: 0 4px 4px 0; }",
138
+ "#hud-box div { color: #bbb; font-size: 11px; line-height: 17px; }",
139
+ ".hud-title { color: #4ecca3; font-size: 13px; font-weight: bold; margin-bottom: 3px; letter-spacing: 1px; }",
140
+ ".hud-sep { border-top: 1px solid #333; margin: 4px 0; }",
141
+ "#crosshair { position: fixed; top: 50%; left: 50%; width: 24px; height: 24px; margin: -12px 0 0 -12px; z-index: 100; pointer-events: none; }",
142
+ "#crosshair::before { content: ''; position: absolute; top: 11px; left: 3px; width: 18px; height: 2px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
143
+ "#crosshair::after { content: ''; position: absolute; top: 3px; left: 11px; width: 2px; height: 18px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
144
+ "#controls-hint { position: fixed; bottom: 10px; right: 10px; z-index: 100; pointer-events: none; background: rgba(0,0,0,0.55); padding: 8px 14px; color: #666; font-size: 10px; border-radius: 4px; line-height: 16px; backdrop-filter: blur(4px); }",
145
+ "#controls-hint b { color: #888; }"
146
+ ].join("\n");
147
+
148
+ var vsSrc = [
149
+ "attribute vec3 aPos;",
150
+ "attribute vec3 aColor;",
151
+ "attribute vec3 aNormal;",
152
+ "uniform mat4 uProj;",
153
+ "uniform mat4 uView;",
154
+ "varying vec3 vColor;",
155
+ "varying vec3 vNormal;",
156
+ "varying vec3 vWorldPos;",
157
+ "void main() {",
158
+ " vColor = aColor;",
159
+ " vNormal = aNormal;",
160
+ " vWorldPos = aPos;",
161
+ " gl_Position = uProj * uView * vec4(aPos, 1.0);",
162
+ "}"
163
+ ].join("\\n");
164
+
165
+ var fsSrc = [
166
+ "precision mediump float;",
167
+ "varying vec3 vColor;",
168
+ "varying vec3 vNormal;",
169
+ "varying vec3 vWorldPos;",
170
+ "uniform vec3 uSunDir;",
171
+ "uniform vec3 uSunColor;",
172
+ "uniform vec3 uAmbient;",
173
+ "uniform vec3 uFogColor;",
174
+ "uniform float uFogNear;",
175
+ "uniform float uFogFar;",
176
+ "uniform vec3 uCamPos;",
177
+ "void main() {",
178
+ " vec3 norm = normalize(vNormal);",
179
+ " float diff = max(dot(norm, normalize(uSunDir)), 0.0);",
180
+ " vec3 lit = vColor * (uAmbient + uSunColor * diff);",
181
+ " float dist = length(vWorldPos - uCamPos);",
182
+ " float fog = clamp((dist - uFogNear) / (uFogFar - uFogNear), 0.0, 1.0);",
183
+ " vec3 final = mix(lit, uFogColor, fog);",
184
+ " gl_FragColor = vec4(final, 1.0);",
185
+ "}"
186
+ ].join("\\n");
187
+
188
+ var js = [];
189
+ js.push("var CHUNK_SIZE = " + chunkSize + ";");
190
+ js.push("var RENDER_DISTANCE = " + renderDist + ";");
191
+ js.push("var INFO_ID = " + JSON.stringify(infoID) + ";");
192
+ js.push("var INFO_VERSION = " + JSON.stringify(infoVer) + ";");
193
+ js.push("var VS_SRC = \"" + vsSrc + "\";");
194
+ js.push("var FS_SRC = \"" + fsSrc + "\";");
195
+ js.push("");
196
+ js.push("var game = {");
197
+ js.push(" gl: null, canvas: null, program: null,");
198
+ js.push(" camX: " + spX + ", camY: " + spY + ", camZ: " + spZ + ",");
199
+ js.push(" pitch: -0.3, yaw: 0,");
200
+ js.push(" keys: {}, locked: false,");
201
+ js.push(" velY: 0, onGround: false,");
202
+ js.push(" gravity: " + grav + ",");
203
+ js.push(" speed: " + pSpeed + ",");
204
+ js.push(" jumpHeight: " + jHeight + ",");
205
+ js.push(" fps: 0, frames: 0, lastFps: 0,");
206
+ js.push(" chunks: {},");
207
+ js.push(" totalVerts: 0,");
208
+ js.push(" totalBlocks: 0,");
209
+ js.push(" uProj: null, uView: null, uSunDir: null, uSunColor: null,");
210
+ js.push(" uAmbient: null, uFogColor: null, uFogNear: null, uFogFar: null, uCamPos: null,");
211
+ js.push(" started: false");
212
+ js.push("};");
213
+ js.push("");
214
+
215
+ // Chunk generation - flat terrain
216
+ js.push("function generateChunkBlocks(cx, cz) {");
217
+ js.push(" var blocks = [];");
218
+ js.push(" for (var x = 0; x < CHUNK_SIZE; x++) {");
219
+ js.push(" for (var z = 0; z < CHUNK_SIZE; z++) {");
220
+ js.push(" blocks.push({ x: cx * CHUNK_SIZE + x, y: 0, z: cz * CHUNK_SIZE + z, type: 'grass' });");
221
+ js.push(" }");
222
+ js.push(" }");
223
+ js.push(" return blocks;");
224
+ js.push("}");
225
+ js.push("");
226
+
227
+ // Face helper
228
+ js.push("function addFace(pos, col, nrm, v0, v1, v2, v3, color, normal) {");
229
+ js.push(" pos.push(v0[0],v0[1],v0[2], v1[0],v1[1],v1[2], v2[0],v2[1],v2[2]);");
230
+ js.push(" pos.push(v0[0],v0[1],v0[2], v2[0],v2[1],v2[2], v3[0],v3[1],v3[2]);");
231
+ js.push(" for (var i = 0; i < 6; i++) {");
232
+ js.push(" col.push(color[0], color[1], color[2]);");
233
+ js.push(" nrm.push(normal[0], normal[1], normal[2]);");
234
+ js.push(" }");
235
+ js.push("}");
236
+ js.push("");
237
+
238
+ // Build mesh for a chunk
239
+ js.push("function buildChunkMesh(blocks, blockMap) {");
240
+ js.push(" var pos = [], col = [], nrm = [];");
241
+ js.push(" var topC = [0.36, 0.63, 0.21];");
242
+ js.push(" var sideC = [0.50, 0.38, 0.22];");
243
+ js.push(" var botC = [0.45, 0.32, 0.17];");
244
+ js.push(" for (var i = 0; i < blocks.length; i++) {");
245
+ js.push(" var b = blocks[i];");
246
+ js.push(" var x = b.x, y = b.y, z = b.z;");
247
+ js.push(" var k = function(bx,by,bz) { return bx+','+by+','+bz; };");
248
+ js.push(" if (!blockMap[k(x,y+1,z)]) addFace(pos,col,nrm, [x,y+1,z],[x+1,y+1,z],[x+1,y+1,z+1],[x,y+1,z+1], topC, [0,1,0]);");
249
+ js.push(" if (!blockMap[k(x,y-1,z)]) addFace(pos,col,nrm, [x,y,z+1],[x+1,y,z+1],[x+1,y,z],[x,y,z], botC, [0,-1,0]);");
250
+ js.push(" if (!blockMap[k(x,y,z-1)]) addFace(pos,col,nrm, [x,y,z],[x+1,y,z],[x+1,y+1,z],[x,y+1,z], sideC, [0,0,-1]);");
251
+ js.push(" if (!blockMap[k(x,y,z+1)]) addFace(pos,col,nrm, [x+1,y,z+1],[x,y,z+1],[x,y+1,z+1],[x+1,y+1,z+1], sideC, [0,0,1]);");
252
+ js.push(" if (!blockMap[k(x-1,y,z)]) addFace(pos,col,nrm, [x,y,z+1],[x,y,z],[x,y+1,z],[x,y+1,z+1], sideC, [-1,0,0]);");
253
+ js.push(" if (!blockMap[k(x+1,y,z)]) addFace(pos,col,nrm, [x+1,y,z],[x+1,y,z+1],[x+1,y+1,z+1],[x+1,y+1,z], sideC, [1,0,0]);");
254
+ js.push(" }");
255
+ js.push(" return { positions: pos, colors: col, normals: nrm };");
256
+ js.push("}");
257
+ js.push("");
258
+
259
+ // Build block map including neighbor chunks
260
+ js.push("function buildBlockMap(blocks, cx, cz) {");
261
+ js.push(" var map = {};");
262
+ js.push(" for (var i = 0; i < blocks.length; i++) {");
263
+ js.push(" var b = blocks[i];");
264
+ js.push(" map[b.x+','+b.y+','+b.z] = true;");
265
+ js.push(" }");
266
+ js.push(" var dirs = [[cx-1,cz],[cx+1,cz],[cx,cz-1],[cx,cz+1]];");
267
+ js.push(" for (var n = 0; n < dirs.length; n++) {");
268
+ js.push(" var nk = dirs[n][0]+','+dirs[n][1];");
269
+ js.push(" if (game.chunks[nk]) {");
270
+ js.push(" var nb = game.chunks[nk].blocks;");
271
+ js.push(" for (var j = 0; j < nb.length; j++) {");
272
+ js.push(" map[nb[j].x+','+nb[j].y+','+nb[j].z] = true;");
273
+ js.push(" }");
274
+ js.push(" }");
275
+ js.push(" }");
276
+ js.push(" return map;");
277
+ js.push("}");
278
+ js.push("");
279
+
280
+ // Load a chunk
281
+ js.push("function loadChunk(cx, cz) {");
282
+ js.push(" var chunkKey = cx+','+cz;");
283
+ js.push(" var blocks = generateChunkBlocks(cx, cz);");
284
+ js.push(" var blockMap = buildBlockMap(blocks, cx, cz);");
285
+ js.push(" var mesh = buildChunkMesh(blocks, blockMap);");
286
+ js.push(" var vertCount = mesh.positions.length / 3;");
287
+ js.push(" var gl = game.gl;");
288
+ js.push(" var vbo = gl.createBuffer();");
289
+ js.push(" var data = new Float32Array(vertCount * 9);");
290
+ js.push(" for (var v = 0; v < vertCount; v++) {");
291
+ js.push(" data[v*9+0] = mesh.positions[v*3+0];");
292
+ js.push(" data[v*9+1] = mesh.positions[v*3+1];");
293
+ js.push(" data[v*9+2] = mesh.positions[v*3+2];");
294
+ js.push(" data[v*9+3] = mesh.colors[v*3+0];");
295
+ js.push(" data[v*9+4] = mesh.colors[v*3+1];");
296
+ js.push(" data[v*9+5] = mesh.colors[v*3+2];");
297
+ js.push(" data[v*9+6] = mesh.normals[v*3+0];");
298
+ js.push(" data[v*9+7] = mesh.normals[v*3+1];");
299
+ js.push(" data[v*9+8] = mesh.normals[v*3+2];");
300
+ js.push(" }");
301
+ js.push(" gl.bindBuffer(gl.ARRAY_BUFFER, vbo);");
302
+ js.push(" gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);");
303
+ js.push(" game.chunks[chunkKey] = { vbo: vbo, vertCount: vertCount, blocks: blocks, cx: cx, cz: cz };");
304
+ js.push(" game.totalVerts += vertCount;");
305
+ js.push(" game.totalBlocks += blocks.length;");
306
+ js.push("}");
307
+ js.push("");
308
+
309
+ // Unload a chunk
310
+ js.push("function unloadChunk(chunkKey) {");
311
+ js.push(" var chunk = game.chunks[chunkKey];");
312
+ js.push(" if (chunk) {");
313
+ js.push(" game.gl.deleteBuffer(chunk.vbo);");
314
+ js.push(" game.totalVerts -= chunk.vertCount;");
315
+ js.push(" game.totalBlocks -= chunk.blocks.length;");
316
+ js.push(" delete game.chunks[chunkKey];");
317
+ js.push(" }");
318
+ js.push("}");
319
+ js.push("");
320
+
321
+ // Update chunks based on player position
322
+ js.push("function updateChunks() {");
323
+ js.push(" var pcx = Math.floor(game.camX / CHUNK_SIZE);");
324
+ js.push(" var pcz = Math.floor(game.camZ / CHUNK_SIZE);");
325
+ js.push(" var needed = {};");
326
+ js.push(" for (var dx = -RENDER_DISTANCE; dx <= RENDER_DISTANCE; dx++) {");
327
+ js.push(" for (var dz = -RENDER_DISTANCE; dz <= RENDER_DISTANCE; dz++) {");
328
+ js.push(" var cx = pcx + dx;");
329
+ js.push(" var cz = pcz + dz;");
330
+ js.push(" var ck = cx+','+cz;");
331
+ js.push(" needed[ck] = true;");
332
+ js.push(" if (!game.chunks[ck]) loadChunk(cx, cz);");
333
+ js.push(" }");
334
+ js.push(" }");
335
+ js.push(" var loaded = Object.keys(game.chunks);");
336
+ js.push(" for (var i = 0; i < loaded.length; i++) {");
337
+ js.push(" if (!needed[loaded[i]]) unloadChunk(loaded[i]);");
338
+ js.push(" }");
339
+ js.push("}");
340
+ js.push("");
341
+
342
+ // Init WebGL
343
+ js.push("game.init = function () {");
344
+ js.push(" this.canvas = document.getElementById('cag-canvas');");
345
+ js.push(" this.gl = this.canvas.getContext('webgl', { antialias: true });");
346
+ js.push(" if (!this.gl) { alert('WebGL not supported'); return; }");
347
+ js.push(" this.resize();");
348
+ js.push(" window.addEventListener('resize', function () { game.resize(); });");
349
+ js.push("");
350
+ js.push(" var gl = this.gl;");
351
+ js.push(" var vs = gl.createShader(gl.VERTEX_SHADER);");
352
+ js.push(" gl.shaderSource(vs, VS_SRC);");
353
+ js.push(" gl.compileShader(vs);");
354
+ js.push(" var fsh = gl.createShader(gl.FRAGMENT_SHADER);");
355
+ js.push(" gl.shaderSource(fsh, FS_SRC);");
356
+ js.push(" gl.compileShader(fsh);");
357
+ js.push("");
358
+ js.push(" this.program = gl.createProgram();");
359
+ js.push(" gl.attachShader(this.program, vs);");
360
+ js.push(" gl.attachShader(this.program, fsh);");
361
+ js.push(" gl.bindAttribLocation(this.program, 0, 'aPos');");
362
+ js.push(" gl.bindAttribLocation(this.program, 1, 'aColor');");
363
+ js.push(" gl.bindAttribLocation(this.program, 2, 'aNormal');");
364
+ js.push(" gl.linkProgram(this.program);");
365
+ js.push(" gl.useProgram(this.program);");
366
+ js.push("");
367
+ js.push(" this.uProj = gl.getUniformLocation(this.program, 'uProj');");
368
+ js.push(" this.uView = gl.getUniformLocation(this.program, 'uView');");
369
+ js.push(" this.uSunDir = gl.getUniformLocation(this.program, 'uSunDir');");
370
+ js.push(" this.uSunColor = gl.getUniformLocation(this.program, 'uSunColor');");
371
+ js.push(" this.uAmbient = gl.getUniformLocation(this.program, 'uAmbient');");
372
+ js.push(" this.uFogColor = gl.getUniformLocation(this.program, 'uFogColor');");
373
+ js.push(" this.uFogNear = gl.getUniformLocation(this.program, 'uFogNear');");
374
+ js.push(" this.uFogFar = gl.getUniformLocation(this.program, 'uFogFar');");
375
+ js.push(" this.uCamPos = gl.getUniformLocation(this.program, 'uCamPos');");
376
+ js.push("");
377
+ js.push(" gl.uniform3f(this.uSunDir, " + sunD.x + ", " + sunD.y + ", " + sunD.z + ");");
378
+ js.push(" gl.uniform3f(this.uSunColor, 1.0, 0.95, 0.8);");
379
+ js.push(" gl.uniform3f(this.uAmbient, " + amb.r + ", " + amb.g + ", " + amb.b + ");");
380
+ js.push(" gl.uniform3f(this.uFogColor, " + fogC.r + ", " + fogC.g + ", " + fogC.b + ");");
381
+ js.push(" gl.uniform1f(this.uFogNear, " + fogNear + ");");
382
+ js.push(" gl.uniform1f(this.uFogFar, " + fogFar + ");");
383
+ js.push("");
384
+ js.push(" gl.enable(gl.DEPTH_TEST);");
385
+ js.push(" gl.enable(gl.CULL_FACE);");
386
+ js.push(" gl.cullFace(gl.BACK);");
387
+ js.push(" gl.clearColor(" + sky.r + ", " + sky.g + ", " + sky.b + ", 1.0);");
388
+ js.push("");
389
+ js.push(" updateChunks();");
390
+ js.push("");
391
+ js.push(" document.addEventListener('keydown', function (e) { game.keys[e.code] = true; });");
392
+ js.push(" document.addEventListener('keyup', function (e) { game.keys[e.code] = false; });");
393
+ js.push(" document.addEventListener('mousemove', function (e) {");
394
+ js.push(" if (!game.locked) return;");
395
+ js.push(" game.yaw += e.movementX * 0.002;");
396
+ js.push(" game.pitch -= e.movementY * 0.002;");
397
+ js.push(" game.pitch = Math.max(-Math.PI/2 + 0.01, Math.min(Math.PI/2 - 0.01, game.pitch));");
398
+ js.push(" });");
399
+ js.push("");
400
+ js.push(" this.canvas.addEventListener('click', function () {");
401
+ js.push(" game.canvas.requestPointerLock();");
402
+ js.push(" });");
403
+ js.push(" document.addEventListener('pointerlockchange', function () {");
404
+ js.push(" game.locked = document.pointerLockElement === game.canvas;");
405
+ js.push(" document.getElementById('crosshair').style.display = game.locked ? 'block' : 'none';");
406
+ js.push(" });");
407
+ js.push("");
408
+ js.push(" setInterval(function () { game.fps = game.frames; game.frames = 0; }, 1000);");
409
+ js.push("");
410
+ js.push(" var loadingEl = document.getElementById('loading');");
411
+ js.push(" loadingEl.classList.add('fade-out');");
412
+ js.push(" var overlayEl = document.getElementById('click-overlay');");
413
+ js.push(" overlayEl.style.display = 'flex';");
414
+ js.push(" overlayEl.addEventListener('click', function () {");
415
+ js.push(" overlayEl.style.display = 'none';");
416
+ js.push(" game.canvas.requestPointerLock();");
417
+ js.push(" game.started = true;");
418
+ js.push(" game.loop();");
419
+ js.push(" });");
420
+ js.push("};");
421
+ js.push("");
422
+
423
+ // Resize
424
+ js.push("game.resize = function () {");
425
+ js.push(" this.canvas.width = window.innerWidth;");
426
+ js.push(" this.canvas.height = window.innerHeight;");
427
+ js.push(" this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);");
428
+ js.push("};");
429
+ js.push("");
430
+
431
+ // Update
432
+ js.push("game.update = function () {");
433
+ js.push(" if (!this.locked) return;");
434
+ js.push(" var speed = this.speed;");
435
+ js.push(" var sprint = this.keys['ShiftLeft'] || this.keys['ShiftRight'];");
436
+ js.push(" if (sprint) speed *= 1.8;");
437
+ js.push(" var dx = 0, dz = 0;");
438
+ js.push(" var sinY = Math.sin(this.yaw);");
439
+ js.push(" var cosY = Math.cos(this.yaw);");
440
+ js.push(" if (this.keys['KeyW']) { dx -= sinY * speed; dz -= cosY * speed; }");
441
+ js.push(" if (this.keys['KeyS']) { dx += sinY * speed; dz += cosY * speed; }");
442
+ js.push(" if (this.keys['KeyA']) { dx -= cosY * speed; dz += sinY * speed; }");
443
+ js.push(" if (this.keys['KeyD']) { dx += cosY * speed; dz -= sinY * speed; }");
444
+ js.push(" this.camX += dx;");
445
+ js.push(" this.camZ += dz;");
446
+ js.push(" this.velY -= this.gravity;");
447
+ js.push(" this.camY += this.velY;");
448
+ js.push(" if (this.camY < 1 + 1.6) {");
449
+ js.push(" this.camY = 1 + 1.6;");
450
+ js.push(" this.velY = 0;");
451
+ js.push(" this.onGround = true;");
452
+ js.push(" } else {");
453
+ js.push(" this.onGround = false;");
454
+ js.push(" }");
455
+ js.push(" if (this.keys['Space'] && this.onGround) {");
456
+ js.push(" this.velY = this.jumpHeight;");
457
+ js.push(" this.onGround = false;");
458
+ js.push(" }");
459
+ js.push("};");
460
+ js.push("");
461
+
462
+ // Render
463
+ js.push("game.render = function () {");
464
+ js.push(" var gl = this.gl;");
465
+ js.push(" gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);");
466
+ js.push("");
467
+ js.push(" var fovRad = " + fov + " * Math.PI / 180;");
468
+ js.push(" var aspect = this.canvas.width / this.canvas.height;");
469
+ js.push(" var near = 0.1, far = 300;");
470
+ js.push(" var f = 1.0 / Math.tan(fovRad / 2);");
471
+ js.push(" var proj = new Float32Array([");
472
+ js.push(" f/aspect, 0, 0, 0,");
473
+ js.push(" 0, f, 0, 0,");
474
+ js.push(" 0, 0, (far+near)/(near-far), -1,");
475
+ js.push(" 0, 0, (2*far*near)/(near-far), 0");
476
+ js.push(" ]);");
477
+ js.push(" gl.uniformMatrix4fv(this.uProj, false, proj);");
478
+ js.push("");
479
+ js.push(" var cp = Math.cos(this.pitch), sp = Math.sin(this.pitch);");
480
+ js.push(" var cy = Math.cos(this.yaw), sy = Math.sin(this.yaw);");
481
+ js.push(" var fx = -sy*cp, fy = sp, fz = -cy*cp;");
482
+ js.push(" var rx = fy*0 - fz*1, ry = fz*0 - fx*0, rz = fx*1 - fy*0;");
483
+ js.push(" var rl = Math.sqrt(rx*rx+ry*ry+rz*rz);");
484
+ js.push(" rx /= rl; ry /= rl; rz /= rl;");
485
+ js.push(" var ux = ry*fz-rz*fy, uy = rz*fx-rx*fz, uz = rx*fy-ry*fx;");
486
+ js.push(" var view = new Float32Array([");
487
+ js.push(" rx, ux, -fx, 0,");
488
+ js.push(" ry, uy, -fy, 0,");
489
+ js.push(" rz, uz, -fz, 0,");
490
+ js.push(" -(rx*this.camX+ry*this.camY+rz*this.camZ),");
491
+ js.push(" -(ux*this.camX+uy*this.camY+uz*this.camZ),");
492
+ js.push(" -(-fx*this.camX+-fy*this.camY+-fz*this.camZ),");
493
+ js.push(" 1");
494
+ js.push(" ]);");
495
+ js.push(" gl.uniformMatrix4fv(this.uView, false, view);");
496
+ js.push(" gl.uniform3f(this.uCamPos, this.camX, this.camY, this.camZ);");
497
+ js.push("");
498
+ js.push(" var stride = 9 * 4;");
499
+ js.push(" var keys = Object.keys(this.chunks);");
500
+ js.push(" for (var i = 0; i < keys.length; i++) {");
501
+ js.push(" var chunk = this.chunks[keys[i]];");
502
+ js.push(" if (chunk.vertCount === 0) continue;");
503
+ js.push(" gl.bindBuffer(gl.ARRAY_BUFFER, chunk.vbo);");
504
+ js.push(" gl.enableVertexAttribArray(0);");
505
+ js.push(" gl.enableVertexAttribArray(1);");
506
+ js.push(" gl.enableVertexAttribArray(2);");
507
+ js.push(" gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, 0);");
508
+ js.push(" gl.vertexAttribPointer(1, 3, gl.FLOAT, false, stride, 12);");
509
+ js.push(" gl.vertexAttribPointer(2, 3, gl.FLOAT, false, stride, 24);");
510
+ js.push(" gl.drawArrays(gl.TRIANGLES, 0, chunk.vertCount);");
511
+ js.push(" }");
512
+ js.push("};");
513
+ js.push("");
514
+
515
+ // HUD
516
+ js.push("game.updateHUD = function () {");
517
+ js.push(" var pcx = Math.floor(this.camX / CHUNK_SIZE);");
518
+ js.push(" var pcz = Math.floor(this.camZ / CHUNK_SIZE);");
519
+ js.push(" var loadedCount = Object.keys(this.chunks).length;");
520
+ js.push(" var sprint = this.keys['ShiftLeft'] || this.keys['ShiftRight'];");
521
+ js.push(" var html = '<div class=\"hud-title\">' + INFO_ID + '</div>';");
522
+ js.push(" html += '<div>v' + INFO_VERSION + '</div>';");
523
+ js.push(" html += '<div class=\"hud-sep\"></div>';");
524
+ js.push(" html += '<div>FPS: ' + this.fps + '</div>';");
525
+ js.push(" html += '<div>Triangles: ' + Math.floor(this.totalVerts / 3) + '</div>';");
526
+ js.push(" html += '<div>Blocks: ' + this.totalBlocks + '</div>';");
527
+ js.push(" html += '<div>Chunks: ' + loadedCount + '</div>';");
528
+ js.push(" html += '<div class=\"hud-sep\"></div>';");
529
+ js.push(" html += '<div>X: ' + this.camX.toFixed(1) + '</div>';");
530
+ js.push(" html += '<div>Y: ' + this.camY.toFixed(1) + '</div>';");
531
+ js.push(" html += '<div>Z: ' + this.camZ.toFixed(1) + '</div>';");
532
+ js.push(" html += '<div>Chunk: ' + pcx + ', ' + pcz + '</div>';");
533
+ js.push(" html += '<div class=\"hud-sep\"></div>';");
534
+ js.push(" html += '<div>Sprint: ' + (sprint ? 'ON' : 'OFF') + '</div>';");
535
+ js.push(" document.getElementById('hud-info').innerHTML = html;");
536
+ js.push("};");
537
+ js.push("");
538
+
539
+ // Game loop
540
+ js.push("game.loop = function () {");
541
+ js.push(" requestAnimationFrame(function () { game.loop(); });");
542
+ js.push(" game.frames++;");
543
+ js.push(" game.update();");
544
+ js.push(" updateChunks();");
545
+ js.push(" game.render();");
546
+ js.push(" game.updateHUD();");
547
+ js.push("};");
548
+ js.push("");
549
+
550
+ // Loading animation
551
+ js.push("var bar = document.getElementById('bar-inner');");
552
+ js.push("var status = document.getElementById('load-status');");
553
+ js.push("setTimeout(function () { bar.style.width = '25%'; status.textContent = 'Compiling shaders...'; }, 200);");
554
+ js.push("setTimeout(function () { bar.style.width = '50%'; status.textContent = 'Generating chunks...'; }, 600);");
555
+ js.push("setTimeout(function () { bar.style.width = '75%'; status.textContent = 'Building meshes...'; }, 1000);");
556
+ js.push("setTimeout(function () { bar.style.width = '100%'; status.textContent = 'Ready!'; status.style.color = '#4ecca3'; }, 1400);");
557
+ js.push("setTimeout(function () { game.init(); }, 1800);");
558
+
559
+ var html = "<!DOCTYPE html>\n<html>\n<head>\n";
560
+ html += "<meta charset='utf-8'>\n";
561
+ html += "<title>" + windowTitle + " - CaG Engine</title>\n";
562
+ html += "<style>\n" + css + "\n</style>\n";
563
+ html += "</head>\n<body>\n";
564
+ html += "<div id='loading'>\n";
565
+ html += " <h1>CaG</h1>\n";
566
+ html += " <h2>" + infoID + "</h2>\n";
567
+ html += " <div id='load-status'>Starting...</div>\n";
568
+ html += " <div id='bar-outer'><div id='bar-inner'></div></div>\n";
569
+ html += " <div class='version-tag'>v" + infoVer + "</div>\n";
570
+ html += "</div>\n";
571
+ html += "<div id='click-overlay' style='display:none'><div class='play-icon'><div class='play-triangle'></div></div><span>CLICK TO PLAY</span></div>\n";
572
+ html += "<div id='crosshair' style='display:none'></div>\n";
573
+ html += "<div id='hud'><div id='hud-box'><div id='hud-info'></div></div></div>\n";
574
+ html += "<div id='controls-hint'><b>WASD</b> Move &nbsp; <b>Mouse</b> Look &nbsp; <b>Space</b> Jump &nbsp; <b>Shift</b> Sprint &nbsp; <b>F11</b> Fullscreen &nbsp; <b>Esc</b> Release</div>\n";
575
+ html += "<canvas id='cag-canvas'></canvas>\n";
576
+ html += "<script>\n" + js.join("\n") + "\n</script>\n";
577
+ html += "</body>\n</html>";
578
+
579
+ return html;
580
+ };
581
+
582
+ ElectronRunner.prototype.cleanup = function () {
583
+ if (fs.existsSync(this.tempDir)) {
584
+ this.removeDirectory(this.tempDir);
585
+ }
586
+ };
587
+
588
+ ElectronRunner.prototype.removeDirectory = function (dir) {
589
+ if (!fs.existsSync(dir)) return;
590
+ var entries = fs.readdirSync(dir, { withFileTypes: true });
591
+ for (var i = 0; i < entries.length; i++) {
592
+ var fullPath = path.join(dir, entries[i].name);
593
+ if (entries[i].isDirectory()) {
594
+ this.removeDirectory(fullPath);
595
+ } else {
596
+ fs.unlinkSync(fullPath);
597
+ }
598
+ }
599
+ fs.rmdirSync(dir);
600
+ };
601
+
602
+ ElectronRunner.extractSettings = function (executor) {
603
+ var settings = {};
604
+ var registry = executor.getRegistry();
605
+
606
+ if (registry.has("Window.API")) {
607
+ var windowAPI = registry.get("Window.API");
608
+ settings.windowTitle = windowAPI.title;
609
+ settings.windowWidth = windowAPI.width;
610
+ settings.windowHeight = windowAPI.height;
611
+ settings.background = windowAPI.background;
612
+ }
613
+ if (registry.has("Render.API")) {
614
+ var renderAPI = registry.get("Render.API");
615
+ settings.skyColor = renderAPI.skyColor;
616
+ settings.fogColor = renderAPI.fogColor;
617
+ settings.fogNear = renderAPI.fogNear;
618
+ settings.fogFar = renderAPI.fogFar;
619
+ settings.sunDirection = renderAPI.sunDirection;
620
+ settings.ambientLight = renderAPI.ambientLight;
621
+ }
622
+ if (registry.has("Camera.API")) {
623
+ var cameraAPI = registry.get("Camera.API");
624
+ settings.fov = cameraAPI.fov;
625
+ }
626
+ if (registry.has("Player.API")) {
627
+ var playerAPI = registry.get("Player.API");
628
+ settings.playerSpeed = playerAPI.speed;
629
+ settings.jumpHeight = playerAPI.jumpHeight;
630
+ settings.gravity = playerAPI.gravity;
631
+ settings.spawnX = playerAPI.spawnPoint.x;
632
+ settings.spawnY = playerAPI.spawnPoint.y;
633
+ settings.spawnZ = playerAPI.spawnPoint.z;
634
+ }
635
+ if (registry.has("Chunk.API")) {
636
+ var chunkAPI = registry.get("Chunk.API");
637
+ settings.chunkSize = chunkAPI.chunkSize;
638
+ settings.renderDistance = chunkAPI.renderDistance;
639
+ }
640
+
641
+ return settings;
642
+ };
643
+
644
+ module.exports = ElectronRunner;
@@ -49,6 +49,13 @@ class Executor {
49
49
  for (var j = 0; j < parsedFile.functions.length; j++) {
50
50
  var func = parsedFile.functions[j];
51
51
 
52
+ if (func.type === "set") {
53
+ context.scope.set(func.variable, func.value);
54
+ results.outputs.push("Set " + func.variable + " = " + func.value);
55
+ results.functionsExecuted++;
56
+ continue;
57
+ }
58
+
52
59
  try {
53
60
  var funcResult = this.executeFunction(func, context);
54
61
  results.outputs.push(this.formatOutput(func, funcResult));