V0.1.4 Closes #200, #185, #186, #184, #193, #179, #178, #174, #172, #166, #171, #168, #163, #129, and #28 See merge request !154tags/v0.1.4^0
@@ -1,7 +1,7 @@ | |||
<!doctype html> | |||
<html> | |||
<head> | |||
<title>dev</title> | |||
<title>GEARED - Login</title> | |||
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script> | |||
<script src="js/system/addons.js"></script> | |||
<script src="plugins/require.js" data-main="js/app"></script> | |||
@@ -16,6 +16,7 @@ require.config({ | |||
'main': 'js/main', | |||
'helpers': 'js/misc/helpers', | |||
'particles': 'plugins/pixi.particles', | |||
'picture': 'plugins/pixi.picture', | |||
'pixi': 'plugins/pixi.min' | |||
}, | |||
shim: { | |||
@@ -38,6 +39,11 @@ require.config({ | |||
'pixi' | |||
] | |||
}, | |||
'picture': { | |||
deps: [ | |||
'pixi' | |||
] | |||
}, | |||
'main': { | |||
deps: [ | |||
'helpers', | |||
@@ -45,7 +45,7 @@ define([ | |||
renderer.setSprite({ | |||
sprite: this.obj.sprite, | |||
cell: (this.row * 8) + this.col + this.frame, | |||
sheetName: this.sheet | |||
sheetName: this.spritesheet || this.sheet | |||
}); | |||
}, | |||
@@ -6,7 +6,7 @@ define([ | |||
renderer | |||
) { | |||
var scale = 40; | |||
return { | |||
type: 'attackAnimation', | |||
@@ -32,6 +32,9 @@ define([ | |||
init: function(blueprint) { | |||
effects.register(this); | |||
if (this.hideSprite) | |||
this.obj.sprite.visible = false; | |||
this.flipped = (Math.random() < 0.5); | |||
this.frameDelayCd = this.frameDelay; | |||
@@ -39,14 +42,15 @@ define([ | |||
var cell = (this.row * 8) + this.col + this.frame; | |||
this.sprite = renderer.buildObject({ | |||
sheetName: this.spriteSheet, | |||
sheetName: this.spritesheet || this.spriteSheet, | |||
cell: cell, | |||
x: this.obj.x, | |||
x: this.obj.x + (this.flipped ? 1 : 0), | |||
y: this.obj.y, | |||
offsetX: this.obj.offsetX, | |||
offsetY: this.obj.offsetY, | |||
flipX: this.flipped | |||
}); | |||
this.sprite.alpha = 1; | |||
}, | |||
renderManual: function() { | |||
@@ -60,29 +64,36 @@ define([ | |||
if (this.loopCounter == this.loop) { | |||
if (this.destroyObject) | |||
this.obj.destroyed = true; | |||
else | |||
else { | |||
if (this.hideSprite) | |||
this.obj.sprite.visible = true; | |||
this.destroyed = true; | |||
} | |||
return; | |||
} | |||
else | |||
} else | |||
this.frame = 0; | |||
} | |||
} | |||
this.sprite.x = this.obj.x * scale; | |||
this.sprite.y = this.obj.y * scale; | |||
if ((!this.hideSprite) || (this.loop > 0)) { | |||
this.sprite.x = this.obj.x * scale; | |||
this.sprite.y = this.obj.y * scale; | |||
} | |||
var cell = (this.row * 8) + this.col + this.frame; | |||
renderer.setSprite({ | |||
sheetName: this.spriteSheet, | |||
sheetName: this.spritesheet || this.spriteSheet, | |||
cell: cell, | |||
flipX: this.flipped, | |||
sprite: this.sprite | |||
}); | |||
if (this.flipped) | |||
this.sprite.x += scale; | |||
if ((!this.hideSprite) || (this.loop > 0)) { | |||
if (this.flipped) | |||
this.sprite.x += scale; | |||
} | |||
}, | |||
destroyManual: function() { | |||
@@ -92,6 +103,6 @@ define([ | |||
}); | |||
effects.unregister(this); | |||
} | |||
} | |||
}; | |||
}); |
@@ -12,7 +12,8 @@ define([ | |||
cdMax: 150, | |||
init: function(blueprint) { | |||
if ((blueprint) && (blueprint.msg)) | |||
this.extend(blueprint); | |||
}, | |||
update: function() { | |||
@@ -4,10 +4,12 @@ var components = [ | |||
'player', | |||
'pather', | |||
'attackAnimation', | |||
'lightningEffect', | |||
'moveAnimation', | |||
'bumpAnimation', | |||
'animation', | |||
'light', | |||
'lightPatch', | |||
'projectile', | |||
'particles', | |||
'explosion', | |||
@@ -41,6 +43,9 @@ define(components, function() { | |||
return { | |||
getTemplate: function(type) { | |||
if (type == 'lightpatch') | |||
type = 'lightPatch'; | |||
return templates[type]; | |||
} | |||
}; |
@@ -0,0 +1,107 @@ | |||
define([ | |||
'js/rendering/renderer', | |||
'picture' | |||
], function( | |||
renderer, | |||
picture | |||
) { | |||
var scale = 40; | |||
var scaleMult = 5; | |||
return { | |||
type: 'lightPatch', | |||
color: 'f7ffb2', | |||
patches: [], | |||
rays: [], | |||
init: function(blueprint) { | |||
this.blueprint = this.blueprint || {}; | |||
var obj = this.obj; | |||
var x = obj.x; | |||
var y = obj.y; | |||
var maxDistance = Math.sqrt(Math.pow(obj.width / 2, 2) + Math.pow(obj.height / 2, 2)); | |||
for (var i = 0; i < obj.width; i++) { | |||
for (var j = 0; j < obj.height; j++) { | |||
var distance = maxDistance - Math.sqrt(Math.pow((obj.width / 2) - i, 2) + Math.pow((obj.width / 2) - i, 2)); | |||
var alpha = distance / maxDistance; | |||
var sprite = renderer.buildObject({ | |||
x: (x + i), | |||
y: (y + j), | |||
sheetName: 'white', | |||
cell: 0, | |||
layerName: 'lightPatches' | |||
}); | |||
sprite.alpha = (0.2 + (Math.random() * 1)) * alpha; | |||
sprite.tint = '0x' + this.color; | |||
sprite.blendMode = PIXI.BLEND_MODES.OVERLAY; | |||
sprite.pluginName = 'picture'; | |||
this.patches.push(sprite); | |||
} | |||
} | |||
var rCount = ((obj.width * obj.height) / 10) + ~~(Math.random() + 2); | |||
for (var i = 0; i < rCount; i++) { | |||
var nx = x + 3 + ~~(Math.random() * (obj.width - 1)); | |||
var ny = y - 4 + ~~(Math.random() * (obj.height)); | |||
var w = 1 + ~~(Math.random() * 2); | |||
var h = 6 + ~~(Math.random() * 13); | |||
var hm = 2; | |||
var rContainer = renderer.buildContainer({ | |||
layerName: 'lightBeams' | |||
}); | |||
this.rays.push(rContainer); | |||
for (var j = 0; j < h; j++) { | |||
var ray = renderer.buildObject({ | |||
x: nx, | |||
y: ny, | |||
cell: 0, | |||
sheetName: 'white', | |||
parent: rContainer | |||
}); | |||
ray.x = ~~((nx * scale) - (scaleMult * j)); | |||
ray.y = (ny * scale) + (scaleMult * j * hm); | |||
ray.alpha = ((1.0 - (j / h)) * 0.4);// * (0.5 + (Math.random() * 0.5)); | |||
ray.width = w * scaleMult; | |||
ray.height = scaleMult * hm; | |||
ray.tint = 0xffeb38; | |||
ray.blendMode = PIXI.BLEND_MODES.ADD; | |||
} | |||
} | |||
}, | |||
update: function() { | |||
var rays = this.rays; | |||
var rLen = rays.length; | |||
for (var i = 0; i < rLen; i++) { | |||
var r = rays[i]; | |||
r.alpha += (Math.random() * 0.03) - 0.015; | |||
if (r.alpha < 0.3) | |||
r.alpha = 0.3; | |||
else if (r.alpha > 1) | |||
r.alpha = 1; | |||
} | |||
}, | |||
destroy: function() { | |||
this.patches.forEach(function(p) { | |||
p.parent.removeChild(p); | |||
}); | |||
this.patches = []; | |||
this.rays.forEach(function(r) { | |||
r.parent.removeChild(r); | |||
}); | |||
this.rays = []; | |||
} | |||
}; | |||
}); |
@@ -0,0 +1,63 @@ | |||
define([ | |||
'js/rendering/lightningBuilder', | |||
'js/rendering/effects' | |||
], function( | |||
lightningBuilder, | |||
effects | |||
) { | |||
return { | |||
type: 'lightningEffect', | |||
cd: 0, | |||
cdMax: 1, | |||
effect: null, | |||
ttl: 6, | |||
init: function() { | |||
effects.register(this); | |||
var xOffset = (this.toX > this.obj.x) ? 1 : 0; | |||
this.effect = lightningBuilder.build({ | |||
fromX: this.obj.x + xOffset, | |||
fromY: this.obj.y + 0.5, | |||
toX: this.toX + 0.5, | |||
toY: this.toY + 0.5 | |||
}); | |||
}, | |||
renderManual: function() { | |||
if (this.cd > 0) { | |||
this.cd--; | |||
return; | |||
} | |||
this.cd = this.cdMax; | |||
lightningBuilder.destroy(this.effect); | |||
this.ttl--; | |||
if (this.ttl == 0) { | |||
this.destroyed = true; | |||
return; | |||
} | |||
var xOffset = (this.toX > this.obj.x) ? 1 : 0; | |||
this.effect = lightningBuilder.build({ | |||
fromX: this.obj.x + xOffset, | |||
fromY: this.obj.y + 0.5, | |||
toX: this.toX + 0.5, | |||
toY: this.toY + 0.5 | |||
}); | |||
}, | |||
destroyManual: function() { | |||
//lightningBuilder.destroy(this.effect); | |||
effects.unregister(this); | |||
} | |||
}; | |||
}); |
@@ -35,8 +35,6 @@ define([ | |||
layerName: 'effects', | |||
x: 0, | |||
y: 0, | |||
w: scale, | |||
h: scale, | |||
sheetName: 'ui', | |||
cell: 7 | |||
}); | |||
@@ -22,7 +22,7 @@ define([ | |||
this.obj.addComponent('pather'); | |||
events.emit('onGetPortrait', this.obj.class); | |||
events.emit('onGetPortrait', this.obj.portrait); | |||
}, | |||
update: function() { | |||
@@ -28,7 +28,7 @@ define([ | |||
renderRange: null, | |||
reticleSprite: null, | |||
tarpSprite: null, | |||
targetSprite: null, | |||
shiftDown: false, | |||
@@ -141,7 +141,12 @@ define([ | |||
}, | |||
tabTarget: function() { | |||
this.onMouseDown(null, objects.getClosest(window.player.x, window.player.y, 10, this.shiftDown, this.target)); | |||
var closest = objects.getClosest(window.player.x, window.player.y, 10, this.shiftDown, this.target); | |||
this.target = closest; | |||
this.targetSprite.visible = !!this.target; | |||
events.emit('onSetTarget', this.target, null); | |||
}, | |||
build: function(destroy) { | |||
@@ -189,11 +194,15 @@ define([ | |||
this.target = this.obj; | |||
} | |||
if ((!spell.targetGround) && (!this.target)) | |||
if ((!spell.targetGround) && (!spell.autoTargetFollower) && (!this.target)) | |||
return; | |||
var hoverTile = this.obj.mouseMover.hoverTile; | |||
var target = spell.targetGround ? hoverTile : this.target.id; | |||
var target = hoverTile; | |||
if ((!spell.targetGround) && (this.target)) | |||
target = this.target.id; | |||
if ((spell.autoTargetFollower) && (target.id == null)) | |||
target = null; | |||
if (this.shiftDown) | |||
this.target = oldTarget; | |||
@@ -12,6 +12,9 @@ define([ | |||
values: null, | |||
hpSprite: null, | |||
hpSpriteInner: null, | |||
init: function(blueprint) { | |||
if (this.obj.self) | |||
events.emit('onGetStats', this.values); | |||
@@ -28,19 +31,19 @@ define([ | |||
this.hpSprite = renderer.buildRectangle({ | |||
layerName: 'effects', | |||
x: 0, | |||
y: 0, | |||
w: 0, | |||
h: 0, | |||
x: obj.x * scale, | |||
y: obj.y * scale, | |||
w: 1, | |||
h: 1, | |||
color: 0x802343 | |||
}); | |||
renderer.buildRectangle({ | |||
this.hpSpriteInner = renderer.buildRectangle({ | |||
x: 0, | |||
y: 0, | |||
w: 0, | |||
h: 0, | |||
parent: this.hpSprite, | |||
w: 1, | |||
h: 1, | |||
layerName: 'effects', | |||
color: 0xd43346 | |||
}); | |||
@@ -66,7 +69,7 @@ define([ | |||
}); | |||
renderer.moveRectangle({ | |||
sprite: this.hpSprite.children[0], | |||
sprite: this.hpSpriteInner, | |||
x: x + 4, | |||
y: y, | |||
w: (this.values.hp / this.values.hpMax) * (scale - 8), | |||
@@ -74,6 +77,7 @@ define([ | |||
}); | |||
this.hpSprite.visible = (this.values.hp < this.values.hpMax); | |||
this.hpSpriteInner.visible = this.hpSprite.visible; | |||
}, | |||
extend: function(blueprint) { | |||
@@ -100,6 +104,11 @@ define([ | |||
sprite: this.hpSprite, | |||
layerName: 'effects' | |||
}); | |||
renderer.destroyObject({ | |||
sprite: this.hpSpriteInner, | |||
layerName: 'effects' | |||
}); | |||
} | |||
}; | |||
}); |
@@ -14,11 +14,13 @@ define([ | |||
'dex': 'dexterity', | |||
'armor': 'armor', | |||
'addCritChance': 'increased crit chance', | |||
'addCritMultiplier': 'increased crit multiplier', | |||
'magicFind': 'magic find', | |||
'sprintChance': 'sprint chance', | |||
'dmgPercent': 'to all damage', | |||
'allAttributes': 'to all attributes', | |||
'xpIncrease': 'additional xp per kill', | |||
'lvlRequire': 'level requirement reduction', | |||
'elementArcanePercent': 'increased arcane damage', | |||
'elementFrostPercent': 'increased frost damage', | |||
@@ -126,12 +126,15 @@ define([ | |||
var components = template.components || []; | |||
delete template.components; | |||
var syncTypes = ['portrait']; | |||
for (var p in template) { | |||
var value = template[p]; | |||
var type = typeof(value); | |||
if (type == 'object') { | |||
if (syncTypes.indexOf(p) > -1) | |||
obj[p] = value; | |||
} else | |||
obj[p] = value; | |||
} | |||
@@ -242,7 +245,6 @@ define([ | |||
} | |||
} | |||
if ((template.x != 0) || (template.y != 0)) { | |||
if (obj.stats) | |||
obj.stats.updateHpSprite(); | |||
@@ -0,0 +1,118 @@ | |||
define([ | |||
'js/rendering/renderer', | |||
'picture' | |||
], function( | |||
renderer, | |||
picture | |||
) { | |||
var scale = 40; | |||
var scaleMult = 5; | |||
return { | |||
build: function(config) { | |||
var obj = { | |||
lines: [] | |||
}; | |||
var maxDeviate = scale * 0.3; | |||
var fx = config.fromX * scale; | |||
var fy = config.fromY * scale; | |||
var tx = config.toX * scale; | |||
var ty = config.toY * scale; | |||
var angle = Math.atan2(ty - fy, tx - fx); | |||
var distance = Math.sqrt(Math.pow(tx - fx, 2) + Math.pow(ty - fy, 2)); | |||
var divDistance = Math.min(20, distance); | |||
var divisions = Math.max(1, distance / divDistance); | |||
var x = fx; | |||
var y = fy; | |||
for (var i = 0; i < divisions; i++) { | |||
var line = { | |||
sprites: [] | |||
}; | |||
var ntx = fx + (Math.cos(angle) * (divDistance * i)) + ~~(Math.random() * (maxDeviate * 2)) - maxDeviate; | |||
var nty = fy + (Math.sin(angle) * (divDistance * i)) + ~~(Math.random() * (maxDeviate * 2)) - maxDeviate; | |||
if (i == divisions - 1) { | |||
ntx = tx; | |||
nty = ty; | |||
} | |||
var nAngle = Math.atan2(nty - y, ntx - x); | |||
var steps = ~~(Math.sqrt(Math.pow(ntx - x, 2) + Math.pow(nty - y, 2)) / scaleMult); | |||
var patches = {}; | |||
for (var j = 0; j < steps; j++) { | |||
var c = [0xffeb38, 0xfaac45, 0xfafcfc][~~(Math.random() * 3)]; | |||
line.sprites.push(renderer.buildRectangle({ | |||
x: ~~(x / scaleMult) * scaleMult, | |||
y: ~~(y / scaleMult) * scaleMult, | |||
w: scaleMult, | |||
h: scaleMult, | |||
color: c, | |||
layerName: 'effects' | |||
})); | |||
var xx = x; | |||
var yy = y; | |||
if (!patches[xx + '-' + yy]) { | |||
patches[xx + '-' + yy] = 1; | |||
var lightPatch = renderer.buildObject({ | |||
sheetName: 'white', | |||
x: 0, | |||
y: 0, | |||
cell: 0, | |||
layerName: 'lightPatches' | |||
}); | |||
lightPatch.alpha = Math.random() * 0.5; | |||
lightPatch.tint = '0xffffff'; | |||
lightPatch.x = ~~((xx - scaleMult) / scaleMult) * scaleMult; | |||
lightPatch.y = ~~((yy - scaleMult) / scaleMult) * scaleMult; | |||
lightPatch.width = scaleMult * 3; | |||
lightPatch.height = scaleMult * 3; | |||
lightPatch.blendMode = PIXI.BLEND_MODES.OVERLAY; | |||
lightPatch.pluginName = 'picture'; | |||
line.sprites.push(lightPatch); | |||
} | |||
x += Math.cos(nAngle) * scaleMult; | |||
y += Math.sin(nAngle) * scaleMult; | |||
} | |||
obj.lines.push(line); | |||
} | |||
return obj; | |||
}, | |||
toHex: function rgbToHex(r, g, b) { | |||
var componentToHex = function(c) { | |||
var hex = c.toString(16); | |||
return hex.length == 1 ? '0' + hex : hex; | |||
}; | |||
return '0x' + componentToHex(r) + componentToHex(g) + componentToHex(b); | |||
}, | |||
update: function(obj) { | |||
}, | |||
destroy: function(obj) { | |||
obj.lines.forEach(function(l) { | |||
l.sprites.forEach(function(s) { | |||
s.parent.removeChild(s); | |||
}); | |||
}); | |||
} | |||
}; | |||
}); |
@@ -24,20 +24,28 @@ define([ | |||
var addY = msg.event ? scale : -(scale * 0.75); | |||
var ttl = 30 * (msg.crit ? 1 : 1); | |||
var ttl = 35; | |||
var numberObj = { | |||
obj: target, | |||
amount: msg.amount, | |||
x: (target.x * scale) + ~~(Math.random() * scale - (scale / 2)), | |||
y: (target.y * scale) + addY, | |||
x: (target.x * scale), | |||
y: (target.y * scale) + scale - (scale / 4), | |||
ttl: ttl, | |||
ttlMax: ttl, | |||
event: msg.event, | |||
text: msg.text, | |||
crit: msg.crit, | |||
heal: msg.heal | |||
}; | |||
}; | |||
if (numberObj.event) { | |||
numberObj.y += (scale / 2); | |||
} | |||
else if (numberObj.heal) | |||
numberObj.x -= scale; | |||
else | |||
numberObj.x += scale; | |||
var text = numberObj.text; | |||
if (!numberObj.event) | |||
@@ -74,9 +82,9 @@ define([ | |||
} | |||
if (l.event) | |||
l.y += 0.75; | |||
l.y += 1; | |||
else | |||
l.y -= 0.75; | |||
l.y -= 1; | |||
var alpha = l.ttl / l.ttlMax; | |||
@@ -1,9 +1,11 @@ | |||
define([ | |||
'particles', | |||
'js/rendering/particleDefaults' | |||
'js/rendering/particleDefaults', | |||
'js/rendering/shaders/outline' | |||
], function( | |||
pixiParticles, | |||
particleDefaults | |||
particleDefaults, | |||
shaderOutline | |||
) { | |||
return { | |||
renderer: null, | |||
@@ -14,6 +16,7 @@ define([ | |||
lastTick: null, | |||
init: function(options) { | |||
this.r = options.r; | |||
this.renderer = options.renderer; | |||
this.stage = options.stage; | |||
this.lastTick = Date.now(); | |||
@@ -22,7 +25,7 @@ define([ | |||
buildEmitter: function(config) { | |||
var options = $.extend(true, {}, particleDefaults, config); | |||
var emitter = new PIXI.particles.Emitter(this.stage, ['images/particles.png'], options); | |||
var emitter = new PIXI.particles.Emitter(this.r.layers.particles, ['images/particles.png'], options); | |||
emitter.emit = true; | |||
this.emitters.push(emitter); | |||
@@ -35,13 +38,23 @@ define([ | |||
}, | |||
update: function() { | |||
var renderer = this.r; | |||
var now = Date.now(); | |||
var emitters = this.emitters; | |||
var eLen = emitters.length; | |||
for (var i = 0; i < eLen; i++) { | |||
var e = emitters[i]; | |||
var destroy = ((!e.emit) && (e.particleCount == 0)); | |||
var visible = null; | |||
var destroy = !e.emit; | |||
if (destroy) { | |||
if (e.particleCount > 0) { | |||
visible = renderer.isVisible(e.spawnPos.x, e.spawnPos.y); | |||
if (visible) | |||
destroy = false; | |||
} | |||
} | |||
if (destroy) { | |||
emitters.splice(i, 1); | |||
@@ -53,7 +66,16 @@ define([ | |||
continue; | |||
} | |||
e.update((now - this.lastTick) * 0.001); | |||
if (visible === null) | |||
visible = renderer.isVisible(e.spawnPos.x, e.spawnPos.y); | |||
if (!visible) | |||
continue; | |||
var r = e.update((now - this.lastTick) * 0.001); | |||
r.forEach(function(rr) { | |||
if (e.blendMode == 'overlay') | |||
rr.pluginName = 'picture'; | |||
}, this); | |||
} | |||
this.lastTick = now; | |||
@@ -76,8 +76,9 @@ define([ | |||
], | |||
wallsNoFlip: [ | |||
156, 158, 162, 163, 167, 168, //Ledges | |||
189 //Wall Sign | |||
156, 158, 162, 163, 167, 168, //Ledges | |||
189, //Wall Sign | |||
195, 196, 197, 198, 199, 200, 201, 202, 203 //Stone Ledges | |||
], | |||
objectsNoFlip: [ | |||
96, 101, //Clotheslines | |||
@@ -88,7 +89,7 @@ define([ | |||
getSheetNum: function(tile) { | |||
if (tile < 192) | |||
return 0; | |||
else if (tile < 384) | |||
else if (tile < 448) | |||
return 1; | |||
else | |||
return 2; | |||
@@ -99,12 +100,12 @@ define([ | |||
if (tile < 192) | |||
sheetNum = 0; | |||
else if (tile < 384) { | |||
else if (tile < 448) { | |||
tile -= 192; | |||
sheetNum = 1; | |||
} | |||
else { | |||
tile -= 384; | |||
tile -= 448; | |||
sheetNum = 2; | |||
} | |||
@@ -124,12 +125,12 @@ define([ | |||
if (tile < 192) | |||
sheetNum = 0; | |||
else if (tile < 384) { | |||
else if (tile < 448) { | |||
tile -= 192; | |||
sheetNum = 1; | |||
} | |||
else { | |||
tile -= 384; | |||
tile -= 448; | |||
sheetNum = 2; | |||
} | |||
@@ -10,6 +10,7 @@ define([ | |||
'walls', | |||
'mobs', | |||
'bosses', | |||
'animBigObjects', | |||
'bigObjects', | |||
'objects', | |||
'characters', | |||
@@ -24,7 +25,9 @@ define([ | |||
'sprites', | |||
'animChar', | |||
'animMob', | |||
'animBoss' | |||
'animBoss', | |||
'white', | |||
'ray' | |||
], | |||
sprites: {}, | |||
ready: false, | |||
@@ -62,6 +62,17 @@ define([ | |||
for (var e in response) { | |||
var r = response[e]; | |||
//Certain messages expect to be performed last (because the object they act on hasn't been greated when they get queued) | |||
r.sort(function(a, b) { | |||
if (a.performLast) | |||
return 1; | |||
else if (b.performLast) | |||
return -1; | |||
else | |||
return 0; | |||
}); | |||
r.forEach(function(o) { | |||
events.emit(e, o); | |||
}); | |||
@@ -754,6 +754,7 @@ if (!Array.prototype.random) { | |||
this.randomColor = config.randomColor || false; | |||
this.randomScale = config.randomScale || false; | |||
this.randomSpeed = config.randomSpeed || false; | |||
this.blendMode = config.blendMode; | |||
/** | |||
* The constructor used to create new particles. The default is | |||
@@ -1425,6 +1426,7 @@ if (!Array.prototype.random) { | |||
* @param {Number} delta Time elapsed since the previous frame, in __seconds__. | |||
*/ | |||
p.update = function(delta) { | |||
var r = []; | |||
//if we don't have a parent to add particles to, then don't do anything. | |||
//this also works as a isDestroyed check | |||
if (!this._parent) return; | |||
@@ -1503,6 +1505,7 @@ if (!Array.prototype.random) { | |||
} else { | |||
p = new this.particleConstructor(this); | |||
} | |||
r.push(p); | |||
//set a random texture if we have more than one | |||
if (this.particleImages.length > 1) { | |||
@@ -1620,6 +1623,8 @@ if (!Array.prototype.random) { | |||
this._prevPosIsValid = true; | |||
this._posChanged = false; | |||
} | |||
return r; | |||
}; | |||
/** | |||
@@ -0,0 +1,428 @@ | |||
var __extends = (this && this.__extends) || function(d, b) { | |||
for (var p in b) | |||
if (b.hasOwnProperty(p)) d[p] = b[p]; | |||
function __() { | |||
this.constructor = d; | |||
} | |||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | |||
}; | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
var shaderLib = [{ | |||
vertUniforms: "", | |||
vertCode: "vTextureCoord = aTextureCoord;", | |||
fragUniforms: "uniform vec4 uTextureClamp;", | |||
fragCode: "vec2 textureCoord = clamp(vTextureCoord, uTextureClamp.xy, uTextureClamp.zw);" | |||
}, { | |||
vertUniforms: "uniform mat3 uTransform;", | |||
vertCode: "vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;", | |||
fragUniforms: "", | |||
fragCode: "vec2 textureCoord = vTextureCoord;" | |||
}, { | |||
vertUniforms: "uniform mat3 uTransform;", | |||
vertCode: "vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;", | |||
fragUniforms: "uniform mat3 uMapCoord;\nuniform vec4 uClampFrame;\nuniform vec2 uClampOffset;", | |||
fragCode: "vec2 textureCoord = mod(vTextureCoord - uClampOffset, vec2(1.0, 1.0)) + uClampOffset;" + | |||
"\ntextureCoord = (uMapCoord * vec3(textureCoord, 1.0)).xy;" + | |||
"\ntextureCoord = clamp(textureCoord, uClampFrame.xy, uClampFrame.zw);" | |||
}]; | |||
var PictureShader = (function(_super) { | |||
__extends(PictureShader, _super); | |||
function PictureShader(gl, vert, frag, tilingMode) { | |||
var lib = shaderLib[tilingMode]; | |||
_super.call(this, gl, vert.replace(/%SPRITE_UNIFORMS%/gi, lib.vertUniforms) | |||
.replace(/%SPRITE_CODE%/gi, lib.vertCode), frag.replace(/%SPRITE_UNIFORMS%/gi, lib.fragUniforms) | |||
.replace(/%SPRITE_CODE%/gi, lib.fragCode)); | |||
this.bind(); | |||
this.tilingMode = tilingMode; | |||
this.tempQuad = new PIXI.Quad(gl); | |||
this.tempQuad.initVao(this); | |||
this.uniforms.uColor = new Float32Array([1, 1, 1, 1]); | |||
this.uniforms.uSampler = [0, 1]; | |||
} | |||
PictureShader.blendVert = "\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nuniform mat3 projectionMatrix;\nuniform mat3 mapMatrix;\n\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n %SPRITE_CODE%\n vMapCoord = (mapMatrix * vec3(aVertexPosition, 1.0)).xy;\n}\n"; | |||
return PictureShader; | |||
}(PIXI.Shader)); | |||
extras.PictureShader = PictureShader; | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
var overlayFrag = "\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n vec4 source = texture2D(uSampler[0], textureCoord) * uColor;\n vec4 target = texture2D(uSampler[1], vMapCoord);\n\n //reverse hardlight\n if (source.a == 0.0) {\n gl_FragColor = vec4(0, 0, 0, 0);\n return;\n }\n //yeah, premultiplied\n vec3 Cb = source.rgb/source.a, Cs;\n if (target.a > 0.0) {\n Cs = target.rgb / target.a;\n }\n vec3 multiply = Cb * Cs * 2.0;\n vec3 Cs2 = Cs * 2.0 - 1.0;\n vec3 screen = Cb + Cs2 - Cb * Cs2;\n vec3 B;\n if (Cb.r <= 0.5) {\n B.r = multiply.r;\n } else {\n B.r = screen.r;\n }\n if (Cb.g <= 0.5) {\n B.g = multiply.g;\n } else {\n B.g = screen.g;\n }\n if (Cb.b <= 0.5) {\n B.b = multiply.b;\n } else {\n B.b = screen.b;\n }\n vec4 res;\n res.xyz = (1.0 - source.a) * Cs + source.a * B;\n res.a = source.a + target.a * (1.0-source.a);\n gl_FragColor = vec4(res.xyz * res.a, res.a);\n}\n"; | |||
var HardLightShader = (function(_super) { | |||
__extends(HardLightShader, _super); | |||
function HardLightShader(gl, tilingMode) { | |||
_super.call(this, gl, extras.PictureShader.blendVert, overlayFrag, tilingMode); | |||
} | |||
return HardLightShader; | |||
}(extras.PictureShader)); | |||
extras.HardLightShader = HardLightShader; | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
function mapFilterBlendModesToPixi(gl, array) { | |||
if (array === void 0) { | |||
array = []; | |||
} | |||
array[PIXI.BLEND_MODES.OVERLAY] = [new extras.OverlayShader(gl, 0), new extras.OverlayShader(gl, 1), new extras.OverlayShader(gl, 2)]; | |||
array[PIXI.BLEND_MODES.HARD_LIGHT] = [new extras.HardLightShader(gl, 0), new extras.HardLightShader(gl, 1), new extras.HardLightShader(gl, 2)]; | |||
return array; | |||
} | |||
extras.mapFilterBlendModesToPixi = mapFilterBlendModesToPixi; | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
var normalFrag = "\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n\n vec4 sample = texture2D(uSampler[0], textureCoord);\n gl_FragColor = sample * uColor;\n}\n"; | |||
var normalVert = "\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n %SPRITE_CODE%\n}\n"; | |||
var NormalShader = (function(_super) { | |||
__extends(NormalShader, _super); | |||
function NormalShader(gl, tilingMode) { | |||
_super.call(this, gl, normalVert, normalFrag, tilingMode); | |||
} | |||
return NormalShader; | |||
}(extras.PictureShader)); | |||
extras.NormalShader = NormalShader; | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
var overlayFrag = "\nvarying vec2 vTextureCoord;\nvarying vec2 vMapCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler[2];\nuniform vec4 uColor;\n%SPRITE_UNIFORMS%\n\nvoid main(void)\n{\n %SPRITE_CODE%\n vec4 source = texture2D(uSampler[0], textureCoord) * uColor;\n vec4 target = texture2D(uSampler[1], vMapCoord);\n\n //reverse hardlight\n if (source.a == 0.0) {\n gl_FragColor = vec4(0, 0, 0, 0);\n return;\n }\n //yeah, premultiplied\n vec3 Cb = source.rgb/source.a, Cs;\n if (target.a > 0.0) {\n Cs = target.rgb / target.a;\n }\n vec3 multiply = Cb * Cs * 2.0;\n vec3 Cb2 = Cb * 2.0 - 1.0;\n vec3 screen = Cb2 + Cs - Cb2 * Cs;\n vec3 B;\n if (Cs.r <= 0.5) {\n B.r = multiply.r;\n } else {\n B.r = screen.r;\n }\n if (Cs.g <= 0.5) {\n B.g = multiply.g;\n } else {\n B.g = screen.g;\n }\n if (Cs.b <= 0.5) {\n B.b = multiply.b;\n } else {\n B.b = screen.b;\n }\n vec4 res;\n res.xyz = (1.0 - source.a) * Cs + source.a * B;\n res.a = source.a + target.a * (1.0-source.a);\n\n if ((target.r <= 0.1) && (target.g <= 0.1) && (target.b <= 0.1)) {\n res.r = 0.177;\n res.g = 0.13;\n res.b = 0.212;\n }\n\n gl_FragColor = vec4(res.xyz * res.a, res.a);\n}\n"; | |||
var OverlayShader = (function(_super) { | |||
__extends(OverlayShader, _super); | |||
function OverlayShader(gl, tilingMode) { | |||
_super.call(this, gl, extras.PictureShader.blendVert, overlayFrag, tilingMode); | |||
} | |||
return OverlayShader; | |||
}(extras.PictureShader)); | |||
extras.OverlayShader = OverlayShader; | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
function nextPow2(v) { | |||
v += (v === 0) ? 1 : 0; | |||
--v; | |||
v |= v >>> 1; | |||
v |= v >>> 2; | |||
v |= v >>> 4; | |||
v |= v >>> 8; | |||
v |= v >>> 16; | |||
return v + 1; | |||
} | |||
var PictureRenderer = (function(_super) { | |||
__extends(PictureRenderer, _super); | |||
function PictureRenderer(renderer) { | |||
_super.call(this, renderer); | |||
} | |||
PictureRenderer.prototype.onContextChange = function() { | |||
var gl = this.renderer.gl; | |||
this.drawModes = extras.mapFilterBlendModesToPixi(gl); | |||
this.normalShader = [new extras.NormalShader(gl, 0), new extras.NormalShader(gl, 1), new extras.NormalShader(gl, 2)]; | |||
this._tempClamp = new Float32Array(4); | |||
this._tempColor = new Float32Array(4); | |||
this._tempRect = new PIXI.Rectangle(); | |||
this._tempRect2 = new PIXI.Rectangle(); | |||
this._tempRect3 = new PIXI.Rectangle(); | |||
this._tempMatrix = new PIXI.Matrix(); | |||
this._tempMatrix2 = new PIXI.Matrix(); | |||
this._bigBuf = new Uint8Array(1 << 20); | |||
this._renderTexture = new PIXI.BaseRenderTexture(1024, 1024); | |||
}; | |||
PictureRenderer.prototype.start = function() {}; | |||
PictureRenderer.prototype.flush = function() {}; | |||
PictureRenderer.prototype._getRenderTexture = function(minWidth, minHeight) { | |||
if (this._renderTexture.width < minWidth || | |||
this._renderTexture.height < minHeight) { | |||
minHeight = nextPow2(minWidth); | |||
minHeight = nextPow2(minHeight); | |||
this._renderTexture.resize(minWidth, minHeight); | |||
} | |||
return this._renderTexture; | |||
}; | |||
PictureRenderer.prototype._getBuf = function(size) { | |||
var buf = this._bigBuf; | |||
if (buf.length < size) { | |||
size = nextPow2(size); | |||
buf = new Uint8Array(size); | |||
this._bigBuf = buf; | |||
} | |||
return buf; | |||
}; | |||
PictureRenderer.prototype.render = function(sprite) { | |||
if (!sprite.texture.valid) { | |||
return; | |||
} | |||
var tilingMode = 0; | |||
if (sprite.tileTransform) { | |||
tilingMode = this._isSimpleSprite(sprite) ? 1 : 2; | |||
} | |||
var blendShader = this.drawModes[sprite.blendMode]; | |||
if (blendShader) { | |||
this._renderBlend(sprite, blendShader[tilingMode]); | |||
} else { | |||
this._renderNormal(sprite, this.normalShader[tilingMode]); | |||
} | |||
}; | |||
PictureRenderer.prototype._renderNormal = function(sprite, shader) { | |||
var renderer = this.renderer; | |||
renderer.bindShader(shader); | |||
renderer.state.setBlendMode(sprite.blendMode); | |||
this._renderInner(sprite, shader); | |||
}; | |||
PictureRenderer.prototype._renderBlend = function(sprite, shader) { | |||
var renderer = this.renderer; | |||
var spriteBounds = sprite.getBounds(); | |||
var renderTarget = renderer._activeRenderTarget; | |||
var matrix = renderTarget.projectionMatrix; | |||
var flipX = matrix.a < 0; | |||
var flipY = matrix.d < 0; | |||
var resolution = renderTarget.resolution; | |||
var screen = this._tempRect; | |||
var fr = renderTarget.sourceFrame || renderTarget.destinationFrame; | |||
screen.x = 0; | |||
screen.y = 0; | |||
screen.width = fr.width; | |||
screen.height = fr.height; | |||
var bounds = this._tempRect2; | |||
var fbw = fr.width * resolution, | |||
fbh = fr.height * resolution; | |||
bounds.x = (spriteBounds.x + matrix.tx / matrix.a) * resolution + fbw / 2; | |||
bounds.y = (spriteBounds.y + matrix.ty / matrix.d) * resolution + fbh / 2; | |||
bounds.width = spriteBounds.width * resolution; | |||
bounds.height = spriteBounds.height * resolution; | |||
if (flipX) { | |||
bounds.y = fbw - bounds.width - bounds.x; | |||
} | |||
if (flipY) { | |||
bounds.y = fbh - bounds.height - bounds.y; | |||
} | |||
var screenBounds = this._tempRect3; | |||
var x_1 = Math.floor(Math.max(screen.x, bounds.x)); | |||
var x_2 = Math.ceil(Math.min(screen.x + screen.width, bounds.x + bounds.width)); | |||
var y_1 = Math.floor(Math.max(screen.y, bounds.y)); | |||
var y_2 = Math.ceil(Math.min(screen.y + screen.height, bounds.y + bounds.height)); | |||
var pixelsWidth = x_2 - x_1; | |||
var pixelsHeight = y_2 - y_1; | |||
if (pixelsWidth <= 0 || pixelsHeight <= 0) { | |||
return; | |||
} | |||
var rt = this._getRenderTexture(pixelsWidth, pixelsHeight); | |||
renderer.bindTexture(rt, 1, true); | |||
var gl = renderer.gl; | |||
if (renderer.renderingToScreen && renderTarget.root) { | |||
var buf = this._getBuf(pixelsWidth * pixelsHeight * 4); | |||
gl.readPixels(x_1, y_1, pixelsWidth, pixelsHeight, gl.RGBA, gl.UNSIGNED_BYTE, this._bigBuf); | |||
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, pixelsWidth, pixelsHeight, gl.RGBA, gl.UNSIGNED_BYTE, this._bigBuf); | |||
} else { | |||
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, x_1, y_1, pixelsWidth, pixelsHeight); | |||
} | |||
renderer.bindShader(shader); | |||
renderer.state.setBlendMode(PIXI.BLEND_MODES.NORMAL); | |||
if (shader.uniforms.mapMatrix) { | |||
var mapMatrix = this._tempMatrix; | |||
mapMatrix.a = bounds.width / rt.width / spriteBounds.width; | |||
if (flipX) { | |||
mapMatrix.a = -mapMatrix.a; | |||
mapMatrix.tx = (bounds.x - x_1) / rt.width - (spriteBounds.x + spriteBounds.width) * mapMatrix.a; | |||
} else { | |||
mapMatrix.tx = (bounds.x - x_1) / rt.width - spriteBounds.x * mapMatrix.a; | |||
} | |||
mapMatrix.d = bounds.height / rt.height / spriteBounds.height; | |||
if (flipY) { | |||
mapMatrix.d = -mapMatrix.d; | |||
mapMatrix.ty = (bounds.y - y_1) / rt.height - (spriteBounds.y + spriteBounds.height) * mapMatrix.d; | |||
} else { | |||
mapMatrix.ty = (bounds.y - y_1) / rt.height - spriteBounds.y * mapMatrix.d; | |||
} | |||
shader.uniforms.mapMatrix = mapMatrix.toArray(true); | |||
} | |||
this._renderInner(sprite, shader); | |||
}; | |||
PictureRenderer.prototype._renderInner = function(sprite, shader) { | |||
var renderer = this.renderer; | |||
if (shader.tilingMode > 0) { | |||
this._renderWithShader(sprite, shader.tilingMode === 1, shader); | |||
} else { | |||
this._renderSprite(sprite, shader); | |||
} | |||
}; | |||
PictureRenderer.prototype._renderWithShader = function(ts, isSimple, shader) { | |||
var quad = shader.tempQuad; | |||
var renderer = this.renderer; | |||
renderer.bindVao(quad.vao); | |||
var vertices = quad.vertices; | |||
var _width = ts._width; | |||
var _height = ts._height; | |||
var _anchorX = ts._anchor._x; | |||
var _anchorY = ts._anchor._y; | |||
var w0 = _width * (1 - _anchorX); | |||
var w1 = _width * -_anchorX; | |||
var h0 = _height * (1 - _anchorY); | |||
var h1 = _height * -_anchorY; | |||
var wt = ts.transform.worldTransform; | |||
var a = wt.a; | |||
var b = wt.b; | |||
var c = wt.c; | |||
var d = wt.d; | |||
var tx = wt.tx; | |||
var ty = wt.ty; | |||
vertices[0] = (a * w1) + (c * h1) + tx; | |||
vertices[1] = (d * h1) + (b * w1) + ty; | |||
vertices[2] = (a * w0) + (c * h1) + tx; | |||
vertices[3] = (d * h1) + (b * w0) + ty; | |||
vertices[4] = (a * w0) + (c * h0) + tx; | |||
vertices[5] = (d * h0) + (b * w0) + ty; | |||
vertices[6] = (a * w1) + (c * h0) + tx; | |||
vertices[7] = (d * h0) + (b * w1) + ty; | |||
vertices = quad.uvs; | |||
vertices[0] = vertices[6] = -ts.anchor.x; | |||
vertices[1] = vertices[3] = -ts.anchor.y; | |||
vertices[2] = vertices[4] = 1.0 - ts.anchor.x; | |||
vertices[5] = vertices[7] = 1.0 - ts.anchor.y; | |||
quad.upload(); | |||
var tex = ts._texture; | |||
var lt = ts.tileTransform.localTransform; | |||
var uv = ts.uvTransform; | |||
var mapCoord = uv.mapCoord; | |||
var uClampFrame = uv.uClampFrame; | |||
var uClampOffset = uv.uClampOffset; | |||
var w = tex.width; | |||
var h = tex.height; | |||
var W = _width; | |||
var H = _height; | |||
var tempMat = this._tempMatrix2; | |||
tempMat.set(lt.a * w / W, lt.b * w / H, lt.c * h / W, lt.d * h / H, lt.tx / W, lt.ty / H); | |||
tempMat.invert(); | |||
if (isSimple) { | |||
tempMat.append(mapCoord); | |||
} else { | |||
shader.uniforms.uMapCoord = mapCoord.toArray(true); | |||
shader.uniforms.uClampFrame = uClampFrame; | |||
shader.uniforms.uClampOffset = uClampOffset; | |||
} | |||
shader.uniforms.uTransform = tempMat.toArray(true); | |||
var color = this._tempColor; | |||
var alpha = ts.worldAlpha; | |||
PIXI.utils.hex2rgb(ts.tint, color); | |||
color[0] *= alpha; | |||
color[1] *= alpha; | |||
color[2] *= alpha; | |||
color[3] = alpha; | |||
shader.uniforms.uColor = color; | |||
renderer.bindTexture(tex, 0, true); | |||
quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); | |||
}; | |||
PictureRenderer.prototype._renderSprite = function(sprite, shader) { | |||
var renderer = this.renderer; | |||
var quad = shader.tempQuad; | |||
renderer.bindVao(quad.vao); | |||
var uvs = sprite.texture._uvs; | |||
var vertices = quad.vertices; | |||
var vd = sprite.vertexData; | |||
for (var i = 0; i < 8; i++) { | |||
quad.vertices[i] = vd[i]; | |||
} | |||
quad.uvs[0] = uvs.x0; | |||
quad.uvs[1] = uvs.y0; | |||
quad.uvs[2] = uvs.x1; | |||
quad.uvs[3] = uvs.y1; | |||
quad.uvs[4] = uvs.x2; | |||
quad.uvs[5] = uvs.y2; | |||
quad.uvs[6] = uvs.x3; | |||
quad.uvs[7] = uvs.y3; | |||
quad.upload(); | |||
var frame = sprite.texture.frame; | |||
var base = sprite.texture.baseTexture; | |||
var clamp = this._tempClamp; | |||
var eps = 0.5 / base.resolution; | |||
clamp[0] = (frame.x + eps) / base.width; | |||
clamp[1] = (frame.y + eps) / base.height; | |||
clamp[2] = (frame.x + frame.width - eps) / base.width; | |||
clamp[3] = (frame.y + frame.height - eps) / base.height; | |||
shader.uniforms.uTextureClamp = clamp; | |||
var color = this._tempColor; | |||
PIXI.utils.hex2rgb(sprite.tint, color); | |||
var alpha = sprite.worldAlpha; | |||
color[0] *= alpha; | |||
color[1] *= alpha; | |||
color[2] *= alpha; | |||
color[3] = alpha; | |||
shader.uniforms.uColor = color; | |||
renderer.bindTexture(base, 0, true); | |||
quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); | |||
}; | |||
PictureRenderer.prototype._isSimpleSprite = function(ts) { | |||
var renderer = this.renderer; | |||
var tex = ts._texture; | |||
var baseTex = tex.baseTexture; | |||
var isSimple = baseTex.isPowerOfTwo && tex.frame.width === baseTex.width && tex.frame.height === baseTex.height; | |||
if (isSimple) { | |||
if (!baseTex._glTextures[renderer.CONTEXT_UID]) { | |||
if (baseTex.wrapMode === PIXI.WRAP_MODES.CLAMP) { | |||
baseTex.wrapMode = PIXI.WRAP_MODES.REPEAT; | |||
} | |||
} else { | |||
isSimple = baseTex.wrapMode !== PIXI.WRAP_MODES.CLAMP; | |||
} | |||
} | |||
return isSimple; | |||
}; | |||
return PictureRenderer; | |||
}(PIXI.ObjectRenderer)); | |||
extras.PictureRenderer = PictureRenderer; | |||
PIXI.WebGLRenderer.registerPlugin('picture', PictureRenderer); | |||
PIXI.CanvasRenderer.registerPlugin('picture', PIXI.CanvasSpriteRenderer); | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
var PictureSprite = (function(_super) { | |||
__extends(PictureSprite, _super); | |||
function PictureSprite(texture) { | |||
_super.call(this, texture); | |||
this.pluginName = 'picture'; | |||
} | |||
return PictureSprite; | |||
}(PIXI.Sprite)); | |||
extras.PictureSprite = PictureSprite; | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); | |||
var PIXI; | |||
(function(PIXI) { | |||
var extras; | |||
(function(extras) { | |||
var PictureTilingSprite = (function(_super) { | |||
__extends(PictureTilingSprite, _super); | |||
function PictureTilingSprite(texture) { | |||
_super.call(this, texture); | |||
this.pluginName = 'picture'; | |||
} | |||
return PictureTilingSprite; | |||
}(extras.TilingSprite)); | |||
extras.PictureTilingSprite = PictureTilingSprite; | |||
})(extras = PIXI.extras || (PIXI.extras = {})); | |||
})(PIXI || (PIXI = {})); |
@@ -88,14 +88,14 @@ | |||
} | |||
.name { | |||
width: 60%; | |||
width: 47%; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
} | |||
.class { | |||
width: 40%; | |||
width: 53%; | |||
text-align: right; | |||
color: darken(@white, 25%); | |||
} | |||
@@ -11,6 +11,7 @@ define([ | |||
) { | |||
return { | |||
tpl: template, | |||
modal: true, | |||
postRender: function() { | |||
this.onEvent('onContextMenu', this.onContextMenu.bind(this)); | |||
@@ -99,11 +99,11 @@ define([ | |||
items | |||
.filter(function(item) { | |||
var spellId = item.spellId; | |||
if ((spellId != null) && (item.slot)) | |||
skipSpellId = spellId; | |||
var runeSlot = item.runeSlot; | |||
if ((runeSlot != null) && (item.slot)) | |||
skipSpellId = runeSlot; | |||
return ((item.eq) && ((item.slot) || (item.spellId != null))); | |||
return ((item.eq) && ((item.slot) || (item.runeSlot != null))); | |||
}, this) | |||
.forEach(function(item) { | |||
var imgX = -item.sprite[0] * 64; | |||
@@ -111,10 +111,10 @@ define([ | |||
var slot = item.slot; | |||
if (!slot) { | |||
var spellId = item.spellId; | |||
if (spellId > skipSpellId) | |||
spellId--; | |||
slot = 'rune-' + spellId; | |||
var runeSlot = item.runeSlot; | |||
if (runeSlot > skipSpellId) | |||
runeSlot--; | |||
slot = 'rune-' + runeSlot; | |||
} | |||
var spritesheet = item.spritesheet || '../../../images/items.png'; | |||
@@ -177,7 +177,7 @@ define([ | |||
.css('background', 'url("' + spriteSheet + '") ' + imgX + 'px ' + imgY + 'px') | |||
.on('mousemove', this.onHoverItem.bind(this, el, item, null)) | |||
.on('mouseleave', this.onHoverItem.bind(this, null, null)) | |||
.on('click', this.equipItem.bind(this, item)); | |||
.on('click', this.equipItem.bind(this, item, slot)); | |||
if (item == this.hoverCompare) | |||
el.find('.icon').addClass('eq'); | |||
@@ -187,7 +187,7 @@ define([ | |||
container.hide(); | |||
}, | |||
equipItem: function(item) { | |||
equipItem: function(item, slot) { | |||
if (item == this.hoverCompare) { | |||
this.find('.itemList').hide(); | |||
return; | |||
@@ -204,8 +204,8 @@ define([ | |||
cpn = 'inventory'; | |||
method = 'learnAbility'; | |||
data = { | |||
id: item.id, | |||
replaceId: this.hoverCompare ? this.hoverCompare.id : null | |||
itemId: item.id, | |||
slot: ~~slot.replace('rune-', '') + 1 | |||
}; | |||
if (item.empty) { | |||
@@ -213,8 +213,10 @@ define([ | |||
this.find('.itemList').hide(); | |||
return; | |||
} | |||
else | |||
data = this.hoverCompare.id; | |||
else { | |||
method = 'unlearnAbility'; | |||
data.itemId = this.hoverCompare.id; | |||
} | |||
} | |||
} | |||
@@ -286,6 +288,7 @@ define([ | |||
}, | |||
offense: { | |||
'crit chance': (~~(stats.critChance * 10) / 10) + '%', | |||
'crit multiplier': (~~(stats.critMultiplier * 10) / 10) + '%', | |||
gap1: '', | |||
'arcane increase': stats.elementArcanePercent + '%', | |||
'fire increase': stats.elementFirePercent + '%', | |||
@@ -23,11 +23,14 @@ define([ | |||
}, | |||
onGetPortrait: function(portrait) { | |||
var x = (['warrior', 'cleric', 'wizard', 'thief'].indexOf(portrait) * -64); | |||
var spritesheet = portrait.spritesheet || '../../../images/portraitIcons.png'; | |||
var x = portrait.x * -64; | |||
var y = portrait.y * -64; | |||
this.find('.portrait') | |||
.css({ | |||
background: 'url("../../../images/portraitIcons.png") ' + x + 'px 0px', | |||
background: 'url("' + spritesheet + '") ' + x + 'px ' + y + 'px', | |||
visibility: 'visible' | |||
}); | |||
}, | |||
@@ -118,11 +118,11 @@ define([ | |||
var itemEl = $(tplItem) | |||
.appendTo(container); | |||
var spritesheet = item.spritesheet || 'items'; | |||
var spritesheet = item.spritesheet || '../../../images/items.png'; | |||
if (item.material) | |||
spritesheet = 'materials'; | |||
spritesheet = '../../../images/materials.png'; | |||
else if (item.quest) | |||
spritesheet = 'questItems'; | |||
spritesheet = '../../../images/questItems.png'; | |||
itemEl | |||
.data('item', item) | |||
@@ -132,7 +132,7 @@ define([ | |||
.on('mousemove', this.onHover.bind(this, itemEl, item)) | |||
.on('mouseleave', this.hideTooltip.bind(this, itemEl, item)) | |||
.find('.icon') | |||
.css('background', 'url(../../../images/' + spritesheet + '.png) ' + imgX + 'px ' + imgY + 'px') | |||
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px') | |||
.on('contextmenu', this.showContext.bind(this, item)); | |||
if (item.quantity) | |||
@@ -23,7 +23,8 @@ define([ | |||
this.on('.btnRegister', 'click', this.onRegisterClick.bind(this)); | |||
this.find('.extra, .version') | |||
.appendTo($('<div class="uiLoginExtra"></div>>').appendTo('.ui-container')); | |||
.appendTo($('<div class="uiLoginExtra"></div>') | |||
.appendTo('.ui-container')); | |||
$('.uiLoginExtra').find('.button').on('click', this.redirect.bind(this)); | |||
@@ -16,5 +16,5 @@ | |||
<div class="el button btnPaypal" location="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BR2CC82WUAVEA">Donate on Paypal</div> | |||
<div class="el button btnWiki" location="http://isleward.gamepedia.com/Isleward_Wiki">Access the Wiki</div> | |||
</div> | |||
<div class="version">v0.1.3</div> | |||
<div class="version">v0.1.4</div> | |||
</div> |
@@ -6,7 +6,7 @@ | |||
width: 400px; | |||
height: 400px; | |||
background-color: @gray; | |||
padding: 32px; | |||
padding: 10px; | |||
border: 4px solid @lightGray; | |||
text-align: center; | |||
@@ -30,11 +30,11 @@ | |||
} | |||
&:nth-child(2) { | |||
width: 60%; | |||
width: 55%; | |||
} | |||
&:nth-child(3) { | |||
width: 30%; | |||
width: 35%; | |||
} | |||
} | |||
@@ -222,11 +222,11 @@ define([ | |||
var imgX = -item.sprite[0] * 64; | |||
var imgY = -item.sprite[1] * 64; | |||
var spritesheet = item.spritesheet || 'items'; | |||
var spritesheet = item.spritesheet || '../../../images/items.png'; | |||
if (item.material) | |||
spritesheet = 'materials'; | |||
spritesheet = '../../../images/materials.png'; | |||
else if (item.quest) | |||
spritesheet = 'questItems'; | |||
spritesheet = '../../../images/questItems.png'; | |||
var el = $(templateItem) | |||
.appendTo(container); | |||
@@ -236,7 +236,7 @@ define([ | |||
.on('mousemove', this.onHover.bind(this, el, item)) | |||
.on('mouseleave', this.hideTooltip.bind(this, el, item)) | |||
.find('.icon') | |||
.css('background', 'url(../../../images/' + spritesheet + '.png) ' + imgX + 'px ' + imgY + 'px'); | |||
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px'); | |||
if (item.quantity) { | |||
var quantityText = item.quantityText; | |||
@@ -18,6 +18,10 @@ | |||
.name { | |||
margin-bottom: 8px; | |||
.type { | |||
color: darken(@white, 40%); | |||
} | |||
.power { | |||
color: @green; | |||
display: none; | |||
@@ -1,4 +1,8 @@ | |||
<div class="name q$QUALITY$"><div class="text">$NAME$</div><div class="power">$POWER$</div></div> | |||
<div class="name q$QUALITY$"> | |||
<div class="text">$NAME$</div> | |||
<div class="type">$TYPE$</div> | |||
<div class="power">$POWER$</div> | |||
</div> | |||
<div class="stats">$STATS$</div> | |||
<div class="effects">$EFFECTS$</div> | |||
<div class="faction">faction: $faction$</div> | |||
@@ -64,7 +64,7 @@ define([ | |||
var statName = statTranslations.translate(s); | |||
var value = tempStats[s]; | |||
if (['addCritChance', 'sprintChance', 'dmgPercent', 'xpIncrease'].indexOf(s) > -1) | |||
if (['addCritChance', 'addCritMultiplier', 'sprintChance', 'dmgPercent', 'xpIncrease'].indexOf(s) > -1) | |||
value += '%'; | |||
else if ((s.indexOf('element') == 0) && (s.indexOf('Resist') == -1)) | |||
value += '%'; | |||
@@ -119,6 +119,14 @@ define([ | |||
else | |||
this.tooltip.find('.level').show(); | |||
if ((!item.type) || (item.type == item.name)) | |||
this.tooltip.find('.type').hide(); | |||
else { | |||
this.tooltip.find('.type') | |||
.html(item.type) | |||
.show(); | |||
} | |||
if (item.power) | |||
this.tooltip.find('.power').show(); | |||
@@ -12,13 +12,15 @@ define([ | |||
var tgtValues = config.target.stats.values; | |||
var statValue = 0; | |||
var statType = config.statType; | |||
if (!(statType instanceof Array)) | |||
statType = [statType]; | |||
var dmg = 0; | |||
statType.forEach(function(s) { | |||
statValue += srcValues[s]; | |||
}); | |||
if (config.statType) { | |||
var statType = config.statType; | |||
if (!(statType instanceof Array)) | |||
statType = [statType]; | |||
var dmg = 0; | |||
statType.forEach(function(s) { | |||
statValue += srcValues[s]; | |||
}); | |||
} | |||
statValue = max(1, statValue); | |||
@@ -30,8 +32,10 @@ define([ | |||
resist += (tgtValues[elementName + 'Resist'] || 0); | |||
} | |||
var statMult = config.statMult || 1; | |||
var dps = ( | |||
(config.statMult * statValue * config.damage) * | |||
(statMult * statValue * config.damage) * | |||
max((0.5 + (dmgPercent / 100)), 0.5) | |||
); | |||
@@ -44,9 +48,12 @@ define([ | |||
); | |||
} | |||
var cd = config.source.mob ? 1 : config.cd; | |||
var amount = dps; | |||
var amount = dps * cd * 0.3; | |||
if ((config.source.mob) || (config.cd)) { | |||
var cd = config.source.mob ? 1 : config.cd; | |||
amount *= cd * 0.3; | |||
} | |||
var isCrit = false; | |||
if (!config.noCrit) { | |||
@@ -54,7 +61,7 @@ define([ | |||
var roll = mathRandom() * 100; | |||
if ((roll < critChance) || (config.crit)) { | |||
isCrit = true; | |||
amount *= 1.5; | |||
amount *= (srcValues.critMultiplier / 100); | |||
} | |||
} | |||
@@ -90,7 +90,7 @@ define([ | |||
//find mobs in range | |||
var range = this.range; | |||
var faction = this.faction; | |||
var inRange = this.physics.getArea(x - range, y - range, x + range, y + range, (c => (((!c.player) || (!obj.player)) && (c.aggro) && (c.aggro.willAttack(obj))))); | |||
var inRange = this.physics.getArea(x - range, y - range, x + range, y + range, (c => (((!c.player) || (!obj.player)) && (c.aggro) && (c.aggro.willAutoAttack(obj))))); | |||
if (inRange.length == 0) | |||
return; | |||
@@ -118,7 +118,22 @@ define([ | |||
} | |||
}, | |||
willAttack: function(target) { | |||
canAttack: function(target) { | |||
var obj = this.obj; | |||
if (target == obj) | |||
return false; | |||
else if ((target.player) && (obj.player)) | |||
return ((obj.prophecies.hasProphecy('butcher')) && (target.prophecies.hasProphecy('butcher'))); | |||
else if ((target.follower) && (target.follower.master.player) && (obj.player)) | |||
return false; | |||
else if (obj.player) | |||
return true; | |||
else if (target.aggro.faction != obj.aggro.faction) | |||
return true; | |||
}, | |||
willAutoAttack: function(target) { | |||
if (this.obj == target) | |||
return false; | |||
@@ -126,9 +141,6 @@ define([ | |||
if (faction == null) | |||
return false; | |||
if ((target.player) && (this.obj.player)) | |||
return ((this.obj.prophecies.hasProphecy('butcher')) && (target.prophecies.hasProphecy('butcher'))); | |||
var rep = this.obj.reputation; | |||
if (!rep) { | |||
var targetRep = target.reputation; | |||
@@ -151,6 +163,10 @@ define([ | |||
}, | |||
tryEngage: function(obj, amount, threatMult) { | |||
//Don't aggro yourself, stupid | |||
if (obj == this.obj) | |||
return; | |||
var result = { | |||
success: true | |||
}; | |||
@@ -235,7 +251,7 @@ define([ | |||
if (this.obj.spellbook) | |||
this.obj.spellbook.unregisterCallback(obj.id, true); | |||
if ((this.list.length == 0) && (this.obj.mob)) | |||
if ((this.list.length == 0) && (this.obj.mob) && (!this.obj.follower)) | |||
this.obj.stats.resetHp(); | |||
}, | |||
@@ -293,7 +309,7 @@ define([ | |||
for (var i = 0; i < lLen; i++) { | |||
var l = list[i]; | |||
if (l.obj.destroyed) { | |||
list.splice(i, 1); | |||
this.unAggro(l.obj); | |||
i--; | |||
lLen--; | |||
} | |||
@@ -90,7 +90,7 @@ define([ | |||
io.set({ | |||
ent: this.charname, | |||
field: 'character', | |||
value: JSON.stringify(simple), | |||
value: JSON.stringify(simple).split(`'`).join(`''`), | |||
callback: callback | |||
}); | |||
@@ -9,7 +9,7 @@ define([ | |||
chats: null, | |||
cdMax: 50, | |||
cd: 0, | |||
chance: 0.02, | |||
chance: 0.035, | |||
init: function(blueprint) { | |||
this.chats = extend(true, [], blueprint.chats); | |||
@@ -17,6 +17,9 @@ define([ | |||
}, | |||
update: function() { | |||
if ((this.obj.aggro) && (this.obj.aggro.list.length > 0)) | |||
return; | |||
if ((this.cd == 0) && (Math.random() < this.chance)) { | |||
this.cd = this.cdMax; | |||
@@ -108,6 +108,11 @@ define([ | |||
else | |||
return null; | |||
} | |||
else if (stateConfig.method) { | |||
stateConfig.method(sourceObj); | |||
if (!stateConfig.msg) | |||
return; | |||
} | |||
var result = { | |||
id: this.obj.id, | |||
@@ -145,16 +150,25 @@ define([ | |||
result.options = Object.keys(result.options); | |||
} | |||
result.options = result.options.map(function(o) { | |||
var gotoState = this.states[(o + '').split('.')[0]]; | |||
if (!gotoState.options[o]) | |||
return null; | |||
result.options = result.options | |||
.map(function(o) { | |||
var gotoState = this.states[(o + '').split('.')[0]]; | |||
var picked = gotoState.options[o]; | |||
if (!picked) | |||
return null; | |||
else if (picked.prereq) { | |||
var doesConform = picked.prereq(sourceObj); | |||
if (!doesConform) | |||
return null; | |||
} | |||
return { | |||
id: o, | |||
msg: gotoState.options[o].msg | |||
}; | |||
}, this); | |||
return { | |||
id: o, | |||
msg: picked.msg | |||
}; | |||
}, this) | |||
.filter(o => !!o); | |||
result.options.push({ | |||
msg: 'Goodbye', | |||
@@ -91,12 +91,8 @@ define([ | |||
this.obj.spellbook.calcDps(); | |||
if ((!this.obj.mob) || (item.ability)) { | |||
if (item.spell) { | |||
this.obj.inventory.learnAbility({ | |||
id: itemId, | |||
spellId: spellId | |||
}, true); | |||
} | |||
if (item.spell) | |||
this.obj.inventory.learnAbility(itemId, item.runeSlot); | |||
else { | |||
var result = item; | |||
if (item.effects) { | |||
@@ -148,11 +144,11 @@ define([ | |||
} | |||
delete item.eq; | |||
this.eq[item.slot] = null; | |||
delete this.eq[item.slot]; | |||
if (item.spell) { | |||
item.eq = true; | |||
this.obj.inventory.learnAbility(itemId); | |||
this.obj.inventory.unlearnAbility(itemId, item.runeSlot); | |||
} else { | |||
if (!item.effects) | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', item); | |||
@@ -30,7 +30,7 @@ define([ | |||
this.items[name] = list; | |||
this.regenList(list); | |||
} else if ((list.level != requestLevel) || (Math.random() < 2)) | |||
} else if (list.level != requestLevel) | |||
this.regenList(list); | |||
var reputation = requestedBy.reputation; | |||
@@ -0,0 +1,138 @@ | |||
define([ | |||
'config/roles', | |||
'world/atlas', | |||
'items/generator', | |||
'misc/random', | |||
'items/config/slots' | |||
], function( | |||
roles, | |||
atlas, | |||
generator, | |||
random, | |||
configSlots | |||
) { | |||
return { | |||
roleLevel: null, | |||
init: function(blueprint) { | |||
this.roleLevel = roles.getRoleLevel(this.obj); | |||
}, | |||
onBeforeChat: function(msg) { | |||
if (this.roleLevel < 10) | |||
return; | |||
var messageText = msg.message; | |||
if (messageText[0] != '/') | |||
return; | |||
messageText = messageText.substr(1).split(' '); | |||
var actionName = messageText.splice(0, 1)[0].toLowerCase(); | |||
actionName = Object.keys(this).find(a => (a.toLowerCase() == actionName)); | |||
if (!actionName) | |||
return; | |||
var config = {}; | |||
if ((messageText.length == 1) && (messageText[0].indexOf('=') == -1)) | |||
config = messageText[0]; | |||
else { | |||
messageText.forEach(function(m) { | |||
m = m.split('='); | |||
config[m[0]] = m[1]; | |||
}); | |||
} | |||
msg.ignore = true; | |||
atlas.performAction(this.obj, { | |||
cpn: 'social', | |||
method: actionName, | |||
data: config | |||
}); | |||
}, | |||
//actions | |||
getItem: function(config) { | |||
if (config.slot == 'set') { | |||
configSlots.slots.forEach(function(s) { | |||
if (s == 'tool') | |||
return; | |||
var newConfig = extend(true, {}, config, { | |||
slot: s | |||
}); | |||
this.getItem(newConfig); | |||
}, this); | |||
return; | |||
} | |||
if (config.stats) | |||
config.stats = config.stats.split(','); | |||
this.obj.inventory.getItem(generator.generate(config)); | |||
}, | |||
getGold: function(amount) { | |||
this.obj.trade.gold += ~~amount; | |||
this.obj.syncer.set(true, 'trade', 'gold', this.obj.trade.gold); | |||
}, | |||
setLevel: function(level) { | |||
var obj = this.obj; | |||
var syncer = obj.syncer; | |||
level = Math.max(1, ~~level); | |||
var stats = obj.stats; | |||
var values = stats.values; | |||
var oldLevel = values.level; | |||
values.level = level; | |||
var delta = level - oldLevel; | |||
values.hpMax += (40 * delta); | |||
syncer.setObject(true, 'stats', 'values', 'level', level); | |||
syncer.setObject(true, 'stats', 'values', 'hpMax', values.hpMax); | |||
process.send({ | |||
method: 'object', | |||
serverId: obj.serverId, | |||
obj: { | |||
level: level | |||
} | |||
}); | |||
stats.calcXpMax(); | |||
}, | |||
godMode: function() { | |||
var obj = this.obj; | |||
var statValues = obj.stats.values; | |||
var newValues = { | |||
int: 10000000, | |||
str: 10000000, | |||
dex: 10000000, | |||
hpMax: 10000000, | |||
hp: 10000000, | |||
manaMax: 10000000, | |||
mana: 10000000 | |||
}; | |||
var syncer = obj.syncer; | |||
for (var s in newValues) { | |||
var newValue = newValues[s]; | |||
statValues[s] = newValue; | |||
syncer.setObject(true, 'stats', 'values', s, newValue); | |||
} | |||
obj.spellbook.calcDps(); | |||
} | |||
}; | |||
}); |
@@ -1,7 +1,7 @@ | |||
define([ | |||
], function( | |||
) { | |||
return { | |||
type: 'follower', | |||
@@ -9,6 +9,12 @@ define([ | |||
master: null, | |||
lifetime: -1, | |||
maxDistance: 10, | |||
lastMasterPos: { | |||
x: 0, | |||
y: 0 | |||
}, | |||
fGetHighest: { | |||
inCombat: null, | |||
@@ -16,9 +22,13 @@ define([ | |||
}, | |||
bindEvents: function() { | |||
this.lifetime = 100; | |||
var master = this.master; | |||
this.lastMasterPos.x = master.x; | |||
this.lastMasterPos.y = master.y; | |||
this.fGetHighest.inCombat = this.master.aggro.getHighest.bind(this.master.aggro); | |||
this.obj.aggro.faction = master.aggro.faction; | |||
this.fGetHighest.inCombat = master.aggro.getHighest.bind(master.aggro); | |||
this.fGetHighest.outOfCombat = this.returnNoAggro.bind(this); | |||
}, | |||
@@ -48,11 +58,42 @@ define([ | |||
}); | |||
}, | |||
teleport: function() { | |||
var obj = this.obj; | |||
var physics = obj.instance.physics; | |||
var syncer = obj.syncer; | |||
var master = this.master; | |||
var newPosition = physics.getOpenCellInArea(master.x - 1, master.y - 1, master.x + 1, master.y + 1); | |||
physics.removeObject(obj, obj.x, obj.y); | |||
obj.x = newPosition.x; | |||
obj.y = newPosition.y; | |||
syncer.o.x = obj.x; | |||
syncer.o.y = obj.y; | |||
physics.addObject(obj, obj.x, obj.y); | |||
obj.instance.syncer.queue('onGetObject', { | |||
x: obj.x, | |||
y: obj.y, | |||
components: [{ | |||
type: 'attackAnimation', | |||
row: 0, | |||
col: 4 | |||
}] | |||
}); | |||
}, | |||
update: function() { | |||
this.lifetime--; | |||
if (this.lifetime <= 0) { | |||
this.despawn(); | |||
return; | |||
if (this.lifetime > 0) { | |||
this.lifetime--; | |||
if (this.lifetime <= 0) { | |||
this.despawn(); | |||
return; | |||
} | |||
} | |||
var obj = this.obj; | |||
@@ -63,18 +104,42 @@ define([ | |||
return; | |||
} | |||
var doMove = ( | |||
(Math.abs(obj.x - master.x) >= 10) || | |||
(Math.abs(obj.y - master.y) >= 10) | |||
); | |||
var attacker = this.fGetHighest.inCombat(); | |||
var maxDistance = this.maxDistance; | |||
var distance = Math.max(Math.abs(obj.x - master.x), Math.abs(obj.y - master.y)); | |||
if (doMove) { | |||
if (obj.aggro.getHighest == this.fGetHighest.inCombat) | |||
obj.mob.target = obj; | |||
var doMove = (distance >= maxDistance); | |||
//When we're too far, just teleport | |||
if ((!attacker) && (distance >= maxDistance * 2)) { | |||
this.teleport(); | |||
return; | |||
} | |||
var doMove = false; | |||
//If we're not too far from the master but the master is not in combat, move anyway | |||
if (!attacker) { | |||
var lastMasterPos = this.lastMasterPos; | |||
if ((master.x != lastMasterPos.x) || (master.y != lastMasterPos.y)) { | |||
doMove = true; | |||
lastMasterPos.x = master.x; | |||
lastMasterPos.y = master.y; | |||
} | |||
} | |||
if (doMove) { | |||
this.obj.clearQueue(); | |||
obj.mob.target = obj; | |||
} | |||
obj.aggro.getHighest = doMove ? this.fGetHighest.outOfCombat : this.fGetHighest.inCombat; | |||
}, | |||
simplify: function() { | |||
return { | |||
type: 'follower', | |||
master: this.master.id | |||
}; | |||
} | |||
}; | |||
}); |
@@ -34,12 +34,16 @@ define([ | |||
var item = items[i]; | |||
//Hacks for old items | |||
if ((item.spell) && (!item.spell.rolls)) | |||
if (((item.spell) && (!item.spell.rolls)) || (!item.sprite)) { | |||
items.splice(i, 1); | |||
i--; | |||
iLen--; | |||
continue; | |||
else if ((item.spell) && (item.type == 'Spear')) { | |||
} else if ((item.spell) && (item.type == 'Spear')) { | |||
item.spell.properties = item.spell.properties || {}; | |||
item.spell.properties.range = item.range; | |||
} | |||
} else if (item.quantity == NaN) | |||
item.quantity = 1; | |||
} | |||
this.hookItemEvents(items); | |||
@@ -48,26 +52,9 @@ define([ | |||
this.getItem(items[i], true); | |||
} | |||
if ((this.obj.player) && (!isTransfer)) { | |||
if ((this.obj.player) && (!isTransfer)) | |||
this.getDefaultAbilities(); | |||
/*this.getItem(generator.generate({ | |||
spell: true, | |||
spellName: 'arcane barrier' | |||
}));*/ | |||
/*for (var i = 0; i < 1; i++) { | |||
var item = generator.generate({ | |||
slot: 'twoHanded', | |||
type: 'Spear', | |||
quality: 4, | |||
level: 1 | |||
}); | |||
this.getItem(item); | |||
}*/ | |||
} | |||
delete blueprint.items; | |||
this.blueprint = blueprint; | |||
@@ -91,7 +78,6 @@ define([ | |||
}); | |||
} | |||
} | |||
}, | |||
//Client Actions | |||
@@ -115,54 +101,62 @@ define([ | |||
this.resolveCallback(msg, result); | |||
}, | |||
learnAbility: function(id, forceLearn) { | |||
var replaceId = null; | |||
var newSpellId = id.spellId; | |||
if (id.id != null) { | |||
replaceId = id.replaceId; | |||
id = id.id; | |||
learnAbility: function(itemId, runeSlot) { | |||
if (itemId.itemId != null) { | |||
var msg = itemId; | |||
itemId = msg.itemId; | |||
runeSlot = msg.slot; | |||
} | |||
var item = this.findItem(id); | |||
if ((!item) || (!item.spell) || ((item.spellId == null) && (item.eq) && (!forceLearn))) { | |||
if (item) | |||
item.eq = false; | |||
var item = this.findItem(itemId); | |||
if (!item) | |||
return; | |||
else if (!item.spell) { | |||
item.eq = false; | |||
return; | |||
} | |||
var spellbook = this.obj.spellbook; | |||
if ((item.eq) && (!forceLearn)) { | |||
delete item.eq; | |||
spellbook.removeSpellById(item.spellId); | |||
delete item.spellId; | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', item); | |||
return; | |||
if (item.slot == 'twoHanded') | |||
runeSlot = 0; | |||
else if (runeSlot == null) { | |||
if (!this.items.some(i => (i.runeSlot == 2))) | |||
runeSlot = 2; | |||
else | |||
runeSlot = 1; | |||
} | |||
if (replaceId != null) { | |||
var replaceItem = this.findItem(replaceId); | |||
if (replaceItem) { | |||
delete replaceItem.eq; | |||
spellbook.removeSpellById(replaceItem.spellId); | |||
newSpellId = replaceItem.spellId; | |||
delete replaceItem.spellId; | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', replaceItem); | |||
} | |||
var currentEq = this.items.find(i => (i.runeSlot == runeSlot)); | |||
if (currentEq) { | |||
spellbook.removeSpellById(runeSlot); | |||
delete currentEq.eq; | |||
delete currentEq.runeSlot; | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', currentEq); | |||
} | |||
if (spellbook.spells.length >= 3) { | |||
if (item.slot) | |||
item.spellId = -1; | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', item); | |||
item.eq = true; | |||
item.runeSlot = runeSlot; | |||
spellbook.addSpellFromRune(item.spell, runeSlot); | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', item); | |||
}, | |||
unlearnAbility: function(itemId) { | |||
if (itemId.itemId != null) | |||
itemId = itemId.itemId; | |||
var item = this.findItem(itemId); | |||
if (!item) | |||
return; | |||
else if (!item.spell) { | |||
item.eq = false; | |||
return; | |||
} | |||
item.spellId = spellbook.addSpellFromRune(item.spell, newSpellId); | |||
if (item.spellId != -1) | |||
item.eq = true; | |||
else | |||
delete item.spell; | |||
var spellbook = this.obj.spellbook; | |||
spellbook.removeSpellById(item.runeSlot); | |||
delete item.eq; | |||
delete item.runeSlot; | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', item); | |||
}, | |||
@@ -176,7 +170,7 @@ define([ | |||
var stash = this.obj.stash; | |||
if (!stash.active) | |||
return; | |||
var clonedItem = extend(true, {}, item); | |||
this.destroyItem(id); | |||
stash.deposit(clonedItem); | |||
@@ -326,25 +320,25 @@ define([ | |||
this.getItem(item); | |||
} | |||
var hasSpell = this.items.some(function(i) { | |||
return ( | |||
(i.spell) && | |||
(i.spell.rolls) && | |||
((i.spell.rolls.damage != null) || (i.spell.rolls.healing != null)) && | |||
(i.slot != 'twoHanded') | |||
); | |||
}); | |||
if (!hasSpell) { | |||
var item = generator.generate({ | |||
spell: true, | |||
spellQuality: 'basic', | |||
spellName: classes.spells[this.obj.class][0] | |||
classes.spells[this.obj.class].forEach(function(spellName) { | |||
var hasSpell = this.items.some(function(i) { | |||
return ( | |||
(i.spell) && | |||
(i.spell.name.toLowerCase() == spellName) | |||
); | |||
}); | |||
item.eq = true; | |||
item.noSalvage = true; | |||
this.getItem(item); | |||
} | |||
if (!hasSpell) { | |||
var item = generator.generate({ | |||
spell: true, | |||
spellQuality: 'basic', | |||
spellName: spellName | |||
}); | |||
item.eq = true; | |||
item.noSalvage = true; | |||
this.getItem(item); | |||
} | |||
}, this); | |||
}, | |||
createBag: function(x, y, items, ownerId) { | |||
@@ -390,7 +384,7 @@ define([ | |||
return obj; | |||
}, | |||
getItem: function(item, hideMessage) { | |||
//We need to know if a mob dropped it for quest purposes | |||
var fromMob = item.fromMob; | |||
@@ -413,7 +407,7 @@ define([ | |||
exists = true; | |||
if (!existItem.quantity) | |||
existItem.quantity = 1; | |||
existItem.quantity += item.quantity; | |||
existItem.quantity += (item.quantity || 1); | |||
item = existItem; | |||
} | |||
} | |||
@@ -508,7 +502,7 @@ define([ | |||
if (item.eq) { | |||
if (item.ability) | |||
this.learnAbility(item.id, true); | |||
this.learnAbility(item.id, item.runeSlot); | |||
else | |||
this.obj.equipment.equip(item.id); | |||
} else { | |||
@@ -521,7 +515,7 @@ define([ | |||
text: e.text, | |||
properties: e.properties | |||
})); | |||
var reputation = this.obj.reputation; | |||
//Don't do this check if we don't have a reputation cpn. That means this is most likely a bag | |||
@@ -585,29 +579,13 @@ define([ | |||
var blueprint = this.blueprint; | |||
if (blueprint.noRandom) { | |||
this.items = []; | |||
var blueprints = blueprint.blueprints; | |||
for (var i = 0; i < blueprints.length; i++) { | |||
var drop = blueprints[i]; | |||
if ((drop.maxLevel) && (drop.maxLevel < killSource.stats.values.level)) | |||
continue; | |||
drop.level = drop.level || level; | |||
drop.magicFind = magicFind; | |||
this.getItem(generator.generate(drop), true); | |||
} | |||
killSource.fireEvent('beforeTargetDeath', this.obj, this.items); | |||
if (this.items.length > 0) | |||
this.createBag(this.obj.x, this.obj.y, this.items, ownerId); | |||
} else { | |||
var instancedItems = extend(true, [], this.items); | |||
var useItems = []; | |||
var savedItems = extend(true, [], this.items); | |||
var instancedItems = extend(true, [], this.items); | |||
this.items = []; | |||
var magicFind = (blueprint.magicFind || 0) + killSource.stats.values.magicFind; | |||
if ((!blueprint.noRandom) || (blueprint.alsoRandom)) { | |||
var magicFind = (blueprint.magicFind || 0); | |||
var bonusMagicFind = killSource.stats.values.magicFind; | |||
for (var i = 0; i < blueprint.rolls; i++) { | |||
if (Math.random() * 100 >= (blueprint.chance || 35)) | |||
continue; | |||
@@ -639,20 +617,43 @@ define([ | |||
slot: useItem.slot, | |||
type: useItem.type, | |||
spell: !!useItem.ability, | |||
stats: useItem.stats ? Object.keys(useItem.stats) : null, | |||
magicFind: magicFind | |||
magicFind: magicFind, | |||
bonusMagicFind: bonusMagicFind | |||
}; | |||
useItem = generator.generate(itemBlueprint); | |||
useItems.push(useItem); | |||
this.getItem(useItem); | |||
} | |||
} | |||
killSource.fireEvent('beforeTargetDeath', this.obj, useItems); | |||
if (blueprint.noRandom) { | |||
var blueprints = blueprint.blueprints; | |||
for (var i = 0; i < blueprints.length; i++) { | |||
var drop = blueprints[i]; | |||
if ((blueprint.chance) && (~~(Math.random() * 100) >= blueprint.chance)) | |||
continue; | |||
if (useItems.length > 0) | |||
this.createBag(this.obj.x, this.obj.y, useItems, ownerId); | |||
if ((drop.maxLevel) && (drop.maxLevel < killSource.stats.values.level)) | |||
continue; | |||
drop.level = drop.level || this.obj.stats.values.level; | |||
drop.magicFind = magicFind; | |||
var item = drop; | |||
if (!item.quest) | |||
item = generator.generate(drop); | |||
this.getItem(item, true); | |||
} | |||
} | |||
killSource.fireEvent('beforeTargetDeath', this.obj, this.items); | |||
if (this.items.length > 0) | |||
this.createBag(this.obj.x, this.obj.y, this.items, ownerId); | |||
this.items = savedItems; | |||
}, | |||
giveItems: function(obj, hideMessage) { | |||
@@ -715,7 +716,7 @@ define([ | |||
if (!effectEvent) | |||
continue; | |||
effectEvent.call(this.obj, item, args[0]); | |||
effectEvent.apply(this.obj, [item, ...args]); | |||
} | |||
} | |||
}, | |||
@@ -31,10 +31,10 @@ define([ | |||
if (this.obj.aggro) | |||
target = this.obj.aggro.getHighest(); | |||
var goHome = false; | |||
if ((target) && (target != this.obj)) { | |||
if ((target) && (target != this.obj) && ((!this.obj.follower) || (this.obj.follower.master != target))) { | |||
this.fight(target); | |||
return; | |||
} else if (this.target) { | |||
} else if ((!target) && (this.target)) { | |||
this.target = null; | |||
this.obj.clearQueue(); | |||
goHome = true; | |||
@@ -105,7 +105,7 @@ define([ | |||
if (!doesCollide) { | |||
hasLos = this.physics.hasLos(x, y, tx, ty); | |||
if (hasLos) { | |||
if (rnd() < 0.65) { | |||
if (((obj.follower) && (obj.follower.master.player)) || (rnd() < 0.65)) { | |||
var spell = obj.spellbook.getRandomSpell(target); | |||
var success = obj.spellbook.cast({ | |||
spell: spell, | |||
@@ -13,6 +13,8 @@ define([ | |||
maxLevel: 0, | |||
contents: [], | |||
init: function(blueprint) { | |||
this.msg = blueprint.msg; | |||
this.actions = blueprint.actions || {}; | |||
@@ -25,11 +25,12 @@ define([ | |||
spawn: function(character) { | |||
var obj = this.obj; | |||
extend(true, obj, { | |||
sheetName: classes.getSpritesheet(character.class), | |||
layerName: 'mobs', | |||
cell: character.cell, | |||
previewSpritesheet: character.previewSpritesheet, | |||
previewSpritesheet: character.previewSpritesheet || null, | |||
name: character.name, | |||
class: character.class, | |||
zoneName: character.zoneName || 'tutorial', | |||
@@ -59,7 +60,8 @@ define([ | |||
stats.stats.logins++; | |||
//obj.addComponent('spellbook', { spells: extend(true, [], classes.spells[obj.class]) }); | |||
obj.portrait = classes.portraits[character.class]; | |||
obj.addComponent('spellbook'); | |||
obj.addComponent('dialogue'); | |||
@@ -67,8 +69,9 @@ define([ | |||
obj.addComponent('reputation', character.components.find(c => c.type == 'reputation')); | |||
obj.addComponent('social'); | |||
obj.social.init(); | |||
obj.addComponent('aggro', { | |||
faction: 1 | |||
faction: 'players' | |||
}); | |||
obj.addComponent('gatherer'); | |||
obj.addComponent('stash', { | |||
@@ -152,7 +155,16 @@ define([ | |||
if (!permadeath) { | |||
var level = this.obj.stats.values.level; | |||
var spawns = this.obj.spawn; | |||
var spawnPos = ((spawns.find(s => ((s.maxLevel) && (s.maxLevel >= level)))) || (spawns[0])); | |||
var spawnPos = spawns.filter(s => (((s.maxLevel) && (s.maxLevel >= level)) || (!s.maxLevel))); | |||
if ((spawnPos.length == 0) || (!source.name)) | |||
spawnPos = spawns[0]; | |||
else if (source.name) { | |||
var sourceSpawnPos = spawnPos.find(s => ((s.source) && (s.source.toLowerCase() == source.name.toLowerCase()))); | |||
if (sourceSpawnPos) | |||
spawnPos = sourceSpawnPos; | |||
else | |||
spawnPos = spawnPos[0]; | |||
} | |||
this.obj.x = spawnPos.x; | |||
this.obj.y = spawnPos.y; | |||
@@ -168,6 +180,8 @@ define([ | |||
else | |||
this.obj.stats.dead = true; | |||
this.obj.fireEvent('onAfterDeath', source); | |||
this.obj.aggro.die(); | |||
this.obj.spellbook.die(); | |||
this.obj.effects.die(); | |||
@@ -34,7 +34,9 @@ define([ | |||
var factionBlueprint = null; | |||
try { | |||
factionBlueprint = require('config/factions/' + factionId); | |||
} catch (e) {} | |||
} catch (e) { | |||
console.log(e); | |||
} | |||
if (factionBlueprint == null) | |||
return; | |||
@@ -12,7 +12,9 @@ define([ | |||
partyLeaderId: null, | |||
party: null, | |||
init: function() {}, | |||
init: function() { | |||
this.obj.extendComponent('social', 'socialCommands', {}); | |||
}, | |||
simplify: function() { | |||
return { | |||
@@ -68,6 +70,10 @@ define([ | |||
}, | |||
chat: function(msg) { | |||
this.onBeforeChat(msg.data); | |||
if (msg.data.ignore) | |||
return; | |||
var charname = this.obj.auth.charname; | |||
var level = this.obj.stats.values.level; | |||
if (level >= 10) | |||
@@ -104,18 +104,19 @@ define([ | |||
var sheetName = this.obj.sheetName; | |||
var animationName = builtSpell.animation; | |||
if (sheetName == 'characters') | |||
animation = animations.classes[this.obj.class]; | |||
else if (sheetName == 'mobs') | |||
if (sheetName == 'mobs') | |||
animation = animations.mobs; | |||
else if (sheetName == 'bosses') | |||
animation = animations.bosses; | |||
else if (sheetName.indexOf('/') > -1) | |||
animation = animations.mobs[sheetName]; | |||
else | |||
animation = animations.classes[this.obj.class]; | |||
if ((animation) && (animation[this.obj.cell]) && (animation[this.obj.cell][animationName])) { | |||
builtSpell.animation = extend(true, {}, animation[this.obj.cell][animationName]); | |||
builtSpell.animation.name = animationName; | |||
} | |||
else | |||
} else | |||
builtSpell.animation = null; | |||
} | |||
@@ -124,21 +125,17 @@ define([ | |||
if ((this.furthestRange == -1) || (builtSpell.range > this.furthestRange)) | |||
this.furthestRange = builtSpell.range; | |||
var id = [0, 1, 2].find(function(s) { | |||
return (!this.spells.some(f => (f.id == s))); | |||
}, this); | |||
builtSpell.id = (spellId == null) ? id : spellId; | |||
if (spellId == null) | |||
this.spells.push(builtSpell); | |||
else | |||
this.spells.splice(spellId, 0, builtSpell); | |||
builtSpell.id = (options.id == null) ? spellId : options.id; | |||
this.spells.push(builtSpell); | |||
this.spells.sort(function(a, b) { | |||
return (a.id - b.id); | |||
}); | |||
builtSpell.calcDps(null, true); | |||
if (builtSpell.init) | |||
builtSpell.init(); | |||
if (this.obj.player) | |||
if (this.obj.player) | |||
this.obj.syncer.setArray(true, 'spellbook', 'getSpells', builtSpell.simplify()); | |||
return builtSpell.id; | |||
@@ -156,7 +153,7 @@ define([ | |||
runeSpell.rolls = {}; | |||
runeSpell.values = {}; | |||
var builtSpell = extend(true, { | |||
values: {} | |||
}, playerSpell, playerSpellConfig); | |||
@@ -172,8 +169,7 @@ define([ | |||
if (int) { | |||
val = ~~val; | |||
r = r.replace('i_', ''); | |||
} | |||
else | |||
} else | |||
val = ~~(val * 10) / 10; | |||
builtSpell[r] = val; | |||
@@ -201,11 +197,14 @@ define([ | |||
}, | |||
removeSpellById: function(id) { | |||
this.spells.spliceWhere(s => (s.id == id)); | |||
var exists = this.spells.spliceFirstWhere(s => (s.id == id)); | |||
this.obj.syncer.setArray(true, 'spellbook', 'removeSpells', id); | |||
if (exists) { | |||
exists.unlearn && exists.unlearn(); | |||
this.auto.spliceWhere(a => a.spell == id); | |||
this.obj.syncer.setArray(true, 'spellbook', 'removeSpells', id); | |||
this.auto.spliceWhere(a => a.spell == id); | |||
} | |||
}, | |||
queueAuto: function(action) { | |||
@@ -220,8 +219,7 @@ define([ | |||
}); | |||
return true; | |||
} | |||
else | |||
} else | |||
exists.target = action.target; | |||
}, | |||
getRandomSpell: function(target) { | |||
@@ -230,10 +228,10 @@ define([ | |||
if (s.canCast(target)) | |||
valid.push(i); | |||
}); | |||
if (valid.length > 0) | |||
return valid[~~(Math.random() * valid.length)] | |||
else | |||
else | |||
return null; | |||
}, | |||
cast: function(action, isAuto) { | |||
@@ -253,17 +251,20 @@ define([ | |||
x: this.obj.x, | |||
y: this.obj.y | |||
}; | |||
} | |||
else if (spell.spellType == 'buff') { | |||
} else if (spell.spellType == 'buff') { | |||
action.target = this.obj; | |||
} | |||
} | |||
if (!spell.targetGround) { | |||
if (action.target == null) { | |||
console.log('NO TARGET'); | |||
console.log(action); | |||
return false; | |||
if (spell.autoTargetFollower) { | |||
action.target = this.spells.find(s => (s.minions) && (s.minions.length > 0)); | |||
if (action.target) | |||
action.target = action.target.minions[0]; | |||
else | |||
return false; | |||
} | |||
} | |||
//Did we pass in the target id? | |||
@@ -273,9 +274,13 @@ define([ | |||
return false; | |||
} | |||
if ((action.target.aggro) && (!this.obj.aggro.willAttack(action.target)) && (spell.spellType != 'buff')) { | |||
if ((this.obj.player) && (action.target.player)) | |||
if (spell.spellType == 'buff') { | |||
if (this.obj.aggro.faction != action.target.aggro.faction) | |||
return; | |||
} else if ((action.target.aggro) && (!this.obj.aggro.canAttack(action.target))) { | |||
if (this.obj.player) | |||
this.sendAnnouncement("You don't feel like attacking that target"); | |||
return; | |||
} | |||
} | |||
@@ -342,7 +347,11 @@ define([ | |||
if (success) { | |||
this.obj.stats.values.mana -= spell.manaCost; | |||
spell.cd = spell.cdMax; | |||
var cd = { | |||
cd: spell.cdMax | |||
}; | |||
this.obj.fireEvent('beforeSetSpellCooldown', cd); | |||
spell.cd = cd.cd; | |||
if (this.obj.player) { | |||
var syncer = this.obj.syncer; | |||
@@ -377,9 +386,9 @@ define([ | |||
if ((spell.range > furthest) && (spell.canCast())) | |||
furthest = spell.range; | |||
} | |||
if (furthest == 0) | |||
if (furthest == 0) | |||
furthest = this.furthestRange; | |||
return furthest; | |||
} | |||
}, | |||
@@ -495,6 +504,26 @@ define([ | |||
}); | |||
}, | |||
fireEvent: function(event, args) { | |||
var spells = this.spells; | |||
var sLen = spells.length; | |||
for (var i = 0; i < sLen; i++) { | |||
var s = spells[i]; | |||
var events = s.events; | |||
if (events) { | |||
var callback = events[event]; | |||
if (!callback) | |||
continue; | |||
callback.apply(s, args); | |||
} | |||
if (s.castEvent == event) | |||
s.cast(); | |||
} | |||
}, | |||
events: { | |||
beforeRezone: function() { | |||
var callbacks = this.callbacks; | |||
@@ -1,7 +1,7 @@ | |||
define([ | |||
'config/animations' | |||
], function( | |||
animations | |||
) { | |||
return { | |||
type: 'stats', | |||
@@ -22,7 +22,9 @@ define([ | |||
regenHp: 0, | |||
regenMana: 10, | |||
addCritChance: 0, | |||
addCritMultiplier: 0, | |||
critChance: 5, | |||
critMultiplier: 150, | |||
armor: 0, | |||
dmgPercent: 0, | |||
@@ -77,7 +79,7 @@ define([ | |||
}, | |||
update: function() { | |||
if ((this.obj.mob) || (this.dead)) | |||
if (((this.obj.mob) && (!this.obj.follower)) || (this.dead)) | |||
return; | |||
var regen = { | |||
@@ -89,6 +91,11 @@ define([ | |||
var values = this.values; | |||
var isInCombat = (this.obj.aggro.list.length > 0); | |||
if (this.obj.follower) { | |||
isInCombat = (this.obj.follower.master.aggro.list.length > 0); | |||
if (isInCombat) | |||
return; | |||
} | |||
var regenHp = 0; | |||
var regenMana = 0; | |||
@@ -137,6 +144,9 @@ define([ | |||
if (stat == 'addCritChance') { | |||
this.values.critChance += (0.05 * value); | |||
this.obj.syncer.setObject(true, 'stats', 'values', 'critChance', this.values.critChance); | |||
} else if (stat == 'addCritMultiplier') { | |||
this.values.critMultiplier += value; | |||
this.obj.syncer.setObject(true, 'stats', 'values', 'critMultiplier', this.values.critMultiplier); | |||
} else if (stat == 'vit') { | |||
this.values.hpMax += (value * this.vitScale); | |||
this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', this.values.hpMax); | |||
@@ -237,7 +247,7 @@ define([ | |||
mult = (1 + (partySize * 0.1)); | |||
} | |||
if (a.obj.stats) { | |||
if ((a.obj.stats) && (!a.obj.follower)) { | |||
//Scale xp by source level so you can't just farm low level mobs (or get boosted on high level mobs). | |||
//Mobs that are farther then 10 levels from you, give no xp | |||
//We don't currently do this for quests/herb gathering | |||
@@ -296,12 +306,16 @@ define([ | |||
amount = this.values.hp; | |||
this.values.hp -= amount; | |||
var recipients = []; | |||
if (this.obj.serverId != null) | |||
recipients.push(this.obj.serverId); | |||
if (source.serverId != null) | |||
recipients.push(source.serverId); | |||
if ((source.follower) && (source.follower.master.serverId)) | |||
recipients.push(source.follower.master.serverId); | |||
if ((this.obj.follower) && (this.obj.follower.master.serverId)) | |||
recipients.push(this.obj.follower.master.serverId); | |||
if (recipients.length > 0) { | |||
this.syncer.queue('onGetDamage', { | |||
id: this.obj.id, | |||
@@ -325,11 +339,12 @@ define([ | |||
var deathEvent = {}; | |||
var killSource = source; | |||
if (source.follower) | |||
killSource = source.follower.master; | |||
if (source.player) | |||
source.stats.kill(this.obj); | |||
if (killSource.player) | |||
killSource.stats.kill(this.obj); | |||
else | |||
this.obj.fireEvent('afterDeath', deathEvent); | |||
@@ -339,18 +354,27 @@ define([ | |||
this.obj.auth.permadie(); | |||
this.syncer.queue('onPermadeath', { | |||
source: source.name | |||
source: killSource.name | |||
}, [this.obj.serverId]); | |||
} else | |||
this.values.hp = 0; | |||
this.obj.player.die(source, deathEvent.permadeath); | |||
this.obj.player.die(killSource, deathEvent.permadeath); | |||
} else { | |||
this.obj.effects.die(); | |||
if (this.obj.spellbook) | |||
this.obj.spellbook.die(); | |||
this.obj.destroyed = true; | |||
var deathAnimation = _.getDeepProperty(animations, ['mobs', this.obj.sheetName, this.obj.cell, 'death']); | |||
if (deathAnimation) { | |||
this.obj.instance.syncer.queue('onGetObject', { | |||
x: this.obj.x, | |||
y: this.obj.y, | |||
components: [deathAnimation] | |||
}); | |||
} | |||
if (this.obj.inventory) { | |||
var aggroList = this.obj.aggro.list; | |||
var aLen = aggroList.length; | |||
@@ -365,14 +389,14 @@ define([ | |||
if (done.some(d => d == p)) | |||
return; | |||
this.obj.inventory.dropBag(p, source); | |||
this.obj.inventory.dropBag(p, killSource); | |||
done.push(p); | |||
}, this); | |||
} else { | |||
if (a.serverId == null) | |||
continue; | |||
this.obj.inventory.dropBag(a.serverId, source); | |||
this.obj.inventory.dropBag(a.serverId, killSource); | |||
done.push(a.serverId); | |||
} | |||
} | |||
@@ -388,13 +412,16 @@ define([ | |||
}, | |||
getHp: function(heal, source) { | |||
var amount = heal.amount; | |||
if (amount == 0) | |||
return; | |||
var values = this.values; | |||
var hpMax = values.hpMax; | |||
if (values.hp >= hpMax) | |||
return; | |||
var amount = heal.amount; | |||
if (hpMax - values.hp < amount) | |||
amount = hpMax - values.hp; | |||
@@ -1,9 +1,9 @@ | |||
define([ | |||
'misc/events' | |||
], function( | |||
events | |||
) { | |||
return { | |||
var animations = { | |||
classes: { | |||
wizard: { | |||
'2': { | |||
@@ -312,4 +312,8 @@ define([ | |||
} | |||
} | |||
}; | |||
events.emit('onBeforeGetAnimations', animations); | |||
return animations; | |||
}); |
@@ -4,8 +4,26 @@ define([ | |||
events | |||
) { | |||
var classes = { | |||
portraits: { | |||
warrior: { | |||
x: 0, | |||
y: 0 | |||
}, | |||
cleric: { | |||
x: 1, | |||
y: 0 | |||
}, | |||
wizard: { | |||
x: 2, | |||
y: 0 | |||
}, | |||
thief: { | |||
x: 3, | |||
y: 0 | |||
} | |||
}, | |||
spells: { | |||
wizard: ['ice spear'], | |||
wizard: ['ice spear', 'fireblast'], | |||
cleric: ['healing circle'], | |||
warrior: ['charge'], | |||
thief: ['smokebomb'] | |||
@@ -0,0 +1,15 @@ | |||
define([ | |||
], function( | |||
) { | |||
return { | |||
type: 'frenzy', | |||
events: { | |||
beforeSetSpellCooldown: function(msg, spell) { | |||
msg.cd = 0; | |||
} | |||
} | |||
}; | |||
}); |
@@ -0,0 +1,99 @@ | |||
define([ | |||
'world/spawners', | |||
'world/mobBuilder', | |||
'combat/combat' | |||
], function( | |||
spawners, | |||
mobBuilder, | |||
combat | |||
) { | |||
return { | |||
id: 'akarei', | |||
name: 'The Akarei', | |||
description: `The last descendents of the ancient Akarei.`, | |||
uniqueStat: { | |||
damage: 1, | |||
chance: { | |||
min: 20, | |||
max: 45 | |||
}, | |||
generate: function(item) { | |||
var chance = this.chance; | |||
var chanceRoll = ~~(random.norm(chance.min, chance.max) * 10) / 10; | |||
var result = null; | |||
if (item.effects) | |||
result = item.effects.find(e => (e.factionId == 'akarei')); | |||
if (!result) { | |||
if (!item.effects) | |||
item.effects = []; | |||
result = { | |||
factionId: 'akarei', | |||
chance: chanceRoll, | |||
text: chanceRoll + '% chance on to cast a lightning bolt when you critically hit an enemy', | |||
events: {} | |||
}; | |||
item.effects.push(result); | |||
} | |||
if (!result.events) | |||
result.events = {}; | |||
for (var e in this.events) { | |||
result.events[e] = this.events[e]; | |||
} | |||
return result; | |||
}, | |||
events: { | |||
beforeDealDamage: function(item, damage, target) { | |||
if (!damage.crit) | |||
return; | |||
/*var effect = item.effects.find(e => (e.factionId == 'akarei')); | |||
var roll = Math.random() * 100; | |||
if (roll >= effect.chance) | |||
return;*/ | |||
var cbExplode = function(target) { | |||
if ((this.destroyed) || (target.destroyed)) | |||
return; | |||
var damage = combat.getDamage({ | |||
source: this, | |||
target: target, | |||
damage: 1, | |||
element: 'arcane', | |||
noCrit: true | |||
}); | |||
target.stats.takeDamage(damage, 1, this); | |||
}; | |||
this.instance.syncer.queue('onGetObject', { | |||
id: this.id, | |||
components: [{ | |||
type: 'lightningEffect', | |||
toX: target.x, | |||
toY: target.y | |||
}] | |||
}); | |||
this.spellbook.registerCallback(this.id, cbExplode.bind(this, target), 1); | |||
} | |||
} | |||
}, | |||
rewards: { | |||
} | |||
}; | |||
}); |
@@ -71,7 +71,9 @@ define([ | |||
sheetName: 'mobs', | |||
name: 'Squiggle', | |||
properties: { | |||
cpnFollower: {} | |||
cpnFollower: { | |||
lifetime: 100 | |||
} | |||
}, | |||
extraProperties: { | |||
follower: { | |||
@@ -0,0 +1,27 @@ | |||
module.exports = { | |||
'cultist biorn': [{ | |||
msg: 'That pool sure looks cold' | |||
}, { | |||
msg: '*sighs*' | |||
}, { | |||
msg: 'Come on, think!' | |||
}, { | |||
msg: 'How will we ever get across?' | |||
}], | |||
'cultist veleif': [{ | |||
msg: `How about we try and jump?` | |||
}, { | |||
msg: '*scratches his head*' | |||
}, { | |||
msg: 'Thorald will be expecting us soon' | |||
}, { | |||
msg: 'This is useless' | |||
}], | |||
'cultist': [{ | |||
msg: '*pockets a small crystal*' | |||
}, { | |||
msg: '*sighs*' | |||
}, { | |||
msg: '*stares into a crystal*' | |||
}] | |||
}; |
@@ -0,0 +1,236 @@ | |||
module.exports = { | |||
'thaumaturge yala': { | |||
'1': { | |||
msg: [{ | |||
msg: `Yes?`, | |||
options: [1.1, 1.2, 1.3, 1.4, 1.5, 1.6] | |||
}], | |||
options: { | |||
'1.1': { | |||
msg: `Who are you?`, | |||
goto: '2' | |||
}, | |||
'1.2': { | |||
msg: `Where did you come from?`, | |||
goto: '5' | |||
}, | |||
'1.3': { | |||
msg: `Do you have any items for sale?`, | |||
goto: 'tradeBuy' | |||
}, | |||
'1.4': { | |||
msg: `I have some items to sell.`, | |||
goto: 'tradeSell' | |||
}, | |||
'1.5': { | |||
msg: `I want to buy something back.`, | |||
goto: 'tradeBuyback' | |||
}, | |||
'1.6': { | |||
msg: `I have some crystals for you.`, | |||
prereq: function(obj) { | |||
var crystals = obj.inventory.items.find(i => (i.name == 'Digested Crystal')); | |||
return !!crystals; | |||
}, | |||
goto: 'giveCrystals' | |||
} | |||
} | |||
}, | |||
'2': { | |||
msg: [{ | |||
msg: `I am Thaumaturge Yala, thirty-fourth in line to the throne of the Akarei.`, | |||
options: [2.1] | |||
}], | |||
options: { | |||
'2.1': { | |||
msg: `Who are the Akarei?`, | |||
goto: '3' | |||
} | |||
} | |||
}, | |||
'3': { | |||
msg: [{ | |||
msg: `The Akarei are both the first, and the last wielders of true magic.`, | |||
options: [3.1] | |||
}], | |||
options: { | |||
'3.1': { | |||
msg: `True magic?`, | |||
goto: '4' | |||
} | |||
} | |||
}, | |||
'4': { | |||
msg: [{ | |||
msg: `Others toil away in an effort to bend magic to their wills; tainting it. We, the Akarei, perform only the purest of incantations.`, | |||
options: [4.1] | |||
}], | |||
options: { | |||
'4.1': { | |||
msg: `I would like to ask something else.`, | |||
goto: '1' | |||
} | |||
} | |||
}, | |||
'5': { | |||
msg: [{ | |||
msg: `We hail from the city Iskar; our home and haven.`, | |||
options: [5.1, 5.2] | |||
}], | |||
options: { | |||
'5.1': { | |||
msg: `How did you get here?`, | |||
goto: '6' | |||
}, | |||
'5.2': { | |||
msg: `What are you doing here?`, | |||
goto: '10' | |||
} | |||
} | |||
}, | |||
'6': { | |||
msg: [{ | |||
msg: `Through a portal, for Iskar lies in another dimension. But now, we are trapped here. The portal has been closed from the other side and we lack the power to repopen it from here.`, | |||
options: [6.1] | |||
}], | |||
options: { | |||
'6.1': { | |||
msg: `Why would the portal have been closed?`, | |||
goto: '7' | |||
} | |||
} | |||
}, | |||
'7': { | |||
msg: [{ | |||
msg: `I can not say for certain, but the Akarei have never been wanting for enemies.`, | |||
options: [7.1] | |||
}], | |||
options: { | |||
'7.1': { | |||
msg: `A foe?`, | |||
goto: '8' | |||
} | |||
} | |||
}, | |||
'8': { | |||
msg: [{ | |||
msg: `A darkness. A nameless, faceless enemy that we can neither identify, nor combat. It has been chipping away at our defenses...almost consuming them. I fear if we do not return soon, it will be too late.`, | |||
options: [8.1] | |||
}], | |||
options: { | |||
'8.1': { | |||
msg: `How can I help?`, | |||
goto: '9' | |||
} | |||
} | |||
}, | |||
'9': { | |||
msg: [{ | |||
msg: `Slay the snails, gather crystals and bring them to me. The Akarei may reward those who assist them.`, | |||
options: [9.1] | |||
}], | |||
options: { | |||
'9.1': { | |||
msg: `I would like to ask something else.`, | |||
goto: '1' | |||
} | |||
} | |||
}, | |||
'10': { | |||
msg: [{ | |||
msg: `We were sent here by the Iskar Council, to seek a new form of energy.`, | |||
options: [10.1, 10.2] | |||
}], | |||
options: { | |||
'10.1': { | |||
msg: `How will the energy be used?`, | |||
goto: '11' | |||
}, | |||
'10.2': { | |||
msg: `And, did you find it?`, | |||
goto: '12' | |||
} | |||
} | |||
}, | |||
'11': { | |||
msg: [{ | |||
msg: `Iskar is protected by a magic dome, created by the Founders; a bulwark of pure energy that has stood the test of all but our newest foe.`, | |||
options: [7.1] | |||
}], | |||
options: { | |||
} | |||
}, | |||
'12': { | |||
msg: [{ | |||
msg: `In a manner. The crystals in this cave are highly energized but in their natural form, they are too unstable for use.`, | |||
options: [12.1] | |||
}], | |||
options: { | |||
'12.1': { | |||
msg: `So, your mission failed?`, | |||
goto: '13' | |||
} | |||
} | |||
}, | |||
'13': { | |||
msg: [{ | |||
msg: `Not so. Curiously, the snails that inhabit this cave seem to have quite a taste for the crystals' unstable energy and once passed through their digestive tracts, it is expelled in a stable, usable form.`, | |||
options: [13.1] | |||
}], | |||
options: { | |||
'13.1': { | |||
msg: `Well...that's disgusting.`, | |||
goto: '14' | |||
} | |||
} | |||
}, | |||
'14': { | |||
msg: [{ | |||
msg: `Perhaps. But it may prove to be our salvation. For now we have double need of the energy. Our portal was closed from the other side and we don't have sufficient power between us to repopen it from here.`, | |||
options: [14.1, 6.1] | |||
}], | |||
options: { | |||
'14.1': { | |||
msg: `How can I help?`, | |||
goto: '9' | |||
} | |||
} | |||
}, | |||
tradeBuy: { | |||
cpn: 'trade', | |||
method: 'startBuy', | |||
args: [{ | |||
targetName: 'cult leader' | |||
}] | |||
}, | |||
tradeSell: { | |||
cpn: 'trade', | |||
method: 'startSell', | |||
args: [{ | |||
targetName: 'cult leader' | |||
}] | |||
}, | |||
tradeBuyback: { | |||
cpn: 'trade', | |||
method: 'startBuyback', | |||
args: [{ | |||
targetName: 'cult leader' | |||
}] | |||
}, | |||
giveCrystals: { | |||
msg: [{ | |||
msg: `The Akarei thank you.`, | |||
options: [1.1, 1.2, 1.3, 1.4, 1.5] | |||
}], | |||
method: function(obj) { | |||
var inventory = obj.inventory; | |||
var crystals = inventory.items.find(i => (i.name == 'Digested Crystal')); | |||
obj.reputation.getReputation('akarei', crystals.quantity * 15); | |||
inventory.destroyItem(crystals.id); | |||
} | |||
} | |||
} | |||
}; |
@@ -0,0 +1,9 @@ | |||
define([ | |||
], function( | |||
) { | |||
return { | |||
infini: [] | |||
}; | |||
}); |
@@ -0,0 +1,668 @@ | |||
module.exports = { | |||
name: 'cave', | |||
level: 20, | |||
addLevel: 0, | |||
resources: {}, | |||
mobs: { | |||
default: { | |||
regular: { | |||
drops: { | |||
chance: 35, | |||
rolls: 1 | |||
} | |||
} | |||
}, | |||
'crystal snail': { | |||
level: 14, | |||
regular: { | |||
drops: { | |||
chance: 30, | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
name: 'Digested Crystal', | |||
quality: 0, | |||
quest: true, | |||
sprite: [1, 1] | |||
}] | |||
} | |||
}, | |||
spells: [{ | |||
type: 'melee' | |||
}, { | |||
type: 'smokeBomb', | |||
radius: 0, | |||
repeat: 5, | |||
duration: 7, | |||
randomPos: true, | |||
range: 2, | |||
selfCast: 0.2, | |||
statMult: 1, | |||
damage: 0.225, | |||
element: 'arcane', | |||
cdMax: 5, | |||
particles: { | |||
scale: { | |||
start: { | |||
min: 10, | |||
max: 25 | |||
}, | |||
end: { | |||
min: 10, | |||
max: 0 | |||
} | |||
}, | |||
opacity: { | |||
start: 0.3, | |||
end: 0 | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 2 | |||
}, | |||
speed: { | |||
start: 3, | |||
end: 0 | |||
}, | |||
color: { | |||
start: ['fc66f7', 'a24eff'], | |||
end: ['933159', '393268'] | |||
}, | |||
chance: 0.125, | |||
randomColor: true, | |||
randomScale: true, | |||
blendMode: 'add', | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -10, | |||
y: -10, | |||
w: 20, | |||
h: 20 | |||
} | |||
} | |||
}] | |||
}, | |||
'crystal whelk': { | |||
level: 16, | |||
spells: [{ | |||
type: 'melee' | |||
}], | |||
regular: { | |||
drops: { | |||
chance: 30, | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
name: 'Digested Crystal', | |||
quality: 0, | |||
quest: true, | |||
sprite: [1, 1] | |||
}] | |||
} | |||
}, | |||
spells: [{ | |||
type: 'melee' | |||
}, { | |||
type: 'smokeBomb', | |||
radius: 0, | |||
repeat: 5, | |||
duration: 7, | |||
randomPos: true, | |||
range: 2, | |||
selfCast: 0.2, | |||
statMult: 1, | |||
damage: 0.2, | |||
element: 'arcane', | |||
cdMax: 5, | |||
particles: { | |||
scale: { | |||
start: { | |||
min: 4, | |||
max: 14 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
opacity: { | |||
start: 0.2, | |||
end: 0 | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 2 | |||
}, | |||
speed: { | |||
start: 2, | |||
end: 0 | |||
}, | |||
color: { | |||
start: ['ff6942', 'ffeb38'], | |||
end: ['953f36', '9a5a3c'] | |||
}, | |||
chance: 0.125, | |||
randomColor: true, | |||
randomScale: true, | |||
blendMode: 'add', | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -10, | |||
y: -10, | |||
w: 20, | |||
h: 20 | |||
} | |||
} | |||
}] | |||
}, | |||
'radulos': { | |||
level: 18, | |||
regular: { | |||
hpMult: 75, | |||
dmgMult: 2, | |||
drops: { | |||
chance: 100, | |||
rolls: 5, | |||
magicFind: [2000, 200] | |||
} | |||
}, | |||
rare: { | |||
count: 0 | |||
}, | |||
mobile: false, | |||
spells: [{ | |||
type: 'projectile', | |||
particles: { | |||
scale: { | |||
start: { | |||
min: 6, | |||
max: 18 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
color: { | |||
start: ['fc66f7', 'a24eff'], | |||
end: ['393268', '933159'] | |||
}, | |||
chance: 0.65, | |||
randomScale: true, | |||
randomColor: true, | |||
} | |||
}, { | |||
type: 'smokeBomb', | |||
radius: 1, | |||
repeat: 4, | |||
duration: 14, | |||
randomPos: true, | |||
range: 6, | |||
selfCast: 0.25, | |||
statMult: 1, | |||
damage: 0.15, | |||
element: 'arcane', | |||
cdMax: 8, | |||
particles: { | |||
scale: { | |||
start: { | |||
min: 6, | |||
max: 18 | |||
}, | |||
end: { | |||
min: 4, | |||
max: 10 | |||
} | |||
}, | |||
opacity: { | |||
start: 0.01, | |||
end: 0 | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 3 | |||
}, | |||
speed: { | |||
start: 2, | |||
end: 0 | |||
}, | |||
color: { | |||
start: ['ff4252', 'd43346'], | |||
end: ['802343', 'a82841'] | |||
}, | |||
chance: 0.125, | |||
randomColor: true, | |||
randomScale: true, | |||
blendMode: 'add', | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -10, | |||
y: -10, | |||
w: 20, | |||
h: 20 | |||
} | |||
} | |||
}, { | |||
type: 'summonConsumableFollower' | |||
}] | |||
}, | |||
'akarei scout': { | |||
level: 20, | |||
faction: 'akarei', | |||
deathRep: -3 | |||
}, | |||
'biorn': { | |||
level: 22, | |||
walkDistance: 0, | |||
faction: 'akarei', | |||
deathRep: -3 | |||
}, | |||
'veleif': { | |||
level: 22, | |||
walkDistance: 0, | |||
faction: 'akarei', | |||
deathRep: -3 | |||
}, | |||
'akarei artificer': { | |||
level: 24, | |||
faction: 'akarei', | |||
deathRep: -6 | |||
}, | |||
'thaumaturge yala': { | |||
level: 30, | |||
walkDistance: 0, | |||
deathRep: -15, | |||
regular: { | |||
hpMult: 100, | |||
dmgMult: 2 | |||
}, | |||
rare: { | |||
count: 0 | |||
}, | |||
properties: { | |||
cpnTrade: { | |||
items: { | |||
min: 5, | |||
max: 10, | |||
extra: [] | |||
}, | |||
faction: { | |||
id: 'akarei', | |||
tier: 5 | |||
}, | |||
markup: { | |||
buy: 0.25, | |||
sell: 10 | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
objects: { | |||
redwall: { | |||
components: { | |||
cpnBlocker: { | |||
init: function() { | |||
this.obj.instance.physics.setCollision(this.obj.x, this.obj.y, true); | |||
} | |||
} | |||
} | |||
}, | |||
bigportal: { | |||
components: { | |||
cpnAttackAnimation: { | |||
simplify: function() { | |||
return { | |||
type: 'attackAnimation', | |||
spriteSheet: 'animBigObjects', | |||
row: 1, | |||
col: 0, | |||
frames: 6, | |||
frameDelay: 7, | |||
loop: -1, | |||
hideSprite: true | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
pinktile: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function() { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['fc66f7', 'a24eff'], | |||
end: ['933159', '393268'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.04, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -20, | |||
y: -20, | |||
w: 60, | |||
h: 60 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
walltrigger: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function() { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['fff4252', 'ff6942'], | |||
end: ['802343', 'f953f36'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 6 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 0, | |||
max: 4 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 0 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 2 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -20, | |||
y: -20, | |||
w: 40, | |||
h: 40 | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
cpnTrigger: { | |||
init: function() { | |||
this.obj.instance.triggerPuzzle = { | |||
activated: [] | |||
}; | |||
}, | |||
collisionEnter: function(o) { | |||
if (!o.player) | |||
return; | |||
var order = this.obj.order; | |||
var triggerPuzzle = this.obj.instance.triggerPuzzle; | |||
var activated = triggerPuzzle.activated; | |||
if (this.obj.forceOpen) { | |||
triggerPuzzle.activated = []; | |||
this.activate(); | |||
return; | |||
} | |||
activated.push(order); | |||
var valid = true; | |||
for (var i = 0; i < activated.length; i++) { | |||
if (activated[i] != i) { | |||
valid = false; | |||
break; | |||
} | |||
} | |||
if (!valid) { | |||
triggerPuzzle.activated = []; | |||
process.send({ | |||
method: 'events', | |||
data: { | |||
'onGetAnnouncement': [{ | |||
obj: { | |||
msg: 'nothing happens' | |||
}, | |||
to: [o.serverId] | |||
}] | |||
} | |||
}); | |||
return; | |||
} else if (activated.length == 4) { | |||
triggerPuzzle.activated = []; | |||
this.activate(); | |||
} | |||
process.send({ | |||
method: 'events', | |||
data: { | |||
'onGetAnnouncement': [{ | |||
obj: { | |||
msg: this.obj.message | |||
}, | |||
to: [o.serverId] | |||
}] | |||
} | |||
}); | |||
}, | |||
activate: function() { | |||
var syncer = this.obj.instance.syncer; | |||
var physics = this.obj.instance.physics; | |||
var walls = this.obj.instance.objects.objects.filter(o => (o.objZoneName == 'redWall')); | |||
walls.forEach(function(w) { | |||
w.destroyed = true; | |||
physics.setCollision(w.x, w.y, false); | |||
syncer.queue('onGetObject', { | |||
x: w.x, | |||
y: w.y, | |||
components: [{ | |||
type: 'attackAnimation', | |||
row: 0, | |||
col: 4 | |||
}] | |||
}); | |||
}); | |||
} | |||
} | |||
} | |||
}, | |||
gas: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function() { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['c0c3cf', '929398'], | |||
end: ['69696e', '69696e'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 32, | |||
max: 18 | |||
}, | |||
end: { | |||
min: 16, | |||
max: 8 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 2, | |||
max: 6 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 4 | |||
} | |||
}, | |||
lifetime: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
alpha: { | |||
start: 0.2, | |||
end: 0 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.02, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
blendMode: 'screen', | |||
spawnRect: { | |||
x: -80, | |||
y: -80, | |||
w: 160, | |||
h: 160 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
bubbles: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function() { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['48edff', '3fa7dd'], | |||
end: ['69696e', '42548d'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 8 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 4 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 2, | |||
max: 6 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 4 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 3 | |||
}, | |||
alpha: { | |||
start: 0.5, | |||
end: 0 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
blendMode: 'screen', | |||
spawnRect: { | |||
x: -20, | |||
y: -20, | |||
w: 60, | |||
h: 60 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
shopyala: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'thaumaturge yala' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}; |
@@ -11,14 +11,15 @@ module.exports = { | |||
}, | |||
regular: { | |||
dmgMult: 8, | |||
hpMult: 1.5, | |||
dmgMult: 2, | |||
drops: { | |||
chance: 45, | |||
rolls: 1, | |||
magicFind: 70 | |||
magicFind: 500 | |||
} | |||
} | |||
} | |||
}, | |||
'giant gull': { | |||
level: 6, | |||
@@ -4,9 +4,10 @@ define([ | |||
) { | |||
return [ | |||
'tutorial', | |||
'estuary', | |||
'cave', | |||
'city', | |||
'sewer' | |||
'estuary', | |||
'sewer', | |||
'tutorial' | |||
]; | |||
}); |
@@ -13,7 +13,7 @@ module.exports = { | |||
grantRep: { | |||
fjolgard: 4 | |||
}, | |||
level: 8, | |||
level: 10, | |||
rare: { | |||
count: 0 | |||
@@ -25,7 +25,7 @@ module.exports = { | |||
grantRep: { | |||
fjolgard: 10 | |||
}, | |||
level: 11, | |||
level: 12, | |||
rare: { | |||
count: 0 | |||
@@ -1,27 +0,0 @@ | |||
module.exports = { | |||
mobs: { | |||
seagull: { | |||
level: 1, | |||
rare: { | |||
count: 0 | |||
}, | |||
regular: { | |||
drops: { | |||
chance: 100, | |||
rolls: 1, | |||
noRandom: true, | |||
blueprints: [{ | |||
name: 'Family Heirloom', | |||
quality: 2, | |||
slot: 'neck', | |||
type: 'Pendant', | |||
noSalvage: true, | |||
stats: ['hpMax', 'regenHp', 'regenMana'] | |||
}] | |||
} | |||
} | |||
} | |||
} | |||
}; |
@@ -153,7 +153,7 @@ module.exports = { | |||
} | |||
}, | |||
elk: { | |||
level: 4, | |||
level: 5, | |||
regular: { | |||
drops: { | |||
chance: 55, | |||
@@ -171,7 +171,7 @@ module.exports = { | |||
crab: { | |||
faction: 'gaekatla', | |||
deathRep: -3, | |||
level: 5, | |||
level: 7, | |||
regular: { | |||
drops: { | |||
@@ -190,7 +190,7 @@ module.exports = { | |||
'titan crab': { | |||
faction: 'gaekatla', | |||
deathRep: -5, | |||
level: 6, | |||
level: 8, | |||
rare: { | |||
name: 'The Pincer King' | |||
} | |||
@@ -5,7 +5,7 @@ define([ | |||
) { | |||
return { | |||
accounts: { | |||
admin: { | |||
waffle: { | |||
level: 10, | |||
messageStyle: 'color-cyan', | |||
messagePrefix: '(dev) ', | |||
@@ -45,6 +45,13 @@ define([ | |||
} | |||
}, | |||
getRoleLevel: function(player) { | |||
var account = player.account; | |||
var level = this.accounts[account] ? this.accounts[account].level : 0; | |||
return level; | |||
}, | |||
isRoleLevel: function(player, requireLevel, message) { | |||
var account = player.account; | |||
var level = this.accounts[account] ? this.accounts[account].level : 0; | |||
@@ -0,0 +1,10 @@ | |||
define([ | |||
], function( | |||
) { | |||
return { | |||
port: 4000, | |||
startupMessage: 'Server: ready' | |||
} | |||
}); |
@@ -290,7 +290,7 @@ define([ | |||
description: 'Jagged Crystals break ground at your target destination', | |||
type: 'warnBlast', | |||
animation: 'raiseHands', | |||
icon: [4, 0], | |||
icon: [0, 7], | |||
particles: { | |||
color: { | |||
start: ['c0c3cf', '929398'], | |||
@@ -332,55 +332,11 @@ define([ | |||
randomColor: true | |||
} | |||
}, { | |||
name: 'Arcane Barrier', | |||
name: 'Chain Lightning', | |||
description: 'Creates a circle of pure holy energy that heals allies for a brief period.', | |||
type: 'arcaneBarrier', | |||
type: 'chainLightning', | |||
icon: [0, 1], | |||
animation: 'raiseStaff', | |||
particles: { | |||
scale: { | |||
start: { | |||
min: 6, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 4 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 2, | |||
max: 12 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 4 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 3 | |||
}, | |||
alpha: { | |||
start: 0.45, | |||
end: 0 | |||
}, | |||
color: { | |||
start: ['ffeb38', 'fcfcfc'], | |||
end: ['fcfcfc', 'faac45'] | |||
}, | |||
spawnType: 'circle', | |||
spawnCircle: { | |||
x: 0, | |||
y: 0, | |||
r: 12 | |||
}, | |||
randomScale: true, | |||
randomColor: true, | |||
randomSpeed: true, | |||
chance: 0.02 | |||
} | |||
animation: 'raiseStaff' | |||
}]; | |||
events.emit('onBeforeGetSpellsInfo', spells); | |||
@@ -21,7 +21,7 @@ define([ | |||
var isPlayer = !!this.caster.player; | |||
var isTargetPlayer = !!o.player; | |||
if ((this.caster.aggro.willAttack(o)) || (isPlayer != isTargetPlayer)) | |||
if ((this.caster.aggro.canAttack(o)) || (isPlayer != isTargetPlayer)) | |||
return; | |||
this.contents.push(o); | |||
@@ -0,0 +1,43 @@ | |||
define([ | |||
], function( | |||
) { | |||
return { | |||
type: 'chainLightning', | |||
cdMax: 5, | |||
manaCost: 0, | |||
range: 9, | |||
needLos: true, | |||
damage: 1, | |||
cast: function(action) { | |||
var target = action.target; | |||
this.sendBump(target); | |||
this.obj.instance.syncer.queue('onGetObject', { | |||
id: this.obj.id, | |||
components: [{ | |||
type: 'lightningEffect', | |||
toX: target.x, | |||
toY: target.y | |||
}] | |||
}); | |||
this.queueCallback(this.explode.bind(this, target), 1); | |||
return true; | |||
}, | |||
explode: function(target) { | |||
if ((this.obj.destroyed) || (target.destroyed)) | |||
return; | |||
var damage = this.getDamage(target); | |||
target.stats.takeDamage(damage, this.threatMult, this.obj); | |||
} | |||
}; | |||
}); |
@@ -56,7 +56,7 @@ define([ | |||
var isPlayer = !!this.obj.player; | |||
var isTargetPlayer = !!m.player; | |||
if ((!this.obj.aggro.willAttack(m)) && (isPlayer == isTargetPlayer)) | |||
if ((!this.obj.aggro.canAttack(m)) && (isPlayer == isTargetPlayer)) | |||
continue; | |||
var targetEffect = m.effects.addEffect({ | |||