diff --git a/src/client/images/bigObjects.png b/src/client/images/bigObjects.png index 06855258..b989cd85 100644 Binary files a/src/client/images/bigObjects.png and b/src/client/images/bigObjects.png differ diff --git a/src/client/images/bigObjects.pyxel b/src/client/images/bigObjects.pyxel index 45aeea94..f0af4a9e 100644 Binary files a/src/client/images/bigObjects.pyxel and b/src/client/images/bigObjects.pyxel differ diff --git a/src/client/images/bosses.png b/src/client/images/bosses.png index 0041dd45..7db0d026 100644 Binary files a/src/client/images/bosses.png and b/src/client/images/bosses.png differ diff --git a/src/client/images/bosses.pyxel b/src/client/images/bosses.pyxel index 0e415fcc..d3015eb4 100644 Binary files a/src/client/images/bosses.pyxel and b/src/client/images/bosses.pyxel differ diff --git a/src/client/images/objects.png b/src/client/images/objects.png index 0af5ba01..0440978d 100644 Binary files a/src/client/images/objects.png and b/src/client/images/objects.png differ diff --git a/src/client/images/objects.pyxel b/src/client/images/objects.pyxel index 36fe394d..67227dad 100644 Binary files a/src/client/images/objects.pyxel and b/src/client/images/objects.pyxel differ diff --git a/src/client/images/tiles.png b/src/client/images/tiles.png index 11ccf7c2..11bbd4d8 100644 Binary files a/src/client/images/tiles.png and b/src/client/images/tiles.png differ diff --git a/src/client/images/tiles.pyxel b/src/client/images/tiles.pyxel index 2e3e6f2b..e3eb6ef9 100644 Binary files a/src/client/images/tiles.pyxel and b/src/client/images/tiles.pyxel differ diff --git a/src/client/images/walls.png b/src/client/images/walls.png index e799bdae..32e72d8b 100644 Binary files a/src/client/images/walls.png and b/src/client/images/walls.png differ diff --git a/src/client/images/walls.pyxel b/src/client/images/walls.pyxel index 01d320bf..ccf2bb23 100644 Binary files a/src/client/images/walls.pyxel and b/src/client/images/walls.pyxel differ diff --git a/src/client/js/canvas.js b/src/client/js/canvas.js new file mode 100644 index 00000000..685af4af --- /dev/null +++ b/src/client/js/canvas.js @@ -0,0 +1,303 @@ +define([ + 'js/system/events', + 'js/resources', + 'js/rendering/tileOpacity', + 'js/misc/physics', + 'js/spriteBuilder' +], function( + events, + resources, + tileOpacity, + physics, + spriteBuilder +) { + return { + map: null, + mapSprite: null, + + //TEST: Remove + visMap: null, + + pos: { + x: 0, + y: 0 + }, + moveTo: null, + moveSpeed: 0, + moveSpeedMax: 1.5, + moveSpeedInc: 0.5, + moveSpeedFlatten: 16, + + size: { + x: 0, + y: 0, + }, + layers: {}, + + tileSize: { + w: 32, + h: 32 + }, + + zoneId: null, + + player: null, + + init: function() { + events.on('onGetMap', this.onGetMap.bind(this)); + + var canvas = $('.canvasContainer canvas'); + + canvas.each(function(i, c) { + c = $(c); + + if (!c.attr('layer')) + return; + + this.layers[c.attr('layer')] = { + canvas: c, + ctx: c[0].getContext('2d') + }; + + c[0].width = this.size.x = $('body').width(); + c[0].height = this.size.y = $('body').height(); + c.css('z-index', i + 1); + }.bind(this)); + + this.layers.particles.ctx.globalCompositeOperation = 'lighter'; + + $('.canvasContainer') + .width(this.size.x) + .height(this.size.y); + }, + onGetMap: function(msg) { + $('.canvasContainer') + .removeClass('visible'); + + setTimeout(function() { + $('.canvasContainer').addClass('visible'); + }, 1000); + + if (this.zoneId != null) { + events.emit('onRezone', this.zoneId); + } + + this.zoneId = msg.zoneId; + + $('.canvasContainer').addClass('visible'); + + this.map = msg.map; + this.visMap = msg.visMap; + + physics.init(msg.collisionMap); + + msg.clientObjects.forEach(function(c) { + c.zoneId = this.zoneId; + events.emit('onGetObject', c); + }, this); + + this.mapSprite = spriteBuilder.buildSprite( + ['tiles', 'walls', 'objects'], [this.map['tiles'], this.map['walls'], this.map['objects']], [0.55, 0.85, 0.85] + ); + }, + fadeOut: function() { + $('.canvasContainer') + .removeClass('visible'); + + setTimeout(function() { + $('.canvasContainer').addClass('visible'); + }, 1000); + }, + setPosition: function(pos, instant) { + if (instant) { + this.fadeOut(); + + this.moveTo = null; + this.pos = pos; + return; + } + + this.moveTo = pos; + }, + clear: function(filter) { + for (var l in this.layers) { + var ctx = this.layers[l].ctx; + + ctx.save(); + + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, this.size.x, this.size.y); + + ctx.restore(); + } + }, + begin: function() { + if (this.moveTo) { + var deltaX = this.moveTo.x - this.pos.x; + var deltaY = this.moveTo.y - this.pos.y; + + if ((deltaX != 0) || (deltaY != 0)) { + var moveSpeed = this.moveSpeed; + var distance = Math.max(Math.abs(deltaX), Math.abs(deltaY)); + if (moveSpeed > distance) + moveSpeed = distance; + + if (distance > this.moveSpeedFlatten) { + var maxSpeed = this.moveSpeedMax; + if (distance > 64) { + maxSpeed += (distance - 64) / 1000; + } + if (this.moveSpeed < maxSpeed) + this.moveSpeed += this.moveSpeedInc; + } + + deltaX = (deltaX / distance) * moveSpeed; + deltaY = (deltaY / distance) * moveSpeed; + + this.pos.x = this.pos.x + (deltaX); + this.pos.y = this.pos.y + (deltaY); + } else { + this.moveSpeed = 0; + this.moveTo = null; + } + } + + for (var l in this.layers) { + var ctx = this.layers[l].ctx; + + ctx.save(); + ctx.translate(-~~this.pos.x, -~~this.pos.y); + } + }, + end: function() { + for (var l in this.layers) { + var ctx = this.layers[l].ctx; + + ctx.restore(); + } + }, + + renderMap: function() { + if (!this.player) + return; + + this.layers['tiles'].ctx.drawImage( + this.mapSprite, + this.pos.x, + this.pos.y, + this.size.x, + this.size.y, + this.pos.x, + this.pos.y, + this.size.x, + this.size.y + ); + }, + renderObject: function(obj) { + if (!this.player) + return; + + var x = obj.x; + var y = obj.y; + + var pX = this.player.x; + var pY = this.player.y; + + var dx = Math.abs(x - pX); + var dy = Math.abs(y - pY); + + var dxMax = (this.size.x / 64) + 4; + var dyMax = (this.size.y / 64) + 4; + + if ((dx > dxMax) || (dy > dyMax)) + return; + + var sprite = resources.sprites[obj.sheetName].image; + var ctx = this.layers[obj.layerName || obj.sheetName].ctx; + + var tileY = ~~(obj.cell / 8); + var tileX = obj.cell - (tileY * 8); + + var offsetX = obj.offsetX || 0; + var offsetY = obj.offsetY || 0; + + var alpha = 1; + if (obj.alpha != null) + alpha = obj.alpha; + + ctx.globalAlpha = alpha; + + var size = obj.size || 32; + + if (obj.flipX) { + ctx.save(); + ctx.scale(-1, 1); + ctx.drawImage( + sprite, + tileX * 32, + tileY * 32, + 32, + 32, + -(x * 32) - (~~(offsetX / 4) * 4), + (y * 32) + (~~(offsetY / 4) * 4), + -32, + 32 + ); + ctx.restore(); + } + else if (obj.flipY) { + ctx.save(); + ctx.scale(1, -1); + ctx.drawImage( + sprite, + tileX * 32, + tileY * 32, + 32, + 32, + (x * 32) - (~~(offsetX / 4) * 4), + -(y * 32) + (~~(offsetY / 4) * 4), + 32, + -32 + ); + ctx.restore(); + } else + ctx.drawImage(sprite, tileX * size, tileY * size, size, size, (x * 32) + (~~(offsetX / 4) * 4), (y * 32) + (~~(offsetY / 4) * 4), size, size); + }, + renderRect: function(layer, x, y, color) { + if (!this.player) + return; + + var pX = this.player.x; + var pY = this.player.y; + + var dx = Math.abs(x - pX); + var dy = Math.abs(y - pY); + + var dxMax = (this.size.x / 64) + 4; + var dyMax = (this.size.y / 64) + 4; + + if ((dx > dxMax) || (dy > dyMax)) + return; + + var ctx = this.layers[layer].ctx; + ctx.fillStyle = color; + ctx.fillRect(x * 32, y * 32, 32, 32); + }, + renderText: function(layer, text, x, y) { + var ctx = this.layers[layer].ctx; + ctx.fillStyle = 'white'; + + ctx.fillText(text, x, y); + }, + renderOutlineText: function(layer, text, x, y, centerX) { + var ctx = this.layers[layer].ctx; + ctx.lineWidth = 2; + + if (centerX) + x -= (ctx.measureText(text).width / 2); + + ctx.strokeText(text, x, y); + ctx.fillText(text, x, y); + } + }; +}); \ No newline at end of file diff --git a/src/client/js/components/social.js b/src/client/js/components/social.js new file mode 100644 index 00000000..cade9098 --- /dev/null +++ b/src/client/js/components/social.js @@ -0,0 +1,15 @@ +define([ + 'js/system/events' +], function( + events +) { + return { + type: 'social', + + customChannels: null, + + init: function() { + events.emit('onGetCustomChatChannels', this.customChannels); + } + }; +}); \ No newline at end of file diff --git a/src/client/js/renderer.js b/src/client/js/renderer.js new file mode 100644 index 00000000..3d71f0b3 --- /dev/null +++ b/src/client/js/renderer.js @@ -0,0 +1,716 @@ +define([ + 'js/resources', + 'js/system/events', + 'js/misc/physics', + 'js/rendering/effects', + 'js/rendering/tileOpacity', + 'js/rendering/particles', + 'js/rendering/shaders/outline' +], function( + resources, + events, + physics, + effects, + tileOpacity, + particles, + shaderOutline +) { + var scale = 40; + var scaleMult = 5; + var pixi = PIXI; + + return { + stage: null, + layers: { + objects: null, + mobs: null, + characters: null, + attacks: null, + effects: null, + particles: null, + tileSprites: null, + hiders: null + }, + + chunkSize: 30, + + titleScreen: false, + + pad: { + x: 10, + y: 10 + }, + + width: 0, + height: 0, + + pos: { + x: 0, + y: 0 + }, + moveTo: null, + moveSpeed: 0, + moveSpeedMax: 1.50, + moveSpeedInc: 0.5, + moveSpeedFlatten: 16, + + zoneId: null, + + textures: {}, + textureCache: {}, + + lastTick: null, + + init: function() { + PIXI.GC_MODES.DEFAULT = PIXI.GC_MODES.AUTO; + PIXI.SCALE_MODES.DEFAULT = PIXI.SCALE_MODES.NEAREST; + + events.on('onGetMap', this.onGetMap.bind(this)); + events.on('onDeath', this.onDeath.bind(this)); + events.on('onToggleFullscreen', this.toggleScreen.bind(this)); + + this.width = $('body').width(); + this.height = $('body').height(); + + this.pad.x = ~~((this.width / 2) / 32); + this.pad.y = ~~((this.height / 2) / 32); + + this.renderer = pixi.autoDetectRenderer(this.width, this.height, { + backgroundColor: 0x2d2136 + }); + + window.onresize = this.onResize.bind(this); + + $(this.renderer.view) + .appendTo('.canvasContainer'); + + this.stage = new pixi.Container(); + + var layers = this.layers; + Object.keys(layers).forEach(function(l) { + if (l == 'tileSprites') { + layers[l] = new pixi.particles.ParticleContainer(2500); + layers[l].layer = 'tiles'; + } else + layers[l] = new pixi.Container(); + + this.stage.addChild(layers[l]) + }, this); + + var spriteNames = ['sprites', 'tiles', 'mobs', 'bosses', 'bigObjects', 'objects', 'characters', 'attacks', 'auras', 'walls', 'ui', 'animChar', 'animMob', 'animBoss']; + resources.spriteNames.forEach(function(s) { + if (s.indexOf('.png') > -1) + spriteNames.push(s); + }); + + spriteNames.forEach(function(t) { + this.textures[t] = new pixi.BaseTexture(resources.sprites[t].image); + this.textures[t].scaleMode = pixi.SCALE_MODES.NEAREST; + }, this); + + particles.init({ + renderer: this.renderer, + stage: this.layers.particles + }); + }, + + toggleScreen: function() { + var screenMode = 0; + + var isFullscreen = (window.innerHeight == screen.height); + if (isFullscreen) + screenMode = 0; + else + screenMode = 1; + + if (screenMode == 0) { + (document.cancelFullscreen || document.msCancelFullscreen || document.mozCancelFullscreen || document.webkitCancelFullScreen).call(document); + return 'Windowed'; + } else if (screenMode == 1) { + var el = $('body')[0]; + (el.requestFullscreen || el.msRequestFullscreen || el.mozRequestFullscreen || el.webkitRequestFullscreen).call(el); + return 'Fullscreen'; + } + }, + + buildTitleScreen: function() { + this.titleScreen = true; + + this.setPosition({ + x: 0, + y: 0 + }, true); + + var w = Math.ceil(this.width / scale) + 1; + var h = Math.ceil(this.height / scale) + 1; + + var container = this.layers.tileSprites; + + for (var i = 0; i < w; i++) { + for (var j = 0; j < h; j++) { + var tile = 5; + if (Math.random() < 0.4) + tile = 6; + var tile = new pixi.Sprite(this.getTexture('sprites', tile)); + + var alpha = Math.sin((i % 4) + Math.cos(j % 8)); + if (tile == 5) + alpha /= 2; + + tile.alpha = alpha; + tile.position.x = i * scale; + tile.position.y = j * scale; + tile.width = scale; + tile.height = scale; + + if (Math.random() < 0.5) { + tile.position.x += scale; + tile.scale.x = -scaleMult; + } + + container.addChild(tile); + } + } + }, + + onDeath: function(pos) { + this.setPosition({ + x: (pos.x - (this.width / (scale * 2))) * scale, + y: (pos.y - (this.height / (scale * 2))) * scale + }, true); + }, + + onResize: function() { + var zoom = window.devicePixelRatio; + + this.width = $('body').width() * zoom; + this.height = $('body').height() * zoom; + + this.renderer.resize(this.width, this.height); + if (window.player) { + this.setPosition({ + x: (window.player.x - (this.width / (scale * 2))) * scale, + y: (window.player.y - (this.height / (scale * 2))) * scale + }, true); + } + + if (this.titleScreen) { + this.clean(); + this.buildTitleScreen(); + } + + events.emit('onResize'); + }, + + getTexture: function(baseTex, cell, size) { + size = size || 8; + var name = baseTex + '_' + cell; + + var textureCache = this.textureCache; + + var cached = textureCache[name]; + + if (!cached) { + var y = ~~(cell / 8); + var x = cell - (y * 8); + cached = new pixi.Texture(this.textures[baseTex], new pixi.Rectangle(x * size, y * size, size, size)); + textureCache[name] = cached; + } + + return cached; + }, + + clean: function() { + var container = this.layers.tileSprites; + this.stage.removeChild(container); + + this.layers.tileSprites = container = new pixi.particles.ParticleContainer(2500); + container.layer = 'tiles'; + this.stage.addChild(container); + + this.stage.children.sort(function(a, b) { + if (a.layer == 'tiles') + return -1; + else if (b.layer == 'tiles') + return 1; + else + return 0; + }, this); + }, + + onGetMapCustomization: function(msg) { + if (!msg.collide) { + var children = this.layers.tiles.children; + var cLen = children.length; + var x = msg.x * scale; + var y = msg.y * scale; + for (var i = cLen - 1; i >= 0; i--) { + var c = children[i]; + var cx = c.x; + if (c.scale.x < 0) + cx -= scale; + if ((cx == x) && (c.y == y)) { + c.parent.removeChild(c); + break; + } + } + } + + var tile = new pixi.Sprite(this.getTexture('sprites', msg.tile)) + + tile.alpha = tileOpacity.map(msg.tile); + tile.position.x = msg.x * scale; + tile.position.y = msg.y * scale; + tile.width = scale; + tile.height = scale; + + if (Math.random() < 0.5) { + tile.position.x += scale; + tile.scale.x = -scaleMult; + } + + this.layers.tiles.addChild(tile); + + physics.collisionMap[msg.x][msg.y] = msg.collide; + physics.graph.grid[msg.x][msg.y] = !msg.collide; + }, + + buildTile: function(c, i, j) { + var alpha = tileOpacity.map(c); + var canFlip = tileOpacity.canFlip(c); + + var tile = new pixi.Sprite(this.getTexture('sprites', c + (0 * 160))); + + tile.alpha = alpha; + tile.position.x = i * 8; + tile.position.y = j * 8; + tile.width = 8; + tile.height = 8; + + if (canFlip) { + if (Math.random() < 0.5) { + tile.position.x += 8; + tile.scale.x = -1; + } + } + + return tile; + }, + + onGetMap: function(msg) { + this.titleScreen = false; + physics.init(msg.collisionMap); + + var map = msg.map; + var w = this.w = map.length; + var h = this.h = map[0].length; + + var hiddenWalls = msg.hiddenWalls; + var hiddenTiles = msg.hiddenTiles; + + this.hiddenRooms = msg.hiddenRooms; + this.hiddenRooms.forEach(function(h) { + h.container = new pixi.Container(); + this.layers.hiders.addChild(h.container); + + this.buildRectangle({ + x: h.x * scale, + y: h.y * scale, + w: h.width * scale, + h: h.height * scale, + color: 0x2d2136, + parent: h.container + }); + + for (var i = h.x; i < h.x + h.width; i++) { + for (var j = h.y; j < h.y + h.height; j++) { + var cell = hiddenTiles[i][j]; + if (cell != 0) { + var tile = this.buildTile(cell - 1, i, j); + + tile.position.x *= scaleMult; + tile.position.y *= scaleMult; + tile.width = scale; + tile.height = scale; + + h.container.addChild(tile); + } + + cell = hiddenWalls[i][j]; + if (cell == 0) + continue; + + var tile = this.buildTile(cell - 1, i, j); + + tile.position.x *= scaleMult; + tile.position.y *= scaleMult; + tile.width = scale; + tile.height = scale; + + h.container.addChild(tile); + } + } + }, this); + + var padding = msg.padding ? JSON.parse(msg.padding) : {}; + + this.clean(); + var container = new pixi.particles.ParticleContainer(270000); + + var isPadX = false; + var isPadY = false; + var padX = 0; + var padY = 0; + + if (!msg.padding) { + padX = 0; + padY = 0; + } + + var chunkSize = this.chunkSize; + + for (var i = -padX; i < w + padX; i++) { + if ((i < 0) || (i >= w)) + isPadX = true; + else + isPadX = false; + + for (var j = -padY; j < h + padY; j++) { + if ((j < 0) || (j >= h)) + isPadY = true; + else + isPadY = false; + + var cell = null; + + cell = map[i][j]; + if (!cell) + continue; + if (!cell.split) + cell += ''; + cell = cell.split(','); + for (var k = 0; k < cell.length; k++) { + var c = cell[k]; + if (c == 0) + continue; + + c--; + + var tile = this.buildTile(c, i, j); + + container.addChild(tile); + } + } + } + + var renderTexture = pixi.RenderTexture.create(w * 8, h * 8); + this.renderer.render(container, renderTexture); + + var cw = w / this.chunkSize; + var ch = h / this.chunkSize; + + for (var i = 0; i < cw; i++) { + var tw = Math.min(this.chunkSize, w - (i * chunkSize)); + + for (var j = 0; j < ch; j++) { + var th = Math.min(this.chunkSize, h - (j * chunkSize)); + + var texture = new pixi.Texture(renderTexture, new pixi.Rectangle(i * this.chunkSize * 8, j * this.chunkSize * 8, tw * 8, th * 8)); + + var sprite = new pixi.Sprite(texture); + sprite.position.x = i * this.chunkSize * scale; + sprite.position.y = j * this.chunkSize * scale; + sprite.width = tw * scale; + sprite.height = th * scale; + + this.layers.tileSprites.addChild(sprite); + } + } + + this.stage.children.sort(function(a, b) { + if (a.layer == 'tiles') + return -1; + else if (b.layer == 'tiles') + return 1; + else + return 0; + }, this); + + if (this.zoneId != null) + events.emit('onRezone', this.zoneId); + this.zoneId = msg.zoneId; + + msg.clientObjects.forEach(function(c) { + c.zoneId = this.zoneId; + events.emit('onGetObject', c); + }, this); + }, + + setPosition: function(pos, instant) { + pos.x += 16; + pos.y += 16; + + this.hideHiders(); + + if (instant) { + this.moveTo = null; + this.pos = pos; + this.stage.x = -~~this.pos.x; + this.stage.y = -~~this.pos.y; + return; + } + + this.moveTo = pos; + }, + + hideHiders: function() { + var player = window.player; + if (!player) + return; + + var x = player.x; + var y = player.y; + + var hiddenRooms = this.hiddenRooms; + var hLen = hiddenRooms.length; + for (var i = 0; i < hLen; i++) { + var h = hiddenRooms[i]; + h.container.visible = ( + (x < h.x) || + (x >= h.x + h.width) || + (y < h.y) || + (y >= h.y + h.height) + ); + } + }, + + update: function() { + var time = +new Date; + + if (this.moveTo) { + var deltaX = this.moveTo.x - this.pos.x; + var deltaY = this.moveTo.y - this.pos.y; + + if ((deltaX != 0) || (deltaY != 0)) { + var moveSpeed = this.moveSpeed; + var distance = Math.max(Math.abs(deltaX), Math.abs(deltaY)); + + var moveSpeedMax = this.moveSpeedMax; + if (distance > 100) + moveSpeedMax *= 1.75; + if (this.moveSpeed < moveSpeedMax) + this.moveSpeed += this.moveSpeedInc; + + var elapsed = time - this.lastTick; + moveSpeed *= (elapsed / 16.67); + + if (moveSpeed > distance) + moveSpeed = distance; + + deltaX = (deltaX / distance) * moveSpeed; + deltaY = (deltaY / distance) * moveSpeed; + + this.pos.x = this.pos.x + (deltaX); + this.pos.y = this.pos.y + (deltaY); + } else { + this.moveSpeed = 0; + this.moveTo = null; + } + + this.stage.x = -~~this.pos.x; + this.stage.y = -~~this.pos.y; + + events.emit('onSceneMove'); + } + + this.lastTick = time; + }, + + buildContainer: function(obj) { + var container = new pixi.Container; + this.layers[obj.layerName || obj.sheetName].addChild(container); + + return container; + }, + + buildRectangle: function(obj) { + var graphics = new pixi.Graphics; + + var alpha = obj.alpha; + if (alpha != null) + graphics.alpha = alpha; + + var fillAlpha = obj.fillAlpha; + if (fillAlpha == null) + fillAlpha = 1; + + graphics.beginFill(obj.color || '0x48edff', fillAlpha); + + if (obj.strokeColor) + graphics.lineStyle(scaleMult, obj.strokeColor); + + graphics.moveTo(obj.x, obj.y); + graphics.lineTo(obj.x + obj.w, obj.y); + graphics.lineTo(obj.x + obj.w, obj.y + obj.h); + graphics.lineTo(obj.x, obj.y + obj.h); + graphics.lineTo(obj.x, obj.y); + + graphics.endFill(); + + (obj.parent || this.layers[obj.layerName || obj.sheetName]).addChild(graphics); + + return graphics; + }, + + moveRectangle(obj) { + var points = obj.sprite.graphicsData[0].shape.points; + if (!points) + return; + + obj.sprite.dirty = true; + obj.sprite.clearDirty = true; + + points[0] = obj.x; + points[1] = obj.y; + points[2] = obj.x + obj.w; + points[3] = obj.y; + points[4] = obj.x + obj.w; + points[5] = obj.y + obj.h; + points[6] = obj.x; + points[7] = obj.y + obj.h; + points[8] = obj.x; + points[9] = obj.y; + }, + + buildObject: function(obj) { + var w = 8; + var h = 8; + if (obj.w) { + w = obj.w / scaleMult; + h = obj.h / scaleMult; + } + + if (obj.sheetName == 'bosses') { + obj.layerName = 'mobs'; + w = 24; + h = 24; + obj.w = w * scaleMult; + obj.h = h * scaleMult; + } else if (obj.sheetName == 'bigObjects') { + obj.layerName = 'mobs'; + w = 24; + h = 24; + obj.w = w * scaleMult; + obj.h = h * scaleMult; + } + + var sprite = new pixi.Sprite(this.getTexture(obj.sheetName, obj.cell, w)) + sprite.x = obj.x * scale; + sprite.y = obj.y * scale; + sprite.width = obj.w || scale; + sprite.height = obj.h || scale; + + if (obj.sheetName == 'bosses') { + sprite.x -= scale; + sprite.y -= (scale * 2); + } else if (obj.sheetName == 'bigObjects') { + sprite.x -= scale; + sprite.y -= (scale * 2); + sprite.alpha = 0.80; + } + + if (obj.flipX) { + sprite.scale.x *= -1; + if ((obj.sheetName == 'bosses') || (obj.sheetName == 'bigObjects')) + sprite.x += (scale * 2); + else + sprite.x += scale; + } + (this.layers[obj.layerName || obj.sheetName] || this.layers.objects).addChild(sprite); + + return sprite; + }, + + addFilter: function(sprite) { + var thickness = 16; + if (sprite.width > scale) + thickness = 8; + + var filter = new shaderOutline(this.renderer.width, this.renderer.height, thickness, '0xffffff'); + + if (!sprite.filters) + sprite.filters = [filter]; + else + sprite.filters.push(); + + return filter; + }, + + removeFilter: function(sprite, filter) { + if (!sprite.filters) + return; + + sprite.filters = null; + }, + + buildText: function(obj) { + var textSprite = new pixi.Text(obj.text, { + fontFamily: 'bitty', + fontSize: (obj.fontSize || 14), + fill: obj.color || 0xF2F5F5, + stroke: 0x2d2136, + strokeThickness: 4, + align: 'center' + }); + + textSprite.x = obj.x - (textSprite.width / 2); + textSprite.y = obj.y; + + var parent = obj.parent || this.layers[obj.layerName] + parent.addChild(textSprite); + + return textSprite; + }, + + buildEmitter: function(config) { + return particles.buildEmitter(config); + }, + + destroyEmitter: function(emitter) { + particles.destroyEmitter(emitter); + }, + + setSprite: function(obj) { + var cell = obj.cell; + var y = ~~(cell / 8); + var x = cell - (y * 8); + + var baseTex = this.textures[obj.sheetName]; + obj.sprite.texture = this.getTexture(obj.sheetName, obj.cell, obj.sprite.width / scaleMult); + }, + + reorder: function(sprite) { + var mobLayer = this.layers.mobs; + var mobs = mobLayer.children; + mobs.sort(function(a, b) { + return (b.y - a.y); + }); + }, + + destroyObject: function(obj) { + if (!obj.sprite.parent) + return; + obj.sprite.parent.removeChild(obj.sprite); + }, + + render: function() { + if (!this.stage) + return; + + effects.render(); + particles.update(); + + this.renderer.render(this.stage); + } + }; +}); \ No newline at end of file diff --git a/src/client/js/rendering/spriteShader.js b/src/client/js/rendering/spriteShader.js new file mode 100644 index 00000000..b0fcdcf6 --- /dev/null +++ b/src/client/js/rendering/spriteShader.js @@ -0,0 +1,111 @@ +define([ + 'js/resources' +], function( + resources +) { + var canvas = $('').appendTo('body').hide(); + + return { + outline: function(imgName, offsetX, offsetY, imgW, imgH, ur, ug, ub, ua) { + var img = resources.sprites[imgName].image; + + canvas[0].width = imgW; + canvas[0].height = imgH; + + var ctx = canvas[0].getContext('2d'); + ctx.drawImage(img, offsetX, offsetY, imgW, imgH, 2, 2, imgW, imgH); + + var imgData = ctx.getImageData(0, 0, imgW, imgH); + var pixels = imgData.data; + + var secondData = ctx.getImageData(0, 0, imgW, imgH); + var secondPixels = secondData.data; + + var newData = ctx.createImageData(imgW * 4, imgH * 4); + var newPixels = newData.data; + + var fillPixels = function(x, y, r, g, b, a) { + var index = ((y * imgW * 4) + x) * 4; + + for (var i = 0; i < 4; i++) { + for (var j = 0; j < 4; j++) { + var newIndex = index + (i * 4) + (j * (imgW * 4 * 4)); + newPixels[newIndex] = r; + newPixels[newIndex + 1] = g; + newPixels[newIndex + 2] = b; + newPixels[newIndex + 3] = a; + } + } + } + + for (var i = 0; i < imgW; i++) { + for (var j = 0; j < imgW; j++) { + var index = ((j * imgW) + i) * 4; + + var transparent = (pixels[index + 3] == 0); + if (transparent) { + var touchPixel = false; + if (i > 0) + touchPixel = (pixels[index - 1] > 0); + if ((!touchPixel) && (j > 0)) + touchPixel = (pixels[index - (imgW * 4) + 3] > 0); + if ((!touchPixel) && (i < imgW - 1)) + touchPixel = (pixels[index + 7] > 0); + if ((!touchPixel) && (j < imgH - 1)) + touchPixel = (pixels[index + (imgW * 4) + 3] > 0); + + if (touchPixel) { + secondPixels[index] = 0; + secondPixels[index + 1] = 0; + secondPixels[index + 2] = 0; + secondPixels[index + 3] = 255; + } + } + } + } + + for (var i = 0; i < imgW; i++) { + for (var j = 0; j < imgW; j++) { + var index = ((j * imgW) + i) * 4; + + var transparent = (secondPixels[index + 3] == 0); + if (transparent) { + var touchPixel = false; + if (i > 0) + touchPixel = (secondPixels[index - 1] > 0) + if ((!touchPixel) && (j > 0)) + touchPixel = (secondPixels[index - (imgW * 4) + 3] > 0) + if ((!touchPixel) && (i < imgW - 1)) + touchPixel = (secondPixels[index + 7] > 0) + if ((!touchPixel) && (j < imgH - 1)) + touchPixel = (secondPixels[index + (imgW * 4) + 3] > 0) + + if (touchPixel) + fillPixels(i * 4, j * 4, ur, ug, ub, ua); + + continue; + } + + var r = secondPixels[index]; + var g = secondPixels[index + 1]; + var b = secondPixels[index + 2]; + var a = secondPixels[index + 3]; + if ((r + g + b == 0) && (a == 255)) { + a = 0; + } + + fillPixels(i * 4, j * 4, r, g, b, a); + } + } + + canvas[0].width = imgW * 4; + canvas[0].height = imgH * 4; + + ctx.putImageData(newData, 0, 0); + + var url = canvas[0].toDataURL(); + + return url; + } + }; +}); diff --git a/src/mtx/skins/0001.png b/src/mtx/skins/0001.png new file mode 100644 index 00000000..6b0c02ae Binary files /dev/null and b/src/mtx/skins/0001.png differ diff --git a/src/mtx/skins/0001.pyxel b/src/mtx/skins/0001.pyxel new file mode 100644 index 00000000..8e6d6a4d Binary files /dev/null and b/src/mtx/skins/0001.pyxel differ diff --git a/src/mtx/skins/0010.pyxel b/src/mtx/skins/0010.pyxel new file mode 100644 index 00000000..f987b78e Binary files /dev/null and b/src/mtx/skins/0010.pyxel differ