malc-game-engine 1.0.1 → 1.0.2

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.
Files changed (3) hide show
  1. package/malc.js +947 -944
  2. package/malc.min.js +294 -0
  3. package/package.json +1 -1
package/malc.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MALC Game Engine Library
3
- * Version: 1.0.0
3
+ * Version: 1.0.2
4
4
  * Description: A comprehensive 2D game engine for p5.js
5
5
  */
6
6
 
@@ -17,6 +17,9 @@
17
17
  }
18
18
  }(typeof self !== 'undefined' ? self : this, function(p5) {
19
19
 
20
+ // Store reference to p5 instance
21
+ const _p5 = p5;
22
+
20
23
  // ========== GLOBAL ARRAYS ==========
21
24
  const MALCgameObjects = [];
22
25
  const MALCbuttons = [];
@@ -37,1121 +40,1121 @@ function generateId(prefix) {
37
40
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
38
41
  }
39
42
 
40
- // ========== GAME OBJECT CLASS WITH GRAVITY ==========
41
- class gameObject {
42
- static objects = [];
43
+ // ========== SCENE CLASS (DEFINED FIRST) ==========
44
+ class Scene {
45
+ static scenes = [];
46
+ static activeScene = "blank";
43
47
  static started = false;
44
- static gravity = GRAVITY;
45
- static terminalVelocity = TERMINAL_VELOCITY;
46
-
47
- static render() {
48
- MALCgameObjects.forEach(o => {
49
- if (o.active) o.render();
50
- });
51
- }
52
-
48
+ static sceneHistory = [];
49
+ static historyLimit = 10;
50
+
53
51
  static update() {
54
52
  this.started = true;
55
- this.objects = MALCgameObjects;
53
+ this.scenes = MALCScene;
56
54
 
57
- // Update all active objects
58
- MALCgameObjects.forEach(o => {
59
- if (o.active) o.update();
60
- });
61
- }
62
-
63
- static initialize() {
64
- console.log("MALC gameObjects initialized");
55
+ let activeSceneFound = false;
65
56
 
66
- // Add objects to their scenes
67
- MALCgameObjects.forEach(o => {
68
- o.scenes.forEach(sceneId => {
69
- let scene = Scene.getSceneById(sceneId);
70
- if (scene && !scene.objects.includes(o)) {
71
- scene.objects.push(o);
57
+ this.scenes.forEach(S => {
58
+ if (S.id == this.activeScene) {
59
+ activeSceneFound = true;
60
+
61
+ this.scenes.forEach(s => {
62
+ if (s != S) {
63
+ s._active = false;
64
+ s.active = false;
65
+
66
+ s.objects.forEach(o => {
67
+ if (o && typeof o.active !== 'undefined') o.active = false;
68
+ });
69
+ }
70
+ });
71
+
72
+ S.active = true;
73
+
74
+ if (!S._active) {
75
+ S.activated = MALC.time.getTime();
76
+ if (typeof S.onActivate == 'function') S.onActivate();
72
77
  }
73
- });
78
+
79
+ S._active = true;
80
+ S.timeActive = MALC.time.getTime() - S.activated;
81
+
82
+ S.objects.forEach(o => {
83
+ if (o && typeof o.active !== 'undefined') o.active = true;
84
+ });
85
+
86
+ _p5.prototype.push();
87
+ if (window.camera && typeof camera.render == 'function') {
88
+ camera.render();
89
+ }
90
+
91
+ S.render();
92
+
93
+ _p5.prototype.pop();
94
+ }
74
95
  });
96
+
97
+ if (!activeSceneFound && this.activeScene != "blank") {
98
+ console.warn(`Scene "${this.activeScene}" not found, switching to blank`);
99
+ this.activeScene = "blank";
100
+ }
75
101
  }
76
102
 
77
- static getObjectByIndex(index) {
78
- return MALCgameObjects[index] || null;
103
+ static getSceneById(id) {
104
+ return MALCScene.find(scene => scene.id == id) || null;
79
105
  }
80
106
 
81
- static getActiveObjects() {
82
- return MALCgameObjects.filter(o => o.active);
107
+ static getActiveScene() {
108
+ return this.getSceneById(this.activeScene);
83
109
  }
84
110
 
85
- static getObjectsInScene(sceneId) {
86
- let scene = Scene.getSceneById(sceneId);
87
- return scene ? scene.objects : [];
111
+ static switchToScene(id, addToHistory = true) {
112
+ let scene = this.getSceneById(id);
113
+ if (scene) {
114
+ if (addToHistory && this.activeScene) {
115
+ this.sceneHistory.push(this.activeScene);
116
+ if (this.sceneHistory.length > this.historyLimit) {
117
+ this.sceneHistory.shift();
118
+ }
119
+ }
120
+ this.activeScene = id;
121
+ } else {
122
+ console.error(`Cannot switch to scene "${id}" - not found`);
123
+ }
88
124
  }
89
125
 
90
- static setGlobalGravity(value) {
91
- this.gravity = value;
126
+ static goBack() {
127
+ if (this.sceneHistory.length > 0) {
128
+ let previousScene = this.sceneHistory.pop();
129
+ this.switchToScene(previousScene, false);
130
+ return true;
131
+ }
132
+ return false;
92
133
  }
93
134
 
94
- static getGlobalGravity() {
95
- return this.gravity;
135
+ static getAllScenes() {
136
+ return [...MALCScene];
137
+ }
138
+
139
+ static getScenesWithObject(object) {
140
+ return MALCScene.filter(scene => scene.objects.includes(object));
141
+ }
142
+
143
+ static getScenesByTag(tag) {
144
+ return MALCScene.filter(scene => scene.hasTag(tag));
96
145
  }
97
146
 
98
- constructor(x = 0, y = 0, w = 20, h = 20, ...scenes) {
99
- this.id = generateId('gameObject');
100
- this.x = x;
101
- this.y = y;
102
- this.width = w;
103
- this.height = h;
104
- this.rotation = 0;
105
- this.rotationMode = "degrees";
106
- this.velocity = [0, 0];
107
- this.velocityMatrix = [0, 0];
108
- this.velocityMode = "polar";
109
- this.rvm = "unlinked";
110
-
111
- // Gravity properties
112
- this.gravity = {
113
- enabled: false,
114
- velocity: 0,
115
- grounded: false,
116
- groundTolerance: 1, // pixels
117
- mass: 1,
118
- bounce: 0, // 0 = no bounce, 1 = full bounce
119
- friction: 0.1 // ground friction
120
- };
147
+ constructor(id, backgroundColor, ...scripts) {
148
+ MALCScene.forEach(s => {
149
+ if (s.id == id || typeof id != "string") {
150
+ throw new Error(`Scenes must have unique IDs and be strings. Duplicate/Invalid ID: "${id}"`);
151
+ }
152
+ });
121
153
 
122
- this.formatting = {
123
- outline: [false, 0, "black"],
124
- color: "white",
125
- };
154
+ this.id = id;
155
+ this.backColor = backgroundColor;
156
+ this.scripts = scripts;
126
157
 
127
- this.collition = true;
158
+ this.objects = [];
159
+ this.uiPlanes = [];
128
160
 
129
- this.scripts = [];
130
- this.scenes = scenes.length < 1 ? ["blank"] : [...new Set(scenes)];
131
161
  this.active = false;
132
- this.visible = true;
133
- this.parentScene = null;
134
-
135
- this.debug = false;
136
- this.hitbox = {
137
- x: 0,
138
- y: 0,
139
- width: 0,
140
- height: 0,
141
- rotation: 0,
142
- parts: null,
143
- outline: 1,
144
- };
162
+ this._active = false;
163
+ this.activated = 0;
164
+ this.timeActive = -1;
145
165
 
146
- this.objectInstance = MALCgameObjects.length;
147
- this.lastGroundY = y;
166
+ this.tags = [];
167
+ this.paused = false;
168
+ this.transition = null;
169
+ this.onActivateCallbacks = [];
170
+ this.onDeactivateCallbacks = [];
171
+ this.onUpdateCallbacks = [];
148
172
 
149
- MALCgameObjects.push(this);
150
- }
151
-
152
- // Enable gravity for this object
153
- enableGravity() {
154
- this.gravity.enabled = true;
155
- return this;
156
- }
157
-
158
- // Disable gravity for this object
159
- disableGravity() {
160
- this.gravity.enabled = false;
161
- this.gravity.velocity = 0;
162
- return this;
163
- }
164
-
165
- // Set gravity parameters
166
- setGravity(options = {}) {
167
- if (options.enabled !== undefined) this.gravity.enabled = options.enabled;
168
- if (options.mass !== undefined) this.gravity.mass = Math.max(0.1, options.mass);
169
- if (options.bounce !== undefined) this.gravity.bounce = Math.min(1, Math.max(0, options.bounce));
170
- if (options.friction !== undefined) this.gravity.friction = Math.min(1, Math.max(0, options.friction));
171
- if (options.groundTolerance !== undefined) this.gravity.groundTolerance = options.groundTolerance;
172
- return this;
173
+ this.sceneInstance = MALCScene.length;
174
+ MALCScene.push(this);
173
175
  }
174
176
 
175
- // Apply gravity to this object
176
- applyGravity() {
177
- if (!this.gravity.enabled) return;
178
-
179
- // Apply gravity acceleration (scaled by mass)
180
- this.gravity.velocity += gameObject.gravity * this.gravity.mass;
177
+ render() {
178
+ if (this.paused) return;
181
179
 
182
- // Limit to terminal velocity
183
- this.gravity.velocity = Math.min(this.gravity.velocity, gameObject.terminalVelocity);
180
+ if (this.transition) {
181
+ this.applyTransition();
182
+ }
184
183
 
185
- // Store last position before moving
186
- let lastY = this.y;
184
+ _p5.prototype.background(this.backColor);
187
185
 
188
- // Apply vertical movement
189
- this.y += this.gravity.velocity;
186
+ this.scripts.forEach(exe => {
187
+ if (typeof exe == "function") exe(this);
188
+ });
190
189
 
191
- // Check for ground collision with other objects
192
- this.checkGroundCollision();
190
+ this.onUpdateCallbacks.forEach(cb => {
191
+ if (typeof cb == "function") cb(this);
192
+ });
193
193
 
194
- // If we just landed, stop downward velocity
195
- if (this.gravity.grounded) {
196
- this.gravity.velocity = 0;
197
-
198
- // Apply ground friction to horizontal movement
199
- if (this.gravity.friction > 0 && this.velocityMode === "polar") {
200
- this.velocity[0] *= (1 - this.gravity.friction);
201
- if (Math.abs(this.velocity[0]) < 0.01) this.velocity[0] = 0;
194
+ this.objects.forEach(o => {
195
+ if (o && typeof o.update == "function") {
196
+ o.update(true);
202
197
  }
203
- }
204
- }
205
-
206
- // Check if this object is standing on another object
207
- checkGroundCollision() {
208
- if (!this.collition || !this.gravity.enabled) return;
209
-
210
- let wasGrounded = this.gravity.grounded;
211
- this.gravity.grounded = false;
198
+ if (o && typeof o.render == "function") {
199
+ o.render();
200
+ }
201
+ });
212
202
 
213
- // Check collision with other objects in the same scene
214
- if (this.parentScene && this.parentScene.objects) {
215
- this.parentScene.objects.forEach(other => {
216
- // Skip self and inactive objects
217
- if (other.id === this.id || !other.active) return;
218
-
219
- // Only check if gravity is enabled on this object and we're moving downward
220
- if (this.gravity.velocity <= 0) return;
221
-
222
- // Check if other object is below this one
223
- let verticalDistance = (other.y - other.height/2) - (this.y + this.height/2);
224
-
225
- // If within ground tolerance and horizontally overlapping
226
- if (Math.abs(verticalDistance) <= this.gravity.groundTolerance &&
227
- this.x + this.width/2 > other.x - other.width/2 &&
228
- this.x - this.width/2 < other.x + other.width/2) {
229
-
230
- this.gravity.grounded = true;
231
- this.lastGroundY = other.y - other.height/2 - this.height/2;
232
-
233
- // Apply bounce if enabled
234
- if (this.gravity.bounce > 0 && wasGrounded === false) {
235
- this.gravity.velocity = -this.gravity.velocity * this.gravity.bounce;
236
-
237
- // If bounce velocity is very small, just set to zero
238
- if (Math.abs(this.gravity.velocity) < 0.1) {
239
- this.gravity.velocity = 0;
240
- }
241
- } else {
242
- // Position exactly on ground
243
- this.y = this.lastGroundY;
244
- }
203
+ if (typeof UIPlanes !== 'undefined' && UIPlanes.length > 0) {
204
+ UIPlanes.forEach(ui => {
205
+ if (ui && typeof ui.belongsToScene == "function" && ui.belongsToScene(this.id)) {
206
+ ui.render();
245
207
  }
246
208
  });
247
209
  }
248
- }
249
-
250
- update() {
251
- if (!this.active) return;
252
-
253
- // Apply gravity if enabled
254
- this.applyGravity();
255
-
256
- let vel = this.velocity[0];
257
- let angle = this.velocity[1];
258
210
 
259
- if (this.velocityMode == "polar") {
260
- let linked = false;
261
- if (!/unlinked/i.test(this.rvm) && /linked/i.test(this.rvm)) {
262
- this.velocity[1] = this.rotation;
263
- linked = true;
264
- }
265
-
266
- let rot = linked ?
267
- (this.rotationMode == "degrees" ? (this.rotation) : radians(this.rotation)) :
268
- (this.rotationMode == "degrees" ? (this.velocity[1]) : (this.velocity[1]));
269
-
270
- if(isNaN(rot)){
271
- vel = 0;
272
- rot = 0;
273
- }
274
-
275
- let vx = vel * cos(rot);
276
- let vy = vel * sin(rot);
277
-
278
- this.velocityMatrix = [vx, vy];
279
-
280
- // Don't apply horizontal movement if gravity is enabled and we're grounded with friction
281
- if (!(this.gravity.enabled && this.gravity.grounded && this.gravity.friction > 0)) {
282
- this.x += vx;
283
- }
284
-
285
- // Vertical movement is handled by gravity when enabled
286
- if (!this.gravity.enabled) {
287
- this.y += vy;
288
- }
289
- } else {
290
- // Cartesian velocity mode
291
- if (!(this.gravity.enabled && this.gravity.grounded && this.gravity.friction > 0)) {
292
- this.x += vel;
293
- }
294
- if (!this.gravity.enabled) {
295
- this.y += angle;
211
+ this.uiPlanes.forEach(ui => {
212
+ if (ui && typeof ui.render == "function") {
213
+ ui.render();
296
214
  }
297
- }
298
-
299
- // Update parent scene reference
300
- this.updateParentScene();
215
+ });
301
216
 
302
- MALCgameObjects[this.objectInstance] = this;
217
+ MALCScene[this.sceneInstance] = this;
303
218
  }
304
219
 
305
- render() {
306
- if (!this.active) return;
307
-
308
- this.scripts.forEach(s => {
309
- if(typeof s == "function")s(this);
310
- });
220
+ applyTransition() {
221
+ if (!this.transition || !this.transition.active) return;
311
222
 
312
- let outline = this.formatting.outline;
313
- let hb = this.hitbox;
223
+ this.transition.progress += 1/60;
314
224
 
315
- // Draw debug hitbox if enabled
316
- if (this.debug) {
317
- push();
318
- translate(this.x, this.y);
319
- rectMode(CENTER);
320
- if (this.rotationMode == "degrees") angleMode(DEGREES);
321
- rotate(this.rotation + hb.rotation);
322
-
323
- stroke("#00FF27");
324
- strokeWeight(hb.outline);
325
- noFill();
326
- rect(hb.x, hb.y, this.width + hb.width, this.height + hb.height);
327
-
328
- // Draw gravity indicator if enabled
329
- if (this.gravity.enabled) {
330
- stroke(0, 255, 0, 100);
331
- line(0, 0, 0, this.gravity.velocity * 5);
332
- }
333
-
334
- pop();
225
+ if (this.transition.progress >= this.transition.duration) {
226
+ this.transition.active = false;
227
+ this.transition = null;
228
+ return;
335
229
  }
336
230
 
337
- if (!this.visible) return;
338
-
339
- push();
340
- translate(this.x, this.y);
341
- rectMode(CENTER);
342
- if (this.rotationMode == "degrees") angleMode(DEGREES);
343
- rotate(this.rotation);
231
+ let t = this.transition.progress / this.transition.duration;
344
232
 
345
- if (outline[0]) {
346
- strokeWeight(outline[1]);
347
- stroke(outline[2]);
348
- } else {
349
- noStroke();
233
+ _p5.prototype.push();
234
+ switch(this.transition.type) {
235
+ case "fade":
236
+ _p5.prototype.fill(0, 255 * (1 - t));
237
+ _p5.prototype.rect(0, 0, _p5.prototype.width, _p5.prototype.height);
238
+ break;
239
+ case "slide":
240
+ _p5.prototype.translate(_p5.prototype.width * (1 - t), 0);
241
+ break;
350
242
  }
351
-
352
- fill(this.formatting.color);
353
- rect(0, 0, this.width, this.height);
354
- pop();
355
- }
356
-
357
- // ========== HELPFUL METHODS ==========
358
-
359
- belongsToScene(sceneId) {
360
- return this.scenes.includes(sceneId);
243
+ _p5.prototype.pop();
361
244
  }
362
245
 
363
- addToScene(sceneId) {
364
- if (!this.scenes.includes(sceneId)) {
365
- this.scenes.push(sceneId);
366
- let scene = Scene.getSceneById(sceneId);
367
- if (scene && !scene.objects.includes(this)) {
368
- scene.objects.push(this);
246
+ addObject(object) {
247
+ if (object && !this.objects.includes(object)) {
248
+ this.objects.push(object);
249
+ if (typeof object.addToScene == "function") {
250
+ object.addToScene(this.id);
369
251
  }
370
252
  }
371
253
  return this;
372
254
  }
373
255
 
374
- removeFromScene(sceneId) {
375
- this.scenes = this.scenes.filter(id => id != sceneId);
376
- let scene = Scene.getSceneById(sceneId);
377
- if (scene) {
378
- scene.objects = scene.objects.filter(obj => obj != this);
256
+ addObjects(objects) {
257
+ objects.forEach(obj => this.addObject(obj));
258
+ return this;
259
+ }
260
+
261
+ removeObject(object) {
262
+ this.objects = this.objects.filter(obj => obj != object);
263
+ if (object && typeof object.removeFromScene == "function") {
264
+ object.removeFromScene(this.id);
379
265
  }
380
266
  return this;
381
267
  }
382
268
 
383
- removeFromAllScenes() {
384
- this.scenes.forEach(sceneId => {
385
- let scene = Scene.getSceneById(sceneId);
386
- if (scene) {
387
- scene.objects = scene.objects.filter(obj => obj != this);
269
+ clearObjects() {
270
+ this.objects.forEach(obj => {
271
+ if (obj && typeof obj.removeFromScene == "function") {
272
+ obj.removeFromScene(this.id);
388
273
  }
389
274
  });
390
- this.scenes = [];
275
+ this.objects = [];
391
276
  return this;
392
277
  }
393
278
 
394
- updateParentScene() {
395
- if (Scene.activeScene) {
396
- let activeScene = Scene.getSceneById(Scene.activeScene);
397
- if (activeScene && this.belongsToScene(activeScene.id)) {
398
- this.parentScene = activeScene;
399
- }
279
+ getObjects(filter) {
280
+ if (typeof filter == "function") {
281
+ return this.objects.filter(filter);
282
+ } else if (filter == "button") {
283
+ return this.objects.filter(obj => obj instanceof Button);
284
+ } else if (filter == "gameObject") {
285
+ return this.objects.filter(obj => obj instanceof gameObject);
400
286
  }
287
+ return this.objects;
401
288
  }
402
289
 
403
- distanceTo(target) {
404
- let dx = target.x !== undefined ? target.x - this.x : target[0] - this.x;
405
- let dy = target.y !== undefined ? target.y - this.y : target[1] - this.y;
406
- return Math.sqrt(dx * dx + dy * dy);
407
- }
408
-
409
- collidesWith(other) {
410
- return (this.x < other.x + other.width &&
411
- this.x + this.width > other.x &&
412
- this.y < other.y + other.height &&
413
- this.y + this.height > other.y);
290
+ getObjectById(id) {
291
+ return this.objects.find(obj => obj && obj.id == id);
414
292
  }
415
293
 
416
- directionTo(x, y, err = 0.5) {
417
- let pa = [x - this.x, y - this.y];
418
-
419
- let angle = (x && y) ? atan(pa[1]/pa[0]) : this.rotation;
420
- var quads = [
421
- pa[0] < -err && pa[1] > err,
422
- pa[0] < -err && pa[1] < -err,
423
- pa[0] > err && pa[1] > err,
424
- pa[0] > err && pa[1] < -err,
425
- (pa[0] < err && pa[0] > -err),
426
- (pa[1] < err && pa[1] > -err),
427
- ];
428
-
429
- let da = (Math.atan(pa[1]/pa[0])*180)/Math.PI;
430
-
431
- if((pa[1] > -err && pa[1] < err) && (pa[0] > -err && pa[0] < err)){
432
- angle = NaN;
433
- } else if(quads[0]){
434
- angle = da+180;
435
- } else if(quads[1]){
436
- angle = da+180;
437
- } else if(quads[2]){
438
- angle = da;
439
- } else if(quads[3]){
440
- angle = da;
441
- } else if(quads[4]){
442
- if(pa[1] < -err) {
443
- angle = -90;
444
- } else if(pa[1] > err) {
445
- angle = 90;
446
- }
447
- } else if(quads[5]){
448
- if(pa[0] < -err) {
449
- angle = 180;
450
- } else if(pa[0] > err) {
451
- angle = 0;
294
+ addUIPlane(uiPlane) {
295
+ if (uiPlane && !this.uiPlanes.includes(uiPlane)) {
296
+ this.uiPlanes.push(uiPlane);
297
+ if (typeof uiPlane.addToScene == "function") {
298
+ uiPlane.addToScene(this.id);
452
299
  }
453
300
  }
454
-
455
- return angle;
456
- }
457
-
458
- pointTo(target) {
459
- this.rotation = this.directionTo(target);
460
301
  return this;
461
302
  }
462
303
 
463
- setPosition(x, y) {
464
- this.x = x;
465
- this.y = y;
304
+ removeUIPlane(uiPlane) {
305
+ this.uiPlanes = this.uiPlanes.filter(ui => ui != uiPlane);
306
+ if (uiPlane && typeof uiPlane.removeFromScene == "function") {
307
+ uiPlane.removeFromScene(this.id);
308
+ }
466
309
  return this;
467
310
  }
468
311
 
469
- move(dx, dy) {
470
- this.x += dx;
471
- this.y += dy;
472
- return this;
473
- }
474
-
475
- setVelocity(speed, x, y, err = 0.5){
476
- let angle = this.directionTo(x,y,err);
477
-
478
- if(isNaN(angle)){
479
- angle = 0;
480
- speed = 0;
481
- }
482
-
483
- this.velocity = [speed, angle];
484
- return this.velocity;
312
+ clearUIPlanes() {
313
+ this.uiPlanes.forEach(ui => {
314
+ if (ui && typeof ui.removeFromScene == "function") {
315
+ ui.removeFromScene(this.id);
316
+ }
317
+ });
318
+ this.uiPlanes = [];
319
+ return this;
485
320
  }
486
321
 
487
- destroy() {
488
- this.removeFromAllScenes();
489
- let index = MALCgameObjects.indexOf(this);
490
- if (index > -1) {
491
- MALCgameObjects.splice(index, 1);
322
+ addScript(script) {
323
+ if (typeof script == "function" && !this.scripts.includes(script)) {
324
+ this.scripts.push(script);
492
325
  }
326
+ return this;
493
327
  }
494
328
 
495
- clone() {
496
- let clone = new gameObject(this.x, this.y, this.width, this.height, ...this.scenes);
497
- clone.rotation = this.rotation;
498
- clone.rotationMode = this.rotationMode;
499
- clone.velocity = [...this.velocity];
500
- clone.velocityMode = this.velocityMode;
501
- clone.rvm = this.rvm;
502
- clone.formatting = JSON.parse(JSON.stringify(this.formatting));
503
- clone.gravity = JSON.parse(JSON.stringify(this.gravity));
504
- clone.debug = this.debug;
505
- clone.hitbox = JSON.parse(JSON.stringify(this.hitbox));
506
- return clone;
329
+ removeScript(script) {
330
+ this.scripts = this.scripts.filter(s => s != script);
331
+ return this;
507
332
  }
508
-
509
- screenToWorld(screenX, screenY) {
510
- if (window.camera && typeof camera.screenToWorld == "function") {
511
- return camera.screenToWorld(screenX, screenY);
512
- }
513
- return { x: screenX, y: screenY };
333
+
334
+ clearScripts() {
335
+ this.scripts = [];
336
+ return this;
514
337
  }
515
338
 
516
- isOnScreen() {
517
- if (!window.camera) return true;
518
-
519
- let cameraPos = camera.getOrientation();
520
- let screenRight = cameraPos[0] + camera.width;
521
- let screenBottom = cameraPos[1] + camera.height;
522
-
523
- return (this.x + this.width/2 > cameraPos[0] &&
524
- this.x - this.width/2 < screenRight &&
525
- this.y + this.height/2 > cameraPos[1] &&
526
- this.y - this.height/2 < screenBottom);
339
+ onActivate(callback) {
340
+ if (typeof callback == "function") {
341
+ this.onActivateCallbacks.push(callback);
342
+ }
343
+ return this;
527
344
  }
528
- }
529
-
530
- // ========== BUTTON CLASS ==========
531
- class Button extends gameObject {
532
- static buttons = [];
533
345
 
534
- static updateButton() {
535
- this.startedButtons = true;
536
- this.buttons = MALCbuttons;
537
-
538
- this.buttons.forEach(b => {
539
- if (!b.active) return;
540
-
541
- b.isHovered = b.events.hover();
542
-
543
- if (MALCbuttons.every(b => !b.events.hover())) {
544
- cursor();
545
- } else if (b.isHovered) {
546
- cursor(b.cursor);
547
- }
548
- });
346
+ onDeactivate(callback) {
347
+ if (typeof callback == "function") {
348
+ this.onDeactivateCallbacks.push(callback);
349
+ }
350
+ return this;
549
351
  }
550
352
 
551
- static getButtonByIndex(index) {
552
- return MALCbuttons[index] || null;
353
+ onUpdate(callback) {
354
+ if (typeof callback == "function") {
355
+ this.onUpdateCallbacks.push(callback);
356
+ }
357
+ return this;
553
358
  }
554
359
 
555
- static getHoveredButton() {
556
- return MALCbuttons.find(b => b.active && b.events.hover());
360
+ pause() {
361
+ this.paused = true;
362
+ return this;
557
363
  }
558
364
 
559
- static getPressedButton() {
560
- return MALCbuttons.find(b => b.active && b.events.pressed());
365
+ resume() {
366
+ this.paused = false;
367
+ return this;
561
368
  }
562
369
 
563
- constructor(x = 0, y = 0, w = 20, h = 20, displayText = "Button", ...scenes) {
564
- super(x, y, w, h, ...scenes);
565
-
566
- this.formatting.button = {
567
- hover: 220,
568
- clicked: 190,
569
- text: {
570
- color: 0,
571
- size: 14,
572
- style:"normal",
573
- display: displayText,
574
- },
575
- colors: {
576
- normal: 255,
577
- hover: 220,
578
- pressed: 190,
579
- disabled: 150
580
- }
581
- };
582
-
583
- this.cursor = "pointer";
584
- this.isHovered = false;
585
- this.isPressed = false;
586
- this.isDisabled = false;
587
- this.clickCooldown = 100;
588
- this.lastClickTime = 0;
589
- this.cooldownActive = false;
590
-
591
- this.events = {
592
- hover: (err = 0) => {
593
- return !this.isDisabled && (
594
- mouse.x < this.x + this.width / 2 + err &&
595
- mouse.x > this.x - (this.width / 2 + err) &&
596
- mouse.y < this.y + this.height / 2 + err &&
597
- mouse.y > this.y - (this.height / 2 + err)
598
- );
599
- },
600
- pressed: () => {
601
- return this.events.hover() && mouse.down;
602
- },
603
- clicked: () => {
604
- let wasPressed = this.wasPressed;
605
- let isHovering = this.events.hover();
606
- let mouseReleased = !mouse.down && wasPressed;
607
-
608
- this.wasPressed = mouse.down && isHovering;
609
-
610
- return mouseReleased && isHovering;
611
- }
370
+ setTransition(type, duration = 1.0) {
371
+ this.transition = {
372
+ type: type,
373
+ duration: duration,
374
+ progress: 0,
375
+ active: true
612
376
  };
613
-
614
- this.wasPressed = false;
615
- this.onClick = null;
616
-
617
- this.buttonIndex = MALCbuttons.length;
618
- MALCbuttons.push(this);
619
- }
620
-
621
- update(boolean) {
622
- super.update(boolean);
623
-
624
- if (this.cooldownActive) {
625
- let currentTime = Date.now();
626
- if (currentTime - this.lastClickTime >= this.clickCooldown) {
627
- this.cooldownActive = false;
628
- }
629
- }
630
-
631
- this.isHovered = this.events.hover();
632
- this.isPressed = this.events.pressed();
633
-
634
- if (this.events.clicked() && this.onClick && !this.isDisabled && !this.cooldownActive) {
635
- this.onClick(this);
636
- this.lastClickTime = Date.now();
637
- this.cooldownActive = true;
638
- }
639
-
640
- MALCbuttons[this.buttonIndex] = this;
377
+ return this;
641
378
  }
642
-
643
- render() {
644
- if (!this.active) return;
645
-
646
- let btnFormat = this.formatting.button;
647
- let buttonColor;
648
-
649
- if (this.isDisabled) {
650
- buttonColor = btnFormat.colors.disabled;
651
- } else if (this.isPressed) {
652
- buttonColor = btnFormat.colors.pressed;
653
- } else if (this.isHovered) {
654
- buttonColor = btnFormat.colors.hover;
655
- } else {
656
- buttonColor = btnFormat.colors.normal;
379
+
380
+ addTag(tag) {
381
+ if (!this.tags.includes(tag)) {
382
+ this.tags.push(tag);
657
383
  }
658
-
659
- let originalColor = this.formatting.color;
660
- this.formatting.color = buttonColor;
661
-
662
- super.render();
663
-
664
- if(!this.visible) return;
665
-
666
- push();
667
- translate(this.x, this.y);
668
- if (this.rotationMode == "degrees") angleMode(DEGREES);
669
- rotate(this.rotation);
670
-
671
- textStyle(btnFormat.text.style);
672
- textSize(btnFormat.text.size);
673
- fill(btnFormat.text.color);
674
- coloredText(btnFormat.text.display, 0, 0, CENTER, CENTER);
675
- pop();
676
-
677
- this.formatting.color = originalColor;
384
+ return this;
678
385
  }
679
386
 
680
- // ========== BUTTON-SPECIFIC HELPER METHODS ==========
681
-
682
- setText(text) {
683
- this.formatting.button.text.display = text;
387
+ removeTag(tag) {
388
+ this.tags = this.tags.filter(t => t != tag);
684
389
  return this;
685
390
  }
686
391
 
687
- getRGBFromColor(colorInput) {
688
- let c = color(colorInput);
689
- return [red(c), green(c), blue(c)];
392
+ hasTag(tag) {
393
+ return this.tags.includes(tag);
690
394
  }
691
395
 
692
- getBrightness(colorInput) {
693
- let rgb = this.getRGBFromColor(colorInput);
694
- return 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
396
+ reset() {
397
+ this.clearObjects();
398
+ this.clearUIPlanes();
399
+ this.clearScripts();
400
+ this.onActivateCallbacks = [];
401
+ this.onDeactivateCallbacks = [];
402
+ this.onUpdateCallbacks = [];
403
+ this.tags = [];
404
+ this.paused = false;
405
+ this.transition = null;
406
+ this.timeActive = 0;
407
+ return this;
695
408
  }
696
409
 
697
- scaleColor(baseColor, scaleFactor) {
698
- let rgb = this.getRGBFromColor(baseColor);
699
- let scaledRGB = rgb.map(val => constrain(val * scaleFactor, 0, 255));
700
- return color(scaledRGB);
410
+ destroy() {
411
+ let index = MALCScene.indexOf(this);
412
+ if (index > -1) {
413
+ MALCScene.splice(index, 1);
414
+ }
415
+
416
+ this.clearObjects();
417
+ this.clearUIPlanes();
418
+
419
+ if (Scene.activeScene == this.id) {
420
+ Scene.activeScene = "blank";
421
+ }
701
422
  }
702
423
 
703
- setColors(normal, hover = null, pressed = null, disabled = null) {
704
- if (hover === null && pressed === null) {
705
- let originalNormal = this.formatting.button.colors.normal;
706
- let originalHover = this.formatting.button.colors.hover;
707
- let originalPressed = this.formatting.button.colors.pressed;
708
-
709
- let normalBrightness = this.getBrightness(originalNormal);
710
- let hoverBrightness = this.getBrightness(originalHover);
711
- let pressedBrightness = this.getBrightness(originalPressed);
712
-
713
- let hoverScale = normalBrightness !== 0 ? hoverBrightness / normalBrightness : 0.86;
714
- let pressedScale = normalBrightness !== 0 ? pressedBrightness / normalBrightness : 0.75;
715
-
716
- hover = this.scaleColor(normal, hoverScale);
717
- pressed = this.scaleColor(normal, pressedScale);
718
- } else {
719
- hover = color(hover);
720
- pressed = color(pressed);
721
- }
722
-
723
- let normalColor = color(normal);
724
- let disabledColor = disabled !== null ? color(disabled) : null;
725
-
726
- this.formatting.button.colors.normal = normalColor;
727
- this.formatting.button.colors.hover = hover;
728
- this.formatting.button.colors.pressed = pressed;
729
- if (disabledColor !== null) {
730
- this.formatting.button.colors.disabled = disabledColor;
731
- }
732
-
733
- return this;
734
- }
735
-
736
- textStyle(color, size) {
737
- if (color !== undefined) this.formatting.button.text.color = color;
738
- if (size !== undefined) this.formatting.button.text.size = size;
739
- return this;
740
- }
741
-
742
- Disable(disabled = true) {
743
- this.isDisabled = disabled;
744
- return this;
424
+ clone(newId) {
425
+ let clone = new Scene(newId || this.id + "_copy", this.backColor, ...this.scripts);
426
+ clone.objects = [...this.objects];
427
+ clone.uiPlanes = [...this.uiPlanes];
428
+ clone.tags = [...this.tags];
429
+ return clone;
745
430
  }
746
431
 
747
- click(call) {
748
- if(typeof call == "function" && this.events.pressed()){
749
- call();
750
- }
751
- return this.events.clicked();
432
+ getInfo() {
433
+ return {
434
+ id: this.id,
435
+ active: this.active,
436
+ timeActive: this.timeActive,
437
+ objectCount: this.objects.length,
438
+ uiPlaneCount: this.uiPlanes.length,
439
+ scriptCount: this.scripts.length,
440
+ tags: this.tags,
441
+ paused: this.paused
442
+ };
752
443
  }
753
444
  }
754
445
 
755
- // ========== SCENE CLASS ==========
756
- class Scene {
757
- static scenes = [];
758
- static activeScene = "blank";
446
+ // ========== GAME OBJECT CLASS WITH GRAVITY ==========
447
+ class gameObject {
448
+ static objects = [];
759
449
  static started = false;
760
- static sceneHistory = [];
761
- static historyLimit = 10;
762
-
450
+ static gravity = GRAVITY;
451
+ static terminalVelocity = TERMINAL_VELOCITY;
452
+
453
+ static render() {
454
+ MALCgameObjects.forEach(o => {
455
+ if (o.active) o.render();
456
+ });
457
+ }
458
+
763
459
  static update() {
764
460
  this.started = true;
765
- this.scenes = MALCScene;
461
+ this.objects = MALCgameObjects;
766
462
 
767
- let activeSceneFound = false;
463
+ // Update all active objects
464
+ MALCgameObjects.forEach(o => {
465
+ if (o.active) o.update();
466
+ });
467
+ }
468
+
469
+ static initialize() {
470
+ console.log("MALC gameObjects initialized");
768
471
 
769
- this.scenes.forEach(S => {
770
- if (S.id == this.activeScene) {
771
- activeSceneFound = true;
772
-
773
- this.scenes.forEach(s => {
774
- if (s != S) {
775
- s._active = false;
776
- s.active = false;
777
-
778
- s.objects.forEach(o => {
779
- if (o && typeof o.active !== 'undefined') o.active = false;
780
- });
781
- }
782
- });
783
-
784
- S.active = true;
785
-
786
- if (!S._active) {
787
- S.activated = MALC.time.getTime();
788
- if (typeof S.onActivate == 'function') S.onActivate();
789
- }
790
-
791
- S._active = true;
792
- S.timeActive = MALC.time.getTime() - S.activated;
793
-
794
- S.objects.forEach(o => {
795
- if (o && typeof o.active !== 'undefined') o.active = true;
796
- });
797
-
798
- push();
799
- if (window.camera && typeof camera.render == 'function') {
800
- camera.render();
472
+ // Add objects to their scenes
473
+ MALCgameObjects.forEach(o => {
474
+ o.scenes.forEach(sceneId => {
475
+ let scene = Scene.getSceneById(sceneId);
476
+ if (scene && !scene.objects.includes(o)) {
477
+ scene.objects.push(o);
801
478
  }
802
-
803
- S.render();
804
-
805
- pop();
806
- }
479
+ });
807
480
  });
808
-
809
- if (!activeSceneFound && this.activeScene != "blank") {
810
- console.warn(`Scene "${this.activeScene}" not found, switching to blank`);
811
- this.activeScene = "blank";
812
- }
813
481
  }
814
482
 
815
- static getSceneById(id) {
816
- return MALCScene.find(scene => scene.id == id) || null;
483
+ static getObjectByIndex(index) {
484
+ return MALCgameObjects[index] || null;
817
485
  }
818
486
 
819
- static getActiveScene() {
820
- return this.getSceneById(this.activeScene);
487
+ static getActiveObjects() {
488
+ return MALCgameObjects.filter(o => o.active);
821
489
  }
822
490
 
823
- static switchToScene(id, addToHistory = true) {
824
- let scene = this.getSceneById(id);
825
- if (scene) {
826
- if (addToHistory && this.activeScene) {
827
- this.sceneHistory.push(this.activeScene);
828
- if (this.sceneHistory.length > this.historyLimit) {
829
- this.sceneHistory.shift();
830
- }
831
- }
832
- this.activeScene = id;
833
- } else {
834
- console.error(`Cannot switch to scene "${id}" - not found`);
835
- }
491
+ static getObjectsInScene(sceneId) {
492
+ let scene = Scene.getSceneById(sceneId);
493
+ return scene ? scene.objects : [];
836
494
  }
837
495
 
838
- static goBack() {
839
- if (this.sceneHistory.length > 0) {
840
- let previousScene = this.sceneHistory.pop();
841
- this.switchToScene(previousScene, false);
842
- return true;
843
- }
844
- return false;
496
+ static setGlobalGravity(value) {
497
+ this.gravity = value;
845
498
  }
846
499
 
847
- static getAllScenes() {
848
- return [...MALCScene];
500
+ static getGlobalGravity() {
501
+ return this.gravity;
502
+ }
503
+
504
+ constructor(x = 0, y = 0, w = 20, h = 20, ...scenes) {
505
+ this.id = generateId('gameObject');
506
+ this.x = x;
507
+ this.y = y;
508
+ this.width = w;
509
+ this.height = h;
510
+ this.rotation = 0;
511
+ this.rotationMode = "degrees";
512
+ this.velocity = [0, 0];
513
+ this.velocityMatrix = [0, 0];
514
+ this.velocityMode = "polar";
515
+ this.rvm = "unlinked";
516
+
517
+ // Gravity properties
518
+ this.gravity = {
519
+ enabled: false,
520
+ velocity: 0,
521
+ grounded: false,
522
+ groundTolerance: 1, // pixels
523
+ mass: 1,
524
+ bounce: 0, // 0 = no bounce, 1 = full bounce
525
+ friction: 0.1 // ground friction
526
+ };
527
+
528
+ this.formatting = {
529
+ outline: [false, 0, "black"],
530
+ color: "white",
531
+ };
532
+
533
+ this.collition = true;
534
+
535
+ this.scripts = [];
536
+ this.scenes = scenes.length < 1 ? ["blank"] : [...new Set(scenes)];
537
+ this.active = false;
538
+ this.visible = true;
539
+ this.parentScene = null;
540
+
541
+ this.debug = false;
542
+ this.hitbox = {
543
+ x: 0,
544
+ y: 0,
545
+ width: 0,
546
+ height: 0,
547
+ rotation: 0,
548
+ parts: null,
549
+ outline: 1,
550
+ };
551
+
552
+ this.objectInstance = MALCgameObjects.length;
553
+ this.lastGroundY = y;
554
+
555
+ MALCgameObjects.push(this);
849
556
  }
850
557
 
851
- static getScenesWithObject(object) {
852
- return MALCScene.filter(scene => scene.objects.includes(object));
558
+ // Enable gravity for this object
559
+ enableGravity() {
560
+ this.gravity.enabled = true;
561
+ return this;
853
562
  }
854
563
 
855
- static getScenesByTag(tag) {
856
- return MALCScene.filter(scene => scene.hasTag(tag));
564
+ // Disable gravity for this object
565
+ disableGravity() {
566
+ this.gravity.enabled = false;
567
+ this.gravity.velocity = 0;
568
+ return this;
857
569
  }
858
-
859
- constructor(id, backgroundColor, ...scripts) {
860
- MALCScene.forEach(s => {
861
- if (s.id == id || typeof id != "string") {
862
- throw new Error(`Scenes must have unique IDs and be strings. Duplicate/Invalid ID: "${id}"`);
570
+
571
+ // Set gravity parameters
572
+ setGravity(options = {}) {
573
+ if (options.enabled !== undefined) this.gravity.enabled = options.enabled;
574
+ if (options.mass !== undefined) this.gravity.mass = Math.max(0.1, options.mass);
575
+ if (options.bounce !== undefined) this.gravity.bounce = Math.min(1, Math.max(0, options.bounce));
576
+ if (options.friction !== undefined) this.gravity.friction = Math.min(1, Math.max(0, options.friction));
577
+ if (options.groundTolerance !== undefined) this.gravity.groundTolerance = options.groundTolerance;
578
+ return this;
579
+ }
580
+
581
+ // Apply gravity to this object
582
+ applyGravity() {
583
+ if (!this.gravity.enabled) return;
584
+
585
+ // Apply gravity acceleration (scaled by mass)
586
+ this.gravity.velocity += gameObject.gravity * this.gravity.mass;
587
+
588
+ // Limit to terminal velocity
589
+ this.gravity.velocity = Math.min(this.gravity.velocity, gameObject.terminalVelocity);
590
+
591
+ // Store last position before moving
592
+ let lastY = this.y;
593
+
594
+ // Apply vertical movement
595
+ this.y += this.gravity.velocity;
596
+
597
+ // Check for ground collision with other objects
598
+ this.checkGroundCollision();
599
+
600
+ // If we just landed, stop downward velocity
601
+ if (this.gravity.grounded) {
602
+ this.gravity.velocity = 0;
603
+
604
+ // Apply ground friction to horizontal movement
605
+ if (this.gravity.friction > 0 && this.velocityMode === "polar") {
606
+ this.velocity[0] *= (1 - this.gravity.friction);
607
+ if (Math.abs(this.velocity[0]) < 0.01) this.velocity[0] = 0;
863
608
  }
864
- });
609
+ }
610
+ }
611
+
612
+ // Check if this object is standing on another object
613
+ checkGroundCollision() {
614
+ if (!this.collition || !this.gravity.enabled) return;
865
615
 
866
- this.id = id;
867
- this.backColor = backgroundColor;
868
- this.scripts = scripts;
616
+ let wasGrounded = this.gravity.grounded;
617
+ this.gravity.grounded = false;
869
618
 
870
- this.objects = [];
871
- this.uiPlanes = [];
619
+ // Check collision with other objects in the same scene
620
+ if (this.parentScene && this.parentScene.objects) {
621
+ this.parentScene.objects.forEach(other => {
622
+ // Skip self and inactive objects
623
+ if (other.id === this.id || !other.active) return;
624
+
625
+ // Only check if gravity is enabled on this object and we're moving downward
626
+ if (this.gravity.velocity <= 0) return;
627
+
628
+ // Check if other object is below this one
629
+ let verticalDistance = (other.y - other.height/2) - (this.y + this.height/2);
630
+
631
+ // If within ground tolerance and horizontally overlapping
632
+ if (Math.abs(verticalDistance) <= this.gravity.groundTolerance &&
633
+ this.x + this.width/2 > other.x - other.width/2 &&
634
+ this.x - this.width/2 < other.x + other.width/2) {
635
+
636
+ this.gravity.grounded = true;
637
+ this.lastGroundY = other.y - other.height/2 - this.height/2;
638
+
639
+ // Apply bounce if enabled
640
+ if (this.gravity.bounce > 0 && wasGrounded === false) {
641
+ this.gravity.velocity = -this.gravity.velocity * this.gravity.bounce;
642
+
643
+ // If bounce velocity is very small, just set to zero
644
+ if (Math.abs(this.gravity.velocity) < 0.1) {
645
+ this.gravity.velocity = 0;
646
+ }
647
+ } else {
648
+ // Position exactly on ground
649
+ this.y = this.lastGroundY;
650
+ }
651
+ }
652
+ });
653
+ }
654
+ }
655
+
656
+ update() {
657
+ if (!this.active) return;
872
658
 
873
- this.active = false;
874
- this._active = false;
875
- this.activated = 0;
876
- this.timeActive = -1;
659
+ // Apply gravity if enabled
660
+ this.applyGravity();
877
661
 
878
- this.tags = [];
879
- this.paused = false;
880
- this.transition = null;
881
- this.onActivateCallbacks = [];
882
- this.onDeactivateCallbacks = [];
883
- this.onUpdateCallbacks = [];
662
+ let vel = this.velocity[0];
663
+ let angle = this.velocity[1];
664
+
665
+ if (this.velocityMode == "polar") {
666
+ let linked = false;
667
+ if (!/unlinked/i.test(this.rvm) && /linked/i.test(this.rvm)) {
668
+ this.velocity[1] = this.rotation;
669
+ linked = true;
670
+ }
671
+
672
+ let rot = linked ?
673
+ (this.rotationMode == "degrees" ? (this.rotation) : _p5.prototype.radians(this.rotation)) :
674
+ (this.rotationMode == "degrees" ? (this.velocity[1]) : (this.velocity[1]));
675
+
676
+ if(isNaN(rot)){
677
+ vel = 0;
678
+ rot = 0;
679
+ }
680
+
681
+ let vx = vel * _p5.prototype.cos(rot);
682
+ let vy = vel * _p5.prototype.sin(rot);
683
+
684
+ this.velocityMatrix = [vx, vy];
685
+
686
+ // Don't apply horizontal movement if gravity is enabled and we're grounded with friction
687
+ if (!(this.gravity.enabled && this.gravity.grounded && this.gravity.friction > 0)) {
688
+ this.x += vx;
689
+ }
690
+
691
+ // Vertical movement is handled by gravity when enabled
692
+ if (!this.gravity.enabled) {
693
+ this.y += vy;
694
+ }
695
+ } else {
696
+ // Cartesian velocity mode
697
+ if (!(this.gravity.enabled && this.gravity.grounded && this.gravity.friction > 0)) {
698
+ this.x += vel;
699
+ }
700
+ if (!this.gravity.enabled) {
701
+ this.y += angle;
702
+ }
703
+ }
884
704
 
885
- this.sceneInstance = MALCScene.length;
886
- MALCScene.push(this);
705
+ // Update parent scene reference
706
+ this.updateParentScene();
707
+
708
+ MALCgameObjects[this.objectInstance] = this;
887
709
  }
888
710
 
889
711
  render() {
890
- if (this.paused) return;
891
-
892
- if (this.transition) {
893
- this.applyTransition();
894
- }
895
-
896
- background(this.backColor);
712
+ if (!this.active) return;
897
713
 
898
- this.scripts.forEach(exe => {
899
- if (typeof exe == "function") exe(this);
714
+ this.scripts.forEach(s => {
715
+ if(typeof s == "function")s(this);
900
716
  });
901
717
 
902
- this.onUpdateCallbacks.forEach(cb => {
903
- if (typeof cb == "function") cb(this);
904
- });
718
+ let outline = this.formatting.outline;
719
+ let hb = this.hitbox;
905
720
 
906
- this.objects.forEach(o => {
907
- if (o && typeof o.update == "function") {
908
- o.update(true);
909
- }
910
- if (o && typeof o.render == "function") {
911
- o.render();
721
+ // Draw debug hitbox if enabled
722
+ if (this.debug) {
723
+ _p5.prototype.push();
724
+ _p5.prototype.translate(this.x, this.y);
725
+ _p5.prototype.rectMode(_p5.prototype.CENTER);
726
+ if (this.rotationMode == "degrees") _p5.prototype.angleMode(_p5.prototype.DEGREES);
727
+ _p5.prototype.rotate(this.rotation + hb.rotation);
728
+
729
+ _p5.prototype.stroke("#00FF27");
730
+ _p5.prototype.strokeWeight(hb.outline);
731
+ _p5.prototype.noFill();
732
+ _p5.prototype.rect(hb.x, hb.y, this.width + hb.width, this.height + hb.height);
733
+
734
+ // Draw gravity indicator if enabled
735
+ if (this.gravity.enabled) {
736
+ _p5.prototype.stroke(0, 255, 0, 100);
737
+ _p5.prototype.line(0, 0, 0, this.gravity.velocity * 5);
912
738
  }
913
- });
914
-
915
- if (typeof UIPlanes !== 'undefined' && UIPlanes.length > 0) {
916
- UIPlanes.forEach(ui => {
917
- if (ui && typeof ui.belongsToScene == "function" && ui.belongsToScene(this.id)) {
918
- ui.render();
919
- }
920
- });
739
+
740
+ _p5.prototype.pop();
921
741
  }
922
742
 
923
- this.uiPlanes.forEach(ui => {
924
- if (ui && typeof ui.render == "function") {
925
- ui.render();
926
- }
927
- });
928
-
929
- MALCScene[this.sceneInstance] = this;
930
- }
931
-
932
- applyTransition() {
933
- if (!this.transition || !this.transition.active) return;
743
+ if (!this.visible) return;
934
744
 
935
- this.transition.progress += 1/60;
745
+ _p5.prototype.push();
746
+ _p5.prototype.translate(this.x, this.y);
747
+ _p5.prototype.rectMode(_p5.prototype.CENTER);
748
+ if (this.rotationMode == "degrees") _p5.prototype.angleMode(_p5.prototype.DEGREES);
749
+ _p5.prototype.rotate(this.rotation);
936
750
 
937
- if (this.transition.progress >= this.transition.duration) {
938
- this.transition.active = false;
939
- this.transition = null;
940
- return;
751
+ if (outline[0]) {
752
+ _p5.prototype.strokeWeight(outline[1]);
753
+ _p5.prototype.stroke(outline[2]);
754
+ } else {
755
+ _p5.prototype.noStroke();
941
756
  }
942
757
 
943
- let t = this.transition.progress / this.transition.duration;
944
-
945
- push();
946
- switch(this.transition.type) {
947
- case "fade":
948
- fill(0, 255 * (1 - t));
949
- rect(0, 0, width, height);
950
- break;
951
- case "slide":
952
- translate(width * (1 - t), 0);
953
- break;
954
- }
955
- pop();
758
+ _p5.prototype.fill(this.formatting.color);
759
+ _p5.prototype.rect(0, 0, this.width, this.height);
760
+ _p5.prototype.pop();
956
761
  }
762
+
763
+ // ========== HELPFUL METHODS ==========
957
764
 
958
- addObject(object) {
959
- if (object && !this.objects.includes(object)) {
960
- this.objects.push(object);
961
- if (typeof object.addToScene == "function") {
962
- object.addToScene(this.id);
963
- }
964
- }
965
- return this;
765
+ belongsToScene(sceneId) {
766
+ return this.scenes.includes(sceneId);
966
767
  }
967
768
 
968
- addObjects(objects) {
969
- objects.forEach(obj => this.addObject(obj));
769
+ addToScene(sceneId) {
770
+ if (!this.scenes.includes(sceneId)) {
771
+ this.scenes.push(sceneId);
772
+ let scene = Scene.getSceneById(sceneId);
773
+ if (scene && !scene.objects.includes(this)) {
774
+ scene.objects.push(this);
775
+ }
776
+ }
970
777
  return this;
971
778
  }
972
779
 
973
- removeObject(object) {
974
- this.objects = this.objects.filter(obj => obj != object);
975
- if (object && typeof object.removeFromScene == "function") {
976
- object.removeFromScene(this.id);
780
+ removeFromScene(sceneId) {
781
+ this.scenes = this.scenes.filter(id => id != sceneId);
782
+ let scene = Scene.getSceneById(sceneId);
783
+ if (scene) {
784
+ scene.objects = scene.objects.filter(obj => obj != this);
977
785
  }
978
786
  return this;
979
787
  }
980
788
 
981
- clearObjects() {
982
- this.objects.forEach(obj => {
983
- if (obj && typeof obj.removeFromScene == "function") {
984
- obj.removeFromScene(this.id);
789
+ removeFromAllScenes() {
790
+ this.scenes.forEach(sceneId => {
791
+ let scene = Scene.getSceneById(sceneId);
792
+ if (scene) {
793
+ scene.objects = scene.objects.filter(obj => obj != this);
985
794
  }
986
795
  });
987
- this.objects = [];
796
+ this.scenes = [];
988
797
  return this;
989
798
  }
990
799
 
991
- getObjects(filter) {
992
- if (typeof filter == "function") {
993
- return this.objects.filter(filter);
994
- } else if (filter == "button") {
995
- return this.objects.filter(obj => obj instanceof Button);
996
- } else if (filter == "gameObject") {
997
- return this.objects.filter(obj => obj instanceof gameObject);
800
+ updateParentScene() {
801
+ if (Scene.activeScene) {
802
+ let activeScene = Scene.getSceneById(Scene.activeScene);
803
+ if (activeScene && this.belongsToScene(activeScene.id)) {
804
+ this.parentScene = activeScene;
805
+ }
998
806
  }
999
- return this.objects;
1000
807
  }
1001
808
 
1002
- getObjectById(id) {
1003
- return this.objects.find(obj => obj && obj.id == id);
809
+ distanceTo(target) {
810
+ let dx = target.x !== undefined ? target.x - this.x : target[0] - this.x;
811
+ let dy = target.y !== undefined ? target.y - this.y : target[1] - this.y;
812
+ return Math.sqrt(dx * dx + dy * dy);
1004
813
  }
1005
814
 
1006
- addUIPlane(uiPlane) {
1007
- if (uiPlane && !this.uiPlanes.includes(uiPlane)) {
1008
- this.uiPlanes.push(uiPlane);
1009
- if (typeof uiPlane.addToScene == "function") {
1010
- uiPlane.addToScene(this.id);
815
+ collidesWith(other) {
816
+ return (this.x < other.x + other.width &&
817
+ this.x + this.width > other.x &&
818
+ this.y < other.y + other.height &&
819
+ this.y + this.height > other.y);
820
+ }
821
+
822
+ directionTo(x, y, err = 0.5) {
823
+ let pa = [x - this.x, y - this.y];
824
+
825
+ let angle = (x && y) ? _p5.prototype.atan(pa[1]/pa[0]) : this.rotation;
826
+ var quads = [
827
+ pa[0] < -err && pa[1] > err,
828
+ pa[0] < -err && pa[1] < -err,
829
+ pa[0] > err && pa[1] > err,
830
+ pa[0] > err && pa[1] < -err,
831
+ (pa[0] < err && pa[0] > -err),
832
+ (pa[1] < err && pa[1] > -err),
833
+ ];
834
+
835
+ let da = (_p5.prototype.atan(pa[1]/pa[0]) * 180)/Math.PI;
836
+
837
+ if((pa[1] > -err && pa[1] < err) && (pa[0] > -err && pa[0] < err)){
838
+ angle = NaN;
839
+ } else if(quads[0]){
840
+ angle = da+180;
841
+ } else if(quads[1]){
842
+ angle = da+180;
843
+ } else if(quads[2]){
844
+ angle = da;
845
+ } else if(quads[3]){
846
+ angle = da;
847
+ } else if(quads[4]){
848
+ if(pa[1] < -err) {
849
+ angle = -90;
850
+ } else if(pa[1] > err) {
851
+ angle = 90;
852
+ }
853
+ } else if(quads[5]){
854
+ if(pa[0] < -err) {
855
+ angle = 180;
856
+ } else if(pa[0] > err) {
857
+ angle = 0;
1011
858
  }
1012
859
  }
860
+
861
+ return angle;
862
+ }
863
+
864
+ pointTo(target) {
865
+ this.rotation = this.directionTo(target);
1013
866
  return this;
1014
867
  }
1015
868
 
1016
- removeUIPlane(uiPlane) {
1017
- this.uiPlanes = this.uiPlanes.filter(ui => ui != uiPlane);
1018
- if (uiPlane && typeof uiPlane.removeFromScene == "function") {
1019
- uiPlane.removeFromScene(this.id);
1020
- }
869
+ setPosition(x, y) {
870
+ this.x = x;
871
+ this.y = y;
872
+ return this;
873
+ }
874
+
875
+ move(dx, dy) {
876
+ this.x += dx;
877
+ this.y += dy;
1021
878
  return this;
1022
879
  }
1023
880
 
1024
- clearUIPlanes() {
1025
- this.uiPlanes.forEach(ui => {
1026
- if (ui && typeof ui.removeFromScene == "function") {
1027
- ui.removeFromScene(this.id);
881
+ setVelocity(speed, x, y, err = 0.5){
882
+ let angle = this.directionTo(x,y,err);
883
+
884
+ if(isNaN(angle)){
885
+ angle = 0;
886
+ speed = 0;
887
+ }
888
+
889
+ this.velocity = [speed, angle];
890
+ return this.velocity;
891
+ }
892
+
893
+ destroy() {
894
+ this.removeFromAllScenes();
895
+ let index = MALCgameObjects.indexOf(this);
896
+ if (index > -1) {
897
+ MALCgameObjects.splice(index, 1);
898
+ }
899
+ }
900
+
901
+ clone() {
902
+ let clone = new gameObject(this.x, this.y, this.width, this.height, ...this.scenes);
903
+ clone.rotation = this.rotation;
904
+ clone.rotationMode = this.rotationMode;
905
+ clone.velocity = [...this.velocity];
906
+ clone.velocityMode = this.velocityMode;
907
+ clone.rvm = this.rvm;
908
+ clone.formatting = JSON.parse(JSON.stringify(this.formatting));
909
+ clone.gravity = JSON.parse(JSON.stringify(this.gravity));
910
+ clone.debug = this.debug;
911
+ clone.hitbox = JSON.parse(JSON.stringify(this.hitbox));
912
+ return clone;
913
+ }
914
+
915
+ screenToWorld(screenX, screenY) {
916
+ if (window.camera && typeof camera.screenToWorld == "function") {
917
+ return camera.screenToWorld(screenX, screenY);
918
+ }
919
+ return { x: screenX, y: screenY };
920
+ }
921
+
922
+ isOnScreen() {
923
+ if (!window.camera) return true;
924
+
925
+ let cameraPos = camera.getOrientation();
926
+ let screenRight = cameraPos[0] + camera.width;
927
+ let screenBottom = cameraPos[1] + camera.height;
928
+
929
+ return (this.x + this.width/2 > cameraPos[0] &&
930
+ this.x - this.width/2 < screenRight &&
931
+ this.y + this.height/2 > cameraPos[1] &&
932
+ this.y - this.height/2 < screenBottom);
933
+ }
934
+ }
935
+
936
+ // ========== BUTTON CLASS ==========
937
+ class Button extends gameObject {
938
+ static buttons = [];
939
+
940
+ static updateButton() {
941
+ this.startedButtons = true;
942
+ this.buttons = MALCbuttons;
943
+
944
+ this.buttons.forEach(b => {
945
+ if (!b.active) return;
946
+
947
+ b.isHovered = b.events.hover();
948
+
949
+ if (MALCbuttons.every(b => !b.events.hover())) {
950
+ _p5.prototype.cursor();
951
+ } else if (b.isHovered) {
952
+ _p5.prototype.cursor(b.cursor);
1028
953
  }
1029
954
  });
1030
- this.uiPlanes = [];
1031
- return this;
1032
955
  }
1033
956
 
1034
- addScript(script) {
1035
- if (typeof script == "function" && !this.scripts.includes(script)) {
1036
- this.scripts.push(script);
1037
- }
1038
- return this;
957
+ static getButtonByIndex(index) {
958
+ return MALCbuttons[index] || null;
1039
959
  }
1040
960
 
1041
- removeScript(script) {
1042
- this.scripts = this.scripts.filter(s => s != script);
1043
- return this;
961
+ static getHoveredButton() {
962
+ return MALCbuttons.find(b => b.active && b.events.hover());
1044
963
  }
1045
964
 
1046
- clearScripts() {
1047
- this.scripts = [];
1048
- return this;
965
+ static getPressedButton() {
966
+ return MALCbuttons.find(b => b.active && b.events.pressed());
1049
967
  }
1050
968
 
1051
- onActivate(callback) {
1052
- if (typeof callback == "function") {
1053
- this.onActivateCallbacks.push(callback);
969
+ constructor(x = 0, y = 0, w = 20, h = 20, displayText = "Button", ...scenes) {
970
+ super(x, y, w, h, ...scenes);
971
+
972
+ this.formatting.button = {
973
+ hover: 220,
974
+ clicked: 190,
975
+ text: {
976
+ color: 0,
977
+ size: 14,
978
+ style:"normal",
979
+ display: displayText,
980
+ },
981
+ colors: {
982
+ normal: 255,
983
+ hover: 220,
984
+ pressed: 190,
985
+ disabled: 150
986
+ }
987
+ };
988
+
989
+ this.cursor = "pointer";
990
+ this.isHovered = false;
991
+ this.isPressed = false;
992
+ this.isDisabled = false;
993
+ this.clickCooldown = 100;
994
+ this.lastClickTime = 0;
995
+ this.cooldownActive = false;
996
+
997
+ this.events = {
998
+ hover: (err = 0) => {
999
+ return !this.isDisabled && (
1000
+ window.mouse.x < this.x + this.width / 2 + err &&
1001
+ window.mouse.x > this.x - (this.width / 2 + err) &&
1002
+ window.mouse.y < this.y + this.height / 2 + err &&
1003
+ window.mouse.y > this.y - (this.height / 2 + err)
1004
+ );
1005
+ },
1006
+ pressed: () => {
1007
+ return this.events.hover() && window.mouse.down;
1008
+ },
1009
+ clicked: () => {
1010
+ let wasPressed = this.wasPressed;
1011
+ let isHovering = this.events.hover();
1012
+ let mouseReleased = !window.mouse.down && wasPressed;
1013
+
1014
+ this.wasPressed = window.mouse.down && isHovering;
1015
+
1016
+ return mouseReleased && isHovering;
1017
+ }
1018
+ };
1019
+
1020
+ this.wasPressed = false;
1021
+ this.onClick = null;
1022
+
1023
+ this.buttonIndex = MALCbuttons.length;
1024
+ MALCbuttons.push(this);
1025
+ }
1026
+
1027
+ update(boolean) {
1028
+ super.update(boolean);
1029
+
1030
+ if (this.cooldownActive) {
1031
+ let currentTime = Date.now();
1032
+ if (currentTime - this.lastClickTime >= this.clickCooldown) {
1033
+ this.cooldownActive = false;
1034
+ }
1054
1035
  }
1055
- return this;
1036
+
1037
+ this.isHovered = this.events.hover();
1038
+ this.isPressed = this.events.pressed();
1039
+
1040
+ if (this.events.clicked() && this.onClick && !this.isDisabled && !this.cooldownActive) {
1041
+ this.onClick(this);
1042
+ this.lastClickTime = Date.now();
1043
+ this.cooldownActive = true;
1044
+ }
1045
+
1046
+ MALCbuttons[this.buttonIndex] = this;
1056
1047
  }
1057
-
1058
- onDeactivate(callback) {
1059
- if (typeof callback == "function") {
1060
- this.onDeactivateCallbacks.push(callback);
1048
+
1049
+ render() {
1050
+ if (!this.active) return;
1051
+
1052
+ let btnFormat = this.formatting.button;
1053
+ let buttonColor;
1054
+
1055
+ if (this.isDisabled) {
1056
+ buttonColor = btnFormat.colors.disabled;
1057
+ } else if (this.isPressed) {
1058
+ buttonColor = btnFormat.colors.pressed;
1059
+ } else if (this.isHovered) {
1060
+ buttonColor = btnFormat.colors.hover;
1061
+ } else {
1062
+ buttonColor = btnFormat.colors.normal;
1061
1063
  }
1062
- return this;
1064
+
1065
+ let originalColor = this.formatting.color;
1066
+ this.formatting.color = buttonColor;
1067
+
1068
+ super.render();
1069
+
1070
+ if(!this.visible) return;
1071
+
1072
+ _p5.prototype.push();
1073
+ _p5.prototype.translate(this.x, this.y);
1074
+ if (this.rotationMode == "degrees") _p5.prototype.angleMode(_p5.prototype.DEGREES);
1075
+ _p5.prototype.rotate(this.rotation);
1076
+
1077
+ _p5.prototype.textStyle(btnFormat.text.style);
1078
+ _p5.prototype.textSize(btnFormat.text.size);
1079
+ _p5.prototype.fill(btnFormat.text.color);
1080
+ _p5.prototype.coloredText(btnFormat.text.display, 0, 0, _p5.prototype.CENTER, _p5.prototype.CENTER);
1081
+ _p5.prototype.pop();
1082
+
1083
+ this.formatting.color = originalColor;
1063
1084
  }
1064
1085
 
1065
- onUpdate(callback) {
1066
- if (typeof callback == "function") {
1067
- this.onUpdateCallbacks.push(callback);
1068
- }
1086
+ // ========== BUTTON-SPECIFIC HELPER METHODS ==========
1087
+
1088
+ setText(text) {
1089
+ this.formatting.button.text.display = text;
1069
1090
  return this;
1070
1091
  }
1071
1092
 
1072
- pause() {
1073
- this.paused = true;
1074
- return this;
1093
+ getRGBFromColor(colorInput) {
1094
+ let c = _p5.prototype.color(colorInput);
1095
+ return [_p5.prototype.red(c), _p5.prototype.green(c), _p5.prototype.blue(c)];
1075
1096
  }
1076
1097
 
1077
- resume() {
1078
- this.paused = false;
1079
- return this;
1098
+ getBrightness(colorInput) {
1099
+ let rgb = this.getRGBFromColor(colorInput);
1100
+ return 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
1080
1101
  }
1081
1102
 
1082
- setTransition(type, duration = 1.0) {
1083
- this.transition = {
1084
- type: type,
1085
- duration: duration,
1086
- progress: 0,
1087
- active: true
1088
- };
1089
- return this;
1103
+ scaleColor(baseColor, scaleFactor) {
1104
+ let rgb = this.getRGBFromColor(baseColor);
1105
+ let scaledRGB = rgb.map(val => _p5.prototype.constrain(val * scaleFactor, 0, 255));
1106
+ return _p5.prototype.color(scaledRGB);
1090
1107
  }
1091
1108
 
1092
- addTag(tag) {
1093
- if (!this.tags.includes(tag)) {
1094
- this.tags.push(tag);
1109
+ setColors(normal, hover = null, pressed = null, disabled = null) {
1110
+ if (hover === null && pressed === null) {
1111
+ let originalNormal = this.formatting.button.colors.normal;
1112
+ let originalHover = this.formatting.button.colors.hover;
1113
+ let originalPressed = this.formatting.button.colors.pressed;
1114
+
1115
+ let normalBrightness = this.getBrightness(originalNormal);
1116
+ let hoverBrightness = this.getBrightness(originalHover);
1117
+ let pressedBrightness = this.getBrightness(originalPressed);
1118
+
1119
+ let hoverScale = normalBrightness !== 0 ? hoverBrightness / normalBrightness : 0.86;
1120
+ let pressedScale = normalBrightness !== 0 ? pressedBrightness / normalBrightness : 0.75;
1121
+
1122
+ hover = this.scaleColor(normal, hoverScale);
1123
+ pressed = this.scaleColor(normal, pressedScale);
1124
+ } else {
1125
+ hover = _p5.prototype.color(hover);
1126
+ pressed = _p5.prototype.color(pressed);
1127
+ }
1128
+
1129
+ let normalColor = _p5.prototype.color(normal);
1130
+ let disabledColor = disabled !== null ? _p5.prototype.color(disabled) : null;
1131
+
1132
+ this.formatting.button.colors.normal = normalColor;
1133
+ this.formatting.button.colors.hover = hover;
1134
+ this.formatting.button.colors.pressed = pressed;
1135
+ if (disabledColor !== null) {
1136
+ this.formatting.button.colors.disabled = disabledColor;
1095
1137
  }
1138
+
1096
1139
  return this;
1097
1140
  }
1098
1141
 
1099
- removeTag(tag) {
1100
- this.tags = this.tags.filter(t => t != tag);
1142
+ textStyle(color, size) {
1143
+ if (color !== undefined) this.formatting.button.text.color = color;
1144
+ if (size !== undefined) this.formatting.button.text.size = size;
1101
1145
  return this;
1102
1146
  }
1103
1147
 
1104
- hasTag(tag) {
1105
- return this.tags.includes(tag);
1106
- }
1107
-
1108
- reset() {
1109
- this.clearObjects();
1110
- this.clearUIPlanes();
1111
- this.clearScripts();
1112
- this.onActivateCallbacks = [];
1113
- this.onDeactivateCallbacks = [];
1114
- this.onUpdateCallbacks = [];
1115
- this.tags = [];
1116
- this.paused = false;
1117
- this.transition = null;
1118
- this.timeActive = 0;
1148
+ Disable(disabled = true) {
1149
+ this.isDisabled = disabled;
1119
1150
  return this;
1120
1151
  }
1121
1152
 
1122
- destroy() {
1123
- let index = MALCScene.indexOf(this);
1124
- if (index > -1) {
1125
- MALCScene.splice(index, 1);
1126
- }
1127
-
1128
- this.clearObjects();
1129
- this.clearUIPlanes();
1130
-
1131
- if (Scene.activeScene == this.id) {
1132
- Scene.activeScene = "blank";
1153
+ click(call) {
1154
+ if(typeof call == "function" && this.events.pressed()){
1155
+ call();
1133
1156
  }
1134
- }
1135
-
1136
- clone(newId) {
1137
- let clone = new Scene(newId || this.id + "_copy", this.backColor, ...this.scripts);
1138
- clone.objects = [...this.objects];
1139
- clone.uiPlanes = [...this.uiPlanes];
1140
- clone.tags = [...this.tags];
1141
- return clone;
1142
- }
1143
-
1144
- getInfo() {
1145
- return {
1146
- id: this.id,
1147
- active: this.active,
1148
- timeActive: this.timeActive,
1149
- objectCount: this.objects.length,
1150
- uiPlaneCount: this.uiPlanes.length,
1151
- scriptCount: this.scripts.length,
1152
- tags: this.tags,
1153
- paused: this.paused
1154
- };
1157
+ return this.events.clicked();
1155
1158
  }
1156
1159
  }
1157
1160
 
@@ -1351,20 +1354,20 @@ class UIPlane {
1351
1354
  }
1352
1355
 
1353
1356
  render() {
1354
- push();
1357
+ _p5.prototype.push();
1355
1358
 
1356
1359
  this.applyOrientation();
1357
1360
  this.applyTextFormatting();
1358
1361
 
1359
1362
  if (this.formatting.objectScale !== 1) {
1360
- scale(this.formatting.objectScale);
1363
+ _p5.prototype.scale(this.formatting.objectScale);
1361
1364
  }
1362
1365
 
1363
1366
  if (typeof this.executable == "function") {
1364
1367
  this.executable(this);
1365
1368
  }
1366
1369
 
1367
- pop();
1370
+ _p5.prototype.pop();
1368
1371
  }
1369
1372
 
1370
1373
  applyOrientation() {
@@ -1378,21 +1381,21 @@ class UIPlane {
1378
1381
  } else {
1379
1382
  cameraPos = [window.camera.x || 0, window.camera.y || 0];
1380
1383
  }
1381
- translate(cameraPos[0] + offsetX, cameraPos[1] + offsetY);
1384
+ _p5.prototype.translate(cameraPos[0] + offsetX, cameraPos[1] + offsetY);
1382
1385
  } else {
1383
- translate(offsetX, offsetY);
1386
+ _p5.prototype.translate(offsetX, offsetY);
1384
1387
  }
1385
1388
  } else if (mode.toLowerCase() == "screen") {
1386
- translate(offsetX, offsetY);
1389
+ _p5.prototype.translate(offsetX, offsetY);
1387
1390
  } else if (mode.includes(",")) {
1388
1391
  try {
1389
1392
  let coords = mode.split(",").map(Number);
1390
1393
  if (coords.length >= 2) {
1391
1394
  if (window.camera && typeof window.camera.worldToScreen == "function") {
1392
1395
  let screenPos = window.camera.worldToScreen(coords[0], coords[1]);
1393
- translate(screenPos.x + offsetX, screenPos.y + offsetY);
1396
+ _p5.prototype.translate(screenPos.x + offsetX, screenPos.y + offsetY);
1394
1397
  } else {
1395
- translate(coords[0] + offsetX, coords[1] + offsetY);
1398
+ _p5.prototype.translate(coords[0] + offsetX, coords[1] + offsetY);
1396
1399
  }
1397
1400
  }
1398
1401
  } catch (e) {
@@ -1402,46 +1405,46 @@ class UIPlane {
1402
1405
  }
1403
1406
 
1404
1407
  applyTextFormatting() {
1405
- textSize(this.formatting.txt.base);
1406
- fill(this.formatting.txt.color);
1407
- textAlign(LEFT, TOP);
1408
+ _p5.prototype.textSize(this.formatting.txt.base);
1409
+ _p5.prototype.fill(this.formatting.txt.color);
1410
+ _p5.prototype.textAlign(_p5.prototype.LEFT, _p5.prototype.TOP);
1408
1411
  }
1409
1412
 
1410
1413
  drawText(str, x, y, hAlign = LEFT, vAlign = TOP) {
1411
- push();
1414
+ _p5.prototype.push();
1412
1415
 
1413
1416
  if (str.startsWith("[title]")) {
1414
- textSize(this.formatting.txt.title);
1417
+ _p5.prototype.textSize(this.formatting.txt.title);
1415
1418
  str = str.replace("[title]", "");
1416
1419
  } else if (str.startsWith("[heading]")) {
1417
- textSize(this.formatting.txt.heading);
1420
+ _p5.prototype.textSize(this.formatting.txt.heading);
1418
1421
  str = str.replace("[heading]", "");
1419
1422
  } else if (str.startsWith("[subtitle]")) {
1420
- textSize(this.formatting.txt.subtitle);
1423
+ _p5.prototype.textSize(this.formatting.txt.subtitle);
1421
1424
  str = str.replace("[subtitle]", "");
1422
1425
  } else {
1423
- textSize(this.formatting.txt.base);
1426
+ _p5.prototype.textSize(this.formatting.txt.base);
1424
1427
  }
1425
1428
 
1426
- fill(this.formatting.txt.color);
1427
- textAlign(hAlign, vAlign);
1428
- text(str, x, y);
1429
+ _p5.prototype.fill(this.formatting.txt.color);
1430
+ _p5.prototype.textAlign(hAlign, vAlign);
1431
+ _p5.prototype.text(str, x, y);
1429
1432
 
1430
- pop();
1433
+ _p5.prototype.pop();
1431
1434
  }
1432
1435
 
1433
1436
  drawButton(button, x, y) {
1434
- push();
1437
+ _p5.prototype.push();
1435
1438
 
1436
1439
  if (this.formatting.objectScale !== 1) {
1437
- scale(this.formatting.objectScale);
1440
+ _p5.prototype.scale(this.formatting.objectScale);
1438
1441
  }
1439
1442
 
1440
1443
  if (button && typeof button.render == "function") {
1441
1444
  button.render();
1442
1445
  }
1443
1446
 
1444
- pop();
1447
+ _p5.prototype.pop();
1445
1448
  }
1446
1449
 
1447
1450
  belongsToScene(sceneId) {
@@ -1591,7 +1594,7 @@ class Camera {
1591
1594
  }
1592
1595
 
1593
1596
  let [translateX, translateY] = this.getOrientation();
1594
- translate(-translateX, -translateY);
1597
+ _p5.prototype.translate(-translateX, -translateY);
1595
1598
  }
1596
1599
 
1597
1600
  unlink() {
@@ -2130,14 +2133,14 @@ class GameController {
2130
2133
  back: null,
2131
2134
  primary: null,
2132
2135
  secondary: null,
2133
- leftbumber: null, // Fixed: changed from leftBumber
2134
- rightbumber: null, // Fixed: changed from rightBumber
2135
- lefttrigger: null, // Fixed: changed from leftTrigger
2136
- righttrigger: null, // Fixed: changed from rightTrigger
2136
+ leftbumber: null,
2137
+ rightbumber: null,
2138
+ lefttrigger: null,
2139
+ righttrigger: null,
2137
2140
  view: null,
2138
2141
  menu: null,
2139
- leftstick: null, // Fixed: changed from leftStick
2140
- rightstick: null, // Fixed: changed from rightStick
2142
+ leftstick: null,
2143
+ rightstick: null,
2141
2144
  up: null,
2142
2145
  down: null,
2143
2146
  left: null,
@@ -2366,7 +2369,7 @@ const helpDocs = {
2366
2369
  // Game Engine Overview
2367
2370
  overview: `
2368
2371
  MALC Game Engine - A comprehensive 2D game engine for p5.js
2369
- Version: 1.0.0
2372
+ Version: 1.0.1
2370
2373
 
2371
2374
  Core Features:
2372
2375
  - Scene management system
@@ -2520,8 +2523,8 @@ const helpDocs = {
2520
2523
  isButtonPressed: "Check button by index",
2521
2524
  getButtonValue: "Get analog button value"
2522
2525
  },
2523
- buttonNames: ["select", "back", "primary", "secondary", "leftBumber", "rightBumber",
2524
- "leftTrigger", "rightTrigger", "view", "menu", "leftStick", "rightStick",
2526
+ buttonNames: ["select", "back", "primary", "secondary", "leftbumber", "rightbumber",
2527
+ "lefttrigger", "righttrigger", "view", "menu", "leftstick", "rightstick",
2525
2528
  "up", "down", "left", "right", "home"]
2526
2529
  }
2527
2530
  },
@@ -2542,10 +2545,10 @@ const helpDocs = {
2542
2545
  }
2543
2546
 
2544
2547
  // 2. Create a scene
2545
- let gameScene = new Scene("game", 220);
2548
+ let gameScene = new MALC.Scene("game", 220);
2546
2549
 
2547
2550
  // 3. Create a game object with gravity
2548
- let player = new gameObject(100, 100, 50, 50, "game")
2551
+ let player = new MALC.gameObject(100, 100, 50, 50, "game")
2549
2552
  .enableGravity()
2550
2553
  .setGravity({ mass: 1, bounce: 0.3 });
2551
2554
 
@@ -2558,7 +2561,7 @@ const helpDocs = {
2558
2561
 
2559
2562
  // ========== MALC MAIN OBJECT ==========
2560
2563
  const MALC = {
2561
- version: "1.0.0",
2564
+ version: "1.0.1",
2562
2565
 
2563
2566
  // Core classes
2564
2567
  gameObject: gameObject,
@@ -2675,7 +2678,7 @@ const MALC = {
2675
2678
 
2676
2679
  // Initialize the engine
2677
2680
  init: function(canvasX, canvasY) {
2678
- createCanvas(canvasX, canvasY);
2681
+ _p5.prototype.createCanvas(canvasX, canvasY);
2679
2682
 
2680
2683
  this.time = new Date();
2681
2684
  this.startTime = this.time.getTime();
@@ -2693,7 +2696,7 @@ const MALC = {
2693
2696
  // Create default scenes
2694
2697
  new Scene("blank", 70);
2695
2698
  new Scene("loading", 50, function(self) {
2696
- textSize(24);
2699
+ _p5.prototype.textSize(24);
2697
2700
  let timed = (self.timeActive / 250 % 4);
2698
2701
  let dots = "";
2699
2702
 
@@ -2701,11 +2704,11 @@ const MALC = {
2701
2704
  else if (timed < 2) dots = "..";
2702
2705
  else if (timed < 3) dots = "...";
2703
2706
 
2704
- coloredText(`\\lime|Loading Game${dots}| `, 120, 200, LEFT, CENTER);
2705
- textSize(16);
2707
+ _p5.prototype.coloredText(`\\lime|Loading Game${dots}| `, 120, 200, _p5.prototype.LEFT, _p5.prototype.CENTER);
2708
+ _p5.prototype.textSize(16);
2706
2709
 
2707
2710
  let num = (Math.floor(self.timeActive / 100) / 10);
2708
- coloredText(`\\red|${ Math.round((10 - num) * 10) / 10 + ((num + "").length < 2 ? ".0" : "")}|`, 200, 225, CENTER, CENTER);
2711
+ _p5.prototype.coloredText(`\\red|${ Math.round((10 - num) * 10) / 10 + ((num + "").length < 2 ? ".0" : "")}|`, 200, 225, _p5.prototype.CENTER, _p5.prototype.CENTER);
2709
2712
  });
2710
2713
 
2711
2714
  Scene.activeScene = "loading";
@@ -2720,11 +2723,11 @@ const MALC = {
2720
2723
  this.timer = this.time - this.startTime;
2721
2724
 
2722
2725
  if (this.mouse) {
2723
- this.mouse.rawX = mouseX;
2724
- this.mouse.rawY = mouseY;
2725
- this.mouse.x = this.mouse.rawX + camera.getOrientation()[0];
2726
- this.mouse.y = this.mouse.rawY + camera.getOrientation()[1];
2727
- this.mouse.down = mouseIsPressed;
2726
+ this.mouse.rawX = _p5.prototype.mouseX;
2727
+ this.mouse.rawY = _p5.prototype.mouseY;
2728
+ this.mouse.x = this.mouse.rawX + window.camera.getOrientation()[0];
2729
+ this.mouse.y = this.mouse.rawY + window.camera.getOrientation()[1];
2730
+ this.mouse.down = _p5.prototype.mouseIsPressed;
2728
2731
  }
2729
2732
 
2730
2733
  controller.update();
@@ -2734,8 +2737,8 @@ const MALC = {
2734
2737
 
2735
2738
  this.fps = fps;
2736
2739
 
2737
- if (typeof camera.render == "function") {
2738
- camera.render();
2740
+ if (typeof window.camera.render == "function") {
2741
+ window.camera.render();
2739
2742
  }
2740
2743
  Scene.update();
2741
2744
  }