malc-game-engine 1.0.0 → 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 +986 -973
  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}"`);
863
- }
864
- });
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;
865
584
 
866
- this.id = id;
867
- this.backColor = backgroundColor;
868
- this.scripts = scripts;
585
+ // Apply gravity acceleration (scaled by mass)
586
+ this.gravity.velocity += gameObject.gravity * this.gravity.mass;
869
587
 
870
- this.objects = [];
871
- this.uiPlanes = [];
588
+ // Limit to terminal velocity
589
+ this.gravity.velocity = Math.min(this.gravity.velocity, gameObject.terminalVelocity);
872
590
 
873
- this.active = false;
874
- this._active = false;
875
- this.activated = 0;
876
- this.timeActive = -1;
591
+ // Store last position before moving
592
+ let lastY = this.y;
877
593
 
878
- this.tags = [];
879
- this.paused = false;
880
- this.transition = null;
881
- this.onActivateCallbacks = [];
882
- this.onDeactivateCallbacks = [];
883
- this.onUpdateCallbacks = [];
594
+ // Apply vertical movement
595
+ this.y += this.gravity.velocity;
884
596
 
885
- this.sceneInstance = MALCScene.length;
886
- MALCScene.push(this);
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;
608
+ }
609
+ }
887
610
  }
888
611
 
889
- render() {
890
- if (this.paused) return;
612
+ // Check if this object is standing on another object
613
+ checkGroundCollision() {
614
+ if (!this.collition || !this.gravity.enabled) return;
891
615
 
892
- if (this.transition) {
893
- this.applyTransition();
894
- }
616
+ let wasGrounded = this.gravity.grounded;
617
+ this.gravity.grounded = false;
895
618
 
896
- background(this.backColor);
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;
897
658
 
898
- this.scripts.forEach(exe => {
899
- if (typeof exe == "function") exe(this);
900
- });
659
+ // Apply gravity if enabled
660
+ this.applyGravity();
901
661
 
902
- this.onUpdateCallbacks.forEach(cb => {
903
- if (typeof cb == "function") cb(this);
904
- });
662
+ let vel = this.velocity[0];
663
+ let angle = this.velocity[1];
905
664
 
906
- this.objects.forEach(o => {
907
- if (o && typeof o.update == "function") {
908
- o.update(true);
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;
909
699
  }
910
- if (o && typeof o.render == "function") {
911
- o.render();
700
+ if (!this.gravity.enabled) {
701
+ this.y += angle;
912
702
  }
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
- });
921
703
  }
922
704
 
923
- this.uiPlanes.forEach(ui => {
924
- if (ui && typeof ui.render == "function") {
925
- ui.render();
926
- }
927
- });
705
+ // Update parent scene reference
706
+ this.updateParentScene();
928
707
 
929
- MALCScene[this.sceneInstance] = this;
708
+ MALCgameObjects[this.objectInstance] = this;
930
709
  }
931
710
 
932
- applyTransition() {
933
- if (!this.transition || !this.transition.active) return;
711
+ render() {
712
+ if (!this.active) return;
934
713
 
935
- this.transition.progress += 1/60;
714
+ this.scripts.forEach(s => {
715
+ if(typeof s == "function")s(this);
716
+ });
936
717
 
937
- if (this.transition.progress >= this.transition.duration) {
938
- this.transition.active = false;
939
- this.transition = null;
940
- return;
718
+ let outline = this.formatting.outline;
719
+ let hb = this.hitbox;
720
+
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);
738
+ }
739
+
740
+ _p5.prototype.pop();
941
741
  }
942
742
 
943
- let t = this.transition.progress / this.transition.duration;
743
+ if (!this.visible) return;
944
744
 
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;
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);
750
+
751
+ if (outline[0]) {
752
+ _p5.prototype.strokeWeight(outline[1]);
753
+ _p5.prototype.stroke(outline[2]);
754
+ } else {
755
+ _p5.prototype.noStroke();
954
756
  }
955
- pop();
757
+
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;
1021
872
  return this;
1022
873
  }
1023
874
 
