Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added falling damage #23

Merged
merged 3 commits into from
Jan 5, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules
/config.json
/map
/server.js
1 change: 1 addition & 0 deletions lib/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var Map = require("./map");

var Game = module.exports = function Game(config) {
events.EventEmitter.call(this);
this.setMaxListeners(0);

if (!config) {
config = {};
Expand Down
4 changes: 4 additions & 0 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ var Player = module.exports = function Player(game, options) {
this.stance = 0;
this.on_ground = 1;

this.health = 20;
this.food = 20;
this.saturation = 5;

if (typeof this.options.stance === "number") { this.stance = this.options.stance; }
if (typeof this.options.on_ground === "number") { this.on_ground = this.options.on_ground; }
};
Expand Down
2 changes: 1 addition & 1 deletion lib/serialiser.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Serialiser.prototype.write = function write(packet) {
}

case 0x08: {
this.emit("data", S().uint8(packet.pid).int16be(packet.health).int16be(packet.health).floatbe(packet.saturation).result());
this.emit("data", S().uint8(packet.pid).int16be(packet.health).int16be(packet.food).floatbe(packet.saturation).result());
break;
}

Expand Down
27 changes: 27 additions & 0 deletions plugins/player-abilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
Player abilities plugin.
This is used to keep track of player ability state between the client and the server.
Handles 0xCA in protocol.
*/
module.exports = function() {
return function(game) {
game.on("player:join", function(player) {
player.client.on("packet:ca", function(packet) {

var creativeMode = (packet.flags & 0x01);
var flying = (packet.flags & 0x02) >> 1;
var canFly = (packet.flags & 0x04) >> 2;
var invulnerable = (packet.flags & 0x08) >> 3;

if(player.mode != creativeMode) {
// something weird is happening here, lets log it
console.warn('[Warning] Player client side game mode does not match server side!');
}

player.flying = flying;
// trigger an event here to notify any plugins listening for flying status
player.emit("flying", flying);
});
});
};
};
76 changes: 76 additions & 0 deletions plugins/player-fall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
Fall plugin.
When a user falls apply the damage to their health.
This is by no means complete. Just having some fun playing with node and minecraft.
Also, this doesn't yet take into consideration velocity, and falling into water.

This might need to be loaded after player-abilities plugin.
*/
module.exports = function() {
return function(game) {
game.on("player:join", function(player) {
player.client.on("packet:0a", on_fall.bind(player, game));
player.client.on("packet:0b", on_fall.bind(player, game));
player.client.on("packet:0c", on_fall.bind(player, game));
player.client.on("packet:0d", on_fall.bind(player, game));
player.on("flying", handle_falling_pos.bind(player));
});
};
};

var set_falling_pos = function set_falling_pos(player) {
player.start_falling_pos = player.y;
}

var handle_falling_pos = function handle_falling_pos(flying) {
// if the player stops flying
if(!flying) set_falling_pos(this);
}

var on_fall = function on_fall(game, packet) {
// Need the block type to determine if the player is swimming.
// Maybe there is a better way to do this? ...swimming/flying should probably be kept track of in player.js with movement.js
var x = packet.x || this.x,
z = packet.z || this.z,
y = packet.y || this.y;

var block_x = x & 0x0f,
block_z = z & 0x0f,
block_y = y;

var WATER = 8;
var STATIONARY_WATER = 9;

game.map.get_abs_chunk(x, z, function(err, chunk) {
var blockType = chunk.get_block_type(block_x, block_z, block_y);
var swimming = blockType == STATIONARY_WATER || blockType == WATER;

// try to guess if they are flying to begin with
// if the block below them is air then they are probably flying
// this is untested - might need to find a way to do this with the protocol
if(typeof this.flying == "undefined") {
this.flying = chunk.get_block_type(block_x, block_z, block_y - 1) == 0;
}

// don't apply fall if they are swimming
if(!swimming) {
// when on_ground changes from true to false start keeping track of distance
if(this.on_ground && !packet["on_ground"]) {
set_falling_pos(this);
}
// Edge case:
// also when flying changes from true to false we need to set the height
// - this will get handled when the "flying",false event fires

// when it changes from false to true then calculate damage
if(!this.on_ground && packet["on_ground"]) {
var damage = Math.floor(this.start_falling_pos - packet.y - 3);
//trigger a damage event on the player
if(damage > 0) this.emit("damage", damage);

//TODO?: emit sound 0x3e
}
}

}.bind(this));
};
32 changes: 32 additions & 0 deletions plugins/player-health.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module.exports = function() {
return function(game) {
game.on("player:join", function(player) {
player.on("damage", on_damage.bind(player));

// defaults
player.damaged = 0;
});

game.on("tick", update_health.bind(null, game));
};
};

var on_damage = function on_damage(damage) {
//set the damage to the player if they aren't on creative mode, so 0 or 2
if(this.mode != 1) {
this.damaged += damage;
}
};

var update_health = function update_health(game) {
game.players.filter(function(e) {
return e.damaged > 0;
}).forEach(function(player) {
// modify the players health to match the damage done
player.health -= player.damaged;
// send out necessary packet to update the health
player.health < 0 ? player.kill() : player.setHealth(player.health, player.food, player.saturation);
// reset the damage done
player.damaged = 0;
});
};
12 changes: 12 additions & 0 deletions server.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@ game.use(SaveMapPlugin(__dirname + "/map"));
var AdminPlugin = require('./plugins/admin');
game.use(AdminPlugin());

// This is used to keep track of player ability state between the client and the server
var PlayerAbilitiesPlugin = require('./plugins/player-abilities');
game.use(PlayerAbilitiesPlugin());

// This plugin manages players health & hunger
var PlayerHealthPlugin = require('./plugins/player-health');
game.use(PlayerHealthPlugin());

// This plugin controlls falling damage/death
var PlayerFallPlugin = require('./plugins/player-fall');
game.use(PlayerFallPlugin());

// The server object is basically a wrapper around `net.Server` that constructs
// `Client` objects as they connect.

Expand Down