Browse Source

Merge branch 'v0.1.4' into 'master'

V0.1.4

Closes #200, #185, #186, #184, #193, #179, #178, #174, #172, #166, #171, #168, #163, #129, and #28

See merge request !154
tags/v0.1.4^0
Big Bad Waffle 6 years ago
parent
commit
7677c59c71
100 changed files with 6545 additions and 1907 deletions
  1. BIN
     
  2. BIN
     
  3. BIN
     
  4. BIN
     
  5. BIN
     
  6. BIN
     
  7. BIN
     
  8. BIN
     
  9. BIN
     
  10. BIN
     
  11. BIN
     
  12. BIN
     
  13. BIN
     
  14. BIN
     
  15. BIN
     
  16. BIN
     
  17. BIN
     
  18. BIN
     
  19. BIN
     
  20. BIN
     
  21. BIN
     
  22. BIN
     
  23. BIN
     
  24. BIN
     
  25. +1
    -1
      src/client/index.html
  26. +6
    -0
      src/client/js/app.js
  27. +1
    -1
      src/client/js/components/animation.js
  28. +23
    -12
      src/client/js/components/attackAnimation.js
  29. +2
    -1
      src/client/js/components/chatter.js
  30. +5
    -0
      src/client/js/components/components.js
  31. +107
    -0
      src/client/js/components/lightPatch.js
  32. +63
    -0
      src/client/js/components/lightningEffect.js
  33. +0
    -2
      src/client/js/components/mouseMover.js
  34. +1
    -1
      src/client/js/components/player.js
  35. +13
    -4
      src/client/js/components/spellbook.js
  36. +18
    -9
      src/client/js/components/stats.js
  37. +2
    -0
      src/client/js/misc/statTranslations.js
  38. +4
    -2
      src/client/js/objects/objects.js
  39. +118
    -0
      src/client/js/rendering/lightningBuilder.js
  40. +14
    -6
      src/client/js/rendering/numbers.js
  41. +27
    -5
      src/client/js/rendering/particles.js
  42. +841
    -775
      src/client/js/rendering/renderer.js
  43. +8
    -7
      src/client/js/rendering/tileOpacity.js
  44. +4
    -1
      src/client/js/resources.js
  45. +11
    -0
      src/client/js/system/client.js
  46. +21
    -10
      src/client/plugins/pixi.min.js
  47. +5
    -0
      src/client/plugins/pixi.particles.js
  48. +428
    -0
      src/client/plugins/pixi.picture.js
  49. +2
    -2
      src/client/ui/templates/characters/styles.less
  50. +1
    -0
      src/client/ui/templates/context/context.js
  51. +17
    -14
      src/client/ui/templates/equipment/equipment.js
  52. +5
    -2
      src/client/ui/templates/hud/hud.js
  53. +4
    -4
      src/client/ui/templates/inventory/inventory.js
  54. +2
    -1
      src/client/ui/templates/login/login.js
  55. +1
    -1
      src/client/ui/templates/login/template.html
  56. +3
    -3
      src/client/ui/templates/online/styles.less
  57. +4
    -4
      src/client/ui/templates/smithing/smithing.js
  58. +4
    -0
      src/client/ui/templates/tooltipItem/styles.less
  59. +5
    -1
      src/client/ui/templates/tooltipItem/templateTooltip.html
  60. +9
    -1
      src/client/ui/templates/tooltipItem/tooltipItem.js
  61. +18
    -11
      src/server/combat/combat.js
  62. +23
    -7
      src/server/components/aggro.js
  63. +1
    -1
      src/server/components/auth.js
  64. +4
    -1
      src/server/components/chatter.js
  65. +23
    -9
      src/server/components/dialogue.js
  66. +4
    -8
      src/server/components/equipment.js
  67. +1
    -1
      src/server/components/extensions/factionVendor.js
  68. +138
    -0
      src/server/components/extensions/socialCommands.js
  69. +80
    -15
      src/server/components/follower.js
  70. +110
    -109
      src/server/components/inventory.js
  71. +3
    -3
      src/server/components/mob.js
  72. +2
    -0
      src/server/components/notice.js
  73. +18
    -4
      src/server/components/player.js
  74. +3
    -1
      src/server/components/reputation.js
  75. +7
    -1
      src/server/components/social.js
  76. +64
    -35
      src/server/components/spellbook.js
  77. +39
    -12
      src/server/components/stats.js
  78. +7
    -3
      src/server/config/animations.js
  79. +19
    -1
      src/server/config/classes.js
  80. +15
    -0
      src/server/config/effects/effectFrenzy.js
  81. +99
    -0
      src/server/config/factions/akarei.js
  82. +3
    -1
      src/server/config/factions/gaekatla.js
  83. +27
    -0
      src/server/config/maps/cave/chats.js
  84. +236
    -0
      src/server/config/maps/cave/dialogues.js
  85. +2807
    -0
      src/server/config/maps/cave/map.json
  86. +9
    -0
      src/server/config/maps/cave/quests.js
  87. +668
    -0
      src/server/config/maps/cave/zone.js
  88. +4
    -3
      src/server/config/maps/estuary/zone.js
  89. +4
    -3
      src/server/config/maps/mapList.js
  90. +2
    -2
      src/server/config/maps/sewer/zone.js
  91. +0
    -430
      src/server/config/maps/tutorial-cove/map.json
  92. +0
    -27
      src/server/config/maps/tutorial-cove/zone.js
  93. +257
    -295
      src/server/config/maps/tutorial/map.json
  94. +3
    -3
      src/server/config/maps/tutorial/zone.js
  95. +8
    -1
      src/server/config/roles.js
  96. +10
    -0
      src/server/config/serverConfig.js
  97. +4
    -48
      src/server/config/spells.js
  98. +1
    -1
      src/server/config/spells/spellArcaneBarrier.js
  99. +43
    -0
      src/server/config/spells/spellChainLightning.js
  100. +1
    -1
      src/server/config/spells/spellFireblast.js

BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


+ 1
- 1
src/client/index.html View File

@@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>dev</title>
<title>GEARED - Login</title>
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="js/system/addons.js"></script>
<script src="plugins/require.js" data-main="js/app"></script>


+ 6
- 0
src/client/js/app.js View File

