Browse Source

merged master for v0.9

tags/v0.9.0
Shaun 3 years ago
parent
commit
0a84d52766
100 changed files with 1290 additions and 6464 deletions
  1. +0
    -1
      .gitignore
  2. +1
    -1
      Dockerfile
  3. +0
    -0
     
  4. +1
    -0
      src/client/css/loader.css
  5. +1
    -0
      src/client/css/main.css
  6. +1
    -1
      src/client/js/app.js
  7. +3
    -1
      src/client/js/components/attackAnimation.js
  8. +2
    -2
      src/client/js/components/components.js
  9. +3
    -1
      src/client/js/components/effects.js
  10. +3
    -0
      src/client/js/components/explosion.js
  11. +2
    -0
      src/client/js/components/light.js
  12. +2
    -0
      src/client/js/components/lightPatch.js
  13. +2
    -0
      src/client/js/components/particles.js
  14. +1
    -0
      src/client/js/components/pather.js
  15. +3
    -2
      src/client/js/components/player.js
  16. +1
    -1
      src/client/js/components/spellbook.js
  17. +27
    -0
      src/client/js/objects/objBase.js
  18. +42
    -96
      src/client/js/objects/objects.js
  19. +1
    -1
      src/client/js/rendering/numbers.js
  20. +11
    -4
      src/client/js/rendering/renderer.js
  21. +1
    -5
      src/client/js/rendering/tileOpacity.js
  22. +23
    -16
      src/client/js/sound/sound.js
  23. +1
    -1
      src/client/package.json
  24. +1
    -0
      src/client/ui/templates/announcements/styles.css
  25. +1
    -0
      src/client/ui/templates/buffs/styles.css
  26. +1
    -0
      src/client/ui/templates/characters/styles.css
  27. +1
    -0
      src/client/ui/templates/context/styles.css
  28. +1
    -0
      src/client/ui/templates/createCharacter/styles.css
  29. +1
    -0
      src/client/ui/templates/death/styles.css
  30. +1
    -0
      src/client/ui/templates/dialogue/styles.css
  31. +1
    -0
      src/client/ui/templates/equipment/styles.css
  32. +1
    -0
      src/client/ui/templates/events/styles.css
  33. +1
    -0
      src/client/ui/templates/help/styles.css
  34. +1
    -0
      src/client/ui/templates/hud/styles.css
  35. +3
    -0
      src/client/ui/templates/inventory/inventory.js
  36. +1
    -0
      src/client/ui/templates/inventory/styles.css
  37. +1
    -0
      src/client/ui/templates/leaderboard/styles.css
  38. +1
    -0
      src/client/ui/templates/login/styles.css
  39. +2
    -2
      src/client/ui/templates/login/template.html
  40. +1
    -0
      src/client/ui/templates/mainMenu/styles.css
  41. +1
    -0
      src/client/ui/templates/menu/styles.css
  42. +1
    -0
      src/client/ui/templates/messages/styles.css
  43. +1
    -0
      src/client/ui/templates/middleHud/styles.css
  44. +1
    -0
      src/client/ui/templates/online/styles.css
  45. +1
    -0
      src/client/ui/templates/options/styles.css
  46. +1
    -0
      src/client/ui/templates/overlay/styles.css
  47. +1
    -0
      src/client/ui/templates/party/styles.css
  48. +1
    -1
      src/client/ui/templates/party/styles.less
  49. +1
    -0
      src/client/ui/templates/passives/styles.css
  50. +1
    -0
      src/client/ui/templates/progressBar/styles.css
  51. +1
    -0
      src/client/ui/templates/quests/styles.css
  52. +1
    -0
      src/client/ui/templates/reputation/styles.css
  53. +1
    -0
      src/client/ui/templates/spells/styles.css
  54. +1
    -0
      src/client/ui/templates/stash/styles.css
  55. +1
    -0
      src/client/ui/templates/talk/styles.css
  56. +1
    -0
      src/client/ui/templates/target/styles.css
  57. +1
    -0
      src/client/ui/templates/terms/styles.css
  58. +1
    -0
      src/client/ui/templates/tooltipInfo/styles.css
  59. +1
    -0
      src/client/ui/templates/tooltipItem/styles.css
  60. +1
    -0
      src/client/ui/templates/tooltips/styles.css
  61. +1
    -0
      src/client/ui/templates/trade/styles.css
  62. +1
    -0
      src/client/ui/templates/wardrobe/styles.css
  63. +1
    -0
      src/client/ui/templates/workbench/styles.css
  64. +36
    -0
      src/server/combat/avoid.js
  65. +36
    -88
      src/server/combat/combat.js
  66. +56
    -0
      src/server/combat/mitigate.js
  67. +82
    -0
      src/server/combat/scale.js
  68. +6
    -0
      src/server/components/door.js
  69. +97
    -33
      src/server/components/mob.js
  70. +6
    -0
      src/server/components/player.js
  71. +10
    -24
      src/server/components/portal.js
  72. +46
    -0
      src/server/components/portal/sendObjToZone.js
  73. +4
    -37
      src/server/components/stats.js
  74. +62
    -0
      src/server/components/stats/die.js
  75. +36
    -4
      src/server/config/clientConfig.js
  76. +14
    -0
      src/server/config/effects/effectInvulnerability.js
  77. +15
    -7
      src/server/config/maps/cave/map.json
  78. +0
    -23
      src/server/config/maps/fjolarok/chats.js
  79. +0
    -163
      src/server/config/maps/fjolarok/dialogues.js
  80. +383
    -1736
      src/server/config/maps/fjolarok/map.json
  81. +7
    -693
      src/server/config/maps/fjolarok/zone.js
  82. +0
    -4
      src/server/config/maps/mapList.js
  83. +0
    -291
      src/server/config/maps/sewer/events/plagueOfRats.js
  84. +0
    -2461
      src/server/config/maps/sewer/map.json
  85. +0
    -455
      src/server/config/maps/sewer/zone.js
  86. +6
    -2
      src/server/config/serverConfig.js
  87. +2
    -0
      src/server/config/spells/spellIceSpear.js
  88. +4
    -0
      src/server/config/spells/spellProjectile.js
  89. +3
    -1
      src/server/db/ioRethink.js
  90. +4
    -4
      src/server/index.js
  91. +17
    -8
      src/server/items/generator.js
  92. +1
    -1
      src/server/mods/class-necromancer/index.js
  93. +24
    -20
      src/server/mods/class-necromancer/spells/spellBloodBarrier.js
  94. +26
    -20
      src/server/objects/objBase.js
  95. +26
    -16
      src/server/objects/objects.js
  96. +38
    -114
      src/server/package-lock.json
  97. +2
    -2
      src/server/package.json
  98. +3
    -1
      src/server/security/connections.js
  99. +0
    -120
      src/server/server.js
  100. +63
    -0
      src/server/server/index.js

+ 0
- 1
.gitignore View File

@@ -4,7 +4,6 @@ package-lock.json
storage.db
*.sublime-project
*.sublime-workspace
*.css
src/server/mods/iwd-*
!helpers/item-tooltip/styles.css
!helpers/passives/**/*.css


+ 1
- 1
Dockerfile View File

@@ -11,7 +11,7 @@ COPY . .
WORKDIR /usr/src/isleward/src/server/

# Install only production npm modules specified in package.json
RUN npm install --only=production
RUN npm ci --only=production

# Expose container's port 4000
EXPOSE 4000


+ 0
- 0
View File


+ 1
- 0
src/client/css/loader.css
File diff suppressed because it is too large
View File


+ 1
- 0
src/client/css/main.css View File

