<strong>Move around with the arrow keys, and click replay after you're done.</strong>
<div class="rpg"></div>
<script type="text/mustache" id="avatar">
<div class="map">
<div class="character {{#isMoving}}moving{{/isMoving}} {{ player.position.direction }}" style="left: {{ player.position.left }}px; top: {{ player.position.top}}px"></div>
<div class="fire-pit"></div>
</div>
<button class="replay" {{#isSaving}}disabled="disabled"{{/isSaving}}>Replay!</button>
<span>{{#isSaving}}Saving!{{/isSaving}}</span>
</script>
$(document).ready(function(){
/* SERVER EMULATION */
var getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var positions = [];
can.fixture('POST /players', function(req){
var attrs = req.data || {};
positions.push(attrs.position); // Save current position
attrs.id = 1;
return attrs;
})
can.fixture('PUT /players/{id}', function(req, respondWith){
var attrs = req.data || {},
position = attrs.position;
if(position.top > 80 && position.top < 100 && position.left > 60 && position.left < 100){ // you stepped on the fire, you die
respondWith(406);
} else {
positions.push(attrs.position); // save current position
setTimeout(function(){
respondWith(attrs);
}, getRandomInt(10, 50)) // change delay to simulate slow and spotty connection
}
})
can.fixture('GET /game', function(req){
return positions;
})
var directions = {
37 : 'LEFT',
38 : 'UP',
39 : 'RIGHT',
40 : 'DOWN'
},
max = function(val, max){
return val > max ? max : val;
},
min = function(val, min){
return val < min ? min : val;
};
var Player = can.Model({
create : "POST /players",
update : "PUT /players/{id}"
}, {});
var Replay = can.Model({
findAll : "GET /game"
}, {})
var RPG = can.Control({
init : function(){
this.player = new Player({
position: {
top : 0,
left : 0,
direction: "down"
}
});
this.isMoving = can.compute(false);
this.isSaving = can.compute(false);
this.element.html(can.view('avatar', {
player : this.player,
isMoving : this.isMoving,
isSaving : this.isSaving
}));
// save starting position
this.savePosition();
},
move : function(direction){
var position, direction;
if(direction){
this.isMoving(true);
this.player.attr('position.direction', direction.toLowerCase());
// move the avatar in the right direction
if(direction === 'UP'){
this.player.attr('position.top', min(this.player.attr('position.top') - 5, 0));
} else if(direction === 'DOWN'){
this.player.attr('position.top', max(this.player.attr('position.top') + 5, 300 - 32));
} else if(direction === 'RIGHT'){
this.player.attr('position.left', max(this.player.attr('position.left') + 5, 300 - 24));
} else if(direction === 'LEFT'){
this.player.attr('position.left', min(this.player.attr('position.left') - 5, 0));
}
// after we move the avatar save the position again
this.savePosition();
}
},
".replay click" : function(){
Replay.findAll({}, $.proxy(this.replay, this));
},
replay : function(positions){
var currentPosition = positions.shift(),
self = this;
if(currentPosition){
this.isMoving(true);
this.player.attr('position').attr(currentPosition.attr());
setTimeout(function(){
self.replay(positions);
}, 50)
} else {
this.isMoving(false);
}
},
// On each keydown event move the avatar
"{document} keydown" : function(el, ev){
if(!this.isSaving()){
this.move(directions[ev.which]);
}
ev.preventDefault();
},
// When user stops pressing arrow keys remove moving class
"{document} keyup" : function(el, ev){
this.isMoving(false);
},
savePosition : function(){
this.isSaving(true);
this.player.save($.proxy(function(){
this.isSaving(false);
}, this), $.proxy(this.walkinFire, this));
},
walkinFire : function(){
this.isSaving(false);
alert("You walked in to the fire! You are dead!");
this.player.attr('position').attr({
direction: "down",
top: 0,
left: 0
});
this.isMoving(false);
}
})
new RPG('.rpg');
})
body {
font-family: Arial, sans-serif;
}
.character {
width: 24px;
height: 32px;
position: absolute;
z-index: 1000;
/* created with http://charas-project.net/charas2/index.php */
background-image: url('http://s21.postimage.org/byyd7owj7/static.gif');
}
.fire-pit {
width: 24px;
height: 24px;
background: url('http://s21.postimage.org/krfbvdho3/fire.gif');
position: absolute;
top: 100px;
left: 80px;
}
.up {
background-position: 0 0;
}
.down {
background-position: 0 -64px;
}
.right {
background-position: 0 -32px;
}
.left {
background-position: 0 -96px;
}
.moving {
background-image: url('http://s21.postimage.org/b85n1wu5v/animated.gif');
}
.map {
width: 300px;
height: 300px;
position: relative;
margin: 10px 0;
/* Texture from http://zooboingreview.blogspot.com/2009/11/seamless-texture-more-nice-grass.html */
background-image: url('http://s21.postimage.org/fxvkqil6f/363_tile_Grass.jpg');
}