Browse Source

merged in v0.3.3

tags/v0.3.3
Shaun Kichenbrand 4 years ago
parent
commit
918a5565af
80 changed files with 3144 additions and 33887 deletions
  1. +8
    -0
      .eslintrc
  2. +1
    -0
      .gitignore
  3. +30
    -10
      .gitlab-ci.yml
  4. +2
    -2
      Dockerfile
  5. +62
    -7
      helpers/sqlite-to-rethink/cheatsheet.js
  6. BIN
     
  7. BIN
     
  8. BIN
     
  9. BIN
     
  10. BIN
     
  11. +1
    -1
      src/client/index.html
  12. +9
    -15
      src/client/js/app.js
  13. +2
    -1
      src/client/js/components/components.js
  14. +17
    -11
      src/client/js/components/lightPatch.js
  15. +67
    -0
      src/client/js/components/whirlwind.js
  16. +0
    -16
      src/client/js/misc/helpers.js
  17. +19
    -22
      src/client/js/objects/objBase.js
  18. +3
    -6
      src/client/js/rendering/lightningBuilder.js
  19. +9
    -9
      src/client/js/rendering/renderer.js
  20. +0
    -174
      src/client/plugins/css.js
  21. +0
    -4
      src/client/plugins/howler.min.js
  22. +0
    -2
      src/client/plugins/jquery.min.js
  23. +0
    -31443
      src/client/plugins/pixi.js
  24. +0
    -21
      src/client/plugins/pixi.min.js
  25. +142
    -167
      src/client/plugins/pixi.particles.js
  26. +1
    -0
      src/client/plugins/pixi.particles.min.js
  27. +0
    -428
      src/client/plugins/pixi.picture.js
  28. +0
    -1
      src/client/plugins/require.min.js
  29. +0
    -3
      src/client/plugins/socket.js
  30. +0
    -391
      src/client/plugins/text.js
  31. +0
    -354
      src/client/plugins/wquery.js
  32. +0
    -1
      src/client/plugins/wquery.min.js
  33. +48
    -1
      src/client/ui/templates/equipment/equipment.js
  34. +38
    -6
      src/client/ui/templates/equipment/styles.less
  35. +2
    -2
      src/client/ui/templates/login/template.html
  36. +1
    -1
      src/client/ui/templates/options/options.js
  37. +4
    -12
      src/client/ui/templates/smithing/smithing.js
  38. +3
    -0
      src/client/ui/templates/spells/spells.js
  39. +3
    -3
      src/server/combat/combat.js
  40. +4
    -1
      src/server/components/aggro.js
  41. +1
    -6
      src/server/components/dialogue.js
  42. +8
    -22
      src/server/components/equipment.js
  43. +14
    -0
      src/server/components/equipment/helpers.js
  44. +3
    -1
      src/server/components/extensions/factionVendor.js
  45. +31
    -2
      src/server/components/extensions/socialCommands.js
  46. +4
    -0
      src/server/components/gatherer.js
  47. +49
    -49
      src/server/components/inventory.js
  48. +11
    -6
      src/server/components/mob.js
  49. +46
    -42
      src/server/components/social.js
  50. +71
    -71
      src/server/components/stats.js
  51. +18
    -0
      src/server/components/syncer.js
  52. +15
    -28
      src/server/components/trade.js
  53. +17
    -3
      src/server/config/effects/effectLifeDrain.js
  54. +7
    -2
      src/server/config/eventPhases/phaseEventChain.js
  55. +6
    -0
      src/server/config/herbs.js
  56. +1
    -1
      src/server/config/maps/dungeon.json
  57. +1
    -0
      src/server/config/maps/dungeon/zone.js
  58. +2
    -2
      src/server/config/maps/fjolarok/events/fishingTournament.js
  59. +2
    -2
      src/server/config/maps/fjolarok/map.json
  60. +1871
    -0
      src/server/config/maps/newSewer/map.json
  61. +39
    -0
      src/server/config/maps/newSewer/zone.js
  62. +1
    -1
      src/server/config/maps/sewer/map.json
  63. +27
    -0
      src/server/config/recipes/alchemy.js
  64. +4
    -2
      src/server/config/serverConfig.js
  65. +11
    -2
      src/server/config/spells/spellAura.js
  66. +3
    -4
      src/server/config/spells/spellFireblast.js
  67. +4
    -1
      src/server/config/spells/spellTemplate.js
  68. +71
    -0
      src/server/config/spells/spellWhirlwind.js
  69. +0
    -4
      src/server/index.js
  70. +153
    -130
      src/server/items/enchanter.js
  71. +7
    -0
      src/server/items/generator.js
  72. +8
    -4
      src/server/objects/objBase.js
  73. +12
    -10
      src/server/package-lock.json
  74. +1
    -1
      src/server/package.json
  75. +0
    -226
      src/server/security/ioFirebase.js
  76. +16
    -27
      src/server/security/ioRethink.js
  77. +6
    -29
      src/server/world/map.js
  78. +30
    -0
      src/server/world/map/mapObjects.js
  79. +97
    -90
      src/server/world/syncer.js
  80. +0
    -4
      src/server/world/worker.js

+ 8
- 0
.eslintrc View File

@@ -146,6 +146,14 @@
"func-style": [1,"expression"],
"indent": [2, "tab"],
"key-spacing": [2,{"afterColon":true}],
"max-lines-per-function": [
2,
{
"max": 120,
"skipBlankLines": false,
"skipComments": false
}
],
"new-parens": 2,
"no-inline-comments": 2,
"no-lonely-if": 2,


+ 1
- 0
.gitignore View File

@@ -13,3 +13,4 @@ helpers/sqlite-to-rethink/rethinkdb_data
helpers/sqlite-to-rethink/tmp
helpers/sqlite-to-rethink/log_file
helpers/sqlite-to-rethink/rethinkdb.exe
.idea

+ 30
- 10
.gitlab-ci.yml View File

@@ -1,4 +1,4 @@
image: node:lts-jessie
image: node:10

stages:
- test
@@ -16,24 +16,26 @@ audit:
lint-server:
stage: test
script:
- npm install -g eslint eslint-plugin-prettier prettier babel-eslint
- npm install eslint eslint-plugin-prettier prettier babel-eslint
- cd src/server
- eslint .
- ../../node_modules/.bin/eslint .
only:
- merge_requests
- master
allow_failure: true

lint-client:
stage: test
script:
- npm install -g eslint eslint-plugin-prettier prettier babel-eslint
- npm install eslint eslint-plugin-prettier prettier babel-eslint
- cd src/client
- eslint .
- ../../node_modules/.bin/eslint .
only:
- merge_requests
- master
allow_failure: true

build-image:
build-image-latest:
stage: build
image: docker:stable
services:
@@ -41,10 +43,28 @@ build-image:
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
IMAGE_TAG_REF: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
IMAGE_TAG_ALIAS: $CI_REGISTRY_IMAGE:latest
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
- docker build -t $IMAGE_TAG_REF -t $IMAGE_TAG_ALIAS .
- docker push $IMAGE_TAG_REF
- docker push $IMAGE_TAG_ALIAS
only:
- master
- tags

build-image-stable:
stage: build
image: docker:stable
services:
- docker:dind
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
variables:
IMAGE_TAG_REF: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
IMAGE_TAG_ALIAS: $CI_REGISTRY_IMAGE:stable
script:
- docker build -t $IMAGE_TAG_REF -t $IMAGE_TAG_ALIAS .
- docker push $IMAGE_TAG_REF
- docker push $IMAGE_TAG_ALIAS
only:
- tags

+ 2
- 2
Dockerfile View File

@@ -1,5 +1,5 @@
# Base image on Node.js 10.x LTS (dubnium)
FROM node:10
FROM node:10-alpine

# Create app directory
WORKDIR /usr/src/isleward
@@ -17,4 +17,4 @@ RUN npm install --only-production
EXPOSE 4000

# Launch Isleward server
CMD ["node", "--expose-gc", "index.js"]
CMD ["node", "index.js"]

+ 62
- 7
helpers/sqlite-to-rethink/cheatsheet.js View File

@@ -2,14 +2,14 @@
const r = null;