1024
- clearUIPlanes() {
1025
- this.uiPlanes.forEach(ui => {
1026
- if (ui && typeof ui.removeFromScene == "function") {
1027
- ui.removeFromScene(this.id);
875
+ move(dx, dy) {
876
+ this.x += dx;
877
+ this.y += dy;
878
+ return this;
879
+ }
880
+
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() {
@@ -2126,24 +2129,24 @@ class GameController {
2126
2129
  this.id = "";
2127
2130
 
2128
2131
  this.binds = {
2129
- select: null,
2130
- back: null,
2131
- primary: null,
2132
- secondary: null,
2133
- leftBumber: null,
2134
- rightBumber: null,
2135
- leftTrigger: null,
2136
- rightTrigger: null,
2137
- view: null,
2138
- menu: null,
2139
- leftStick: null,
2140
- rightStick: null,
2141
- up: null,
2142
- down: null,
2143
- left: null,
2144
- right: null,
2145
- home: null,
2146
- };
2132
+ select: null,
2133
+ back: null,
2134
+ primary: null,
2135
+ secondary: null,
2136
+ leftbumber: null,
2137
+ rightbumber: null,
2138
+ lefttrigger: null,
2139
+ righttrigger: null,
2140
+ view: null,
2141
+ menu: null,
2142
+ leftstick: null,
2143
+ rightstick: null,
2144
+ up: null,
2145
+ down: null,
2146
+ left: null,
2147
+ right: null,
2148
+ home: null,
2149
+ };
2147
2150
 
2148
2151
  this.setupListeners();
2149
2152
  }
@@ -2208,28 +2211,38 @@ class GameController {
2208
2211
 
2209
2212
  getButton(name){
2210
2213
  name = name.toLowerCase();
2211
- let btn = {
2212
- select: 0,
2213
- back: 1,
2214
- primary: 2,
2215
- secondary: 3,
2216
- lefttumber: 4,
2217
- righttumber: 5,
2218
- lefttrigger: 6,
2219
- righttrigger: 7,
2220
- view: 8,
2221
- menu: 9,
2222
- leftstick: 10,
2223
- rightstick: 11,
2224
- up: 12,
2225
- down: 13,
2226
- left: 14,
2227
- right: 15,
2228
- home: 16,
2229
- }[name];
2230
-
2231
- if(btn == undefined) throw new Error(`Controller|TypeError: "${name}" isn't a valid button mapping getter!`);
2232
-
2214
+
2215
+ // Handle common variations
2216
+ const buttonMap = {
2217
+ // Primary button names
2218
+ 'select': 0, 'back': 1, 'primary': 2, 'secondary': 3,
2219
+ 'home': 16,
2220
+
2221
+ // Bumpers (with common variations)
2222
+ 'leftbumper': 4, 'leftbumber': 4, 'lb': 4,
2223
+ 'rightbumper': 5, 'rightbumber': 5, 'rb': 5,
2224
+
2225
+ // Triggers
2226
+ 'lefttrigger': 6, 'lt': 6,
2227
+ 'righttrigger': 7, 'rt': 7,
2228
+
2229
+ // System buttons
2230
+ 'view': 8, 'menu': 9,
2231
+
2232
+ // Stick clicks
2233
+ 'leftstick': 10, 'l3': 10,
2234
+ 'rightstick': 11, 'r3': 11,
2235
+
2236
+ // D-Pad
2237
+ 'up': 12, 'down': 13, 'left': 14, 'right': 15
2238
+ };
2239
+
2240
+ const btn = buttonMap[name];
2241
+
2242
+ if(btn == undefined) {
2243
+ throw new Error(`Controller|TypeError: "${name}" isn't a valid button mapping getter!`);
2244
+ }
2245
+
2233
2246
  return this.buttons[btn]?.pressed || false;
2234
2247
  }
2235
2248
  }
@@ -2356,7 +2369,7 @@ const helpDocs = {
2356
2369
  // Game Engine Overview
2357
2370
  overview: `
2358
2371
  MALC Game Engine - A comprehensive 2D game engine for p5.js
2359
- Version: 1.0.0
2372
+ Version: 1.0.1
2360
2373
 
2361
2374
  Core Features:
2362
2375
  - Scene management system
@@ -2510,8 +2523,8 @@ const helpDocs = {
2510
2523
  isButtonPressed: "Check button by index",
2511
2524
  getButtonValue: "Get analog button value"
2512
2525
  },
2513
- buttonNames: ["select", "back", "primary", "secondary", "leftBumber", "rightBumber",
2514
- "leftTrigger", "rightTrigger", "view", "menu", "leftStick", "rightStick",
2526
+ buttonNames: ["select", "back", "primary", "secondary", "leftbumber", "rightbumber",
2527
+ "lefttrigger", "righttrigger", "view", "menu", "leftstick", "rightstick",
2515
2528
  "up", "down", "left", "right", "home"]
2516
2529
  }
2517
2530
  },
@@ -2532,10 +2545,10 @@ const helpDocs = {
2532
2545
  }
2533
2546
 
2534
2547
  // 2. Create a scene
2535
- let gameScene = new Scene("game", 220);
2548
+ let gameScene = new MALC.Scene("game", 220);
2536
2549
 
2537
2550
  // 3. Create a game object with gravity
2538
- let player = new gameObject(100, 100, 50, 50, "game")
2551
+ let player = new MALC.gameObject(100, 100, 50, 50, "game")
2539
2552
  .enableGravity()
2540
2553
  .setGravity({ mass: 1, bounce: 0.3 });
2541
2554
 
@@ -2548,7 +2561,7 @@ const helpDocs = {
2548
2561
 
2549
2562
  // ========== MALC MAIN OBJECT ==========
2550
2563
  const MALC = {
2551
- version: "1.0.0",
2564
+ version: "1.0.1",
2552
2565
 
2553
2566
  // Core classes
2554
2567
  gameObject: gameObject,
@@ -2665,7 +2678,7 @@ const MALC = {
2665
2678
 
2666
2679
  // Initialize the engine
2667
2680
  init: function(canvasX, canvasY) {
2668
- createCanvas(canvasX, canvasY);
2681
+ _p5.prototype.createCanvas(canvasX, canvasY);
2669
2682
 
2670
2683
  this.time = new Date();
2671
2684
  this.startTime = this.time.getTime();
@@ -2683,7 +2696,7 @@ const MALC = {
2683
2696
  // Create default scenes
2684
2697
  new Scene("blank", 70);
2685
2698
  new Scene("loading", 50, function(self) {
2686
- textSize(24);
2699
+ _p5.prototype.textSize(24);
2687
2700
  let timed = (self.timeActive / 250 % 4);
2688
2701
  let dots = "";
2689
2702
 
@@ -2691,11 +2704,11 @@ const MALC = {
2691
2704
  else if (timed < 2) dots = "..";
2692
2705
  else if (timed < 3) dots = "...";
2693
2706
 
2694
- coloredText(`\\lime|Loading Game${dots}| `, 120, 200, LEFT, CENTER);
2695
- textSize(16);
2707
+ _p5.prototype.coloredText(`\\lime|Loading Game${dots}| `, 120, 200, _p5.prototype.LEFT, _p5.prototype.CENTER);
2708
+ _p5.prototype.textSize(16);
2696
2709
 
2697
2710
  let num = (Math.floor(self.timeActive / 100) / 10);
2698
- 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);
2699
2712
  });
2700
2713
 
2701
2714
  Scene.activeScene = "loading";
@@ -2710,11 +2723,11 @@ const MALC = {
2710
2723
  this.timer = this.time - this.startTime;
2711
2724
 
2712
2725
  if (this.mouse) {
2713
- this.mouse.rawX = mouseX;
2714
- this.mouse.rawY = mouseY;
2715
- this.mouse.x = this.mouse.rawX + camera.getOrientation()[0];
2716
- this.mouse.y = this.mouse.rawY + camera.getOrientation()[1];
2717
- 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;
2718
2731
  }
2719
2732
 
2720
2733
  controller.update();
@@ -2724,8 +2737,8 @@ const MALC = {
2724
2737
 
2725
2738
  this.fps = fps;
2726
2739
 
2727
- if (typeof camera.render == "function") {
2728
- camera.render();
2740
+ if (typeof window.camera.render == "function") {
2741
+ window.camera.render();
2729
2742
  }
2730
2743
  Scene.update();
2731
2744
  }