document.querySelector("*").setAttribute("style", ` *:focus { outline: none; box-shadow: none; } *{ user-select: none; } canvas { transition: 1s; } `); const _engineTransitions = Object.freeze({ "linear": (element, element1)=> { element.style.opacity = 0; element1.style.opacity = 1; }, "fade": (element, element1)=> { element.style.opacity = 0; element1.style.opacity = 1; } }); document.body.setAttribute("ondragstart", "return false"); document.body.setAttribute("ondrop", "return false"); const css = document.createElement("style"); css.textContent = ` * { user-select: none; } *:focus { outline: none; box-shadow: none; } body { margin: 0; overflow: hidden; } canvas { transition: 1s; } #splash { position: fixed; inset: 0; /* left:0; top:0; width:100%; height:100%; */ background: #111; z-index: 9999; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: opacity .6s ease, transform .6s ease; } #splash h1 { font-family: monospace; font-size: 2rem; color: #5f5; animation: splash-text 1.2s ease-in-out forwards; text-shadow: 0 0 10px #5f55; } @keyframes splash-text { 0% { color: #aaa; opacity: 0; letter-spacing: 20px; transform: translateY(40px) scale(1.1); text-shadow: none; } 60% { opacity: 1; letter-spacing: 6px; transform: translateY(-10px) scale(1); } 100% { color: #5f5; letter-spacing: 2px; } } `; document.head.appendChild(css); const splash = document.createElement("div"); splash.id = "splash"; const splashText = document.createElement("h1"); splashText.innerText = "Made in Ready2D"; splash.appendChild(splashText); document.body.appendChild(splash); splash.addEventListener("click", () => { splash.style.opacity = "0"; splash.style.transform = "scale(0.9)"; setTimeout(() => splash.remove(), 600); }); addEventListener("contextmenu", (e)=>{ e.preventDefault(); }); var _actualEngine = undefined; class Engine { constructor(data) { this.data = data || { title : "Ready2D Project", width : innerWidth, height : innerHeight, background : "#111" }; this.active = true; this.sprites = []; this.onCall = ()=>{}; this.canvas = document.createElement("canvas"); document.title = this.data.title || "Ready2D Project"; this.canvas.width = this.data.width || innerWidth; this.canvas.height = this.data.height || innerHeight; this.canvas.style.backgroundColor = this.data.background || "#111"; this.canvas.style.position = "fixed"; this.canvas.style.left = "0px"; this.canvas.style.top = "0px"; this.canvas.style.width = "100%"; this.canvas.style.height = "100%"; this.canvas.style.zIndex = "-1"; this.ctx = this.canvas.getContext("2d"); this.tick = ()=> { requestAnimationFrame(this.tick); this.onCall(); this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height); if(true) { for(let x in this.sprites) { this.ctx.fillStyle = this.sprites[x].color; if(Camera.point == this) { if(this.sprites[x].color) this.ctx.fillRect(this.canvas.width/2 - this.sprites[x].width/2, this.canvas.height/2 - this.sprites[x].height/2, this.sprites[x].width, this.sprites[x].height); if(this.sprites[x].sprite) this.ctx.drawImage(this.sprites[x].img, this.canvas.width/2 - this.sprites[x].width/2, this.canvas.height/2 - this.sprites[x].height/2, this.sprites[x].width, this.sprites[x].height); } else { if(this.sprites[x].color) this.ctx.fillRect(this.canvas.width /2 + this.sprites[x].x - this.sprites[x].width/2 + (Camera.point.x*-1), this.canvas.height / 2 + this.sprites[x].y - this.sprites[x].height/2 + (Camera.point.y*-1), this.sprites[x].width, this.sprites[x].height); if(this.sprites[x].sprite) this.ctx.drawImage(this.sprites[x].img, this.canvas.width /2 + this.sprites[x].x - this.sprites[x].width/2 + (Camera.point.x*-1), this.canvas.height / 2 + this.sprites[x].y - this.sprites[x].height/2 + (Camera.point.y*-1), this.sprites[x].width, this.sprites[x].height); } } } } this.tick(); } add(sprite) { this.sprites.push(sprite); sprite.clone = ()=>{ let data = {}; Object.keys(sprite).forEach((key, index)=> { if(key != ("clone")) data[key] = sprite[key]; }); data.clone = true; let sclone = new Sprite(data); this.sprites.push(sclone); sclone.physic = data.physic; if(sclone.physic != undefined) data.physic.add(sclone); } } remove(sprite) { if(this.sprites.includes(sprite)) { this.sprites.splice(this.sprites.indexOf(sprite), 1); sprite = undefined; } } } class Sprite { constructor(data) { this.x = data.x || 0; this.y = data.y || 0; this.width = data.width || 50; this.height = data.height || 50; this.color = data.color || "transparent"; this.sprite = data.sprite || ""; this.flip = { x : false, y : false }, this.physic = undefined; this.img = new Image(); this.img.src = this.sprite; this.vX = 0; this.vY = 0; this.fixed = false; } } class Physics { constructor(enter) { this.gravity = enter || [0, 0.1]; this.bodies = []; this.friction = 0.90; this.elasticity = 0.7; this.add = (sprite)=> { this.bodies.push(sprite); sprite.physic = this; } } update() { for(let x in this.bodies) { if(!this.bodies[x].fixed) { let obj = this.bodies[x]; obj.vX += this.gravity[0]; obj.vY += this.gravity[1]; obj.x += obj.vX; obj.y += obj.vY; obj.vX *= this.friction; if(Math.abs(obj.vX) < 0.01) obj.vX = 0; if(Math.abs(obj.vY) < 0.01) obj.vY = 0; if(obj.vX > 0) obj.vX-=0.01; else if(obj.vX < 0) obj.vX+=0.01; } for(let y in this.bodies) { if(this.bodies[x] != this.bodies[y]) { reflect_if_colliding(this.bodies[x], this.bodies[y], this.elasticity); } } } } } const testCollision = (rectangle1, rectangle2, buffer = 2) => { const dx = rectangle1.x - rectangle2.x; const dy = rectangle1.y - rectangle2.y; const overlapX = (rectangle1.width / 2 + rectangle2.width / 2) - Math.abs(dx); const overlapY = (rectangle1.height / 2 + rectangle2.height / 2) - Math.abs(dy); return overlapX > 0 && overlapY > -buffer; }; const reflect_if_colliding = (rectangle1, rectangle2, elasticity = 0.7) => { const dx = rectangle1.x - rectangle2.x; const dy = rectangle1.y - rectangle2.y; const overlapX = (rectangle1.width/2 + rectangle2.width/2) - Math.abs(dx); const overlapY = (rectangle1.height/2 + rectangle2.height/2) - Math.abs(dy); if(!rectangle1.fixed && overlapX > 0 && overlapY > 0) { if (overlapX > overlapY) { if (dy > 0) rectangle1.y += overlapY; else rectangle1.y -= overlapY; rectangle1.vY = -rectangle1.vY * elasticity; } else { if (dx > 0) rectangle1.x += overlapX; else rectangle1.x -= overlapX; rectangle1.vX = -rectangle1.vX * elasticity; } } }; const Camera = { point : {x : 0, y : 0} } class KeyInput { constructor(key, code=()=>{}) { this.key = key; this.down = false; this.event = code; addEventListener("keypress", (e)=>{ if(e.key == this.key) this.event(); }); addEventListener("keydown", (e)=>{ if(e.key == this.key) this.down = true; }); addEventListener("keyup", (e)=>{ if(e.key == this.key) this.down = false; }); } } class Joystick { constructor(x, y, size) { this.center = { x, y }; this.radius = size / 2; this.direction = { x: 0, y: 0 }; this.activeTouches = {}; // Armazena toques ativos this.down = false; // Criar base do joystick this.base = document.createElement("div"); Object.assign(this.base.style, { position: "fixed", left: `${x - this.radius}px`, top: `${y - this.radius}px`, width: `${size}px`, height: `${size}px`, borderRadius: "50%", background: "#0005", touchAction: "none", // Evita zoom e comportamento padrão no mobile }); // Criar stick do joystick this.stick = document.createElement("div"); Object.assign(this.stick.style, { position: "absolute", left: "50%", top: "50%", width: `${this.radius}px`, height: `${this.radius}px`, borderRadius: "50%", background: "#5f5", transform: "translate(-50%, -50%)", cursor: "pointer", touchAction: "none", // Evita conflitos de toque }); this.base.appendChild(this.stick); document.body.appendChild(this.base); // Adicionar eventos de mouse e toque this.addEvents(); } addEvents() { // Listener para início do movimento (mouse ou toque) this.stick.addEventListener("pointerdown", (e) => { this.down = true; this.activeTouches[e.pointerId] = e; // Registrar toque/cursor }); // Listener para movimentação (mouse ou toque) document.addEventListener("pointermove", (e) => { if (this.down && this.activeTouches[e.pointerId]) { this.move(e); } }); // Listener para término do movimento (mouse ou toque) document.addEventListener("pointerup", (e) => { if (this.activeTouches[e.pointerId]) { delete this.activeTouches[e.pointerId]; if (Object.keys(this.activeTouches).length === 0) { this.reset(); } } }); } move(e) { let dx = e.clientX - this.center.x; let dy = e.clientY - this.center.y; // Limitar dentro do círculo let dist = Math.sqrt(dx * dx + dy * dy); if (dist > this.radius) { let angle = Math.atan2(dy, dx); dx = Math.cos(angle) * this.radius; dy = Math.sin(angle) * this.radius; } this.stick.style.left = `${50 + (dx / this.radius) * 50}%`; this.stick.style.top = `${50 + (dy / this.radius) * 50}%`; // Normalizar direção (-1 a 1) this.direction.x = (dx / this.radius).toFixed(2); this.direction.y = (dy / this.radius).toFixed(2); } reset() { this.down = false; this.stick.style.left = "50%"; this.stick.style.top = "50%"; this.direction = { x: 0, y: 0 }; } } class Gui { constructor(x, y, w, h, c) { this.x = x; this.y = y; this.width = w; this.height = h; this.color = c; this.element = document.createElement("div"); document.body.appendChild(this.element); this.element.style.position = "fixed"; this.element.style.left = this.x + "px"; this.element.style.top = this.y + "px"; this.element.style.width = this.width + "px"; this.element.style.height = this.height + "px"; this.element.style.backgroundColor = this.color; this.element.style.color = "#fff"; this.element.style.display = "flex"; this.element.style.textAlign = "center"; this.element.style.justifyContent = "center"; this.element.style.alignItems = "center"; this.element.style.fontFamily = "arial"; this.element.style.cursor = "default"; } setText(text){ this.element.textContent = text; } hide() { this.element.style.display = "none"; } show() { this.element.style.display = "block"; } onClick(event){ this.clicked = false; this.touched = false; this.touch = 0; this.element.addEventListener("mousedown",(e)=>{ event(); this.clicked = true; }); this.element.addEventListener("mouseup",(e)=>{ this.clicked = false; }); this.element.addEventListener("touchstart",(e)=>{ [...e.touches].forEach((touch) => { this.touch = touch; }); this.touched = true; event(); }); this.element.addEventListener("touchend",(e)=>{ [...e.touches].forEach((touch) => { if(this.touch == touch) { this.touched = false; this.touch = 0; } }); }); this.element.style.cursor = "pointer"; } } const makeARequest = async (url)=> { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error("Error fetching data:", error); } } const require = (caminho)=> { return new Promise((resolve, reject) => { const script = document.createElement("script"); script.src = caminho; script.onload = resolve; script.onerror = reject; document.body.appendChild(script); }); } const changeSceneTo = (scene, _animation="linear")=> { if(_actualEngine != undefined) _actualEngine.active = false; _actualEngine = scene; _actualEngine.active = true; _engineTransitions[_animation](_actualEngine.canvas, scene.canvas); document.body.childNodes.forEach((key, index)=>{ if(key.nodeName == "CANVAS") document.body.removeChild(key); }); document.body.appendChild(scene.canvas); setTimeout(()=>{ },1000); _actualEngine.onCall(); }