@@ -0,0 +1 @@
body,html{position:fixed;overflow:hidden;overscroll-behavior:none}body{margin:0px;width:100vw;height:100vh;background-color:#2d2136}.ui-container{width:100%;height:100%;position:absolute;left:0px;top:0px;z-index:20;overflow:hidden;display:flex;justify-content:center;align-items:center}.ui-container>.right{position:absolute;right:10px;top:94px}.ui-container.mobile .uiTooltipInfo{display:none !important}.ui-container.hideMonetization .monetization{display:none}.mobile.ui-container>.right{top:10px;right:158px;z-index:2;display:flex;flex-direction:row-reverse}*{box-sizing:border-box;font-family:bitty;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.canvas-container{position:relative;float:left}.canvas-container.visible{opacity:1;transition:1s}.canvas-container canvas{left:0px;top:0px;width:100vw}.disabled{opacity:.4 !important;pointer-events:none !important}[class^="ui"] input,[class^="ui"] textarea{-webkit-user-select:text;-khtml-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}[class^="ui"] .el{height:35px;text-align:center;padding:0px 0px 0px 0px;background-color:transparent}[class^="ui"] .textbox{border:none;outline:none;font-size:16px}[class^="ui"] div.textbox{padding-top:6px}[class^="ui"] .btn{cursor:pointer;display:flex;justify-content:center;align-items:center}[class^="ui"] .btn:hover{background-color:#473456}[class^="ui"] .spacer-h{width:100%}[class^="ui"].modal .btnClose{position:absolute;background-color:#505360;color:#ff6942;cursor:pointer;height:31px;width:31px;text-align:center;right:0px;top:0px;padding-top:0px;display:flex;justify-content:center;align-items:center}[class^="ui"].modal .btnClose:hover{background-color:#676b7c}::-webkit-scrollbar{width:16px}::-webkit-scrollbar-track{background-color:#3c3f4c;-webkit-border-radius:0px;border-radius:0px}::-webkit-scrollbar-thumb{-webkit-border-radius:10px;border-radius:0px;background:#929398}:root{scrollbar-color:#929398 #3c3f4c !important;scrollbar-width:thin !important;--color-element-default:#f2f5f5;--color-element-arcane:#fc66f7;--color-element-frost:#48edff;--color-element-fire:#ff4252;--color-element-holy:#ffeb38;--color-element-poison:#51fc9a}.q0{color:#f2f5f5}.q1{color:#4ac441}.q2{color:#3fa7dd}.q3{color:#a24eff}.q4{color:#ff6942}.color-red{color:#d43346 !important}.color-redA{color:#ff4252 !important}.color-blueA{color:#48edff !important}.color-blueB{color:#3fa7dd !important}.color-greenB{color:#4ac441 !important}.color-yellowB{color:#faac45 !important}.color-green{color:#80f643 !important}.color-brownC{color:#b15a30 !important}.color-brownD{color:#763b3b !important}.color-grayA{color:#f2f5f5 !important}.color-grayB{color:#c0c3cf !important}.color-grayC{color:#929398 !important}.color-grayD{color:#69696e !important}.color-pinkA{color:#fc66f7 !important}.color-pinkB{color:#de43ae !important}.color-purpleA{color:#a24eff !important}.color-tealB{color:#51fc9a !important}[class^="ui"] .renderItem{width:72px;height:72px;float:left;position:relative;cursor:pointer;box-sizing:border-box;margin:4px;background-color:#312136}[class^="ui"] .renderItem.hover{background-color:rgba(72,237,255,0.1)}[class^="ui"] .renderItem.dragging{position:absolute;opacity:.5;pointer-events:none;background-color:transparent}[class^="ui"] .renderItem.dragging .icon{filter:brightness(100%) drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136);-moz-filter:brightness(100%) drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136)}[class^="ui"] .renderItem .quantity{left:6px;bottom:3px;position:absolute;color:#f2f5f5;filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136);-moz-filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136)}[class^="ui"] .renderItem .icon{width:64px;height:64px;position:absolute;left:4px;top:4px}[class^="ui"] .renderItem.eq .quantity{color:#ffeb38}[class^="ui"] .renderItem.new .quantity{color:#80f643}[class^="ui"] .renderItem:hover .icon{filter:brightness(160%);-moz-filter:brightness(160%)}.hasBorderShadow{box-shadow:0 -2px 0 #2d2136,0 2px 0 #2d2136,2px 0 0 #2d2136,-2px 0 0 #2d2136}

+ 1
- 1
src/client/js/app.js View File

@@ -3,7 +3,7 @@ require.config({
waitSeconds: 120,
paths: {
socket: 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.slim',
jquery: 'https://code.jquery.com/jquery-3.4.1.slim.min',
jquery: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.slim.min',
text: 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min',
html: 'plugins/html',
css: 'https://cdnjs.cloudflare.com/ajax/libs/require-css/0.1.10/css.min',


+ 3
- 1
src/client/js/components/attackAnimation.js View File

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

if (this.noSprite)
this.obj.sheetName = null;

this.sprite.visible = this.obj.isVisible;
},

renderManual: function () {
@@ -66,7 +68,7 @@ define([
if (this.destroyObject)
this.obj.destroyed = true;
else {
if (this.obj.sprite && this.hideSprite)
if (this.obj.isVisible && this.obj.sprite)
this.obj.sprite.visible = true;

this.destroyed = true;


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

@@ -67,8 +67,8 @@ define([
return;

t.eventList = {};
t.hookEvent = hookEvent.bind(t);
t.unhookEvents = unhookEvents.bind(t);
t.hookEvent = hookEvent;
t.unhookEvents = unhookEvents;

templates[t.type] = t;
});


+ 3
- 1
src/client/js/components/effects.js View File

@@ -113,7 +113,9 @@ define([
});

e.sprite.alpha = useAlpha;
}, this);

e.sprite.visible = this.obj.isVisible;
});
},

setVisible: function (visible) {


+ 3
- 0
src/client/js/components/explosion.js View File

@@ -65,6 +65,9 @@ define([
},

explode: function (blueprint) {
if (!this.obj.isVisible)
return;

let particles = this.obj.addComponent('particles', this.blueprint);

particles.emitter.update(0.2);


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

@@ -76,6 +76,8 @@ define([
});
}
}

this.setVisible(this.obj.isVisible);
},

update: function () {


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

@@ -79,6 +79,8 @@ define([
ray.blendMode = PIXI.BLEND_MODES.ADD;
}
}

this.setVisible(this.obj.isVisible);
},

update: function () {


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

@@ -17,6 +17,8 @@ define([
this.blueprint.obj = this.obj;

this.emitter = renderer.buildEmitter(this.blueprint);

this.setVisible(this.obj.isVisible);
},

setVisible: function (visible) {


+ 1
- 0
src/client/js/components/pather.js View File

@@ -27,6 +27,7 @@ define([
lastY: 0,

init: function () {
events.on('onRespawn', this.onDeath.bind(this));
events.on('onDeath', this.onDeath.bind(this));
events.on('onClearQueue', this.onDeath.bind(this));



+ 3
- 2
src/client/js/components/player.js View File

@@ -68,8 +68,9 @@ define([
}, instant);
},

onRespawn: function (position) {
this.positionCamera(position.x, position.y, true);
onRespawn: function ({ x, y }) {
this.positionCamera(x, y, true);
sound.update(x, y);
},

destroy: function () {


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

@@ -226,7 +226,7 @@ define([
if (!target)
return;

if (this.target.destroyed || this.target.nonSelectable) {
if (this.target.destroyed || this.target.nonSelectable || !this.target.isVisible) {
this.target = null;
this.targetSprite.visible = false;
}


+ 27
- 0
src/client/js/objects/objBase.js View File

@@ -108,6 +108,28 @@ define([
stats.updateHpSprite();
},

updateVisibility: function () {
const { x, y, hidden, isVisible } = this;

const vis = (
!hidden &&
(
this.self ||
(
renderer.sprites[x] &&
renderer.sprites[x][y].length > 0 &&
!renderer.isHidden(x, y)
)
)
);

if (vis === isVisible)
return;

this.isVisible = vis;
this.setVisible(vis);
},

setVisible: function (visible) {
if (this.sprite)
this.sprite.visible = visible;
@@ -115,6 +137,11 @@ define([
if (this.nameSprite)
this.nameSprite.visible = (visible && config.showNames);

if (!visible && this.stats && this.stats.hpSprite && this.stats.hpSprite.visible) {
this.stats.hpSprite.visible = false;
this.stats.hpSpriteInner.visible = false;
}

this.components.forEach(c => {
if (c.setVisible)
c.setVisible(visible);


+ 42
- 96
src/client/js/objects/objects.js View File

@@ -50,7 +50,7 @@ define([
getClosest: function (x, y, maxDistance, reverse, fromMob) {
let objects = this.objects;

let list = objects.filter(function (o) {
let list = objects.filter(o => {
if ((!o.stats) || (o.nonSelectable) || (o === window.player) || (!o.sprite.visible))
return false;

@@ -65,7 +65,7 @@ define([
if (list.length === 0)
return null;

list.sort(function (a, b) {
list.sort((a, b) => {
let aDistance = Math.max(Math.abs(x - a.x), Math.abs(y - a.y));
let bDistance = Math.max(Math.abs(x - b.x), Math.abs(y - b.y));

@@ -77,9 +77,7 @@ define([
if (!fromMob)
return list[0];

let fromIndex = list.findIndex(function (l) {
return (l.id === fromMob.id);
});
let fromIndex = list.findIndex(l => l.id === fromMob.id);

if (reverse)
fromIndex = (fromIndex === 0 ? list.length : fromIndex) - 1;
@@ -136,38 +134,36 @@ define([
obj[p] = value;
}

components.forEach(function (c) {
if (obj.sheetName)
obj.sprite = renderer.buildObject(obj);

if ((obj.name) && (obj.sprite)) {
obj.nameSprite = renderer.buildText({
layerName: 'effects',
text: obj.name,
x: (obj.x * scale) + (scale / 2),
y: (obj.y * scale) + scale
});
}

//We need to set visibility before components kick in as they sometimes need access to isVisible
obj.updateVisibility();

components.forEach(c => {
//Map ids to objects
let keys = Object.keys(c).filter(function (k) {
return ((k.indexOf('id') === 0) && (k.length > 2));
let keys = Object.keys(c).filter(k => {
return (k.indexOf('id') === 0 && k.length > 2);
});
keys.forEach(function (k) {
keys.forEach(k => {
let value = c[k];
let newKey = k.substr(2, k.length).toLowerCase();

c[newKey] = this.objects.find(function (o) {
return (o.id === value);
});
c[newKey] = this.objects.find(o => o.id === value);
delete c[k];
}, this);
});

obj.addComponent(c.type, c);
}, this);

if (obj.sheetName) {
obj.sprite = renderer.buildObject(obj);
if (template.hidden) {
obj.sprite.visible = false;
if (obj.nameSprite)
obj.nameSprite.visible = false;
if ((obj.stats) && (obj.stats.hpSprite)) {
obj.stats.hpSprite.visible = false;
obj.stats.hpSpriteInner.visible = false;
}
}
}

this.objects.push(obj);
});

if (obj.self) {
events.emit('onGetPlayer', obj);
@@ -181,27 +177,7 @@ define([
}, true);
}

if ((obj.name) && (obj.sprite)) {
obj.nameSprite = renderer.buildText({
layerName: 'effects',
text: obj.name,
x: (obj.x * scale) + (scale / 2),
y: (obj.y * scale) + scale
});
}

if (renderer.sprites) {
let isVisible = (
obj.self ||
(
renderer.sprites[obj.x] &&
renderer.sprites[obj.x][obj.y].length > 0 &&
!renderer.isHidden(obj.x, obj.y)
)
);

obj.setVisible(isVisible);
}
this.objects.push(obj);

return obj;
},
@@ -209,28 +185,26 @@ define([
updateObject: function (obj, template) {
let components = template.components || [];

components.forEach(function (c) {
components.forEach(c => {
//Map ids to objects
let keys = Object.keys(c).filter(function (k) {
return ((k.indexOf('id') === 0) && (k.length > 2));
let keys = Object.keys(c).filter(k => {
return (k.indexOf('id') === 0 && k.length > 2);
});
keys.forEach(function (k) {
keys.forEach(k => {
let value = c[k];
let newKey = k.substr(2, k.length).toLowerCase();

c[newKey] = this.objects.find(function (o) {
return (o.id === value);
});
c[newKey] = this.objects.find(o => o.id === value);
delete c[k];
}, this);
});

obj.addComponent(c.type, c);
}, this);
});

delete template.components;

if (template.removeComponents) {
template.removeComponents.forEach(function (r) {
template.removeComponents.forEach(r => {
obj.removeComponent(r);
});
delete template.removeComponents;
@@ -265,22 +239,6 @@ define([

if (((template.sheetName) || (template.cell)) && (sprite))
renderer.setSprite(obj);
if (sprite) {
if (template.hidden !== null) {
sprite.visible = !template.hidden;
if (obj.nameSprite)
obj.nameSprite.visible = config.showNames;
if ((obj.stats) && (obj.stats.hpSprite)) {
obj.stats.hpSprite.visible = !template.hidden;
obj.stats.hpSpriteInner.visible = !template.hidden;
}
}
}

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

if ((!obj.sprite) && (template.sheetName))
obj.sprite = renderer.buildObject(obj);
@@ -292,27 +250,15 @@ define([
x: (obj.x * scale) + (scale / 2),
y: (obj.y * scale) + scale
});
obj.nameSprite.visible = config.showNames;
}

if (obj.sprite) {
let ix = ~~obj.x;
let iy = ~~obj.y;

let isVisible = (
!!obj.player ||
(
!obj.hidden &&
renderer.sprites[ix] &&
renderer.sprites[ix][iy] &&
renderer.sprites[ix][iy].length > 0 &&
!renderer.isHidden(obj.x, obj.y)
)
);
obj.setVisible(isVisible);
}
if ((template.x !== 0) || (template.y !== 0)) {
obj.updateVisibility();
obj.setSpritePosition();

obj.setSpritePosition();
if (obj.stats)
obj.stats.updateHpSprite();
}
},

update: function () {
@@ -340,13 +286,13 @@ define([
for (let i = 0; i < oLen; i++) {
let o = objects[i];

let onPos = tiles.some(function (t) {
let onPos = tiles.some(t => {
return (!(t.x !== o.x || t.y !== o.y));
});
if (!onPos)
continue;

o.setVisible(visible);
o.updateVisibility();
}
},



+ 1
- 1
src/client/js/rendering/numbers.js View File

@@ -36,7 +36,7 @@ define([
let target = objects.objects.find(function (o) {
return (o.id === msg.id);
});
if (!target)
if (!target || !target.isVisible)
return;

let ttl = 35;


+ 11
- 4
src/client/js/rendering/renderer.js View File

@@ -126,7 +126,7 @@ define([
},

buildSpritesTexture: function () {
const { clientConfig: { atlasTextures } } = globals;
const { clientConfig: { atlasTextureDimensions, atlasTextures } } = globals;

let container = new pixi.Container();

@@ -139,7 +139,7 @@ define([
tile.x = 0;
tile.y = totalHeight;

tileOpacity.atlasTextureDimensions[t] = {
atlasTextureDimensions[t] = {
w: texture.width / 8,
h: texture.height / 8
};
@@ -374,7 +374,7 @@ define([
let foundHiddenLayer = null;

hiddenRooms.forEach(h => {
const { discovered, layer } = h;
const { discovered, layer, interior } = h;
const { x: hx, y: hy, width, height, area } = h;

//Is the tile outside the hider
@@ -383,8 +383,15 @@ define([
x >= hx + width ||
y < hy ||
y >= hy + height
)
) {
//If the hider is an interior, the tile should be hidden if the player is inside the hider
if (interior) {
if (physics.isInPolygon(px, py, area))
foundHiddenLayer = layer;
}

return;
}

//Is the tile inside the hider
if (!physics.isInPolygon(x, y, area))


+ 1
- 5
src/client/js/rendering/tileOpacity.js View File

@@ -4,9 +4,6 @@ define([
globals
) {
return {
//Set by renderer
atlasTextureDimensions: {},

getSheetNum: function (tile) {
if (tile < 224)
return 0;
@@ -25,8 +22,7 @@ define([
},

getOffsetAndSheet: function (tile) {
const { clientConfig: { atlasTextures } } = globals;
const { atlasTextureDimensions } = this;
const { clientConfig: { atlasTextureDimensions, atlasTextures } } = globals;

let offset = 0;
let sheetName = null;


+ 23
- 16
src/client/js/sound/sound.js View File

@@ -125,8 +125,6 @@ define([
if (this.currentMusic === soundEntry && sound.volume() === musicVolume / 100)
return;

soundEntry.volume = 1;

this.currentMusic = soundEntry;

sound.fade(sound.volume(), (musicVolume / 100), fadeDuration);
@@ -169,7 +167,7 @@ define([
}

//Exponential fall-off
const volume = s.volume * (1 - (Math.pow(distance, 2) / Math.pow(minDistance, 2)));
const volume = s.maxVolume * (1 - (Math.pow(distance, 2) / Math.pow(minDistance, 2)));
this.playSoundHelper(s, volume);
});
},
@@ -178,23 +176,31 @@ define([
const sounds = this.sounds;

const areaMusic = sounds.filter(s => s.music && s.area);
const currentMusic = areaMusic.find(s => physics.isInPolygon(playerX, playerY, s.area));
const defaultMusic = sounds.find(s => s.music && s.defaultMusic);

if (!currentMusic) {
if (defaultMusic)
this.playMusicHelper(defaultMusic);
//All music that should be playing because we're in the correct polygon
const playMusic = areaMusic.filter(s => physics.isInPolygon(playerX, playerY, s.area));

const activeMusic = sounds.filter(s => s.music && s !== defaultMusic);
activeMusic.forEach(s => this.stopSoundHelper(s));
} else {
if (defaultMusic)
this.stopSoundHelper(defaultMusic);
//All music that should stop playing because we're in the incorrect polygon
const stopMusic = areaMusic.filter(s => s.sound && s.sound.playing() && !playMusic.some(m => m === s));

if (currentMusic)
this.playMusicHelper(currentMusic);
//Stop or start defaultMusic, depending on whether anything else was found
const defaultMusic = sounds.filter(a => a.defaultMusic);
if (defaultMusic) {
if (!playMusic.length)
defaultMusic.forEach(m => this.playMusicHelper(m));
else
defaultMusic.forEach(m => this.stopSoundHelper(m));
}

//If there's a music entry in both 'play' and 'stop' that shares a fileName, we'll just ignore it. This happens when you
// move to a building interior, for example. Unfortunately, we can't have different volume settings for these kinds of entries.
// The one that starts playing first will get priority
const filesPlaying = [...playMusic.map(p => p.file), ...stopMusic.map(p => p.file)];
playMusic.spliceWhere(p => filesPlaying.filter(f => f === p.file).length > 1);
stopMusic.spliceWhere(p => filesPlaying.filter(f => f === p.file).length > 1);

stopMusic.forEach(m => this.stopSoundHelper(m));
playMusic.forEach(m => this.playMusicHelper(m));
},

update: function (playerX, playerY) {
@@ -230,6 +236,7 @@ define([
x,
y,
volume,
maxVolume: volume,
area,
music,
defaultMusic


+ 1
- 1
src/client/package.json View File

@@ -1,6 +1,6 @@
{
"name": "isleward_client",
"version": "0.8.6",
"version": "0.9.0",
"description": "isleward",
"dependencies": {},
"devDependencies": {


+ 1
- 0
src/client/ui/templates/announcements/styles.css View File

@@ -0,0 +1 @@
.uiAnnouncements{pointer-events:none;position:absolute;left:0px;top:0px;width:100%;top:calc(50% + 92px)}.uiAnnouncements .list{position:relative;width:100%}.uiAnnouncements .list .message{position:absolute;height:36px;left:50%;transform:translateX(-50%);text-align:center;color:#f2f5f5;font-size:18px;background-color:rgba(49,33,54,0.15);padding:8px;text-shadow:2px 2px 0 #312136,-2px -2px 0 #312136,2px -2px 0 #312136,-2px 2px 0 #312136,2px 2px 0 #312136;animation:.5s ease-in-out infinite bounce}.uiAnnouncements .list .message.success{color:#80f643}.uiAnnouncements .list .message.failure{color:#d43346}@keyframes bounce{0%{top:0px}50%{top:4px}100%{top:0px}}

+ 1
- 0
src/client/ui/templates/buffs/styles.css View File

@@ -0,0 +1 @@
.uiBuffs{position:absolute;left:16px;top:104px}.uiBuffs .icon{width:40px;height:40px;padding:4px;background-color:rgba(49,33,54,0.75);margin-right:16px;float:left}.uiBuffs .icon .inner{width:32px;height:32px;background:url('../../../images/statusIcons.png') 0 0}.mobile .uiBuffs{left:316px;top:10px}

+ 1
- 0
src/client/ui/templates/characters/styles.css View File

@@ -0,0 +1 @@
.uiCharacters{display:flex;flex-direction:column;align-items:center;position:absolute}.uiCharacters .logo{width:562px;height:200px;margin-bottom:60px}.uiCharacters .bottom{display:flex;height:300px}.uiCharacters .bottom .left,.uiCharacters .bottom .right{background-color:#3a3b4a;filter:drop-shadow(0 -2px 0 #2d2136) drop-shadow(0 2px 0 #2d2136) drop-shadow(2px 0 0 #2d2136) drop-shadow(-2px 0 0 #2d2136)}.uiCharacters .bottom .left{width:400px;overflow-y:auto}.uiCharacters .bottom .left .character{height:50px;width:100%;color:#f2f5f5;cursor:pointer;display:flex;justify-content:center;align-items:center}.uiCharacters .bottom .left .character:hover{background-color:#505360}.uiCharacters .bottom .left .character.selected{background-color:#69696e}.uiCharacters .bottom .right{width:400px;padding:10px;margin-left:30px;display:flex;flex-direction:column}.uiCharacters .bottom .right .portrait{height:205px;background-color:#2d2136;display:flex;justify-content:center;align-items:center}.uiCharacters .bottom .right .portrait .sprite{width:8px;height:8px;transform-origin:50% 50%;transform:scale(16);image-rendering:pixelated;image-rendering:optimizeSpeed;image-rendering:crisp-edges;display:none}.uiCharacters .bottom .right .info{height:30px;margin-bottom:10px;background-color:#2d2136;color:#f2f5f5;display:flex;justify-content:space-between;padding:0px 10px}.uiCharacters .bottom .right .info .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.uiCharacters .bottom .right .info .class{text-align:right;color:#aabebe}.uiCharacters .bottom .right .buttons{display:flex}.uiCharacters .bottom .right .buttons .btn{background-color:#3a71ba;width:calc((100% - (10px * 2)) / 3);float:left;margin-right:10px;color:#f2f5f5}.uiCharacters .bottom .right .buttons .btn:last-child{margin-right:0px}.uiCharacters .bottom .right .buttons .btn:hover{background-color:#42548d}.uiCharacters .bottom .right .buttons .btn.deleting{background-color:#d43346;color:#f2f5f5}.uiCharacters .message{height:30px;width:100%;margin-top:30px;float:left;text-align:center;color:#f2f5f5;filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136)}.mobile .uiCharacters{height:300px}.mobile .uiCharacters .logo{display:none}

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

@@ -0,0 +1 @@
.uiContext{display:none;position:absolute;z-index:999999;border:5px solid #42548d;background-color:rgba(49,33,54,0.95);padding:5px;color:#f2f5f5;text-align:center;line-height:15px}.uiContext .list{width:100%;height:100%}.uiContext .list .option{width:100%;padding:5px;color:#f2f5f5;cursor:pointer;display:flex;justify-content:center}.uiContext .list .option:hover:not(.no-hover){background-color:#3a71ba}.uiContext .list .option.divider{line-height:2px;padding:0px;margin:5px 0px;color:transparent;background-color:#69696e}.uiContext .list .option.no-hover:not(.divider){color:#929398}.uiContext .list .option .hotkey{color:#48edff}.uiContext .list .option .hotkey:not(:empty){margin-left:10px}

+ 1
- 0
src/client/ui/templates/createCharacter/styles.css View File

@@ -0,0 +1 @@
.uiCreateCharacter{width:1040px;height:584px}.uiCreateCharacter .logo{width:562px;height:200px;margin-left:240.5px;margin-bottom:54px}.uiCreateCharacter .box-left{float:left;height:276px;width:613px;background-color:#3a3b4a}.uiCreateCharacter .box-left .left,.uiCreateCharacter .box-left .right{float:left;height:100%}.uiCreateCharacter .box-left .left{width:calc(100% - (27px * 2) - 160px);padding:27px 0 27px 27px}.uiCreateCharacter .box-left .left .txtClass,.uiCreateCharacter .box-left .left .txtCostume{cursor:pointer;-webkit-user-select:none}.uiCreateCharacter .box-left .left .txtClass:active,.uiCreateCharacter .box-left .left .txtCostume:active{background-color:#0f1013}.uiCreateCharacter .box-left .left .label{color:#c0c3cf;text-align:center;margin-bottom:8.3px}.uiCreateCharacter .box-left .left .skinBox{display:flex;flex-direction:row;height:35px}.uiCreateCharacter .box-left .left .skinBox .btn{width:35px;margin:0px;flex-shrink:0;height:100%;display:flex;justify-content:center;align-items:center;padding:0px;background-color:#69696e}.uiCreateCharacter .box-left .left .skinBox .btn:first-child{margin-right:10px}.uiCreateCharacter .box-left .left .skinBox .btn:last-child{margin-left:10px}.uiCreateCharacter .box-left .left .skinBox .btn:hover{background-color:#929398}.uiCreateCharacter .box-left .left .skinBox .txtCostume{height:100%;display:flex;justify-content:center;align-items:center;padding:0px}.uiCreateCharacter .box-left .right{width:calc(160px + (27px * 2));padding:27px 27px 27px 27px}.uiCreateCharacter .box-left .portrait{width:100%;height:160px;background-color:#2d2136;padding:16px 0 0 16px;margin-bottom:27px}.uiCreateCharacter .box-left .portrait .sprite{width:8px;height:8px;transform:scale(16);transform-origin:0% 0%;image-rendering:optimizeSpeed;image-rendering:pixelated;image-rendering:crisp-edges}.uiCreateCharacter .box-left .textbox{width:100%;background-color:#1a1c21;border:3px solid #757b92;color:#f2f5f5;margin-bottom:22px}.uiCreateCharacter .box-left .btn{background-color:#3a71ba;width:calc((100% - 27px) / 2);float:left;margin-right:27px;color:#f2f5f5}.uiCreateCharacter .box-left .btn:last-child{margin-right:0px}.uiCreateCharacter .box-left .btn:hover{background-color:#3fa7dd}.uiCreateCharacter .box-left .message{height:27px;width:100%;margin-top:27px;float:left;text-align:center;color:#ff4252}.uiCreateCharacter .box-right{padding:27px 27px 27px 27px;float:left;height:276px;width:400px;margin-left:13px;background-color:#3a3b4a}.uiCreateCharacter .box-right .top{width:100%;color:#f2f5f5}.uiCreateCharacter .box-right .top>*{width:100%;text-align:center}.uiCreateCharacter .box-right .top .heading{margin-bottom:27px;color:#48edff}.uiCreateCharacter .box-right .top .list .prophecy{padding-top:0px;width:50%;float:left;height:24px;margin-bottom:24px;color:#ff4252;cursor:pointer}.uiCreateCharacter .box-right .top .list .prophecy:after{content:' ✗'}.uiCreateCharacter .box-right .top .list .prophecy.active{color:#80f643}.uiCreateCharacter .box-right .top .list .prophecy.active:after{content:' ✔'}.uiCreateCharacter .box-right .top .list .prophecy.disabled{color:#f2f5f5}.uiCreateCharacter .box-right .top .list .prophecy:hover{color:#f2f5f5}.mobile .uiCreateCharacter{height:276px;width:calc(1040px - 80px);display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center}.mobile .uiCreateCharacter .logo{display:none}.mobile .uiCreateCharacter .box-left{width:45%}.mobile .uiCreateCharacter .box-left .skinBox{height:35px}.mobile .uiCreateCharacter .box-right{width:45%}

+ 1
- 0
src/client/ui/templates/death/styles.css View File

@@ -0,0 +1 @@
.uiDeath{display:none;width:400px;background-color:#3c3f4c;border:4px solid #929398;text-align:center;padding:16px 16px 0px 16px}.uiDeath .msg{color:#f2f5f5}.uiDeath .msg .inner{display:inline-block;color:#ff6942}.uiDeath .penalty{color:#faac45;margin-top:15px}.uiDeath .btn{color:#f2f5f5;width:100%;height:32px;background-color:#3fa7dd;margin:16px 0px;padding-top:8px;cursor:pointer}.uiDeath .btn:hover{background-color:#80c5e9;color:#2d2136}.uiDeath.permadeath .buttons .btn-respawn{display:none}.uiDeath.permadeath .buttons .btn-logout{display:block}.uiDeath .buttons .btn-logout{display:none}

+ 1
- 0
src/client/ui/templates/dialogue/styles.css View File

@@ -0,0 +1 @@
.uiDialogue{position:absolute;bottom:16px;width:calc(320px + (8px * 2));padding:8px;background-color:rgba(55,48,65,0.9);display:none;box-shadow:0 -2px 0 #2d2136,0 2px 0 #2d2136,2px 0 0 #2d2136,-2px 0 0 #2d2136}.uiDialogue .portraitBox{float:left;margin-right:12px;display:none}.uiDialogue .portraitBox .portrait{width:64px;height:64px}.uiDialogue .textBox{width:calc(100%);float:left;height:100%;color:#f2f5f5;text-align:center;filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136)}

+ 1
- 0
src/client/ui/templates/equipment/styles.css
File diff suppressed because it is too large
View File


+ 1
- 0
src/client/ui/templates/events/styles.css View File

@@ -0,0 +1 @@
.uiEvents{margin-top:10px;width:326px;box-shadow:0 -2px 0 #2d2136,0 2px 0 #2d2136,2px 0 0 #2d2136,-2px 0 0 #2d2136}.uiEvents .heading{color:#ffeb38;padding:8px;background-color:rgba(58,59,74,0.9)}.uiEvents .list .event{cursor:pointer;padding:8px;background-color:rgba(58,59,74,0.9)}.uiEvents .list .event:hover{background-color:rgba(92,93,117,0.9)}.uiEvents .list .event .name{color:#f2f5f5;margin-bottom:3px}.uiEvents .list .event .description{color:#8da7a7}.uiEvents .list .event.active .name{color:#3fa7dd}.uiEvents .list .event.ready .name{color:#80f643}.uiEvents .list .event.ready .description{display:none}.uiEvents .btnCollapse{display:none}.mobile .uiEvents{background-color:rgba(55,48,65,0.9);margin-top:0px}.mobile .uiEvents:not(.active){width:64px;height:64px}.mobile .uiEvents:not(.active):after{content:'';position:absolute;left:0px;top:0px;background:url('../../../images/uiIcons.png');background-position:-128px -128px;width:64px;height:64px}.mobile .uiEvents:not(.active)>*{display:none}.mobile .uiEvents.active{z-index:3;position:absolute;right:0px;top:0px}.mobile .uiEvents.active .btn{display:block;position:absolute;right:calc(100% + 10px);top:0px;background-color:rgba(55,48,65,0.9);width:64px;height:64px;padding-top:0px}.mobile .uiEvents.active .btn .icon{width:100%;height:100%;background:url('../../../images/uiIcons.png') -448px 0}

+ 1
- 0
src/client/ui/templates/help/styles.css View File

@@ -0,0 +1 @@
.uiHelp{display:none;z-index:2;border:5px solid #3c3f4c;text-align:center;flex-direction:column}.uiHelp>.heading{color:#48edff;width:100%;height:36px;background-color:#3c3f4c;position:relative}.uiHelp>.heading .heading-text{padding-top:8px;margin:auto}.uiHelp .content{overflow-y:auto;background-color:#312136;width:100%;flex:1;display:flex;flex-direction:column;padding:5px}.uiHelp .content .row{padding:5px;display:flex;justify-content:center;background-color:#373041}.uiHelp .content .row+.row{margin-top:5px}.uiHelp .content .row.mobile{display:none}.uiHelp .content .row .topic{color:#3fa7dd;text-align:right;width:30%}.uiHelp .content .row .desc{color:#c0c3cf;text-align:left;width:70%;max-width:400px;padding-left:20px}.uiHelp .content .row .toslink{color:#f2f5f5;cursor:pointer}.uiHelp .content .row .toslink:hover{color:#48edff}.mobile .uiHelp{height:80vh}.mobile .uiHelp .row{display:none}.mobile .uiHelp .row.mobile{display:flex}

+ 1
- 0
src/client/ui/templates/hud/styles.css View File

@@ -0,0 +1 @@
.uiHud{position:absolute;left:10px;top:10px}.uiHud .hudBox{position:absolute;left:0px;top:0px;width:calc(280px + (8px * 2));padding:8px;background-color:rgba(58,59,74,0.9);box-shadow:0 -2px 0 #2d2136,0 2px 0 #2d2136,2px 0 0 #2d2136,-2px 0 0 #2d2136}.uiHud .hudBox .portraitBox{float:left;margin-right:12px}.uiHud .hudBox .portraitBox .portrait{visibility:hidden;width:64px;height:64px}.uiHud .hudBox .boxes{width:calc(100% - 76px);float:left;height:100%}.uiHud .hudBox .boxes .statBox{width:100%;height:18px;margin-bottom:5px;position:relative}.uiHud .hudBox .boxes .statBox:last-child{margin-bottom:0px}.uiHud .hudBox .boxes .statBox [class^="stat"]{position:absolute;left:0px;top:0px;height:100%}.uiHud .hudBox .boxes .statBox .statManaReserve{position:absolute;right:0%;top:0px;left:auto;height:100%;background-color:#c0c3cf}.uiHud .hudBox .boxes .statBox .text{position:absolute;left:0px;top:0px;width:100%;height:100%;text-align:center;color:#f2f5f5;padding:2px 0px;line-height:16px;text-shadow:2px 0 #2d2136,-2px 0 #2d2136,0 -2px #2d2136,0 2px #2d2136,-2px -2px #2d2136,-2px 2px #2d2136,2px -2px #2d2136,2px 2px #2d2136}.uiHud .hudBox .boxes .statBox:nth-child(1){background-color:#802343}.uiHud .hudBox .boxes .statBox:nth-child(2){background-color:#42548d}.uiHud .hudBox .boxes .statBox:nth-child(3){background-color:#386646}.uiHud .hudBox .boxes .statBox .statHp{background-color:#d43346}.uiHud .hudBox .boxes .statBox .statMana{background-color:#3fa7dd}.uiHud .hudBox .boxes .statBox .statXp{background-color:#4ac441}.uiHud .quickBar{position:absolute;left:calc(290px + (8px * 2));top:0px;box-shadow:0 -2px 0 #2d2136,0 2px 0 #2d2136,2px 0 0 #2d2136,-2px 0 0 #2d2136}.uiHud .quickBar .quickItem{display:none;width:80px;height:80px;background-color:rgba(58,59,74,0.9);margin-bottom:8px;cursor:pointer;padding:8px;position:relative}.uiHud .quickBar .quickItem .icon{height:100%;filter:brightness(100%) drop-shadow(0 -4px 0 #373041) drop-shadow(0 4px 0 #373041) drop-shadow(4px 0 0 #373041) drop-shadow(-4px 0 0 #373041)}.uiHud .quickBar .quickItem .info{left:6px;bottom:3px;position:absolute;color:#f2f5f5;filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136);-moz-filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136)}.uiHud .quickBar .quickItem:hover .icon{filter:brightness(160%);-moz-filter:brightness(160%)}.uiHud .quickBar .quickItem:hover.empty{background-color:rgba(72,237,255,0.1)}

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

@@ -168,6 +168,7 @@ define([
this.dragItem = el;

events.emit('onHideItemTooltip', this.hoverItem);
events.emit('onStartMoveItem', this.dragItem.data('item'));
this.hoverItem = null;
} else if (this.dragItem) {
let method = 'moveItem';
@@ -219,6 +220,8 @@ define([
}
});

events.emit('onStopMoveItem', this.dragItem.data('item'));

this.build();
}



+ 1
- 0
src/client/ui/templates/inventory/styles.css
File diff suppressed because it is too large
View File


+ 1
- 0
src/client/ui/templates/leaderboard/styles.css View File

@@ -0,0 +1 @@
.uiLeaderboard{display:none;width:600px;height:410px;border:5px solid rgba(60,63,76,0.9);text-align:center}.uiLeaderboard>.heading{color:#48edff;width:100%;height:36px;background-color:rgba(60,63,76,0.9)}.uiLeaderboard>.heading .heading-text{padding-top:8px;margin:auto}.uiLeaderboard .result{background-color:rgba(55,48,65,0.9);float:left;width:400px;height:calc(100% - 36px);padding:5px}.uiLeaderboard .result .headings{width:100%;height:36px}.uiLeaderboard .result .headings .col{text-align:left;padding:5px 10px;width:50%;float:left;color:#f2f5f5}.uiLeaderboard .result .list{height:calc(100% - 26px - 36px)}.uiLeaderboard .result .list .row{width:100%;height:26px}.uiLeaderboard .result .list .row.self .col{color:#80f643}.uiLeaderboard .result .list .row.online .col{color:#4ac441}.uiLeaderboard .result .list .row .col{padding:5px 10px;float:left;text-align:left;width:50%;color:#c0c3cf;height:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.uiLeaderboard .result .buttons{width:calc(100% - 26px);height:26px}.uiLeaderboard .result .buttons .btn{width:calc((100% - (13px * 3)) / 4);margin-right:13px;float:left;height:100%;color:#f2f5f5;cursor:pointer;background-color:#7a3ad3}.uiLeaderboard .result .buttons .btn:hover{background-color:#a24eff}.uiLeaderboard .result .buttons .btn:last-child{margin-right:0px}.uiLeaderboard .prophecies{float:left;width:calc(100% - 400px);height:calc(100% - 36px);position:relative;background-color:rgba(55,48,65,0.9);padding:5px}.uiLeaderboard .prophecies .heading{width:100%;height:36px;color:#3fa7dd}.uiLeaderboard .prophecies .heading .heading-text{margin:auto}.uiLeaderboard .prophecies .prophecy,.uiLeaderboard .prophecies .btn{cursor:pointer}.uiLeaderboard .prophecies .prophecy{width:100%;padding:5px 10px;background-color:#69696e;color:#f2f5f5;margin-bottom:12px}.uiLeaderboard .prophecies .prophecy.selected{color:#80f643}.uiLeaderboard .prophecies .prophecy:hover{background-color:#929398}.uiLeaderboard .prophecies .prophecy.prophecy-mine,.uiLeaderboard .prophecies .prophecy.prophecy-none{background-color:#929398}.uiLeaderboard .prophecies .prophecy.prophecy-mine:hover,.uiLeaderboard .prophecies .prophecy.prophecy-none:hover{background-color:#c0c3cf}.uiLeaderboard .prophecies .btn-refresh{width:calc(100% - 10px);background-color:#3a71ba;padding:5px 10px;position:absolute;bottom:5px;color:#f2f5f5}.uiLeaderboard .prophecies .btn-refresh:hover{background-color:#3fa7dd}.mobile .uiLeaderboard{z-index:2}

+ 1
- 0
src/client/ui/templates/login/styles.css View File

@@ -0,0 +1 @@
.uiLogin{display:none;width:562px;height:475px;margin-top:-80px;display:flex;flex-direction:column;align-items:center}.uiLogin>*{flex-shrink:0}.uiLogin .logo{width:562px;height:200px;margin-bottom:48px;filter:drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136)}.uiLogin .right{height:169px;background-color:#3a3b4a;width:400px;padding:16px}.uiLogin .right .label,.uiLogin .right input{float:left}.uiLogin .right .label{width:30%;padding-top:10px;color:#80f643}.uiLogin .right input{width:70%}.uiLogin .right input,.uiLogin .right .textbox,.uiLogin .right input:-webkit-autofill{-webkit-appearance:none;box-shadow:0 0 0 20px #1a1c21 inset;color:#f2f5f5;-webkit-text-fill-color:#f2f5f5;margin-bottom:16px}.uiLogin .right input,.uiLogin .right .textbox{-webkit-appearance:none;box-shadow:0 0 0 20px #1a1c21 inset;color:#f2f5f5;-webkit-text-fill-color:#f2f5f5;margin-bottom:16px}.uiLogin .right .message{height:16px;width:200%;margin-left:-50%;margin-top:36px;float:left;text-align:center;color:#ff6942;filter:brightness(100%) drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136);-moz-filter:brightness(100%) drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136)}.uiLogin .right .top-buttons{width:100%;height:35px}.uiLogin .right .top-buttons .btn{background-color:#3a71ba;width:calc((100% - 16px) / 2);float:left;margin-right:16px;color:#f2f5f5}.uiLogin .right .top-buttons .btn:last-child{margin-right:0px}.uiLogin .right .top-buttons .btn:hover{background-color:#42548d}.uiLogin .news{margin-top:40px;color:#f2f5f5;text-align:center;cursor:pointer;filter:drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136)}.uiLogin .spacer-h{height:61px}.uiLoginExtra .extra{position:absolute;left:50%;bottom:10px;transform:translateX(-50%);display:flex}.uiLoginExtra .extra .btn{width:180px;padding-left:10px;padding-right:10px;background-color:#b15a30;color:#f2f5f5;filter:drop-shadow(0 -2px 0 #2d2136) drop-shadow(0 2px 0 #2d2136) drop-shadow(2px 0 0 #2d2136) drop-shadow(-2px 0 0 #2d2136)}.uiLoginExtra .extra .btn+.btn{margin-left:20px}.uiLoginExtra .extra .btn:hover{background-color:#763b3b}.uiLoginExtra .version{position:absolute;right:10px;bottom:10px;color:#ffeb38;cursor:pointer;filter:brightness(100%) drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136);-moz-filter:brightness(100%) drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136)}.mobile .uiLogin{margin-top:0px}.mobile .uiLogin .logo{margin-bottom:30px}.mobile .uiLogin .news{margin-top:20px}

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

@@ -11,11 +11,11 @@
</div>
<div class="message"></div>
</div>
<div class="news" location="https://gitlab.com/Isleward/play.isleward.com/-/wikis/v0.8.6-Release-Notes">[ Latest Release Notes ]</div>
<div class="news" location="https://gitlab.com/Isleward/play.isleward.com/-/wikis/v0.9.0-Release-Notes">[ Latest Release Notes ]</div>
<div class="extra">
<div class="el btn btnPatreon monetization" location="https://patreon.com/bigbadwaffle">Pledge on Patreon</div>
<div class="el btn btnPaypal monetization" location="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BR2CC82WUAVEA">Donate on Paypal</div>
<div class="el btn btnWiki" location="http://wiki.isleward.com/Main_Page">Access the Wiki</div>
</div>
<div class="version" location="https://gitlab.com/Isleward/play.isleward.com/-/wikis/v0.8.6-Release-Notes">v0.8.6</div>
<div class="version" location="https://gitlab.com/Isleward/play.isleward.com/-/wikis/v0.9.0-Release-Notes">v0.9.0</div>
</div>

+ 1
- 0
src/client/ui/templates/mainMenu/styles.css View File

@@ -0,0 +1 @@
.uiMainMenu{display:none;background-color:#373041;border:5px solid #3c3f4c;text-align:center}.uiMainMenu>.heading{color:#48edff;width:100%;height:36px;background-color:#3c3f4c;position:relative}.uiMainMenu>.heading .heading-text{padding-top:8px;margin:auto}.uiMainMenu .options{display:flex;flex-direction:column;padding:10px}.uiMainMenu .options .btn{color:#f2f5f5;width:100%;height:35px;background-color:#3a71ba;cursor:pointer;padding:0px 20px}.uiMainMenu .options .btn+.btn{margin-top:10px}.uiMainMenu .options .btn.btnNames{display:none}.uiMainMenu .options .btn:hover{background-color:#42548d}.uiMainMenu .options .btn.btn-red{background-color:#a82841}.uiMainMenu .options .btn.btn-red:hover{background-color:#802343}.uiMainMenu .options .btn.btn-orange{background-color:#b34b3a}.uiMainMenu .options .btn.btn-orange:hover{background-color:#953f36}.uiMainMenu .options .btnIssue{background-color:#a82841}.uiMainMenu .options .btnIssue:hover{background-color:#802343}.mobile .uiMainMenu .btnScreen{display:none}.mobile .uiMainMenu .btnNames{display:block}

+ 1
- 0
src/client/ui/templates/menu/styles.css View File

@@ -0,0 +1 @@
.uiMenu{position:absolute;right:10px;bottom:10px;width:336px;height:144px;padding:8px;background-color:rgba(55,48,65,0.9)}.uiMenu [class^="btn"]{width:64px;height:64px;margin:0 8px;float:left;cursor:pointer;position:relative}.uiMenu [class^="btn"]:hover{background-color:rgba(242,245,245,0.1)}.uiMenu [class^="btn"] .icon{pointer-events:none;position:absolute;left:0px;top:0px;width:100%;height:100%}.uiMenu [class^="btn"].btnCollapse{display:none}.uiMenu [class^="btn"].btnInventory .icon{background:url('../../../images/uiIcons.png') 0 0}.uiMenu [class^="btn"].btnEquipment .icon{background:url('../../../images/uiIcons.png') -192px 0}.uiMenu [class^="btn"].btnSmithing .icon{background:url('../../../images/uiIcons.png') -128px -64px}.uiMenu [class^="btn"].btnPassives{position:relative}.uiMenu [class^="btn"].btnPassives .points{width:100%;text-align:center;left:0px;bottom:23px;position:absolute;color:#f2f5f5;filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136)}.uiMenu [class^="btn"].btnPassives .icon{background:url('../../../images/uiIcons.png') -320px -64px}.uiMenu [class^="btn"].btnOnline .icon{background:url('../../../images/uiIcons.png') -64px 0}.uiMenu [class^="btn"].btnHelp .icon{background:url('../../../images/uiIcons.png') -128px 0}.uiMenu [class^="btn"].btnLeaderboard .icon{background:url('../../../images/uiIcons.png') -256px 0}.uiMenu [class^="btn"].btnReputation .icon{background:url('../../../images/uiIcons.png') -320px 0}.uiMenu [class^="btn"].btnMainMenu .icon{background:url('../../../images/uiIcons.png') 0 -64px}.mobile .uiMenu{bottom:auto;top:10px;right:10px;width:64px;height:64px}.mobile .uiMenu:after{content:'';position:absolute;left:0px;top:0px;background:url('../../../images/uiIcons.png');background-position:0 -64px;width:64px;height:64px}.mobile .uiMenu>*{display:none}.mobile .uiMenu.active{display:flex;flex-direction:column;height:calc(100% - 20px);width:200px;z-index:2;flex-wrap:wrap-reverse;align-items:center;justify-content:center}.mobile .uiMenu.active:after{display:none}.mobile .uiMenu.active>*{display:block;margin:0px}.mobile .uiMenu.active .btnCollapse{display:block;position:absolute;right:calc(100% + 10px);top:50%;transform:translateY(-50%);background-color:rgba(55,48,65,0.9);width:64px;height:64px}.mobile .uiMenu.active .btnCollapse .icon{width:100%;height:100%;background:url('../../../images/uiIcons.png') -448px 0}

+ 1
- 0
src/client/ui/templates/messages/styles.css
File diff suppressed because it is too large
View File


+ 1
- 0
src/client/ui/templates/middleHud/styles.css View File

@@ -0,0 +1 @@
.uiMiddleHud{position:absolute;left:calc(50% - 12px);top:calc(50% + 31px)}.uiMiddleHud .btnGather{display:none;background-color:rgba(58,59,74,0.9);padding:8px 10px 8px 10px;width:120px;margin-top:8px;margin-left:calc(12px - 60px);text-align:center;position:absolute;left:calc(50vw - 88px);top:calc(50vh - 113px);width:64px;height:64px}.uiMiddleHud .btnGather .icon{position:absolute;left:0px;top:0px;width:100%;height:100%;background:url(../../../images/uiIcons.png) -448px -64px}.uiMiddleHud .casting{display:none;width:32px;height:5px;position:relative;background-color:#763b3b}.uiMiddleHud .casting .bar{width:0%;height:100%;background-color:#faac45}.uiMiddleHud .casting .text{position:absolute;left:0px;top:0px;padding-top:2px;width:100%;text-align:center;color:#2d2136}

+ 1
- 0
src/client/ui/templates/online/styles.css View File

@@ -0,0 +1 @@
.uiOnline{display:none;background-color:#3c3f4c;border:5px solid #3c3f4c;text-align:center;width:450px;height:396px}.uiOnline>.heading{color:#48edff;width:100%;height:36px;background-color:#3c3f4c}.uiOnline>.heading .heading-text{padding-top:8px;margin:auto}.uiOnline .bottom{height:calc(100% - 36px);background-color:#373041;padding:8px}.uiOnline .bottom .list{width:100%;height:100%;overflow-y:auto;background-color:#373041;display:flex;flex-direction:column}.uiOnline .bottom .list .heading{height:24px;color:#f2f5f5}.uiOnline .bottom .list .heading>div{float:left;padding:4px;height:24px}.uiOnline .bottom .list .heading>div:nth-child(1){width:10%}.uiOnline .bottom .list .heading>div:nth-child(2){width:55%}.uiOnline .bottom .list .heading>div:nth-child(3){width:35%}.uiOnline .bottom .list .onlineUser{color:#929398;height:24px;cursor:pointer}.uiOnline .bottom .list .onlineUser>div{float:left;padding:4px;height:24px}.uiOnline .bottom .list .onlineUser>div:nth-child(1){width:10%}.uiOnline .bottom .list .onlineUser>div:nth-child(2){width:55%}.uiOnline .bottom .list .onlineUser>div:nth-child(3){width:35%}.uiOnline .bottom .list .onlineUser:hover{background-color:#75668a}.mobile .uiOnline{z-index:2}

+ 1
- 0
src/client/ui/templates/options/styles.css View File

@@ -0,0 +1 @@
.uiOptions{display:none;width:400px;background-color:#373041;border:5px solid #3c3f4c;flex-direction:column}.uiOptions>.heading{color:#48edff;width:100%;height:36px;background-color:#3c3f4c;position:relative;text-align:center}.uiOptions>.heading .heading-text{padding-top:8px;margin:auto}.uiOptions .bottom{padding:10px;height:100%;overflow-y:auto}.uiOptions .bottom .list{display:flex;flex-direction:column}.uiOptions .bottom .list .heading{color:#929398;border-bottom:2px solid #69696e;margin:0px 10px 5px 10px;padding-bottom:5px}.uiOptions .bottom .list .heading:not(:first-child){margin:10px 10px 5px 10px}.uiOptions .bottom .list .item{height:30px;display:flex;justify-content:space-between;align-items:center}.uiOptions .bottom .list .item .name{color:#3fa7dd;flex:1;height:100%;display:flex;align-items:center;padding:0px 10px;margin-right:10px;cursor:pointer}.uiOptions .bottom .list .item .name:hover{color:#48edff;background-color:#505360}.uiOptions .bottom .list .item .value{color:#f2f5f5;display:flex;align-items:center;justify-content:flex-end;padding-right:10px}.uiOptions .bottom .list .item.volume{flex-wrap:wrap;height:60px}.uiOptions .bottom .list .item.volume .name{pointer-events:none}.uiOptions .bottom .list .item.volume .name,.uiOptions .bottom .list .item.volume .value{width:50%;height:30px}.uiOptions .bottom .list .item.volume .slider{padding:0px 10px;width:100%;height:30px;display:flex;position:relative}.uiOptions .bottom .list .item.volume .slider .btn{width:30px;margin-top:5px;height:calc(100% - 10px);display:flex;justify-content:center;align-items:center;color:#f2f5f5;background-color:#505360;padding-top:unset}.uiOptions .bottom .list .item.volume .slider .btn:first-child{margin-right:10px}.uiOptions .bottom .list .item.volume .slider .btn:last-child{margin-left:10px}.uiOptions .bottom .list .item.volume .slider .btn:hover{background-color:#69696e}.uiOptions .bottom .list .item.volume .slider .bar{width:100%;background-color:#42548d;border-top:10px solid #373041;border-bottom:10px solid #373041;height:100%;position:relative}.uiOptions .bottom .list .item.volume .slider .bar .tick{position:absolute;width:5px;height:20px;top:-5px;left:100%;background-color:#48edff}.mobile .uiOptions{max-height:100%}.mobile .uiOptions .item.screen{display:none}

+ 1
- 0
src/client/ui/templates/overlay/styles.css View File

@@ -0,0 +1 @@
.uiOverlay{display:none;position:absolute;left:0px;top:0px;background-color:rgba(60,63,76,0.55);width:100%;height:100%;z-index:99999}

+ 1
- 0
src/client/ui/templates/party/styles.css View File

@@ -0,0 +1 @@
.uiParty .party{position:absolute;left:16px;top:154px}.uiParty .party .list{overflow-y:auto;max-height:calc(100vh - 591px);padding-right:22px}.uiParty .party .list .member{width:160px;background-color:#3a3b4a;color:#f2f5f5;cursor:pointer}.uiParty .party .list .member+.member{margin-top:5px}.uiParty .party .list .member.differentZone{opacity:.4}.uiParty .party .list .member.differentZone .statBox [class^="stat"]{width:0% !important}.uiParty .party .list .member:last-child{margin-bottom:0px}.uiParty .party .list .member .statBox{width:100%;height:20px;background-color:#3c3f4c;position:relative;display:flex;justify-content:center;align-items:center}.uiParty .party .list .member .statBox+.statBox{border-top:2px solid #2d2136}.uiParty .party .list .member .statBox [class^="stat"]{position:absolute;left:0px;top:0px;height:100%}.uiParty .party .list .member .statBox .text{color:#f2f5f5;z-index:1;padding:0px 2px;overflow:hidden;text-overflow:ellipsis;text-shadow:2px 0 #2d2136,-2px 0 #2d2136,0 -2px #2d2136,0 2px #2d2136,-2px -2px #2d2136,-2px 2px #2d2136,2px -2px #2d2136,2px 2px #2d2136}.uiParty .party .list .member .statBox:nth-child(1){background-color:#802343}.uiParty .party .list .member .statBox:nth-child(2){background-color:#42548d}.uiParty .party .list .member .statBox .statHp{background-color:#d43346}.uiParty .party .list .member .statBox .statMana{background-color:#3fa7dd}.uiParty .invite{position:absolute;right:436px;bottom:10px;background-color:rgba(55,48,65,0.9);padding:10px;color:#f2f5f5;display:flex;flex-direction:column}.uiParty .invite>.text{margin-bottom:15px;text-align:center;display:flex;flex-direction:column}.uiParty .invite>.text .name{margin-top:10px}.uiParty .invite .buttons{display:flex}.uiParty .invite .buttons .btn{width:100%;background-color:#505360;text-align:center;padding:10px;cursor:pointer;display:flex;justify-content:center;align-items:center}.uiParty .invite .buttons .btn .text{pointer-events:none;color:#f2f5f5;text-shadow:2px 0 #2d2136,-2px 0 #2d2136,0 -2px #2d2136,0 2px #2d2136,-2px -2px #2d2136,-2px 2px #2d2136,2px -2px #2d2136,2px 2px #2d2136}.uiParty .invite .buttons .btn+.btn{margin-left:10px}.uiParty .invite .buttons .btn.btnDecline{background-color:#d43346}.uiParty .invite .buttons .btn.btnDecline:hover{background-color:#a82841}.uiParty .invite .buttons .btn.btnAccept{background-color:#3f8d6d}.uiParty .invite .buttons .btn.btnAccept:hover{background-color:#386646}.uiParty.full .party .list .member{padding:5px;background-color:rgba(55,48,65,0.9)}.uiParty.minimal .party .list .statBox:nth-child(2){display:none}.mobile .uiParty .list{max-height:calc(100% - 154px - 10px)}

+ 1
- 1
src/client/ui/templates/party/styles.less View File

@@ -206,4 +206,4 @@

.mobile .uiParty .list {
max-height: calc(100% - @topPosition - 10px);
}
}

+ 1
- 0
src/client/ui/templates/passives/styles.css View File

@@ -0,0 +1 @@
.uiPassives{display:none;z-index:3;border:5px solid #3c3f4c;text-align:center;height:100%;width:100%;position:absolute;left:0px;top:0px}.uiPassives>.heading{color:#f2f5f5;width:100%;height:36px;background-color:#3c3f4c}.uiPassives>.heading .heading-text{padding-top:8px;margin:auto}.uiPassives .bottom{height:calc(100% - 36px - 36px);background-color:#373041}.uiPassives .bottom .btn.btnReset{position:absolute;height:35px;padding-left:10px;padding-right:10px;bottom:46px;right:10px;background-color:#a82841;color:#f2f5f5;cursor:pointer}.uiPassives .bottom .btn.btnReset:hover{background-color:#d43346}.uiPassives .bottom .canvas{width:calc(100vw - 10px)}.uiPassives .status{width:100%;height:36px;background-color:#3c3f4c}.uiPassives .status .points{padding-top:8px;margin:auto;color:#48edff}

+ 1
- 0
src/client/ui/templates/progressBar/styles.css View File

@@ -0,0 +1 @@
.uiProgressBar{pointer-events:none;position:absolute;left:0px;top:0px;width:192px;left:calc(50% - 96px);top:calc(50% + 142px)}.uiProgressBar .barContainer{width:100%;height:28px;background-color:#42548d;border:2px solid #312136}.uiProgressBar .barContainer .bar{background-color:#3fa7dd;height:100%}.uiProgressBar .barContainer .text{padding-top:7px;color:#f2f5f5;position:absolute;left:0px;top:0px;text-align:center;width:100%;height:100%;z-index:1;text-shadow:2px 0 #2d2136,-2px 0 #2d2136,0 -2px #2d2136,0 2px #2d2136,-2px -2px #2d2136,-2px 2px #2d2136,2px -2px #2d2136,2px 2px #2d2136}

+ 1
- 0
src/client/ui/templates/quests/styles.css View File

@@ -0,0 +1 @@
.uiQuests{width:326px;box-shadow:0 -2px 0 #2d2136,0 2px 0 #2d2136,2px 0 0 #2d2136,-2px 0 0 #2d2136}.uiQuests .heading{color:#ffeb38;padding:8px;background-color:rgba(58,59,74,0.9)}.uiQuests .list .quest{cursor:pointer;padding:8px;background-color:rgba(58,59,74,0.9)}.uiQuests .list .quest:hover{background-color:rgba(92,93,117,0.9)}.uiQuests .list .quest .zone{color:#69696e;margin-bottom:4px;text-align:right}.uiQuests .list .quest .name{color:#f2f5f5;margin-bottom:3px}.uiQuests .list .quest .description{color:#8da7a7}.uiQuests .list .quest.disabled .description{display:none}.uiQuests .list .quest .reward{display:none;color:#44cb95}.uiQuests .list .quest:hover>.description{display:none}.uiQuests .list .quest:hover>.reward{display:block}.uiQuests .list .quest .ready-text{display:none}.uiQuests .list .quest.active .name{color:#3fa7dd}.uiQuests .list .quest.ready .name{color:#80f643}.uiQuests .list .quest.ready .description{display:none}.uiQuests .list .quest.ready .reward{display:none}.uiQuests .list .quest.ready .ready-text{display:block;color:#f2f5f5}.uiQuests .btn{display:none}.uiQuests.minimal .quest.disabled{display:none}.mobile .uiQuests{background-color:rgba(55,48,65,0.9)}.mobile .uiQuests:not(.active){width:64px;height:64px;margin-left:10px;position:relative}.mobile .uiQuests:not(.active):after{content:'';position:absolute;left:0px;top:0px;background:url('../../../images/uiIcons.png');background-position:-64px -128px;width:64px;height:64px}.mobile .uiQuests:not(.active)>*{display:none}.mobile .uiQuests.active{z-index:3;position:absolute;right:0px;top:0px}.mobile .uiQuests.active .btn{display:block;position:absolute;right:calc(100% + 10px);top:0px;background-color:rgba(55,48,65,0.9);width:64px;height:64px;padding-top:0px}.mobile .uiQuests.active .btn .icon{width:100%;height:100%;background:url('../../../images/uiIcons.png') -448px 0}

+ 1
- 0
src/client/ui/templates/reputation/styles.css View File

@@ -0,0 +1 @@
.uiReputation{display:none;width:600px;height:385px;border:5px solid rgba(60,63,76,0.9);text-align:center}.uiReputation>.heading{color:#48edff;width:100%;height:36px;background-color:rgba(60,63,76,0.9)}.uiReputation>.heading .heading-text{padding-top:8px;margin:auto}.uiReputation .list,.uiReputation .info{background-color:rgba(55,48,65,0.9);float:left;height:calc(100% - 36px)}.uiReputation .list{width:40%;padding:15px 15px 15px 15px}.uiReputation .list .faction{width:100%;padding:5px 10px;background-color:#69696e;color:#f2f5f5;margin-bottom:15px;cursor:pointer}.uiReputation .list .faction.selected{color:#80f643}.uiReputation .list .faction:hover{background-color:#929398}.uiReputation .info{width:60%;padding:15px 15px 15px 0px}.uiReputation .info .heading{width:100%;height:36px;color:#3fa7dd}.uiReputation .info .heading .heading-bottom{margin:auto}.uiReputation .info .description{color:#c0c3cf;height:calc(100% - 36px - 25px);text-align:justify}.uiReputation .info .bar-outer{width:100%;margin:auto;height:25px;position:relative}.uiReputation .info .bar-outer .back{width:100%;height:100%;background-color:#533399}.uiReputation .info .bar-outer .front{width:50%;height:100%;position:absolute;left:0px;top:0px;background-color:#7a3ad3}.uiReputation .info .bar-outer .tier{width:100%;height:100%;color:#f2f5f5;text-align:center;position:absolute;left:0px;top:5px;filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136);-moz-filter:drop-shadow(0 -2px 0 #312136) drop-shadow(0 2px 0 #312136) drop-shadow(2px 0 0 #312136) drop-shadow(-2px 0 0 #312136)}.uiReputation .list:empty{display:none}.uiReputation .list:empty+.info{width:100%}.mobile .uiReputation{z-index:2}

+ 1
- 0
src/client/ui/templates/spells/styles.css View File

@@ -0,0 +1 @@
.uiSpells{position:absolute;right:10px;top:10px;height:72px;box-shadow:0 -2px 0 #2d2136,0 2px 0 #2d2136,2px 0 0 #2d2136,-2px 0 0 #2d2136}.uiSpells .spell{width:64px;height:64px;float:left;margin-left:10px;background-color:#2d2136;cursor:pointer;position:relative;box-sizing:content-box;border:5px solid rgba(58,59,74,0.9);transition-property:filter;transition-duration:.1s}.uiSpells .spell:first-child{margin-left:0px}.uiSpells .spell:hover{filter:brightness(160%);-moz-filter:brightness(160%)}.uiSpells .spell.active .hotkey{color:#80f643}.uiSpells .spell .icon{position:absolute;left:0px;top:0px;width:100%;height:100%}.uiSpells .spell .cooldown{position:absolute;left:0px;top:0px;width:0px;height:100%;background-color:rgba(212,51,70,0.75)}.uiSpells .spell .hotkey{position:absolute;left:-2px;bottom:-6px;color:#f2f5f5;font-size:18px;z-index:2;text-shadow:2px 2px 0 #2d2136,-2px -2px 0 #2d2136,2px -2px 0 #2d2136,-2px 2px 0 #2d2136,2px 2px 0 #2d2136}.uiSpells .spell .hotkey.no-mana{color:#d43346}.mobile .uiSpells{top:auto;bottom:10px;padding:0px;height:auto;background-color:transparent;display:flex;flex-direction:column-reverse}.mobile .uiSpells .spell{position:relative;margin:10px 0px 0px 0px;background-color:rgba(58,59,74,0.9);border:none}.mobile .uiSpells .spell .hotkey{display:none}.mobile .uiSpells .spell.active:before{content:'';width:5px;height:100%;left:-5px;top:0%;background-color:#4ac441;position:absolute}

+ 1
- 0
src/client/ui/templates/stash/styles.css View File

@@ -0,0 +1 @@
.uiStash.scrolls{width:834px}.uiStash.scrolls .grid{overflow-y:auto;width:825px}.uiStash .footer{color:#3fa7dd;background-color:#373041;padding-bottom:7px}

+ 1
- 0
src/client/ui/templates/talk/styles.css View File

@@ -0,0 +1 @@
.uiTalk{border:5px solid rgba(60,63,76,0.9);width:400px;display:none;text-align:center;position:absolute;top:10px}.uiTalk .source{padding:5px 5px;background-color:rgba(60,63,76,0.9)}.uiTalk .source .name{color:#48edff;margin-bottom:5px}.uiTalk .source .msg{color:#f2f5f5;line-height:22px}.uiTalk .options{padding:5px;background-color:rgba(55,48,65,0.9)}.uiTalk .options .option{color:#3fa7dd;padding:5px 5px;cursor:pointer;line-height:22px}.uiTalk .options .option:hover{color:#f2f5f5}

+ 1
- 0
src/client/ui/templates/target/styles.css View File

@@ -0,0 +1 @@
.uiTarget{position:absolute;left:406px;top:10px;width:calc(200px + (8px * 2));padding:8px;background-color:rgba(58,59,74,0.9);display:none}.uiTarget .boxes{width:100%;float:left;height:100%}.uiTarget .boxes .infoBox{color:#f2f5f5;width:100%;height:18px;margin-bottom:5px;position:relative}.uiTarget .boxes .infoBox>*{float:left}.uiTarget .boxes .infoBox .infoName{width:70%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.uiTarget .boxes .infoBox .infoLevel{width:30%;text-align:right}.uiTarget .boxes .infoBox .infoLevel.high-level{text-shadow:2px 0 #d43346,-2px 0 #d43346,0 -2px #d43346,0 2px #d43346,-2px -2px #d43346,-2px 2px #d43346,2px -2px #d43346,2px 2px #d43346}.uiTarget .boxes .statBox{width:100%;height:18px;margin-bottom:5px;position:relative}.uiTarget .boxes .statBox:nth-child(3){margin-bottom:0px}.uiTarget .boxes .statBox:last-child{display:none;position:absolute;margin-left:-8px;margin-top:13px;background-color:#69696e}.uiTarget .boxes .statBox [class^="stat"]{position:absolute;left:0px;top:0px;height:100%}.uiTarget .boxes .statBox .text{position:absolute;left:0px;top:0px;width:100%;height:100%;text-align:center;color:#f2f5f5;padding:2px 0px;text-shadow:2px 0 #2d2136,-2px 0 #2d2136,0 -2px #2d2136,0 2px #2d2136,-2px -2px #2d2136,-2px 2px #2d2136,2px -2px #2d2136,2px 2px #2d2136}.uiTarget .boxes .statBox:nth-child(2){background-color:#802343}.uiTarget .boxes .statBox:nth-child(3){background-color:#42548d}.uiTarget .boxes .statBox:nth-child(4){background-color:#763b3b}.uiTarget .boxes .statBox .statHp{background-color:#d43346}.uiTarget .boxes .statBox .statMana{background-color:#3fa7dd}.uiTarget .boxes .statBox .statCasting{background-color:#faac45}.mobile .uiTarget{left:10px;top:100px}

+ 1
- 0
src/client/ui/templates/terms/styles.css View File

@@ -0,0 +1 @@
.uiTerms{display:none;width:562px;height:700px;margin-top:-80px;display:flex;flex-direction:column;align-items:center}.uiTerms>*{flex-shrink:0}.uiTerms .logo{width:562px;height:200px;margin-bottom:24px;filter:drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136)}.uiTerms .heading{color:#ff6942;filter:drop-shadow(0 -4px 0 #312136) drop-shadow(0 4px 0 #312136) drop-shadow(4px 0 0 #312136) drop-shadow(-4px 0 0 #312136);margin-bottom:24px}.uiTerms .content{flex:1;width:100%;overflow-y:auto;background-color:#373041;border:5px solid #3c3f4c;padding:10px;color:#c0c3cf;text-align:justify}.uiTerms .content .title{color:#f2f5f5}.uiTerms .buttons{width:100%;background-color:#373041;display:flex;justify-content:space-between;border-style:solid;border-color:#3c3f4c;border-width:0px 5px 5px 5px;padding:5px}.uiTerms .buttons .btn{background-color:#3a71ba;color:#f2f5f5;height:35px;padding-left:10px;padding-right:10px}.uiTerms .buttons .btn:hover{background-color:#3fa7dd}.uiTerms a{color:#3fa7dd}.uiTerms a:hover{color:#48edff}.mobile .uiTerms{height:80%;margin-top:0px}

+ 1
- 0
src/client/ui/templates/tooltipInfo/styles.css View File

@@ -0,0 +1 @@
.uiTooltipInfo{position:absolute;right:11px;bottom:164px;background-color:rgba(55,48,65,0.9);padding:8px 32px;line-height:20px;color:#f2f5f5;text-align:center}

+ 1
- 0
src/client/ui/templates/tooltipItem/styles.css View File

@@ -0,0 +1 @@
.uiTooltipItem>*{z-index:999999}.uiTooltipItem .tooltip{display:none;position:absolute;margin-left:-4px;margin-top:-4px;background-color:rgba(60,63,76,0.95);pointer-events:none;padding:10px;color:#f2f5f5;text-align:center;line-height:18px;max-width:300px}.uiTooltipItem .tooltip .name{margin-bottom:8px}.uiTooltipItem .tooltip .name .type{color:#7f9c9c}.uiTooltipItem .tooltip .name .power{color:#80f643;display:none}.uiTooltipItem .tooltip>.implicitStats{color:#b8c9c9;margin-bottom:8px;padding-bottom:8px;border-bottom:2px solid #505360}.uiTooltipItem .tooltip>.implicitStats .gainStat{color:#80f643}.uiTooltipItem .tooltip>.implicitStats .loseStat{color:#d43346}.uiTooltipItem .tooltip>.stats{color:#b8c9c9;margin-bottom:8px;padding-bottom:8px;border-bottom:2px solid #505360}.uiTooltipItem .tooltip>.stats .gainStat{color:#80f643}.uiTooltipItem .tooltip>.stats .loseStat{color:#d43346}.uiTooltipItem .tooltip>.stats .enchanted{color:#44cb95 !important}.uiTooltipItem .tooltip .effects{color:#f2f5f5;margin-bottom:8px}.uiTooltipItem .tooltip .faction{color:#7f9c9c;margin-bottom:8px}.uiTooltipItem .tooltip .requires{margin-top:8px;color:#7f9c9c}.uiTooltipItem .tooltip .requires.high-level{color:#d43346}.uiTooltipItem .tooltip .requires .high-level{color:#d43346}.uiTooltipItem .tooltip .requires>*:not(.high-level){color:#7f9c9c}.uiTooltipItem .tooltip .material{color:#7f9c9c;display:none}.uiTooltipItem .tooltip .quest{color:#7f9c9c;display:none}.uiTooltipItem .tooltip .info{color:#4f6666}.uiTooltipItem .tooltip .damage .gainDamage{color:#80f643}.uiTooltipItem .tooltip .damage .loseDamage{color:#d43346}.uiTooltipItem .tooltip .worth{display:none}.uiTooltipItem .tooltip .worth.no-afford{color:#d43346}.uiTooltipItem .tooltip.no-compare .info{display:none}.uiTooltipItem>.btn{position:absolute;background-color:#3fa7dd;color:#f2f5f5;padding:10px;text-align:center}.mobile .uiTooltipItem{z-index:9999999999}.mobile .uiTooltipItem>*{z-index:9999999999}

+ 1
- 0
src/client/ui/templates/tooltips/styles.css View File

@@ -0,0 +1 @@
.uiTooltips{z-index:4}.uiTooltips .tooltip{display:none;z-index:4;background-color:rgba(58,59,74,0.85);position:absolute;padding:8px;color:#f2f5f5;text-align:justify}.uiTooltips .tooltip.bright{background-color:#c0c3cf;color:#2d2136}.uiTooltips .tooltip .hidden{display:none}

+ 1
- 0
src/client/ui/templates/trade/styles.css View File

@@ -0,0 +1 @@
.uiTrade{width:auto}.uiTrade .no-afford{position:absolute;left:5px;top:5px;width:calc(100% - 10px);height:calc(100% - 10px);background-color:rgba(212,51,70,0.15)}.uiTrade .grid .item .icon.skin{width:16px;height:16px;transform:scale(4);transform-origin:0 0;image-rendering:optimizeSpeed;image-rendering:pixelated;image-rendering:crisp-edges}

+ 1
- 0
src/client/ui/templates/wardrobe/styles.css View File

@@ -0,0 +1 @@
.uiWardrobe{display:none;width:600px;height:350px;border:5px solid #3c3f4c;text-align:center;background-color:#373041}.uiWardrobe>.heading{color:#48edff;width:100%;height:36px;background-color:#3c3f4c;position:relative}.uiWardrobe>.heading .heading-text{padding-top:8px;margin:auto}.uiWardrobe .bottom{height:calc(100% - 36px)}.uiWardrobe .bottom .left,.uiWardrobe .bottom .right{float:left;height:100%}.uiWardrobe .bottom .left{width:calc(100% * .4)}.uiWardrobe .bottom .left .heading{width:100%;height:30px;color:#ffeb38;padding-top:7px}.uiWardrobe .bottom .left .list{width:100%;height:calc(100% - 30px);overflow:auto}.uiWardrobe .bottom .left .list .skinName{width:100%;cursor:pointer;color:white;padding:5px}.uiWardrobe .bottom .left .list .skinName:hover{background-color:#3c3f4c}.uiWardrobe .bottom .left .list .skinName.active{color:#48edff}.uiWardrobe .bottom .left .list .skinName.current:after{content:' (x)'}.uiWardrobe .bottom .right{width:calc(100% * .6);position:relative;background-color:#2d2136}.uiWardrobe .bottom .right .heading{width:100%;height:30px;color:#ffeb38;padding-top:7px}.uiWardrobe .bottom .right .preview{padding-left:calc((100% - (8 * 16px)) / 2);position:relative;height:calc(100% - 80px)}.uiWardrobe .bottom .right .preview .sprite{width:8px;height:8px;position:absolute;left:calc((100% - (8 * 16px)) / 2);top:calc((100% - (8 * 16px)) / 2);transform:scale(16);transform-origin:0% 0%;image-rendering:optimizeSpeed;image-rendering:pixelated;image-rendering:crisp-edges}.uiWardrobe .bottom .right .buttons{width:100%;padding:0px 10px;height:50px}.uiWardrobe .bottom .right .buttons .btn{float:left;width:calc((100% - 50px) / 2);height:40px;padding-top:12px;color:white;cursor:pointer}.uiWardrobe .bottom .right .buttons .btn:first-child{margin-right:50px}.uiWardrobe .bottom .right .buttons .btnCancel{background-color:#d43346}.uiWardrobe .bottom .right .buttons .btnCancel:hover{background-color:#ff4252}.uiWardrobe .bottom .right .buttons .btnApply{background-color:#4ac441}.uiWardrobe .bottom .right .buttons .btnApply:hover{background-color:#80f643}

+ 1
- 0
src/client/ui/templates/workbench/styles.css View File

@@ -0,0 +1 @@
.uiWorkbench{display:none;width:827px;height:447px;border:5px solid #3c3f4c;color:#f2f5f5;position:relative;z-index:2}.uiWorkbench>.heading,.uiWorkbench>.itemPicker>.heading{color:#ff6942;width:100%;height:36px;background-color:#3c3f4c;text-align:center}.uiWorkbench>.heading .heading-text,.uiWorkbench>.itemPicker>.heading .heading-text{padding-top:8px;margin:auto}.uiWorkbench>.bottom{background-color:#373041;height:calc(100% - 36px);width:100%}.uiWorkbench>.bottom .heading{color:#3fa7dd;margin-bottom:10px;text-align:center}.uiWorkbench>.bottom .left,.uiWorkbench>.bottom .right{float:left;height:100%;padding:10px}.uiWorkbench>.bottom .left{width:300px}.uiWorkbench>.bottom .left .list{height:calc(100% - 25px);overflow-y:auto;display:flex;flex-direction:column}.uiWorkbench>.bottom .left .list .item{width:100%;padding:5px 10px;cursor:pointer;color:#c0c3cf}.uiWorkbench>.bottom .left .list .item.selected{background-color:#3c3f4c;color:#f2f5f5}.uiWorkbench>.bottom .left .list .item:hover{background-color:#3c3f4c}.uiWorkbench>.bottom .right{width:calc(100% - 300px);display:flex;flex-direction:column;justify-content:space-between}.uiWorkbench>.bottom .right>*{width:100%}.uiWorkbench>.bottom .right .info{height:calc(100% - 100px - 35px);flex:1}.uiWorkbench>.bottom .right .info .title{color:#3fa7dd;padding-bottom:10px;text-align:center}.uiWorkbench>.bottom .right .info .description{color:#c0c3cf;text-align:justify}.uiWorkbench>.bottom .right .materialList{visibility:hidden;margin-bottom:20px}.uiWorkbench>.bottom .right .materialList .material.need{color:#d43346}.uiWorkbench>.bottom .right .needItems{display:none;margin-bottom:20px;flex-direction:column}.uiWorkbench>.bottom .right .needItems .title{color:#3fa7dd;padding-bottom:10px;text-align:center}.uiWorkbench>.bottom .right .needItems .list{display:flex;justify-content:space-around}.uiWorkbench>.bottom .right .buttons{height:40px}.uiWorkbench>.bottom .right .buttons>.btn{height:35px;width:100px;color:#f2f5f5;text-align:center;padding-top:10px;background-color:#3c3f4c;cursor:pointer;float:left}.uiWorkbench>.bottom .right .buttons>.btn:last-child{float:right}.uiWorkbench>.bottom .right .buttons>.btn:hover{background-color:#505360}.uiWorkbench>.itemPicker{display:none;position:absolute;left:0px;top:0px;width:100%;height:100%;background-color:#373041;flex-direction:column}.uiWorkbench>.itemPicker .list{display:flex;flex-wrap:wrap;overflow-y:auto}.mobile .uiWorkbench{z-index:2}

+ 36
- 0
src/server/combat/avoid.js View File

@@ -0,0 +1,36 @@
//Prebound Methods
const mathRandom = Math.random.bind(Math);

//Helpers
const willBlock = ({ isAttack, tgtValues: { blockAttackChance, blockSpellChance } }) => {
const blockChance = isAttack ? blockAttackChance : blockSpellChance;
const roll = mathRandom() * 100;

const result = roll < blockChance;

return result;
};

const willDodge = ({ isAttack, tgtValues: { dodgeAttackChance, dodgeSpellChance } }) => {
const dodgeChance = isAttack ? dodgeAttackChance : dodgeSpellChance;
const roll = mathRandom() * 100;

const result = roll < dodgeChance;

return result;
};

//Method
const avoid = (config, result) => {
//Heals, among other things, should not be avoided
if (config.noMitigate)
return;

result.blocked = willBlock(config);

if (!result.blocked)
result.dodged = willDodge(config);
};

//Exports
module.exports = avoid;

+ 36
- 88
src/server/combat/combat.js View File

@@ -1,91 +1,39 @@
let max = Math.max.bind(Math);
let mathRandom = Math.random.bind(Math);
//Imports
const avoid = require('./avoid');
const scale = require('./scale');
const mitigate = require('./mitigate');

//Method
const getDamage = config => {
const { damage, element } = config;

//Add convenience properties
config.srcValues = config.source.stats.values;
config.tgtValues = config.target.stats.values;
if (element)
config.elementName = `element${element[0].toUpperCase()}${element.substr(1)}`;

const result = {
amount: damage,
blocked: false,
dodged: false,
crit: false,
element
};

avoid(config, result);
scale(config, result);
mitigate(config, result);

//Remove convenience properties
delete config.srcValues;
delete config.tgtValues;
delete config.elementName;

return result;
};

//Exports
module.exports = {
getDamage: function (config) {
let srcValues = config.source.stats.values;
let tgtValues = config.target.stats.values;

let amount = config.damage;
let blocked = false;
let dodged = false;
let isCrit = false;

//Don't block heals
if (!config.noMitigate) {
let blockChance = config.isAttack ? tgtValues.blockAttackChance : tgtValues.blockSpellChance;
if (mathRandom() * 100 < blockChance) {
blocked = true;
amount = 0;
}

if (!blocked) {
let dodgeChance = config.isAttack ? tgtValues.dodgeAttackChance : tgtValues.dodgeSpellChance;
if (mathRandom() * 100 < dodgeChance) {
dodged = true;
amount = 0;
}
}
}

if (!blocked && !dodged && !config.noScale) {
if (config.statType) {
let statValue = 0;
let statType = config.statType;
if (!(statType instanceof Array))
statType = [statType];

statType.forEach(function (s) {
statValue += srcValues[s];
});

statValue = max(1, statValue);
let statMult = config.statMult || 1;

amount *= statValue * statMult;
}

let dmgPercent = 100 + (srcValues.dmgPercent || 0);

if (config.isAttack)
dmgPercent += srcValues.physicalPercent || 0;
else
dmgPercent += srcValues.spellPercent || 0;

if (config.element) {
let elementName = 'element' + config.element[0].toUpperCase() + config.element.substr(1);
dmgPercent += (srcValues[elementName + 'Percent'] || 0);

//Don't mitigate heals
if (!config.noMitigate) {
let resist = tgtValues[elementName + 'Resist'] || 0;
amount *= max(0.5 + max((1 - (resist / 100)) / 2, -0.5), 0.5);
}
} else if (!config.noMitigate)
amount *= max(0.5 + max((1 - ((tgtValues.armor || 0) / (srcValues.level * 50))) / 2, -0.5), 0.5);

amount *= (dmgPercent / 100);

if (!config.noCrit) {
let critChance = srcValues.critChance;
critChance += (config.isAttack) ? srcValues.attackCritChance : srcValues.spellCritChance;

let critMultiplier = srcValues.critMultiplier;
critMultiplier += (config.isAttack) ? srcValues.attackCritMultiplier : srcValues.spellCritMultiplier;

if ((config.crit) || (mathRandom() * 100 < critChance)) {
isCrit = true;
amount *= (critMultiplier / 100);
}
}
}

return {
amount,
blocked,
dodged,
crit: isCrit,
element: config.element
};
}
getDamage
};

+ 56
- 0
src/server/combat/mitigate.js View File

@@ -0,0 +1,56 @@
//Prebound Methods
const max = Math.max.bind(Math);
const pow = Math.pow.bind(Math);

//Helpers
const mitigateResistances = ({ elementName, noMitigate, tgtValues }, result) => {
//Don't mitigate physical damage
if (!elementName)
return;

const resist = tgtValues[elementName + 'Resist'] || 0;

const resistanceMultiplier = max(0.5 + max((1 - (resist / 100)) / 2, -0.5), 0.5);

result.amount *= resistanceMultiplier;
};

const mitigateArmor = ({ element, tgtValues, srcValues }, result) => {
//Don't mitigate elemental damage
if (element)
return;

const armorMultiplier = max(0.5 + max((1 - ((tgtValues.armor || 0) / (srcValues.level * 50))) / 2, -0.5), 0.5);

result.amount *= armorMultiplier;
};

const mitigatePvp = ({ source, target, srcValues }, result) => {
const isPvp = (
(source.player || (source.follower && source.follower.master && source.follower.master.player)) &&
(target.player || (target.follower && target.follower.master && target.follower.master.player))
);

if (!isPvp)
return;

const multiplier = 1 / pow(2, srcValues.level / 5);

result.amount *= multiplier;
};

//Method
const mitigate = (config, result) => {
const { blocked, dodged } = result;

//Heals, among other things, should not be mitigated
if (blocked || dodged || config.noMitigate)
return;

mitigateResistances(config, result);
mitigateArmor(config, result);
mitigatePvp(config, result);
};

//Exports
module.exports = mitigate;

+ 82
- 0
src/server/combat/scale.js View File

@@ -0,0 +1,82 @@
//Prebound Methods
const mathRandom = Math.random.bind(Math);
const max = Math.max.bind(Math);

//Helpers
const scaleStatType = (config, result) => {
const { statType, statMult = 1, srcValues } = config;

if (!statType)
return;
let statValue = 0;

if (!statType.push)
statValue = srcValues[statType];
else {
statType.forEach(s => {
statValue += srcValues[s];
});
}

statValue = max(1, statValue);

result.amount *= statValue * statMult;
};

const scalePercentMultipliers = ({ isAttack, elementName, srcValues }, result) => {
const { dmgPercent = 0, physicalPercent = 0, spellPercent = 0 } = srcValues;

let totalPercent = 100 + dmgPercent;

if (isAttack)
totalPercent += physicalPercent;
else
totalPercent += spellPercent;

if (elementName)
totalPercent += (srcValues[elementName + 'Percent'] || 0);

result.amount *= (totalPercent / 100);
};

const scaleCrit = ({ noCrit, isAttack, crit: forceCrit, srcValues }, result) => {
if (noCrit)
return;

const { critChance, attackCritChance, spellCritChance } = srcValues;
const { critMultiplier, attackCritMultiplier, spellCritMultiplier } = srcValues;

let totalCritChance = critChance;
let totalCritMultiplier = critMultiplier;

if (isAttack) {
totalCritChance += attackCritChance;
totalCritMultiplier += attackCritMultiplier;
} else {
totalCritChance += spellCritChance;
totalCritMultiplier += spellCritMultiplier;
}

const didCrit = forceCrit || mathRandom() * 100 < totalCritChance;

if (didCrit) {
result.crit = true;
result.amount *= (totalCritMultiplier / 100);
}
};

//Method
const scale = (config, result) => {
const { blocked, dodged } = result;

if (blocked || dodged || config.noScale)
return;

scaleStatType(config, result);
scalePercentMultipliers(config, result);
scaleCrit(config, result);
};

//Exports
module.exports = scale;

+ 6
- 0
src/server/components/door.js View File

@@ -129,6 +129,8 @@ module.exports = {
const message = `The ${key.name} disintegrates on use`;
obj.social.notifySelf({ message });
}

this.obj.instance.syncer.queue('onDoorUnlock', null, [obj.serverId]);
}

if (this.closed) {
@@ -139,6 +141,8 @@ module.exports = {

this.closed = false;
this.enterArea(obj);

this.obj.instance.syncer.queue('onDoorOpen', null, [obj.serverId]);
} else {
thisObj.cell = this.closedSprite;
syncO.cell = this.closedSprite;
@@ -147,6 +151,8 @@ module.exports = {

this.closed = true;
this.enterArea(obj);

this.obj.instance.syncer.queue('onDoorClose', null, [obj.serverId]);
}
},



+ 97
- 33
src/server/components/mob.js View File

@@ -16,6 +16,71 @@ const teleportHome = (physics, obj, mob) => {
obj.aggro.move();
};

const performPatrolAction = ({ obj }, node) => {
const { action } = node;

const { chatter, syncer, instance: { scheduler } } = obj;

if (action === 'chat') {
if (!chatter)
obj.addComponent('chatter');

syncer.set(false, 'chatter', 'msg', node.msg);

return true;
}

if (action === 'wait') {
if (node.cron) {
const isActive = scheduler.isActive(node);

return isActive;
} else if (node.ttl === undefined) {
node.ttl = node.duration;

return false;
}

node.ttl--;

if (!node.ttl) {
delete node .ttl;
return true;
}
}
};

const getNextPatrolTarget = mob => {
const { patrol, obj: { x, y } } = mob;
let toX, toY;

do {
const toNode = patrol[mob.patrolTargetNode];
if (toNode.action) {
const nodeDone = performPatrolAction(mob, toNode);
if (!nodeDone)
return true;

mob.patrolTargetNode++;
if (mob.patrolTargetNode >= patrol.length)
mob.patrolTargetNode = 0;

continue;
}

toX = toNode[0];
toY = toNode[1];
if ((toX - x === 0) && (toY - y === 0)) {
mob.patrolTargetNode++;
if (mob.patrolTargetNode >= patrol.length)
mob.patrolTargetNode = 0;
} else
break;
} while (toX - x !== 0 || toY - y !== 0);

return [ toX, toY ];
};

module.exports = {
type: 'mob',

@@ -112,17 +177,13 @@ module.exports = {
toX = this.originX + ~~(rnd() * (walkDistance * 2)) - walkDistance;
toY = this.originY + ~~(rnd() * (walkDistance * 2)) - walkDistance;
} else if (this.patrol) {
do {
let toNode = this.patrol[this.patrolTargetNode];
toX = toNode[0];
toY = toNode[1];
if ((toX - obj.x === 0) && (toY - obj.y === 0)) {
this.patrolTargetNode++;
if (this.patrolTargetNode >= this.patrol.length)
this.patrolTargetNode = 0;
} else
break;
} while (toX - obj.x !== 0 || toY - obj.y !== 0);
const patrolResult = getNextPatrolTarget(this);

//When an action is performed, we will only get a boolean value back
if (!patrolResult.push)
return;

[toX, toY] = patrolResult;
}

//We use goHome to force followers to follow us around but they should never stay in that state
@@ -135,7 +196,8 @@ module.exports = {

if (dx + dy === 0)
return;
else if (dx <= 1 && dy <= 1) {

if (dx <= 1 && dy <= 1) {
obj.queue({
action: 'move',
data: {
@@ -143,27 +205,29 @@ module.exports = {
y: toY
}
});
} else {
let path = this.physics.getPath({
x: obj.x,
y: obj.y
}, {
x: toX,
y: toY
}, false);

let pLen = path.length;
for (let i = 0; i < pLen; i++) {
let p = path[i];

obj.queue({
action: 'move',
data: {
x: p.x,
y: p.y
}
});
}

return;
}

const path = this.physics.getPath({
x: obj.x,
y: obj.y
}, {
x: toX,
y: toY
}, false);

const pLen = path.length;
for (let i = 0; i < pLen; i++) {
let p = path[i];

obj.queue({
action: 'move',
data: {
x: p.x,
y: p.y
}
});
}
},



+ 6
- 0
src/server/components/player.js View File

@@ -229,6 +229,12 @@ module.exports = {
syncer.o.x = obj.x;
syncer.o.y = obj.y;

obj.effects.addEffect({
type: 'invulnerability',
force: true,
ttl: 28
});

obj.aggro.move();

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


+ 10
- 24
src/server/components/portal.js View File

@@ -1,4 +1,6 @@
let roles = require('../config/roles');
const roles = require('../config/roles');

const sendObjToZone = require('./portal/sendObjToZone');

module.exports = {
type: 'portal',
@@ -24,30 +26,14 @@ module.exports = {
return;
}

obj.fireEvent('beforeRezone');

obj.destroyed = true;

await obj.auth.doSave();

const simpleObj = obj.getSimple(true, false, true);

const { toPos, toRelativePos } = this;
if (toPos) {
simpleObj.x = this.toPos.x;
simpleObj.y = this.toPos.y;
} else if (toRelativePos) {
simpleObj.x = this.obj.x + toRelativePos.x;
simpleObj.y = this.obj.y + toRelativePos.y;
}
const { toZone: zoneName, toPos, toRelativePos } = this;

process.send({
method: 'rezone',
id: obj.serverId,
args: {
obj: simpleObj,
newZone: this.toZone
}
await sendObjToZone({
obj,
invokingObj: this,
zoneName,
toPos,
toRelativePos
});
}
};

+ 46
- 0
src/server/components/portal/sendObjToZone.js View File

@@ -0,0 +1,46 @@
const sendObjToZone = async ({ obj, invokingObj, zoneName, toPos, toRelativePos }) => {
const { instance: { physics, syncer: globalSyncer } } = obj;

if (obj.zoneName === zoneName) {
physics.removeObject(obj, obj.x, obj.y);

obj.x = toPos.x;
obj.y = toPos.y;

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

globalSyncer.queue('onRespawn', {
x: obj.x,
y: obj.y
}, [obj.serverId]);

return;
}

obj.fireEvent('beforeRezone');

obj.destroyed = true;

await obj.auth.doSave();

const simpleObj = obj.getSimple(true, false, true);

if (toPos) {
simpleObj.x = toPos.x;
simpleObj.y = toPos.y;
} else if (toRelativePos) {
simpleObj.x = invokingObj.obj.x + toRelativePos.x;
simpleObj.y = invokingObj.obj.y + toRelativePos.y;
}

process.send({
method: 'rezone',
id: obj.serverId,
args: {
obj: simpleObj,
newZone: zoneName
}
});
};

module.exports = sendObjToZone;

+ 4
- 37
src/server/components/stats.js View File

@@ -1,3 +1,6 @@
//Methods
const die = require('./stats/die');

let animations = require('../config/animations');
let spirits = require('../config/spirits');
let scheduler = require('../misc/scheduler');
@@ -460,43 +463,7 @@ module.exports = {
},

die: function (source) {
let obj = this.obj;
let values = this.values;

this.syncer.queue('onGetDamage', {
id: obj.id,
event: true,
text: 'death'
}, -1);

obj.syncer.set(true, null, 'dead', true);

let syncO = obj.syncer.o;

obj.hidden = true;
obj.nonSelectable = true;
syncO.hidden = true;
syncO.nonSelectable = true;

let xpLoss = ~~Math.min(values.xp, values.xpMax * 0.05);

values.xp -= xpLoss;
obj.syncer.setObject(true, 'stats', 'values', 'xp', values.xp);

this.syncer.queue('onDeath', {
source: source.name,
xpLoss: xpLoss
}, [obj.serverId]);

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

respawn: function () {


+ 62
- 0
src/server/components/stats/die.js View File

@@ -0,0 +1,62 @@
const loseXpOnDeath = ({ stats: { values }, syncer }, deathSource) => {
const { xp, xpMax, level } = values;

const noLoseXp = (
level < 5 ||
deathSource.player ||
(
deathSource.follower &&
deathSource.follower.master &&
deathSource.follower.master.player
)
);

if (noLoseXp)
return 0;
const xpLoss = ~~Math.min(xp, xpMax * 0.05);

values.xp -= xpLoss;
syncer.setObject(true, 'stats', 'values', 'xp', values.xp);

return xpLoss;
};

const die = (cpnStats, deathSource) => {
const { obj, syncer: syncerGlobal } = cpnStats;
const { x, y, serverId, syncer } = obj;

syncerGlobal.queue('onGetDamage', {
id: obj.id,
event: true,
text: 'death'
}, -1);

syncer.set(true, null, 'dead', true);

const syncO = syncer.o;

obj.hidden = true;
obj.nonSelectable = true;
syncO.hidden = true;
syncO.nonSelectable = true;

const xpLoss = loseXpOnDeath(obj, deathSource);

syncerGlobal.queue('onDeath', {
source: deathSource.name,
xpLoss: xpLoss
}, [serverId]);

syncerGlobal.queue('onGetObject', {
x,
y,
components: [{
type: 'attackAnimation',
row: 0,
col: 4
}]
}, -1);
};

module.exports = die;

+ 36
- 4
src/server/config/clientConfig.js View File

@@ -33,11 +33,15 @@ const config = {
'bosses',
'auras'
],
atlasTextureDimensions: {},
atlasTextures: [
'tiles',
'walls',
'objects'
],
blockingTileIndices: [
6, 7, 54, 55, 62, 63, 154, 189, 190, 192, 193, 194, 195, 196, 197
],
tileOpacities: {
default: {
default: 0.4,
@@ -196,8 +200,6 @@ const config = {
module.exports = {
config,

atlasTextureDimensions: {},

init: async function () {
events.emit('onBeforeGetClientConfig', config);

@@ -213,14 +215,44 @@ module.exports = {

//The client needs to know this as well as the map loader
calculateAtlasTextureDimensions: async function () {
for (const tex of config.atlasTextures) {
const { atlasTextures, atlasTextureDimensions } = config;

for (const tex of atlasTextures) {
if (atlasTextureDimensions[tex])
return;

const path = tex.includes('.png') ? `../${tex}` : `../client/images/${tex}.png`;
const dimensions = await imageSize(path);

delete dimensions.type;

this.atlasTextureDimensions[tex] = dimensions;
atlasTextureDimensions[tex] = dimensions;
}
},

getTileIndexInAtlas: async function (spriteSheet, tileIndexInSource) {
const { atlasTextures, atlasTextureDimensions } = config;

//We need to perform this check because once mods start adding sheets to atlasTextures,
// things get out of control. We need to fix this in the future as it will become screwy.
if (Object.keys(atlasTextureDimensions).length !== atlasTextures)
await this.calculateAtlasTextureDimensions();

const indexOfSheet = atlasTextures.indexOf(spriteSheet);

let tileCountBeforeSheet = 0;

for (let i = 0; i < indexOfSheet; i++) {
const sheet = atlasTextures[i];
const { width, height } = atlasTextureDimensions[sheet];

tileCountBeforeSheet += ((width / 8) * (height / 8));
}

//Tile index 0 is 'no tile' in map files so we need to increment by 1
const result = tileCountBeforeSheet + tileIndexInSource + 1;

return result;
},

//Used to send to clients


+ 14
- 0
src/server/config/effects/effectInvulnerability.js View File

@@ -0,0 +1,14 @@
module.exports = {
type: 'invulnerability',

events: {
beforeDealDamage: function (damage) {
if (damage)
damage.amount = 0;
},

beforeTakeDamage: function (damage, source) {
damage.amount = 0;
}
}
};

+ 15
- 7
src/server/config/maps/cave/map.json View File

@@ -1,4 +1,12 @@
{ "backgroundcolor":"#32222e",
"compressionlevel":-1,
"editorsettings":
{
"export":
{
"target":"."
}
},
"height":200,
"infinite":false,
"layers":[
@@ -2339,7 +2347,7 @@
{
"name":"cpnPortal",
"type":"string",
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}"
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}"
}],
"rotation":0,
"type":"",
@@ -2375,7 +2383,7 @@
{
"name":"cpnPortal",
"type":"string",
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}"
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}"
}],
"rotation":0,
"type":"",
@@ -2393,7 +2401,7 @@
{
"name":"cpnPortal",
"type":"string",
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}"
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}"
}],
"rotation":0,
"type":"",
@@ -2411,7 +2419,7 @@
{
"name":"cpnPortal",
"type":"string",
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}"
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}"
}],
"rotation":0,
"type":"",
@@ -2429,7 +2437,7 @@
{
"name":"cpnPortal",
"type":"string",
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}"
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}"
}],
"rotation":0,
"type":"",
@@ -2465,7 +2473,7 @@
{
"name":"cpnPortal",
"type":"string",
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}"
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}"
}],
"rotation":0,
"type":"",
@@ -2500,7 +2508,7 @@
"value":"[{\"x\":16,\"y\":136}, {\"source\": \"radulos\", \"x\": 163, \"y\": 35}]"
}],
"renderorder":"right-down",
"tiledversion":"1.2.5",
"tiledversion":"1.3.3",
"tileheight":8,
"tilesets":[
{


+ 0
- 23
src/server/config/maps/fjolarok/chats.js View File

@@ -1,27 +1,4 @@
module.exports = {
estrid: [{
msg: '*polishes some vials*'
}, {
msg: 'Where did I put that flask?'
}, {
msg: '*coughs*'
}, {
msg: 'Hm...'
}],
priest: [{
msg: 'peace be with you'
}, {
msg: 'walk in the light'
}],
cow: [{
msg: '*moos*'
}],
goat: [{
msg: '*bleats*'
}],
pig: [{
msg: '*oinks*'
}],
hermit: [{
msg: '*strokes his beard*'
}, {


+ 0
- 163
src/server/config/maps/fjolarok/dialogues.js View File

@@ -106,168 +106,5 @@ module.exports = {
targetName: 'hermit'
}]
}
},
estrid: {
1: {
msg: [{
msg: 'Is there anything I can help you with today?',
options: [1.1, 1.3, 1.4, 1.5]
}],
options: {
1.1: {
msg: 'How long have you been working here?',
goto: 2
},
1.3: {
msg: 'I\'d like to browse your wares.',
goto: 'tradeBuy'
},
1.4: {
msg: 'I have some items to sell',
goto: 'tradeSell'
},
1.5: {
msg: 'I want to buy something back',
goto: 'tradeBuyback'
}
}
},
2: {
msg: 'I haven\'t been working here long, but I was born and raised here by my mother. She ran the shop before me.',
options: {
2.1: {
msg: 'Where is your mother now?',
goto: '2-1'
},
2.2: {
msg: 'I\'d like to ask something else.',
goto: 1
}
}
},
'2-1': {
msg: 'A few months ago, she...took ill. She\'s been bedridden upstairs ever since.',
options: {
'2-1.1': {
msg: 'I\'d like to ask something else.',
goto: 1
}
}
},
tradeBuy: {
cpn: 'trade',
method: 'startBuy',
args: [{
targetName: 'estrid'
}]
},
tradeSell: {
cpn: 'trade',
method: 'startSell',
args: [{
targetName: 'estrid'
}]
},
tradeBuyback: {
cpn: 'trade',
method: 'startBuyback',
args: [{
targetName: 'estrid'
}]
}
},
vikar: {
1: {
msg: [{
msg: 'How may I help you today?',
options: []
}]
}
},
asvald: {
1: {
msg: [{
msg: 'Is there anything I can help you with today?',
options: [1.1, 1.2, 1.3]
}],
options: {
1.1: {
msg: 'I would like to buy some runes',
goto: 'tradeBuy'
},
1.2: {
msg: 'I have some items I would like to sell',
goto: 'tradeSell'
},
1.3: {
msg: 'Could I see the items I sold to you?',
goto: 'tradeBuyback'
}
}
},
tradeBuy: {
cpn: 'trade',
method: 'startBuy',
args: [{
targetName: 'asvald'
}]
},
tradeSell: {
cpn: 'trade',
method: 'startSell',
args: [{
targetName: 'asvald'
}]
},
tradeBuyback: {
cpn: 'trade',
method: 'startBuyback',
args: [{
targetName: 'asvald'
}]
}
},
priest: {
1: {
msg: [{
msg: 'Is there anything I can help you with today?',
options: [1.1, 1.2, 1.3]
}],
options: {
1.1: {
msg: 'I\'d like to browse your wares.',
goto: 'tradeBuy'
},
1.2: {
msg: 'I have some items to sell',
goto: 'tradeSell'
},
1.3: {
msg: 'I want to buy something back',
goto: 'tradeBuyback'
}
}
},
tradeBuy: {
cpn: 'trade',
method: 'startBuy',
args: [{
targetName: 'priest'
}]
},
tradeSell: {
cpn: 'trade',
method: 'startSell',
args: [{
targetName: 'priest'
}]
},
tradeBuyback: {
cpn: 'trade',
method: 'startBuyback',
args: [{
targetName: 'priest'
}]
}
}
};

+ 383
- 1736
src/server/config/maps/fjolarok/map.json
File diff suppressed because it is too large
View File


+ 7
- 693
src/server/config/maps/fjolarok/zone.js View File

@@ -13,30 +13,6 @@ module.exports = {
}
},
objects: {
'sun carp school': {
max: 9,
type: 'fish',
quantity: [6, 12]
},
shopestrid: {
properties: {
cpnNotice: {
actions: {
enter: {
cpn: 'dialogue',
method: 'talk',
args: [{
targetName: 'estrid'
}]
},
exit: {
cpn: 'dialogue',
method: 'stopTalk'
}
}
}
}
},
shophermit: {
properties: {
cpnNotice: {
@@ -56,406 +32,10 @@ module.exports = {
}
}
},
shopvikar: {
properties: {
cpnNotice: {
actions: {
enter: {
cpn: 'dialogue',
method: 'talk',
args: [{
targetName: 'vikar'
}]
},
exit: {
cpn: 'dialogue',
method: 'stopTalk'
}
}
}
}
},
shopasvald: {
properties: {
cpnNotice: {
actions: {
enter: {
cpn: 'dialogue',
method: 'talk',
args: [{
targetName: 'asvald'
}]
},
exit: {
cpn: 'dialogue',
method: 'stopTalk'
}
}
}
}
},
shoppriest: {
properties: {
cpnNotice: {
actions: {
enter: {
cpn: 'dialogue',
method: 'talk',
args: [{
targetName: 'priest'
}]
},
exit: {
cpn: 'dialogue',
method: 'stopTalk'
}
}
}
}
},
gas: {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['c0c3cf', '80f643'],
end: ['386646', '69696e']
},
scale: {
start: {
min: 18,
max: 64
},
end: {
min: 8,
max: 24
}
},
speed: {
start: {
min: 2,
max: 6
},
end: {
min: 0,
max: 4
}
},
lifetime: {
min: 4,
max: 24
},
alpha: {
start: 0.05,
end: 0
},
randomScale: true,
randomSpeed: true,
chance: 0.02,
randomColor: true,
spawnType: 'rect',
blendMode: 'screen',
spawnRect: {
x: -80,
y: -80,
w: 160,
h: 160
}
}
};
}
}
}
},
greencandle: {
components: {
cpnLight: {
simplify: function () {
return {
type: 'light',
blueprint: {
color: {
start: ['80f643'],
end: ['4ac441', '51fc9a', 'd07840']
},
lifetime: {
min: 2,
max: 6
}
}
};
}
}
}
},
fountain: {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['48edff', '3fa7dd'],
end: ['3a71ba', '42548d']
},
scale: {
start: {
min: 2,
max: 10
},
end: {
min: 0,
max: 2
}
},
speed: {
start: {
min: 4,
max: 16
},
end: {
min: 2,
max: 8
}
},
lifetime: {
min: 2,
max: 5
},
randomScale: true,
randomSpeed: true,
chance: 0.8,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -10,
y: -21,
w: 20,
h: 8
}
}
};
}
}
}
},
alchgreenpot: {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['80f643', '80f643'],
end: ['4ac441', '4ac441']
},
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.1,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -15,
y: -20,
w: 30,
h: 8
}
}
};
}
}
}
},
alchredpot: {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['ff4252', 'ff4252'],
end: ['a82841', 'a82841']
},
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.2,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -15,
y: -28,
w: 30,
h: 8
}
}
};
}
}
}
},
'alchemy workbench': {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['ff4252', 'ff4252'],
end: ['a82841', 'a82841']
},
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.2,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -15,
y: -28,
w: 30,
h: 8
}
}
};
}
},
cpnWorkbench: {
type: 'alchemy'
}
}
},
etchbench: {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['ff4252', 'ff4252'],
end: ['a82841', 'a82841']
},
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.2,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -15,
y: -28,
w: 30,
h: 8
}
}
};
}
},
cpnWorkbench: {
type: 'etching'
}
}
'sun carp school': {
max: 9,
type: 'fish',
quantity: [6, 12]
},
fireplace: {
components: {
@@ -463,61 +43,6 @@ module.exports = {
type: 'cooking'
}
}
},
'enchanting shrine': {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['48edff', 'fc66f7'],
end: ['393268', '42548d']
},
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.2,
randomColor: true,
spawnType: 'rect',
spawnRect: {
x: -15,
y: -28,
w: 30,
h: 8
}
}
};
}
},
cpnWorkbench: {
type: 'enchanting'
}
}
}
},
mobs: {
@@ -557,7 +82,7 @@ module.exports = {
level: 2,
regular: {
drops: {
chance: 60,
chance: 55,
rolls: 1
}
},
@@ -573,7 +98,7 @@ module.exports = {
level: 3,
regular: {
drops: {
chance: 56,
chance: 50,
rolls: 1
}
},
@@ -607,7 +132,7 @@ module.exports = {
level: 4,
regular: {
drops: {
chance: 50,
chance: 45,
rolls: 1
}
},
@@ -623,7 +148,6 @@ module.exports = {
level: 5,
regular: {
drops: {
chance: 45,
rolls: 1
}
}
@@ -645,15 +169,6 @@ module.exports = {
name: 'The Pincer King'
}
},
'mud crab': {
level: 9
},
frog: {
level: 8,
rare: {
name: 'The Muck Prince'
}
},
eagle: {
level: 10,
regular: {
@@ -727,147 +242,6 @@ module.exports = {
}
}
},
guard: {
level: 20,
attackable: false,

walkDistance: 0,

rare: {
count: 0
}
},
estrid: {
level: 15,
attackable: false,
walkDistance: 5,

rare: {
count: 0
},

properties: {
cpnTrade: {
items: {
min: 0,
max: 0,
extra: [{
name: 'Empty Vial',
material: true,
sprite: [0, 9],
worth: 10,
infinite: true
}]
},
faction: {
id: 'fjolgard',
tier: 5
},
markup: {
buy: 0.25,
sell: 2.5
}
}
}
},
vikar: {
level: 15,
walkDistance: 0,
attackable: false,
rare: {
count: 0
}
},
luta: {
level: 15,
walkDistance: 0,
attackable: false,
rare: {
count: 0
}
},
asvald: {
level: 15,
walkDistance: 0,
attackable: false,
rare: {
count: 0
},

properties: {
cpnTrade: {
items: {
min: 0,
max: 0,
extra: [{
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'magic missile',
worth: 3
}, {
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'ice spear',
worth: 3
}, {
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'smite',
worth: 3
}, {
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'consecrate',
worth: 3
}, {
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'slash',
worth: 3
}, {
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'charge',
worth: 3
}, {
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'flurry',
worth: 3
}, {
generate: true,
spell: true,
quality: 0,
infinite: true,
spellName: 'smokebomb',
worth: 3
}]
},
faction: {
id: 'fjolgard',
tier: 5
},
markup: {
buy: 0.25,
sell: 10
}
}
}
},
rodriguez: {
attackable: false,
level: 10,
@@ -896,66 +270,6 @@ module.exports = {
count: 0
}
},
priest: {
level: 20,
attackable: false,
walkDistance: 0,
rare: {
count: 0
},

properties: {
cpnTrade: {
items: {
min: 5,
max: 10,
extra: [{
type: 'skin',
skinId: 'gaekatlan-druid',
worth: 100,
factions: [{
id: 'gaekatla',
tier: 7
}]
}, {
worth: 100,
infinite: true,
generate: true,
name: 'Cowl of Obscurity',
level: [4, 13],
quality: 4,
noSpell: true,
slot: 'head',
sprite: [2, 0],
spritesheet: '../../../images/legendaryItems.png',
type: 'Silk Cowl',
description: 'Imbued with the powers of Gaekatla herself.',
stats: ['hpMax', 'hpMax', 'int', 'int'],
effects: [{
type: 'healOnCrit',
rolls: {
i_chance: [20, 60],
i_percentage: [3, 7]
}
}],
factions: [{
id: 'gaekatla',
tier: 7
}]
}]
},
faction: {
id: 'gaekatla',
tier: 5
},
markup: {
buy: 0.25,
sell: 10
}
}
}
},

sundfehr: {
level: 9,
walkDistance: 0,


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

@@ -5,10 +5,6 @@ let config = [
name: 'cave',
path: 'config/maps'
},
{
name: 'sewer',
path: 'config/maps'
},
{
name: 'fjolarok',
path: 'config/maps'


+ 0
- 291
src/server/config/maps/sewer/events/plagueOfRats.js View File

@@ -1,291 +0,0 @@
const { mobs: { rat: { level, faction, grantRep, regular: { drops } } } } = require('../zone');
const rewardGenerator = require('../../../../misc/rewardGenerator');

const rewardConfig = {
regularKill: [{
name: 'Iron Bar',
sprite: [0, 0],
quality: 0,
quantity: 5,
chance: 10
}, {
name: 'Cloth Scrap',
sprite: [0, 1],
quality: 0,
quantity: 5,
chance: 10
}, {
name: 'Leather Scrap',
sprite: [0, 7],
quality: 0,
quantity: 5,
chance: 10
}, {
name: 'Muddy Runestone',
material: true,
quality: 0,
sprite: [6, 0],
spritesheet: 'images/materials.png',
chance: 1
}, {
name: 'Epic Essence',
material: true,
sprite: [0, 5],
quality: 3,
chance: 1
}, {
name: 'Rat Claw',
material: true,
sprite: [3, 0],
spritesheet: 'images/materials.png',
chance: 3
}]
};

const descriptionStrings = {
leadup: 'A bandit alchemist has been spotted in the sewer tunnels.',
active: 'Rats are swarming towards the city.',
success: 'Success: The rat invasion has been averted.',
failure: 'Failure: The rats have made it to the city.',
escapeCounter: 'Escapees: $ratEscapees$'
};

const config = {
cron: '0 */3 * * *',
//cron: '* * * * *',
idFirstSpawnPhase: 6,
idFailPhase: 19,
maxEscapees: 5,
repeats: [5, 5, 3],
//repeats: [1, 1, 1],
rewardItemsPerKill: 3
};

const ratTargetPos = {
x: 97,
y: 87
};

const rat = {
name: 'Bloodthirsty Rat',
cell: 24,
level,
faction,
grantRep,
drops,
hpMult: 3,
pos: {
x: 61,
y: 62
},
originX: ratTargetPos.x,
originY: ratTargetPos.y,
maxChaseDistance: 1000,
spells: [{
type: 'smokeBomb',
radius: 1,
duration: 20,
range: 2,
selfCast: 1,
statMult: 1,
damage: 0.125,
element: 'poison',
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: ['db5538', '4ac441'],
end: ['953f36', '386646']
},
chance: 0.125,
randomColor: true,
randomScale: true,
blendMode: 'screen',
spawnType: 'rect',
spawnRect: {
x: -10,
y: -10,
w: 20,
h: 20
}
}
}],
events: {
afterMove: function () {
const { obj: { x, y } } = this;
if (x !== ratTargetPos.x || y !== ratTargetPos.y)
return;

const eventManager = this.obj.instance.events;
const eventName = this.obj.event.config.name;

eventManager.incrementEventVariable(eventName, 'ratEscapees', 1);

const { active, escapeCounter } = descriptionStrings;
const newDesc = `${active}<br /><br />${escapeCounter}`;

eventManager.setEventDescription(eventName, newDesc);

this.obj.destroyed = true;

const totalEscapees = this.obj.event.variables.ratEscapees;
if (totalEscapees >= config.maxEscapees) {
const event = this.obj.event;
event.currentPhase.end = true;
event.nextPhase = config.idFailPhase;
}
},

afterDeath: function ({ source: { name } }) {
const eventManager = this.obj.instance.events;
const eventName = this.obj.event.config.name;

const itemCount = config.rewardItemsPerKill;
const rewards = rewardGenerator(itemCount, rewardConfig.regularKill);

eventManager.addParticipantRewards(eventName, name, rewards);
}
}
};

module.exports = {
name: 'Plague of Rats',
description: descriptionStrings.leadup,
distance: -1,
cron: config.cron,

phases: [{
type: 'spawnMob',
auto: true,
mobs: [{
id: 'banditAlchemist',
name: 'Bandit Alchemist',
attackable: false,
hpMult: 1,
cell: 79,
level: 15,
pos: {
x: 117,
y: 62
}
}]
}, {
type: 'moveMob',
id: 'banditAlchemist',
pos: {
x: 64,
y: 63
}
}, {
type: 'eventChain',
config: [{
type: 'mobTalk',
id: 'banditAlchemist',
text: 'I think I cracked it! The serum should finally be complete.',
delay: 15
}, {
type: 'mobTalk',
id: 'banditAlchemist',
text: 'One taste of this and the rats\' hunger for blood will be instatiable.',
delay: 15
}, {
type: 'mobTalk',
id: 'banditAlchemist',
text: '*pours a bubbling liquid into a rat nest*',
delay: 15
}, {
type: 'mobTalk',
id: 'banditAlchemist',
text: 'Let\'s see how Fjolgard handles a rat infestation of the bloodthirsty variety.',
delay: 15
}, {
type: 'mobTalk',
id: 'banditAlchemist',
text: '*laughs*',
delay: 15
}]
}, {
type: 'moveMob',
id: 'banditAlchemist',
pos: {
x: 117,
y: 63
},
auto: true
}, {
type: 'despawnMob',
id: 'banditAlchemist'
}, {
type: 'setDescription',
desc: descriptionStrings.active,
auto: true
}, {
type: 'spawnMob',
mobs: [rat],
auto: true
}, {
type: 'wait',
ttl: 15
}, {
type: 'goto',
gotoPhaseIndex: config.idFirstSpawnPhase,
repeats: config.repeats[0]
}, {
type: 'spawnMob',
mobs: [rat],
auto: true
}, {
type: 'wait',
ttl: 7
}, {
type: 'goto',
gotoPhaseIndex: config.idFirstSpawnPhase + 3,
repeats: config.repeats[1]
}, {
type: 'spawnMob',
mobs: [rat],
auto: true
}, {
type: 'wait',
ttl: 3
}, {
type: 'goto',
gotoPhaseIndex: config.idFirstSpawnPhase + 6,
repeats: config.repeats[2]
}, {
type: 'killAllMobs'
}, {
type: 'setDescription',
desc: descriptionStrings.success,
ttl: 100
}, {
type: 'giveRewards'
}, {
type: 'end'
}, {
type: 'setDescription',
desc: descriptionStrings.failure,
ttl: 100
}]
};

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


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

@@ -1,455 +0,0 @@
const balance = {
rat: {
clawChance: 3
},
giantRat: {
clawChance: 5
},
enragedRat: {
clawChance: 80
},
stinktooth: {
runestoneChance: 10,
recipeChance: 3,
shankChance: 0.2
},
bandit: {
keyChance: 1
},
direRat: {
clawChance: 7
},
bera: {
recipeChance: 3,
keyChance: 3
}
};

module.exports = {
name: 'The Fjolgard Sewer',
level: [11, 13],

resources: {
Stinkcap: {
type: 'herb',
max: 1
}
},

mobs: {
default: {
faction: 'fjolgard',
deathRep: -5
},

rat: {
faction: 'fjolgard',
grantRep: {
fjolgard: 6
},
level: 11,

regular: {
drops: {
rolls: 1,
noRandom: true,
alsoRandom: true,
blueprints: [{
chance: balance.rat.clawChance,
name: 'Rat Claw',
material: true,
sprite: [3, 0],
spritesheet: 'images/materials.png'
}]
}
},

rare: {
name: 'Enraged Rat',
cell: 24,

drops: {
rolls: 1,
noRandom: true,
alsoRandom: true,
blueprints: [{
chance: balance.enragedRat.clawChance,
name: 'Rat Claw',
material: true,
sprite: [3, 0],
spritesheet: 'images/materials.png'
}]
}
}
},

'giant rat': {
faction: 'fjolgard',
grantRep: {
fjolgard: 6
},
level: 12,

regular: {
hpMult: 2,
dmgMult: 1.2,

drops: {
rolls: 1,
noRandom: true,
alsoRandom: true,
blueprints: [{
chance: balance.giantRat.clawChance,
name: 'Rat Claw',
material: true,
sprite: [3, 0],
spritesheet: 'images/materials.png'
}]
}
},

rare: {
count: 0
}
},

stinktooth: {
faction: 'hostile',
grantRep: {
fjolgard: 15
},
level: 13,
spawnCd: 1714,

regular: {
hpMult: 10,
dmgMult: 3,

drops: {
rolls: 3,
chance: 100,
noRandom: true,
alsoRandom: true,
magicFind: [2000, 125],
blueprints: [{
chance: balance.stinktooth.shankChance,
name: 'Putrid Shank',
level: 13,
quality: 4,
slot: 'oneHanded',
type: 'Dagger',
spritesheet: '../../../images/legendaryItems.png',
sprite: [0, 1],
implicitStat: {
stat: 'lifeOnHit',
value: [5, 20]
},
effects: [{
type: 'castSpellOnHit',
rolls: {
i_chance: [5, 20],
spell: 'smokeBomb'
}
}]
}, {
chance: balance.stinktooth.recipeChance,
type: 'recipe',
name: 'Recipe: Rune of Whirlwind',
profession: 'etching',
teaches: 'runeWhirlwind'
}, {
chance: balance.stinktooth.runestoneChance,
name: 'Muddy Runestone',
material: true,
sprite: [6, 0],
spritesheet: 'images/materials.png'
}]
}
},

rare: {
count: 0
},

spells: [{
type: 'melee',
statMult: 1,
damage: 0.08
}, {
type: 'whirlwind',
range: 2,
damage: 0.2,
cdMax: 40
}, {
type: 'summonSkeleton',
killMinionsOnDeath: false,
killMinionsBeforeSummon: false,
minionsDieOnAggroClear: true,
needLos: false,
sheetName: 'mobs',
cdMax: 60,
positions: [[30, 30], [40, 30], [30, 40], [40, 40]],
summonTemplates: [{
name: 'Biter Rat',
cell: 16,
hpPercent: 20,
dmgPercent: 0.1,
basicSpell: 'melee'
}, {
name: 'Spitter Rat',
cell: 24,
hpPercent: 10,
dmgPercent: 0.175,
basicSpell: 'projectile'
}]
}, {
type: 'charge',
castOnEnd: 1,
cdMax: 50,
targetRandom: true,
damage: 0.3
}, {
type: 'fireblast',
range: 2,
damage: 0.001,
pushback: 2,
procCast: true
}]
},

bandit: {
faction: 'hostile',
grantRep: {
fjolgard: 18
},
level: 11,

regular: {
drops: {
noRandom: true,
alsoRandom: true,
blueprints: [{
chance: balance.bandit.keyChance,
type: 'key',
noSalvage: true,
name: 'Rusted Key',
keyId: 'rustedSewer',
singleUse: true,
sprite: [12, 1],
quantity: 1
}]
}
},

rare: {
name: 'Cutthroat'
}
},

'dire rat': {
level: 13,
walkDistance: 0,
faction: 'hostile',
noQuest: true,
grantRep: {
fjolgard: 22
},

regular: {
hpMult: 5,
dmgMult: 1.2,

drops: {
rolls: 1,
noRandom: true,
alsoRandom: true,
blueprints: [{
chance: balance.direRat.clawChance,
name: 'Rat Claw',
material: true,
sprite: [3, 0],
spritesheet: 'images/materials.png'
}]
}
},

rare: {
count: 0
}
},

'bera the blade': {
faction: 'hostile',
grantRep: {
fjolgard: 25
},
level: 14,
walkDistance: 0,

regular: {
hpMult: 3,
dmgMult: 1.5,

drops: {
rolls: 1,
noRandom: true,
alsoRandom: true,
blueprints: [{
chance: balance.bera.recipeChance,
type: 'recipe',
name: 'Recipe: Rune of Ambush',
profession: 'etching',
teaches: 'runeAmbush'
}, {
chance: balance.bera.keyChance,
type: 'key',
noSalvage: true,
name: 'Rusted Key',
keyId: 'rustedSewer',
singleUse: true,
sprite: [12, 1],
quantity: 1
}]
}
},

rare: {
count: 0
}
}
},
objects: {
'mudfish school': {
max: 9,
type: 'fish',
quantity: [6, 12]
},

sewerdoor: {
properties: {
cpnDoor: {
autoClose: 171,
locked: true,
key: 'rustedSewer',
destroyKey: true
}
}
},

bubbles: {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['51fc9a', '80f643'],
end: ['386646', '44cb95']
},
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: -40,
y: -40,
w: 80,
h: 80
}
}
};
}
}
}
},

gas: {
components: {
cpnParticles: {
simplify: function () {
return {
type: 'particles',
blueprint: {
color: {
start: ['c0c3cf', '80f643'],
end: ['386646', '69696e']
},
scale: {
start: {
min: 18,
max: 64
},
end: {
min: 8,
max: 24
}
},
speed: {
start: {
min: 2,
max: 6
},
end: {
min: 0,
max: 4
}
},
lifetime: {
min: 4,
max: 24
},
alpha: {
start: 0.05,
end: 0
},
randomScale: true,
randomSpeed: true,
chance: 0.02,
randomColor: true,
spawnType: 'rect',
blendMode: 'screen',
spawnRect: {
x: -80,
y: -80,
w: 160,
h: 160
}
}
};
}
}
}
}
}
};

+ 6
- 2
src/server/config/serverConfig.js View File

@@ -1,5 +1,5 @@
module.exports = {
version: '0.8.6',
version: '0.9.0',
port: 4000,
startupMessage: 'Server: ready',
defaultZone: 'fjolarok',
@@ -14,5 +14,9 @@ module.exports = {
//eslint-disable-next-line no-process-env
dbPort: process.env.IWD_DB_PORT || 28015,
//eslint-disable-next-line no-process-env
dbName: process.env.IWD_DB_NAME || 'live'
dbName: process.env.IWD_DB_NAME || 'live',
//eslint-disable-next-line no-process-env
dbUser: process.env.IWD_DB_USER || 'admin',
//eslint-disable-next-line no-process-env
dbPass: process.env.IWD_DB_PASS || ''
};

+ 2
- 0
src/server/config/spells/spellIceSpear.js View File

@@ -21,6 +21,8 @@ module.exports = {

let projectileConfig = {
caster: this.obj.id,
x: obj.x,
y: obj.y,
components: [{
idSource: this.obj.id,
idTarget: target.id,


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

@@ -33,6 +33,8 @@ module.exports = {
let projectileConfig = {
caster: this.obj.id,
extraTargets: 0,
x: obj.x,
y: obj.y,
components: [{
idSource: this.obj.id,
idTarget: target.id,
@@ -95,6 +97,8 @@ module.exports = {

let projectileConfig = {
caster: obj.id,
x: obj.x,
y: obj.y,
components: [{
idSource: obj.id,
idTarget: t.id,


+ 3
- 1
src/server/db/ioRethink.js View File

@@ -4,7 +4,9 @@ const tableNames = require('./tableNames');
const r = require('rethinkdbdash')({
host: serverConfig.dbHost,
port: serverConfig.dbPort,
db: serverConfig.dbName
db: serverConfig.dbName,
user: serverConfig.dbUser,
password: serverConfig.dpPass
});

module.exports = {


+ 4
- 4
src/server/index.js View File

@@ -1,6 +1,6 @@
require('./globals');

let server = require('./server');
let server = require('./server/index');
let components = require('./components/components');
let mods = require('./misc/mods');
let animations = require('./config/animations');
@@ -49,11 +49,11 @@ let startup = {
onComponentsReady: async function () {
skins.init();
factions.init();

await clientConfig.init();
server.init(this.onServerReady.bind(this));
},

onServerReady: async function () {
await server.init();

await leaderboard.init();

atlas.init();


+ 17
- 8
src/server/items/generator.js View File

@@ -41,15 +41,24 @@ module.exports = {
global.instancer.instances[0].eventEmitter.emitNoSticky('onBeforeGetDropChances', dropChancesEvent);

let currencyChance = dropChancesEvent.currencyChance;
//If you kill a mob that's too low of a level, idols are much more rare
if (
blueprint.level &&
ownerLevel &&
ownerLevel - blueprint.level > 4
) {
const levelDelta = ownerLevel - blueprint.level;
currencyChance /= Math.pow(levelDelta - 3, 2);

if (blueprint.level) {
//Idol droprate before level 5 is 0, after which it slowly increases and flattens out at level 15
if (blueprint.level < 5)
currencyChance = 0;
else if (blueprint.level < 14)
currencyChance = (blueprint.level - 4) / 11;

//If you kill a mob that's too low of a level, idols are much more rare
if (
ownerLevel &&
ownerLevel - blueprint.level > 4
) {
const levelDelta = ownerLevel - blueprint.level;
currencyChance /= Math.pow(levelDelta - 3, 2);
}
}

if (blueprint.noCurrency)
currencyChance = 0;



+ 1
- 1
src/server/mods/class-necromancer/index.js View File

@@ -18,7 +18,7 @@ module.exports = {
},

onAfterGetZone: function (zone, config) {
if (zone !== 'fjolarok')
if (zone !== 'fjolgard')
return;

let newRunes = [{


+ 24
- 20
src/server/mods/class-necromancer/spells/spellBloodBarrier.js View File

@@ -18,37 +18,41 @@ module.exports = {
noTargetSelf: true,

cast: function (action) {
let obj = this.obj;
let target = action.target;
const { target } = action;

this.sendBump(target);

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

this.sendBump({
x: obj.x,
y: obj.y - 1
});
this.queueCallback(this.explode.bind(this, action), 1, null, target);

return true;
},
explode: function (target) {
if ((this.obj.destroyed) || (target.destroyed))

explode: function (action) {
const { obj } = this;
const { target } = action;

if ((obj.destroyed) || (target.destroyed))
return;

let amount = (this.obj.stats.values.hpMax / 100) * this.drainPercentage;
let damage = {
amount: amount
};
this.obj.stats.takeDamage(damage, 0, this.obj);
let amount = (obj.stats.values.hpMax / 100) * this.drainPercentage;
const damage = { amount };
obj.stats.takeDamage(damage, 0, obj);

amount = amount * this.shieldMultiplier;
let heal = {
amount: amount
};
target.stats.getHp(heal, this.obj);
const heal = { amount };
target.stats.getHp(heal, obj);

//Only reset the first spell's cooldown if it's an auto attack and not a spell
const firstSpell = target.spellbook.spells[0];
const resetFirstSpell = (
firstSpell &&
firstSpell.isAttack &&
firstSpell.auto
);

if (resetFirstSpell)
target.spellbook.spells[0].cd = 0;

target.spellbook.spells[0].cd = 0;
target.effects.addEffect({
type: 'frenzy',
ttl: this.frenzyDuration,


+ 26
- 20
src/server/objects/objBase.js View File

@@ -275,10 +275,12 @@ module.exports = {
},

performMove: function (action) {
let data = action.data;
let physics = this.instance.physics;
const { x: xOld, y: yOld, syncer, aggro, mob, instance: { physics } } = this;

if (!action.force) {
const { maxDistance = 1, force, data } = action;
const { x: xNew, y: yNew } = data;

if (!force) {
if (physics.isTileBlocking(data.x, data.y))
return true;

@@ -290,10 +292,8 @@ module.exports = {
return true;
}

let maxDistance = action.maxDistance || 1;

let deltaX = Math.abs(this.x - data.x);
let deltaY = Math.abs(this.y - data.y);
let deltaX = Math.abs(xOld - xNew);
let deltaY = Math.abs(yOld - yNew);
if (
(
(deltaX > maxDistance) ||
@@ -308,27 +308,33 @@ module.exports = {
}

//Don't allow mob overlap during combat
if ((this.mob) && (this.mob.target)) {
if (physics.addObject(this, data.x, data.y)) {
physics.removeObject(this, this.x, this.y);
if (mob && mob.target) {
this.x = xNew;
this.y = yNew;

if (physics.addObject(this, xNew, yNew))
physics.removeObject(this, xOld, yOld);
else {
this.x = xOld;
this.y = yOld;

this.x = data.x;
this.y = data.y;
} else
return false;
}
} else {
physics.removeObject(this, this.x, this.y, data.x, data.y);
physics.addObject(this, data.x, data.y, this.x, this.y);
this.x = data.x;
this.y = data.y;
physics.removeObject(this, xOld, yOld, xNew, yNew);

this.x = xNew;
this.y = yNew;

physics.addObject(this, xNew, yNew, xOld, yOld);
}

let syncer = this.syncer;
//We can't use xNew and yNew because addObject could have changed the position (like entering a building interior with stairs)
syncer.o.x = this.x;
syncer.o.y = this.y;

if (this.aggro)
this.aggro.move();
if (aggro)
aggro.move();

this.fireEvent('afterMove');



+ 26
- 16
src/server/objects/objects.js View File

@@ -15,25 +15,26 @@ module.exports = {
return ++this.lastId;
},

build: function (skipPush, clientObj, id) {
build: function (isClientObj, id) {
let o = extend({}, objBase);

if (clientObj)
if (isClientObj)
o.update = null;
else {
o.id = id || this.getNextId();
o.addComponent('syncer');
o.instance = this.instance;

if (!skipPush)
this.objects.push(o);
}

return o;
},

pushObjectToList: function (obj) {
this.objects.push(obj);
},

transferObject: function (o) {
let obj = this.build();
const obj = this.build();

let components = o.components;
delete o.components;
@@ -42,11 +43,11 @@ module.exports = {
for (let p in o)
obj[p] = o[p];

let cLen = components.length;
const cLen = components.length;
for (let i = 0; i < cLen; i++) {
let c = components[i];

let cpn = obj.addComponent(c.type, null, true);
const cpn = obj.addComponent(c.type, null, true);

for (let p in c)
cpn[p] = c[p];
@@ -55,7 +56,8 @@ module.exports = {
cpn.transfer();
}

//this.physics.addObject(obj, obj.x, obj.y);
this.pushObjectToList(obj);
this.physics.addObject(obj, obj.x, obj.y);

return obj;
},
@@ -65,7 +67,7 @@ module.exports = {
for (let i = 0; i < lLen; i++) {
let l = list[i];

let obj = this.build(skipPush, l.clientObj, l.id);
let obj = this.build(l.clientObj, l.id);

obj.sheetName = l.sheetName;
obj.cell = l.cell;
@@ -139,6 +141,9 @@ module.exports = {
if (obj.aggro)
obj.aggro.move();

if (!skipPush)
this.pushObjectToList(obj);

if (lLen === 1)
return obj;
}
@@ -148,6 +153,10 @@ module.exports = {
return this.objects.find(callback);
},

filter: function (callback) {
return this.objects.filter(callback);
},

removeObject: function (obj, callback, useServerId) {
let found = this.objects.spliceFirstWhere(o => obj.id === (useServerId ? o.serverId : o.id));
if (!found)
@@ -168,24 +177,25 @@ module.exports = {
},

addObject: function (o, callback) {
let newO = this.build(true);
const newO = this.build();

let components = o.components;
const components = o.components;

delete o.components;

for (let p in o)
newO[p] = o[p];

let len = components.length;
const len = components.length;
for (let i = 0; i < len; i++) {
let c = components[i];
const c = components[i];

let newC = newO.addComponent(c.type, c);
const newC = newO.addComponent(c.type, c);
extend(newC, c);
}

this.objects.push(newO);
this.pushObjectToList(newO);

if (!newO.dead)
this.physics.addObject(newO, newO.x, newO.y);



+ 38
- 114
src/server/package-lock.json View File

@@ -1,6 +1,6 @@
{
"name": "isleward_server",
"version": "0.8.5",
"version": "0.9.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -332,12 +332,6 @@
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
"optional": true
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -538,11 +532,6 @@
"source-map": "~0.6.0"
}
},
"clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -796,9 +785,9 @@
}
},
"engine.io-client": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz",
"integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==",
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz",
"integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==",
"requires": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
@@ -809,7 +798,7 @@
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~7.4.2",
"xmlhttprequest-ssl": "~1.5.4",
"xmlhttprequest-ssl": "~1.6.2",
"yeast": "0.1.2"
},
"dependencies": {
@@ -844,15 +833,6 @@
"ansi-colors": "^4.1.1"
}
},
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
"optional": true,
"requires": {
"prr": "~1.0.1"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -999,6 +979,23 @@
"prettier-linter-helpers": "^1.0.0"
}
},
"eslint-plugin-requirejs": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-requirejs/-/eslint-plugin-requirejs-4.0.1.tgz",
"integrity": "sha512-7SCBeOZha5E1TGRxbfvNypG0/1Da3gonaT8IeWfNN9iUriamtnwXrGwYipEFr/yMHNCHRpua4LL/7DSov6aUEQ==",
"dev": true,
"requires": {
"ignore": "5.0.5"
},
"dependencies": {
"ignore": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz",
"integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==",
"dev": true
}
}
},
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -1287,11 +1284,6 @@
"rimraf": "2"
}
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
@@ -1444,9 +1436,9 @@
},
"dependencies": {
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}
}
},
@@ -1454,6 +1446,7 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"dev": true,
"optional": true
},
"gtoken": {
@@ -1488,14 +1481,6 @@
"har-schema": "^2.0.0"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"requires": {
"function-bind": "^1.1.1"
}
},
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
@@ -1645,11 +1630,6 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"is": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz",
"integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg=="
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1783,40 +1763,6 @@
"safe-buffer": "^5.0.1"
}
},
"less": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz",
"integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==",
"requires": {
"clone": "^2.1.2",
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"mime": "^1.4.1",
"mkdirp": "^0.5.0",
"promise": "^7.1.1",
"request": "^2.83.0",
"source-map": "~0.6.0"
},
"dependencies": {
"image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
"optional": true
}
}
},
"less-middleware": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/less-middleware/-/less-middleware-3.1.0.tgz",
"integrity": "sha512-1FcTlNE73AVTNwcBykDbCnDPgKZT4VuCZxtlst/i4nB93Y/NsVQsZopK78zfDcT/2lsjBa5xZI8g4SFUhJqsJg==",
"requires": {
"less": "~3.9.0",
"mkdirp": "~0.5.1",
"node.extend": "~2.0.2"
}
},
"levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -1828,9 +1774,9 @@
}
},
"lodash": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"lru-cache": {
@@ -1893,7 +1839,8 @@
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"minipass": {
"version": "2.9.0",
@@ -1918,6 +1865,7 @@
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
"integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
@@ -2073,15 +2021,6 @@
}
}
},
"node.extend": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz",
"integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==",
"requires": {
"has": "^1.0.3",
"is": "^3.2.1"
}
},
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -2284,15 +2223,6 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"optional": true,
"requires": {
"asap": "~2.0.3"
}
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@@ -2302,12 +2232,6 @@
"ipaddr.js": "1.9.1"
}
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"optional": true
},
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
@@ -3020,14 +2944,14 @@
}
},
"ws": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz",
"integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA=="
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz",
"integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.2.tgz",
"integrity": "sha512-tYOaldF/0BLfKuoA39QMwD4j2m8lq4DIncqj1yuNELX4vz9+z/ieG/vwmctjJce+boFHXstqhWnHSxc4W8f4qg=="
},
"yallist": {
"version": "3.1.1",


+ 2
- 2
src/server/package.json View File

@@ -1,6 +1,6 @@
{
"name": "isleward_server",
"version": "0.8.6",
"version": "0.9.0",
"description": "isleward",
"dependencies": {
"axios": "^0.21.1",
@@ -10,7 +10,6 @@
"express-minify": "^1.0.0",
"google-spreadsheet": "^3.1.15",
"image-size": "^0.9.2",
"less-middleware": "^3.1.0",
"rethinkdbdash": "^2.3.31",
"socket.io": "^2.4.1",
"universal-analytics": "^0.4.23"
@@ -19,6 +18,7 @@
"babel-eslint": "^10.1.0",
"eslint": "^7.12.0",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-requirejs": "^4.0.1",
"sqlite3": "^5.0.0"
}
}

+ 3
- 1
src/server/security/connections.js View File

@@ -7,11 +7,13 @@ module.exports = {
playing: 0,

onHandshake: function (socket) {
let p = objects.build();
const p = objects.build();
p.socket = socket;
p.addComponent('auth');
p.addComponent('player');

objects.pushObjectToList(p);

this.players.push(p);
},



+ 0
- 120
src/server/server.js View File

@@ -1,120 +0,0 @@
let compression = require('compression');
let minify = require('express-minify');
let config = require('./config/serverConfig');
let router = require('./security/router');
let rest = require('./security/rest');

module.exports = {
init: function (callback) {
let app = require('express')();
let server = require('http').createServer(app);
let socketServer = require('socket.io')(server, {
transports: ['websocket']
});

global.cons.sockets = socketServer.sockets;

app.use(compression());
app.use(minify());

app.use(function (req, res, next) {
if (
!rest.willHandle(req.url) &&
req.url.indexOf('/server') !== 0 &&
req.url.indexOf('/mods') !== 0
)
req.url = '/client/' + req.url;

next();
});

let lessMiddleware = require('less-middleware');
app.use(lessMiddleware('../', {
render: {
strictMath: true
}
}));

rest.init(app);

app.get('/', this.requests.root.bind(this));
app.get(/^(.*)$/, this.requests.default.bind(this));

socketServer.on('connection', this.listeners.onConnection.bind(this));

let port = config.port || 4000;
server.listen(port, function () {
let message = config.startupMessage || 'Server: Ready';
_.log(message);

callback();
});
},
listeners: {
onConnection: function (socket) {
socket.on('handshake', this.listeners.onHandshake.bind(this, socket));
socket.on('disconnect', this.listeners.onDisconnect.bind(this, socket));
socket.on('request', this.listeners.onRequest.bind(this, socket));

socket.emit('handshake');
},
onHandshake: function (socket) {
cons.onHandshake(socket);
},
onDisconnect: function (socket) {
cons.onDisconnect(socket);
},
onRequest: function (socket, msg, callback) {
msg.callback = callback;

if (!msg.data)
msg.data = {};

if (msg.cpn) {
if (!router.allowedCpn(msg))
return;

cons.route(socket, msg);
} else if (msg.threadModule) {
if (!router.allowedGlobalCall(msg.threadModule, msg.method))
return;

cons.route(socket, msg);
} else {
if (!router.allowedGlobal(msg))
return;

msg.socket = socket;
global[msg.module][msg.method](msg);
}
}
},
requests: {
root: function (req, res) {
res.sendFile('index.html');
},
default: function (req, res) {
let root = req.url.split('/')[1];
let file = req.params[0];
file = file.replace('/' + root + '/', '');
const validModPatterns = ['.png', '/ui/', '/clientComponents/', '/audio/'];
const validRequest = (
root !== 'server' ||
(
file.includes('mods/') &&
validModPatterns.some(v => file.includes(v))
)
);
if (!validRequest)
return null;
res.sendFile(file, {
root: '../' + root
});
}
}
};

+ 63
- 0
src/server/server/index.js View File

@@ -0,0 +1,63 @@
//Imports
const http = require('http');

const socketIo = require('socket.io');

const express = require('express');
const compression = require('compression');
const minify = require('express-minify');

const rest = require('../security/rest');

const {
port = 4000,
startupMessage = 'Server: Ready'
} = require('../config/serverConfig');

const onConnection = require('./onConnection');
const { appRoot, appFile } = require('./requestHandlers');

//Methods
const init = async () => {
return new Promise(resolve => {
const app = express();
const server = http.createServer(app);
const socketServer = socketIo(server, {
transports: ['websocket']
});

global.cons.sockets = socketServer.sockets;

app.use(compression());
app.use(minify());

app.use((req, res, next) => {
if (
!rest.willHandle(req.url) &&
req.url.indexOf('/server') !== 0 &&
req.url.indexOf('/mods') !== 0
)
req.url = '/client/' + req.url;

next();
});

rest.init(app);

app.get('/', appRoot);
app.get(/^(.*)$/, appFile);

socketServer.on('connection', onConnection);

server.listen(port, () => {
_.log(startupMessage);

resolve();
});
});
};

//Exports
module.exports = {
init
};

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

Loading…
Cancel
Save