@@ -11,21 +11,22 @@ define(["lib/socket.io", "entityfactory"], function(io, EntityFactory) {
conn.emit(Types.MESSAGES.HELLO, this.game.storage.id); // init handshake
conn.on(Types.MESSAGES.WELCOME, function(player, map) { // complete handshake
game.start(player, map);

conn.on(Types.MESSAGES.SPAWN, function(e) { // spawn entity in vision
game.receiveEntity(e);
});
conn.on(Types.MESSAGES.DESPAWN, function(id) { // remove entity from vision
game.removeEntity(id);
});
conn.on(Types.MESSAGES.MOVE, function(id, x, y) {
game.map.unregisterEntity(game.entities[id]);
game.entities[id].setGridPosition(x, y);
game.map.registerEntity(game.entities[id]);
});
});
conn.on(Types.MESSAGES.ERROR, function(msg) { //handle error
console.log("Error: " + msg);
});
conn.on(Types.MESSAGES.SPAWN, function(entity) { // spawn entity in vision
game.addEntity(EntityFactory[entity.type](entity));
});
conn.on(Types.MESSAGES.DESPAWN, function(id) { // remove entity from vision
game.removeEntity(id);
});
conn.on(Types.MESSAGES.MOVE, function(id, x, y) {
game.map.unregisterEntity(game.entities[id]);
game.entities[id].setGridPosition(x, y);
game.map.registerEntity(game.entities[id]);
});
},
emit: function() {
var args = Array.prototype.slice.call(arguments); // convert to array
@@ -5,7 +5,7 @@ requirejs.config({
}
});

define(["jquery", "../class", "game", "../util", "../constants"], function($, Class, Game) {
define(["jquery", "lib/underscore.min", "../class", "game", "../util", "../constants"], function($, _, Class, Game) {

var canvas = document.getElementById("game");
var game = new Game(canvas);
@@ -1,15 +1,33 @@
define(["camera", "artist"], function(Camera, Artist) {
define(["camera", "artist", "sprites", "sprite"], function(Camera, Artist, sprites, Sprite) {
var Renderer = Class.extend({
init: function(game, canvas) {
this.game = game; // parent game

this.isLoaded = false;

// drawing context
this.context = (canvas && canvas.getContext) ? canvas.getContext("2d") : null;
this.canvas = canvas; // drawing canvas

//this.sprites = sprites; unnecessary for now
this.spriteset = {};
this.loadSprites();

this.resize();
this.createCamera();
},
loadSprites: function() {
var counter = Object.keys(sprites).length;
for(var name in sprites) {
this.spriteset[name] = new Sprite(sprites[name], (id) => {
console.log("Sprite " + id + " loaded.");
counter--;
if(counter == 0) {
this.isLoaded = true;
}
});
}
},
setScale: function(scale) {
this.camera.setScale(scale);
},
@@ -39,16 +57,24 @@ define(["camera", "artist"], function(Camera, Artist) {
this.camera = new Camera(this);
},
panUp: function() {
this.camera.setY(this.camera.getY()-1);
if(this.camera.getY() > 0) {
this.camera.setY(this.camera.getY()-1);
}
},
panDown: function() {
this.camera.setY(this.camera.getY()+1);
if(this.camera.getY() < this.getHeight()) {
this.camera.setY(this.camera.getY()+1);
}
},
panLeft: function() {
this.camera.setX(this.camera.getX()-1);
if(this.camera.getX() > 0) {
this.camera.setX(this.camera.getX()-1);
}
},
panRight: function() {
this.camera.setX(this.camera.getX()+1);
if(this.camera.getX() < this.getWidth()) {
this.camera.setX(this.camera.getX()+1);
}
},
renderFrame: function() {
var ctx = this.context;
@@ -0,0 +1,43 @@
// need to do a lot of stuff with this

define(['animation', 'sprites'], function(Animation, sprites) {
var Sprite = Class.extend({
init: function(info, onLoad) {
this.id = info.id;
this.filepath = "img/sprites/" + info.id + ".png";
this.isLoaded = false;
this.offsetX = info.offsetX || 0;
this.offsetY = info.offsetY || 0;

this.width = info.width;
this.height = info.height;

this.animationData = info.animations;

this.ready_callback = onLoad;

this.load();
},
load: function() {
this.image = new Image();
this.image.src = this.filepath;

this.image.onload = () => {
this.isLoaded = true;
this.ready_callback(this.id);
};
},
createAnimations: function() {
var animations = {};

for(var name in this.animationData) {
var a = this.animationData[name];
animations[name] = new Animation(name, a.length, a.row, this.width, this.height);
}

return animations;
},
});

return Sprite;
});
@@ -0,0 +1,14 @@

define(['text!../sprites/monster.json',
], function() {

var sprites = {};

_.each(arguments, function(spriteJson) {
var sprite = JSON.parse(spriteJson);

sprites[sprite.id] = sprite;
});

return sprites;
});
@@ -4,62 +4,74 @@ define(["state", "uihandler"], function(State, UIHandler) {
this._super(game);

this.selected = [];

// generate ui
this.UIElements["main_panel"] = UIHandler.createRect(0, game.renderer.getHeight()-200, game.renderer.getWidth(), 200, "#ccc", "#000");

},
mousedown: function(mouse) {
var camera = this.game.renderer.camera;
if(mouse.button === 0) {
this.UIElements["selectionRect"] = UIHandler.createRectOutline(mouse.x-mouse.x%TILESIZE, mouse.y-mouse.y%TILESIZE, 0, 0);
this.UIElements["selection_rect"] = UIHandler.createRectOutline(
mouse.x-mouse.x%TILESIZE-camera.getX()%TILESIZE,
mouse.y-mouse.y%TILESIZE-camera.getY()%TILESIZE,
0, 0, "#ccc");
}
},
mouseup: function(mouse) {
var camera = this.game.renderer.camera;
if(mouse.button === 2) { // move units
this.selected.forEach((actor) => {
actor.setTarget({
x: Math.floor(mouse.x/TILESIZE),
y: Math.floor(mouse.y/TILESIZE)
x: Math.floor((mouse.x+camera.getX())/TILESIZE),
y: Math.floor((mouse.y+camera.getY())/TILESIZE)
});
});
} else if(mouse.button === 0) { // select units
var rect = this.UIElements["selectionRect"];
var rect = this.UIElements["selection_rect"];
var x, y, entity;

// handle possible negative rect
if(rect.getWidth() < 0) {
x = rect.getGridX()+rect.getGridWidth();
x = rect.getX()+rect.getWidth();
rect.setWidth(-rect.getWidth());
} else {
x = rect.getGridX();
x = rect.getX();
}
if(rect.getHeight() < 0) {
y = rect.getGridY()+rect.getGridHeight();
y = rect.getY()+rect.getHeight();
rect.setHeight(-rect.getHeight());
} else {
y = rect.getGridY();
y = rect.getY();
}

x = (x+camera.getX())/TILESIZE;
y = (y+camera.getY())/TILESIZE;

this.selected = []; // clear list
for(var i = x; i < x+rect.getGridWidth(); i++) {
for(var j = y; j < y+rect.getGridHeight(); j++) {
entity = this.game.entityAt(i, j);
if(entity) {
if(entity instanceof Array) {
// handle list
} else {
} else if(entity.owner === this.game.id){
this.selected.push(entity);
}
}
}
}
delete this.UIElements["selectionRect"];
delete this.UIElements["selection_rect"];
}
},
mousemove: function(mouse) {
if(this.UIElements["selectionRect"]) {
var rect = this.UIElements["selectionRect"];
if(this.UIElements["selection_rect"]) {
var rect = this.UIElements["selection_rect"];
rect.setWidth(mouse.x-rect.getX());
rect.setHeight(mouse.y-rect.getY());
}
},
update: function() {
update: function(time) {
// move camera
if(this.game.keys[40]) { // arrow down
this.game.renderer.panDown();
@@ -77,6 +89,11 @@ define(["state", "uihandler"], function(State, UIHandler) {
this.game.client.emitList(action);
}
});
this.game.forEachEntity((entity) => {
if(entity.currentAnimation) {
entity.currentAnimation.update(time);
}
});
}
});

Large diffs are not rendered by default.

@@ -1,7 +1,20 @@
define(["entity"], function(Entity) {
var UI = Entity.extend({
init: function() {
init: function(onTrigger, onUntrigger) {
this._super();

this.trigger_callback = onTrigger;
this.untrigger_callback = onUntrigger;
},
trigger: function() {
if(this.trigger_callback) {
this.trigger_callback.apply(this, arguments);
}
},
untrigger: function() {
if(this.untrigger_callback) {
this.untrigger_callback.apply(this, arguments);
}
}
});

@@ -1,7 +1,7 @@
define(["ui"], function(UI) {
var Rect = UI.extend({
init: function(x, y, width, height, color, outlineColor) {
this._super();
init: function(x, y, width, height, color, outlineColor, onTrigger, onUntrigger) {
this._super(onTrigger, onUntrigger);

this.setX(x);
this.setY(y);
@@ -1,7 +1,7 @@
define(["ui"], function(UI) {
var RectOutline = UI.extend({
init: function(x, y, width, height, color) {
this._super();
init: function(x, y, width, height, color, onTrigger, onUntrigger) {
this._super(onTrigger, onUntrigger);

this.setX(x);
this.setY(y);
@@ -0,0 +1,45 @@
{
"id": "monster",
"width": 78,
"height": 78,
"animations": {
"atk_right": {
"length": 3,
"row": 0
},
"walk_right": {
"length": 3,
"row": 1
},
"idle_right": {
"length": 2,
"row": 2
},
"atk_up": {
"length": 3,
"row": 3
},
"walk_up": {
"length": 4,
"row": 4
},
"idle_up": {
"length": 2,
"row": 5
},
"atk_down": {
"length": 3,
"row": 6
},
"walk_down": {
"length": 4,
"row": 7
},
"idle_down": {
"length": 2,
"row": 8
}
},
"offset_x": -15,
"offset_y": -27
}
@@ -19,7 +19,8 @@ Types = {
},
VIEWDISTANCE: {
Person: 10,
Tree: 0
Tree: 0,
Monster: 4
},
ACTORS: {
Person: 1,
@@ -50,7 +51,7 @@ Types = {
}
};

TILESIZE = 16;
TILESIZE = 32;

if(!(typeof module === 'undefined')) {
module.exports = Types;
@@ -12,6 +12,7 @@ var Entity = Class.extend({
this.type = type;
this.visibleTo = {};
this.visibleTo[owner] = true;
this.state = "idle_down";

if(this.type == "Person") {
/* This is an assortment of the persons character traits.
@@ -56,6 +57,7 @@ var Entity = Class.extend({
owner: this.owner,
type: this.type,
id: this.id,
state: this.state
};
if(this.type == "Person") {
obj['character'] = this.character;
@@ -0,0 +1,12 @@
var Entity = require('./entity');

var EntityFactory = {
Person: function(owner, x, y) {
return new Entity(owner, "Person", x, y, 1, 1);
},
Monster: function(x, y) {
return new Entity(-1, "Monster", x, y, 1, 1);
}
};

module.exports = EntityFactory;
@@ -135,14 +135,16 @@ var Player = Class.extend({
}
},
canSee: function(e) {
var child, childArea, vr;
if(this.dirtiestArea.collides(e)) { // possibly visible
var vr = Types.VIEWDISTANCE[e.type], size = 2*vr+1;
var entityArea = new Area(e.x-vr, e.y-vr, size, size);
for(var i in this.dirtyAreas) { // check dirty areas
if(this.dirtyAreas[i].collides(e)) {
if(this.dirtyAreas[i].contains(e.x, e.y)) {
if(this.dirtyAreas[i].children.length > 1) { // if combo area
for(var j in this.dirtyAreas[i].children) { // check specific areas
if(Util.collides(this.dirtyAreas[i].children[j], entityArea)) { // collision
child = this.dirtyAreas[i].children[j]
vr = Types.VIEWDISTANCE[child.type];
childArea = new Area(child.x-vr, child.y-vr, 2*vr+1, 2*vr+1);
if(childArea.contains(e.x, e.y)) { // collision
return true;
}
}
@@ -2,6 +2,7 @@ var Class = require('../shared/class');
var Types = require('../shared/constants');
var Player = require('./player');
var Entity = require('./entity');
var EntityFactory = require('./entityfactory');
var Map = require('./map');
var Area = require('./area');

@@ -11,21 +12,26 @@ var World = Class.extend({
this.entities = {};
this.entityId = 1;
this.map = new Map(5000, 5000);

this.addEntity(EntityFactory["Monster"](5, 5));
},
welcomePlayer: function(name, id, socket) {
id = parseInt(id);
if(!this.players[id]) {
this.players[id] = new Player(name, id, socket);
this.addEntity(new Entity(id, "Person", 10, 10, 1, 1)); // give commander
this.addEntity(EntityFactory["Person"](id, 10, 10)); // give commander
}

return this.players[id];
},
addEntity: function(e) {
e.id = this.entityId++;
this.entities[e.id] = e;
this.players[e.owner].entities.push(e);
this.players[e.owner].createDirtyAreas();
if(e.owner > 0) {
this.players[e.owner].entities.push(e);
this.players[e.owner].createDirtyAreas();
}
this.updateVisible(e);
this.map.registerEntity(e);
},
canOrder: function(ownerId, entityId) {
@@ -36,9 +42,25 @@ var World = Class.extend({
return Types.getKind(e.type) == "actor" && !this.map.blocked(x, y, e.width, e.height);
},
updateVisible: function(e) {
// check if moved into sight of new player
// check if players can see entity
for(var id in this.players) {
if(id !== e.owner) { // owner can always see
if(this.players[id].canSee(e)) { // if visible
if(!e.visibleTo[id]) {
e.visibleTo[id] = true;
this.players[id].socket.emit(Types.MESSAGES.SPAWN, e.toSendable());
}
} else if(e.visibleTo[id]){ // otherwise despawn
this.players[id].socket.emit(Types.MESSAGES.DESPAWN, e.id);
delete e.visibleTo[id];
}
}
}
},
updateVisibleAndMove: function(e) {
// check if moved into sight of new player
for(var id in this.players) {
if(parseInt(id) !== e.owner) { // owner can always see
if(this.players[id].canSee(e)) { // add to player view
if(e.visibleTo[id]) { // if visible move
this.players[id].socket.emit(Types.MESSAGES.MOVE, e.id, e.x, e.y);
@@ -54,13 +76,20 @@ var World = Class.extend({
this.players[id].socket.emit(Types.MESSAGES.MOVE, e.id, e.x, e.y);
}
}
// check if moved into sight of new entity
// check if moved into/out of sight of new entity
var vr = Types.VIEWDISTANCE[e.type];
var viewBox = new Area(e.x-vr, e.y-vr, 2*vr+1, 2*vr+1);
var viewable;
for(var id in this.entities) {
if(!this.entities[id].visibleTo[e.owner] && viewBox.collides(this.entities[id])) {
this.entities[id].visibleTo[e.owner] = true;
this.players[e.owner].socket.emit(Types.MESSAGES.SPAWN, this.entities[id].toSendable());
if(id != e.id) {
viewable = viewBox.collides(this.entities[id]);
if(!this.entities[id].visibleTo[e.owner] && viewable) {
this.entities[id].visibleTo[e.owner] = true;
this.players[e.owner].socket.emit(Types.MESSAGES.SPAWN, this.entities[id].toSendable());
} else if(this.entities[id].visibleTo[e.owner] && !viewable) {
this.entities[id].visibleTo[e.owner] = false
this.players[e.owner].socket.emit(Types.MESSAGES.DESPAWN, id);
}
}
}
},
@@ -70,7 +99,7 @@ var World = Class.extend({
e.setPosition(x, y); // move to pos
this.map.registerEntity(e);
this.players[e.owner].updateDirtyAreas(e); // update owner sight blocks
this.updateVisible(e); // update who can see entity/send move signal
this.updateVisibleAndMove(e); // update who can see entity/send move signal
}
});