@@ -16,6 +16,7 @@ require.config({
'main': 'js/main',
'helpers': 'js/misc/helpers',
'particles': 'plugins/pixi.particles',
'picture': 'plugins/pixi.picture',
'pixi': 'plugins/pixi.min'
},
shim: {
@@ -38,6 +39,11 @@ require.config({
'pixi'
]
},
'picture': {
deps: [
'pixi'
]
},
'main': {
deps: [
'helpers',


+ 1
- 1
src/client/js/components/animation.js View File

@@ -45,7 +45,7 @@ define([
renderer.setSprite({
sprite: this.obj.sprite,
cell: (this.row * 8) + this.col + this.frame,
sheetName: this.sheet
sheetName: this.spritesheet || this.sheet
});
},



+ 23
- 12
src/client/js/components/attackAnimation.js View File

@@ -6,7 +6,7 @@ define([
renderer
) {
var scale = 40;
return {
type: 'attackAnimation',

@@ -32,6 +32,9 @@ define([
init: function(blueprint) {
effects.register(this);

if (this.hideSprite)
this.obj.sprite.visible = false;

this.flipped = (Math.random() < 0.5);

this.frameDelayCd = this.frameDelay;
@@ -39,14 +42,15 @@ define([
var cell = (this.row * 8) + this.col + this.frame;

this.sprite = renderer.buildObject({
sheetName: this.spriteSheet,
sheetName: this.spritesheet || this.spriteSheet,
cell: cell,
x: this.obj.x,
x: this.obj.x + (this.flipped ? 1 : 0),
y: this.obj.y,
offsetX: this.obj.offsetX,
offsetY: this.obj.offsetY,
flipX: this.flipped
});
this.sprite.alpha = 1;
},

renderManual: function() {
@@ -60,29 +64,36 @@ define([
if (this.loopCounter == this.loop) {
if (this.destroyObject)
this.obj.destroyed = true;
else
else {
if (this.hideSprite)
this.obj.sprite.visible = true;

this.destroyed = true;
}
return;
}
else
} else
this.frame = 0;
}
}

this.sprite.x = this.obj.x * scale;
this.sprite.y = this.obj.y * scale;
if ((!this.hideSprite) || (this.loop > 0)) {
this.sprite.x = this.obj.x * scale;
this.sprite.y = this.obj.y * scale;
}

var cell = (this.row * 8) + this.col + this.frame;

renderer.setSprite({
sheetName: this.spriteSheet,
sheetName: this.spritesheet || this.spriteSheet,
cell: cell,
flipX: this.flipped,
sprite: this.sprite
});

if (this.flipped)
this.sprite.x += scale;
if ((!this.hideSprite) || (this.loop > 0)) {
if (this.flipped)
this.sprite.x += scale;
}
},

destroyManual: function() {
@@ -92,6 +103,6 @@ define([
});

effects.unregister(this);
}
}
};
});

+ 2
- 1
src/client/js/components/chatter.js View File

@@ -12,7 +12,8 @@ define([
cdMax: 150,

init: function(blueprint) {

if ((blueprint) && (blueprint.msg))
this.extend(blueprint);
},

update: function() {


+ 5
- 0
src/client/js/components/components.js View File

@@ -4,10 +4,12 @@ var components = [
'player',
'pather',
'attackAnimation',
'lightningEffect',
'moveAnimation',
'bumpAnimation',
'animation',
'light',
'lightPatch',
'projectile',
'particles',
'explosion',
@@ -41,6 +43,9 @@ define(components, function() {

return {
getTemplate: function(type) {
if (type == 'lightpatch')
type = 'lightPatch';
return templates[type];
}
};

+ 107
- 0
src/client/js/components/lightPatch.js View File

@@ -0,0 +1,107 @@
define([
'js/rendering/renderer',
'picture'
], function(
renderer,
picture
) {
var scale = 40;
var scaleMult = 5;

return {
type: 'lightPatch',

color: 'f7ffb2',
patches: [],
rays: [],

init: function(blueprint) {
this.blueprint = this.blueprint || {};

var obj = this.obj;

var x = obj.x;
var y = obj.y;

var maxDistance = Math.sqrt(Math.pow(obj.width / 2, 2) + Math.pow(obj.height / 2, 2));
for (var i = 0; i < obj.width; i++) {
for (var j = 0; j < obj.height; j++) {
var distance = maxDistance - Math.sqrt(Math.pow((obj.width / 2) - i, 2) + Math.pow((obj.width / 2) - i, 2));
var alpha = distance / maxDistance;

var sprite = renderer.buildObject({
x: (x + i),
y: (y + j),
sheetName: 'white',
cell: 0,
layerName: 'lightPatches'
});
sprite.alpha = (0.2 + (Math.random() * 1)) * alpha;
sprite.tint = '0x' + this.color;

sprite.blendMode = PIXI.BLEND_MODES.OVERLAY;
sprite.pluginName = 'picture';

this.patches.push(sprite);
}
}

var rCount = ((obj.width * obj.height) / 10) + ~~(Math.random() + 2);
for (var i = 0; i < rCount; i++) {
var nx = x + 3 + ~~(Math.random() * (obj.width - 1));
var ny = y - 4 + ~~(Math.random() * (obj.height));
var w = 1 + ~~(Math.random() * 2);
var h = 6 + ~~(Math.random() * 13);
var hm = 2;

var rContainer = renderer.buildContainer({
layerName: 'lightBeams'
});
this.rays.push(rContainer);

for (var j = 0; j < h; j++) {
var ray = renderer.buildObject({
x: nx,
y: ny,
cell: 0,
sheetName: 'white',
parent: rContainer
});
ray.x = ~~((nx * scale) - (scaleMult * j));
ray.y = (ny * scale) + (scaleMult * j * hm);
ray.alpha = ((1.0 - (j / h)) * 0.4);// * (0.5 + (Math.random() * 0.5));
ray.width = w * scaleMult;
ray.height = scaleMult * hm;
ray.tint = 0xffeb38;
ray.blendMode = PIXI.BLEND_MODES.ADD;
}
}
},

update: function() {
var rays = this.rays;
var rLen = rays.length;
for (var i = 0; i < rLen; i++) {
var r = rays[i];

r.alpha += (Math.random() * 0.03) - 0.015;
if (r.alpha < 0.3)
r.alpha = 0.3;
else if (r.alpha > 1)
r.alpha = 1;
}
},

destroy: function() {
this.patches.forEach(function(p) {
p.parent.removeChild(p);
});
this.patches = [];

this.rays.forEach(function(r) {
r.parent.removeChild(r);
});
this.rays = [];
}
};
});

+ 63
- 0
src/client/js/components/lightningEffect.js View File

@@ -0,0 +1,63 @@
define([
'js/rendering/lightningBuilder',
'js/rendering/effects'
], function(
lightningBuilder,
effects
) {
return {
type: 'lightningEffect',

cd: 0,
cdMax: 1,

effect: null,

ttl: 6,

init: function() {
effects.register(this);

var xOffset = (this.toX > this.obj.x) ? 1 : 0;

this.effect = lightningBuilder.build({
fromX: this.obj.x + xOffset,
fromY: this.obj.y + 0.5,
toX: this.toX + 0.5,
toY: this.toY + 0.5
});
},

renderManual: function() {
if (this.cd > 0) {
this.cd--;
return;
}

this.cd = this.cdMax;

lightningBuilder.destroy(this.effect);

this.ttl--;
if (this.ttl == 0) {
this.destroyed = true;
return;
}

var xOffset = (this.toX > this.obj.x) ? 1 : 0;

this.effect = lightningBuilder.build({
fromX: this.obj.x + xOffset,
fromY: this.obj.y + 0.5,
toX: this.toX + 0.5,
toY: this.toY + 0.5
});
},

destroyManual: function() {
//lightningBuilder.destroy(this.effect);

effects.unregister(this);
}
};
});

+ 0
- 2
src/client/js/components/mouseMover.js View File

@@ -35,8 +35,6 @@ define([
layerName: 'effects',
x: 0,
y: 0,
w: scale,
h: scale,
sheetName: 'ui',
cell: 7
});


+ 1
- 1
src/client/js/components/player.js View File

@@ -22,7 +22,7 @@ define([

this.obj.addComponent('pather');

events.emit('onGetPortrait', this.obj.class);
events.emit('onGetPortrait', this.obj.portrait);
},

update: function() {


+ 13
- 4
src/client/js/components/spellbook.js View File

@@ -28,7 +28,7 @@ define([
renderRange: null,

reticleSprite: null,
tarpSprite: null,
targetSprite: null,

shiftDown: false,

@@ -141,7 +141,12 @@ define([
},

tabTarget: function() {
this.onMouseDown(null, objects.getClosest(window.player.x, window.player.y, 10, this.shiftDown, this.target));
var closest = objects.getClosest(window.player.x, window.player.y, 10, this.shiftDown, this.target);

this.target = closest;
this.targetSprite.visible = !!this.target;

events.emit('onSetTarget', this.target, null);
},

build: function(destroy) {
@@ -189,11 +194,15 @@ define([
this.target = this.obj;
}

if ((!spell.targetGround) && (!this.target))
if ((!spell.targetGround) && (!spell.autoTargetFollower) && (!this.target))
return;

var hoverTile = this.obj.mouseMover.hoverTile;
var target = spell.targetGround ? hoverTile : this.target.id;
var target = hoverTile;
if ((!spell.targetGround) && (this.target))
target = this.target.id;
if ((spell.autoTargetFollower) && (target.id == null))
target = null;

if (this.shiftDown)
this.target = oldTarget;


+ 18
- 9
src/client/js/components/stats.js View File

@@ -12,6 +12,9 @@ define([

values: null,

hpSprite: null,
hpSpriteInner: null,

init: function(blueprint) {
if (this.obj.self)
events.emit('onGetStats', this.values);
@@ -28,19 +31,19 @@ define([

this.hpSprite = renderer.buildRectangle({
layerName: 'effects',
x: 0,
y: 0,
w: 0,
h: 0,
x: obj.x * scale,
y: obj.y * scale,
w: 1,
h: 1,
color: 0x802343
});

renderer.buildRectangle({
this.hpSpriteInner = renderer.buildRectangle({
x: 0,
y: 0,
w: 0,
h: 0,
parent: this.hpSprite,
w: 1,
h: 1,
layerName: 'effects',
color: 0xd43346
});

@@ -66,7 +69,7 @@ define([
});

renderer.moveRectangle({
sprite: this.hpSprite.children[0],
sprite: this.hpSpriteInner,
x: x + 4,
y: y,
w: (this.values.hp / this.values.hpMax) * (scale - 8),
@@ -74,6 +77,7 @@ define([
});

this.hpSprite.visible = (this.values.hp < this.values.hpMax);
this.hpSpriteInner.visible = this.hpSprite.visible;
},

extend: function(blueprint) {
@@ -100,6 +104,11 @@ define([
sprite: this.hpSprite,
layerName: 'effects'
});

renderer.destroyObject({
sprite: this.hpSpriteInner,
layerName: 'effects'
});
}
};
});

+ 2
- 0
src/client/js/misc/statTranslations.js View File

@@ -14,11 +14,13 @@ define([
'dex': 'dexterity',
'armor': 'armor',
'addCritChance': 'increased crit chance',
'addCritMultiplier': 'increased crit multiplier',
'magicFind': 'magic find',
'sprintChance': 'sprint chance',
'dmgPercent': 'to all damage',
'allAttributes': 'to all attributes',
'xpIncrease': 'additional xp per kill',
'lvlRequire': 'level requirement reduction',

'elementArcanePercent': 'increased arcane damage',
'elementFrostPercent': 'increased frost damage',


+ 4
- 2
src/client/js/objects/objects.js View File

@@ -126,12 +126,15 @@ define([
var components = template.components || [];
delete template.components;

var syncTypes = ['portrait'];

for (var p in template) {
var value = template[p];
var type = typeof(value);

if (type == 'object') {

if (syncTypes.indexOf(p) > -1)
obj[p] = value;
} else
obj[p] = value;
}
@@ -242,7 +245,6 @@ define([
}
}


if ((template.x != 0) || (template.y != 0)) {
if (obj.stats)
obj.stats.updateHpSprite();


+ 118
- 0
src/client/js/rendering/lightningBuilder.js View File

@@ -0,0 +1,118 @@
define([
'js/rendering/renderer',
'picture'
], function(
renderer,
picture
) {
var scale = 40;
var scaleMult = 5;

return {
build: function(config) {
var obj = {
lines: []
};

var maxDeviate = scale * 0.3;

var fx = config.fromX * scale;
var fy = config.fromY * scale;

var tx = config.toX * scale;
var ty = config.toY * scale;

var angle = Math.atan2(ty - fy, tx - fx);
var distance = Math.sqrt(Math.pow(tx - fx, 2) + Math.pow(ty - fy, 2));
var divDistance = Math.min(20, distance);
var divisions = Math.max(1, distance / divDistance);

var x = fx;
var y = fy;

for (var i = 0; i < divisions; i++) {
var line = {
sprites: []
};

var ntx = fx + (Math.cos(angle) * (divDistance * i)) + ~~(Math.random() * (maxDeviate * 2)) - maxDeviate;
var nty = fy + (Math.sin(angle) * (divDistance * i)) + ~~(Math.random() * (maxDeviate * 2)) - maxDeviate;

if (i == divisions - 1) {
ntx = tx;
nty = ty;
}

var nAngle = Math.atan2(nty - y, ntx - x);
var steps = ~~(Math.sqrt(Math.pow(ntx - x, 2) + Math.pow(nty - y, 2)) / scaleMult);

var patches = {};

for (var j = 0; j < steps; j++) {
var c = [0xffeb38, 0xfaac45, 0xfafcfc][~~(Math.random() * 3)];
line.sprites.push(renderer.buildRectangle({
x: ~~(x / scaleMult) * scaleMult,
y: ~~(y / scaleMult) * scaleMult,
w: scaleMult,
h: scaleMult,
color: c,
layerName: 'effects'
}));

var xx = x;
var yy = y;
if (!patches[xx + '-' + yy]) {
patches[xx + '-' + yy] = 1;

var lightPatch = renderer.buildObject({
sheetName: 'white',
x: 0,
y: 0,
cell: 0,
layerName: 'lightPatches'
});
lightPatch.alpha = Math.random() * 0.5;
lightPatch.tint = '0xffffff';
lightPatch.x = ~~((xx - scaleMult) / scaleMult) * scaleMult;
lightPatch.y = ~~((yy - scaleMult) / scaleMult) * scaleMult;
lightPatch.width = scaleMult * 3;
lightPatch.height = scaleMult * 3;

lightPatch.blendMode = PIXI.BLEND_MODES.OVERLAY;
lightPatch.pluginName = 'picture';

line.sprites.push(lightPatch);
}

x += Math.cos(nAngle) * scaleMult;
y += Math.sin(nAngle) * scaleMult;
}

obj.lines.push(line);
}

return obj;
},

toHex: function rgbToHex(r, g, b) {
var componentToHex = function(c) {
var hex = c.toString(16);
return hex.length == 1 ? '0' + hex : hex;
};

return '0x' + componentToHex(r) + componentToHex(g) + componentToHex(b);
},

update: function(obj) {

},

destroy: function(obj) {
obj.lines.forEach(function(l) {
l.sprites.forEach(function(s) {
s.parent.removeChild(s);
});
});
}
};
});

+ 14
- 6
src/client/js/rendering/numbers.js View File

@@ -24,20 +24,28 @@ define([

var addY = msg.event ? scale : -(scale * 0.75);

var ttl = 30 * (msg.crit ? 1 : 1);
var ttl = 35;

var numberObj = {
obj: target,
amount: msg.amount,
x: (target.x * scale) + ~~(Math.random() * scale - (scale / 2)),
y: (target.y * scale) + addY,
x: (target.x * scale),
y: (target.y * scale) + scale - (scale / 4),
ttl: ttl,
ttlMax: ttl,
event: msg.event,
text: msg.text,
crit: msg.crit,
heal: msg.heal
};
};

if (numberObj.event) {
numberObj.y += (scale / 2);
}
else if (numberObj.heal)
numberObj.x -= scale;
else
numberObj.x += scale;

var text = numberObj.text;
if (!numberObj.event)
@@ -74,9 +82,9 @@ define([
}

if (l.event)
l.y += 0.75;
l.y += 1;
else
l.y -= 0.75;
l.y -= 1;

var alpha = l.ttl / l.ttlMax;



+ 27
- 5
src/client/js/rendering/particles.js View File

@@ -1,9 +1,11 @@
define([
'particles',
'js/rendering/particleDefaults'
'js/rendering/particleDefaults',
'js/rendering/shaders/outline'
], function(
pixiParticles,
particleDefaults
particleDefaults,
shaderOutline
) {
return {
renderer: null,
@@ -14,6 +16,7 @@ define([
lastTick: null,

init: function(options) {
this.r = options.r;
this.renderer = options.renderer;
this.stage = options.stage;
this.lastTick = Date.now();
@@ -22,7 +25,7 @@ define([
buildEmitter: function(config) {
var options = $.extend(true, {}, particleDefaults, config);

var emitter = new PIXI.particles.Emitter(this.stage, ['images/particles.png'], options);
var emitter = new PIXI.particles.Emitter(this.r.layers.particles, ['images/particles.png'], options);
emitter.emit = true;

this.emitters.push(emitter);
@@ -35,13 +38,23 @@ define([
},

update: function() {
var renderer = this.r;
var now = Date.now();

var emitters = this.emitters;
var eLen = emitters.length;
for (var i = 0; i < eLen; i++) {
var e = emitters[i];
var destroy = ((!e.emit) && (e.particleCount == 0));

var visible = null;
var destroy = !e.emit;
if (destroy) {
if (e.particleCount > 0) {
visible = renderer.isVisible(e.spawnPos.x, e.spawnPos.y);
if (visible)
destroy = false;
}
}

if (destroy) {
emitters.splice(i, 1);
@@ -53,7 +66,16 @@ define([
continue;
}

e.update((now - this.lastTick) * 0.001);
if (visible === null)
visible = renderer.isVisible(e.spawnPos.x, e.spawnPos.y);
if (!visible)
continue;

var r = e.update((now - this.lastTick) * 0.001);
r.forEach(function(rr) {
if (e.blendMode == 'overlay')
rr.pluginName = 'picture';
}, this);
}

this.lastTick = now;


+ 841
- 775
src/client/js/rendering/renderer.js
File diff suppressed because it is too large
View File


+ 8
- 7
src/client/js/rendering/tileOpacity.js View File

@@ -76,8 +76,9 @@ define([

],
wallsNoFlip: [
156, 158, 162, 163, 167, 168, //Ledges
189 //Wall Sign
156, 158, 162, 163, 167, 168, //Ledges
189, //Wall Sign
195, 196, 197, 198, 199, 200, 201, 202, 203 //Stone Ledges
],
objectsNoFlip: [
96, 101, //Clotheslines
@@ -88,7 +89,7 @@ define([
getSheetNum: function(tile) {
if (tile < 192)
return 0;
else if (tile < 384)
else if (tile < 448)
return 1;
else
return 2;
@@ -99,12 +100,12 @@ define([

if (tile < 192)
sheetNum = 0;
else if (tile < 384) {
else if (tile < 448) {
tile -= 192;
sheetNum = 1;
}
else {
tile -= 384;
tile -= 448;
sheetNum = 2;
}
@@ -124,12 +125,12 @@ define([

if (tile < 192)
sheetNum = 0;
else if (tile < 384) {
else if (tile < 448) {
tile -= 192;
sheetNum = 1;
}
else {
tile -= 384;
tile -= 448;
sheetNum = 2;
}


+ 4
- 1
src/client/js/resources.js View File

@@ -10,6 +10,7 @@ define([
'walls',
'mobs',
'bosses',
'animBigObjects',
'bigObjects',
'objects',
'characters',
@@ -24,7 +25,9 @@ define([
'sprites',
'animChar',
'animMob',
'animBoss'
'animBoss',
'white',
'ray'
],
sprites: {},
ready: false,


+ 11
- 0
src/client/js/system/client.js View File

@@ -62,6 +62,17 @@ define([

for (var e in response) {
var r = response[e];

//Certain messages expect to be performed last (because the object they act on hasn't been greated when they get queued)
r.sort(function(a, b) {
if (a.performLast)
return 1;
else if (b.performLast)
return -1;
else
return 0;
});

r.forEach(function(o) {
events.emit(e, o);
});


+ 21
- 10
src/client/plugins/pixi.min.js
File diff suppressed because it is too large
View File


+ 5
- 0
src/client/plugins/pixi.particles.js View File

@@ -754,6 +754,7 @@ if (!Array.prototype.random) {
this.randomColor = config.randomColor || false;
this.randomScale = config.randomScale || false;
this.randomSpeed = config.randomSpeed || false;
this.blendMode = config.blendMode;

/**
* The constructor used to create new particles. The default is
@@ -1425,6 +1426,7 @@ if (!Array.prototype.random) {
* @param {Number} delta Time elapsed since the previous frame, in __seconds__.
*/
p.update = function(delta) {
var r = [];
//if we don't have a parent to add particles to, then don't do anything.
//this also works as a isDestroyed check
if (!this._parent) return;
@@ -1503,6 +1505,7 @@ if (!Array.prototype.random) {
} else {
p = new this.particleConstructor(this);
}
r.push(p);

//set a random texture if we have more than one
if (this.particleImages.length > 1) {
@@ -1620,6 +1623,8 @@ if (!Array.prototype.random) {
this._prevPosIsValid = true;
this._posChanged = false;
}

return r;
};

/**


+ 428
- 0
src/client/plugins/pixi.picture.js View File

@@ -0,0 +1,428 @@
var __extends = (this && this.__extends) || function(d, b) {
for (var p in b)
if (b.hasOwnProperty(p)) d[p] = b[p];

function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
var shaderLib = [{
vertUniforms: "",
vertCode: "vTextureCoord = aTextureCoord;",
fragUniforms: "uniform vec4 uTextureClamp;",
fragCode: "vec2 textureCoord = clamp(vTextureCoord, uTextureClamp.xy, uTextureClamp.zw);"
}, {
vertUniforms: "uniform mat3 uTransform;",
vertCode: "vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;",
fragUniforms: "",
fragCode: "vec2 textureCoord = vTextureCoord;"
}, {
vertUniforms: "uniform mat3 uTransform;",
vertCode: "vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;",
fragUniforms: "uniform mat3 uMapCoord;\nuniform vec4 uClampFrame;\nuniform vec2 uClampOffset;",
fragCode: "vec2 textureCoord = mod(vTextureCoord - uClampOffset, vec2(1.0, 1.0)) + uClampOffset;" +
"\ntextureCoord = (uMapCoord * vec3(textureCoord, 1.0)).xy;" +
"\ntextureCoord = clamp(textureCoord, uClampFrame.xy, uClampFrame.zw);"
}];
var PictureShader = (function(_super) {
__extends(PictureShader, _super);

function PictureShader(gl, vert, frag, tilingMode) {
var lib = shaderLib[tilingMode];
_super.call(this, gl, vert.replace(/%SPRITE_UNIFORMS%/gi, lib.vertUniforms)
.replace(/%SPRITE_CODE%/gi, lib.vertCode), frag.replace(/%SPRITE_UNIFORMS%/gi, lib.fragUniforms)
.replace(/%SPRITE_CODE%/gi, lib.fragCode));
this.bind();
this.tilingMode = tilingMode;
this.tempQuad = new PIXI.Quad(gl);
this.tempQuad.initVao(this);
this.uniforms.uColor = new Float32Array([1, 1, 1, 1]);
this.uniforms.uSampler = [0, 1];
}
PictureShader.blendVert = "\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nuniform mat3 projectionMatrix;\nuniform mat3 mapMatrix;\n\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n %SPRITE_CODE%\n vMapCoord = (mapMatrix * vec3(aVertexPosition, 1.0)).xy;\n}\n";
return PictureShader;
}(PIXI.Shader));
extras.PictureShader = PictureShader;
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
var overlayFrag = "\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n vec4 source = texture2D(uSampler[0], textureCoord) * uColor;\n vec4 target = texture2D(uSampler[1], vMapCoord);\n\n //reverse hardlight\n if (source.a == 0.0) {\n gl_FragColor = vec4(0, 0, 0, 0);\n return;\n }\n //yeah, premultiplied\n vec3 Cb = source.rgb/source.a, Cs;\n if (target.a > 0.0) {\n Cs = target.rgb / target.a;\n }\n vec3 multiply = Cb * Cs * 2.0;\n vec3 Cs2 = Cs * 2.0 - 1.0;\n vec3 screen = Cb + Cs2 - Cb * Cs2;\n vec3 B;\n if (Cb.r <= 0.5) {\n B.r = multiply.r;\n } else {\n B.r = screen.r;\n }\n if (Cb.g <= 0.5) {\n B.g = multiply.g;\n } else {\n B.g = screen.g;\n }\n if (Cb.b <= 0.5) {\n B.b = multiply.b;\n } else {\n B.b = screen.b;\n }\n vec4 res;\n res.xyz = (1.0 - source.a) * Cs + source.a * B;\n res.a = source.a + target.a * (1.0-source.a);\n gl_FragColor = vec4(res.xyz * res.a, res.a);\n}\n";
var HardLightShader = (function(_super) {
__extends(HardLightShader, _super);

function HardLightShader(gl, tilingMode) {
_super.call(this, gl, extras.PictureShader.blendVert, overlayFrag, tilingMode);
}
return HardLightShader;
}(extras.PictureShader));
extras.HardLightShader = HardLightShader;
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
function mapFilterBlendModesToPixi(gl, array) {
if (array === void 0) {
array = [];
}
array[PIXI.BLEND_MODES.OVERLAY] = [new extras.OverlayShader(gl, 0), new extras.OverlayShader(gl, 1), new extras.OverlayShader(gl, 2)];
array[PIXI.BLEND_MODES.HARD_LIGHT] = [new extras.HardLightShader(gl, 0), new extras.HardLightShader(gl, 1), new extras.HardLightShader(gl, 2)];
return array;
}
extras.mapFilterBlendModesToPixi = mapFilterBlendModesToPixi;
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
var normalFrag = "\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n\n vec4 sample = texture2D(uSampler[0], textureCoord);\n gl_FragColor = sample * uColor;\n}\n";
var normalVert = "\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n %SPRITE_CODE%\n}\n";
var NormalShader = (function(_super) {
__extends(NormalShader, _super);

function NormalShader(gl, tilingMode) {
_super.call(this, gl, normalVert, normalFrag, tilingMode);
}
return NormalShader;
}(extras.PictureShader));
extras.NormalShader = NormalShader;
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
var overlayFrag = "\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n vec4 source = texture2D(uSampler[0], textureCoord) * uColor;\n vec4 target = texture2D(uSampler[1], vMapCoord);\n\n //reverse hardlight\n if (source.a == 0.0) {\n gl_FragColor = vec4(0, 0, 0, 0);\n return;\n }\n //yeah, premultiplied\n vec3 Cb = source.rgb/source.a, Cs;\n if (target.a > 0.0) {\n Cs = target.rgb / target.a;\n }\n vec3 multiply = Cb * Cs * 2.0;\n vec3 Cb2 = Cb * 2.0 - 1.0;\n vec3 screen = Cb2 + Cs - Cb2 * Cs;\n vec3 B;\n if (Cs.r <= 0.5) {\n B.r = multiply.r;\n } else {\n B.r = screen.r;\n }\n if (Cs.g <= 0.5) {\n B.g = multiply.g;\n } else {\n B.g = screen.g;\n }\n if (Cs.b <= 0.5) {\n B.b = multiply.b;\n } else {\n B.b = screen.b;\n }\n vec4 res;\n res.xyz = (1.0 - source.a) * Cs + source.a * B;\n res.a = source.a + target.a * (1.0-source.a);\n\n if ((target.r <= 0.1) && (target.g <= 0.1) && (target.b <= 0.1)) {\n res.r = 0.177;\n res.g = 0.13;\n res.b = 0.212;\n }\n\n gl_FragColor = vec4(res.xyz * res.a, res.a);\n}\n";
var OverlayShader = (function(_super) {
__extends(OverlayShader, _super);

function OverlayShader(gl, tilingMode) {
_super.call(this, gl, extras.PictureShader.blendVert, overlayFrag, tilingMode);
}
return OverlayShader;
}(extras.PictureShader));
extras.OverlayShader = OverlayShader;
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
function nextPow2(v) {
v += (v === 0) ? 1 : 0;
--v;
v |= v >>> 1;
v |= v >>> 2;
v |= v >>> 4;
v |= v >>> 8;
v |= v >>> 16;
return v + 1;
}
var PictureRenderer = (function(_super) {
__extends(PictureRenderer, _super);

function PictureRenderer(renderer) {
_super.call(this, renderer);
}
PictureRenderer.prototype.onContextChange = function() {
var gl = this.renderer.gl;
this.drawModes = extras.mapFilterBlendModesToPixi(gl);
this.normalShader = [new extras.NormalShader(gl, 0), new extras.NormalShader(gl, 1), new extras.NormalShader(gl, 2)];
this._tempClamp = new Float32Array(4);
this._tempColor = new Float32Array(4);
this._tempRect = new PIXI.Rectangle();
this._tempRect2 = new PIXI.Rectangle();
this._tempRect3 = new PIXI.Rectangle();
this._tempMatrix = new PIXI.Matrix();
this._tempMatrix2 = new PIXI.Matrix();
this._bigBuf = new Uint8Array(1 << 20);
this._renderTexture = new PIXI.BaseRenderTexture(1024, 1024);
};
PictureRenderer.prototype.start = function() {};
PictureRenderer.prototype.flush = function() {};
PictureRenderer.prototype._getRenderTexture = function(minWidth, minHeight) {
if (this._renderTexture.width < minWidth ||
this._renderTexture.height < minHeight) {
minHeight = nextPow2(minWidth);
minHeight = nextPow2(minHeight);
this._renderTexture.resize(minWidth, minHeight);
}
return this._renderTexture;
};
PictureRenderer.prototype._getBuf = function(size) {
var buf = this._bigBuf;
if (buf.length < size) {
size = nextPow2(size);
buf = new Uint8Array(size);
this._bigBuf = buf;
}
return buf;
};
PictureRenderer.prototype.render = function(sprite) {
if (!sprite.texture.valid) {
return;
}
var tilingMode = 0;
if (sprite.tileTransform) {
tilingMode = this._isSimpleSprite(sprite) ? 1 : 2;
}
var blendShader = this.drawModes[sprite.blendMode];
if (blendShader) {
this._renderBlend(sprite, blendShader[tilingMode]);
} else {
this._renderNormal(sprite, this.normalShader[tilingMode]);
}
};
PictureRenderer.prototype._renderNormal = function(sprite, shader) {
var renderer = this.renderer;
renderer.bindShader(shader);
renderer.state.setBlendMode(sprite.blendMode);
this._renderInner(sprite, shader);
};
PictureRenderer.prototype._renderBlend = function(sprite, shader) {
var renderer = this.renderer;
var spriteBounds = sprite.getBounds();
var renderTarget = renderer._activeRenderTarget;
var matrix = renderTarget.projectionMatrix;
var flipX = matrix.a < 0;
var flipY = matrix.d < 0;
var resolution = renderTarget.resolution;
var screen = this._tempRect;
var fr = renderTarget.sourceFrame || renderTarget.destinationFrame;
screen.x = 0;
screen.y = 0;
screen.width = fr.width;
screen.height = fr.height;
var bounds = this._tempRect2;
var fbw = fr.width * resolution,
fbh = fr.height * resolution;
bounds.x = (spriteBounds.x + matrix.tx / matrix.a) * resolution + fbw / 2;
bounds.y = (spriteBounds.y + matrix.ty / matrix.d) * resolution + fbh / 2;
bounds.width = spriteBounds.width * resolution;
bounds.height = spriteBounds.height * resolution;
if (flipX) {
bounds.y = fbw - bounds.width - bounds.x;
}
if (flipY) {
bounds.y = fbh - bounds.height - bounds.y;
}
var screenBounds = this._tempRect3;
var x_1 = Math.floor(Math.max(screen.x, bounds.x));
var x_2 = Math.ceil(Math.min(screen.x + screen.width, bounds.x + bounds.width));
var y_1 = Math.floor(Math.max(screen.y, bounds.y));
var y_2 = Math.ceil(Math.min(screen.y + screen.height, bounds.y + bounds.height));
var pixelsWidth = x_2 - x_1;
var pixelsHeight = y_2 - y_1;
if (pixelsWidth <= 0 || pixelsHeight <= 0) {
return;
}
var rt = this._getRenderTexture(pixelsWidth, pixelsHeight);
renderer.bindTexture(rt, 1, true);
var gl = renderer.gl;
if (renderer.renderingToScreen && renderTarget.root) {
var buf = this._getBuf(pixelsWidth * pixelsHeight * 4);
gl.readPixels(x_1, y_1, pixelsWidth, pixelsHeight, gl.RGBA, gl.UNSIGNED_BYTE, this._bigBuf);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, pixelsWidth, pixelsHeight, gl.RGBA, gl.UNSIGNED_BYTE, this._bigBuf);
} else {
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, x_1, y_1, pixelsWidth, pixelsHeight);
}
renderer.bindShader(shader);
renderer.state.setBlendMode(PIXI.BLEND_MODES.NORMAL);
if (shader.uniforms.mapMatrix) {
var mapMatrix = this._tempMatrix;
mapMatrix.a = bounds.width / rt.width / spriteBounds.width;
if (flipX) {
mapMatrix.a = -mapMatrix.a;
mapMatrix.tx = (bounds.x - x_1) / rt.width - (spriteBounds.x + spriteBounds.width) * mapMatrix.a;
} else {
mapMatrix.tx = (bounds.x - x_1) / rt.width - spriteBounds.x * mapMatrix.a;
}
mapMatrix.d = bounds.height / rt.height / spriteBounds.height;
if (flipY) {
mapMatrix.d = -mapMatrix.d;
mapMatrix.ty = (bounds.y - y_1) / rt.height - (spriteBounds.y + spriteBounds.height) * mapMatrix.d;
} else {
mapMatrix.ty = (bounds.y - y_1) / rt.height - spriteBounds.y * mapMatrix.d;
}
shader.uniforms.mapMatrix = mapMatrix.toArray(true);
}
this._renderInner(sprite, shader);
};
PictureRenderer.prototype._renderInner = function(sprite, shader) {
var renderer = this.renderer;
if (shader.tilingMode > 0) {
this._renderWithShader(sprite, shader.tilingMode === 1, shader);
} else {
this._renderSprite(sprite, shader);
}
};
PictureRenderer.prototype._renderWithShader = function(ts, isSimple, shader) {
var quad = shader.tempQuad;
var renderer = this.renderer;
renderer.bindVao(quad.vao);
var vertices = quad.vertices;
var _width = ts._width;
var _height = ts._height;
var _anchorX = ts._anchor._x;
var _anchorY = ts._anchor._y;
var w0 = _width * (1 - _anchorX);
var w1 = _width * -_anchorX;
var h0 = _height * (1 - _anchorY);
var h1 = _height * -_anchorY;
var wt = ts.transform.worldTransform;
var a = wt.a;
var b = wt.b;
var c = wt.c;
var d = wt.d;
var tx = wt.tx;
var ty = wt.ty;
vertices[0] = (a * w1) + (c * h1) + tx;
vertices[1] = (d * h1) + (b * w1) + ty;
vertices[2] = (a * w0) + (c * h1) + tx;
vertices[3] = (d * h1) + (b * w0) + ty;
vertices[4] = (a * w0) + (c * h0) + tx;
vertices[5] = (d * h0) + (b * w0) + ty;
vertices[6] = (a * w1) + (c * h0) + tx;
vertices[7] = (d * h0) + (b * w1) + ty;
vertices = quad.uvs;
vertices[0] = vertices[6] = -ts.anchor.x;
vertices[1] = vertices[3] = -ts.anchor.y;
vertices[2] = vertices[4] = 1.0 - ts.anchor.x;
vertices[5] = vertices[7] = 1.0 - ts.anchor.y;
quad.upload();
var tex = ts._texture;
var lt = ts.tileTransform.localTransform;
var uv = ts.uvTransform;
var mapCoord = uv.mapCoord;
var uClampFrame = uv.uClampFrame;
var uClampOffset = uv.uClampOffset;
var w = tex.width;
var h = tex.height;
var W = _width;
var H = _height;
var tempMat = this._tempMatrix2;
tempMat.set(lt.a * w / W, lt.b * w / H, lt.c * h / W, lt.d * h / H, lt.tx / W, lt.ty / H);
tempMat.invert();
if (isSimple) {
tempMat.append(mapCoord);
} else {
shader.uniforms.uMapCoord = mapCoord.toArray(true);
shader.uniforms.uClampFrame = uClampFrame;
shader.uniforms.uClampOffset = uClampOffset;
}
shader.uniforms.uTransform = tempMat.toArray(true);
var color = this._tempColor;
var alpha = ts.worldAlpha;
PIXI.utils.hex2rgb(ts.tint, color);
color[0] *= alpha;
color[1] *= alpha;
color[2] *= alpha;
color[3] = alpha;
shader.uniforms.uColor = color;
renderer.bindTexture(tex, 0, true);
quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0);
};
PictureRenderer.prototype._renderSprite = function(sprite, shader) {
var renderer = this.renderer;
var quad = shader.tempQuad;
renderer.bindVao(quad.vao);
var uvs = sprite.texture._uvs;
var vertices = quad.vertices;
var vd = sprite.vertexData;
for (var i = 0; i < 8; i++) {
quad.vertices[i] = vd[i];
}
quad.uvs[0] = uvs.x0;
quad.uvs[1] = uvs.y0;
quad.uvs[2] = uvs.x1;
quad.uvs[3] = uvs.y1;
quad.uvs[4] = uvs.x2;
quad.uvs[5] = uvs.y2;
quad.uvs[6] = uvs.x3;
quad.uvs[7] = uvs.y3;
quad.upload();
var frame = sprite.texture.frame;
var base = sprite.texture.baseTexture;
var clamp = this._tempClamp;
var eps = 0.5 / base.resolution;
clamp[0] = (frame.x + eps) / base.width;
clamp[1] = (frame.y + eps) / base.height;
clamp[2] = (frame.x + frame.width - eps) / base.width;
clamp[3] = (frame.y + frame.height - eps) / base.height;
shader.uniforms.uTextureClamp = clamp;
var color = this._tempColor;
PIXI.utils.hex2rgb(sprite.tint, color);
var alpha = sprite.worldAlpha;
color[0] *= alpha;
color[1] *= alpha;
color[2] *= alpha;
color[3] = alpha;
shader.uniforms.uColor = color;
renderer.bindTexture(base, 0, true);
quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0);
};
PictureRenderer.prototype._isSimpleSprite = function(ts) {
var renderer = this.renderer;
var tex = ts._texture;
var baseTex = tex.baseTexture;
var isSimple = baseTex.isPowerOfTwo && tex.frame.width === baseTex.width && tex.frame.height === baseTex.height;
if (isSimple) {
if (!baseTex._glTextures[renderer.CONTEXT_UID]) {
if (baseTex.wrapMode === PIXI.WRAP_MODES.CLAMP) {
baseTex.wrapMode = PIXI.WRAP_MODES.REPEAT;
}
} else {
isSimple = baseTex.wrapMode !== PIXI.WRAP_MODES.CLAMP;
}
}
return isSimple;
};
return PictureRenderer;
}(PIXI.ObjectRenderer));
extras.PictureRenderer = PictureRenderer;
PIXI.WebGLRenderer.registerPlugin('picture', PictureRenderer);
PIXI.CanvasRenderer.registerPlugin('picture', PIXI.CanvasSpriteRenderer);
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
var PictureSprite = (function(_super) {
__extends(PictureSprite, _super);

function PictureSprite(texture) {
_super.call(this, texture);
this.pluginName = 'picture';
}
return PictureSprite;
}(PIXI.Sprite));
extras.PictureSprite = PictureSprite;
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));
var PIXI;
(function(PIXI) {
var extras;
(function(extras) {
var PictureTilingSprite = (function(_super) {
__extends(PictureTilingSprite, _super);

function PictureTilingSprite(texture) {
_super.call(this, texture);
this.pluginName = 'picture';
}
return PictureTilingSprite;
}(extras.TilingSprite));
extras.PictureTilingSprite = PictureTilingSprite;
})(extras = PIXI.extras || (PIXI.extras = {}));
})(PIXI || (PIXI = {}));

+ 2
- 2
src/client/ui/templates/characters/styles.less View File

@@ -88,14 +88,14 @@
}

.name {
width: 60%;
width: 47%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.class {
width: 40%;
width: 53%;
text-align: right;
color: darken(@white, 25%);
}


+ 1
- 0
src/client/ui/templates/context/context.js View File

@@ -11,6 +11,7 @@ define([
) {
return {
tpl: template,
modal: true,

postRender: function() {
this.onEvent('onContextMenu', this.onContextMenu.bind(this));


+ 17
- 14
src/client/ui/templates/equipment/equipment.js View File

@@ -99,11 +99,11 @@ define([

items
.filter(function(item) {
var spellId = item.spellId;
if ((spellId != null) && (item.slot))
skipSpellId = spellId;
var runeSlot = item.runeSlot;
if ((runeSlot != null) && (item.slot))
skipSpellId = runeSlot;

return ((item.eq) && ((item.slot) || (item.spellId != null)));
return ((item.eq) && ((item.slot) || (item.runeSlot != null)));
}, this)
.forEach(function(item) {
var imgX = -item.sprite[0] * 64;
@@ -111,10 +111,10 @@ define([

var slot = item.slot;
if (!slot) {
var spellId = item.spellId;
if (spellId > skipSpellId)
spellId--;
slot = 'rune-' + spellId;
var runeSlot = item.runeSlot;
if (runeSlot > skipSpellId)
runeSlot--;
slot = 'rune-' + runeSlot;
}

var spritesheet = item.spritesheet || '../../../images/items.png';
@@ -177,7 +177,7 @@ define([
.css('background', 'url("' + spriteSheet + '") ' + imgX + 'px ' + imgY + 'px')
.on('mousemove', this.onHoverItem.bind(this, el, item, null))
.on('mouseleave', this.onHoverItem.bind(this, null, null))
.on('click', this.equipItem.bind(this, item));
.on('click', this.equipItem.bind(this, item, slot));

if (item == this.hoverCompare)
el.find('.icon').addClass('eq');
@@ -187,7 +187,7 @@ define([
container.hide();
},

equipItem: function(item) {
equipItem: function(item, slot) {
if (item == this.hoverCompare) {
this.find('.itemList').hide();
return;
@@ -204,8 +204,8 @@ define([
cpn = 'inventory';
method = 'learnAbility';
data = {
id: item.id,
replaceId: this.hoverCompare ? this.hoverCompare.id : null
itemId: item.id,
slot: ~~slot.replace('rune-', '') + 1
};

if (item.empty) {
@@ -213,8 +213,10 @@ define([
this.find('.itemList').hide();
return;
}
else
data = this.hoverCompare.id;
else {
method = 'unlearnAbility';
data.itemId = this.hoverCompare.id;
}
}
}

@@ -286,6 +288,7 @@ define([
},
offense: {
'crit chance': (~~(stats.critChance * 10) / 10) + '%',
'crit multiplier': (~~(stats.critMultiplier * 10) / 10) + '%',
gap1: '',
'arcane increase': stats.elementArcanePercent + '%',
'fire increase': stats.elementFirePercent + '%',


+ 5
- 2
src/client/ui/templates/hud/hud.js View File

@@ -23,11 +23,14 @@ define([
},

onGetPortrait: function(portrait) {
var x = (['warrior', 'cleric', 'wizard', 'thief'].indexOf(portrait) * -64);
var spritesheet = portrait.spritesheet || '../../../images/portraitIcons.png';

var x = portrait.x * -64;
var y = portrait.y * -64;

this.find('.portrait')
.css({
background: 'url("../../../images/portraitIcons.png") ' + x + 'px 0px',
background: 'url("' + spritesheet + '") ' + x + 'px ' + y + 'px',
visibility: 'visible'
});
},


+ 4
- 4
src/client/ui/templates/inventory/inventory.js View File

@@ -118,11 +118,11 @@ define([
var itemEl = $(tplItem)
.appendTo(container);

var spritesheet = item.spritesheet || 'items';
var spritesheet = item.spritesheet || '../../../images/items.png';
if (item.material)
spritesheet = 'materials';
spritesheet = '../../../images/materials.png';
else if (item.quest)
spritesheet = 'questItems';
spritesheet = '../../../images/questItems.png';

itemEl
.data('item', item)
@@ -132,7 +132,7 @@ define([
.on('mousemove', this.onHover.bind(this, itemEl, item))
.on('mouseleave', this.hideTooltip.bind(this, itemEl, item))
.find('.icon')
.css('background', 'url(../../../images/' + spritesheet + '.png) ' + imgX + 'px ' + imgY + 'px')
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px')
.on('contextmenu', this.showContext.bind(this, item));

if (item.quantity)


+ 2
- 1
src/client/ui/templates/login/login.js View File

@@ -23,7 +23,8 @@ define([
this.on('.btnRegister', 'click', this.onRegisterClick.bind(this));

this.find('.extra, .version')
.appendTo($('<div class="uiLoginExtra"></div>>').appendTo('.ui-container'));
.appendTo($('<div class="uiLoginExtra"></div>')
.appendTo('.ui-container'));

$('.uiLoginExtra').find('.button').on('click', this.redirect.bind(this));



+ 1
- 1
src/client/ui/templates/login/template.html View File

@@ -16,5 +16,5 @@
<div class="el button btnPaypal" location="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BR2CC82WUAVEA">Donate on Paypal</div>
<div class="el button btnWiki" location="http://isleward.gamepedia.com/Isleward_Wiki">Access the Wiki</div>
</div>
<div class="version">v0.1.3</div>
<div class="version">v0.1.4</div>
</div>

+ 3
- 3
src/client/ui/templates/online/styles.less View File

@@ -6,7 +6,7 @@
width: 400px;
height: 400px;
background-color: @gray;
padding: 32px;
padding: 10px;
border: 4px solid @lightGray;
text-align: center;
@@ -30,11 +30,11 @@
}

&:nth-child(2) {
width: 60%;
width: 55%;
}

&:nth-child(3) {
width: 30%;
width: 35%;
}
}



+ 4
- 4
src/client/ui/templates/smithing/smithing.js View File

@@ -222,11 +222,11 @@ define([
var imgX = -item.sprite[0] * 64;
var imgY = -item.sprite[1] * 64;

var spritesheet = item.spritesheet || 'items';
var spritesheet = item.spritesheet || '../../../images/items.png';
if (item.material)
spritesheet = 'materials';
spritesheet = '../../../images/materials.png';
else if (item.quest)
spritesheet = 'questItems';
spritesheet = '../../../images/questItems.png';

var el = $(templateItem)
.appendTo(container);
@@ -236,7 +236,7 @@ define([
.on('mousemove', this.onHover.bind(this, el, item))
.on('mouseleave', this.hideTooltip.bind(this, el, item))
.find('.icon')
.css('background', 'url(../../../images/' + spritesheet + '.png) ' + imgX + 'px ' + imgY + 'px');
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px');

if (item.quantity) {
var quantityText = item.quantityText;


+ 4
- 0
src/client/ui/templates/tooltipItem/styles.less View File

@@ -18,6 +18,10 @@
.name {
margin-bottom: 8px;

.type {
color: darken(@white, 40%);
}

.power {
color: @green;
display: none;


+ 5
- 1
src/client/ui/templates/tooltipItem/templateTooltip.html View File

@@ -1,4 +1,8 @@
<div class="name q$QUALITY$"><div class="text">$NAME$</div><div class="power">$POWER$</div></div>
<div class="name q$QUALITY$">
<div class="text">$NAME$</div>
<div class="type">$TYPE$</div>
<div class="power">$POWER$</div>
</div>
<div class="stats">$STATS$</div>
<div class="effects">$EFFECTS$</div>
<div class="faction">faction: $faction$</div>


+ 9
- 1
src/client/ui/templates/tooltipItem/tooltipItem.js View File

@@ -64,7 +64,7 @@ define([
var statName = statTranslations.translate(s);
var value = tempStats[s];

if (['addCritChance', 'sprintChance', 'dmgPercent', 'xpIncrease'].indexOf(s) > -1)
if (['addCritChance', 'addCritMultiplier', 'sprintChance', 'dmgPercent', 'xpIncrease'].indexOf(s) > -1)
value += '%';
else if ((s.indexOf('element') == 0) && (s.indexOf('Resist') == -1))
value += '%';
@@ -119,6 +119,14 @@ define([
else
this.tooltip.find('.level').show();

if ((!item.type) || (item.type == item.name))
this.tooltip.find('.type').hide();
else {
this.tooltip.find('.type')
.html(item.type)
.show();
}

if (item.power)
this.tooltip.find('.power').show();



+ 18
- 11
src/server/combat/combat.js View File

@@ -12,13 +12,15 @@ define([
var tgtValues = config.target.stats.values;

var statValue = 0;
var statType = config.statType;
if (!(statType instanceof Array))
statType = [statType];
var dmg = 0;
statType.forEach(function(s) {
statValue += srcValues[s];
});
if (config.statType) {
var statType = config.statType;
if (!(statType instanceof Array))
statType = [statType];
var dmg = 0;
statType.forEach(function(s) {
statValue += srcValues[s];
});
}

statValue = max(1, statValue);

@@ -30,8 +32,10 @@ define([
resist += (tgtValues[elementName + 'Resist'] || 0);
}

var statMult = config.statMult || 1;

var dps = (
(config.statMult * statValue * config.damage) *
(statMult * statValue * config.damage) *
max((0.5 + (dmgPercent / 100)), 0.5)
);

@@ -44,9 +48,12 @@ define([
);
}

var cd = config.source.mob ? 1 : config.cd;
var amount = dps;

var amount = dps * cd * 0.3;
if ((config.source.mob) || (config.cd)) {
var cd = config.source.mob ? 1 : config.cd;
amount *= cd * 0.3;
}

var isCrit = false;
if (!config.noCrit) {
@@ -54,7 +61,7 @@ define([
var roll = mathRandom() * 100;
if ((roll < critChance) || (config.crit)) {
isCrit = true;
amount *= 1.5;
amount *= (srcValues.critMultiplier / 100);
}
}



+ 23
- 7
src/server/components/aggro.js View File

@@ -90,7 +90,7 @@ define([
//find mobs in range
var range = this.range;
var faction = this.faction;
var inRange = this.physics.getArea(x - range, y - range, x + range, y + range, (c => (((!c.player) || (!obj.player)) && (c.aggro) && (c.aggro.willAttack(obj)))));
var inRange = this.physics.getArea(x - range, y - range, x + range, y + range, (c => (((!c.player) || (!obj.player)) && (c.aggro) && (c.aggro.willAutoAttack(obj)))));

if (inRange.length == 0)
return;
@@ -118,7 +118,22 @@ define([
}
},

willAttack: function(target) {
canAttack: function(target) {
var obj = this.obj;

if (target == obj)
return false;
else if ((target.player) && (obj.player))
return ((obj.prophecies.hasProphecy('butcher')) && (target.prophecies.hasProphecy('butcher')));
else if ((target.follower) && (target.follower.master.player) && (obj.player))
return false;
else if (obj.player)
return true;
else if (target.aggro.faction != obj.aggro.faction)
return true;
},

willAutoAttack: function(target) {
if (this.obj == target)
return false;

@@ -126,9 +141,6 @@ define([
if (faction == null)
return false;

if ((target.player) && (this.obj.player))
return ((this.obj.prophecies.hasProphecy('butcher')) && (target.prophecies.hasProphecy('butcher')));

var rep = this.obj.reputation;
if (!rep) {
var targetRep = target.reputation;
@@ -151,6 +163,10 @@ define([
},

tryEngage: function(obj, amount, threatMult) {
//Don't aggro yourself, stupid
if (obj == this.obj)
return;

var result = {
success: true
};
@@ -235,7 +251,7 @@ define([
if (this.obj.spellbook)
this.obj.spellbook.unregisterCallback(obj.id, true);

if ((this.list.length == 0) && (this.obj.mob))
if ((this.list.length == 0) && (this.obj.mob) && (!this.obj.follower))
this.obj.stats.resetHp();
},

@@ -293,7 +309,7 @@ define([
for (var i = 0; i < lLen; i++) {
var l = list[i];
if (l.obj.destroyed) {
list.splice(i, 1);
this.unAggro(l.obj);
i--;
lLen--;
}


+ 1
- 1
src/server/components/auth.js View File

@@ -90,7 +90,7 @@ define([
io.set({
ent: this.charname,
field: 'character',
value: JSON.stringify(simple),
value: JSON.stringify(simple).split(`'`).join(`''`),
callback: callback
});



+ 4
- 1
src/server/components/chatter.js View File

@@ -9,7 +9,7 @@ define([
chats: null,
cdMax: 50,
cd: 0,
chance: 0.02,
chance: 0.035,

init: function(blueprint) {
this.chats = extend(true, [], blueprint.chats);
@@ -17,6 +17,9 @@ define([
},

update: function() {
if ((this.obj.aggro) && (this.obj.aggro.list.length > 0))
return;

if ((this.cd == 0) && (Math.random() < this.chance)) {
this.cd = this.cdMax;



+ 23
- 9
src/server/components/dialogue.js View File

@@ -108,6 +108,11 @@ define([
else
return null;
}
else if (stateConfig.method) {
stateConfig.method(sourceObj);
if (!stateConfig.msg)
return;
}

var result = {
id: this.obj.id,
@@ -145,16 +150,25 @@ define([
result.options = Object.keys(result.options);
}

result.options = result.options.map(function(o) {
var gotoState = this.states[(o + '').split('.')[0]];
if (!gotoState.options[o])
return null;
result.options = result.options
.map(function(o) {
var gotoState = this.states[(o + '').split('.')[0]];
var picked = gotoState.options[o];

if (!picked)
return null;
else if (picked.prereq) {
var doesConform = picked.prereq(sourceObj);
if (!doesConform)
return null;
}

return {
id: o,
msg: gotoState.options[o].msg
};
}, this);
return {
id: o,
msg: picked.msg
};
}, this)
.filter(o => !!o);

result.options.push({
msg: 'Goodbye',


+ 4
- 8
src/server/components/equipment.js View File

@@ -91,12 +91,8 @@ define([
this.obj.spellbook.calcDps();

if ((!this.obj.mob) || (item.ability)) {
if (item.spell) {
this.obj.inventory.learnAbility({
id: itemId,
spellId: spellId
}, true);
}
if (item.spell)
this.obj.inventory.learnAbility(itemId, item.runeSlot);
else {
var result = item;
if (item.effects) {
@@ -148,11 +144,11 @@ define([
}

delete item.eq;
this.eq[item.slot] = null;
delete this.eq[item.slot];

if (item.spell) {
item.eq = true;
this.obj.inventory.learnAbility(itemId);
this.obj.inventory.unlearnAbility(itemId, item.runeSlot);
} else {
if (!item.effects)
this.obj.syncer.setArray(true, 'inventory', 'getItems', item);


+ 1
- 1
src/server/components/extensions/factionVendor.js View File

@@ -30,7 +30,7 @@ define([

this.items[name] = list;
this.regenList(list);
} else if ((list.level != requestLevel) || (Math.random() < 2))
} else if (list.level != requestLevel)
this.regenList(list);

var reputation = requestedBy.reputation;


+ 138
- 0
src/server/components/extensions/socialCommands.js View File

@@ -0,0 +1,138 @@
define([
'config/roles',
'world/atlas',
'items/generator',
'misc/random',
'items/config/slots'
], function(
roles,
atlas,
generator,
random,
configSlots
) {
return {
roleLevel: null,

init: function(blueprint) {
this.roleLevel = roles.getRoleLevel(this.obj);
},

onBeforeChat: function(msg) {
if (this.roleLevel < 10)
return;

var messageText = msg.message;
if (messageText[0] != '/')
return;

messageText = messageText.substr(1).split(' ');
var actionName = messageText.splice(0, 1)[0].toLowerCase();
actionName = Object.keys(this).find(a => (a.toLowerCase() == actionName));
if (!actionName)
return;

var config = {};
if ((messageText.length == 1) && (messageText[0].indexOf('=') == -1))
config = messageText[0];
else {
messageText.forEach(function(m) {
m = m.split('=');
config[m[0]] = m[1];
});
}

msg.ignore = true;

atlas.performAction(this.obj, {
cpn: 'social',
method: actionName,
data: config
});
},

//actions
getItem: function(config) {
if (config.slot == 'set') {
configSlots.slots.forEach(function(s) {
if (s == 'tool')
return;

var newConfig = extend(true, {}, config, {
slot: s
});

this.getItem(newConfig);
}, this);

return;
}

if (config.stats)
config.stats = config.stats.split(',');

this.obj.inventory.getItem(generator.generate(config));
},

getGold: function(amount) {
this.obj.trade.gold += ~~amount;
this.obj.syncer.set(true, 'trade', 'gold', this.obj.trade.gold);
},

setLevel: function(level) {
var obj = this.obj;
var syncer = obj.syncer;

level = Math.max(1, ~~level);

var stats = obj.stats;
var values = stats.values;
var oldLevel = values.level;

values.level = level;

var delta = level - oldLevel;

values.hpMax += (40 * delta);

syncer.setObject(true, 'stats', 'values', 'level', level);
syncer.setObject(true, 'stats', 'values', 'hpMax', values.hpMax);

process.send({
method: 'object',
serverId: obj.serverId,
obj: {
level: level
}
});

stats.calcXpMax();
},

godMode: function() {
var obj = this.obj;

var statValues = obj.stats.values;
var newValues = {
int: 10000000,
str: 10000000,
dex: 10000000,
hpMax: 10000000,
hp: 10000000,
manaMax: 10000000,
mana: 10000000
};

var syncer = obj.syncer;

for (var s in newValues) {
var newValue = newValues[s];
statValues[s] = newValue;

syncer.setObject(true, 'stats', 'values', s, newValue);
}

obj.spellbook.calcDps();
}
};
});

+ 80
- 15
src/server/components/follower.js View File

@@ -1,7 +1,7 @@
define([
], function(
) {
return {
type: 'follower',
@@ -9,6 +9,12 @@ define([
master: null,

lifetime: -1,
maxDistance: 10,

lastMasterPos: {
x: 0,
y: 0
},

fGetHighest: {
inCombat: null,
@@ -16,9 +22,13 @@ define([
},

bindEvents: function() {
this.lifetime = 100;
var master = this.master;
this.lastMasterPos.x = master.x;
this.lastMasterPos.y = master.y;

this.fGetHighest.inCombat = this.master.aggro.getHighest.bind(this.master.aggro);
this.obj.aggro.faction = master.aggro.faction;

this.fGetHighest.inCombat = master.aggro.getHighest.bind(master.aggro);
this.fGetHighest.outOfCombat = this.returnNoAggro.bind(this);
},

@@ -48,11 +58,42 @@ define([
});
},

teleport: function() {
var obj = this.obj;
var physics = obj.instance.physics;
var syncer = obj.syncer;
var master = this.master;

var newPosition = physics.getOpenCellInArea(master.x - 1, master.y - 1, master.x + 1, master.y + 1);
physics.removeObject(obj, obj.x, obj.y);

obj.x = newPosition.x;
obj.y = newPosition.y;

syncer.o.x = obj.x;
syncer.o.y = obj.y;

physics.addObject(obj, obj.x, obj.y);

obj.instance.syncer.queue('onGetObject', {
x: obj.x,
y: obj.y,
components: [{
type: 'attackAnimation',
row: 0,
col: 4
}]
});
},

update: function() {
this.lifetime--;
if (this.lifetime <= 0) {
this.despawn();
return;
if (this.lifetime > 0) {
this.lifetime--;
if (this.lifetime <= 0) {
this.despawn();
return;
}
}

var obj = this.obj;
@@ -63,18 +104,42 @@ define([
return;
}

var doMove = (
(Math.abs(obj.x - master.x) >= 10) ||
(Math.abs(obj.y - master.y) >= 10)
);
var attacker = this.fGetHighest.inCombat();
var maxDistance = this.maxDistance;
var distance = Math.max(Math.abs(obj.x - master.x), Math.abs(obj.y - master.y));

if (doMove) {
if (obj.aggro.getHighest == this.fGetHighest.inCombat)
obj.mob.target = obj;
var doMove = (distance >= maxDistance);
//When we're too far, just teleport
if ((!attacker) && (distance >= maxDistance * 2)) {
this.teleport();
return;
}

var doMove = false;
//If we're not too far from the master but the master is not in combat, move anyway
if (!attacker) {
var lastMasterPos = this.lastMasterPos;

if ((master.x != lastMasterPos.x) || (master.y != lastMasterPos.y)) {
doMove = true;
lastMasterPos.x = master.x;
lastMasterPos.y = master.y;
}
}

if (doMove) {
this.obj.clearQueue();
obj.mob.target = obj;
}

obj.aggro.getHighest = doMove ? this.fGetHighest.outOfCombat : this.fGetHighest.inCombat;
},

simplify: function() {
return {
type: 'follower',
master: this.master.id
};
}
};
});

+ 110
- 109
src/server/components/inventory.js View File

@@ -34,12 +34,16 @@ define([
var item = items[i];

//Hacks for old items
if ((item.spell) && (!item.spell.rolls))
if (((item.spell) && (!item.spell.rolls)) || (!item.sprite)) {
items.splice(i, 1);
i--;
iLen--;
continue;
else if ((item.spell) && (item.type == 'Spear')) {
} else if ((item.spell) && (item.type == 'Spear')) {
item.spell.properties = item.spell.properties || {};
item.spell.properties.range = item.range;
}
} else if (item.quantity == NaN)
item.quantity = 1;
}

this.hookItemEvents(items);
@@ -48,26 +52,9 @@ define([
this.getItem(items[i], true);
}

if ((this.obj.player) && (!isTransfer)) {
if ((this.obj.player) && (!isTransfer))
this.getDefaultAbilities();

/*this.getItem(generator.generate({
spell: true,
spellName: 'arcane barrier'
}));*/

/*for (var i = 0; i < 1; i++) {
var item = generator.generate({
slot: 'twoHanded',
type: 'Spear',
quality: 4,
level: 1
});

this.getItem(item);
}*/
}

delete blueprint.items;

this.blueprint = blueprint;
@@ -91,7 +78,6 @@ define([
});
}
}

},

//Client Actions
@@ -115,54 +101,62 @@ define([
this.resolveCallback(msg, result);
},

learnAbility: function(id, forceLearn) {
var replaceId = null;
var newSpellId = id.spellId;
if (id.id != null) {
replaceId = id.replaceId;
id = id.id;
learnAbility: function(itemId, runeSlot) {
if (itemId.itemId != null) {
var msg = itemId;
itemId = msg.itemId;
runeSlot = msg.slot;
}

var item = this.findItem(id);
if ((!item) || (!item.spell) || ((item.spellId == null) && (item.eq) && (!forceLearn))) {
if (item)
item.eq = false;
var item = this.findItem(itemId);
if (!item)
return;
else if (!item.spell) {
item.eq = false;
return;
}

var spellbook = this.obj.spellbook;

if ((item.eq) && (!forceLearn)) {
delete item.eq;
spellbook.removeSpellById(item.spellId);
delete item.spellId;
this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
return;
if (item.slot == 'twoHanded')
runeSlot = 0;
else if (runeSlot == null) {
if (!this.items.some(i => (i.runeSlot == 2)))
runeSlot = 2;
else
runeSlot = 1;
}

if (replaceId != null) {
var replaceItem = this.findItem(replaceId);
if (replaceItem) {
delete replaceItem.eq;
spellbook.removeSpellById(replaceItem.spellId);
newSpellId = replaceItem.spellId;
delete replaceItem.spellId;
this.obj.syncer.setArray(true, 'inventory', 'getItems', replaceItem);
}
var currentEq = this.items.find(i => (i.runeSlot == runeSlot));
if (currentEq) {
spellbook.removeSpellById(runeSlot);
delete currentEq.eq;
delete currentEq.runeSlot;
this.obj.syncer.setArray(true, 'inventory', 'getItems', currentEq);
}
if (spellbook.spells.length >= 3) {
if (item.slot)
item.spellId = -1;
this.obj.syncer.setArray(true, 'inventory', 'getItems', item);

item.eq = true;
item.runeSlot = runeSlot;
spellbook.addSpellFromRune(item.spell, runeSlot);
this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
},

unlearnAbility: function(itemId) {
if (itemId.itemId != null)
itemId = itemId.itemId;

var item = this.findItem(itemId);
if (!item)
return;
else if (!item.spell) {
item.eq = false;
return;
}

item.spellId = spellbook.addSpellFromRune(item.spell, newSpellId);
if (item.spellId != -1)
item.eq = true;
else
delete item.spell;
var spellbook = this.obj.spellbook;
spellbook.removeSpellById(item.runeSlot);
delete item.eq;
delete item.runeSlot;
this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
},

@@ -176,7 +170,7 @@ define([
var stash = this.obj.stash;
if (!stash.active)
return;
var clonedItem = extend(true, {}, item);
this.destroyItem(id);
stash.deposit(clonedItem);
@@ -326,25 +320,25 @@ define([
this.getItem(item);
}

var hasSpell = this.items.some(function(i) {
return (
(i.spell) &&
(i.spell.rolls) &&
((i.spell.rolls.damage != null) || (i.spell.rolls.healing != null)) &&
(i.slot != 'twoHanded')
);
});

if (!hasSpell) {
var item = generator.generate({
spell: true,
spellQuality: 'basic',
spellName: classes.spells[this.obj.class][0]
classes.spells[this.obj.class].forEach(function(spellName) {
var hasSpell = this.items.some(function(i) {
return (
(i.spell) &&
(i.spell.name.toLowerCase() == spellName)
);
});
item.eq = true;
item.noSalvage = true;
this.getItem(item);
}

if (!hasSpell) {
var item = generator.generate({
spell: true,
spellQuality: 'basic',
spellName: spellName
});
item.eq = true;
item.noSalvage = true;
this.getItem(item);
}
}, this);
},

createBag: function(x, y, items, ownerId) {
@@ -390,7 +384,7 @@ define([

return obj;
},
getItem: function(item, hideMessage) {
//We need to know if a mob dropped it for quest purposes
var fromMob = item.fromMob;
@@ -413,7 +407,7 @@ define([
exists = true;
if (!existItem.quantity)
existItem.quantity = 1;
existItem.quantity += item.quantity;
existItem.quantity += (item.quantity || 1);
item = existItem;
}
}
@@ -508,7 +502,7 @@ define([

if (item.eq) {
if (item.ability)
this.learnAbility(item.id, true);
this.learnAbility(item.id, item.runeSlot);
else
this.obj.equipment.equip(item.id);
} else {
@@ -521,7 +515,7 @@ define([
text: e.text,
properties: e.properties
}));
var reputation = this.obj.reputation;

//Don't do this check if we don't have a reputation cpn. That means this is most likely a bag
@@ -585,29 +579,13 @@ define([

var blueprint = this.blueprint;

if (blueprint.noRandom) {
this.items = [];
var blueprints = blueprint.blueprints;
for (var i = 0; i < blueprints.length; i++) {
var drop = blueprints[i];
if ((drop.maxLevel) && (drop.maxLevel < killSource.stats.values.level))
continue;

drop.level = drop.level || level;
drop.magicFind = magicFind;

this.getItem(generator.generate(drop), true);
}

killSource.fireEvent('beforeTargetDeath', this.obj, this.items);

if (this.items.length > 0)
this.createBag(this.obj.x, this.obj.y, this.items, ownerId);
} else {
var instancedItems = extend(true, [], this.items);
var useItems = [];
var savedItems = extend(true, [], this.items);
var instancedItems = extend(true, [], this.items);
this.items = [];

var magicFind = (blueprint.magicFind || 0) + killSource.stats.values.magicFind;
if ((!blueprint.noRandom) || (blueprint.alsoRandom)) {
var magicFind = (blueprint.magicFind || 0);
var bonusMagicFind = killSource.stats.values.magicFind;
for (var i = 0; i < blueprint.rolls; i++) {
if (Math.random() * 100 >= (blueprint.chance || 35))
continue;
@@ -639,20 +617,43 @@ define([
slot: useItem.slot,
type: useItem.type,
spell: !!useItem.ability,
stats: useItem.stats ? Object.keys(useItem.stats) : null,
magicFind: magicFind
magicFind: magicFind,
bonusMagicFind: bonusMagicFind
};

useItem = generator.generate(itemBlueprint);

useItems.push(useItem);
this.getItem(useItem);
}
}

killSource.fireEvent('beforeTargetDeath', this.obj, useItems);
if (blueprint.noRandom) {
var blueprints = blueprint.blueprints;
for (var i = 0; i < blueprints.length; i++) {
var drop = blueprints[i];
if ((blueprint.chance) && (~~(Math.random() * 100) >= blueprint.chance))
continue;

if (useItems.length > 0)
this.createBag(this.obj.x, this.obj.y, useItems, ownerId);
if ((drop.maxLevel) && (drop.maxLevel < killSource.stats.values.level))
continue;

drop.level = drop.level || this.obj.stats.values.level;
drop.magicFind = magicFind;

var item = drop;
if (!item.quest)
item = generator.generate(drop);

this.getItem(item, true);
}
}

killSource.fireEvent('beforeTargetDeath', this.obj, this.items);

if (this.items.length > 0)
this.createBag(this.obj.x, this.obj.y, this.items, ownerId);

this.items = savedItems;
},

giveItems: function(obj, hideMessage) {
@@ -715,7 +716,7 @@ define([
if (!effectEvent)
continue;

effectEvent.call(this.obj, item, args[0]);
effectEvent.apply(this.obj, [item, ...args]);
}
}
},


+ 3
- 3
src/server/components/mob.js View File

@@ -31,10 +31,10 @@ define([
if (this.obj.aggro)
target = this.obj.aggro.getHighest();
var goHome = false;
if ((target) && (target != this.obj)) {
if ((target) && (target != this.obj) && ((!this.obj.follower) || (this.obj.follower.master != target))) {
this.fight(target);
return;
} else if (this.target) {
} else if ((!target) && (this.target)) {
this.target = null;
this.obj.clearQueue();
goHome = true;
@@ -105,7 +105,7 @@ define([
if (!doesCollide) {
hasLos = this.physics.hasLos(x, y, tx, ty);
if (hasLos) {
if (rnd() < 0.65) {
if (((obj.follower) && (obj.follower.master.player)) || (rnd() < 0.65)) {
var spell = obj.spellbook.getRandomSpell(target);
var success = obj.spellbook.cast({
spell: spell,


+ 2
- 0
src/server/components/notice.js View File

@@ -13,6 +13,8 @@ define([

maxLevel: 0,

contents: [],

init: function(blueprint) {
this.msg = blueprint.msg;
this.actions = blueprint.actions || {};


+ 18
- 4
src/server/components/player.js View File

@@ -25,11 +25,12 @@ define([

spawn: function(character) {
var obj = this.obj;

extend(true, obj, {
sheetName: classes.getSpritesheet(character.class),
layerName: 'mobs',
cell: character.cell,
previewSpritesheet: character.previewSpritesheet,
previewSpritesheet: character.previewSpritesheet || null,
name: character.name,
class: character.class,
zoneName: character.zoneName || 'tutorial',
@@ -59,7 +60,8 @@ define([

stats.stats.logins++;

//obj.addComponent('spellbook', { spells: extend(true, [], classes.spells[obj.class]) });
obj.portrait = classes.portraits[character.class];

obj.addComponent('spellbook');

obj.addComponent('dialogue');
@@ -67,8 +69,9 @@ define([
obj.addComponent('reputation', character.components.find(c => c.type == 'reputation'));

obj.addComponent('social');
obj.social.init();
obj.addComponent('aggro', {
faction: 1
faction: 'players'
});
obj.addComponent('gatherer');
obj.addComponent('stash', {
@@ -152,7 +155,16 @@ define([
if (!permadeath) {
var level = this.obj.stats.values.level;
var spawns = this.obj.spawn;
var spawnPos = ((spawns.find(s => ((s.maxLevel) && (s.maxLevel >= level)))) || (spawns[0]));
var spawnPos = spawns.filter(s => (((s.maxLevel) && (s.maxLevel >= level)) || (!s.maxLevel)));
if ((spawnPos.length == 0) || (!source.name))
spawnPos = spawns[0];
else if (source.name) {
var sourceSpawnPos = spawnPos.find(s => ((s.source) && (s.source.toLowerCase() == source.name.toLowerCase())));
if (sourceSpawnPos)
spawnPos = sourceSpawnPos;
else
spawnPos = spawnPos[0];
}

this.obj.x = spawnPos.x;
this.obj.y = spawnPos.y;
@@ -168,6 +180,8 @@ define([
else
this.obj.stats.dead = true;

this.obj.fireEvent('onAfterDeath', source);

this.obj.aggro.die();
this.obj.spellbook.die();
this.obj.effects.die();


+ 3
- 1
src/server/components/reputation.js View File

@@ -34,7 +34,9 @@ define([
var factionBlueprint = null;
try {
factionBlueprint = require('config/factions/' + factionId);
} catch (e) {}
} catch (e) {
console.log(e);
}

if (factionBlueprint == null)
return;


+ 7
- 1
src/server/components/social.js View File

@@ -12,7 +12,9 @@ define([
partyLeaderId: null,
party: null,

init: function() {},
init: function() {
this.obj.extendComponent('social', 'socialCommands', {});
},

simplify: function() {
return {
@@ -68,6 +70,10 @@ define([
},

chat: function(msg) {
this.onBeforeChat(msg.data);
if (msg.data.ignore)
return;

var charname = this.obj.auth.charname;
var level = this.obj.stats.values.level;
if (level >= 10)


+ 64
- 35
src/server/components/spellbook.js View File

@@ -104,18 +104,19 @@ define([
var sheetName = this.obj.sheetName;
var animationName = builtSpell.animation;

if (sheetName == 'characters')
animation = animations.classes[this.obj.class];
else if (sheetName == 'mobs')
if (sheetName == 'mobs')
animation = animations.mobs;
else if (sheetName == 'bosses')
animation = animations.bosses;
else if (sheetName.indexOf('/') > -1)
animation = animations.mobs[sheetName];
else
animation = animations.classes[this.obj.class];

if ((animation) && (animation[this.obj.cell]) && (animation[this.obj.cell][animationName])) {
builtSpell.animation = extend(true, {}, animation[this.obj.cell][animationName]);
builtSpell.animation.name = animationName;
}
else
} else
builtSpell.animation = null;
}

@@ -124,21 +125,17 @@ define([
if ((this.furthestRange == -1) || (builtSpell.range > this.furthestRange))
this.furthestRange = builtSpell.range;

var id = [0, 1, 2].find(function(s) {
return (!this.spells.some(f => (f.id == s)));
}, this);
builtSpell.id = (spellId == null) ? id : spellId;

if (spellId == null)
this.spells.push(builtSpell);
else
this.spells.splice(spellId, 0, builtSpell);
builtSpell.id = (options.id == null) ? spellId : options.id;
this.spells.push(builtSpell);
this.spells.sort(function(a, b) {
return (a.id - b.id);
});

builtSpell.calcDps(null, true);
if (builtSpell.init)
builtSpell.init();

if (this.obj.player)
if (this.obj.player)
this.obj.syncer.setArray(true, 'spellbook', 'getSpells', builtSpell.simplify());

return builtSpell.id;
@@ -156,7 +153,7 @@ define([
runeSpell.rolls = {};

runeSpell.values = {};
var builtSpell = extend(true, {
values: {}
}, playerSpell, playerSpellConfig);
@@ -172,8 +169,7 @@ define([
if (int) {
val = ~~val;
r = r.replace('i_', '');
}
else
} else
val = ~~(val * 10) / 10;

builtSpell[r] = val;
@@ -201,11 +197,14 @@ define([
},

removeSpellById: function(id) {
this.spells.spliceWhere(s => (s.id == id));
var exists = this.spells.spliceFirstWhere(s => (s.id == id));

this.obj.syncer.setArray(true, 'spellbook', 'removeSpells', id);
if (exists) {
exists.unlearn && exists.unlearn();

this.auto.spliceWhere(a => a.spell == id);
this.obj.syncer.setArray(true, 'spellbook', 'removeSpells', id);
this.auto.spliceWhere(a => a.spell == id);
}
},

queueAuto: function(action) {
@@ -220,8 +219,7 @@ define([
});

return true;
}
else
} else
exists.target = action.target;
},
getRandomSpell: function(target) {
@@ -230,10 +228,10 @@ define([
if (s.canCast(target))
valid.push(i);
});
if (valid.length > 0)
return valid[~~(Math.random() * valid.length)]
else
else
return null;
},
cast: function(action, isAuto) {
@@ -253,17 +251,20 @@ define([
x: this.obj.x,
y: this.obj.y
};
}
else if (spell.spellType == 'buff') {
} else if (spell.spellType == 'buff') {
action.target = this.obj;
}
}

if (!spell.targetGround) {
if (action.target == null) {
console.log('NO TARGET');
console.log(action);
return false;
if (spell.autoTargetFollower) {
action.target = this.spells.find(s => (s.minions) && (s.minions.length > 0));
if (action.target)
action.target = action.target.minions[0];
else
return false;
}
}

//Did we pass in the target id?
@@ -273,9 +274,13 @@ define([
return false;
}

if ((action.target.aggro) && (!this.obj.aggro.willAttack(action.target)) && (spell.spellType != 'buff')) {
if ((this.obj.player) && (action.target.player))
if (spell.spellType == 'buff') {
if (this.obj.aggro.faction != action.target.aggro.faction)
return;
} else if ((action.target.aggro) && (!this.obj.aggro.canAttack(action.target))) {
if (this.obj.player)
this.sendAnnouncement("You don't feel like attacking that target");
return;
}
}

@@ -342,7 +347,11 @@ define([

if (success) {
this.obj.stats.values.mana -= spell.manaCost;
spell.cd = spell.cdMax;
var cd = {
cd: spell.cdMax
};
this.obj.fireEvent('beforeSetSpellCooldown', cd);
spell.cd = cd.cd;

if (this.obj.player) {
var syncer = this.obj.syncer;
@@ -377,9 +386,9 @@ define([
if ((spell.range > furthest) && (spell.canCast()))
furthest = spell.range;
}
if (furthest == 0)
if (furthest == 0)
furthest = this.furthestRange;
return furthest;
}
},
@@ -495,6 +504,26 @@ define([
});
},

fireEvent: function(event, args) {
var spells = this.spells;
var sLen = spells.length;
for (var i = 0; i < sLen; i++) {
var s = spells[i];

var events = s.events;
if (events) {
var callback = events[event];
if (!callback)
continue;

callback.apply(s, args);
}

if (s.castEvent == event)
s.cast();
}
},

events: {
beforeRezone: function() {
var callbacks = this.callbacks;


+ 39
- 12
src/server/components/stats.js View File

@@ -1,7 +1,7 @@
define([
'config/animations'
], function(
animations
) {
return {
type: 'stats',
@@ -22,7 +22,9 @@ define([
regenHp: 0,
regenMana: 10,
addCritChance: 0,
addCritMultiplier: 0,
critChance: 5,
critMultiplier: 150,
armor: 0,
dmgPercent: 0,

@@ -77,7 +79,7 @@ define([
},

update: function() {
if ((this.obj.mob) || (this.dead))
if (((this.obj.mob) && (!this.obj.follower)) || (this.dead))
return;

var regen = {
@@ -89,6 +91,11 @@ define([

var values = this.values;
var isInCombat = (this.obj.aggro.list.length > 0);
if (this.obj.follower) {
isInCombat = (this.obj.follower.master.aggro.list.length > 0);
if (isInCombat)
return;
}

var regenHp = 0;
var regenMana = 0;
@@ -137,6 +144,9 @@ define([
if (stat == 'addCritChance') {
this.values.critChance += (0.05 * value);
this.obj.syncer.setObject(true, 'stats', 'values', 'critChance', this.values.critChance);
} else if (stat == 'addCritMultiplier') {
this.values.critMultiplier += value;
this.obj.syncer.setObject(true, 'stats', 'values', 'critMultiplier', this.values.critMultiplier);
} else if (stat == 'vit') {
this.values.hpMax += (value * this.vitScale);
this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', this.values.hpMax);
@@ -237,7 +247,7 @@ define([
mult = (1 + (partySize * 0.1));
}

if (a.obj.stats) {
if ((a.obj.stats) && (!a.obj.follower)) {
//Scale xp by source level so you can't just farm low level mobs (or get boosted on high level mobs).
//Mobs that are farther then 10 levels from you, give no xp
//We don't currently do this for quests/herb gathering
@@ -296,12 +306,16 @@ define([
amount = this.values.hp;

this.values.hp -= amount;

var recipients = [];
if (this.obj.serverId != null)
recipients.push(this.obj.serverId);
if (source.serverId != null)
recipients.push(source.serverId);
if ((source.follower) && (source.follower.master.serverId))
recipients.push(source.follower.master.serverId);
if ((this.obj.follower) && (this.obj.follower.master.serverId))
recipients.push(this.obj.follower.master.serverId);

if (recipients.length > 0) {
this.syncer.queue('onGetDamage', {
id: this.obj.id,
@@ -325,11 +339,12 @@ define([
var deathEvent = {};

var killSource = source;

if (source.follower)
killSource = source.follower.master;

if (source.player)
source.stats.kill(this.obj);
if (killSource.player)
killSource.stats.kill(this.obj);
else
this.obj.fireEvent('afterDeath', deathEvent);

@@ -339,18 +354,27 @@ define([
this.obj.auth.permadie();

this.syncer.queue('onPermadeath', {
source: source.name
source: killSource.name
}, [this.obj.serverId]);
} else
this.values.hp = 0;

this.obj.player.die(source, deathEvent.permadeath);
this.obj.player.die(killSource, deathEvent.permadeath);
} else {
this.obj.effects.die();
if (this.obj.spellbook)
this.obj.spellbook.die();
this.obj.destroyed = true;

var deathAnimation = _.getDeepProperty(animations, ['mobs', this.obj.sheetName, this.obj.cell, 'death']);
if (deathAnimation) {
this.obj.instance.syncer.queue('onGetObject', {
x: this.obj.x,
y: this.obj.y,
components: [deathAnimation]
});
}

if (this.obj.inventory) {
var aggroList = this.obj.aggro.list;
var aLen = aggroList.length;
@@ -365,14 +389,14 @@ define([
if (done.some(d => d == p))
return;

this.obj.inventory.dropBag(p, source);
this.obj.inventory.dropBag(p, killSource);
done.push(p);
}, this);
} else {
if (a.serverId == null)
continue;

this.obj.inventory.dropBag(a.serverId, source);
this.obj.inventory.dropBag(a.serverId, killSource);
done.push(a.serverId);
}
}
@@ -388,13 +412,16 @@ define([
},

getHp: function(heal, source) {
var amount = heal.amount;
if (amount == 0)
return;

var values = this.values;
var hpMax = values.hpMax;

if (values.hp >= hpMax)
return;

var amount = heal.amount;
if (hpMax - values.hp < amount)
amount = hpMax - values.hp;



+ 7
- 3
src/server/config/animations.js View File

@@ -1,9 +1,9 @@
define([
'misc/events'
], function(
events
) {
return {
var animations = {
classes: {
wizard: {
'2': {
@@ -312,4 +312,8 @@ define([
}
}
};

events.emit('onBeforeGetAnimations', animations);

return animations;
});

+ 19
- 1
src/server/config/classes.js View File

@@ -4,8 +4,26 @@ define([
events
) {
var classes = {
portraits: {
warrior: {
x: 0,
y: 0
},
cleric: {
x: 1,
y: 0
},
wizard: {
x: 2,
y: 0
},
thief: {
x: 3,
y: 0
}
},
spells: {
wizard: ['ice spear'],
wizard: ['ice spear', 'fireblast'],
cleric: ['healing circle'],
warrior: ['charge'],
thief: ['smokebomb']


+ 15
- 0
src/server/config/effects/effectFrenzy.js View File

@@ -0,0 +1,15 @@
define([
], function(
) {
return {
type: 'frenzy',

events: {
beforeSetSpellCooldown: function(msg, spell) {
msg.cd = 0;
}
}
};
});

+ 99
- 0
src/server/config/factions/akarei.js View File

@@ -0,0 +1,99 @@
define([
'world/spawners',
'world/mobBuilder',
'combat/combat'
], function(
spawners,
mobBuilder,
combat
) {
return {
id: 'akarei',
name: 'The Akarei',
description: `The last descendents of the ancient Akarei.`,

uniqueStat: {
damage: 1,

chance: {
min: 20,
max: 45
},

generate: function(item) {
var chance = this.chance;
var chanceRoll = ~~(random.norm(chance.min, chance.max) * 10) / 10;

var result = null;
if (item.effects)
result = item.effects.find(e => (e.factionId == 'akarei'));

if (!result) {
if (!item.effects)
item.effects = [];

result = {
factionId: 'akarei',
chance: chanceRoll,
text: chanceRoll + '% chance on to cast a lightning bolt when you critically hit an enemy',
events: {}
};

item.effects.push(result);
}

if (!result.events)
result.events = {};

for (var e in this.events) {
result.events[e] = this.events[e];
}

return result;
},

events: {
beforeDealDamage: function(item, damage, target) {
if (!damage.crit)
return;

/*var effect = item.effects.find(e => (e.factionId == 'akarei'));

var roll = Math.random() * 100;
if (roll >= effect.chance)
return;*/

var cbExplode = function(target) {
if ((this.destroyed) || (target.destroyed))
return;

var damage = combat.getDamage({
source: this,
target: target,
damage: 1,
element: 'arcane',
noCrit: true
});

target.stats.takeDamage(damage, 1, this);
};

this.instance.syncer.queue('onGetObject', {
id: this.id,
components: [{
type: 'lightningEffect',
toX: target.x,
toY: target.y
}]
});

this.spellbook.registerCallback(this.id, cbExplode.bind(this, target), 1);
}
}
},

rewards: {

}
};
});

+ 3
- 1
src/server/config/factions/gaekatla.js View File

@@ -71,7 +71,9 @@ define([
sheetName: 'mobs',
name: 'Squiggle',
properties: {
cpnFollower: {}
cpnFollower: {
lifetime: 100
}
},
extraProperties: {
follower: {


+ 27
- 0
src/server/config/maps/cave/chats.js View File

@@ -0,0 +1,27 @@
module.exports = {
'cultist biorn': [{
msg: 'That pool sure looks cold'
}, {
msg: '*sighs*'
}, {
msg: 'Come on, think!'
}, {
msg: 'How will we ever get across?'
}],
'cultist veleif': [{
msg: `How about we try and jump?`
}, {
msg: '*scratches his head*'
}, {
msg: 'Thorald will be expecting us soon'
}, {
msg: 'This is useless'
}],
'cultist': [{
msg: '*pockets a small crystal*'
}, {
msg: '*sighs*'
}, {
msg: '*stares into a crystal*'
}]
};

+ 236
- 0
src/server/config/maps/cave/dialogues.js View File

@@ -0,0 +1,236 @@
module.exports = {
'thaumaturge yala': {
'1': {
msg: [{
msg: `Yes?`,
options: [1.1, 1.2, 1.3, 1.4, 1.5, 1.6]
}],
options: {
'1.1': {
msg: `Who are you?`,
goto: '2'
},
'1.2': {
msg: `Where did you come from?`,
goto: '5'
},
'1.3': {
msg: `Do you have any items for sale?`,
goto: 'tradeBuy'
},
'1.4': {
msg: `I have some items to sell.`,
goto: 'tradeSell'
},
'1.5': {
msg: `I want to buy something back.`,
goto: 'tradeBuyback'
},
'1.6': {
msg: `I have some crystals for you.`,
prereq: function(obj) {
var crystals = obj.inventory.items.find(i => (i.name == 'Digested Crystal'));
return !!crystals;
},
goto: 'giveCrystals'
}
}
},
'2': {
msg: [{
msg: `I am Thaumaturge Yala, thirty-fourth in line to the throne of the Akarei.`,
options: [2.1]
}],
options: {
'2.1': {
msg: `Who are the Akarei?`,
goto: '3'
}
}
},
'3': {
msg: [{
msg: `The Akarei are both the first, and the last wielders of true magic.`,
options: [3.1]
}],
options: {
'3.1': {
msg: `True magic?`,
goto: '4'
}
}
},
'4': {
msg: [{
msg: `Others toil away in an effort to bend magic to their wills; tainting it. We, the Akarei, perform only the purest of incantations.`,
options: [4.1]
}],
options: {
'4.1': {
msg: `I would like to ask something else.`,
goto: '1'
}
}
},
'5': {
msg: [{
msg: `We hail from the city Iskar; our home and haven.`,
options: [5.1, 5.2]
}],
options: {
'5.1': {
msg: `How did you get here?`,
goto: '6'
},
'5.2': {
msg: `What are you doing here?`,
goto: '10'
}
}
},
'6': {
msg: [{
msg: `Through a portal, for Iskar lies in another dimension. But now, we are trapped here. The portal has been closed from the other side and we lack the power to repopen it from here.`,
options: [6.1]
}],
options: {
'6.1': {
msg: `Why would the portal have been closed?`,
goto: '7'
}
}
},
'7': {
msg: [{
msg: `I can not say for certain, but the Akarei have never been wanting for enemies.`,
options: [7.1]
}],
options: {
'7.1': {
msg: `A foe?`,
goto: '8'
}
}
},
'8': {
msg: [{
msg: `A darkness. A nameless, faceless enemy that we can neither identify, nor combat. It has been chipping away at our defenses...almost consuming them. I fear if we do not return soon, it will be too late.`,
options: [8.1]
}],
options: {
'8.1': {
msg: `How can I help?`,
goto: '9'
}
}
},
'9': {
msg: [{
msg: `Slay the snails, gather crystals and bring them to me. The Akarei may reward those who assist them.`,
options: [9.1]
}],
options: {
'9.1': {
msg: `I would like to ask something else.`,
goto: '1'
}
}
},
'10': {
msg: [{
msg: `We were sent here by the Iskar Council, to seek a new form of energy.`,
options: [10.1, 10.2]
}],
options: {
'10.1': {
msg: `How will the energy be used?`,
goto: '11'
},
'10.2': {
msg: `And, did you find it?`,
goto: '12'
}
}
},
'11': {
msg: [{
msg: `Iskar is protected by a magic dome, created by the Founders; a bulwark of pure energy that has stood the test of all but our newest foe.`,
options: [7.1]
}],
options: {
}
},
'12': {
msg: [{
msg: `In a manner. The crystals in this cave are highly energized but in their natural form, they are too unstable for use.`,
options: [12.1]
}],
options: {
'12.1': {
msg: `So, your mission failed?`,
goto: '13'
}
}
},
'13': {
msg: [{
msg: `Not so. Curiously, the snails that inhabit this cave seem to have quite a taste for the crystals' unstable energy and once passed through their digestive tracts, it is expelled in a stable, usable form.`,
options: [13.1]
}],
options: {
'13.1': {
msg: `Well...that's disgusting.`,
goto: '14'
}
}
},
'14': {
msg: [{
msg: `Perhaps. But it may prove to be our salvation. For now we have double need of the energy. Our portal was closed from the other side and we don't have sufficient power between us to repopen it from here.`,
options: [14.1, 6.1]
}],
options: {
'14.1': {
msg: `How can I help?`,
goto: '9'
}
}
},
tradeBuy: {
cpn: 'trade',
method: 'startBuy',
args: [{
targetName: 'cult leader'
}]
},
tradeSell: {
cpn: 'trade',
method: 'startSell',
args: [{
targetName: 'cult leader'
}]
},
tradeBuyback: {
cpn: 'trade',
method: 'startBuyback',
args: [{
targetName: 'cult leader'
}]
},
giveCrystals: {
msg: [{
msg: `The Akarei thank you.`,
options: [1.1, 1.2, 1.3, 1.4, 1.5]
}],
method: function(obj) {
var inventory = obj.inventory;

var crystals = inventory.items.find(i => (i.name == 'Digested Crystal'));
obj.reputation.getReputation('akarei', crystals.quantity * 15);

inventory.destroyItem(crystals.id);
}
}
}
};

+ 2807
- 0
src/server/config/maps/cave/map.json
File diff suppressed because it is too large
View File


+ 9
- 0
src/server/config/maps/cave/quests.js View File

@@ -0,0 +1,9 @@
define([

], function(

) {
return {
infini: []
};
});

+ 668
- 0
src/server/config/maps/cave/zone.js View File

@@ -0,0 +1,668 @@
module.exports = {
name: 'cave',
level: 20,
addLevel: 0,
resources: {},
mobs: {
default: {
regular: {
drops: {
chance: 35,
rolls: 1
}
}
},

'crystal snail': {
level: 14,

regular: {
drops: {
chance: 30,
rolls: 1,
noRandom: true,
alsoRandom: true,
blueprints: [{
name: 'Digested Crystal',
quality: 0,
quest: true,
sprite: [1, 1]
}]
}
},

spells: [{
type: 'melee'
}, {
type: 'smokeBomb',
radius: 0,
repeat: 5,
duration: 7,
randomPos: true,
range: 2,
selfCast: 0.2,
statMult: 1,
damage: 0.225,
element: 'arcane',
cdMax: 5,
particles: {
scale: {
start: {
min: 10,
max: 25
},
end: {
min: 10,
max: 0
}
},
opacity: {
start: 0.3,
end: 0
},
lifetime: {
min: 1,
max: 2
},
speed: {
start: 3,
end: 0
},
color: {
start: ['fc66f7', 'a24eff'],
end: ['933159', '393268']
},
chance: 0.125,
randomColor: true,
randomScale: true,
blendMode: 'add',
spawnType: 'rect',
spawnRect: {
x: -10,
y: -10,
w: 20,
h: 20
}
}
}]
},

'crystal whelk': {
level: 16,
spells: [{
type: 'melee'
}],

regular: {
drops: {
chance: 30,
rolls: 1,
noRandom: true,
alsoRandom: true,
blueprints: [{
name: 'Digested Crystal',
quality: 0,
quest: true,
sprite: [1, 1]
}]
}
},

spells: [{
type: 'melee'
}, {
type: 'smokeBomb',
radius: 0,
repeat: 5,
duration: 7,
randomPos: true,
range: 2,
selfCast: 0.2,
statMult: 1,
damage: 0.2,
element: 'arcane',
cdMax: 5,
particles: {
scale: {
start: {
min: 4,
max: 14
},
end: {
min: 2,
max: 8
}
},
opacity: {
start: 0.2,
end: 0
},
lifetime: {
min: 1,
max: 2
},
speed: {
start: 2,
end: 0
},
color: {
start: ['ff6942', 'ffeb38'],
end: ['953f36', '9a5a3c']
},
chance: 0.125,
randomColor: true,
randomScale: true,
blendMode: 'add',
spawnType: 'rect',
spawnRect: {
x: -10,
y: -10,
w: 20,
h: 20
}
}
}]
},

'radulos': {
level: 18,

regular: {
hpMult: 75,
dmgMult: 2,

drops: {
chance: 100,
rolls: 5,
magicFind: [2000, 200]
}
},
rare: {
count: 0
},

mobile: false,
spells: [{
type: 'projectile',
particles: {
scale: {
start: {
min: 6,
max: 18
},
end: {
min: 2,
max: 8
}
},
color: {
start: ['fc66f7', 'a24eff'],
end: ['393268', '933159']
},
chance: 0.65,
randomScale: true,
randomColor: true,
}
}, {
type: 'smokeBomb',
radius: 1,
repeat: 4,
duration: 14,
randomPos: true,
range: 6,
selfCast: 0.25,
statMult: 1,
damage: 0.15,
element: 'arcane',
cdMax: 8,
particles: {
scale: {
start: {
min: 6,
max: 18
},
end: {
min: 4,
max: 10
}
},
opacity: {
start: 0.01,
end: 0
},
lifetime: {
min: 1,
max: 3
},
speed: {
start: 2,
end: 0
},
color: {
start: ['ff4252', 'd43346'],
end: ['802343', 'a82841']
},
chance: 0.125,
randomColor: true,
randomScale: true,
blendMode: 'add',
spawnType: 'rect',
spawnRect: {
x: -10,
y: -10,
w: 20,
h: 20
}
}
}, {
type: 'summonConsumableFollower'
}]
},

'akarei scout': {
level: 20,
faction: 'akarei',
deathRep: -3
},
'biorn': {
level: 22,
walkDistance: 0,
faction: 'akarei',
deathRep: -3
},
'veleif': {
level: 22,
walkDistance: 0,
faction: 'akarei',
deathRep: -3
},

'akarei artificer': {
level: 24,
faction: 'akarei',
deathRep: -6
},
'thaumaturge yala': {
level: 30,
walkDistance: 0,

deathRep: -15,

regular: {
hpMult: 100,
dmgMult: 2
},

rare: {
count: 0
},

properties: {
cpnTrade: {
items: {
min: 5,
max: 10,
extra: []
},
faction: {
id: 'akarei',
tier: 5
},
markup: {
buy: 0.25,
sell: 10
}
}
}
}
},
objects: {
redwall: {
components: {
cpnBlocker: {
init: function() {
this.obj.instance.physics.setCollision(this.obj.x, this.obj.y, true);
}
}
}
},
bigportal: {
components: {
cpnAttackAnimation: {
simplify: function() {
return {
type: 'attackAnimation',
spriteSheet: 'animBigObjects',
row: 1,
col: 0,
frames: 6,
frameDelay: 7,
loop: -1,
hideSprite: true
};
}
}
}
},
pinktile: {
components: {
cpnParticles: {
simplify: function() {
return {
type: 'particles',
blueprint: {
color: {
start: ['fc66f7', 'a24eff'],
end: ['933159', '393268']
},
scale: {
start: {
min: 2,
max: 10
},
end: {
min: 0,
max: 2
}
},
speed: {
start: {
min: 4,
max: 16
},
end: {
min: 2,
max: 8
}
},
lifetime: {
min: 1,
max: 4
},
randomScale: true,
randomSpeed: true,
chance: 0.04,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -20,
y: -20,
w: 60,
h: 60
}
}
}
}
}
}
},
walltrigger: {
components: {
cpnParticles: {
simplify: function() {
return {
type: 'particles',
blueprint: {
color: {
start: ['fff4252', 'ff6942'],
end: ['802343', 'f953f36']
},
scale: {
start: {
min: 2,
max: 6
},
end: {
min: 0,
max: 2
}
},
speed: {
start: {
min: 0,
max: 4
},
end: {
min: 0,
max: 0
}
},
lifetime: {
min: 1,
max: 2
},
randomScale: true,
randomSpeed: true,
chance: 0.2,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -20,
y: -20,
w: 40,
h: 40
}
}
}
}
},
cpnTrigger: {
init: function() {
this.obj.instance.triggerPuzzle = {
activated: []
};
},
collisionEnter: function(o) {
if (!o.player)
return;

var order = this.obj.order;
var triggerPuzzle = this.obj.instance.triggerPuzzle;
var activated = triggerPuzzle.activated;

if (this.obj.forceOpen) {
triggerPuzzle.activated = [];
this.activate();
return;
}

activated.push(order);
var valid = true;
for (var i = 0; i < activated.length; i++) {
if (activated[i] != i) {
valid = false;
break;
}
}

if (!valid) {
triggerPuzzle.activated = [];

process.send({
method: 'events',
data: {
'onGetAnnouncement': [{
obj: {
msg: 'nothing happens'
},
to: [o.serverId]
}]
}
});

return;
} else if (activated.length == 4) {
triggerPuzzle.activated = [];
this.activate();
}

process.send({
method: 'events',
data: {
'onGetAnnouncement': [{
obj: {
msg: this.obj.message
},
to: [o.serverId]
}]
}
});
},
activate: function() {
var syncer = this.obj.instance.syncer;
var physics = this.obj.instance.physics;
var walls = this.obj.instance.objects.objects.filter(o => (o.objZoneName == 'redWall'));
walls.forEach(function(w) {
w.destroyed = true;
physics.setCollision(w.x, w.y, false);

syncer.queue('onGetObject', {
x: w.x,
y: w.y,
components: [{
type: 'attackAnimation',
row: 0,
col: 4
}]
});
});
}
}
}
},
gas: {
components: {
cpnParticles: {
simplify: function() {
return {
type: 'particles',
blueprint: {
color: {
start: ['c0c3cf', '929398'],
end: ['69696e', '69696e']
},
scale: {
start: {
min: 32,
max: 18
},
end: {
min: 16,
max: 8
}
},
speed: {
start: {
min: 2,
max: 6
},
end: {
min: 0,
max: 4
}
},
lifetime: {
min: 4,
max: 16
},
alpha: {
start: 0.2,
end: 0
},
randomScale: true,
randomSpeed: true,
chance: 0.02,
randomColor: true,
spawnType: 'rect',
blendMode: 'screen',
spawnRect: {
x: -80,
y: -80,
w: 160,
h: 160
}
}
}
}
}
}
},
bubbles: {
components: {
cpnParticles: {
simplify: function() {
return {
type: 'particles',
blueprint: {
color: {
start: ['48edff', '3fa7dd'],
end: ['69696e', '42548d']
},
scale: {
start: {
min: 2,
max: 8
},
end: {
min: 2,
max: 4
}
},
speed: {
start: {
min: 2,
max: 6
},
end: {
min: 0,
max: 4
}
},
lifetime: {
min: 1,
max: 3
},
alpha: {
start: 0.5,
end: 0
},
randomScale: true,
randomSpeed: true,
chance: 0.2,
randomColor: true,
spawnType: 'rect',
blendMode: 'screen',
spawnRect: {
x: -20,
y: -20,
w: 60,
h: 60
}
}
}
}
}
}
},

shopyala: {
properties: {
cpnNotice: {
actions: {
enter: {
cpn: 'dialogue',
method: 'talk',
args: [{
targetName: 'thaumaturge yala'
}]
},
exit: {
cpn: 'dialogue',
method: 'stopTalk'
}
}
}
}
}
}
};

+ 4
- 3
src/server/config/maps/estuary/zone.js View File

@@ -11,14 +11,15 @@ module.exports = {
},

regular: {
dmgMult: 8,
hpMult: 1.5,
dmgMult: 2,

drops: {
chance: 45,
rolls: 1,
magicFind: 70
magicFind: 500
}
}
}
},
'giant gull': {
level: 6,


+ 4
- 3
src/server/config/maps/mapList.js View File

@@ -4,9 +4,10 @@ define([

) {
return [
'tutorial',
'estuary',
'cave',
'city',
'sewer'
'estuary',
'sewer',
'tutorial'
];
});

+ 2
- 2
src/server/config/maps/sewer/zone.js View File

@@ -13,7 +13,7 @@ module.exports = {
grantRep: {
fjolgard: 4
},
level: 8,
level: 10,

rare: {
count: 0
@@ -25,7 +25,7 @@ module.exports = {
grantRep: {
fjolgard: 10
},
level: 11,
level: 12,

rare: {
count: 0


+ 0
- 430
src/server/config/maps/tutorial-cove/map.json
File diff suppressed because it is too large
View File


+ 0
- 27
src/server/config/maps/tutorial-cove/zone.js View File

@@ -1,27 +0,0 @@
module.exports = {
mobs: {
seagull: {
level: 1,

rare: {
count: 0
},

regular: {
drops: {
chance: 100,
rolls: 1,
noRandom: true,
blueprints: [{
name: 'Family Heirloom',
quality: 2,
slot: 'neck',
type: 'Pendant',
noSalvage: true,
stats: ['hpMax', 'regenHp', 'regenMana']
}]
}
}
}
}
};

+ 257
- 295
src/server/config/maps/tutorial/map.json
File diff suppressed because it is too large
View File


+ 3
- 3
src/server/config/maps/tutorial/zone.js View File

@@ -153,7 +153,7 @@ module.exports = {
}
},
elk: {
level: 4,
level: 5,
regular: {
drops: {
chance: 55,
@@ -171,7 +171,7 @@ module.exports = {
crab: {
faction: 'gaekatla',
deathRep: -3,
level: 5,
level: 7,

regular: {
drops: {
@@ -190,7 +190,7 @@ module.exports = {
'titan crab': {
faction: 'gaekatla',
deathRep: -5,
level: 6,
level: 8,
rare: {
name: 'The Pincer King'
}


+ 8
- 1
src/server/config/roles.js View File

@@ -5,7 +5,7 @@ define([
) {
return {
accounts: {
admin: {
waffle: {
level: 10,
messageStyle: 'color-cyan',
messagePrefix: '(dev) ',
@@ -45,6 +45,13 @@ define([
}
},

getRoleLevel: function(player) {
var account = player.account;
var level = this.accounts[account] ? this.accounts[account].level : 0;

return level;
},

isRoleLevel: function(player, requireLevel, message) {
var account = player.account;
var level = this.accounts[account] ? this.accounts[account].level : 0;


+ 10
- 0
src/server/config/serverConfig.js View File

@@ -0,0 +1,10 @@
define([

], function(

) {
return {
port: 4000,
startupMessage: 'Server: ready'
}
});

+ 4
- 48
src/server/config/spells.js View File

@@ -290,7 +290,7 @@ define([
description: 'Jagged Crystals break ground at your target destination',
type: 'warnBlast',
animation: 'raiseHands',
icon: [4, 0],
icon: [0, 7],
particles: {
color: {
start: ['c0c3cf', '929398'],
@@ -332,55 +332,11 @@ define([
randomColor: true
}
}, {
name: 'Arcane Barrier',
name: 'Chain Lightning',
description: 'Creates a circle of pure holy energy that heals allies for a brief period.',
type: 'arcaneBarrier',
type: 'chainLightning',
icon: [0, 1],
animation: 'raiseStaff',
particles: {
scale: {
start: {
min: 6,
max: 16
},
end: {
min: 0,
max: 4
}
},
speed: {
start: {
min: 2,
max: 12
},
end: {
min: 0,
max: 4
}
},
lifetime: {
min: 1,
max: 3
},
alpha: {
start: 0.45,
end: 0
},
color: {
start: ['ffeb38', 'fcfcfc'],
end: ['fcfcfc', 'faac45']
},
spawnType: 'circle',
spawnCircle: {
x: 0,
y: 0,
r: 12
},
randomScale: true,
randomColor: true,
randomSpeed: true,
chance: 0.02
}
animation: 'raiseStaff'
}];

events.emit('onBeforeGetSpellsInfo', spells);


+ 1
- 1
src/server/config/spells/spellArcaneBarrier.js View File

@@ -21,7 +21,7 @@ define([
var isPlayer = !!this.caster.player;
var isTargetPlayer = !!o.player;

if ((this.caster.aggro.willAttack(o)) || (isPlayer != isTargetPlayer))
if ((this.caster.aggro.canAttack(o)) || (isPlayer != isTargetPlayer))
return;

this.contents.push(o);


+ 43
- 0
src/server/config/spells/spellChainLightning.js View File

@@ -0,0 +1,43 @@

define([

], function(

) {
return {
type: 'chainLightning',
cdMax: 5,
manaCost: 0,
range: 9,
needLos: true,

damage: 1,

cast: function(action) {
var target = action.target;

this.sendBump(target);

this.obj.instance.syncer.queue('onGetObject', {
id: this.obj.id,
components: [{
type: 'lightningEffect',
toX: target.x,
toY: target.y
}]
});

this.queueCallback(this.explode.bind(this, target), 1);

return true;
},
explode: function(target) {
if ((this.obj.destroyed) || (target.destroyed))
return;
var damage = this.getDamage(target);
target.stats.takeDamage(damage, this.threatMult, this.obj);
}
};
});

+ 1
- 1
src/server/config/spells/spellFireblast.js View File

@@ -56,7 +56,7 @@ define([
var isPlayer = !!this.obj.player;
var isTargetPlayer = !!m.player;

if ((!this.obj.aggro.willAttack(m)) && (isPlayer == isTargetPlayer))
if ((!this.obj.aggro.canAttack(m)) && (isPlayer == isTargetPlayer))
continue;

var targetEffect = m.effects.addEffect({


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save