From 05be3c4e898b6c8c8368f2f777d331e332ee740e Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Sun, 6 Dec 2015 14:44:10 -0800 Subject: [PATCH 1/2] Basic XP implementation --- src/lib/plugins/experience.js | 106 ++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/lib/plugins/experience.js diff --git a/src/lib/plugins/experience.js b/src/lib/plugins/experience.js new file mode 100644 index 00000000..a1d60f3f --- /dev/null +++ b/src/lib/plugins/experience.js @@ -0,0 +1,106 @@ +module.exports.player = function(player, serv) { + player.xp = 0; + player.displayXp = 0; + player.xpLevel = 0; + + player.sendXp = () => { + player._client.write('experience', { + experienceBar: player.displayXp, + level: player.level, + totalExperience: player.xp + }); + } + + player.setXpLevel = (level) => { + player.xpLevel = level; + player.sendXp(); + } + + player.setDisplayXp = (num) => { + player.displayXp = Math.max(0, Math.min(1, player.displayXp)); + player.sendXp(); + } + + player.setXp = (xp, { setLevel=true, setDisplay=true, send=true }={}) => { + player.xp = xp; + if (setLevel) player.level = serv.getXpLevel(xp); + if (setDisplay) player.displayXp = serv.distanceToXpLevel(xp); + if (send) player.sendXp(); + } + + player.commands.add({ + base: 'xp', + info: 'Give yourself experience', + usage: '/xp [player] OR /xp L [player]', + op: true, + parse(str) { + return str.match(/(-?\d+)(L)? ?([a-zA-Z0-9_]+)?/) || false; + }, + action(args) { + var isLevel = !!args[2]; + var amt = parseInt(args[1]); + var user = args[3] ? serv.getPlayer(args[3]) : player; + if (!user) return args[3] + ' is not on this server!'; + + if (!isLevel) { + user.setXp(user.xp + amt); + player.chat('Gave ' + user.username + ' ' + amt + ' xp'); + } else { + var currLevel = serv.getXpLevel(player.xp); + var baseCurrLevel = serv.getBaseXpFromLevel(currLevel); + var extraXp = player.xp - baseCurrLevel; + user.setXp(serv.getBaseXpFromLevel(currLevel + amt) + extraXp); + player.chat('Gave ' + user.username + ' ' + amt + ' levels'); + } + } + }); +} + +module.exports.server = function(serv) { + serv.distanceToXpLevel = (xp, toLevel) => { + var level = serv.getXpLevel(xp); + if (!toLevel) toLevel = level+1; + var levelBaseXp = serv.getBaseXpFromLevel(level); + var requiredXp = serv.getXpRequired(level, toLevel); + return (xp - levelBaseXp) / requiredXp; + } + + serv.getXpLevel = (xp) => { + // I have to use quadratic equation to reverse the equation from serv.getBaseXpFromLevel(). Ugh. + var a; + var b; + var c; + if (xp < 352) { // 352 == Experience at level 16 + a = 1; + b = 6; + c = 0; + } else if (xp < 1507) { // 1507 == Experience at level 31 + a = 2.5; + b = -40.5; + c = 360; + } else { // Level 32+ + a = 4.5; + b = -162.5; + c = 2220; + } + c -= xp; + return Math.floor((-b + Math.sqrt(b*b - 4*a*c)) / (2 * a)); // Math class was useful I guess mmph + } + + serv.getXpRequired = (level, toLevel) => { + if (!toLevel) toLevel = level + 1; + return serv.getBaseXpFromLevel(toLevel) - serv.getBaseXpFromLevel(level); + } + + serv.getBaseXpFromLevel = (level) => { + // The equations in this function are stupid and directly from the MC Wiki + // http://minecraft.gamepedia.com/Experience#Leveling_up + if (level <= 16) { + return level*level + 6*level; + } else if (level <= 31) { + return 2.5*level*level - 40.5*level + 360; + } else { // 32+ + return 4.5*level*level - 162.5*level + 2220; + } + } +} \ No newline at end of file From dc729a11fc55a6b17c1b1cf6230a8adaa0c62072 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Sun, 6 Dec 2015 15:28:08 -0800 Subject: [PATCH 2/2] Update docs on XP --- doc/API.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/doc/API.md b/doc/API.md index f4b6a645..60d3a9ea 100644 --- a/doc/API.md +++ b/doc/API.md @@ -45,6 +45,10 @@ - [server.playNoteBlock(world, position, pitch)](#serverplaynoteblockworld-position-pitch) - [server.getNote(note)](#servergetnotenote) - [server.emitParticle(particle, world, position, opt)](#serveremitparticleparticle-world-position-opt) + - [serv.getXpLevel(xp)](#servgetxplevelxp) + - [serv.getXpRequired(level, toLevel=level+1)](#servgetxprequiredlevel-tolevellevel1) + - [serv.getBaseXpFromLevel(level)](#servgetbasexpfromlevellevel) + - [serv.distanceToXpLevel(xp, toLevel=startLevel+1, startLevel=xp level)](#servdistancetoxplevelxp-tolevelstartlevel1-startlevelxp-level) - [Low level methods](#low-level-methods) - [server._writeAll(packetName, packetFields)](#server_writeallpacketname-packetfields) - [server._writeArray(packetName, packetFields, playerArray)](#server_writearraypacketname-packetfields-playerarray) @@ -92,6 +96,9 @@ - [Properties](#properties-2) - [player.username](#playerusername) - [player.view](#playerview) + - [player.xp](#playerxp) + - [player.displayXp](#playerdisplayxp) + - [player.xpLevel](#playerxplevel) - [Events](#events-2) - ["connected"](#connected) - ["spawned"](#spawned) @@ -132,6 +139,10 @@ - [player.spawnAPlayer(spawnedPlayer)](#playerspawnaplayerspawnedplayer) - [player.updateAndSpawnNearbyPlayers()](#playerupdateandspawnnearbyplayers) - [player.playSound(sound, opt)](#playerplaysoundsound-opt) + - [player.setXp(xp, opt)](#playersetxpxp-opt) + - [player.sendXp()](#playersendxp) + - [player.setXpLevel(level)](#playersetxplevellevel) + - [player.setDisplayXp(num)](#playersetdisplayxpnum) - [Low level properties](#low-level-properties) - [player._client](#player_client) - [Low level methods](#low-level-methods-1) @@ -353,6 +364,28 @@ Opt: - size: vec3 of the size. (0,0,0) will be at an exact position, (10,10,10) will be very spread out (particles less dense) - count: Number of particles. 100,000,000+ will crash the client. Try not to go over 100,000 (sincerely, minecraft clients) +#### serv.getXpLevel(xp) + +Get level given XP amount + +#### serv.getXpRequired(level, toLevel=level+1) + +Get's the amount of xp required to get from level to toLevel (or level to level+1) + +#### serv.getBaseXpFromLevel(level) + +Gets the minimum amount of xp required to be at that level (or "base xp" for that level) + +#### serv.distanceToXpLevel(xp, toLevel=startLevel+1, startLevel=xp level) + +Gets a number between 0 and 1 (used in player.displayXp as the green bar at the bottom) that is the progress of xp between startLevel and toLevel. + +By default, startLevel will be the xp's lowest possible level: serv.getXpLevel(xp) + +By default, toLevel is startLevel + 1. + +This means when startLevel and toLevel are at their defaults, this function returns the progress to the next level of XP (from 0.0 to 1.0) + ### Low level methods #### server._writeAll(packetName, packetFields) @@ -605,6 +638,18 @@ The username of the player The view size of the player, for example 8 for 16x16 +#### player.xp + +Total experience the player has (int). Set this using player.setXp() + +#### player.displayXp + +Number from 0 to 1.0 representing the progress bar at the bottom of the player's screen. Set this with player.setDisplayXp() + +#### player.xpLevel + +Level of xp the player has. Set this with player.setXpLevel() + ### Events #### "connected" @@ -876,6 +921,25 @@ Spawn and despawn the correct players depending on distance for `player`. Easy way to only play a sound for one player. Same opt as serv.playSound except no `whitelist`. +#### player.setXp(xp, opt) + +Sets the player's XP level. Options: +- setLevel: Calculate and set player.level (default: true) +- setDisplay: Calculate and set player.displayXp (default: true) +- send: Send xp packet (default: true) + +#### player.sendXp() + +Updates the player's xp based on player.xp, player.displayXp, and player.xpLevel + +#### player.setXpLevel(level) + +Sets and sends the player's new level + +#### player.setDisplayXp(num) + +Sets and sends the palyer's new display amount. num should be from 0 to 1.0 + ### Low level properties #### player._client