Browse Source

Merge branch '1832-buffs' into 'master'

Draft: Rewrite buffs

Closes #1832

See merge request Isleward/isleward!564
merge-requests/564/merge
kckckc 2 years ago
parent
commit
bdd86ecf50
21 changed files with 378 additions and 267 deletions
  1. +2
    -2
      src/client/js/components/components.js
  2. +0
    -59
      src/client/ui/templates/buffs/buffs.js
  3. +0
    -1
      src/client/ui/templates/buffs/styles.css
  4. +0
    -1
      src/client/ui/templates/buffs/template.html
  5. +54
    -0
      src/client/ui/templates/effects/effects.js
  6. +1
    -0
      src/client/ui/templates/effects/styles.css
  7. +2
    -2
      src/client/ui/templates/effects/styles.less
  8. +1
    -0
      src/client/ui/templates/effects/template.html
  9. +1
    -1
      src/client/ui/templates/effects/templateEffect.html
  10. +83
    -91
      src/server/clientComponents/effects.js
  11. +136
    -0
      src/server/clientComponents/effects/auras.js
  12. +60
    -74
      src/server/components/effects.js
  13. +6
    -1
      src/server/config/clientConfig.js
  14. +15
    -1
      src/server/config/effects/effectTemplate.js
  15. +4
    -5
      src/server/config/spells/spellAura.js
  16. +3
    -12
      src/server/config/spells/spellCharge.js
  17. +2
    -3
      src/server/config/spells/spellFireblast.js
  18. +1
    -9
      src/server/config/spells/spellIceSpear.js
  19. +1
    -1
      src/server/config/spells/spellReflectDamage.js
  20. +5
    -3
      src/server/config/spells/spellStealth.js
  21. +1
    -1
      src/server/config/spells/spellTrailDash.js

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

