A semi-simple dyed object effect.
Forked from Alain Galvan's Pen Canvas Blend Modes to Recolor Sprites.
A semi-simple dyed object effect.
Forked from Alain Galvan's Pen Canvas Blend Modes to Recolor Sprites.
A semi-simple dyed object effect.
Forked from Alain Galvan's Pen Canvas Blend Modes to Recolor Sprites.
<div id="game"></div> |
var globalTimer = 0; | |
var globalGlowLoop = 0; | |
var itemTypes = 5; | |
var shapeMaps = []; // TODO | |
var glowMaps = []; // TODO | |
var itemSprites = new Image(); | |
itemSprites.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAABgCAYAAABbjPFwAAADwUlEQVR4nO2asU7rMBSG+yKMPABiZ+h0ERNsXJYrRu6V2Fh4AQaEGChdQIgJBgYWJgSsLAxIiBF4AARMTKi++aOc6tR1HMfHTijyL/1qm/TU33/stA6iMxgMOrCvpPViSQcvq7+7u1NXV1fq9PRUHR4eqrqfK6rPoFThv4Xz13U+gw++s7NTO4C0nkL8Klz/AzJ5Dx6ivggwV9gbojWlAJm+vr4UHJrNKnYBj13EdYMA/uPjo7kABeSfzMuZF9kMzLnOBnUdfnt7Uy8vL/kjuQn4f5l/l8BbQwD68/NzCAt47qghigDLDvBzT09PRgiC18G58QMVM0DZshn6+vp6/ubmxgjhAo8fp5gB5m3g8O7u7uLJyYlzAEDzbYHkh801xBg0gdvgSQTMoUVbgrrC+iZjqZABrsPH2gg2okGAbXhSkkFLS0sKdj2ua2ZmRsGux4OJAF9fX9XFxcUILB2H8Xx2drYUcG9vT62vr4/A0nEYz6empsIGARSgCRLPOSQPYDoPKEATJJ5zSB7AdD5IAA7Iu206ZwrAAan7fNnx90QJwGeAg5qOVc0ABcD79JB4X/AAFKIqgAleB+RrnwfgQYPDl4U4OjoahrDBc0D+mur5sWjwkOlidel+mXT46AKcZAZ0mWYgqvQAOvzEzADBUgd9A6A+Ju+YAMdh6wbQz5kC1GmAlzCAzbHrk368ut2uIrfNUluAxh8OHh8fJy8Ah8fjRAVoDH56elrBIT+z0c4Dfm1tLViIVpYN4Hu9XpAQraz5kAFau2B5iNDXRGOiEHWvCdzUSHaR0voR0Qy4hqCbe99Nl3d9BtexAbouJXROsmP0rg954bYi6jBmIikpSa6NjQ21urqae2FhIXedbxdpvUgYvOycy425tF4kdMzlfWUQ0nqxbAC0LLAcYtWL5AsWql4sG4BL16T1YlWty9jnR5TdgOTu9/tqe3tb0esqfZsQBE/gm5ubyiWAPgj+Aerh4UGZzvlAOgfgXSfjnsCpuBDBn5+fq62trWZ3ubzr8MrKivMMcB0cHDQPD/GuE7xPgKSkpKTmhX+1vL+/N/ry8rLyK1VaL9L7+7siPT8/j/j29ja3DQL1VWNEC8Hh9RAEbwvhAk8aq+/1euIAmOKyrp+dnQ19fHys9vf3x2BR7zqWqT6IeAi96zZ4Xl81RjR4DqHD07JxGdwWIjo8CZ12WTa2ev1YY/AcguwzOA/RODwfWDK4tP7nKMTXbNKPVXaFKJU/TGZ9R1T8DeqTkpKSkpKSkpI81fZGTryL/Q7b8P/CoXlWBeFGFAAAAABJRU5ErkJggg=="; | |
var enchSprite = new Image(); | |
enchSprite.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAArUlEQVR4nO2WjQqAIAyEff+XXn8oFtN5zmmLDoLQfdeZlAsUQjguRLQr3Sv5y4DaLShT5NGH53wc5NNVTHrmxBo2HSAtX083iTczNgtmIimttBh4tUtfb/qeAZPbj0jJ8wOygYqH5G57VDxXqNpbkIfUcwpyHu3Fj5WgAYr8K/oBFB7eDxTTVUx65sSavx+wNP5WPzCcd3fgLO8HRMORcrc9Kt5VP3Caz+St+oENabmTe+GCasUAAAAASUVORK5CYII="; | |
/** | |
* Manages rendering objects on canvas. | |
*/ | |
var Engine = (function() { | |
// Initzalize Renderer | |
function Engine() { | |
this.canvas = document.createElement('canvas'); | |
this.canvas.tabIndex = 1; | |
this.canvas.width = 320; | |
this.canvas.height = 240; | |
this.context = this.canvas.getContext('2d'); | |
this.generateEffectMaps(); | |
} | |
Engine.prototype.generateEffectMaps = function() { | |
var tempContext = document.createElement('canvas').getContext('2d'); | |
tempContext.canvas.width = 16; | |
tempContext.canvas.height = 16; | |
//Generate shape maps. | |
for (var i = 0; i < itemTypes; i++) { | |
tempContext.globalCompositeOperation = 'source-over'; | |
tempContext.clearRect(0, 0, 16, 16); | |
tempContext.drawImage(itemSprites, 0, 0 + (i * 16), 16, 16, 0, 0, 16, 16); | |
tempContext.drawImage(itemSprites, 16, 0 + (i * 16), 16, 16, 0, 0, 16, 16); | |
tempContext.drawImage(itemSprites, 32, 0 + (i * 16), 16, 16, 0, 0, 16, 16); | |
for (var a = 0; a < 6; a++) { | |
tempContext.globalCompositeOperation = 'lighter'; | |
tempContext.drawImage(itemSprites, 0, 0 + (i * 16), 16, 16, 0, 0, 16, 16); | |
tempContext.drawImage(itemSprites, 16, 0 + (i * 16), 16, 16, 0, 0, 16, 16); | |
tempContext.drawImage(itemSprites, 32, 0 + (i * 16), 16, 16, 0, 0, 16, 16); | |
} | |
shapeMaps[i] = convertCanvasToImage(tempContext.canvas); | |
} | |
//Generate glow maps. | |
for (var i = 0; i < itemTypes; i++) { | |
tempContext.globalCompositeOperation = 'source-over'; | |
tempContext.clearRect(0, 0, 16, 16); | |
tempContext.drawImage(shapeMaps[i], 0, 0, 16, 16, -1, 0, 16, 16); | |
tempContext.drawImage(shapeMaps[i], 0, 0, 16, 16, 1, 0, 16, 16); | |
tempContext.drawImage(shapeMaps[i], 0, 0, 16, 16, 0, -1, 16, 16); | |
tempContext.drawImage(shapeMaps[i], 0, 0, 16, 16, 0, 1, 16, 16); | |
tempContext.globalCompositeOperation = 'destination-out'; | |
tempContext.drawImage(shapeMaps[i], 0, 0, 16, 16, 0, 0, 16, 16); | |
glowMaps[i] = convertCanvasToImage(tempContext.canvas); | |
} | |
tempContext.globalCompositeOperation = 'source-over'; | |
} | |
//Refreshes the screen with everything in the scene. | |
Engine.prototype.render = function(scene) { | |
var _this = this; | |
this.context.save(); | |
globalTimer++; | |
//Viewport | |
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
//Render Scene | |
this.context.fillStyle = "#030303"; | |
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); | |
this.context.font = "bold 9px Lucida Bright,Georgia,serif"; | |
this.context.globalCompositeOperation = 'source-over'; | |
/*for (var i = 0; i < thePlayer.inventory.length; i++) { | |
thePlayer.inventory[i].render(_this.context, 8 + ((i % 19) * 16), 8 + (Math.floor(i / 19) * 16)); | |
}*/ | |
/*for (var i = 0; i < shapeMaps.length; i++) { | |
_this.context.drawImage(shapeMaps[i], 0, 0, 16, 16, 0 + (i * 16), 160, 16, 16); | |
} | |
for (var i = 0; i < glowMaps.length; i++) { | |
_this.context.drawImage(glowMaps[i], 0, 0, 16, 16, 0 + (i * 16), 176, 16, 16); | |
}*/ | |
for (var i = 0; i < Math.min(thePlayer.inventory.length, 13); i++) { | |
thePlayer.inventory[i].render(_this.context, 8, 8 + (i * 16)); | |
switch (thePlayer.inventory[i].parts) { | |
case 1: this.context.fillStyle = "#F3F3F3"; break; | |
case 2: this.context.fillStyle = "#8FF"; break; | |
case 3: this.context.fillStyle = "#F8F"; break; | |
case 4: this.context.fillStyle = "#FF8"; break; | |
default: this.context.fillStyle = "#FFF"; break; | |
} | |
_this.context.fillText(thePlayer.inventory[i].name, 26, 8 + (i * 16) + 8); | |
this.context.fillStyle = "#C3C3C3"; | |
_this.context.fillText(thePlayer.inventory[i].parts + " Parts", 26, 8 + (i * 16) + 16); | |
} | |
if (thePlayer.inventory.length > 13) { | |
this.context.fillStyle = "#F3F3F3"; | |
_this.context.fillText("More...", 8, 4 + (14 * 16)); | |
} | |
scene.map(function(o) { | |
if ('render' in o) { | |
if (scene) { | |
o.render(_this.context); | |
} | |
} | |
}); | |
this.context.restore(); | |
}; | |
return Engine; | |
})(); | |
var Player = (function(){ | |
function Player() { | |
this.inventory = []; | |
} | |
return Player; | |
})(); | |
var Item = (function() { | |
function Item(type, parts) { | |
this.colour = []; | |
this.type = type; | |
this.parts = parts; | |
this.name = ""; | |
this.sprite = new Image(); | |
this.sprite.src = itemSprites.src; | |
this.enchEff = new Image(); | |
this.enchEff.src = enchSprite.src; | |
this.tempContext = document.createElement('canvas').getContext('2d'); | |
this.tempContext.canvas.width = 16; | |
this.tempContext.canvas.height = 16; | |
this.initRando(); | |
this.generateSprite(); | |
} | |
Item.prototype.render = function(context, x, y) { | |
context.globalCompositeOperation = 'source-over'; | |
context.drawImage(this.sprite, 0, 0, 16, 16, x, y, 16, 16); | |
if (this.parts > 2) { | |
//Draw on temp canvas. | |
this.tempContext.globalCompositeOperation = 'source-over'; | |
this.tempContext.clearRect(0, 0, 16, 16); | |
this.tempContext.drawImage(glowMaps[this.type], 0, 0, 16, 16, 0, 0, 16, 16); | |
//Apply the colours! | |
this.tempContext.globalCompositeOperation = 'multiply'; | |
this.tempContext.fillStyle = this.colour[2]; | |
this.tempContext.fillRect(0, 0, 16, 16); | |
//Remove outside. | |
this.tempContext.globalCompositeOperation = 'destination-in'; | |
this.tempContext.drawImage(glowMaps[this.type], 0, 0, 16, 16, 0, 0, 16, 16); | |
context.globalAlpha = globalGlowLoop; | |
//Draw the Item. | |
context.drawImage(this.tempContext.canvas, 0, 0, 16, 16, x, y, 16, 16); | |
context.globalAlpha = 1; | |
} | |
if (this.parts > 3) { | |
var enchOffset = 16 - ((globalTimer / 4) % 16); | |
//Draw on temp canvas. | |
this.tempContext.globalCompositeOperation = 'source-over'; | |
this.tempContext.clearRect(0, 0, 16, 16); | |
this.tempContext.drawImage(this.enchEff, 0 + enchOffset, 0 + enchOffset, 16, 16, 0, 0, 16, 16); | |
//Apply the colours! | |
this.tempContext.globalCompositeOperation = 'multiply'; | |
this.tempContext.fillStyle = this.colour[3]; | |
this.tempContext.fillRect(0, 0, 16, 16); | |
//Remove outside. | |
this.tempContext.globalCompositeOperation = 'destination-in'; | |
this.tempContext.drawImage(this.enchEff, 0 + enchOffset, 0 + enchOffset, 16, 16, 0, 0, 16, 16); | |
this.tempContext.drawImage(shapeMaps[this.type], 0, 0, 16, 16, 0, 0, 16, 16); | |
context.globalAlpha = 0.5; | |
context.globalCompositeOperation = 'lighter'; | |
context.drawImage(this.tempContext.canvas, 0, 0, 16, 16, x, y, 16, 16); | |
context.globalCompositeOperation = 'source-over'; | |
context.globalAlpha = 1; | |
} | |
}; | |
Item.prototype.initRando = function() { | |
var rando; | |
switch (this.type) { | |
case 0: this.name = "Potion"; break; | |
case 1: this.name = "Tunic"; break; | |
case 2: this.name = "Sword"; break; | |
case 3: this.name = "Amulet"; break; | |
case 4: this.name = "Gemstone"; break; | |
default: this.name = "ERROR"; break; | |
} | |
if (this.parts > 0) { | |
rando = randomRangeRounded(0, 11); | |
switch (rando) { | |
case 0: this.colour.push("#CBA"); this.name = "Iron " + this.name; break; | |
case 1: this.colour.push("#45E"); this.name = "Cobalt " + this.name; break; | |
case 2: this.colour.push("#B96"); this.name = "Bronze " + this.name; break; | |
case 3: this.colour.push("#6E7"); this.name = "Emerald " + this.name; break; | |
case 4: this.colour.push("#DDE"); this.name = "Silver " + this.name; break; | |
case 5: this.colour.push("#BA2"); this.name = "Golden " + this.name; break; | |
case 6: this.colour.push("#3AC"); this.name = "Crystal " + this.name; break; | |
case 7: this.colour.push("#132"); this.name = "Ebonic " + this.name; break; | |
case 8: this.colour.push("#888"); this.name = "Neutral " + this.name; break; | |
case 9: this.colour.push("#600"); this.name = "Blooded " + this.name; break; | |
case 10: this.colour.push("#5C3"); this.name = "Wacky " + this.name; break; | |
case 11: this.colour.push("#EEE"); this.name = "Light " + this.name; break; | |
default: this.colour.push("#404"); this.name = "ERROR " + this.name; break; | |
} | |
} else { | |
this.name = "NO PARTS"; | |
} | |
if (this.parts > 1) { | |
rando = randomRangeRounded(0, 6); | |
switch (rando) { | |
case 0: this.colour.push("#BA2"); this.name = "Guilded " + this.name; break; | |
case 1: this.colour.push("#CBA"); this.name = "Bolstered " + this.name; break; | |
case 2: this.colour.push("#FBD"); this.name = "Unusual " + this.name; break; | |
case 3: this.colour.push("#C45"); this.name = "Enrosed " + this.name; break; | |
case 4: this.colour.push("#75A"); this.name = "Warpular " + this.name; break; | |
case 5: this.colour.push("#888"); this.name = "Nullified " + this.name; break; | |
case 6: this.colour.push("#EEE"); this.name = "Pale " + this.name; break; | |
default: this.colour.push("#404"); this.name = "ERROR " + this.name; break; | |
} | |
} | |
if (this.parts > 2) { | |
rando = randomRangeRounded(0, 10); | |
switch (rando) { | |
case 0: this.colour.push("#0DF"); this.name += " of Souls"; break; | |
case 1: this.colour.push("#C22"); this.name += " of Rage"; break; | |
case 2: this.colour.push("#CB2"); this.name += " of Cheese"; break; | |
case 3: this.colour.push("#0BE"); this.name += " of Neon"; break; | |
case 4: this.colour.push("#1D1"); this.name += " of Tech"; break; | |
case 5: this.colour.push("#FFA"); this.name += " of the Divine"; break; | |
case 6: this.colour.push("#888"); this.name += " of Null"; break; | |
case 7: this.colour.push("#080"); this.name += " of Links"; break; | |
case 8: this.colour.push("#801"); this.name += " of Blood"; break; | |
case 9: this.colour.push("#00C"); this.name += " of Posidon"; break; | |
case 10: this.colour.push("#EEE"); this.name += " of Purity"; break; | |
default: this.colour.push("#404"); this.name += " of ERRORs"; break; | |
} | |
} | |
if (this.parts > 3) { | |
rando = randomRangeRounded(0, 7); | |
switch (rando) { | |
case 0: this.colour.push("#B00"); this.name += " I"; break; | |
case 1: this.colour.push("#BB0"); this.name += " II"; break; | |
case 2: this.colour.push("#0B0"); this.name += " III"; break; | |
case 3: this.colour.push("#0BB"); this.name += " IV"; break; | |
case 4: this.colour.push("#00B"); this.name += " V"; break; | |
case 5: this.colour.push("#B0B"); this.name += " VI"; break; | |
case 6: this.colour.push("#BBB"); this.name += " VII"; break; | |
case 7: this.colour.push("#FFF"); this.name += " IIX"; break; | |
default: this.colour.push("#404"); this.name += " ???"; break; | |
} | |
} | |
}; | |
Item.prototype.generateSprite = function() { | |
var tempContext2 = document.createElement('canvas').getContext('2d'); | |
tempContext2.canvas.width = 16; | |
tempContext2.canvas.height = 16; | |
this.tempContext.globalCompositeOperation = 'source-over'; | |
this.tempContext.clearRect(0, 0, 16, 16); | |
this.tempContext.drawImage(itemSprites, 16, 0 + (this.type * 16), 16, 16, 0, 0, 16, 16); | |
this.tempContext.globalCompositeOperation = 'multiply'; | |
this.tempContext.fillStyle = this.colour[0]; | |
this.tempContext.fillRect(0, 0, 16, 16); | |
this.tempContext.globalCompositeOperation = 'destination-in'; | |
this.tempContext.drawImage(itemSprites, 16, 0 + (this.type * 16), 16, 16, 0, 0, 16, 16); | |
tempContext2.drawImage(this.tempContext.canvas, 0, 0, 16, 16, 0, 0, 16, 16); | |
if (this.parts > 1) { | |
this.tempContext.globalCompositeOperation = 'source-over'; | |
this.tempContext.clearRect(0, 0, 16, 16); | |
this.tempContext.drawImage(itemSprites, 32, 0 + (this.type * 16), 16, 16, 0, 0, 16, 16); | |
this.tempContext.globalCompositeOperation = 'multiply'; | |
this.tempContext.fillStyle = this.colour[1]; | |
this.tempContext.fillRect(0, 0, 16, 16); | |
this.tempContext.globalCompositeOperation = 'destination-in'; | |
this.tempContext.drawImage(itemSprites, 32, 0 + (this.type * 16), 16, 16, 0, 0, 16, 16); | |
tempContext2.drawImage(this.tempContext.canvas, 0, 0, 16, 16, 0, 0, 16, 16); | |
} | |
tempContext2.drawImage(itemSprites, 0, 0 + (this.type * 16), 16, 16, 0, 0, 16, 16); | |
this.sprite = convertCanvasToImage(tempContext2.canvas); | |
}; | |
return Item; | |
})(); | |
/** | |
* Main Start | |
*/ | |
var engine, scene, thePlayer; | |
function start() { | |
engine = new Engine(); | |
thePlayer = new Player(); | |
var canvas = engine.canvas; | |
document.getElementById('game').appendChild(canvas); | |
canvas.focus(); | |
scene = []; | |
for (var i = 0; i < 266; i++) { | |
thePlayer.inventory.push(new Item(randomRangeRounded(0, itemTypes - 1), randomRangeRounded(1, 4))); | |
} | |
} | |
function animate() { | |
globalGlowLoop = Math.abs(Math.sin(globalTimer / 30)); | |
engine.render(scene); | |
requestAnimationFrame(animate); | |
} | |
function randomRangeRounded(min, max) { | |
return Math.floor((Math.random() * ((max + 0.49) - min)) + min); | |
} | |
function getRandomColor() { | |
var letters = '0123456789ABCDEF'.split(''); | |
var color = '#'; | |
for (var i = 0; i < 6; i++) { | |
color += letters[Math.floor(Math.random() * 16)]; | |
} | |
return color; | |
} | |
function convertCanvasToImage(canvas) { | |
// Credit to David Walsh. | |
var image = new Image(); | |
image.src = canvas.toDataURL("image/png"); | |
return image; | |
} | |
start(); | |
animate(); |
html, body { | |
margin: 0; | |
padding: 0; | |
width: 100%; | |
height: 100%; | |
background: #154615; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
flex-direction: column; | |
font-family: sans-serif; | |
color: #fff; | |
} | |
body { | |
background-color: rgba(0, 0, 0, 1.0); | |
background-image: | |
linear-gradient(rgba(0, 160, 0, 0.25), rgba(0, 64, 0, 0.1)), | |
radial-gradient(circle, rgba(46, 224, 72, 0.9), rgba(16, 64, 24, 0.1)); | |
} | |
canvas { | |
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6); | |
outline-style: dashed; | |
outline-color: 'white'; | |
outline-width: 1px; | |
image-rendering: -moz-crisp-edges; | |
image-rendering: -webkit-optimize-contrast; | |
image-rendering: pixelated; | |
} | |
canvas:focus { | |
outline-style: solid; | |
outline-color: 'white'; | |
outline-width: 1px; | |
} | |
@media (min-width:640px) and (min-height:480px) { | |
canvas { | |
transform: scale(2); | |
} | |
} | |
@media (min-width:960px) and (min-height:720px) { | |
canvas { | |
transform: scale(3); | |
} | |
} |