//Count the amount of permadead characters
r.db('test').table('character').filter({
r.db('live').table('character').filter({
value: {
permadead: true
}
}).count();

//Count the amount of permadead characters per spirit
r.db('test').table('character')
r.db('live').table('character')
.filter({
value: {
permadead: true
@@ -21,7 +21,7 @@ r.db('test').table('character')
.count();

//All players that are level 20
r.db('test').table('character')
r.db('live').table('character')
.filter(row => {
return row('value')('components').contains(cpn => {
return cpn('type').eq('stats').and(cpn('values')('level').ge(20));
@@ -29,10 +29,11 @@ r.db('test').table('character')
});

//Group by mod action source, aggregate and order by count
r.db('test').table('modLog')
r.db('live').table('modLog')
.group(f => f('value')('source')).count().ungroup().orderBy('reduction');

r.db('test').table('character')
//List Items with dex > 30
r.db('live').table('character')
.concatMap(row => {
return row('value')('components')
.filter(cpn => {
@@ -51,8 +52,8 @@ r.db('test').table('character')
});
});

//Play time per account from low to thigh
r.db('test').table('character')
//Play time per account from low to high
r.db('live').table('character')
.concatMap(row => {
return row('value')('components')
.filter(cpn => {
@@ -70,3 +71,57 @@ r.db('test').table('character')
.sum('played')
.ungroup()
.orderBy('reduction');

//Amount of characters per level
r.db('live').table('character')
.concatMap(row => {
return row('value')('components')
.filter(cpn => {
return cpn('type').eq('stats');
})
.concatMap(c => {
return [{
level: c('values')('level')
}];
});
})
.group('level')
.count();

r.db('live').table('character')
.concatMap(row => {
return row('value')('components')
.filter(cpn => {
return cpn('type').eq('stats');
})
.concatMap(c => {
return [{
level: c('values')('level'),
xp: c('values')('xpTotal')
}];
});
})
.filter(c => {
return c('level').eq(2);
})
.group('xp')
.count();

r.db('live').table('character')
.concatMap(row => {
return row('value')('components')
.filter(cpn => {
return cpn('type').eq('stats');
})
.concatMap(c => {
return [{
level: c('values')('level'),
xp: c('values')('xpTotal'),
streaks: c('stats')('mobKillStreaks')
}];
});
})
.filter(c => {
return c('level').eq(2).and(c('xp').eq(10));
});

BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


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

@@ -11,7 +11,7 @@
<link rel="stylesheet" href="css/main.css">
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="js/system/addons.js"></script>
<script src="plugins/require.min.js" data-main="js/app"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" data-main="js/app"></script>
<script>if (window.module) module = window.module;</script>
<link rel="icon" href="images/favicon.ico">
<link rel="apple-touch-icon" href="images/icon.png">


+ 9
- 15
src/client/js/app.js View File

@@ -5,17 +5,16 @@ require.config({
baseUrl: '',
waitSeconds: 120,
paths: {
socket: 'plugins/socket',
wquery: 'plugins/jquery.min',
text: 'plugins/text',
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',
text: 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min',
html: 'plugins/html',
css: 'plugins/css',
css: 'https://cdnjs.cloudflare.com/ajax/libs/require-css/0.1.10/css.min',
main: 'js/main',
helpers: 'js/misc/helpers',
particles: 'plugins/pixi.particles',
picture: 'plugins/pixi.picture',
pixi: 'plugins/pixi.min',
howler: 'plugins/howler.min'
particles: 'plugins/pixi.particles.min',
pixi: 'https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min',
howler: 'https://cdnjs.cloudflare.com/ajax/libs/howler/2.1.2/howler.core.min'
},
shim: {
howler: {
@@ -24,12 +23,12 @@ require.config({
socket: {
exports: 'io'
},
wquery: {
jquery: {
exports: '$'
},
helpers: {
deps: [
'wquery'
'jquery'
]
},
pixi: {
@@ -40,11 +39,6 @@ require.config({
'pixi'
]
},
picture: {
deps: [
'pixi'
]
},
main: {
deps: [
'helpers',


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

@@ -32,7 +32,8 @@ let components = [
'serverActions',
'social',
'passives',
'sound'
'sound',
'whirlwind'
].map(function (c) {
return 'js/components/' + c;
});


+ 17
- 11
src/client/js/components/lightPatch.js View File

@@ -1,14 +1,12 @@
define([
'js/rendering/renderer',
'picture'
'js/rendering/renderer'
], function (
renderer,
picture
renderer
) {
return {
type: 'lightPatch',

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

@@ -20,11 +18,13 @@ define([
let x = obj.x;
let y = obj.y;

let maxDistance = Math.sqrt(Math.pow(obj.width / 2, 2) + Math.pow(obj.height / 2, 2));
let maxDistance = Math.sqrt(Math.pow(obj.width / 3, 2) + Math.pow(obj.height / 3, 2));
for (let i = 0; i < obj.width; i++) {
for (let j = 0; j < obj.height; j++) {
let distance = maxDistance - Math.sqrt(Math.pow((obj.width / 2) - i, 2) + Math.pow((obj.width / 2) - i, 2));
let alpha = distance / maxDistance;
let distance = maxDistance - Math.sqrt(Math.pow((obj.width / 2) - i, 2) + Math.pow((obj.width / 2) - j, 2));
const maxAlpha = (distance / maxDistance) * 0.2;
if (maxAlpha <= 0.05)
continue;

let sprite = renderer.buildObject({
x: (x + i),
@@ -33,11 +33,17 @@ define([
cell: 0,
layerName: 'lightPatches'
});
sprite.alpha = (0.2 + (Math.random() * 1)) * alpha;
sprite.alpha = (maxAlpha * 0.3) + (Math.random() * (maxAlpha * 0.7));
sprite.tint = '0x' + this.color;

sprite.blendMode = PIXI.BLEND_MODES.OVERLAY;
sprite.pluginName = 'picture';
const size = (3 + ~~(Math.random() * 6)) * scaleMult;

sprite.width = size;
sprite.height = size;
sprite.x += scaleMult * ~~(Math.random() * 4);
sprite.y += scaleMult * ~~(Math.random() * 4);

sprite.blendMode = PIXI.BLEND_MODES.ADD;

this.patches.push(sprite);
}


+ 67
- 0
src/client/js/components/whirlwind.js View File

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

source: null,

row: null,
col: null,

delay: 40,
coordinates: [],

objects: null,

init: async function (blueprint) {
await this.getObjectsModule();

if (!this.source) {
this.obj.destroyed = true;
return;
}

this.coordinates.forEach(([x, y], i) => {
const wait = i * this.delay;

setTimeout(this.spawnThing.bind(this, x, y), wait);
});

effects.register(this);
},

getObjectsModule: async function () {
return new Promise(res => {
require(['js/objects/objects'], o => {
this.objects = o;
res();
});
});
},

spawnThing: function (x, y) {
const { row, col } = this;

this.objects.buildObject({
x,
y,
components: [{
type: 'attackAnimation',
row,
col
}]
});
},

renderManual: function () {
},

destroy: function () {
effects.unregister(this);
}
};
});

+ 0
- 16
src/client/js/misc/helpers.js View File

@@ -73,22 +73,6 @@ if (!String.prototype.padStart) {
};
}

if (!String.prototype.padStart) {
//eslint-disable-next-line no-extend-native
String.prototype.padStart = function padStart (targetLength, padString) {
targetLength = targetLength >> 0;
padString = String(typeof padString !== 'undefined' ? padString : ' ');
if (this.length >= targetLength)
return String(this);
targetLength = targetLength - this.length;
if (targetLength > padString.length)
padString += padString.repeat(targetLength / padString.length);
return padString.slice(0, targetLength) + String(this);
};
}

window._ = {
get2dArray: function (w, h, def) {
def = def || 0;


+ 19
- 22
src/client/js/objects/objBase.js View File

@@ -18,8 +18,8 @@ define([
addComponent: function (type, options) {
let c = this[type];

if ((!c) || (options.new)) {
let template = components.getTemplate(type);
if (!c || options.new) {
const template = components.getTemplate(type);
if (!template)
return;

@@ -30,7 +30,7 @@ define([
c[o] = options[o];

//Only use component to initialize other components?
if ((c.init) && (c.init(options)))
if (c.init && c.init(options))
return null;

this[c.type] = c;
@@ -49,18 +49,18 @@ define([
if (!cpn)
return;

this.components.spliceWhere(function (c) {
return (c === cpn);
this.components.spliceWhere(c => {
return c === cpn;
});

delete this[type];
},

update: function () {
let oComponents = this.components;
const oComponents = this.components;
let len = oComponents.length;
for (let i = 0; i < len; i++) {
let c = oComponents[i];
const c = oComponents[i];
if (c.update)
c.update();

@@ -86,7 +86,7 @@ define([
return;

this.sprite.x = (this.x * scale) + (this.flipX ? scale : 0) + this.offsetX;
let oldY = this.sprite.x;
const oldY = this.sprite.y;
this.sprite.y = (this.y * scale) + this.offsetY;

if (this.sprite.width > scale) {
@@ -101,10 +101,10 @@ define([
if (oldY !== this.sprite.y)
renderer.reorder();

this.sprite.scale.x = (this.flipX ? -scaleMult : scaleMult);
this.sprite.scale.x = this.flipX ? -scaleMult : scaleMult;

['nameSprite', 'chatSprite'].forEach(function (s, i) {
let sprite = this[s];
['nameSprite', 'chatSprite'].forEach((s, i) => {
const sprite = this[s];
if (!sprite)
return;

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

sprite.x = (this.x * scale) + (scale / 2) - (sprite.width / 2);
sprite.y = (this.y * scale) + yAdd;
}, this);
});

if (this.stats)
this.stats.updateHpSprite();
@@ -127,9 +127,9 @@ define([
this.sprite.visible = visible;

if (this.nameSprite)
this.nameSprite.visible = ((visible) && (config.showNames));
this.nameSprite.visible = (visible && config.showNames);

this.components.forEach(function (c) {
this.components.forEach(c => {
if (c.setVisible)
c.setVisible(visible);
});
@@ -145,10 +145,10 @@ define([
});
}

let oComponents = this.components;
let cLen = oComponents.length;
const oComponents = this.components;
const cLen = oComponents.length;
for (let i = 0; i < cLen; i++) {
let c = oComponents[i];
const c = oComponents[i];
if (c.destroy)
c.destroy();
}
@@ -162,11 +162,8 @@ define([
if (this.pather)
this.pather.onDeath();

for (let e in this.eventCallbacks) {
this.eventCallbacks[e].forEach(function (c) {
events.off(e, c);
}, this);
}
for (let e in this.eventCallbacks)
this.eventCallbacks[e].forEach(c => events.off(e, c));
}
};
});

+ 3
- 6
src/client/js/rendering/lightningBuilder.js View File

@@ -1,9 +1,7 @@
define([
'js/rendering/renderer',
'picture'
'js/rendering/renderer'
], function (
renderer,
picture
renderer
) {
return {
build: function (config) {
@@ -80,8 +78,7 @@ define([
lightPatch.width = scaleMult * 3;
lightPatch.height = scaleMult * 3;

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

line.sprites.push(lightPatch);
}


+ 9
- 9
src/client/js/rendering/renderer.js View File

@@ -6,8 +6,7 @@ define([
'js/rendering/tileOpacity',
'js/rendering/particles',
'js/rendering/shaders/outline',
'js/rendering/spritePool',
'picture'
'js/rendering/spritePool'
], function (
resources,
events,
@@ -16,8 +15,7 @@ define([
tileOpacity,
particles,
shaderOutline,
spritePool,
picture
spritePool
) {
let pixi = PIXI;
let mRandom = Math.random.bind(Math);
@@ -71,8 +69,8 @@ define([
hiddenRooms: null,

init: function () {
PIXI.GC_MODES.DEFAULT = PIXI.GC_MODES.AUTO;
PIXI.SCALE_MODES.DEFAULT = PIXI.SCALE_MODES.NEAREST;
PIXI.settings.GC_MODE = PIXI.GC_MODES.AUTO;
PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;

events.on('onGetMap', this.onGetMap.bind(this));
events.on('onToggleFullscreen', this.toggleScreen.bind(this));
@@ -84,7 +82,9 @@ define([
this.showTilesW = Math.ceil((this.width / scale) / 2) + 3;
this.showTilesH = Math.ceil((this.height / scale) / 2) + 3;

this.renderer = pixi.autoDetectRenderer(this.width, this.height, {
this.renderer = new PIXI.Renderer({
width: this.width,
height: this.height,
backgroundColor: '0x2d2136'
});

@@ -335,7 +335,7 @@ define([
this.clean();
spritePool.clean();

this.stage.filters = [new PIXI.filters.VoidFilter()];
this.stage.filters = [new PIXI.filters.AlphaFilter()];
this.stage.filterArea = new PIXI.Rectangle(0, 0, w * scale, h * scale);

this.hiddenRooms = msg.hiddenRooms;
@@ -786,7 +786,7 @@ define([
obj.sprite.texture = this.getTexture(obj.sheetName, obj.cell, obj.sprite.width / scaleMult);
},

reorder: function (sprite) {
reorder: function () {
this.layers.mobs.children.sort((a, b) => b.y - a.y);
},



+ 0
- 174
src/client/plugins/css.js View File

@@ -1,174 +0,0 @@
/*
* Require-CSS RequireJS css! loader plugin
* 0.1.8
* Guy Bedford 2014
* MIT
*/

/*
*
* Usage:
* require(['css!./mycssFile']);
*
* Tested and working in (up to latest versions as of March 2013):
* Android
* iOS 6
* IE 6 - 10
* Chome 3 - 26
* Firefox 3.5 - 19
* Opera 10 - 12
*
* browserling.com used for virtual testing environment
*
* Credit to B Cavalier & J Hann for the IE 6 - 9 method,
* refined with help from Martin Cermak
*
* Sources that helped along the way:
* - https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent
* - http://www.phpied.com/when-is-a-stylesheet-really-loaded/
* - https://github.com/cujojs/curl/blob/master/src/curl/plugin/css.js
*
*/

define(function() {
//>>excludeStart('excludeRequireCss', pragmas.excludeRequireCss)
if (typeof window == 'undefined')
return {
load: function(n, r, load) {
load()
}
};

var head = document.getElementsByTagName('head')[0];

var engine = window.navigator.userAgent.match(/Trident\/([^ ;]*)|AppleWebKit\/([^ ;]*)|Opera\/([^ ;]*)|rv\:([^ ;]*)(.*?)Gecko\/([^ ;]*)|MSIE\s([^ ;]*)|AndroidWebKit\/([^ ;]*)/) || 0;

// use <style> @import load method (IE < 9, Firefox < 18)
var useImportLoad = false;

// set to false for explicit <link> load checking when onload doesn't work perfectly (webkit)
var useOnload = true;

// trident / msie
if (engine[1] || engine[7])
useImportLoad = parseInt(engine[1]) < 6 || parseInt(engine[7]) <= 9;
// webkit
else if (engine[2] || engine[8])
useOnload = false;
// gecko
else if (engine[4])
useImportLoad = parseInt(engine[4]) < 18;

//>>excludeEnd('excludeRequireCss')
//main api object
var cssAPI = {};

//>>excludeStart('excludeRequireCss', pragmas.excludeRequireCss)
cssAPI.pluginBuilder = './css-builder';

// <style> @import load method
var curStyle, curSheet;
var createStyle = function() {
curStyle = document.createElement('style');
head.appendChild(curStyle);
curSheet = curStyle.styleSheet || curStyle.sheet;
}
var ieCnt = 0;
var ieLoads = [];
var ieCurCallback;

var createIeLoad = function(url) {
curSheet.addImport(url);
curStyle.onload = function() {
processIeLoad()
};

ieCnt++;
if (ieCnt == 31) {
createStyle();
ieCnt = 0;
}
}
var processIeLoad = function() {
ieCurCallback();

var nextLoad = ieLoads.shift();

if (!nextLoad) {
ieCurCallback = null;
return;
}

ieCurCallback = nextLoad[1];
createIeLoad(nextLoad[0]);
}
var importLoad = function(url, callback) {
if (!curSheet || !curSheet.addImport)
createStyle();

if (curSheet && curSheet.addImport) {
// old IE
if (ieCurCallback) {
ieLoads.push([url, callback]);
} else {
createIeLoad(url);
ieCurCallback = callback;
}
} else {
// old Firefox
curStyle.textContent = '@import "' + url + '";';

var loadInterval = setInterval(function() {
try {
curStyle.sheet.cssRules;
clearInterval(loadInterval);
callback();
} catch (e) {}
}, 10);
}
}

// <link> load method
var linkLoad = function(url, callback) {
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
if (useOnload) {
link.onload = function() {
link.onload = function() {};
// for style dimensions queries, a short delay can still be necessary
setTimeout(callback, 7);
}
} else {
var loadInterval = setInterval(function() {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (sheet.href == link.href) {
clearInterval(loadInterval);
return callback();
}
}
}, 10);
}
link.href = url;
head.appendChild(link);
}

//>>excludeEnd('excludeRequireCss')
cssAPI.normalize = function(name, normalize) {
if (name.substr(name.length - 4, 4) == '.css')
name = name.substr(0, name.length - 4);

return normalize(name);
}

//>>excludeStart('excludeRequireCss', pragmas.excludeRequireCss)
cssAPI.load = function(cssId, req, load, config) {

(useImportLoad ? importLoad : linkLoad)(req.toUrl(cssId + '.css'), load);

}

//>>excludeEnd('excludeRequireCss')
return cssAPI;
});

+ 0
- 4
src/client/plugins/howler.min.js
File diff suppressed because it is too large
View File


+ 0
- 2
src/client/plugins/jquery.min.js
File diff suppressed because it is too large
View File


+ 0
- 31443
src/client/plugins/pixi.js
File diff suppressed because it is too large
View File


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


+ 142
- 167
src/client/plugins/pixi.particles.js View File

@@ -46,14 +46,11 @@ if (!Array.prototype.random) {
* @namespace PIXI.particles
*/
(function () {

"use strict";

// Check for window, fallback to global
var global = typeof window !== 'undefined' ? window : GLOBAL;
let global = typeof window !== 'undefined' ? window : GLOBAL;

// Define PIXI Flash namespace
var particles = {};
let particles = {};

// Export for Node-compatible environments like Electron
if (typeof module !== 'undefined' && module.exports) {
@@ -68,52 +65,46 @@ if (!Array.prototype.random) {
}
// If we're in the browser make sure PIXI is available
else if (typeof PIXI === 'undefined') {
if (true) {
throw "pixi-particles requires pixi.js to be loaded first";
} else {
throw "Requires pixi.js";
}
if (true)
throw 'pixi-particles requires pixi.js to be loaded first';
else
throw 'Requires pixi.js';
}

// Assign to global namespace
global.PIXI.particles = global.PIXI.particles || particles;

}());
})();
/**
* @module Pixi Particles
* @namespace PIXI.particles
*/
(function (PIXI, undefined) {

"use strict";

var BLEND_MODES = PIXI.BLEND_MODES || PIXI.blendModes;
var Texture = PIXI.Texture;
let BLEND_MODES = PIXI.BLEND_MODES || PIXI.blendModes;
let Texture = PIXI.Texture;

/**
* Contains helper functions for particles and emitters to use.
* @class ParticleUtils
* @static
*/
var ParticleUtils = {};
let ParticleUtils = {};

var DEG_TO_RADS = ParticleUtils.DEG_TO_RADS = Math.PI / 180;
let DEG_TO_RADS = ParticleUtils.DEG_TO_RADS = Math.PI / 180;

ParticleUtils.useAPI3 = false;
// avoid the string replacement of '"1.6.7"'
var version = PIXI["VER" + "SION"]; // jshint ignore:line
if (version && parseInt(version.substring(0, version.indexOf("."))) >= 3) {
let version = PIXI['VER' + 'SION']; // jshint ignore:line
if (version && parseInt(version.substring(0, version.indexOf('.'))) >= 3)
ParticleUtils.useAPI3 = true;
}

var empty = ParticleUtils.EMPTY_TEXTURE = null;
let empty = ParticleUtils.EMPTY_TEXTURE = null;
if (ParticleUtils.useAPI3) {
empty = ParticleUtils.EMPTY_TEXTURE = Texture.EMPTY;
//prevent any events from being used on the empty texture, as well as destruction of it
//v4 of Pixi does this, but doing it again won't hurt
empty.on = empty.destroy = empty.once = empty.emit = function () {};
} else {
var canvas = document.createElement("canvas");
let canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
empty = ParticleUtils.EMPTY_TEXTURE = PIXI.Texture.fromCanvas(canvas);
//have the particle not render, even though we have an empty canvas that would be
@@ -133,10 +124,10 @@ if (!Array.prototype.random) {
ParticleUtils.rotatePoint = function (angle, p) {
if (!angle) return;
angle *= DEG_TO_RADS;
var s = Math.sin(angle);
var c = Math.cos(angle);
var xnew = p.x * c - p.y * s;
var ynew = p.x * s + p.y * c;
let s = Math.sin(angle);
let c = Math.cos(angle);
let xnew = p.x * c - p.y * s;
let ynew = p.x * s + p.y * c;
p.x = xnew;
p.y = ynew;
};
@@ -162,7 +153,7 @@ if (!Array.prototype.random) {
*/
ParticleUtils.normalize = function (point) {
if ((point.x != 0) || (point.y != 0)) {
var oneOverLen = 1 / ParticleUtils.length(point);
let oneOverLen = 1 / ParticleUtils.length(point);
point.x *= oneOverLen;
point.y *= oneOverLen;
}
@@ -206,11 +197,11 @@ if (!Array.prototype.random) {
output.length = 0;
else
output = [];
if (color.charAt(0) == "#")
if (color.charAt(0) == '#')
color = color.substr(1);
else if (color.indexOf("0x") === 0)
else if (color.indexOf('0x') === 0)
color = color.substr(2);
var alpha;
let alpha;
if (color.length == 8) {
alpha = color.substr(0, 2);
color = color.substr(2);
@@ -234,17 +225,17 @@ if (!Array.prototype.random) {
* @static
*/
ParticleUtils.generateEase = function (segments) {
var qty = segments.length;
var oneOverQty = 1 / qty;
let qty = segments.length;
let oneOverQty = 1 / qty;
/*
* Calculates the percentage of change at a given point in time (0-1 inclusive).
* @param {Number} time The time of the ease, 0-1 inclusive.
* @return {Number} The percentage of the change, 0-1 inclusive (unless your
* ease goes outside those bounds).
*/
var simpleEase = function (time) {
var t, s;
var i = (qty * time) | 0; //do a quick floor operation
let simpleEase = function (time) {
let t, s;
let i = (qty * time) | 0; //do a quick floor operation
t = (time - (i * oneOverQty)) * qty;
s = segments[i] || segments[qty - 1];
return (s.s + t * (2 * (1 - t) * (s.cp - s.s) + t * (s.e - s.s)));
@@ -262,25 +253,21 @@ if (!Array.prototype.random) {
ParticleUtils.getBlendMode = function (name) {
if (!name) return BLEND_MODES.NORMAL;
name = name.toUpperCase();
while (name.indexOf(" ") >= 0)
name = name.replace(" ", "_");
while (name.indexOf(' ') >= 0)
name = name.replace(' ', '_');
return BLEND_MODES[name] || BLEND_MODES.NORMAL;
};

PIXI.particles.ParticleUtils = ParticleUtils;

}(PIXI));
})(PIXI);
/**
* @module Pixi Particles
* @namespace PIXI.particles
*/
(function (PIXI, undefined) {

"use strict";

var ParticleUtils = PIXI.particles.ParticleUtils;
var Sprite = PIXI.Sprite;
var useAPI3 = ParticleUtils.useAPI3;
let ParticleUtils = PIXI.particles.ParticleUtils;
let Sprite = PIXI.Sprite;
let useAPI3 = ParticleUtils.useAPI3;

/**
* An individual particle image. You shouldn't have to deal with these.
@@ -288,15 +275,14 @@ if (!Array.prototype.random) {
* @constructor
* @param {Emitter} emitter The emitter that controls this particle.
*/
var Particle = function (emitter) {
let Particle = function (emitter) {
//start off the sprite with a blank texture, since we are going to replace it
//later when the particle is initialized. Pixi v2 requires a texture, v3 supplies a
//blank texture for us.
if (useAPI3) {
if (useAPI3)
Sprite.call(this);
} else {
else
Sprite.call(this, ParticleUtils.EMPTY_TEXTURE);
}

/**
* The emitter that controls this particle.
@@ -489,7 +475,7 @@ if (!Array.prototype.random) {
};

// Reference to the prototype
var p = Particle.prototype = Object.create(Sprite.prototype);
let p = Particle.prototype = Object.create(Sprite.prototype);

/**
* Initializes the particle for use, based on the properties that have to
@@ -563,9 +549,8 @@ if (!Array.prototype.random) {
if (useAPI3) {
//remove warning on PIXI 3
this.texture = art || ParticleUtils.EMPTY_TEXTURE;
} else {
} else
this.setTexture(art || ParticleUtils.EMPTY_TEXTURE);
}
};

/**
@@ -594,7 +579,7 @@ if (!Array.prototype.random) {
}

//determine our interpolation value
var lerp = this.age * this._oneOverLife; //lifetime / maxLife;
let lerp = this.age * this._oneOverLife; //lifetime / maxLife;
if (this.ease) {
if (this.ease.length == 4) {
//the t, b, c, d parameters that some tween libraries use
@@ -612,7 +597,7 @@ if (!Array.prototype.random) {
this.alpha = (this.endAlpha - this.startAlpha) * lerp + this.startAlpha;
//interpolate scale
if (this._doScale) {
var scale = (this.endScale - this.startScale) * lerp + this.startScale;
let scale = (this.endScale - this.startScale) * lerp + this.startScale;
this.rawScale.x = this.rawScale.y = scale;
this.scale.x = this.scale.y = ~~(scale / 2) * 2;
}
@@ -620,7 +605,7 @@ if (!Array.prototype.random) {
if (this._doNormalMovement) {
//interpolate speed
if (this._doSpeed) {
var speed = (this.endSpeed - this.startSpeed) * lerp + this.startSpeed;
let speed = (this.endSpeed - this.startSpeed) * lerp + this.startSpeed;
ParticleUtils.normalize(this.velocity);
ParticleUtils.scaleBy(this.velocity, speed);
} else if (this._doAcceleration) {
@@ -628,12 +613,12 @@ if (!Array.prototype.random) {
this.velocity.y += this.acceleration.y * delta;
}
//adjust position based on velocity
var dir = this.direction;
let dir = this.direction;
if (dir) {
var dx = dir.x;
var dy = dir.y;
var vx = this.velocity.x;
var vy = this.velocity.y;
let dx = dir.x;
let dy = dir.y;
let vx = this.velocity.x;
let vy = this.velocity.y;
if ((vy > 0) && (dy < 0))
vy *= -1;
this.rawPosition.x += dx * vx * delta;
@@ -648,15 +633,15 @@ if (!Array.prototype.random) {
}
//interpolate color
if (this._doColor) {
var r = (this._eR - this._sR) * lerp + this._sR;
var g = (this._eG - this._sG) * lerp + this._sG;
var b = (this._eB - this._sB) * lerp + this._sB;
let r = (this._eR - this._sR) * lerp + this._sR;
let g = (this._eG - this._sG) * lerp + this._sG;
let b = (this._eB - this._sB) * lerp + this._sB;
this.tint = ParticleUtils.combineRGBComponents(r, g, b);
}
//update rotation
if (this.rotationSpeed !== 0) {
if (this.rotationSpeed !== 0)
this.rotation += this.rotationSpeed * delta;
} else if (this.acceleration) {
else if (this.acceleration) {
if (this.allowRotation)
this.rotation = Math.atan2(this.velocity.y, this.velocity.x); // + Math.PI / 2;
else
@@ -700,9 +685,9 @@ if (!Array.prototype.random) {
*/
Particle.parseArt = function (art) {
//convert any strings to Textures.
var i;
let i;
for (i = art.length; i >= 0; --i) {
if (typeof art[i] == "string")
if (typeof art[i] === 'string')
art[i] = PIXI.Texture.fromImage(art[i]);
}
//particles from different base textures will be slower in WebGL than if they
@@ -711,7 +696,7 @@ if (!Array.prototype.random) {
for (i = art.length - 1; i > 0; --i) {
if (art[i].baseTexture != art[i - 1].baseTexture) {
if (window.console)
console.warn("PixiParticles: using particle textures from different images may hinder performance in WebGL");
console.warn('PixiParticles: using particle textures from different images may hinder performance in WebGL');
break;
}
}
@@ -733,20 +718,16 @@ if (!Array.prototype.random) {
};

PIXI.particles.Particle = Particle;

}(PIXI));
})(PIXI);

/**
* @module Pixi Particles
* @namespace PIXI.particles
*/
(function (PIXI, undefined) {

"use strict";

var ParticleUtils = PIXI.particles.ParticleUtils,
let ParticleUtils = PIXI.particles.ParticleUtils,
Particle = PIXI.particles.Particle,
ParticleContainer = PIXI.particles.ParticleContainer || PIXI.ParticleContainer;
ParticleContainer = PIXI.ParticleContainer;

/**
* A particle emitter.
@@ -761,7 +742,7 @@ if (!Array.prototype.random) {
* @param {Boolean} [config.emit=true] If config.emit is explicitly passed as false, the Emitter
* will start disabled.
*/
var Emitter = function (particleParent, particleImages, config) {
let Emitter = function (particleParent, particleImages, config) {
this.chance = config.chance || null;
this.allowRotation = config.allowRotation || false;
this.randomColor = config.randomColor || false;
@@ -1098,22 +1079,22 @@ if (!Array.prototype.random) {
};

// Reference to the prototype
var p = Emitter.prototype = {};
let p = Emitter.prototype = {};

var helperPoint = new PIXI.Point();
let helperPoint = new PIXI.Point();

/**
* Time between particle spawns in seconds. If this value is not a number greater than 0,
* it will be set to 1 (particle per second) to prevent infinite loops.
* @property {Number} frequency
*/
Object.defineProperty(p, "frequency", {
Object.defineProperty(p, 'frequency', {
get: function () {
return this._frequency;
},
set: function (value) {
//do some error checking to prevent infinite loops
if (typeof value == "number" && value > 0)
if (typeof value === 'number' && value > 0)
this._frequency = value;
else
this._frequency = 1;
@@ -1126,7 +1107,7 @@ if (!Array.prototype.random) {
* pooled particles, if the emitter has already been used.
* @property {Function} particleConstructor
*/
Object.defineProperty(p, "particleConstructor", {
Object.defineProperty(p, 'particleConstructor', {
get: function () {
return this._particleConstructor;
},
@@ -1136,9 +1117,9 @@ if (!Array.prototype.random) {
//clean up existing particles
this.cleanup();
//scrap all the particles
for (var particle = this._poolFirst; particle; particle = particle.next) {
for (let particle = this._poolFirst; particle; particle = particle.next)
particle.destroy();
}
this._poolFirst = null;
//re-initialize the emitter so that the new constructor can do anything it needs to
if (this._origConfig && this._origArt)
@@ -1151,7 +1132,7 @@ if (!Array.prototype.random) {
* The display object to add particles to. Settings this will dump any active particles.
* @property {PIXI.DisplayObjectContainer} parent
*/
Object.defineProperty(p, "parent", {
Object.defineProperty(p, 'parent', {
get: function () {
return this._parent;
},
@@ -1159,7 +1140,7 @@ if (!Array.prototype.random) {
//if our previous parent was a ParticleContainer, then we need to remove
//pooled particles from it
if (this._parentIsPC) {
for (var particle = this._poolFirst; particle; particle = particle.next) {
for (let particle = this._poolFirst; particle; particle = particle.next) {
if (particle.parent)
particle.parent.removeChild(particle);
}
@@ -1190,7 +1171,7 @@ if (!Array.prototype.random) {
//set up the array of data, also ensuring that it is an array
art = Array.isArray(art) ? art.slice() : [art];
//run the art through the particle class's parsing function
var partClass = this._particleConstructor;
let partClass = this._particleConstructor;
this.particleImages = partClass.parseArt ? partClass.parseArt(art) : art;
///////////////////////////
// Particle Properties //
@@ -1208,7 +1189,7 @@ if (!Array.prototype.random) {
} else
this.startSpeed = this.endSpeed = 0;
//set up acceleration
var acceleration = config.acceleration;
let acceleration = config.acceleration;
if (acceleration && (acceleration.x || acceleration.y)) {
this.endSpeed = this.startSpeed;
this.acceleration = new PIXI.Point(acceleration.x, acceleration.y);
@@ -1261,7 +1242,7 @@ if (!Array.prototype.random) {
this.particleBlendMode = ParticleUtils.getBlendMode(config.blendMode);
//use the custom ease if provided
if (config.ease) {
this.customEase = typeof config.ease == "function" ?
this.customEase = typeof config.ease === 'function' ?
config.ease :
ParticleUtils.generateEase(config.ease);
} else
@@ -1279,41 +1260,41 @@ if (!Array.prototype.random) {
this.particlesPerWave = 1;
this.particleSpacing = 0;
this.angleStart = 0;
var spawnCircle;
let spawnCircle;
//determine the spawn function to use
switch (config.spawnType) {
case "rect":
this.spawnType = "rect";
case 'rect':
this.spawnType = 'rect';
this._spawnFunc = this._spawnRect;
var spawnRect = config.spawnRect;
this.spawnRect = new PIXI.Rectangle(spawnRect.x, spawnRect.y, spawnRect.w, spawnRect.h);
break;
case "circle":
this.spawnType = "circle";
case 'circle':
this.spawnType = 'circle';
this._spawnFunc = this._spawnCircle;
spawnCircle = config.spawnCircle;
this.spawnCircle = new PIXI.Circle(spawnCircle.x, spawnCircle.y, spawnCircle.r);
break;
case "ring":
this.spawnType = "ring";
case 'ring':
this.spawnType = 'ring';
this._spawnFunc = this._spawnRing;
spawnCircle = config.spawnCircle;
this.spawnCircle = new PIXI.Circle(spawnCircle.x, spawnCircle.y, spawnCircle.r);
this.spawnCircle.minRadius = spawnCircle.minR;
break;
case "burst":
this.spawnType = "burst";
case 'burst':
this.spawnType = 'burst';
this._spawnFunc = this._spawnBurst;
this.particlesPerWave = config.particlesPerWave;
this.particleSpacing = config.particleSpacing;
this.angleStart = config.angleStart ? config.angleStart : 0;
break;
case "point":
this.spawnType = "point";
case 'point':
this.spawnType = 'point';
this._spawnFunc = this._spawnPoint;
break;
default:
this.spawnType = "point";
this.spawnType = 'point';
this._spawnFunc = this._spawnPoint;
break;
}
@@ -1360,10 +1341,8 @@ if (!Array.prototype.random) {
if (this._parentIsPC) {
particle.alpha = 0;
particle.visible = false;
} else {
if (particle.parent)
particle.parent.removeChild(particle);
}
} else if (particle.parent)
particle.parent.removeChild(particle);
//decrease count
--this.particleCount;
};
@@ -1376,7 +1355,7 @@ if (!Array.prototype.random) {
p.rotate = function (newRot) {
if (this.rotation == newRot) return;
//caclulate the difference in rotation for rotating spawnPos
var diff = newRot - this.rotation;
let diff = newRot - this.rotation;
this.rotation = newRot;
//rotate spawnPos
ParticleUtils.rotatePoint(diff, this.spawnPos);
@@ -1424,7 +1403,7 @@ if (!Array.prototype.random) {
* stops new particles from being created, but allows existing ones to die out.
* @property {Boolean} emit
*/
Object.defineProperty(p, "emit", {
Object.defineProperty(p, 'emit', {
get: function () {
return this._emit;
},
@@ -1440,25 +1419,25 @@ if (!Array.prototype.random) {
* @param {Number} delta Time elapsed since the previous frame, in __seconds__.
*/
p.update = function (delta) {
var r = [];
let r = [];
//if we don't have a parent to add particles to, then don't do anything.
//this also works as a isDestroyed check
if (!this._parent) return;
//update existing particles
var i, particle, next;
let i, particle, next;
for (particle = this._activeParticlesFirst; particle; particle = next) {
next = particle.next;
particle.update(delta);
}
var prevX, prevY;
let prevX, prevY;
//if the previous position is valid, store these for later interpolation
if (this._prevPosIsValid) {
prevX = this._prevEmitterPos.x;
prevY = this._prevEmitterPos.y;
}
//store current position of the emitter as local variables
var curX = this.ownerPos.x + this.spawnPos.x;
var curY = this.ownerPos.y + this.spawnPos.y;
let curX = this.ownerPos.x + this.spawnPos.x;
let curY = this.ownerPos.y + this.spawnPos.y;
//spawn new particles
if (this.emit) {
//decrease spawn timer
@@ -1499,7 +1478,7 @@ if (!Array.prototype.random) {
var emitPosX, emitPosY;
if (this._prevPosIsValid && this._posChanged) {
//1 - _spawnTimer / delta, but _spawnTimer is negative
var lerp = 1 + this._spawnTimer / delta;
let lerp = 1 + this._spawnTimer / delta;
emitPosX = (curX - prevX) * lerp + prevX;
emitPosY = (curY - prevY) * lerp + prevY;
} else //otherwise just set to the spawn position
@@ -1509,22 +1488,22 @@ if (!Array.prototype.random) {
}
//create enough particles to fill the wave (non-burst types have a wave of 1)
i = 0;
for (var len = Math.min(this.particlesPerWave, this.maxParticles - this.particleCount); i < len; ++i) {
for (let len = Math.min(this.particlesPerWave, this.maxParticles - this.particleCount); i < len; ++i) {
//create particle
var p;
if (this._poolFirst) {
p = this._poolFirst;
this._poolFirst = this._poolFirst.next;
p.next = null;
} else {
} else
p = new this.particleConstructor(this);
}
r.push(p);

//set a random texture if we have more than one
if (this.particleImages.length > 1) {
if (this.particleImages.length > 1)
p.applyArt(this.particleImages.random());
} else {
else {
//if they are actually the same texture, a standard particle
//will quit early from the texture setting in setTexture().
p.applyArt(this.particleImages[0]);
@@ -1537,36 +1516,34 @@ if (!Array.prototype.random) {
p.startSpeed = this.startSpeed;
p.endSpeed = this.endSpeed;
} else {
var startSpeed = this.startSpeed;
let startSpeed = this.startSpeed;
p.startSpeed = startSpeed.min + ~~(Math.random() * (startSpeed.max - startSpeed.min));
var endSpeed = this.endSpeed;
let endSpeed = this.endSpeed;
p.endSpeed = endSpeed.min + ~~(Math.random() * (endSpeed.max - endSpeed.min));
}
p.acceleration.x = this.acceleration.x;
p.acceleration.y = this.acceleration.y;
if (this.minimumScaleMultiplier != 1) {
var rand = Math.random() * (1 - this.minimumScaleMultiplier) + this.minimumScaleMultiplier;
let rand = Math.random() * (1 - this.minimumScaleMultiplier) + this.minimumScaleMultiplier;
p.startScale = this.startScale * rand;
p.endScale = this.endScale * rand;
} else if (!this.randomScale) {
p.startScale = this.startScale;
p.endScale = this.endScale;
} else {
if (!this.randomScale) {
p.startScale = this.startScale;
p.endScale = this.endScale;
} else {
var startScale = this.startScale;
p.startScale = startScale.min + ~~(Math.random() * (startScale.max - startScale.min));
var endScale = this.endScale;
p.endScale = endScale.min + ~~(Math.random() * (endScale.max - endScale.min));
}
let startScale = this.startScale;
p.startScale = startScale.min + ~~(Math.random() * (startScale.max - startScale.min));
let endScale = this.endScale;
p.endScale = endScale.min + ~~(Math.random() * (endScale.max - endScale.min));
}

if (!this.randomColor) {
p.startColor = this.startColor;
p.endColor = this.endColor;
} else {
var startColor = this.startColor;
let startColor = this.startColor;
p.startColor = startColor[~~(Math.random() * startColor.length)];
var endColor = this.endColor;
let endColor = this.endColor;
p.endColor = endColor[~~(Math.random() * endColor.length)];
}
//randomize the rotation speed
@@ -1600,14 +1577,14 @@ if (!Array.prototype.random) {
} else {
//kind of hacky, but performance friendly
//shuffle children to correct place
var children = this._parent.children;
let children = this._parent.children;
//avoid using splice if possible
if (children[0] == p)
children.shift();
else if (children[children.length - 1] == p)
children.pop();
else {
var index = children.indexOf(p);
let index = children.indexOf(p);
children.splice(index, 1);
}
if (this.addAtBack)
@@ -1620,9 +1597,9 @@ if (!Array.prototype.random) {
this._activeParticlesLast.next = p;
p.prev = this._activeParticlesLast;
this._activeParticlesLast = p;
} else {
} else
this._activeParticlesLast = this._activeParticlesFirst = p;
}
++this.particleCount;
}
}
@@ -1701,9 +1678,10 @@ if (!Array.prototype.random) {
//particle angle and rotation of emitter
if (this.minStartRotation == this.maxStartRotation)
p.rotation = this.minStartRotation + this.rotation;
else
else {
p.rotation = Math.random() * (this.maxStartRotation - this.minStartRotation) +
this.minStartRotation + this.rotation;
}
//place the particle at a random radius in the circle
helperPoint.x = Math.random() * this.spawnCircle.radius;
helperPoint.y = 0;
@@ -1730,14 +1708,15 @@ if (!Array.prototype.random) {
* @param {int} i The particle number in the current wave. Not used for this function.
*/
p._spawnRing = function (p, emitPosX, emitPosY, i) {
var spawnCircle = this.spawnCircle;
let spawnCircle = this.spawnCircle;
//set the initial rotation/direction of the particle based on starting
//particle angle and rotation of emitter
if (this.minStartRotation == this.maxStartRotation)
p.rotation = this.minStartRotation + this.rotation;
else
else {
p.rotation = Math.random() * (this.maxStartRotation - this.minStartRotation) +
this.minStartRotation + this.rotation;
}
//place the particle at a random radius in the ring
if (spawnCircle.minRadius == spawnCircle.radius) {
helperPoint.x = Math.random() * (spawnCircle.radius - spawnCircle.minRadius) +
@@ -1746,7 +1725,7 @@ if (!Array.prototype.random) {
helperPoint.x = spawnCircle.radius;
helperPoint.y = 0;
//rotate the point to a random angle in the circle
var angle = Math.random() * 360;
let angle = Math.random() * 360;
p.rotation += angle;
ParticleUtils.rotatePoint(angle, helperPoint);
//offset by the circle's center
@@ -1786,7 +1765,7 @@ if (!Array.prototype.random) {
* @method cleanup
*/
p.cleanup = function () {
var particle, next;
let particle, next;
for (particle = this._activeParticlesFirst; particle; particle = next) {
next = particle.next;
this.recycle(particle);
@@ -1805,8 +1784,8 @@ if (!Array.prototype.random) {
//puts all active particles in the pool, and removes them from the particle parent
this.cleanup();
//wipe the pool clean
var next;
for (var particle = this._poolFirst; particle; particle = next) {
let next;
for (let particle = this._poolFirst; particle; particle = next) {
//store next value so we don't lose it in our destroy call
next = particle.next;
particle.destroy();
@@ -1816,61 +1795,57 @@ if (!Array.prototype.random) {
};

PIXI.particles.Emitter = Emitter;

}(PIXI));
})(PIXI);

(function (undefined) {

// Check for window, fallback to global
var global = typeof window !== 'undefined' ? window : GLOBAL;
let global = typeof window !== 'undefined' ? window : GLOBAL;

// Deprecate support for the cloudkid namespace
if (typeof cloudkid === "undefined") {
if (typeof cloudkid === 'undefined')
global.cloudkid = {};
}

// Get classes from the PIXI.particles namespace
Object.defineProperties(global.cloudkid, {
AnimatedParticle: {
get: function () {
if (true) {
console.warn("cloudkid namespace is deprecated, please use PIXI.particles");
}
if (true)
console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
return PIXI.particles.AnimatedParticle;
}
},
Emitter: {
get: function () {
if (true) {
console.warn("cloudkid namespace is deprecated, please use PIXI.particles");
}
if (true)
console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
return PIXI.particles.Emitter;
}
},
Particle: {
get: function () {
if (true) {
console.warn("cloudkid namespace is deprecated, please use PIXI.particles");
}
if (true)
console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
return PIXI.particles.Particle;
}
},
ParticleUtils: {
get: function () {
if (true) {
console.warn("cloudkid namespace is deprecated, please use PIXI.particles");
}
if (true)
console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
return PIXI.particles.ParticleUtils;
}
},
PathParticle: {
get: function () {
if (true) {
console.warn("cloudkid namespace is deprecated, please use PIXI.particles");
}
if (true)
console.warn('cloudkid namespace is deprecated, please use PIXI.particles');
return PIXI.particles.PathParticle;
}
}
});

}());
})();

+ 1
- 0
src/client/plugins/pixi.particles.min.js
File diff suppressed because it is too large
View File


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

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

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

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

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

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

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

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

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

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

+ 0
- 1
src/client/plugins/require.min.js
File diff suppressed because it is too large
View File


+ 0
- 3
src/client/plugins/socket.js
File diff suppressed because it is too large
View File


+ 0
- 391
src/client/plugins/text.js View File

@@ -1,391 +0,0 @@
/**
* @license RequireJS text 2.0.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/requirejs/text for details
*/
/*jslint regexp: true */
/*global require, XMLHttpRequest, ActiveXObject,
define, window, process, Packages,
java, location, Components, FileUtils */

define(['module'], function (module) {
'use strict';

var text, fs, Cc, Ci, xpcIsWindows,
progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
hasLocation = typeof location !== 'undefined' && location.href,
defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
defaultHostName = hasLocation && location.hostname,
defaultPort = hasLocation && (location.port || undefined),
buildMap = {},
masterConfig = (module.config && module.config()) || {};

text = {
version: '2.0.14',

strip: function (content) {
//Strips <?xml ...?> declarations so that external SVG and XML
//documents can be added to a document without worry. Also, if the string
//is an HTML document, only the part inside the body tag is returned.
if (content) {
content = content.replace(xmlRegExp, "");
var matches = content.match(bodyRegExp);
if (matches) {
content = matches[1];
}
} else {
content = "";
}
return content;
},

jsEscape: function (content) {
return content.replace(/(['\\])/g, '\\$1')
.replace(/[\f]/g, "\\f")
.replace(/[\b]/g, "\\b")
.replace(/[\n]/g, "\\n")
.replace(/[\t]/g, "\\t")
.replace(/[\r]/g, "\\r")
.replace(/[\u2028]/g, "\\u2028")
.replace(/[\u2029]/g, "\\u2029");
},

createXhr: masterConfig.createXhr || function () {
//Would love to dump the ActiveX crap in here. Need IE 6 to die first.
var xhr, i, progId;
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else if (typeof ActiveXObject !== "undefined") {
for (i = 0; i < 3; i += 1) {
progId = progIds[i];
try {
xhr = new ActiveXObject(progId);
} catch (e) {}

if (xhr) {
progIds = [progId]; // so faster next time
break;
}
}
}

return xhr;
},

/**
* Parses a resource name into its component parts. Resource names
* look like: module/name.ext!strip, where the !strip part is
* optional.
* @param {String} name the resource name
* @returns {Object} with properties "moduleName", "ext" and "strip"
* where strip is a boolean.
*/
parseName: function (name) {
var modName, ext, temp,
strip = false,
index = name.lastIndexOf("."),
isRelative = name.indexOf('./') === 0 ||
name.indexOf('../') === 0;

if (index !== -1 && (!isRelative || index > 1)) {
modName = name.substring(0, index);
ext = name.substring(index + 1);
} else {
modName = name;
}

temp = ext || modName;
index = temp.indexOf("!");
if (index !== -1) {
//Pull off the strip arg.
strip = temp.substring(index + 1) === "strip";
temp = temp.substring(0, index);
if (ext) {
ext = temp;
} else {
modName = temp;
}
}

return {
moduleName: modName,
ext: ext,
strip: strip
};
},

xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,

/**
* Is an URL on another domain. Only works for browser use, returns
* false in non-browser environments. Only used to know if an
* optimized .js version of a text resource should be loaded
* instead.
* @param {String} url
* @returns Boolean
*/
useXhr: function (url, protocol, hostname, port) {
var uProtocol, uHostName, uPort,
match = text.xdRegExp.exec(url);
if (!match) {
return true;
}
uProtocol = match[2];
uHostName = match[3];

uHostName = uHostName.split(':');
uPort = uHostName[1];
uHostName = uHostName[0];

return (!uProtocol || uProtocol === protocol) &&
(!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
((!uPort && !uHostName) || uPort === port);
},

finishLoad: function (name, strip, content, onLoad) {
content = strip ? text.strip(content) : content;
if (masterConfig.isBuild) {
buildMap[name] = content;
}
onLoad(content);
},

load: function (name, req, onLoad, config) {
//Name has format: some.module.filext!strip
//The strip part is optional.
//if strip is present, then that means only get the string contents
//inside a body tag in an HTML string. For XML/SVG content it means
//removing the <?xml ...?> declarations so the content can be inserted
//into the current doc without problems.

// Do not bother with the work if a build and text will
// not be inlined.
if (config && config.isBuild && !config.inlineText) {
onLoad();
return;
}

masterConfig.isBuild = config && config.isBuild;

var parsed = text.parseName(name),
nonStripName = parsed.moduleName +
(parsed.ext ? '.' + parsed.ext : ''),
url = req.toUrl(nonStripName),
useXhr = (masterConfig.useXhr) ||
text.useXhr;

// Do not load if it is an empty: url
if (url.indexOf('empty:') === 0) {
onLoad();
return;
}

//Load the text. Use XHR if possible and in a browser.
if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
text.get(url, function (content) {
text.finishLoad(name, parsed.strip, content, onLoad);
}, function (err) {
if (onLoad.error) {
onLoad.error(err);
}
});
} else {
//Need to fetch the resource across domains. Assume
//the resource has been optimized into a JS module. Fetch
//by the module name + extension, but do not include the
//!strip part to avoid file system issues.
req([nonStripName], function (content) {
text.finishLoad(parsed.moduleName + '.' + parsed.ext,
parsed.strip, content, onLoad);
});
}
},

write: function (pluginName, moduleName, write, config) {
if (buildMap.hasOwnProperty(moduleName)) {
var content = text.jsEscape(buildMap[moduleName]);
write.asModule(pluginName + "!" + moduleName,
"define(function () { return '" +
content +
"';});\n");
}
},

writeFile: function (pluginName, moduleName, req, write, config) {
var parsed = text.parseName(moduleName),
extPart = parsed.ext ? '.' + parsed.ext : '',
nonStripName = parsed.moduleName + extPart,
//Use a '.js' file name so that it indicates it is a
//script that can be loaded across domains.
fileName = req.toUrl(parsed.moduleName + extPart) + '.js';

//Leverage own load() method to load plugin value, but only
//write out values that do not have the strip argument,
//to avoid any potential issues with ! in file names.
text.load(nonStripName, req, function (value) {
//Use own write() method to construct full module value.
//But need to create shell that translates writeFile's
//write() to the right interface.
var textWrite = function (contents) {
return write(fileName, contents);
};
textWrite.asModule = function (moduleName, contents) {
return write.asModule(moduleName, fileName, contents);
};

text.write(pluginName, nonStripName, textWrite, config);
}, config);
}
};

if (masterConfig.env === 'node' || (!masterConfig.env &&
typeof process !== "undefined" &&
process.versions &&
!!process.versions.node &&
!process.versions['node-webkit'] &&
!process.versions['atom-shell'])) {
//Using special require.nodeRequire, something added by r.js.
fs = require.nodeRequire('fs');

text.get = function (url, callback, errback) {
try {
var file = fs.readFileSync(url, 'utf8');
//Remove BOM (Byte Mark Order) from utf8 files if it is there.
if (file[0] === '\uFEFF') {
file = file.substring(1);
}
callback(file);
} catch (e) {
if (errback) {
errback(e);
}
}
};
} else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
text.createXhr())) {
text.get = function (url, callback, errback, headers) {
var xhr = text.createXhr(), header;
xhr.open('GET', url, true);

//Allow plugins direct access to xhr headers
if (headers) {
for (header in headers) {
if (headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header.toLowerCase(), headers[header]);
}
}
}

//Allow overrides specified in config
if (masterConfig.onXhr) {
masterConfig.onXhr(xhr, url);
}

xhr.onreadystatechange = function (evt) {
var status, err;
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
status = xhr.status || 0;
if (status > 399 && status < 600) {
//An http 4xx or 5xx error. Signal an error.
err = new Error(url + ' HTTP status: ' + status);
err.xhr = xhr;
if (errback) {
errback(err);
}
} else {
callback(xhr.responseText);
}

if (masterConfig.onXhrComplete) {
masterConfig.onXhrComplete(xhr, url);
}
}
};
xhr.send(null);
};
} else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
//Why Java, why is this so awkward?
text.get = function (url, callback) {
var stringBuffer, line,
encoding = "utf-8",
file = new java.io.File(url),
lineSeparator = java.lang.System.getProperty("line.separator"),
input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
content = '';
try {
stringBuffer = new java.lang.StringBuffer();
line = input.readLine();

// Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
// http://www.unicode.org/faq/utf_bom.html

// Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
if (line && line.length() && line.charAt(0) === 0xfeff) {
// Eat the BOM, since we've already found the encoding on this file,
// and we plan to concatenating this buffer with others; the BOM should
// only appear at the top of a file.
line = line.substring(1);
}

if (line !== null) {
stringBuffer.append(line);
}

while ((line = input.readLine()) !== null) {
stringBuffer.append(lineSeparator);
stringBuffer.append(line);
}
//Make sure we return a JavaScript string and not a Java string.
content = String(stringBuffer.toString()); //String
} finally {
input.close();
}
callback(content);
};
} else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
typeof Components !== 'undefined' && Components.classes &&
Components.interfaces)) {
//Avert your gaze!
Cc = Components.classes;
Ci = Components.interfaces;
Components.utils['import']('resource://gre/modules/FileUtils.jsm');
xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);

text.get = function (url, callback) {
var inStream, convertStream, fileObj,
readData = {};

if (xpcIsWindows) {
url = url.replace(/\//g, '\\');
}

fileObj = new FileUtils.File(url);

//XPCOM, you so crazy
try {
inStream = Cc['@mozilla.org/network/file-input-stream;1']
.createInstance(Ci.nsIFileInputStream);
inStream.init(fileObj, 1, 0, false);

convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
.createInstance(Ci.nsIConverterInputStream);
convertStream.init(inStream, "utf-8", inStream.available(),
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);

convertStream.readString(inStream.available(), readData);
convertStream.close();
inStream.close();
callback(readData.value);
} catch (e) {
throw new Error((fileObj && fileObj.path || '') + ': ' + e);
}
};
}
return text;
});

+ 0
- 354
src/client/plugins/wquery.js View File

@@ -1,354 +0,0 @@
const sqData = new Map();

const sq = {
default: function (q) {
const type = typeof(q);
if (type === 'string') {
if (q[0] === '<')
return this.build(q);
return sq.find(q);
} else if (type === 'object')
return sq.wrap([q]);

document.addEventListener('DOMContentLoaded', q, false);
},

build: function (tpl) {
let el = document.createElement('template');
el.innerHTML = tpl.trim();
return sq.wrap([el.content.firstChild]);
},

find: function (queryString) {
if (!this[0])
return sq.wrap(document.querySelectorAll(queryString));

let els = [];
this.each(el => els.push(...el.querySelectorAll(queryString)));
return sq.wrap(els);
},

children: function (filter) {
if (!filter)
return sq.wrap(this[0].children);

return this.find(filter);
},

parent: function () {
let parents = [];
this.each(el => parents.push(el.parentElement));

return sq.wrap(parents);
},

on: function (event, fn, preventDefault) {
let fnHandler = function (fn, el, noDefault, e) {
e.target = el;
requestAnimationFrame(fn.bind(null, e));

if (noDefault) {
e.preventDefault();
return false;
}
};

this.each(el => el.addEventListener(event, fnHandler.bind(null, fn, el, preventDefault)));

return this;
},

off: function () {
let newNodes = [];

this.each(el => {
let newNode = el.cloneNode(true);
el.parentNode.replaceChild(newNode, el);
newNodes.push(newNode);
});

return this.wrap(newNodes);
},

clone: function () {
let newNode = this[0].cloneNode(true);
return this.wrap([newNode]);
},

each: function (fn) {
const len = this.length;
for (let i = 0; i < len; i++)
fn(this[i]);
},

appendTo: function (el) {
if (typeof(el) === 'string')
el = sq.find(el)[0];
else
el = el.on ? el[0] : el;

this.each(c => el.appendChild(c));
return this;
},

prependTo: function (el) {
el[0].insertBefore(this[0], el.children()[0]);
return this;
},

remove: function () {
this.each(el => el.remove());
},

addClass: function (classNames) {
if (classNames)
classNames.split(' ').forEach(c => this.each(el => el.classList.add(c)));
return this;
},

removeClass: function (classNames) {
classNames.split(' ').forEach(c => this.each(el => el.classList.remove(c)));
return this;
},

hasClass: function (className) {
return this[0].classList.contains(className);
},

toggleClass: function (className) {
this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
},

show: function () {
this.each(el => {
if ($(el).css('display') !== 'none')
return;

let newDisplay = el.oldDisplay || 'block';
delete el.oldDisplay;
el.attributeStyleMap.set('display', newDisplay);
});

return this;
},

hide: function () {
this.each(el => {
let oldDisplay = $(el).css('display');
if (oldDisplay !== 'none')
el.oldDisplay = oldDisplay;
el.attributeStyleMap.set('display', 'none');
});
return this;
},

css: function (property, value) {
let config = property;
let aLen = arguments.length;

if (aLen === 1 && typeof(property) === 'string') {
let value = this[0].attributeStyleMap.get(property);
if (!value) {
let styles = this[0].computedStyleMap();
value = (styles.get(property) || {});
}
return value.value;
} else if (aLen === 2) {
config = {
[property]: value
};
}

Object.keys(config).forEach(c => {
let val = config[c];
if (['left', 'top', 'width', 'height'].includes(c) && (!val.indexOf || val.indexOf('%') === -1))
val += 'px';

this.each(el => {
if (val)
el.style[c] = val;

else
el.attributeStyleMap.delete(c);
});
});
return this;
},

empty: function () {
let node = this[0];
while (node.firstChild)
node.removeChild(node.firstChild);

return this;
},

is: function (state) {
return (this.width() && this.height());
},

html: function (val) {
if (!arguments.length)
return this[0].innerHTML;

this[0].innerHTML = val;
return this;
},

click: function () {
this[0].click();
return this;
},

next: function () {
return sq.wrap([this[0].nextSibling]);
},

scrollTop: function (value) {
if (!arguments.length)
return this[0].scrollTop;
this[0].scrollTop = value;
},

val: function (val) {
if (!arguments.length)
return this[0].value;

this[0].value = val;
return this;
},

attr: function (property, value) {
if (arguments.length === 1)
return this[0].getAttribute(property);

this[0].setAttribute(property, value);
return this;
},

insertAfter: function (el) {
el[0].parentNode.insertBefore(this[0], el[0].nextSibling);
return this;
},

insertBefore: function (el) {
el[0].parentNode.insertBefore(this[0], el[0]);
return this;
},

index: function () {
let el = this[0];
return Array.prototype.indexOf.call(el.parentElement.children, el);
},

offset: function () {
return this[0].getBoundingClientRect();
},

data: function (property, value) {
let dataSet = sqData.get(this[0]);
if (!dataSet) {
dataSet = {};
sqData.set(this[0], dataSet);
}

if (arguments.length === 1)
return dataSet[property];

dataSet[property] = value;

return this;
},

removeData: function (property) {
if (this.dataSet)
delete this.dataSet[property];

return this;
},

wrap: function (els) {
let res = {
length: els.length
};

Object.keys(sq)
.forEach(k => res[k] = sq[k].bind(res));

for (let i = 0; i < els.length; i++)
res[i] = els[i];

return res;
},

width: function (val) {
if (val) {
this.css('width', val);
return this;
}

return this[0].offsetWidth;
},

height: function (val) {
if (val) {
this.css('height', val);
return this;
}

return this[0].offsetHeight;
},

eq: function (index) {
return sq.wrap([this[index]]);
},

focus: function () {
this[0].focus();
return this;
},

blur: function () {
this[0].blur();
return this;
},

cloneRecursive: function (o, newO) {
if (typeof o !== 'object')
return o;
if (!o)
return o;
if (o instanceof Array) {
if (!newO || !newO.push)
newO = [];

for (let i = 0; i < o.length; i++)
newO[i] = sq.cloneRecursive(o[i], newO[i]);
return newO;
}

if (!newO || typeof(newO) !== 'object')
newO = {};
for (let i in o) {
if (o.hasOwnProperty(i))
newO[i] = sq.cloneRecursive(o[i], newO[i]);
}
return newO;
},

extend: function (temp, o) {
let aLen = arguments.length;
for (let i = 2; i < aLen; i++)
sq.cloneRecursive(arguments[i], o);

return o;
}
};

window.$ = sq.default.bind(sq);

Object.keys(sq).forEach(k => window.$[k] = sq[k].bind(sq));

+ 0
- 1
src/client/plugins/wquery.min.js View File

@@ -1 +0,0 @@
const sq={default:function(t){const e=typeof t;return"string"===e?"<"===t[0]?this.build(t):sq.find(t):"object"===e?sq.wrap([t]):void document.addEventListener("DOMContentLoaded",t,!1)},build:function(t){let e=document.createElement("template");return e.innerHTML=t.trim(),sq.wrap([e.content.firstChild])},find:function(t){const e=(this[0]||document).querySelectorAll(t);return sq.wrap(e)},children:function(){return sq.wrap(this[0].children)},parent:function(){return sq.wrap([this[0].parentElement])},on:function(t,e){return this.each(n=>n.addEventListener(t,t=>{t.target=n,e(t)})),this},each:function(t){const e=this.length;for(let n=0;n<e;n++)t(this[n])},appendTo:function(t){return t="string"==typeof t?sq.find(t)[0]:t.on?t[0]:t,this.each(e=>t.appendChild(e)),this},prependTo:function(t){return t[0].insertBefore(this[0],t.children()[0]),this},remove:function(){this.each(t=>t.remove())},addClass:function(t){return t.split(" ").forEach(t=>this.each(e=>e.classList.add(t))),this},removeClass:function(t){return t.split(" ").forEach(t=>this.each(e=>e.classList.remove(t))),this},hasClass:function(t){return this[0].classList.contains(t)},toggleClass:function(t){this.hasClass(t)?this.removeClass(t):this.addClass(t)},show:function(){return this.each(t=>t.attributeStyleMap.set("display","block")),this},hide:function(){return this.each(t=>t.attributeStyleMap.set("display","none")),this},css:function(t,e){let n=t,i=arguments.length;return 1===i&&"string"==typeof t?(this[0].attributeStyleMap.get(t)||{}).value:(2===i&&(n={[t]:e}),Object.keys(n).forEach(t=>{let e=n[t];!["left","top","width","height"].includes(t)||e.indexOf&&-1!==e.indexOf("%")||(e+="px"),this.each(n=>e?n.attributeStyleMap.set(t,e):n.attributeStyleMap.delete(t))}),this)},empty:function(){let t=this[0];for(;t.firstChild;)t.removeChild(t.firstChild);return this},is:function(t){return this.width()&&this.height()},html:function(t){return arguments.length?(this[0].innerHTML=t,this):this[0].innerHTML},click:function(){return this[0].click(),this},next:function(){return sq.wrap([this[0].nextSibling])},scrollTop:function(t){if(!arguments.length)return this[0].scrollTop;this[0].scrollTop=t},val:function(t){return arguments.length?(this[0].value=t,this):this[0].value},attr:function(t,e){return 1===arguments.length?this[0].getAttribute(t):(this[0].setAttribute(t,e),this)},insertAfter:function(t){return t[0].parentNode.insertBefore(this[0],t[0].nextSibling),this},insertBefore:function(t){return t[0].parentNode.insertBefore(this[0],t[0]),this},index:function(){let t=this[0];return Array.prototype.indexOf.call(t.parentElement.children,t)},offset:function(){return this[0].getBoundingClientRect()},data:function(t,e){return this.dataSet||(this.dataSet={}),1===arguments.length?this.dataSet[t]:(this.dataSet[t]=e,this)},removeData:function(t){return this.dataSet&&delete this.dataSet[t],this},wrap:function(t){let e={length:t.length};Object.keys(sq).forEach(t=>e[t]=sq[t].bind(e));for(let n=0;n<t.length;n++)e[n]=t[n];return e},width:function(){return this[0].offsetWidth},height:function(){return this[0].offsetHeight},eq:function(t){return sq.wrap([this[t]])},focus:function(){return this[0].focus(),this},cloneRecursive:function(t,e){if("object"!=typeof t)return t;if(!t)return t;if(t instanceof Array){e&&e.push||(e=[]);for(let n=0;n<t.length;n++)e[n]=sq.cloneRecursive(t[n],e[n]);return e}e||(e={});for(let n in t)t.hasOwnProperty(n)&&(e[n]=sq.cloneRecursive(t[n],e[n]));return e},extend:function(t,e){let n=arguments.length;for(let t=2;t<n;t++)sq.cloneRecursive(arguments[t],e);return e}};window.$=sq.default.bind(sq),Object.keys(sq).forEach(t=>window.$[t]=sq[t].bind(sq));

+ 48
- 1
src/client/ui/templates/equipment/equipment.js View File

@@ -150,12 +150,56 @@ define([
.find('.icon')
.css('background', 'url("' + spritesheet + '") ' + imgX + 'px ' + imgY + 'px')
.off()
.on('contextmenu', this.showContext.bind(this, item))
.on('mousedown', this.buildSlot.bind(this, elSlot))
.on('mousemove', this.onHoverItem.bind(this, elSlot, item, null))
.on('mouseleave', this.onHoverItem.bind(this, null, null));
});
},

openAugmentUi: function (item) {
events.emit('onSetSmithItem', {
item: item
});
},

showContext: function (item, e) {
let menuItems = {
unequip: {
text: 'unequip',
callback: this.unequipItem.bind(this, item)
},
augment: {
text: 'craft',
callback: this.openAugmentUi.bind(this, item)
}
};

let config = [];

config.push(menuItems.unequip);

if (item.slot)
config.push(menuItems.augment);

events.emit('onContextMenu', config, e);

e.preventDefault();
return false;
},

unequipItem: function (item) {
client.request({
cpn: 'player',
method: 'performAction',
data: {
cpn: 'equipment',
method: 'unequip',
data: item.id
}
});
},

onInspectTarget: function (result) {
this.isInspecting = true;

@@ -168,6 +212,9 @@ define([
},

buildSlot: function (el, e) {
if (e && e.button !== 0)
return;

if (this.isInspecting)
return;

@@ -408,7 +455,7 @@ define([
'item quality': stats.magicFind + '%',
'item quantity': stats.itemQuantity + '%',
gap1: '',
'sprint chance': (stats.sprintChance || 0) + '%',
'sprint chance': ((~~(stats.sprintChance * 100) / 100) || 0) + '%',
gap2: '',
'xp increase': stats.xpIncrease + '%',
gap3: '',


+ 38
- 6
src/client/ui/templates/equipment/styles.less View File

@@ -5,7 +5,7 @@
z-index: 2;
border: 5px solid @blackB;
text-align: center;
width: 474px;
width: 500px;
height: 712px;

> .heading {
@@ -68,12 +68,16 @@
flex-direction: column;
flex-wrap: wrap;

.left, .right {
.left,
.right {
width: 96px;
padding: 8px;
}

.left, .stats, .right, .bottom {
.left,
.stats,
.right,
.bottom {
height: 528px;
}

@@ -105,8 +109,11 @@
font {
color: @yellowB;
}

}

}

}

.quick {
@@ -117,9 +124,10 @@
justify-content: center;

.slot {

}

}

}

.runes {
@@ -134,10 +142,17 @@
&:last-child {
margin-right: 0px;
}

}

}

.left, .right, .itemList, .runes, .tools, .quick {
.left,
.right,
.itemList,
.runes,
.tools,
.quick {
.slot {
width: 80px;
height: 80px;
@@ -216,6 +231,7 @@
&[slot^="quick-0"] .icon {
background-position: -128px -384px;
}

}

.icon {
@@ -246,8 +262,11 @@
&.empty {
background-color: fade(@blueA, 10%);
}

}

}

}

.itemList {
@@ -284,6 +303,7 @@
drop-shadow(2px 0px 0px @blackD)
drop-shadow(-2px 0px 0px @blackD);
}

}

&.new {
@@ -302,11 +322,17 @@
drop-shadow(2px 0px 0px @blackD)
drop-shadow(-2px 0px 0px @blackD);
}

}

}

}

}

}

}

.mobile .uiEquipment {
@@ -327,7 +353,9 @@
justify-content: space-between;
position: relative;

.left, .right, .quick {
.left,
.right,
.quick {
display: flex;
flex-direction: row;
justify-content: space-between;
@@ -337,6 +365,7 @@
.slot {
margin-bottom: 0px;
}

}

.stats {
@@ -361,6 +390,7 @@
order: 2;
padding: 0px 8px 0px 0px;
}

}

.runes {
@@ -371,5 +401,7 @@
bottom: 8px;
padding: 0px 8px 0px 8px;
}

}

}

+ 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/isleward/tags/v0.3.2">[ Latest Release Notes ]</div>
<div class="news" location="https://gitlab.com/Isleward/isleward/tags/v0.3.3">[ Latest Release Notes ]</div>
<div class="extra">
<div class="el btn btnPatreon" location="https://patreon.com/bigbadwaffle">Pledge on Patreon</div>
<div class="el btn btnPaypal" 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/isleward/tags/v0.3.2">v0.3.2</div>
<div class="version" location="https://gitlab.com/Isleward/isleward/tags/v0.3.3">v0.3.3</div>
</div>

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

@@ -26,7 +26,7 @@ define([
postRender: function () {
this.onEvent('onToggleOptions', this.toggle.bind(this));

//Can only toggle fullscreen directly in a listener, not deferred the way wQuery does it
//Can only toggle fullscreen directly in a listener, not deferred the way jQuery does it
this.el.find('.btnScreen')[0].addEventListener('click', this.toggleScreen.bind(this));
this.el.find('.btnNames').on('click', events.emit.bind(events, 'onKeyDown', 'v'));
this.el.find('.btnCharSelect').on('click', this.charSelect.bind(this));


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

@@ -145,16 +145,6 @@ define([
};
events.emit('onGetAnnouncement', resultMsg);

return;
} else if (msg.item.eq) {
let resultMsg = {
msg: 'Cannot augment equipped items',
type: 'failure',
zIndex: 9999999,
top: 180
};
events.emit('onGetAnnouncement', resultMsg);

return;
}

@@ -180,13 +170,15 @@ define([

this.offEvent(this.eventClickInv);

$('.uiInventory').data('ui').toggle();
$('.uiInventory').data('ui').hide();
this.show();

this.el.show();
this.shown = true;

msg.success = false;

if (!msg || !msg.item || !msg.item.slot || msg.item.eq)
if (!msg || !msg.item || !msg.item.slot)
return;

this.item = msg.item;


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

@@ -56,6 +56,9 @@ define([
})
.next().html(hotkey);

if (spell.autoActive)
el.addClass('active');

//HACK - we don't actually know how long a tick is
if (spell.cd) {
this.onGetSpellCooldowns({


+ 3
- 3
src/server/combat/combat.js View File

@@ -82,9 +82,9 @@ module.exports = {
}

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


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

@@ -321,7 +321,10 @@ module.exports = {
if (!highest || l.threat > highest.threat) {
highest = l;
closest = Math.max(Math.abs(x - obj.x), Math.abs(y - obj.y));
} else if (l.threat === highest.threat) {
} else if (l.threat === highest.threat && l.threat !== 0) {
//Don't chase a closer target if both targets are at 0 threat because
// this means that neither of them have attacked the target.
// This stops people from griefing other players by pulling mobs to them.
let distance = Math.max(Math.abs(x - obj.x), Math.abs(y - obj.y));
if (distance < closest) {
highest = l;


+ 1
- 6
src/server/components/dialogue.js View File

@@ -54,12 +54,8 @@ module.exports = {
this.obj.syncer.set(true, 'dialogue', 'state', null);
},

getState: function (sourceObj, state) {
getState: function (sourceObj, state = 1) {
let result = null;

state = state || 1;

//Goto?
if ((state + '').indexOf('.') > -1) {
let config = this.states[(state + '').split('.')[0]];
if (!config)
@@ -89,7 +85,6 @@ module.exports = {
return null;

let useMsg = stateConfig.msg;

if (stateConfig.cpn) {
let cpn = sourceObj[stateConfig.cpn];
let newArgs = extend([], stateConfig.args);


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

@@ -1,3 +1,5 @@
const { applyItemStats } = require('./equipment/helpers');

module.exports = {
type: 'equipment',

@@ -116,16 +118,7 @@ module.exports = {
this.unequip(this.eq[slot], true);
}

let stats = item.stats;
for (let s in stats) {
let val = stats[s];

obj.stats.addStat(s, val);
}

(item.implicitStats || []).forEach(function (s) {
obj.stats.addStat(s.stat, s.value);
}, this);
applyItemStats(obj, item, true);

item.eq = true;
this.eq[slot] = itemId;
@@ -142,6 +135,7 @@ module.exports = {

obj.fireEvent('afterEquipItem', item);
},

unequip: function (itemId, ignoreSpaceCheck) {
let item = itemId;
if (typeof (itemId) === 'object')
@@ -168,21 +162,11 @@ module.exports = {
return;
}

let stats = item.stats;

delete item.eq;
delete this.eq[item.equipSlot];
delete item.equipSlot;

for (let s in stats) {
let val = stats[s];

obj.stats.addStat(s, -val);
}

(item.implicitStats || []).forEach(function (s) {
obj.stats.addStat(s.stat, -s.value);
}, this);
applyItemStats(obj, item, false);

inventory.setItemPosition(itemId);

@@ -198,6 +182,7 @@ module.exports = {

this.unequipAttrRqrGear();
},

unequipAll: function () {
let eq = this.eq;
Object.keys(this.eq).forEach(function (slot) {
@@ -281,7 +266,8 @@ module.exports = {
let message = ({
int: `You suddenly feel too stupid to wear your ${item.name}`,
str: `Your weak body can no longer equip your ${item.name}`,
dex: `Your sluggish physique cannot possibly equip your ${item.name}`
dex: `Your sluggish physique cannot possibly equip your ${item.name}`,
level: `Your level is too low to equip your ${item.name}`
})[errors[0]];

this.obj.instance.syncer.queue('onGetMessages', {


+ 14
- 0
src/server/components/equipment/helpers.js View File

@@ -0,0 +1,14 @@
module.exports = {
applyItemStats: ({ stats: objStats }, { stats, implicitStats = [] }, isEq) => {
for (let s in stats) {
const value = stats[s];
const useValue = isEq ? value : -value;
objStats.addStat(s, useValue);
}

implicitStats.forEach(({ stat, value }) => {
const useValue = isEq ? value : -value;
objStats.addStat(stat, useValue);
});
}
};

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

@@ -42,8 +42,10 @@ module.exports = {

this.items[name] = list;
this.regenList(list);
} else if (list.level !== requestLevel)
} else if (list.level !== requestLevel) {
list.level = requestLevel;
this.regenList(list);
}

let result = list.items.map(m => {
let item = requestedBy.inventory.simplifyItem(m);


+ 31
- 2
src/server/components/extensions/socialCommands.js View File

@@ -18,6 +18,10 @@ let commandRoles = {
mute: 5,
unmute: 5,

//Super Mods
broadcast: 8,
saveAll: 8,

//Admin
getItem: 10,
getGold: 10,
@@ -44,7 +48,9 @@ let localCommands = [
'roll',
'giveSkin',
'block',
'unblock'
'unblock',
'broadcast',
'saveAll'
];

module.exports = {
@@ -86,7 +92,7 @@ module.exports = {
});
}

if (localCommands.indexOf(actionName) > -1)
if (localCommands.includes(actionName))
this[actionName](config);
else {
atlas.performAction(this.obj, {
@@ -603,6 +609,9 @@ module.exports = {
},

getMaterials: function (config) {
if (typeof(config) === 'object')
config = 100;
let inventory = this.obj.inventory;

Object.entries(configMaterials).forEach(([material, blueprint]) => {
@@ -613,5 +622,25 @@ module.exports = {
...blueprint
});
});
},

broadcast: function (msg) {
if (typeof(msg) === 'object')
msg = Object.keys(msg).join(' ');

cons.emit('event', {
event: 'onGetMessages',
data: {
messages: [{
class: 'color-blueA',
message: msg,
type: 'chat'
}]
}
});
},

saveAll: function () {
connections.forceSaveAll();
}
};

+ 4
- 0
src/server/components/gatherer.js View File

@@ -81,6 +81,10 @@ module.exports = {
return;
}

this.completeGathering(gathering, isFish);
},

completeGathering: function (gathering, isFish) {
let resourceNode = gathering.resourceNode;
let gatherResult = extend({
obj: gathering


+ 49
- 49
src/server/components/inventory.js View File

@@ -5,6 +5,7 @@ let classes = require('../config/spirits');
let mtx = require('../mtx/mtx');
let factions = require('../config/factions');
let itemEffects = require('../items/itemEffects');
const { applyItemStats } = require('./equipment/helpers');

module.exports = {
type: 'inventory',
@@ -142,13 +143,31 @@ module.exports = {
},

enchantItem: function (msg) {
let item = this.findItem(msg.itemId);
if ((!item) || (!item.slot) || (item.eq) || (item.noAugment) || ((msg.action === 'scour') && (item.power === 0))) {
const { itemId, action } = msg;
const item = this.findItem(itemId);
if (!item)
return;

const { eq, slot, power, noAugment } = item;

if (!slot || noAugment || (action === 'scour' && !power)) {
this.resolveCallback(msg);
return;
}

enchanter.enchant(this.obj, item, msg);
const obj = this.obj;

if (eq) {
applyItemStats(obj, item, false);
enchanter.enchant(obj, item, msg);
applyItemStats(obj, item, true);

if (item.slot !== slot)
obj.equipment.unequip(itemId);
} else
enchanter.enchant(obj, item, msg);

obj.equipment.unequipAttrRqrGear();
},

getEnchantMaterials: function (msg) {
@@ -249,15 +268,7 @@ module.exports = {

const hasSpace = this.hasSpace(item, true);
if (!hasSpace) {
this.obj.instance.syncer.queue('onGetMessages', {
id: this.obj.id,
messages: [{
class: 'color-redA',
message: 'Your bags are too full to split that stack',
type: 'info'
}]
}, [this.obj.serverId]);

this.notifyNoBagSpace();
return;
}

@@ -548,6 +559,8 @@ module.exports = {

hookItemEvents: function (items) {
items = items || this.items;
if (!items.push)
items = [ items ];
let iLen = items.length;
for (let i = 0; i < iLen; i++) {
let item = items[i];
@@ -568,6 +581,7 @@ module.exports = {
try {
let effectModule = require('../' + effectUrl);
e.events = effectModule.events;
e.text = effectModule.events.onGetText(item, e);
} catch (error) {}
}
});
@@ -778,7 +792,7 @@ module.exports = {
let existItem = this.items.find(i => i.name === item.name);
if (existItem) {
exists = true;
existItem.quantity = (existItem.quantity || 1) + (item.quantity || 1);
existItem.quantity = ~~(existItem.quantity || 1) + ~~(item.quantity || 1);
item = existItem;
}
}
@@ -793,16 +807,8 @@ module.exports = {
let iLen = items.length;

if (!this.hasSpace(item)) {
if (!hideMessage) {
this.obj.instance.syncer.queue('onGetMessages', {
id: this.obj.id,
messages: [{
class: 'color-redA',
message: 'Your bags are too full to loot any more items',
type: 'info'
}]
}, [this.obj.serverId]);
}
if (!hideMessage)
this.notifyNoBagSpace();

return false;
}
@@ -830,19 +836,18 @@ module.exports = {
}

if (this.obj.player) {
let messages = [];

let msg = item.name;
if (quantity)
msg += ' x' + quantity;
else if ((item.stats) && (item.stats.weight))
msg += ` ${item.stats.weight}lb`;
messages.push({
const messages = [{
class: 'q' + item.quality,
message: 'loot: {' + msg + '}',
item: item,
type: 'loot'
});
}];

if (!hideAlert) {
this.obj.instance.syncer.queue('onGetDamage', {
@@ -860,23 +865,8 @@ module.exports = {
}
}

if (item.effects) {
item.effects.forEach(function (e) {
if (e.mtx) {
let mtxUrl = mtx.get(e.mtx);
let mtxModule = require('../' + mtxUrl);

e.events = mtxModule.events;
} else if (e.type) {
let effectUrl = itemEffects.get(e.type);
try {
let effectModule = require('../' + effectUrl);
e.text = effectModule.events.onGetText(item, e);
e.events = effectModule.events;
} catch (error) {}
}
});
}
if (item.effects)
this.hookItemEvents([item]);

if (!exists)
this.items.push(item);
@@ -896,10 +886,8 @@ module.exports = {
this.obj.syncer.setArray(true, 'inventory', 'getItems', this.simplifyItem(item), true);
}

if (!hideMessage) {
if (fromMob)
this.obj.fireEvent('afterLootMobItem', item);
}
if (!hideMessage && fromMob)
this.obj.fireEvent('afterLootMobItem', item);

return item;
},
@@ -948,7 +936,8 @@ module.exports = {
let itemBlueprint = {
level: this.obj.stats.values.level,
magicFind: magicFind,
bonusMagicFind: bonusMagicFind
bonusMagicFind: bonusMagicFind,
noCurrency: i > 0
};

let useItem = generator.generate(itemBlueprint, playerObject.stats.values.level);
@@ -1067,5 +1056,16 @@ module.exports = {

canEquipItem: function (item) {
return (this.equipItemErrors(item).length === 0);
},

notifyNoBagSpace: function () {
this.obj.instance.syncer.queue('onGetMessages', {
id: this.obj.id,
messages: [{
class: 'color-redA',
message: 'Your bags are too full to loot any more items',
type: 'info'
}]
}, [this.obj.serverId]);
}
};

+ 11
- 6
src/server/components/mob.js View File

@@ -104,7 +104,17 @@ module.exports = {
} while (toX - obj.x !== 0 || toY - obj.y !== 0);
}

if (abs(obj.x - toX) <= 1 && abs(obj.y - toY) <= 1) {
//We use goHome to force followers to follow us around but they should never stay in that state
// since it messes with combat
if (obj.follower)
this.goHome = false;

const dx = abs(obj.x - toX);
const dy = abs(obj.y - toY);

if (dx + dy === 0)
return;
else if (dx <= 1 && dy <= 1) {
obj.queue({
action: 'move',
data: {
@@ -134,11 +144,6 @@ module.exports = {
});
}
}

//We use goHome to force followers to follow us around but they should never stay in that state
// since it messes with combat
if (obj.follower)
this.goHome = false;
},

fight: function (target) {


+ 46
- 42
src/server/components/social.js View File

@@ -137,6 +137,49 @@ module.exports = {
}
},

sendPrivateMessage: function (messageString) {
let playerName = '';
//Check if there's a space in the name
if (messageString[1] === "'") {
playerName = messageString.substring(2, messageString.indexOf("'", 2));
messageString = messageString.replace("@'" + playerName + "' ", '');
} else {
playerName = messageString.substring(1, messageString.indexOf(' '));
messageString = messageString.replace('@' + playerName + ' ', '');
}

if (playerName === this.obj.name)
return;

let target = cons.players.find(p => p.name === playerName);
if (!target)
return;

this.obj.socket.emit('event', {
event: 'onGetMessages',
data: {
messages: [{
class: 'color-yellowB',
message: '(you to ' + playerName + '): ' + messageString,
type: 'chat',
source: this.obj.name
}]
}
});

target.socket.emit('event', {
event: 'onGetMessages',
data: {
messages: [{
class: 'color-yellowB',
message: '(' + this.obj.name + ' to you): ' + messageString,
type: 'chat',
source: this.obj.name
}]
}
});
},

chat: function (msg) {
if (!msg.data.message)
return;
@@ -201,48 +244,9 @@ module.exports = {
};
events.emit('onBeforeSendMessage', msgEvent);
messageString = msgEvent.msg;
if (messageString[0] === '@') {
let playerName = '';
//Check if there's a space in the name
if (messageString[1] === "'") {
playerName = messageString.substring(2, messageString.indexOf("'", 2));
messageString = messageString.replace("@'" + playerName + "' ", '');
} else {
playerName = messageString.substring(1, messageString.indexOf(' '));
messageString = messageString.replace('@' + playerName + ' ', '');
}

if (playerName === this.obj.name)
return;

let target = cons.players.find(p => p.name === playerName);
if (!target)
return;

this.obj.socket.emit('event', {
event: 'onGetMessages',
data: {
messages: [{
class: 'color-yellowB',
message: '(you to ' + playerName + '): ' + messageString,
type: 'chat',
source: this.obj.name
}]
}
});

target.socket.emit('event', {
event: 'onGetMessages',
data: {
messages: [{
class: 'color-yellowB',
message: '(' + this.obj.name + ' to you): ' + messageString,
type: 'chat',
source: this.obj.name
}]
}
});
} else if (messageString[0] === '$')
if (messageString[0] === '@')
this.sendPrivateMessage(messageString);
else if (messageString[0] === '$')
this.sendCustomChannelMessage(msg);
else if (messageString[0] === '%')
this.sendPartyMessage(msg);


+ 71
- 71
src/server/components/stats.js View File

@@ -386,6 +386,70 @@ module.exports = {
}
},

preDeath: function (source) {
const obj = this.obj;

let deathEvent = {};

let killSource = source;

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

if (killSource.player)
killSource.stats.kill(obj);

obj.fireEvent('afterDeath', deathEvent);

if (obj.player) {
obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
if (deathEvent.permadeath) {
obj.auth.permadie();

obj.instance.syncer.queue('onGetMessages', {
messages: {
class: 'color-redA',
message: `(level ${this.values.level}) ${obj.name} has forever left the shores of the living.`
}
}, -1);

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

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

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

if (obj.inventory) {
let aggroList = obj.aggro.list;
let aLen = aggroList.length;
for (let i = 0; i < aLen; i++) {
let a = aggroList[i];

if (a.damage <= 0 || !a.obj.has('serverId'))
continue;

obj.inventory.dropBag(a.obj.name, killSource);
}
}
}
},

die: function (source) {
let obj = this.obj;
let values = this.values;
@@ -479,23 +543,18 @@ module.exports = {
},

takeDamage: function (damage, threatMult, source) {
if (this.values.hp <= 0)
return;

let obj = this.obj;

source.fireEvent('beforeDealDamage', damage, obj);
obj.fireEvent('beforeTakeDamage', damage, source);

//Maybe the attacker was stunned?
if (damage.failed)
return;

//Maybe something else killed this mob already?
if (obj.destroyed)
if (damage.failed || obj.destroyed)
return;

let amount = damage.amount;

if (amount > this.values.hp)
amount = this.values.hp;
let amount = Math.min(this.values.hp, damage.amount);

damage.dealt = amount;

@@ -547,67 +606,8 @@ module.exports = {
obj.instance.eventEmitter.emitNoSticky('onBeforeActorDies', death, obj, source);
obj.fireEvent('beforeDeath', death);

if (death.success) {
let deathEvent = {};

let killSource = source;

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

if (killSource.player)
killSource.stats.kill(obj);

obj.fireEvent('afterDeath', deathEvent);

if (obj.player) {
obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
if (deathEvent.permadeath) {
obj.auth.permadie();

obj.instance.syncer.queue('onGetMessages', {
messages: {
class: 'color-redA',
message: `(level ${this.values.level}) ${obj.name} has forever left the shores of the living.`
}
}, -1);

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

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

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

if (obj.inventory) {
let aggroList = obj.aggro.list;
let aLen = aggroList.length;
for (let i = 0; i < aLen; i++) {
let a = aggroList[i];

if (a.damage <= 0 || !a.obj.has('serverId'))
continue;

obj.inventory.dropBag(a.obj.name, killSource);
}
}
}
}
if (death.success)
this.preDeath(source);
} else {
source.aggro.tryEngage(obj, 0);
obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);


+ 18
- 0
src/server/components/syncer.js View File

@@ -1,11 +1,13 @@
module.exports = {
type: 'syncer',

o: {
components: []
},
oSelf: {
components: []
},

reset: function () {
this.o = {
components: []
@@ -15,6 +17,7 @@ module.exports = {
components: []
};
},

get: function (self) {
let o = this.o;
if (self)
@@ -31,6 +34,7 @@ module.exports = {

return o;
},

set: function (self, cpnType, property, value) {
let o = this.o;
if (self)
@@ -50,6 +54,19 @@ module.exports = {
} else
o[property] = value;
},

setComponent: function (self, cpnType, cpn) {
let o = this.o;
if (self)
o = this.oSelf;

let exists = o.components.find(c => c.type === cpnType);
if (exists)
extend(exists, cpn);
else
o.components.push(cpn);
},

setObject: function (self, cpnType, object, property, value) {
let o = this.o;
if (self)
@@ -71,6 +88,7 @@ module.exports = {

obj[property] = value;
},

setArray: function (self, cpnType, property, value, noDuplicate) {
let o = this.o;
if (self)


+ 15
- 28
src/server/components/trade.js View File

@@ -2,6 +2,17 @@ let generator = require('../items/generator');
let statGenerator = require('../items/generators/stats');
let skins = require('../config/skins');

const sendMessage = ({ instance, id, serverId }, color, message) => {
instance.syncer.queue('onGetMessages', {
id: id,
messages: [{
class: color,
message,
type: 'info'
}]
}, [serverId]);
};

module.exports = {
type: 'trade',

@@ -26,7 +37,7 @@ module.exports = {

init: function (blueprint) {
this.blueprint = blueprint;
this.gold = blueprint.gold;
this.gold = blueprint.gold;
this.items = [];
this.regenCd = this.regenCdMax;

@@ -169,15 +180,7 @@ module.exports = {
canAfford = this.gold >= ~~(item.worth * markup);

if (!canAfford) {
this.obj.instance.syncer.queue('onGetMessages', {
id: this.obj.id,
messages: [{
class: 'color-redA',
message: 'you can\'t afford that item',
type: 'info'
}]
}, [this.obj.serverId]);

sendMessage(this.obj, 'color-redA', 'You can\'t afford that item.');
this.resolveCallback(msg);
return;
}
@@ -191,15 +194,7 @@ module.exports = {
let haveSkin = this.obj.auth.doesOwnSkin(item.skinId);

if (haveSkin) {
this.obj.instance.syncer.queue('onGetMessages', {
id: this.obj.id,
messages: [{
class: 'color-redA',
message: 'you have already unlocked that skin',
type: 'info'
}]
}, [this.obj.serverId]);

sendMessage(this.obj, 'color-redA', 'You have already unlocked that skin.');
this.resolveCallback(msg);
return;
}
@@ -240,15 +235,7 @@ module.exports = {
this.obj.syncer.setArray(true, 'trade', 'removeItems', item.id);
} else {
this.obj.auth.saveSkin(item.skinId);

this.obj.instance.syncer.queue('onGetMessages', {
id: this.obj.id,
messages: [{
class: 'color-greenB',
message: 'Unlocked skin: ' + item.name,
type: 'info'
}]
}, [this.obj.serverId]);
sendMessage(this.obj, 'color-greenB', `Unlocked skin: ${item.name}.`);
}

if (item.worth.currency) {


+ 17
- 3
src/server/config/effects/effectLifeDrain.js View File

@@ -1,11 +1,25 @@
let combat = require('../../combat/combat');

module.exports = {
type: 'lifeDrain',

events: {
afterTick: function () {
this.obj.stats.takeDamage({
amount: this.amount
}, 1, this.caster);
let newDamage = combat.getDamage({
source: {
stats: {
values: {}
}
},
isAttack: false,
target: this.obj,
damage: this.amount,
element: this.element,
noScale: this.noScale,
noCrit: true
});
this.obj.stats.takeDamage(newDamage, 1, this.caster);
}
}
};

+ 7
- 2
src/server/config/eventPhases/phaseEventChain.js View File

@@ -56,6 +56,7 @@ module.exports = {
mob.syncer.set(false, 'chatter', 'msg', text);
}
},

addComponents: function (config) {
let objects = this.instance.objects.objects;

@@ -70,13 +71,17 @@ module.exports = {
let mLen = mobs.length;

for (let i = 0; i < mLen; i++) {
let mob = objects.find(o => (o.id === mobs[i]));
let mob = objects.find(o => o.id === mobs[i]);
for (let j = 0; j < cLen; j++) {
let c = components[j];
mob.addComponent(c.type, components[j]);
const newComponent = mob.addComponent(c.type, components[j]);

//Likely, nobody knows about this new component, so we need to sync it
mob.syncer.setComponent(false, c.type, newComponent.simplify());
}
}
},

removeComponents: function (config) {
let objects = this.instance.objects.objects;



+ 6
- 0
src/server/config/herbs.js View File

@@ -25,5 +25,11 @@ module.exports = {
itemSprite: [11, 2],
baseWeight: 3,
ttl: 30
},
'Stink Carp': {
sheetName: 'objects',
itemSprite: [11, 0],
baseWeight: 5,
ttl: 30
}
};

+ 1
- 1
src/server/config/maps/dungeon.json View File

@@ -641,4 +641,4 @@
"type":"map",
"version":1,
"width":200
}
}

+ 1
- 0
src/server/config/maps/dungeon/zone.js View File

@@ -206,6 +206,7 @@ module.exports = {
col: 4,
applyEffect: {
type: 'lifeDrain',
element: 'poison',
ttl: balance.mobs.viridianSerpent.spitDotDuration,
amount: balance.mobs.viridianSerpent.spitDotAmount
},


+ 2
- 2
src/server/config/maps/fjolarok/events/fishingTournament.js View File

@@ -2,7 +2,7 @@ module.exports = {
name: 'Fishing Tournament',
description: 'Catch the heaviest Ancient Carp for a chance to win Angler\'s Marks. Speak with Angler Nayla for more info.',
distance: -1,
cron: '0 19 * * *',
cron: '0 7,19 * * *',

notifications: [{
mark: 0,
@@ -77,7 +77,7 @@ module.exports = {
fish.push(t);
});

let rewardCounts = [35, 20, 10];
let rewardCounts = [25, 15, 7];
let tpl = {
name: 'Angler\'s Mark',
sprite: [12, 9],


+ 2
- 2
src/server/config/maps/fjolarok/map.json View File

@@ -3814,8 +3814,8 @@
"x":0,
"y":0
}],
"nextlayerid":10,
"nextobjectid":882,
"nextlayerid":11,
"nextobjectid":885,
"orientation":"orthogonal",
"properties":[
{


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


+ 39
- 0
src/server/config/maps/newSewer/zone.js View File

@@ -0,0 +1,39 @@
module.exports = {
name: 'Sewer',
level: [11, 13],

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

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

regular: {

},

rare: {
count: 0
},

spells: [{
type: 'whirlwind',
range: 1
}]
}
},
objects: {
'stink carp school': {
max: 3000,
type: 'fish',
quantity: [1, 3]
}
}
};

+ 1
- 1
src/server/config/maps/sewer/map.json View File

@@ -1046,7 +1046,7 @@
"y":0
}],
"nextlayerid":12,
"nextobjectid":891,
"nextobjectid":893,
"orientation":"orthogonal",
"properties":[
{


+ 27
- 0
src/server/config/recipes/alchemy.js View File

@@ -26,4 +26,31 @@ module.exports = [{
name: 'Empty Vial',
quantity: 1
}]
}, {
name: 'Stinky Oil',
description: 'Makes your weapon both stinkier, and hurtier.',
item: {
name: 'Stinky Oil',
type: 'consumable',
sprite: [0, 1],
description: 'Makes your weapon both stinkier, and hurtier.',
worth: 0,
noSalvage: true,
noAugment: true,
uses: 1,
cdMax: 85,
effects: [{
type: 'augmentWeapon',
rolls: {
duration: 500
}
}]
},
materials: [{
name: 'Stink Carp',
quantity: 3
}, {
name: 'Empty Vial',
quantity: 1
}]
}];

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

@@ -1,5 +1,5 @@
module.exports = {
version: '0.3.2',
version: '0.3.3',
port: 4000,
startupMessage: 'Server: ready',
defaultZone: 'fjolarok',
@@ -12,5 +12,7 @@ module.exports = {
//eslint-disable-next-line no-process-env
dbHost: process.env.IWD_DB_HOST || 'localhost',
//eslint-disable-next-line no-process-env
dbPort: process.env.IWD_DB_PORT || 28015
dbPort: process.env.IWD_DB_PORT || 28015,
//eslint-disable-next-line no-process-env
dbName: process.env.IWD_DB_NAME || 'live'
};

+ 11
- 2
src/server/config/spells/spellAura.js View File

@@ -54,7 +54,7 @@ module.exports = {

let range = this.auraRange;

members.forEach(function (m) {
members.forEach(m => {
let effect = effects[m];

let obj = objects.find(f => (f.serverId === m));
@@ -88,7 +88,16 @@ module.exports = {
ttl: -1,
new: true
});
}, this);
});

//Remove effects from players who are no longer in the party
Object.entries(effects).forEach(([serverId, effect]) => {
if (!members.find(m => ~~m === ~~serverId)) {
delete effects[serverId];
const obj = objects.find(f => ~~f.serverId === ~~serverId);
obj.effects.removeEffect(effect);
}
});
},

updateInactive: function () {


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

@@ -9,14 +9,13 @@ module.exports = {

cast: function (action) {
let obj = this.obj;
let { x, y, instance: { physics, syncer } } = obj;

let radius = this.radius;

let x = obj.x;
let y = obj.y;
const particleConfig = extend({}, this.particles);

let physics = obj.instance.physics;
let syncer = obj.instance.syncer;
this.obj.fireEvent('beforeSpawnParticles', this, particleConfig);

const particleConfig = extend({}, this.particles);



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

@@ -239,10 +239,13 @@ module.exports = {
type === 'number' &&
isNaN(value)
) ||
['obj', 'currentAction', 'autoActive', 'events'].includes(p)
['obj', 'currentAction', 'events'].includes(p)
)
continue;

if (p === 'autoActive')
value = value !== null;

values[p] = value;
}



+ 71
- 0
src/server/config/spells/spellWhirlwind.js View File

@@ -0,0 +1,71 @@
const coordinates = [
[[0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1]]
];

//const maxTicks = 8;

module.exports = {
type: 'whirlwind',

cdMax: 5,
manaCost: 10,
range: 1,

damage: 0.0001,
isAttack: true,

channelDuration: 100,

isCasting: false,
ticker: 0,

cast: function (action) {
if (this.isCasting)
return;

//this.isCasting = true;

const { x: playerX, y: playerY } = this.obj;

const coords = coordinates[this.range - 1].map(([x, y]) => [x + playerX, y + playerY]);

let blueprint = {
caster: this.obj.id,
components: [{
idSource: this.obj.id,
type: 'whirlwind',
coordinates: coords,
row: 3,
col: 0
}]
};

this.obj.instance.syncer.queue('onGetObject', blueprint, -1);

return true;
},

reachDestination: function (selfEffect) {
const { effects, destroyed } = this.obj;
if (destroyed)
return;

effects.removeEffect(selfEffect);
},

spawnDamager: function (x, y) {
const { destroyed, instance } = this.obj;
if (destroyed)
return;

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

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

@@ -24,10 +24,6 @@ let startup = {
onDbReady: async function () {
await fixes.fixDb();
setInterval(function () {
global.gc();
}, 60000);

process.on('uncaughtException', this.onError.bind(this));

animations.init();


+ 153
- 130
src/server/items/enchanter.js View File

@@ -7,6 +7,147 @@ let configCurrencies = require('./config/currencies');
let configSlots = require('./config/slots');
let generator = require('./generator');

const reroll = (item, msg) => {
let enchantedStats = item.enchantedStats;
delete item.enchantedStats;
delete item.implicitStats;
delete msg.addStatMsgs;

if ((item.stats) && (item.stats.lvlRequire)) {
item.level += item.stats.lvlRequire;
delete item.originalLevel;
}

item.stats = {};
let bpt = {
slot: item.slot,
type: item.type,
sprite: item.sprite,
spritesheet: item.spritesheet
};
generatorSlots.generate(item, bpt);
generatorTypes.generate(item, bpt);
generatorStats.generate(item, bpt);

for (let p in enchantedStats) {
if (!item.stats[p])
item.stats[p] = 0;

item.stats[p] += enchantedStats[p];

if (p === 'lvlRequire') {
if (!item.originalLevel)
item.originalLevel = item.level;

item.level -= enchantedStats[p];
if (item.level < 1)
item.level = 1;
}
}
item.enchantedStats = enchantedStats || null;
};

const relevel = item => {
if (item.slot === 'tool')
return;

let offset = 1 + ~~(Math.random() * 2);

const maxLevel = consts.maxLevel;

if (!item.originalLevel)
item.level = Math.min(maxLevel, item.level + offset);
else {
offset = Math.min(maxLevel - item.originalLevel, offset);
item.originalLevel = Math.min(maxLevel, item.originalLevel + offset);
item.level = Math.min(maxLevel, item.level + offset);
}
};

const reslot = (item, msg) => {
if (item.effects || item.slot === 'tool')
return;

if (item.originalLevel)
item.level = item.originalLevel;

delete item.enchantedStats;
delete msg.addStatMsgs;

let possibleStats = Object.keys(item.stats || {});

let newItem = generator.generate({
slot: configSlots.getRandomSlot(item.slot),
level: item.level,
quality: item.quality,
stats: possibleStats,
limitSlotStats: true
});

delete item.spritesheet;
delete item.stats;
delete item.spell;
delete item.implicitStats;
delete item.power;

extend(item, newItem);
};

const reforge = item => {
if (!item.spell)
return;

let spellName = item.spell.name.toLowerCase();
let oldSpell = item.spell;
delete item.spell;

generatorSpells.generate(item, {
spellName: spellName
});
item.spell = extend(oldSpell, item.spell);
};

const scour = (item, result) => {
if (!item.power)
return;

for (let p in item.enchantedStats) {
let value = item.enchantedStats[p];

if (item.stats[p]) {
result.addStatMsgs.push({
stat: p,
value: -value
});
item.stats[p] -= value;
if (item.stats[p] <= 0)
delete item.stats[p];

if (p === 'lvlRequire') {
item.level = Math.min(consts.maxLevel, item.level + value);
delete item.originalLevel;
}
}
}

delete item.enchantedStats;
delete item.power;
};

const augment = (item, inventory, result, msg) => {
let newPower = (item.power || 0) + 1;
if (newPower > 3) {
inventory.resolveCallback(msg);
return;
}

item.power = newPower;

generatorStats.generate(item, {
statCount: 1
}, result);
};

module.exports = {
enchant: function (obj, item, msg) {
let inventory = obj.inventory;
@@ -36,142 +177,24 @@ module.exports = {
inventory.destroyItem(invMaterial.id, m.quantity);
});

if (msg.action === 'reroll') {
let enchantedStats = item.enchantedStats;
delete item.enchantedStats;
delete item.implicitStats;
delete msg.addStatMsgs;

if ((item.stats) && (item.stats.lvlRequire)) {
item.level += item.stats.lvlRequire;
delete item.originalLevel;
}

item.stats = {};
let bpt = {
slot: item.slot,
type: item.type,
sprite: item.sprite,
spritesheet: item.spritesheet
};
generatorSlots.generate(item, bpt);
generatorTypes.generate(item, bpt);
generatorStats.generate(item, bpt);

for (let p in enchantedStats) {
if (!item.stats[p])
item.stats[p] = 0;

item.stats[p] += enchantedStats[p];

if (p === 'lvlRequire') {
item.level -= enchantedStats[p];
if (item.level < 1)
item.level = 1;
}
}
item.enchantedStats = enchantedStats || null;
} else if (msg.action === 'relevel') {
if (item.slot === 'tool')
return;

let offset = 1 + ~~(Math.random() * 2);

const maxLevel = consts.maxLevel;

if (!item.originalLevel)
item.level = Math.min(maxLevel, item.level + offset);
else {
offset = Math.min(maxLevel - item.originalLevel, offset);
item.originalLevel = Math.min(maxLevel, item.originalLevel + offset);
item.level = Math.min(maxLevel, item.level + offset);
}
} else if (msg.action === 'reslot') {
if (item.effects || item.slot === 'tool')
return;

if (item.originalLevel)
item.level = item.originalLevel;

delete item.enchantedStats;
delete msg.addStatMsgs;

let possibleStats = Object.keys(item.stats || {});

let newItem = generator.generate({
slot: configSlots.getRandomSlot(item.slot),
level: item.level,
quality: item.quality,
stats: possibleStats,
limitSlotStats: true
});

delete item.spritesheet;
delete item.stats;
delete item.spell;
delete item.implicitStats;
delete item.power;

extend(item, newItem);
} else if (msg.action === 'reforge') {
if (!item.spell)
return;

let spellName = item.spell.name.toLowerCase();
let oldSpell = item.spell;
delete item.spell;

generatorSpells.generate(item, {
spellName: spellName
});
item.spell = extend(oldSpell, item.spell);
} else if (msg.action === 'scour') {
if (!item.power)
return;

for (let p in item.enchantedStats) {
let value = item.enchantedStats[p];

if (item.stats[p]) {
result.addStatMsgs.push({
stat: p,
value: -value
});
item.stats[p] -= value;
if (item.stats[p] <= 0)
delete item.stats[p];

if (p === 'lvlRequire') {
item.level = Math.min(consts.maxLevel, item.level + value);
delete item.originalLevel;
}
}
}

delete item.enchantedStats;
delete item.power;
} else {
let newPower = (item.power || 0) + 1;
if (newPower > 3) {
inventory.resolveCallback(msg);
return;
}

item.power = newPower;
this.addStat(item, result);
}
if (msg.action === 'reroll')
reroll(item, msg);
else if (msg.action === 'relevel')
relevel(item);
else if (msg.action === 'reslot')
reslot(item, msg);
else if (msg.action === 'reforge')
reforge(item);
else if (msg.action === 'scour')
scour(item, result);
else
augment(item, inventory, result, msg);

obj.syncer.setArray(true, 'inventory', 'getItems', inventory.simplifyItem(item));

inventory.resolveCallback(msg, result);
},

addStat: function (item, result) {
generatorStats.generate(item, {
statCount: 1
}, result);
},

getEnchantMaterials: function (item, action) {
let result = null;



+ 7
- 0
src/server/items/generator.js View File

@@ -39,6 +39,8 @@ module.exports = {
const levelDelta = ownerLevel - blueprint.level;
currencyChance /= Math.pow(levelDelta - 3, 2);
}
if (blueprint.noCurrency)
currencyChance = 0;

if ((!blueprint.slot) && (!blueprint.noSpell)) {
isSpell = blueprint.spell;
@@ -94,6 +96,11 @@ module.exports = {
}

delete item.stats[stat];

if (stat === 'lvlRequire') {
item.level = item.originalLevel;
delete item.originalLevel;
}
},

pickRandomSlot: function () {


+ 8
- 4
src/server/objects/objBase.js View File

@@ -190,8 +190,12 @@ module.exports = {
this.actionQueue.spliceWhere(a => a.priority);
this.actionQueue.splice(0, 0, action);
} else {
if (action.priority)
if (action.priority) {
this.spellbook.stopCasting();
this.actionQueue.splice(0, 0, action);
return;
}

this.actionQueue.push(action);
}
},
@@ -266,7 +270,7 @@ module.exports = {

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

data.success = true;
this.fireEvent('beforeMove', data);
@@ -289,7 +293,7 @@ module.exports = {
(deltaX === 0) &&
(deltaY === 0)
)
)
)
return false;
}

@@ -300,7 +304,7 @@ module.exports = {

this.x = data.x;
this.y = data.y;
} else
} else
return false;
} else {
physics.removeObject(this, this.x, this.y, data.x, data.y);


+ 12
- 10
src/server/package-lock.json View File

@@ -168,11 +168,6 @@
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
},
"bluebird": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
"integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE="
},
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
@@ -1401,12 +1396,19 @@
}
}
},
"rethinkdb": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/rethinkdb/-/rethinkdb-2.3.3.tgz",
"integrity": "sha1-PcZYbiL6HavuDSVOZL0ON5+tL3I=",
"rethinkdbdash": {
"version": "2.3.31",
"resolved": "https://registry.npmjs.org/rethinkdbdash/-/rethinkdbdash-2.3.31.tgz",
"integrity": "sha512-6nXrKFjdg2Ug0YpdmPWSvyD/2EisHnFNt4FWZ74dcXGK48ievSv+cNFTmVv+KjLi6I9CCf6w4CKZ6yCYTfMfdQ==",
"requires": {
"bluebird": ">= 2.3.2 < 3"
"bluebird": ">= 3.0.1"
},
"dependencies": {
"bluebird": {
"version": "3.5.5",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w=="
}
}
},
"rimraf": {


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

@@ -9,7 +9,7 @@
"express-minify": "^1.0.0",
"google-spreadsheet": "^2.0.7",
"less-middleware": "^3.1.0",
"rethinkdb": "^2.3.3",
"rethinkdbdash": "^2.3.31",
"socket.io": "^2.2.0",
"sqlite3": "^4.0.6",
"universal-analytics": "^0.4.20"


+ 0
- 226
src/server/security/ioFirebase.js View File

@@ -1,226 +0,0 @@
let firebase = require('firebase');
let fConfig = require('./firebaseConfig');

let fs = require('fs');

module.exports = {
db: null,
io: null,

init: async function (oldIo, convert) {
this.io = oldIo;

firebase.initializeApp(fConfig);
this.db = firebase.database();

if (!process.send && convert)
await this.convert();

this.bindHandlers();
},

bindHandlers: function () {
this.io.process = () => {};

this.io.get = this.get.bind(this);
this.io.set = this.set.bind(this);

this.io.getAsync = this.getAsync.bind(this);
this.io.setAsync = this.setAsync.bind(this);

this.io.getAllAsync = this.getAllAsync.bind(this);
this.io.deleteAsync = this.deleteAsync.bind(this);

this.io.on = this.on.bind(this);
},

convert: async function () {
const skip = [
'character',
'characterList',
'stash',
'skins',
'login',
'leaderboard',
'customMap',
//'mail',
'customChannels',
'error',
'modLog',
'accountInfo'
];

let tables = Object.keys(io.tables);
for (let table of tables) {
if (await this.tableExists(table))
continue;

await this.createTable(table);
}

const options = {
login: {
noParse: true
},
mail: {
noParse: true
},
error: {
noParse: true
},
skins: {
noParse: true
}
};

for (let table of tables) {
if (skip.includes(table)) {
//eslint-disable-next-line no-console
console.log(`Skipping ${table}`);
continue;
}

//eslint-disable-next-line no-console
console.log(`Converting ${table}`);
if (table === 'character') {
await this.convertCorrupted('character', {}, true);

continue;
} else if (table === 'mail') {
await this.convertCorrupted('mail', {
noParse: true
});

continue;
}

let records = await io.getAllAsync({
table: table,
...options[table]
});

let length = records.length;
//eslint-disable-next-line no-console
console.log(`${length} records`);
let i = 0;
for (let record of records) {
//eslint-disable-next-line no-console
console.log(++i + '/' + length);
//if (table === 'login' && i < 12400)
// continue;

if (!record.key || record.key.indexOf('.') > -1 || record.key.indexOf('$') > -1 || record.key.indexOf('#') > -1) {
//eslint-disable-next-line no-console
console.log(`Invalid key ${record.key}`);
fs.appendFileSync(`failed-${table}`, record.key + '\r\n');
continue;
} else
await this.write(table, record.key, record.value);
}
}
},

convertCorrupted: async function (table, options, showErrors) {
let records = await io.getAllAsync({
table: 'characterList'
});

let length = records.length;
let i = 0;
for (let record of records) {
//eslint-disable-next-line no-console
console.log(++i + '/' + length);
let charList = record.value;
for (let charName of charList) {
charName = charName.name || charName;

try {
let character = await io.getAsync({
table: table,
key: charName,
...options
});

if (!character) {
if (showErrors) {
//eslint-disable-next-line no-console
console.log(charName + ' failed');
}
} else
await this.write(table, charName, character);
} catch (e) {}
}
}
},

tableExists: async function (table) {
let res = await this.db.ref(`/${table}/{placeholder}/`).once('value');
return (res.val() === 1);
},

createTable: async function (table) {
await this.db.ref(`/${table}/`).set({
'{placeholder}': 1
});
},

on: async function (options) {
this.db.ref(options.path).on('value', res => {
options.callback(res.val());
});
},

get: async function (options) {
let res = await this.db.ref(`/${options.field}/${options.ent}`).once('value');

if (options.callback)
options.callback(res.val());
},

set: async function (options) {
await this.db.ref(`/${options.field}/${options.ent}`).set(options.value);

if (options.callback)
options.callback();
},

getAsync: async function (options) {
let res = (await this.db.ref(`/${options.table}/${options.key}`).once('value')).val();
if (!res && !options.noDefault && !options.noParse)
res = options.isArray ? [] : {};
else if (typeof(res) === 'string' && !options.noParse)
res = JSON.parse(res);

return res;
},

setAsync: async function (options) {
await this.db.ref(`/${options.table}/${options.key}`).set(options.value);
},

getAllAsync: async function (options) {
let res = await this.db.ref(`/${options.table}`).once('value');
res = res.val();
res = Object.keys(res).map(r => {
return {
key: r,
value: res[r]
};
});

return res;
},

deleteAsync: async function (options) {
await this.db.ref(`/${options.table}/${options.key}`).remove();
},

read: async function (table, key) {
let res = await this.db.ref(`/${table}/${key}`).once('value');
return res.val();
},

write: async function (table, key, value) {
await this.db.ref(`/${table}/${key}`).set(value);
}
};

+ 16
- 27
src/server/security/ioRethink.js View File

@@ -1,5 +1,10 @@
let r = require('rethinkdb');
let serverConfig = require('../config/serverConfig');
const serverConfig = require('../config/serverConfig');

const r = require('rethinkdbdash')({
host: serverConfig.dbHost,
port: serverConfig.dbPort,
db: serverConfig.dbName
});

const dbConfig = {
host: serverConfig.dbHost,
@@ -26,8 +31,6 @@ module.exports = {
staticCon: null,

init: async function (cbReady) {
this.staticCon = await this.getConnection();

await this.create();

cbReady();
@@ -41,14 +44,14 @@ module.exports = {
const con = await this.getConnection();

try {
await r.dbCreate(this.useDb).run(con);
await r.dbCreate(this.useDb).run();
} catch (e) {

}

for (const table of tables) {
try {
await r.tableCreate(table).run(con);
await r.tableCreate(table).run();
} catch (e) {
if (!e.message.includes('already exists'))
_.log(e);
@@ -68,7 +71,7 @@ module.exports = {

let res = await r.table(table)
.get(key)
.run(con);
.run();

if (res)
return res.value;
@@ -89,9 +92,7 @@ module.exports = {
const con = await this.getConnection();

let res = await r.table(table)
.run(con);

res = await res.toArray();
.run();

if (res)
return res;
@@ -109,8 +110,6 @@ module.exports = {
value,
conflict = 'update'
}) {
const con = await this.getConnection();

try {
await r.table(table)
.insert({
@@ -119,12 +118,10 @@ module.exports = {
}, {
conflict
})
.run(con);
.run();
} catch (e) {
this.logError(e, table, id);
}

con.close();
},

deleteAsync: async function ({
@@ -136,15 +133,13 @@ module.exports = {
await r.table(table)
.get(key)
.delete()
.run(con);

con.close();
.run();
},

subscribe: function (table) {
return r.table(table)
.changes()
.run(this.staticCon);
.run();
},

append: async function ({
@@ -153,8 +148,6 @@ module.exports = {
value,
field
}) {
const con = await this.getConnection();

try {
await r.table(table)
.get(key)
@@ -169,12 +162,10 @@ module.exports = {
}
);
})
.run(con);
.run();
} catch (e) {
this.logError(e, table, key);
}

con.close();
},

exists: async function ({
@@ -185,9 +176,7 @@ module.exports = {

let res = await r.table(table)
.get(key)
.run(con);

con.close();
.run();

return !!res;
},


+ 6
- 29
src/server/world/map.js View File

@@ -6,6 +6,8 @@ let globalZone = require('../config/zoneBase');
let randomMap = require('./randomMap');
let events = require('../misc/events');

const mapObjects = require('./map/mapObjects');

let mapFile = null;
let mapScale = null;
let padding = null;
@@ -325,8 +327,9 @@ module.exports = {
}
},
object: function (layerName, cell) {
//Fix for newer versions of tiled
//Fixes for newer versions of tiled
cell.properties = objectifyProperties(cell.properties);
cell.polyline = cell.polyline || cell.polygon;

let clientObj = (layerName === 'clientObjects');
let cellInfo = this.builders.getCellInfo(cell.gid);
@@ -370,34 +373,8 @@ module.exports = {
} else if (cell.width === 24)
blueprint.x++;

if (cell.polyline) {
let lowX = this.size.w;
let lowY = this.size.h;
let highX = 0;
let highY = 0;

blueprint.area = cell.polyline.map(function (v) {
let x = ~~((v.x + cell.x) / mapScale);
let y = ~~((v.y + cell.y) / mapScale);

if (x < lowX)
lowX = x;
if (x > highX)
highX = x;

if (y < lowY)
lowY = y;
if (y > highY)
highY = y;

return [x, y];
});

blueprint.x = lowX;
blueprint.y = lowY;
blueprint.width = (highX - lowX);
blueprint.height = (highY - lowY);
}
if (cell.polyline)
mapObjects.polyline(this.size, blueprint, cell, mapScale);

if (layerName === 'rooms') {
if (blueprint.properties.exit) {


+ 30
- 0
src/server/world/map/mapObjects.js View File

@@ -0,0 +1,30 @@
module.exports = {
polyline: function (size, blueprint, cell, mapScale) {
let lowX = size.w;
let lowY = size.h;
let highX = 0;
let highY = 0;

blueprint.area = cell.polyline.map(function (v) {
let x = ~~((v.x + cell.x) / mapScale);
let y = ~~((v.y + cell.y) / mapScale);

if (x < lowX)
lowX = x;
if (x > highX)
highX = x;

if (y < lowY)
lowY = y;
if (y > highY)
highY = y;

return [x, y];
});

blueprint.x = lowX;
blueprint.y = lowY;
blueprint.width = (highX - lowX);
blueprint.height = (highY - lowY);
}
};

+ 97
- 90
src/server/world/syncer.js View File

@@ -12,125 +12,133 @@ module.exports = {
let oList = objects.objects;
let oLen = oList.length;

//OPTIMIZE: Store a list of all players
let pList = oList.filter(f => f.player);
let pLen = pList.length;

if (pLen === 0)
this.updateZoneEmpty(objects, oList, oLen);
else if (pLen > 0)
this.updateZoneNotEmpty(objects, oList, oLen, pList, pLen);

oLen = oList.length;

for (let i = 0; i < oList.length; i++)
oList[i].syncer.reset();
},

updateZoneEmpty: function (objects, oList, oLen) {
for (let i = 0; i < oLen; i++) {
let o = oList[i];
if (!o.destroyed)
continue;

objects.removeObject(o);

oLen--;
i--;
}
},

updateZoneNotEmpty: function (objects, oList, oLen, pList, pLen) {
let queueFunction = this.queue.bind(this, 'onGetObject');

let cache = {};

if (pLen === 0) {
for (let i = 0; i < oLen; i++) {
let o = oList[i];
if (!o.destroyed)
continue;
for (let i = 0; i < oLen; i++) {
let o = oList[i];
let oId = o.id;
let ox = o.x;
let oy = o.y;

if (!o.syncer)
continue;

let destroyed = o.destroyed;
let sync = null;
let syncSelf = null;
if (!destroyed) {
sync = o.syncer.get();
syncSelf = o.syncer.get(true);
} else {
sync = {
id: o.id,
destroyed: true
};

objects.removeObject(o);

oLen--;
i--;
}
} else if (pLen > 0) {
let queueFunction = this.queue.bind(this, 'onGetObject');

for (let i = 0; i < oLen; i++) {
let o = oList[i];
let oId = o.id;
let ox = o.x;
let oy = o.y;

if (!o.syncer)
continue;

let destroyed = o.destroyed;
let sync = null;
let syncSelf = null;
if (!destroyed) {
sync = o.syncer.get();
syncSelf = o.syncer.get(true);
} else {
sync = {
id: o.id,
destroyed: true
};

objects.removeObject(o);

oLen--;
i--;
}

let toList = [];
let completeList = [];
let completeObj = null;

let sendTo = false;
let sendComplete = false;

for (let j = 0; j < pLen; j++) {
let p = pList[j];
let px = p.x;
let py = p.y;
let toList = [];
let completeList = [];
let completeObj = null;

let canSee = (Math.abs(ox - px) <= 25 && Math.abs(oy - py) < 14);
let sendTo = false;
let sendComplete = false;

let hasSeen = p.player.hasSeen(oId);
for (let j = 0; j < pLen; j++) {
let p = pList[j];
let px = p.x;
let py = p.y;

if (hasSeen) {
if (canSee) {
if (p.id === oId && syncSelf)
queueFunction(syncSelf, [ p.serverId ]);
let canSee = (Math.abs(ox - px) <= 25 && Math.abs(oy - py) < 14);

if (sync) {
toList.push(p.serverId);
sendTo = true;
}
}
let hasSeen = p.player.hasSeen(oId);

if (destroyed || !canSee) {
if (!canSee)
queueFunction({ id: oId, destroyed: true }, [ p.serverId ]);
if (hasSeen) {
if (canSee) {
if (p.id === oId && syncSelf)
queueFunction(syncSelf, [ p.serverId ]);

p.player.unsee(oId);
}
} else if (!destroyed && canSee) {
let cached = null;
if (p.id === oId) {
let syncO = o.getSimple(true);
syncO.self = true;
queueFunction(syncO, [ p.serverId ]);
p.player.see(oId);
continue;
} else {
cached = cache[oId];
if (!cached)
cached = cache[oId] = o.getSimple();
if (sync) {
toList.push(p.serverId);
sendTo = true;
}
}

completeObj = cached;
completeList.push(p.serverId);
sendComplete = true;
if (destroyed || !canSee) {
if (!canSee)
queueFunction({ id: oId, destroyed: true }, [ p.serverId ]);

p.player.unsee(oId);
}
} else if (!destroyed && canSee) {
let cached = null;
if (p.id === oId) {
let syncO = o.getSimple(true);
syncO.self = true;
queueFunction(syncO, [ p.serverId ]);
p.player.see(oId);
continue;
} else {
cached = cache[oId];
if (!cached)
cached = cache[oId] = o.getSimple();
}
}

if (sendTo)
queueFunction(sync, toList);
if (sendComplete)
queueFunction(completeObj, completeList);
completeObj = cached;
completeList.push(p.serverId);
sendComplete = true;

p.player.see(oId);
}
}

if (sendTo)
queueFunction(sync, toList);
if (sendComplete)
queueFunction(completeObj, completeList);
}

this.send();

for (let i = 0; i < oLen; i++)
oList[i].syncer.reset();
},

queue: function (event, obj, to) {
//Send to all players in zone?
if (to === -1) {
//OPTIMIZE: Store a list of all players
let pList = this.objects.objects.filter(o => o.player);
to = pList.map(p => p.serverId);
}
@@ -147,17 +155,16 @@ module.exports = {
obj: obj
});
},

send: function () {
if (!this.dirty)
return;

this.dirty = false;

let buffer = this.buffer;

process.send({
method: 'events',
data: buffer
data: this.buffer
});

this.buffer = {};


+ 0
- 4
src/server/world/worker.js View File

@@ -45,10 +45,6 @@ let onDbReady = function () {

mods.init(onModsReady);

setInterval(function () {
global.gc();
}, 60000);

process.on('uncaughtException', async function (e) {
if (e.toString().indexOf('ERR_IPC_CHANNEL_CLOSED') > -1)
return;


Loading…
Cancel
Save