@@ -31,7 +31,7 @@ define([
if (cpn.type)
templates.push(tpl);
if (cpn.extends)
extenders.push(tpl);
extenders.push({ extends: cpn.extends, tpl });
res();
});
@@ -49,7 +49,7 @@ define([
templates.forEach(t => {
const extensions = extenders.filter(e => e.extends === t.type);

extensions.forEach(e => $.extend(true, t, e));
extensions.forEach(e => $.extend(true, t, e.tpl));

t.eventList = {};
t.hookEvent = hookEvent;


+ 0
- 59
src/client/ui/templates/buffs/buffs.js View File

@@ -1,59 +0,0 @@
define([
'js/system/events',
'html!ui/templates/buffs/template',
'css!ui/templates/buffs/styles',
'html!ui/templates/buffs/templateBuff'
], function (
events,
template,
styles,
templateBuff
) {
let icons = {
stunned: [4, 0],
regenHp: [3, 1],
regenMana: [4, 1],
swiftness: [5, 1],
stealth: [7, 0],
reflectDamage: [2, 1],
holyVengeance: [4, 0]
};

return {
tpl: template,

icons: {},

postRender: function () {
this.onEvent('onGetBuff', this.onGetBuff.bind(this));
this.onEvent('onRemoveBuff', this.onRemoveBuff.bind(this));
},

onGetBuff: function (buff) {
let icon = icons[buff.type];
if (!icon)
return;

let imgX = icon[0] * -32;
let imgY = icon[1] * -32;

let html = templateBuff;
let el = $(html).appendTo(this.el)
.find('.inner')
.css({
background: 'url(../../../images/statusIcons.png) ' + imgX + 'px ' + imgY + 'px'
});

this.icons[buff.id] = el.parent();
},

onRemoveBuff: function (buff) {
let el = this.icons[buff.id];
if (!el)
return;

el.remove();
delete this.icons[buff.id];
}
};
});

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

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

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

@@ -1 +0,0 @@
<div class="uiBuffs"></div>

+ 54
- 0
src/client/ui/templates/effects/effects.js View File

@@ -0,0 +1,54 @@
define([
'html!ui/templates/effects/template',
'css!ui/templates/effects/styles',
'html!ui/templates/effects/templateEffect'
], function (
template,
styles,
templateEffect
) {
return {
tpl: template,

icons: {},

postRender: function () {
this.onEvent('onGetEffectIcon', this.onGetEffectIcon.bind(this));
this.onEvent('onRemoveEffectIcon', this.onRemoveEffectIcon.bind(this));
},

buildIcon: function (config) {
let { icon, url } = config;

if (!url)
url = '../../../images/statusIcons.png';

let imgX = icon[0] * -32;
let imgY = icon[1] * -32;

let html = templateEffect;
let el = $(html).appendTo(this.el)
.find('.inner')
.css({
background: `url(${url}) ${imgX}px ${imgY}px`
});

return el.parent();
},

onGetEffectIcon: function (config) {
let el = this.buildIcon(config);

this.icons[config.id] = el;
},

onRemoveEffectIcon: function (config) {
let el = this.icons[config.id];
if (!el)
return;

el.remove();
delete this.icons[config.id];
}
};
});

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

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

src/client/ui/templates/buffs/styles.less → src/client/ui/templates/effects/styles.less View File

@@ -1,6 +1,6 @@
@import "../../../css/colors.less";

.uiBuffs {
.uiEffects {
position: absolute;
left: 16px;
top: 104px;
@@ -23,7 +23,7 @@

}

.mobile .uiBuffs {
.mobile .uiEffects {
left: 316px;
top: 10px;
}

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

@@ -0,0 +1 @@
<div class="uiEffects"></div>

src/client/ui/templates/buffs/templateBuff.html → src/client/ui/templates/effects/templateEffect.html View File

@@ -1,3 +1,3 @@
<div class="icon buff">
<div class="icon effect">
<div class="inner"></div>
</div>

+ 83
- 91
src/server/clientComponents/effects.js View File

@@ -1,135 +1,127 @@
define([
'js/rendering/renderer'
'js/system/events',
'js/rendering/numbers'
], function (
renderer
events,
numbers
) {
let auras = {
reflectDamage: 0,
stealth: 1,
regenHp: 9,
regenMana: 10,
swiftness: 11,
holyVengeance: 8,
rare: 16
const defaultBuffIcons = {
stunned: [4, 0]
};

return {
type: 'effects',
const effectBase = {
init: function () {
this.defaultDamageText(false);

alpha: 0,
alphaDir: 0.0025,
if (this.self && defaultBuffIcons[this.type]) {
events.emit('onGetEffectIcon', {
id: this.id,
icon: defaultBuffIcons[this.type]
});
}
},

alphaMax: 0.6,
alphaMin: 0.35,
destroy: function () {
if (!this.obj.destroyed)
this.defaultDamageText(true);

alphaCutoff: 0.4,
if (this.self && defaultBuffIcons[this.type]) {
events.emit('onRemoveEffectIcon', {
id: this.id
});
}
},

defaultDamageText: function (removing) {
numbers.onGetDamage({
id: this.obj.id,
event: true,
text: (removing ? '-' : '+') + this.type
});
}
};

return {
type: 'effects',

effects: [],

templates: {
},

init: function (blueprint) {
this.effects = this.effects
.filter(e => auras[e] !== null)
.map(e => {
return {
name: e,
sprite: renderer.buildObject({
layerName: 'effects',
sheetName: 'auras',
x: this.obj.x,
y: this.obj.y + 1,
w: scale * 3,
h: scale * 3,
cell: auras[e]
})
};
});
this.effects = this.effects.map(e => this.buildEffect(e));
},

buildEffect: function (data) {
let template = this.templates[data.type] || {};

let effect = $.extend(true, {}, effectBase, template, data);

effect.self = !!this.obj.self;
effect.obj = this.obj;

if (effect.init)
effect.init();
return effect;
},

extend: function (blueprint) {
if (blueprint.addEffects) {
blueprint.addEffects = blueprint.addEffects
.filter(e => {
return (auras[e] !== null);
})
.map(e => {
return {
name: e,
sprite: renderer.buildObject({
layerName: 'effects',
sheetName: 'auras',
x: this.obj.x,
y: this.obj.y + 1,
w: scale * 3,
h: scale * 3,
cell: auras[e]
})
};
});
blueprint.addEffects = blueprint.addEffects.map(e => this.buildEffect(e));

this.effects.push.apply(this.effects, blueprint.addEffects || []);
}
if (blueprint.removeEffects) {
blueprint.removeEffects.forEach(r => {
let effect = this.effects.find(e => e.name === r);
blueprint.removeEffects.forEach(removeId => {
let effect = this.effects.find(e => e.id === removeId);

if (!effect)
return;

renderer.destroyObject({
layerName: 'effects',
sprite: effect.sprite
});
if (effect.destroy)
effect.destroy();

this.effects.spliceFirstWhere(e => e.name === r);
this.effects.spliceFirstWhere(e => e.id === removeId);
});
}
},

update: function () {
this.alpha += this.alphaDir;
if ((this.alphaDir > 0) && (this.alpha >= this.alphaMax)) {
this.alpha = this.alphaMax;
this.alphaDir *= -1;
} else if ((this.alphaDir < 0) && (this.alpha <= this.alphaMin)) {
this.alpha = this.alphaMin;
this.alphaDir *= -1;
}
if (blueprint.extendEffects) {
blueprint.extendEffects.forEach(u => {
let effect = this.effects.find(e => e.id === u.id);

let x = this.obj.x;
let y = this.obj.y;
if (!effect)
return;

let useAlpha = this.alpha;
if (useAlpha < this.alphaCutoff)
useAlpha = 0;
else {
useAlpha -= this.alphaCutoff;
useAlpha /= (this.alphaMax - this.alphaCutoff);
if (effect.extend)
effect.extend(u.data);
else {
for (let p in u.data)
effect[p] = u.data[p];
}
});
}
},

update: function () {
this.effects.forEach(e => {
renderer.setSpritePosition({
x,
y: y + 1,
sprite: e.sprite
});

e.sprite.alpha = useAlpha;

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

setVisible: function (visible) {
this.effects.forEach(e => {
e.sprite.visible = visible;
if (e.setVisible)
e.setVisible(visible);
});
},

destroy: function () {
this.effects.forEach(e => {
renderer.destroyObject({
layerName: 'effects',
sprite: e.sprite
});
if (e.destroy)
e.destroy();
});
}
};


+ 136
- 0
src/server/clientComponents/effects/auras.js View File

@@ -0,0 +1,136 @@
define([
'js/rendering/renderer',
'js/system/events'
], function (
renderer,
events
) {
const auras = {
reflectDamage: 0,
stealth: 1,
regenHp: 9,
regenMana: 10,
swiftness: 11,
holyVengeance: 8,
rare: 16
};
const buffIcons = {
regenHp: [3, 1],
regenMana: [4, 1],
swiftness: [5, 1],
stealth: [7, 0],
reflectDamage: [2, 1],
holyVengeance: [4, 0]
};

let templates = {};

Object.keys(auras).forEach(type => {
let cell = auras[type];

templates[type] = {
sprite: null,

alpha: 0,
alphaDir: 0.0025,

alphaMax: 0.6,
alphaMin: 0.35,

alphaCutoff: 0.4,
init: function () {
this.sprite = renderer.buildObject({
layerName: 'effects',
sheetName: 'auras',
x: this.obj.x,
y: this.obj.y + 1,
w: scale * 3,
h: scale * 3,
cell: cell
});

this.defaultDamageText();

if (this.self && buffIcons[type]) {
events.emit('onGetEffectIcon', {
id: this.id,
icon: buffIcons[type]
});
}
},

getAlpha: function () {
let listAuras = this.obj.effects.effects.filter(e => auras[e.type]);
let first = listAuras[0];

// The first aura in the list should do all the updating so that all auras pulse together
if (first === this) {
this.alpha += this.alphaDir;
if ((this.alphaDir > 0) && (this.alpha >= this.alphaMax)) {
this.alpha = this.alphaMax;
this.alphaDir *= -1;
} else if ((this.alphaDir < 0) && (this.alpha <= this.alphaMin)) {
this.alpha = this.alphaMin;
this.alphaDir *= -1;
}
} else {
this.alpha = first.alpha;
this.alphaDir = first.alphaDir;
}

let useAlpha = this.alpha;
if (useAlpha < this.alphaCutoff)
useAlpha = 0;
else {
useAlpha -= this.alphaCutoff;
useAlpha /= (this.alphaMax - this.alphaCutoff);
}

return useAlpha;
},

update: function () {
let useAlpha = this.getAlpha();

let x = this.obj.x;
let y = this.obj.y;

renderer.setSpritePosition({
x,
y: y + 1,
sprite: this.sprite
});

this.sprite.alpha = useAlpha;

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

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

this.defaultDamageText(true);

if (this.self && buffIcons[type]) {
events.emit('onRemoveEffectIcon', {
id: this.id
});
}
},

setVisible: function (visible) {
this.sprite.visible = visible;
}
};
});

return {
templates: {
...templates
}
};
});

+ 60
- 74
src/server/components/effects.js View File

@@ -110,14 +110,14 @@ module.exports = {
let effect = effects[i];
if (!forceDestroy) {
if (effect.persist) {
this.syncRemove(effect.id, effect.type);
this.syncRemove(effect.id);
continue;
}
}

this.destroyEffect(effect);

this.syncRemove(effect.id, effect.type);
this.syncRemove(effect.id);
effects.splice(i, 1);
eLen--;
i--;
@@ -137,36 +137,40 @@ module.exports = {
},

addEffect: function (options, source) {
//Skip 0-duration effects
if ((options.has('ttl')) && (options.ttl === 0))
return;

options.caster = options.caster || source;

//"X of Y in Z" cc resist check
if (!options.force && !this.canApplyEffect(options.type))
return;

if (!options.new) {
let exists = this.effects.find(e => e.type === options.type);
if (exists) {
exists.ttl += options.ttl;
let oldEffect = this.effects.find(e => e.type === options.type);

for (let p in options) {
if (p === 'ttl')
continue;
exists[p] = options[p];
}
return exists;
}
//If there is no existing effect or the effect is not stackable, make a new effect
if (!oldEffect || !oldEffect.shouldStack)
return this.buildEffect(options);
//If the effect is stackable and the new effect should stack, stack with the old effect
let shouldStack = oldEffect.shouldStack(options);
if (shouldStack && oldEffect.incrementStack) {
oldEffect.incrementStack(options);
return oldEffect;
}

//Otherwise make a new effect
return this.buildEffect(options);
},

getTypeTemplate: function (type) {
let typeTemplate = null;
if (options.type) {
let type = options.type[0].toUpperCase() + options.type.substr(1);
if (type) {
let capitalizedType = type[0].toUpperCase() + type.substr(1);
let result = {
type: type,
url: 'config/effects/effect' + type + '.js'
url: 'config/effects/effect' + capitalizedType + '.js'
};
this.obj.instance.eventEmitter.emit('onBeforeGetEffect', result);

@@ -174,84 +178,66 @@ module.exports = {
}

let builtEffect = extend({}, effectTemplate, typeTemplate);
return builtEffect;
},

buildEffect: function (options) {
let builtEffect = this.getTypeTemplate(options.type);

for (let p in options)
builtEffect[p] = options[p];
builtEffect.obj = this.obj;
builtEffect.id = this.nextId++;
builtEffect.noMsg = options.noMsg;
builtEffect.silent = options.silent;

if (builtEffect.init)
builtEffect.init(options.source);

this.effects.push(builtEffect);

if (!options.noMsg) {
this.obj.instance.syncer.queue('onGetBuff', {
type: options.type,
id: builtEffect.id
}, [this.obj.serverId]);

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

this.obj.syncer.setArray(false, 'effects', 'addEffects', options.type);
}
if (!options.silent)
this.obj.syncer.setArray(false, 'effects', 'addEffects', builtEffect.simplify());

this.obj.instance.eventEmitter.emit('onAddEffect', this.obj, builtEffect);

return builtEffect;
},

syncRemove: function (id, type, noMsg) {
if ((noMsg) || (!type))
syncExtend: function (id, data) {
let effect = this.effects.find(e => e.id === id);
if (!effect)
return;

this.obj.instance.syncer.queue('onRemoveBuff', {
id: id
}, [this.obj.serverId]);

this.obj.instance.syncer.queue('onGetDamage', {
id: this.obj.id,
event: true,
text: '-' + type
}, -1);
//Never sync silent effects
if (effect.silent)
return;

this.obj.syncer.setArray(false, 'effects', 'removeEffects', type);
this.obj.syncer.setArray(true, 'effects', 'extendEffects', {
id,
data
});
},

removeEffect: function (checkEffect, noMsg) {
let effects = this.effects;
let eLen = effects.length;
for (let i = 0; i < eLen; i++) {
let effect = effects[i];
if (effect === checkEffect) {
this.destroyEffect(effect);
syncRemove: function (id) {
let effect = this.effects.find(e => e.id === id);

this.syncRemove(effect.id, effect.type, noMsg || effect.noMsg);
effects.splice(i, 1);
if (!effect)
return;

return;
}
}
if (effect.silent)
return;

this.obj.syncer.setArray(false, 'effects', 'removeEffects', id);
},
removeEffectByName: function (effectName, noMsg) {
let effects = this.effects;
let eLen = effects.length;
for (let i = 0; i < eLen; i++) {
let effect = effects[i];
if (effect.type === effectName) {
this.destroyEffect(effect);

this.syncRemove(effect.id, effect.type, noMsg || effects.noMsg);
effects.splice(i, 1);
return effect;
}
}
removeEffect: function (id) {
let effect = this.effects.find(e => e.id === id);
this.destroyEffect(effect);

this.syncRemove(effect.id);
this.effects.spliceWhere(e => e.id === id);
},

getEffectByType: function (effectType) {
@@ -303,13 +289,13 @@ module.exports = {
e.update();

if (e.destroyed) {
this.destroyEffect(e);

this.syncRemove(e.id);

effects.splice(i, 1);
eLen--;
i--;

this.destroyEffect(e);

this.syncRemove(e.id, e.type, e.noMsg);
}
}



+ 6
- 1
src/server/config/clientConfig.js View File

@@ -173,7 +173,7 @@ const config = {
'party',
'help',
'dialogue',
'buffs',
'effects',
'tooltips',
'tooltipInfo',
'tooltipItem',
@@ -221,6 +221,11 @@ module.exports = {
});
});

config.clientComponents.push({
extends: 'effects',
path: 'server/clientComponents/effects/auras.js'
});

events.emit('onBeforeGetClientConfig', config);

//Deprecated


+ 15
- 1
src/server/config/effects/effectTemplate.js View File

@@ -1,4 +1,15 @@
module.exports = {
syncExtend: function (data) {
let effects = this.obj.effects;
effects.syncExtend(this.id, data);
},

isFirstOfType: function () {
let effects = this.obj.effects;
let firstOfType = effects.find(f => f.type === this.type);
return (firstOfType.id === this);
},

save: function () {
if (!this.persist)
return null;
@@ -19,6 +30,9 @@ module.exports = {
},

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

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

@@ -69,7 +69,7 @@ module.exports = {
if (distance > range) {
if (effect) {
delete effects[m];
obj.effects.removeEffect(effect);
obj.effects.removeEffect(effect.id);
}

return;
@@ -85,8 +85,7 @@ module.exports = {
type: this.effect,
amount: amount,
caster: this.obj,
ttl: -1,
new: true
ttl: -1
});
});

@@ -96,7 +95,7 @@ module.exports = {
delete effects[serverId];
const obj = objects.find(f => ~~f.serverId === ~~serverId);
if (obj)
obj.effects.removeEffect(effect);
obj.effects.removeEffect(effect.id);
}
});
},
@@ -117,7 +116,7 @@ module.exports = {
return;
}

obj.effects.removeEffect(effect);
obj.effects.removeEffect(effect.id);
delete effects[m];
}, this);
}


+ 3
- 12
src/server/config/spells/spellCharge.js View File

@@ -55,19 +55,10 @@ module.exports = {
type: 'stunned'
});

if (targetEffect) {
this.obj.instance.syncer.queue('onGetDamage', {
id: target.id,
event: true,
text: 'stunned'
}, -1);
}

let selfEffect = this.obj.effects.addEffect({
type: 'stunned',
noMsg: true,
force: true,
new: true
silent: true,
force: true
});

const moveAnimationEffect = {
@@ -115,7 +106,7 @@ module.exports = {

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

obj.effects.removeEffect(selfEffect, true);
obj.effects.removeEffect(selfEffect.id);

this.obj.aggro.move();



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

@@ -115,8 +115,7 @@ module.exports = {

const targetEffect = m.effects.addEffect({
type: 'stunned',
noMsg: true,
new: true
silent: true
});

//If targetEffect is undefined, it means that the target has become resistant
@@ -147,7 +146,7 @@ module.exports = {
},

endEffect: function (target, targetPos, targetEffect) {
target.effects.removeEffect(targetEffect, true);
target.effects.removeEffect(targetEffect.id);

target.instance.physics.removeObject(target, target.x, target.y);



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

@@ -54,19 +54,11 @@ module.exports = {
if (this.obj.destroyed)
return;

let targetEffect = target.effects.addEffect({
target.effects.addEffect({
type: 'slowed',
ttl: this.freezeDuration
});

if (targetEffect) {
this.obj.instance.syncer.queue('onGetDamage', {
id: target.id,
event: true,
text: 'slowed'
}, -1);
}

let damage = this.getDamage(target);
target.stats.takeDamage(damage, this.threatMult, this.obj);
}


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

@@ -36,6 +36,6 @@ module.exports = {

let obj = this.obj;

obj.effects.removeEffect(selfEffect);
obj.effects.removeEffect(selfEffect.id);
}
};

+ 5
- 3
src/server/config/spells/spellStealth.js View File

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

targetGround: true,

effect: null,

cast: function (action) {
//Clear Aggro
this.obj.aggro.die();
@@ -15,10 +17,10 @@ module.exports = {
let ttl = this.duration * consts.tickTime;
let endCallback = this.queueCallback(this.endEffect.bind(this), ttl - 50);

this.obj.effects.addEffect({
this.effect = this.obj.effects.addEffect({
type: 'stealth',
endCallback: endCallback
});
});

return true;
},
@@ -28,7 +30,7 @@ module.exports = {

let obj = this.obj;

obj.effects.removeEffectByName('stealth');
obj.effects.removeEffect(this.effect.id);
this.obj.aggro.move();
}
};

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

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

this.castingEffect = this.obj.effects.addEffect({
type: 'casting',
noMsg: true
silent: true
});

this.casting = true;


Loading…
Cancel
Save