<style> tw-sidebar { display: none; } tw-story { position: absolute; width:100% height:100%; margin:0px; margin-right: auto; margin-left: auto; padding:0px } tw-passage { position: absolute; width:100% height:100%; margin:0px; margin-right: auto; margin-left: auto; padding:0px; } tw-link { padding:0px; color:black; display:inline-block; } a { color: white; } #background { position: absolute; min-width: 1000px; min-height: 1800px; opacity: 1; /* set the opacity to 50% */ z-index:0; top: 0px; background-size:100% 100%; background-image: url('https://cdn.discordapp.com/attachments/1077812497467310184/1078028578333151282/C1_From_above_the_city_looks_like_a_maze_of_streets_and_buildin_cae88879-f3bd-4358-bd59-057b4c864811.png '); } #header { z-index:1; } #sitepal { position: absolute; top: 1300px; left: 600px; width: 400px; height: 400px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text { border:0px solid; padding: 50px; } #marquee{ border: 1px solid; opacity:1; border-radius:20px; color:black; background: rgba(211, 211, 211, 0.2); padding: 10px; top: 800px; left: 100px; max-width: 800px; max-height:600px; } #nav { border-radius:20px; z-index:1; border:1px solid; max-width:200px; color: black; background: rgba(211, 211, 211, 0.5); padding:5px; display:inline-block; } #linkContainer { border-radius:20px; color:black; border: 1px solid; background: rgba(211, 211, 211, 0.8); padding: 5px; } </style> <div id="header" class="header"><a href="#"><h3>CHARACTERS of EMPIRE FABLE</h3></a></div> <div id="nav" class="nav"> <div id="linkContainer" class="linkContainer">[[ADALYN MECHMASTER->ADALYN MECHMASTER INTRO]]</div> <div id="linkContainer" class="linkContainer">[[ARRABELLA BRASSFORD->ARABELLA BRASSFORD INTRO]]</div> <div id="linkContainer" class="linkContainer">[[THRALGAR IRONGEAR->THRALGAR IRONGEAR INTRO]]</div> </div> <div id="background" class="background"> <iframe id="sitepal" class="sitepal" src="https://www.sitepal.com/geturl/?ss=2757274&sl=0&acc=8667722" style="border:0px #ffffff none;" name="myiFrame" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="100%" width="100%" allowfullscreen></iframe> <div id="text" class="text"> <marquee id="marquee" class="marquee" behavior="scroll" direction="up" scrollamount="2"> <p> Ocean Deep from high above, the bustling city sprawls out beneath him like a living, breathing organism. The great human city is situated at the mouth of the Misty Wine River, with its suburbs stretching out along its banks to the north. The river flows into the Sea of Storms, a vast body of water that stretches out to the horizon in all directions. From above, the city looks like a maze of streets and buildings, with the largest structures standing tall above the rest. The grandest of these structures is the Great Citadel, a massive fortress that stands on a hill overlooking the city. The Citadel's walls are high and thick, with towers and battlements at regular intervals. The flag of the Human Empire flies from its highest tower, indicating that this is the seat of power for the entire region. To the east of the Citadel lies the Market District, a sprawling collection of stalls and shops selling all manner of goods. The district is packed with people at all times of day, with merchants shouting out their prices and customers haggling for the best deals. The Market District is surrounded by the bustling streets of the Inner City, where the city's wealthier citizens reside in opulent homes and palaces. To the west of the Citadel lies the Wharf Slums, a seedy district where the city's less fortunate residents live in squalor. The streets here are narrow and winding, with ramshackle buildings leaning precariously against one another. Despite the poverty and desperation that pervades the area, the Wharf Slums are home to a thriving criminal underworld, with thieves and cutthroats operating openly in the shadows. Beyond the Wharf Slums lies the expansive port, with dozens of ships of all shapes and sizes docked at its many piers. The sea itself is a churning mass of waves and foam, with ships battling against the wind and the tides to make their way in and out of the harbor </p> </marquee> </div> </div><style> * { margin: 0; padding: 0; box-sizing: border-box; } body { overflow: hidden; background: black; } canvas { position: absolute; top: 0; left: 0; } </style> <script> const canvas = document.getElementById('canvas1'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; let gradient = ctx.createLinearGradient(0,0, canvas.width, canvas.height); gradient.addColorStop(0, 'red'); gradient.addColorStop(0.2, 'yellow'); gradient.addColorStop(0.4, 'green'); gradient.addColorStop(0.6, 'cyan'); gradient.addColorStop(0.8, 'blue'); gradient.addColorStop(1, 'magenta'); class Symbol { constructor(x, y, fontSize, canvasHeight){ this.characters = 'アァカサタナハマヤャラワガザダバパイィキシチニヒミリヰギジヂビピウゥクスツヌフムユュルグズブヅプエェケセテネヘメレヱゲゼデベペオォコソトノホモヨョロヲゴゾドボポヴッン0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; this.x = x; this.y = y; this.fontSize = fontSize; this.text = 'A'; this.canvasHeight = canvasHeight; } draw(context){ this.text = this.characters.charAt(Math.floor(Math.random()*this.characters.length)); context.fillStyle = 'white'; context.fillText(this.text, this.x * this.fontSize, this.y * this.fontSize); if (this.y * this.fontSize > this.canvasHeight && Math.random() > 0.97){ this.y = 0; } else { this.y += 0.9; } } } class Effect { constructor(canvasWidth, canvasHeight){ this.fontSize = 16; this.canvasWidth = canvasWidth; this.canvasHeight = canvasHeight; this.columns = this.canvasWidth/this.fontSize; this.symbols = []; this.#initialize(); } #initialize(){ for (let i = 0; i < this.columns; i++){ this.symbols[i] = new Symbol(i, 0, this.fontSize, this.canvasHeight); } } resize(width, height){ this.canvasWidth = width; this.canvasHeight = height; this.columns = this.canvasWidth/this.fontSize; this.symbols = []; this.#initialize(); } } const effect = new Effect(canvas.width, canvas.height); let lastTime = 0; const fps = 26; const nextFrame = 1000/fps; let timer = 0; function animate(timeStamp){ const deltaTime = timeStamp - lastTime; lastTime = timeStamp; if(timer > nextFrame){ ctx.textAlign = 'center'; ctx.fillStyle = 'rgba(0,0,0,0.05)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.font = effect.fontSize + 'px monospace'; ctx.fillStyle = gradient; effect.symbols.forEach(symbol => symbol.draw(ctx)); } else { timer += deltaTime; } requestAnimationFrame(animate); } animate(0); window.addEventListener('resize', function(){ canvas.width = window.innerWidth; canvas.height = window.innerHeight; effect.resize(canvas.width, canvas.height); gradient = createLinearGradient(0,0, canvas.width, canvas.height); gradient.addColorStop(0, 'red'); gradient.addColorStop(0.2, 'yellow'); gradient.addColorStop(0.4, 'green'); gradient.addColorStop(0.6, 'cyan'); gradient.addColorStop(0.8, 'blue'); gradient.addColorStop(1, 'magenta'); }) </script> <canvas id="canvas1"></canvas> <style> * { box-sizing: border-box; margin:0; padding: 0; } canvas { position: absolute; top: 0; left: 0; } #canvas2 { background: linear-gradient(120deg, #415411, #646d1c); } .youtubeLabel { color: white; position: absolute; padding: 15px; } .youtubeLabel a{ color: pink; } </style> <canvas id="canvas2"></canvas> <canvas id="canvas1"></canvas> <span class='youtubeLabel'>Created by <a href="https://www.youtube.com/channel/UCEqc149iR-ALYkGM6TG-7vQ" target='blank'>Frank's Laboratory</a> @2021. Full tutorial available <a href="https://youtu.be/aO1VcJ5WpKI" target='blank'>HERE</a></span> :D <script> const canvas = document.getElementById('canvas1'); const ctx = canvas.getContext('2d'); canvas.height = window.innerHeight; canvas.width = window.innerWidth; const canvas2 = document.getElementById('canvas2'); const ctx2 = canvas2.getContext('2d'); canvas2.height = window.innerHeight; canvas2.width = window.innerWidth; //ctx.globalCompositeOperation = 'destination-over'; //ctx.globalCompositeOperation = 'lighter'; ctx.fillStyle = 'yellow'; ctx.strokeStyle = 'transparent'; ctx2.fillStyle = 'transparent'; ctx2.strokeStyle = 'yellow'; ctx.lineWidth = 15; ctx2.lineWidth = 4; ctx.shadowOffsetX = 8; ctx.shadowOffsetY = 8; ctx.shadowBlur = 15; ctx.shadowColor = 'rgba(0,0,0,0.9)'; ctx2.shadowOffsetX = 2; ctx2.shadowOffsetY = 2; ctx2.shadowBlur = 0; ctx2.shadowColor = 'rgba(0,0,0,0.5)'; let hue = 0; let drawing = false; let timer = 0; let sparks = []; const mouse = { x: 0, y: 0, } class Spark { constructor(x,y){ this.x = Math.random() > 0.5 ? x + 30 : x - 30; this.y = y - 30; this.size = Math.random() * 20 + 10; this.speedX = Math.random() * 4 - 2; this.weight = Math.random() * -5 - 1; this.markedForDeletion = false; this.angle = 0; this.va = Math.random() * 0.2 - 0.1; } update(){ this.x += this.speedX; this.angle += this.va; if (this.size > 0.3) { this.size -= 0.2; } else { this.markedForDeletion = true; } this.weight += 0.1; this.y += this.weight; } draw(){ ctx2.save(); ctx2.translate(this.x, this.y); ctx2.rotate(this.angle); drawShape(0, 0, this.size, 0.6, 6, ctx2); //ctx2.fillRect(0,0,10,10) ctx2.restore(); } } function drawShape(x, y, radius, inset, n, ctx){ ctx.beginPath(); ctx.save(); ctx.translate(x, y); ctx.moveTo(0, 0 - radius); for (let i = 0; i < n; i++){ ctx.rotate(Math.PI / n); ctx.lineTo(0, 0 - (radius * inset)); ctx.rotate(Math.PI / n); ctx.lineTo(0, 0 - radius); } ctx.restore(); ctx.closePath(); ctx.stroke(); ctx.fill(); } const radius = 70; const inset = 0.4; const n = 2; drawShape(120, 120, 50, 0.6, 6, ctx); //drawShape(80, 70, 10, 1, 10, ctx); let angle = 0; window.addEventListener('mousemove', function(e){ mouse.x = e.x; mouse.y = e.y; }); window.addEventListener('mousedown', function(){ drawing = true; }); window.addEventListener('mouseup', function(){ drawing = false; }); function animate(){ ctx2.fillStyle = 'hsla(60,100%,0%,0.2)'; ctx2.fillRect(0,0,canvas.width, canvas.height); sparks.forEach(object => object.update()); sparks.forEach(object => object.draw()); sparks = sparks.filter(object => !object.markedForDeletion); console.log(sparks.length) requestAnimationFrame(animate); if (drawing){ ctx.save(); ctx.translate(mouse.x, mouse.y); ctx.rotate(angle); //ctx.fillStyle = '#E45826'; drawShape(0, 0, 50, 0.6, 6, ctx); //ctx.fillStyle = 'rgba(0,0,0,1)'; //ctx.rotate(-angle * 4); //drawShape(0, 0, radius, inset, n); //drawShape(radius/2, radius/2, 10, 1, 10, ctx); hue+=2; angle -= 0.07; ctx.restore(); timer++; //if (timer % 3 === 0) sparks.push(new Spark(mouse.x, mouse.y)) } } animate(); </script><style> body { background: linear-gradient(125deg, red, green, blue); width: 100vw; height: 100vh; overflow: hidden; } canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } #collisionCanvas { opacity: 0; } footer, article { font-family: 'Helvetica', sans-serif; position: absolute; width: 100%; color: black; font-size: 20px; text-shadow: 1px 1px 1px #737373; background: rgba(255,255,255,0.4); left: 0; bottom: 20px; width: 100%; text-align: center; height: 30px; line-height: 30px; padding-right: 20px; } footer a{ color: gold; } </style> <canvas id="collisionCanvas"></canvas> <canvas id="canvas1"></canvas> <footer>Created by <a href="https://www.youtube.com/channel/UCEqc149iR-ALYkGM6TG-7vQ" target='blank'>Frank's Laboratory</a> @2021. Full step by step tutorial available <a href="https://youtu.be/gCa0z4B-CRo" target='blank'>here</a>! </footer> </div> <script> window.addEventListener('load', function(){ const canvas = document.getElementById('canvas1'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const collisionCanvas = document.getElementById('collisionCanvas'); const collisionCtx = collisionCanvas.getContext('2d'); collisionCanvas.width = window.innerWidth; collisionCanvas.height = window.innerHeight; let score = 0; let gameOver = false; ctx.font = '50px Impact'; let timeToNextRaven = 0; let ravenInterval = 500; let lastTime = 0; let ravens = []; class Raven { constructor(){ this.spriteWidth = 271; this.spriteHeight = 194; this.sizeModifier = Math.random() * 0.6 + 0.4; this.width = this.spriteWidth * this.sizeModifier; this.height = this.spriteHeight * this.sizeModifier; this.x = canvas.width; this.y = Math.random() * (canvas.height - this.height); this.directionX = Math.random() * 5 + 3; this.directionY = Math.random() * 5 - 2.5; this.markedForDeletion = false; this.image = new Image(); this.image.src = 'https://frankslaboratory.co.uk/downloads/raven.png'; this.frame = 0; this.maxFrame = 4; this.timeSinceFlap = 0; this.flapInterval = Math.random() * 50 + 50; this.randomColors = [Math.floor(Math.random() * 255), Math.floor(Math.random() * 255), Math.floor(Math.random() * 255)]; this.color = 'rgb(' + this.randomColors[0] + ',' + this.randomColors[1] + ',' + this.randomColors[2] + ')'; this.hasTrail = Math.random() > 0.7; } update(deltatime){ if (this.y < 0 || this.y > canvas.height - this.height){ this.directionY = this.directionY * -1; } this.x -= this.directionX; this.y += this.directionY; if (this.x < 0 - this.width) this.markedForDeletion = true; this.timeSinceFlap += deltatime; if (this.timeSinceFlap > this.flapInterval){ if (this.frame > this.maxFrame) this.frame = 0; else this.frame++; this.timeSinceFlap = 0; if (this.hasTrail){ for (let i = 0; i < 5; i++){ particles.push(new Particle(this.x, this.y, this.width, this.color)); } } } if (this.x < 0 - this.width) gameOver = true; } draw(){ collisionCtx.fillStyle = this.color; collisionCtx.fillRect(this.x, this.y, this.width, this.height); ctx.drawImage(this.image, this.frame * this.spriteWidth, 0, this.spriteWidth, this.spriteHeight, this.x, this.y, this.width, this.height); } } let explosions = []; class Explosion { constructor(x, y, size){ this.image = new Image(); this.image.src = 'https://frankslaboratory.co.uk/downloads/boom.png'; this.spriteWidth = 200; this.spriteHeight = 179; this.size = size; this.x = x; this.y = y; this.frame = 0; this.sound = new Audio(); this.sound.src = 'https://frankslaboratory.co.uk/downloads/boom.wav'; this.timeSinceLastFrame = 0; this.frameInterval = 200; this.markedForDeletion = false; } update(deltatime){ if (this.frame === 0) this.sound.play(); this.timeSinceLastFrame += deltatime; if (this.timeSinceLastFrame > this.frameInterval){ this.frame++; this.timeSinceLastFrame = 0; if (this.frame > 5) this.markedForDeletion = true; } } draw(){ ctx.drawImage(this.image, this.frame * this.spriteWidth, 0, this.spriteWidth, this.spriteHeight, this.x, this.y - this.size/4, this.size, this.size); } } let particles = []; class Particle { constructor(x, y, size, color){ this.size = size; this.x = x + this.size/2 + Math.random() * 50 - 25; this.y = y + this.size/3 + Math.random() * 50 - 25; this.radius = Math.random() * this.size/10; this.maxRadius = Math.random() * 20 + 35; this.markedForDeletion = false; this.speedX = Math.random() * 1 + 0.5; this.color = color; } update(){ this.x += this.speedX; this.radius += 0.3; if (this.radius > this.maxRadius - 1) this.markedForDeletion = true; } draw(){ ctx.save(); ctx.globalAlpha = 1 - this.radius/this.maxRadius; ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } } function drawScore(){ ctx.fillStyle = 'black'; ctx.fillText('Score: ' + score, 50, 75); ctx.fillStyle = 'white'; ctx.fillText('Score: ' + score, 55, 80); } function drawGameOver(){ ctx.textAlign = 'center'; ctx.fillStyle = 'black'; ctx.fillText('GAME OVER, your score is ' + score, canvas.width/2, canvas.height/2); ctx.fillStyle = 'white'; ctx.fillText('GAME OVER, your score is ' + score, canvas.width/2 + 5, canvas.height/2 + 5); } window.addEventListener('click', function(e){ const detectPixelColor = collisionCtx.getImageData(e.x, e.y, 1, 1); console.log(detectPixelColor); const pc = detectPixelColor.data; ravens.forEach(object => { if (object.randomColors[0] === pc[0] && object.randomColors[1] === pc[1] && object.randomColors[2] === pc[2]){ // collision detected object.markedForDeletion = true; score++; explosions.push(new Explosion(object.x, object.y, object.width)); console.log(explosions); } }); }); function animate(timestamp){ ctx.clearRect(0, 0, canvas.width, canvas.height); collisionCtx.clearRect(0, 0, canvas.width, canvas.height); let deltatime = timestamp - lastTime; lastTime = timestamp; timeToNextRaven += deltatime; if (timeToNextRaven > ravenInterval){ ravens.push(new Raven()); timeToNextRaven = 0; ravens.sort(function(a,b){ return a.width - b.width; }); }; drawScore(); [ ...particles, ...ravens, ...explosions].forEach(object => object.update(deltatime)); [ ...particles, ...ravens, ...explosions].forEach(object => object.draw()); ravens = ravens.filter(object => !object.markedForDeletion); explosions = explosions.filter(object => !object.markedForDeletion); particles = particles.filter(object => !object.markedForDeletion); if (!gameOver) requestAnimationFrame(animate); else drawGameOver(); } animate(0); }); </script><style> .canvas-container { width: 800px; height: 800px; background-image:url('https://empirefable.magwebdesigns.net/wp/Ocean_Deep_Map_2-22-23.webp'); } #canvas { width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ } </style> <div id="canvas-container" class="canvas-container"> <div id="status" class="status"> STATUS </div> <div id="text" class="text"> <marquee behavior="scroll" direction="up" scrollamount="2"> TEXT GOES HERE </marquee> </div> <canvas id="canvas" class="canvas"></canvas> </div> <script> //// MOUSE / TOUCH POINTER //////////////////////////////////// class Pointer { constructor(xPos, yPos, xSize, ySize, color, imgSrc) { this.xPos = xPos; this.yPos = yPos; this.xSize = xSize; this.ySize = ySize; this.color = color; this.imgSrc = imgSrc; this.collision = false; } updatePosition(xPos, yPos) { this.xPos = xPos; this.yPos = yPos; } draw(ctx) { ctx.beginPath(); ctx.arc( this.xPos + this.xSize / 2, this.yPos + this.ySize / 2, this.xSize / 2, 0, 2 * Math.PI ); ctx.fillStyle = this.color; ctx.fill(); } } ////// IMAGE CONTAINER ////////////////////////////////////////////////////// class ImageContainer { constructor(imageUrl, x, y, width, height) { this.image = new Image(); this.image.src = imageUrl; this.image.onload = () => { this.isLoaded = true; }; this.x = x; this.y = y; this.width = width; this.height = height; } draw(ctx) { if (this.isLoaded) { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } } } ///// CLICKABLE HOT SPOT ////////////////////////////////////////////// class HotSpot { constructor(id, xPos, yPos, text, imgSrc, xSize, ySize, color) { this.id = id; this.xPos = xPos; this.yPos = yPos; this.text = text; this.imgSrc = imgSrc; this.xSize = xSize; this.ySize = ySize; this.color = color; } checkCollision(pointer) { if ( pointer.xPos < this.xPos + this.xSize && pointer.xPos + pointer.xSize > this.xPos && pointer.yPos < this.yPos + this.ySize && pointer.yPos + pointer.ySize > this.yPos ) { console.log("COLLISION:" + this.id ); document.getElementById("status").innerHTML = "COLLISION WITH " + this.id; } } draw(ctx) { ctx.globalAlpha = 0.25; // set global alpha to 50% transparency ctx.beginPath(); ctx.rect(this.xPos, this.yPos, this.xSize, this.ySize); ctx.fillStyle = this.color; ctx.fill(); ctx.globalAlpha = 1; // reset global alpha back to default (fully opaque) } } //// INSTANTIATE CANVAS AND GET CONTEXT //////////////////////////////// const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 800; canvas.height = 800; ///// INSTANTIATE HOT SPOTS //////////////////////////////////////////////////////////////// const hotSpot1 = new HotSpot("hotspot1", 50, 50, "Click me", null, 100, 100, "white"); const hotSpot2 = new HotSpot("hotspot2", 150, 150, "Click me 2", null, 100, 100, "white"); ///// INSTANTIATE POINTER ////////////////////////////////////////////////////////////////////// const pointer = new Pointer(0, 0, 20, 20, "red", null); //// INSTANCE IMAGE CONTAINERS //////////////////////////////////////////////////////// const imageContainerOne = new ImageContainer( "https://cdn.discordapp.com/attachments/1077812497467310184/1080251481749454858/C1_8k_Fantasy_Human_Female_Archer_Explorer_Steam_Punk_Studio_Gh_fb1eb9a0-0007-4d81-a5ea-1d351ab7d0f9.png", 100, 100, 80, 80, ); ///// update pointer position document.addEventListener("mousemove", (event) => { const rect = canvas.getBoundingClientRect(); pointer.updatePosition( event.clientX - rect.left, event.clientY - rect.top ); }); // check collision between pointer and hot spots on mouse click document.addEventListener("click", () => { hotSpot1.checkCollision(pointer); hotSpot2.checkCollision(pointer); }); //// DRAW GRID /////////////////////////////////////////////////////////////////////// function drawGrid(ctx, width, height) { // Draw vertical lines for (let x = 50; x <= width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); ctx.closePath(); // Draw x-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(x.toString(), x + 5, 15); } // Draw horizontal lines for (let y = 50; y <= height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); ctx.closePath(); // Draw y-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(y.toString(), 5, y - 5); } } ////// UPDATE THE CANVAS ////////////////////////////////// function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); pointer.draw(ctx); hotSpot1.draw(ctx); hotSpot2.draw(ctx); imageContainerOne.draw(ctx); drawGrid(ctx, canvas.width, canvas.height); requestAnimationFrame(update); } update(); </script> COIN DROP GAME JS CANVAS Drops COINS AND SPIKE BALLS. Spike Balls do DMG COINS GIVE GOLD.CAPTURE THE THIEF GAME. THIEVES ARE RAMPAGING THE MARKET PLACE. GWALDOR MUST CLICK ON THE THIEVES TO SEND THE GUARDSMEN TO THEM (OR SEND THEM TO JAIL)PLAYER MUST SHOOT THE RATS INFESTING THE BASEMENT. SMALL AND A GIANT BOSS RAT. <style> .canvas-container { width: 800px; height: 800px; background-image:url('https://empirefable.magwebdesigns.net/wp/Ocean_Deep_Map_2-22-23.webp'); } #canvas { width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ } </style> <div id="canvas-container" class="canvas-container"> <div id="status" class="status"> STATUS </div> <div id="text" class="text"> <marquee behavior="scroll" direction="up" scrollamount="2"> TEXT GOES HERE </marquee> </div> <canvas id="canvas" class="canvas"></canvas> </div> <script> //// MOUSE / TOUCH POINTER //////////////////////////////////// class Pointer { constructor(xPos, yPos, xSize, ySize, color, imgSrc) { this.xPos = xPos; this.yPos = yPos; this.xSize = xSize; this.ySize = ySize; this.color = color; this.imgSrc = imgSrc; this.collision = false; } updatePosition(xPos, yPos) { this.xPos = xPos; this.yPos = yPos; } draw(ctx) { ctx.beginPath(); ctx.arc( this.xPos + this.xSize / 2, this.yPos + this.ySize / 2, this.xSize / 2, 0, 2 * Math.PI ); ctx.fillStyle = this.color; ctx.fill(); } } ////// IMAGE CONTAINER ////////////////////////////////////////////////////// class ImageContainer { constructor(imageUrl, x, y, width, height) { this.image = new Image(); this.image.src = imageUrl; this.image.onload = () => { this.isLoaded = true; }; this.x = x; this.y = y; this.width = width; this.height = height; } draw(ctx) { if (this.isLoaded) { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } } } ///// CLICKABLE HOT SPOT ////////////////////////////////////////////// class HotSpot { constructor(id, xPos, yPos, text, imgSrc, xSize, ySize, color) { this.id = id; this.xPos = xPos; this.yPos = yPos; this.text = text; this.imgSrc = imgSrc; this.xSize = xSize; this.ySize = ySize; this.color = color; } checkCollision(pointer) { if ( pointer.xPos < this.xPos + this.xSize && pointer.xPos + pointer.xSize > this.xPos && pointer.yPos < this.yPos + this.ySize && pointer.yPos + pointer.ySize > this.yPos ) { console.log("COLLISION:" + this.id ); document.getElementById("status").innerHTML = "COLLISION WITH " + this.id; } } draw(ctx) { ctx.globalAlpha = 0.25; // set global alpha to 50% transparency ctx.beginPath(); ctx.rect(this.xPos, this.yPos, this.xSize, this.ySize); ctx.fillStyle = this.color; ctx.fill(); ctx.globalAlpha = 1; // reset global alpha back to default (fully opaque) } } //// INSTANTIATE CANVAS AND GET CONTEXT //////////////////////////////// const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 800; canvas.height = 800; ///// INSTANTIATE HOT SPOTS //////////////////////////////////////////////////////////////// const hotSpot1 = new HotSpot("hotspot1", 50, 50, "Click me", null, 100, 100, "white"); const hotSpot2 = new HotSpot("hotspot2", 150, 150, "Click me 2", null, 100, 100, "white"); //// INSTANCE IMAGE CONTAINERS //////////////////////////////////////////////////////// const imageContainerOne = new ImageContainer("", 100, 100, 80, 80, ); const imageContainerTwo = new ImageContainer("", 100, 100, 80, 80, ); ///// INSTANTIATE POINTER ////////////////////////////////////////////////////////////////////// const pointer = new Pointer(0, 0, 20, 20, "red", null); ///// update pointer position document.addEventListener("mousemove", (event) => { const rect = canvas.getBoundingClientRect(); pointer.updatePosition( event.clientX - rect.left, event.clientY - rect.top ); }); // check collision between pointer and hot spots on mouse click document.addEventListener("click", () => { hotSpot1.checkCollision(pointer); hotSpot2.checkCollision(pointer); }); //// DRAW GRID /////////////////////////////////////////////////////////////////////// function drawGrid(ctx, width, height) { // Draw vertical lines for (let x = 50; x <= width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); ctx.closePath(); // Draw x-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(x.toString(), x + 5, 15); } // Draw horizontal lines for (let y = 50; y <= height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); ctx.closePath(); // Draw y-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(y.toString(), 5, y - 5); } } ////// UPDATE THE CANVAS ////////////////////////////////// function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); pointer.draw(ctx); hotSpot1.draw(ctx); hotSpot2.draw(ctx); imageContainerOne.draw(ctx); drawGrid(ctx, canvas.width, canvas.height); requestAnimationFrame(update); } update(); </script> <style> .canvas-container { width: 800px; height: 800px; background-image:url('https://empirefable.magwebdesigns.net/wp/Ocean_Deep_Map_2-22-23.webp'); } #canvas { width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ } </style> <div id="canvas-container" class="canvas-container"> <div id="status" class="status"> STATUS </div> <div id="text" class="text"> <marquee behavior="scroll" direction="up" scrollamount="2"> TEXT GOES HERE </marquee> </div> <canvas id="canvas" class="canvas"></canvas> </div> <script> //// MOUSE / TOUCH POINTER //////////////////////////////////// class Pointer { constructor(xPos, yPos, xSize, ySize, color, imgSrc) { this.xPos = xPos; this.yPos = yPos; this.xSize = xSize; this.ySize = ySize; this.color = color; this.imgSrc = imgSrc; this.collision = false; } updatePosition(xPos, yPos) { this.xPos = xPos; this.yPos = yPos; } draw(ctx) { ctx.beginPath(); ctx.arc( this.xPos + this.xSize / 2, this.yPos + this.ySize / 2, this.xSize / 2, 0, 2 * Math.PI ); ctx.fillStyle = this.color; ctx.fill(); } } ////// IMAGE CONTAINER ////////////////////////////////////////////////////// class ImageContainer { constructor(imageUrl, x, y, width, height) { this.image = new Image(); this.image.src = imageUrl; this.image.onload = () => { this.isLoaded = true; }; this.x = x; this.y = y; this.width = width; this.height = height; } draw(ctx) { if (this.isLoaded) { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } } } ///// CLICKABLE HOT SPOT ////////////////////////////////////////////// class HotSpot { constructor(id, xPos, yPos, text, imgSrc, xSize, ySize, color) { this.id = id; this.xPos = xPos; this.yPos = yPos; this.text = text; this.imgSrc = imgSrc; this.xSize = xSize; this.ySize = ySize; this.color = color; } checkCollision(pointer) { if ( pointer.xPos < this.xPos + this.xSize && pointer.xPos + pointer.xSize > this.xPos && pointer.yPos < this.yPos + this.ySize && pointer.yPos + pointer.ySize > this.yPos ) { console.log("COLLISION:" + this.id ); document.getElementById("status").innerHTML = "COLLISION WITH " + this.id; } } draw(ctx) { ctx.globalAlpha = 0.25; // set global alpha to 50% transparency ctx.beginPath(); ctx.rect(this.xPos, this.yPos, this.xSize, this.ySize); ctx.fillStyle = this.color; ctx.fill(); ctx.globalAlpha = 1; // reset global alpha back to default (fully opaque) } } //// INSTANTIATE CANVAS AND GET CONTEXT //////////////////////////////// const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 800; canvas.height = 800; ///// INSTANTIATE HOT SPOTS //////////////////////////////////////////////////////////////// const hotSpot1 = new HotSpot("hotspot1", 50, 50, "Click me", null, 100, 100, "white"); const hotSpot2 = new HotSpot("hotspot2", 150, 150, "Click me 2", null, 100, 100, "white"); //// INSTANCE IMAGE CONTAINERS //////////////////////////////////////////////////////// const imageContainerOne = new ImageContainer("https://empirefable.magwebdesigns.net/images/Female-Human-Explorer-1-Front.png", 100, 100, 80, 80, ); const imageContainerTwo = new ImageContainer("https://empirefable.magwebdesigns.net/images/Female-Human-Explorer-1-Front.png", 100, 100, 80, 80, ); ///// INSTANTIATE POINTER ////////////////////////////////////////////////////////////////////// const pointer = new Pointer(0, 0, 20, 20, "red", null); ///// update pointer position document.addEventListener("mousemove", (event) => { const rect = canvas.getBoundingClientRect(); pointer.updatePosition( event.clientX - rect.left, event.clientY - rect.top ); }); // check collision between pointer and hot spots on mouse click document.addEventListener("click", () => { hotSpot1.checkCollision(pointer); hotSpot2.checkCollision(pointer); }); //// DRAW GRID /////////////////////////////////////////////////////////////////////// function drawGrid(ctx, width, height) { // Draw vertical lines for (let x = 50; x <= width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); ctx.closePath(); // Draw x-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(x.toString(), x + 5, 15); } // Draw horizontal lines for (let y = 50; y <= height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); ctx.closePath(); // Draw y-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(y.toString(), 5, y - 5); } } ////// UPDATE THE CANVAS ////////////////////////////////// function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); pointer.draw(ctx); hotSpot1.draw(ctx); hotSpot2.draw(ctx); imageContainerOne.draw(ctx); drawGrid(ctx, canvas.width, canvas.height); requestAnimationFrame(update); } update(); </script> <style> tw-sidebar { display: none; } tw-story { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px } tw-passage { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px; } .canvas-container { position:absolute; width: 800px; height: 800px; z-index:0; background-image:url('https://empirefable.magwebdesigns.net/wp/Ocean_Deep_Map_2-22-23.webp'); } #canvas { position: absolute; width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text-container { position: absolute; width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:0; } #text { position: absolute; width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:0; } </style> <div id="canvas-container" class="canvas-container"> <div id="status" class="status"> STATUS </div> <div id="text" class="text"> <marquee behavior="scroll" direction="up" scrollamount="2"> TEXT GOES HERE </marquee> </div> <canvas id="canvas" class="canvas"></canvas> </div> <script> //// MOUSE / TOUCH POINTER //////////////////////////////////// class Pointer { constructor(xPos, yPos, xSize, ySize, color, imgSrc) { this.xPos = xPos; this.yPos = yPos; this.xSize = xSize; this.ySize = ySize; this.color = color; this.imgSrc = imgSrc; this.collision = false; } updatePosition(xPos, yPos) { this.xPos = xPos; this.yPos = yPos; } draw(ctx) { ctx.beginPath(); ctx.arc( this.xPos + this.xSize / 2, this.yPos + this.ySize / 2, this.xSize / 2, 0, 2 * Math.PI ); ctx.fillStyle = this.color; ctx.fill(); } } ////// IMAGE CONTAINER ////////////////////////////////////////////////////// class ImageContainer { constructor(imageUrl, x, y, width, height,) { this.image = new Image(); this.image.src = imageUrl; this.image.onload = () => { this.isLoaded = true; }; this.x = x; this.y = y; this.width = width; this.height = height; } draw(ctx) { if (this.isLoaded) { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } } } ///// CLICKABLE HOT SPOT ////////////////////////////////////////////// class HotSpot { constructor(id, xPos, yPos, text, imgSrc, xSize, ySize, color) { this.id = id; this.xPos = xPos; this.yPos = yPos; this.text = text; this.imgSrc = imgSrc; this.xSize = xSize; this.ySize = ySize; this.color = color; } checkCollision(pointer) { if ( pointer.xPos < this.xPos + this.xSize && pointer.xPos + pointer.xSize > this.xPos && pointer.yPos < this.yPos + this.ySize && pointer.yPos + pointer.ySize > this.yPos ) { console.log("COLLISION:" + this.id ); document.getElementById("status").innerHTML = "COLLISION WITH " + this.id; } } draw(ctx) { ctx.globalAlpha = 0.25; // set global alpha to 50% transparency ctx.beginPath(); ctx.rect(this.xPos, this.yPos, this.xSize, this.ySize); ctx.fillStyle = this.color; ctx.fill(); ctx.globalAlpha = 1; // reset global alpha back to default (fully opaque) } } //// INSTANTIATE CANVAS AND GET CONTEXT //////////////////////////////// const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 800; canvas.height = 800; ///// INSTANTIATE HOT SPOTS //////////////////////////////////////////////////////////////// const hotSpot1 = new HotSpot("hotspot1", 50, 50, "Click me", null, 100, 100, "white"); const hotSpot2 = new HotSpot("hotspot2", 150, 150, "Click me 2", null, 100, 100, "white"); //// INSTANCE IMAGE CONTAINERS //////////////////////////////////////////////////////// const imageContainerOne = new ImageContainer("https://empirefable.magwebdesigns.net/images/Female-Human-Explorer-1-Front.png", 250, -200, 800, 800, ); const imageContainerTwo = new ImageContainer("", 0, 0, 400, 400, ); ///// INSTANTIATE POINTER ////////////////////////////////////////////////////////////////////// const pointer = new Pointer(0, 0, 20, 20, "red", null); ///// update pointer position document.addEventListener("mousemove", (event) => { const rect = canvas.getBoundingClientRect(); pointer.updatePosition( event.clientX - rect.left, event.clientY - rect.top ); }); // check collision between pointer and hot spots on mouse click document.addEventListener("click", () => { hotSpot1.checkCollision(pointer); hotSpot2.checkCollision(pointer); }); //// DRAW GRID /////////////////////////////////////////////////////////////////////// function drawGrid(ctx, width, height) { // Draw vertical lines for (let x = 50; x <= width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); ctx.closePath(); // Draw x-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(x.toString(), x + 5, 15); } // Draw horizontal lines for (let y = 50; y <= height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); ctx.closePath(); // Draw y-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(y.toString(), 5, y - 5); } } ////// UPDATE THE CANVAS ////////////////////////////////// function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); pointer.draw(ctx); hotSpot1.draw(ctx); hotSpot2.draw(ctx); imageContainerOne.draw(ctx); imageContainerTwo.draw(ctx); drawGrid(ctx, canvas.width, canvas.height); requestAnimationFrame(update); } update(); </script> <style> tw-sidebar { display: none; } tw-story { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px } tw-passage { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px; } .canvas-container { position:relative; width: 800px; height: 800px; z-index:0; background-image:url('https://empirefable.magwebdesigns.net/wp/Ocean_Deep_Map_2-22-23.webp'); } #canvas { position: relative; width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text-container { position: absolute; min-width: 800px; min-height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:0; } #text { position: absolute; min-width: 800px; min-heightheight: 800px; opacity: 1; /* set the opacity to 50% */ z-index:0; } </style> <div id="canvas-container" class="canvas-container"> <div id="status" class="status"> STATUS </div> <div id="text" class="text"> <marquee behavior="scroll" direction="up" scrollamount="2"> <p> TEXT GOES HERE TEXY GOES HERE </p> </marquee> </div> <canvas id="canvas" class="canvas"></canvas> </div> <script> //// MOUSE / TOUCH POINTER //////////////////////////////////// class Pointer { constructor(xPos, yPos, xSize, ySize, color, imgSrc) { this.xPos = xPos; this.yPos = yPos; this.xSize = xSize; this.ySize = ySize; this.color = color; this.imgSrc = imgSrc; this.collision = false; } updatePosition(xPos, yPos) { this.xPos = xPos; this.yPos = yPos; } draw(ctx) { ctx.beginPath(); ctx.arc( this.xPos + this.xSize / 2, this.yPos + this.ySize / 2, this.xSize / 2, 0, 2 * Math.PI ); ctx.fillStyle = this.color; ctx.fill(); } } ////// IMAGE CONTAINER ////////////////////////////////////////////////////// class ImageContainer { constructor(imageUrl, x, y, width, height,) { this.image = new Image(); this.image.src = imageUrl; this.image.onload = () => { this.isLoaded = true; }; this.x = x; this.y = y; this.width = width; this.height = height; } draw(ctx) { if (this.isLoaded) { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } } } ///// CLICKABLE HOT SPOT ////////////////////////////////////////////// class HotSpot { constructor(id, xPos, yPos, text, imgSrc, xSize, ySize, color) { this.id = id; this.xPos = xPos; this.yPos = yPos; this.text = text; this.imgSrc = imgSrc; this.xSize = xSize; this.ySize = ySize; this.color = color; } checkCollision(pointer) { if ( pointer.xPos < this.xPos + this.xSize && pointer.xPos + pointer.xSize > this.xPos && pointer.yPos < this.yPos + this.ySize && pointer.yPos + pointer.ySize > this.yPos ) { console.log("COLLISION:" + this.id ); document.getElementById("status").innerHTML = "COLLISION WITH " + this.id; } } draw(ctx) { ctx.globalAlpha = 0.25; // set global alpha to 50% transparency ctx.beginPath(); ctx.rect(this.xPos, this.yPos, this.xSize, this.ySize); ctx.fillStyle = this.color; ctx.fill(); ctx.globalAlpha = 1; // reset global alpha back to default (fully opaque) } } //// INSTANTIATE CANVAS AND GET CONTEXT //////////////////////////////// const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 800; canvas.height = 800; ///// INSTANTIATE HOT SPOTS //////////////////////////////////////////////////////////////// const hotSpot1 = new HotSpot("hotspot1", 50, 50, "Click me", null, 100, 100, "white"); const hotSpot2 = new HotSpot("hotspot2", 150, 150, "Click me 2", null, 100, 100, "white"); //// INSTANCE IMAGE CONTAINERS //////////////////////////////////////////////////////// const imageContainerOne = new ImageContainer("https://empirefable.magwebdesigns.net/images/Female-Human-Explorer-1-Front.png", 350, -50, 700, 700, ); const imageContainerTwo = new ImageContainer("", 0, 0, 400, 400, ); ///// INSTANTIATE POINTER ////////////////////////////////////////////////////////////////////// const pointer = new Pointer(0, 0, 20, 20, "red", null); ///// update pointer position document.addEventListener("mousemove", (event) => { const rect = canvas.getBoundingClientRect(); pointer.updatePosition( event.clientX - rect.left, event.clientY - rect.top ); }); // check collision between pointer and hot spots on mouse click document.addEventListener("click", () => { hotSpot1.checkCollision(pointer); hotSpot2.checkCollision(pointer); }); //// DRAW GRID /////////////////////////////////////////////////////////////////////// function drawGrid(ctx, width, height) { // Draw vertical lines for (let x = 50; x <= width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); ctx.closePath(); // Draw x-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(x.toString(), x + 5, 15); } // Draw horizontal lines for (let y = 50; y <= height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); ctx.closePath(); // Draw y-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(y.toString(), 5, y - 5); } } ////// UPDATE THE CANVAS ////////////////////////////////// function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); pointer.draw(ctx); hotSpot1.draw(ctx); hotSpot2.draw(ctx); imageContainerOne.draw(ctx); imageContainerTwo.draw(ctx); drawGrid(ctx, canvas.width, canvas.height); requestAnimationFrame(update); } update(); </script> FIX THE TEMPLE MINI GAME Player needs to move movable nodes into hotspots to rebuild the templePUZZLE:. CHOP UP BACKGROUND IMAGE AND PLAYER MUST PLACE PIECES TO FINISH PUZZLEQuests: -- ITEM QUESTS ---- need an item to place into an object. --- KILL ENEMY QUEST 3 bit creatures. S --- SOLDIER W --- WORKER R --- RESEARCHER ############################# 0 - 7 Speed 0 - 7 Strength 0 - 7 Size ############################# 111 Speed Int Size 1 1 1 <style> tw-sidebar { display: none; } tw-story { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px } tw-passage { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px; } .canvas-container { background-size:contain; position:relative; width: 800px; height: 800px; z-index:0; background-image:url('https://empirefable.magwebdesigns.net/wp/Ocean_Deep_Map_2-22-23.webp'); } #canvas { background-size:contain; position: relative; width: 800px; height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text-container { position: absolute; min-width: 800px; min-height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text { position: absolute; min-width: 800px; min-heightheight: 800px; opacity: 1; /* set the opacity to 50% */ z-index:1; } </style> <div id="canvas-container" class="canvas-container"> <div id="status" class="status"> STATUS </div> <div id="text" class="text"> <marquee behavior="scroll" direction="up" scrollamount="2"> <p> TEXT GOES HERE TEXY GOES HERE </p> </marquee> </div> <canvas id="canvas" class="canvas" ontouchstart="handleTouchStart(event)" ontouchmove="handleTouchMove(event)" ontouchend="handleTouchEnd(event)"></canvas> </div> <script> //// MOUSE / TOUCH POINTER //////////////////////////////////// class Pointer { constructor(xPos, yPos, xSize, ySize, color, imgSrc) { this.xPos = xPos; this.yPos = yPos; this.xSize = xSize; this.ySize = ySize; this.color = color; this.imgSrc = imgSrc; this.collision = false; } updatePosition(xPos, yPos) { this.xPos = xPos; this.yPos = yPos; } draw(ctx) { ctx.beginPath(); ctx.arc( this.xPos + this.xSize / 2, this.yPos + this.ySize / 2, this.xSize / 2, 0, 2 * Math.PI ); ctx.fillStyle = this.color; ctx.fill(); } } ////// IMAGE CONTAINER ////////////////////////////////////////////////////// class ImageContainer { constructor(imageUrl, x, y, width, height,) { this.image = new Image(); this.image.src = imageUrl; this.image.onload = () => { this.isLoaded = true; }; this.x = x; this.y = y; this.width = width; this.height = height; } draw(ctx) { if (this.isLoaded) { ctx.drawImage(this.image, this.x, this.y, this.width, this.height); } } } ///// CLICKABLE HOT SPOT ////////////////////////////////////////////// class HotSpot { constructor(id, xPos, yPos, text, imgSrc, xSize, ySize, color) { this.id = id; this.xPos = xPos; this.yPos = yPos; this.text = text; this.imgSrc = imgSrc; this.xSize = xSize; this.ySize = ySize; this.color = color; } checkCollision(pointer) { if ( pointer.xPos < this.xPos + this.xSize && pointer.xPos + pointer.xSize > this.xPos && pointer.yPos < this.yPos + this.ySize && pointer.yPos + pointer.ySize > this.yPos ) { console.log("COLLISION:" + this.id ); document.getElementById("status").innerHTML = "COLLISION WITH " + this.id; } } draw(ctx) { ctx.globalAlpha = 0.25; // set global alpha to 50% transparency ctx.beginPath(); ctx.rect(this.xPos, this.yPos, this.xSize, this.ySize); ctx.fillStyle = this.color; ctx.fill(); ctx.globalAlpha = 1; // reset global alpha back to default (fully opaque) } } //// MOVEVABLE NODE /////////////////////////////////////////////////////////////////// class MovableNode { constructor(title, xPos, yPos, width, height) { this.title = title; this.xPos = xPos; this.yPos = yPos; this.width = width; this.height = height; this.isDragging = false; this.offsetX = 0; this.offsetY = 0; } draw(context) { // Draw the node on the canvas context.beginPath(); context.rect(this.xPos, this.yPos, this.width, this.height); context.fillStyle = "#e3e3e3"; context.fill(); context.lineWidth = 2; context.strokeStyle = "#000000"; context.stroke(); context.closePath(); // Draw the title inside the node context.font = "bold 12px Arial"; context.fillStyle = "#000000"; context.textAlign = "center"; context.fillText( this.title, this.xPos + this.width / 2, this.yPos + this.height / 2 ); } isMouseOver(x, y) { // Check if the mouse is over the node return ( x >= this.xPos && x <= this.xPos + this.width && y >= this.yPos && y <= this.yPos + this.height ); } onTouchStart(event) { const touch = event.touches[0]; if (touch.clientX >= this.xPos && touch.clientX <= this.xPos + this.width && touch.clientY >= this.yPos && touch.clientY <= this.yPos + this.height) { this.isDragging = true; this.offsetX = touch.clientX - this.xPos; this.offsetY = touch.clientY - this.yPos; } } startDragging(x, y) { // Start dragging the node this.isDragging = true; this.offsetX = x - this.xPos; this.offsetY = y - this.yPos; } onTouchMove(event) { if (this.isDragging) { const touch = event.touches[0]; this.xPos = touch.clientX - this.offsetX; this.yPos = touch.clientY - this.offsetY; } } stopDragging() { // Stop dragging the node this.isDragging = false; } onTouchEnd(event) { this.isDragging = false; } updatePosition(x, y) { // Update the node's position while dragging this.xPos = x - this.offsetX; this.yPos = y - this.offsetY; } addEventListeners(canvas) { // Add event listeners to the canvas to handle dragging canvas.addEventListener("mousedown", (event) => { if (this.isMouseOver(event.offsetX, event.offsetY)) { this.startDragging(event.offsetX, event.offsetY); } }); canvas.addEventListener("mousemove", (event) => { if (this.isDragging) { this.updatePosition(event.offsetX, event.offsetY); } }); canvas.addEventListener("mouseup", () => { this.stopDragging(); }); canvas.addEventListener("mouseleave", () => { this.stopDragging(); }); // event handlers function handleTouchStart(event) { movableNode.onTouchStart(event); } function handleTouchMove(event) { movableNode.onTouchMove(event); } function handleTouchEnd(event) { movableNode.onTouchEnd(event); } } } //// INSTANTIATE CANVAS AND GET CONTEXT //////////////////////////////// const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 800; canvas.height = 800; ///// INSTANTIATE HOT SPOTS //////////////////////////////////////////////////////////////// const hotSpot1 = new HotSpot("hotspot1", 150, 0, "Click me", null, 100, 100, "white"); const hotSpot2 = new HotSpot("hotspot2", 250, 250, "HOT SPOT 2", null, 100, 100, "white"); //// INSTANCE IMAGE CONTAINERS //////////////////////////////////////////////////////// const imageContainerOne = new ImageContainer("https://empirefable.magwebdesigns.net/images/Female-Human-Explorer-1-Front.png", 300, -50, 700, 700, ); const imageContainerTwo = new ImageContainer("https://empirefable.magwebdesigns.net/images/dwarven_house_2_scaled_transparent.webp", 150, 0, 100, 100, ); const imageContainerThree = new ImageContainer("https://empirefable.magwebdesigns.net/images/dwarven_house_scaled_transparent_1.png", 250, 250, 100, 100, ); ///// INSTANTIATE POINTER ////////////////////////////////////////////////////////////////////// const pointer = new Pointer(0, 0, 20, 20, "red", null); //// INSTANTIATE MOVAEBLE NODE const moveNodeOne = new MovableNode("MOVABLE NODE", 50, 50, 100, 50); ///// update pointer position document.addEventListener("mousemove", (event) => { const rect = canvas.getBoundingClientRect(); pointer.updatePosition( event.clientX - rect.left, event.clientY - rect.top ); }); // check collision between pointer and hot spots on mouse click document.addEventListener("click", () => { hotSpot1.checkCollision(pointer); hotSpot2.checkCollision(pointer); }); //// DRAW GRID /////////////////////////////////////////////////////////////////////// function drawGrid(ctx, width, height) { // Draw vertical lines for (let x = 50; x <= width; x += 50) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); ctx.closePath(); // Draw x-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(x.toString(), x + 5, 15); } // Draw horizontal lines for (let y = 50; y <= height; y += 50) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); ctx.closePath(); // Draw y-axis label ctx.font = '18px Arial'; ctx.fillStyle = '#000000'; ctx.fillText(y.toString(), 5, y - 5); } } ////// UPDATE THE CANVAS ////////////////////////////////// function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); pointer.draw(ctx); hotSpot1.draw(ctx); hotSpot2.draw(ctx); imageContainerOne.draw(ctx); imageContainerTwo.draw(ctx); imageContainerThree.draw(ctx); moveNodeOne.draw(ctx); moveNodeOne.addEventListeners(canvas); drawGrid(ctx, canvas.width, canvas.height); requestAnimationFrame(update); } update(); </script> <!DOCTYPE html> <html> <head> <title>Moveable Node</title> <style> canvas { border: 1px solid black; } </style> </head> <body> <canvas id="canvas"></canvas> <script> class MoveableNode { constructor(canvas, x, y, radius) { this.canvas = canvas; this.ctx = canvas.getContext("2d"); this.x = x; this.y = y; this.radius = radius; this.isDragging = false; this.canvas.addEventListener("mousedown", this.handleMouseDown.bind(this)); this.canvas.addEventListener("mousemove", this.handleMouseMove.bind(this)); this.canvas.addEventListener("mouseup", this.handleMouseUp.bind(this)); this.canvas.addEventListener("touchstart", this.handleTouchStart.bind(this)); this.canvas.addEventListener("touchmove", this.handleTouchMove.bind(this)); this.canvas.addEventListener("touchend", this.handleTouchEnd.bind(this)); } draw() { this.ctx.beginPath(); this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); this.ctx.fillStyle = "#0000ff"; this.ctx.fill(); this.ctx.closePath(); this.ctx.clearRect(0, 0, canvas.width, canvas.height); } handleMouseDown(event) { const rect = this.canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; if (this.isPointInside(x, y)) { this.isDragging = true; } } handleMouseMove(event) { if (this.isDragging) { const rect = this.canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; this.x = x; this.y = y; this.draw(); } } handleMouseUp(event) { this.isDragging = false; } handleTouchStart(event) { const touch = event.changedTouches[0]; const rect = this.canvas.getBoundingClientRect(); const x = touch.clientX - rect.left; const y = touch.clientY - rect.top; if (this.isPointInside(x, y)) { this.isDragging = true; } } handleTouchMove(event) { if (this.isDragging) { const touch = event.changedTouches[0]; const rect = this.canvas.getBoundingClientRect(); const x = touch.clientX - rect.left; const y = touch.clientY - rect.top; this.x = x; this.y = y; this.draw(); } } handleTouchEnd(event) { this.isDragging = false; } isPointInside(x, y) { return Math.sqrt((this.x - x) ** 2 + (this.y - y) ** 2) < this.radius; } } const canvas = document.getElementById("canvas"); const moveableNode = new MoveableNode(canvas, 100, 100, 50); moveableNode.draw(); </script> </body> </html><style> tw-sidebar { display: none; } tw-story { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px } tw-passage { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px; } #background { position: absolute; min-width: 800px; min-height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:0; top: 0px; background-image: url('https://cdn.discordapp.com/attachments/1077812497467310184/1078033485664637098/C1_large_busy_fantasy_airship_docking_facility_hand_drawn_0e88e1d4-e700-4d12-96be-77e22b3fbcb6.png '); } #header { z-index:1; } #sitepal { position: absolute; top: 530px; left: 400px; width: 400px; height: 400px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text { border:0px solid; padding: 50px; } #marquee{ border: 1px solid; opacity:1; border-radius:20px; color:white; background: rgba(0, 151, 19, 0.2); padding: 10px; } #nav { z-index:1; } </style> <div id="header" class="header"><a href="#"><h3>ARABELLA BRASSFORD</h3></a></div> <div id="nav" class="nav"> <div id="linkContainer" class="linkContainer">[[MAIN->CHARACTERS]]</div> </div> <div id="background" class="background"> <iframe id="sitepal" class="sitepal" src="https://www.sitepal.com/geturl/?ss=2757341&sl=0&acc=8667722" style="border:0px #ffffff none;" name="myiFrame" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="600px" width="800px" allowfullscreen></iframe> <div id="text" class="text"> <marquee id="marquee" class="marquee" behavior="scroll" direction="up" scrollamount="2"> <p> "Welcome aboard the Cloud Strider, my dear guests. I am Arabella BrassFord, a member of the Guild of Science and a fervent supporter of Gwaldor's expeditions. I am pleased to have the opportunity to accompany you on this journey to discover the wonders of Ocean Deep As an explorer and scholar, I have traveled to the far corners of the world in search of knowledge and adventure. But the mysteries of the city have always fascinated me the most. I believe that with Gwaldor's expertise and our combined resources, we will uncover treasures and excitement beyond our wildest dreams. So sit back, relax, and enjoy the ride. The Cloud Strider is a marvel Engineering, and our crew is the finest in the land. I am confident that we will make it to our destination safely and with many tales to tell." </p> </marquee> </div> </div><style> tw-sidebar { display: none; } tw-story { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px } tw-passage { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px; } #background { position: absolute; min-width: 800px; min-height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:0; top: 0px; background-image: url('https://cdn.discordapp.com/attachments/1077812497467310184/1078033485664637098/C1_large_busy_fantasy_airship_docking_facility_hand_drawn_0e88e1d4-e700-4d12-96be-77e22b3fbcb6.png '); } #header { z-index:1; } #sitepal { position: absolute; top: 530px; left: 400px; width: 400px; height: 400px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text { border:0px solid; padding: 50px; } #marquee{ border: 1px solid; opacity:1; border-radius:20px; color:white; background: rgba(0, 151, 19, 0.2); padding: 10px; } #nav { z-index:1; } </style> <div id="header" class="header"><a href="#"><h3>ARABELLA BRASSFORD</h3></a></div> <div id="nav" class="nav"> <div id="linkContainer" class="linkContainer">[[MAIN->CHARACTERS]]</div> </div> <div id="background" class="background"> <iframe id="sitepal" class="sitepal" src="https://www.sitepal.com/geturl/?ss=2757341&sl=0&acc=8667722" style="border:0px #ffffff none;" name="myiFrame" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="600px" width="800px" allowfullscreen></iframe> <div id="text" class="text"> <marquee id="marquee" class="marquee" behavior="scroll" direction="up" scrollamount="2"> <p> "Welcome aboard the Cloud Strider, my dear guests. I am Arabella BrassFord, a member of the Guild of Science and a fervent supporter of Gwaldor's expeditions. I am pleased to have the opportunity to accompany you on this journey to discover the wonders of Ocean Deep As an explorer and scholar, I have traveled to the far corners of the world in search of knowledge and adventure. But the mysteries of the city have always fascinated me the most. I believe that with Gwaldor's expertise and our combined resources, we will uncover treasures and excitement beyond our wildest dreams. So sit back, relax, and enjoy the ride. The Cloud Strider is a marvel Engineering, and our crew is the finest in the land. I am confident that we will make it to our destination safely and with many tales to tell." </p> </marquee> </div> </div><style> tw-sidebar { display: none; } tw-story { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px } tw-passage { position: absolute; width:100% height:100%; margin:10px; margin-right: auto; margin-left: auto; padding:0px; } #background { position: absolute; min-width: 800px; min-height: 800px; opacity: 1; /* set the opacity to 50% */ z-index:0; top: 0px; background-image: url('https://cdn.discordapp.com/attachments/1077812497467310184/1078037831726932048/C1_The_airship_port_was_a_marvel_of_fantasy_steam_punk_engineer_40ce2955-2038-4161-9737-f385bf3d1542.png'); } #header { z-index:1; } #sitepal { position: absolute; top: 530px; left: 400px; width: 400px; height: 400px; opacity: 1; /* set the opacity to 50% */ z-index:1; } #text { border:0px solid; padding: 50px; } #marquee{ border: 1px solid; opacity:1; border-radius:20px; color:white; background: rgba(211, 211, 211, 0.2); padding: 10px; } #nav { z-index:1; } </style> <div id="header" class="header"><a href="#"><h3>ADALYN MECHMASTER</h3></a></div> <div id="nav" class="nav"> <div id="linkContainer" class="linkContainer">[[MAIN->CHARACTERS]]</div> </div> <div id="background" class="background"> <iframe id="sitepal" class="sitepal" src="https://www.sitepal.com/geturl/?ss=2757346&sl=0&acc=8667722" style="border:0px #ffffff none;" name="myiFrame" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="600px" width="800px" allowfullscreen></iframe> <div id="text" class="text"> <marquee id="marquee" class="marquee" behavior="scroll" direction="up" scrollamount="2"> <p> "Welcome aboard the Cloud Strider, I'm Adalyn Mechmaster, the chief mechanic of this vessel. If you need anything fixed, I'm your go-to person. I'm always busy keeping this ship running smoothly and ensuring we don't fall out of the sky. "You know, this ship has been through some tough times, but we always manage to pull through thanks to the skills of the crew. We've faced krakens, rogue airships, and even a dragon or two. But we always come out on top. And I make sure we're always prepared for whatever the skies throw our way. </p> </marquee> </div> </div>GWALDORS INTRO TO OCEN DEEP As the renowned Dwarven Explorer and your guide to the fantastical world of Empire Fable, I welcome you to this interactive fiction game. Here, you will journey with me and my crew as we embark on expeditions and quests throughout the vast lands of the Empire. Allow me to introduce two of my most trusted companions, Arabella BrassFord and Adalyn Mechmaster. Arabella, a member of the esteemed Guild of Science, has generously financed many of our expeditions and has proven herself to be a great asset to our team with her expertise and resourcefulness. Adalyn, our skilled mechanic, keeps our airship, the CloudStrider, running smoothly and efficiently. She's always busy fixing and tinkering with something, but she's never too busy to lend a hand in our adventures. As we journey together, we'll explore the great city of Ocean Deep, with its bustling market district and renowned arena. And who knows, we may even run into other great adventurers like ourselves along the way. So, prepare yourself for the adventure of a lifetime. Together, we'll conquer new lands, face perilous challenges, and discover the hidden treasures of Empire Fable.