@@ -27,6 +27,7 @@ define([ | |||
lastY: 0, | |||
init: function () { | |||
events.on('onRespawn', this.onDeath.bind(this)); | |||
events.on('onDeath', this.onDeath.bind(this)); | |||
events.on('onClearQueue', this.onDeath.bind(this)); | |||
@@ -68,8 +68,9 @@ define([ | |||
}, instant); | |||
}, | |||
onRespawn: function (position) { | |||
this.positionCamera(position.x, position.y, true); | |||
onRespawn: function ({ x, y }) { | |||
this.positionCamera(x, y, true); | |||
sound.update(x, y); | |||
}, | |||
destroy: function () { | |||
@@ -126,7 +126,7 @@ define([ | |||
}, | |||
buildSpritesTexture: function () { | |||
const { clientConfig: { atlasTextures } } = globals; | |||
const { clientConfig: { atlasTextureDimensions, atlasTextures } } = globals; | |||
let container = new pixi.Container(); | |||
@@ -139,7 +139,7 @@ define([ | |||
tile.x = 0; | |||
tile.y = totalHeight; | |||
tileOpacity.atlasTextureDimensions[t] = { | |||
atlasTextureDimensions[t] = { | |||
w: texture.width / 8, | |||
h: texture.height / 8 | |||
}; | |||
@@ -374,7 +374,7 @@ define([ | |||
let foundHiddenLayer = null; | |||
hiddenRooms.forEach(h => { | |||
const { discovered, layer } = h; | |||
const { discovered, layer, interior } = h; | |||
const { x: hx, y: hy, width, height, area } = h; | |||
//Is the tile outside the hider | |||
@@ -383,8 +383,15 @@ define([ | |||
x >= hx + width || | |||
y < hy || | |||
y >= hy + height | |||
) | |||
) { | |||
//If the hider is an interior, the tile should be hidden if the player is inside the hider | |||
if (interior) { | |||
if (physics.isInPolygon(px, py, area)) | |||
foundHiddenLayer = layer; | |||
} | |||
return; | |||
} | |||
//Is the tile inside the hider | |||
if (!physics.isInPolygon(x, y, area)) | |||
@@ -4,9 +4,6 @@ define([ | |||
globals | |||
) { | |||
return { | |||
//Set by renderer | |||
atlasTextureDimensions: {}, | |||
getSheetNum: function (tile) { | |||
if (tile < 224) | |||
return 0; | |||
@@ -25,8 +22,7 @@ define([ | |||
}, | |||
getOffsetAndSheet: function (tile) { | |||
const { clientConfig: { atlasTextures } } = globals; | |||
const { atlasTextureDimensions } = this; | |||
const { clientConfig: { atlasTextureDimensions, atlasTextures } } = globals; | |||
let offset = 0; | |||
let sheetName = null; | |||
@@ -125,8 +125,6 @@ define([ | |||
if (this.currentMusic === soundEntry && sound.volume() === musicVolume / 100) | |||
return; | |||
soundEntry.volume = 1; | |||
this.currentMusic = soundEntry; | |||
sound.fade(sound.volume(), (musicVolume / 100), fadeDuration); | |||
@@ -169,7 +167,7 @@ define([ | |||
} | |||
//Exponential fall-off | |||
const volume = s.volume * (1 - (Math.pow(distance, 2) / Math.pow(minDistance, 2))); | |||
const volume = s.maxVolume * (1 - (Math.pow(distance, 2) / Math.pow(minDistance, 2))); | |||
this.playSoundHelper(s, volume); | |||
}); | |||
}, | |||
@@ -178,23 +176,31 @@ define([ | |||
const sounds = this.sounds; | |||
const areaMusic = sounds.filter(s => s.music && s.area); | |||
const currentMusic = areaMusic.find(s => physics.isInPolygon(playerX, playerY, s.area)); | |||
const defaultMusic = sounds.find(s => s.music && s.defaultMusic); | |||
if (!currentMusic) { | |||
if (defaultMusic) | |||
this.playMusicHelper(defaultMusic); | |||
//All music that should be playing because we're in the correct polygon | |||
const playMusic = areaMusic.filter(s => physics.isInPolygon(playerX, playerY, s.area)); | |||
const activeMusic = sounds.filter(s => s.music && s !== defaultMusic); | |||
activeMusic.forEach(s => this.stopSoundHelper(s)); | |||
} else { | |||
if (defaultMusic) | |||
this.stopSoundHelper(defaultMusic); | |||
//All music that should stop playing because we're in the incorrect polygon | |||
const stopMusic = areaMusic.filter(s => s.sound && s.sound.playing() && !playMusic.some(m => m === s)); | |||
if (currentMusic) | |||
this.playMusicHelper(currentMusic); | |||
//Stop or start defaultMusic, depending on whether anything else was found | |||
const defaultMusic = sounds.filter(a => a.defaultMusic); | |||
if (defaultMusic) { | |||
if (!playMusic.length) | |||
defaultMusic.forEach(m => this.playMusicHelper(m)); | |||
else | |||
defaultMusic.forEach(m => this.stopSoundHelper(m)); | |||
} | |||
//If there's a music entry in both 'play' and 'stop' that shares a fileName, we'll just ignore it. This happens when you | |||
// move to a building interior, for example. Unfortunately, we can't have different volume settings for these kinds of entries. | |||
// The one that starts playing first will get priority | |||
const filesPlaying = [...playMusic.map(p => p.file), ...stopMusic.map(p => p.file)]; | |||
playMusic.spliceWhere(p => filesPlaying.filter(f => f === p.file).length > 1); | |||
stopMusic.spliceWhere(p => filesPlaying.filter(f => f === p.file).length > 1); | |||
stopMusic.forEach(m => this.stopSoundHelper(m)); | |||
playMusic.forEach(m => this.playMusicHelper(m)); | |||
}, | |||
update: function (playerX, playerY) { | |||
@@ -230,6 +236,7 @@ define([ | |||
x, | |||
y, | |||
volume, | |||
maxVolume: volume, | |||
area, | |||
music, | |||
defaultMusic | |||
@@ -206,4 +206,4 @@ | |||
.mobile .uiParty .list { | |||
max-height: calc(100% - @topPosition - 10px); | |||
} | |||
} |
@@ -129,6 +129,8 @@ module.exports = { | |||
const message = `The ${key.name} disintegrates on use`; | |||
obj.social.notifySelf({ message }); | |||
} | |||
this.obj.instance.syncer.queue('onDoorUnlock', null, [obj.serverId]); | |||
} | |||
if (this.closed) { | |||
@@ -139,6 +141,8 @@ module.exports = { | |||
this.closed = false; | |||
this.enterArea(obj); | |||
this.obj.instance.syncer.queue('onDoorOpen', null, [obj.serverId]); | |||
} else { | |||
thisObj.cell = this.closedSprite; | |||
syncO.cell = this.closedSprite; | |||
@@ -147,6 +151,8 @@ module.exports = { | |||
this.closed = true; | |||
this.enterArea(obj); | |||
this.obj.instance.syncer.queue('onDoorClose', null, [obj.serverId]); | |||
} | |||
}, | |||
@@ -17,13 +17,34 @@ module.exports = { | |||
}, | |||
collisionEnter: async function (obj) { | |||
if (!obj.player) | |||
const { player, syncer, instance: { physics, syncer: globalSyncer } } = obj; | |||
if (!player) | |||
return; | |||
else if (this.patronLevel) { | |||
if (!roles.isRoleLevel(obj, this.patronLevel, 'enter this area')) | |||
return; | |||
} | |||
if (obj.zoneName === this.toZone) { | |||
physics.removeObject(obj, obj.x, obj.y); | |||
obj.x = this.toPos.x; | |||
obj.y = this.toPos.y; | |||
syncer.set(false, null, 'x', obj.x); | |||
syncer.set(false, null, 'y', obj.y); | |||
physics.addObject(obj, obj.x, obj.y); | |||
globalSyncer.queue('onRespawn', { | |||
x: obj.x, | |||
y: obj.y | |||
}, [obj.serverId]); | |||
return; | |||
} | |||
obj.fireEvent('beforeRezone'); | |||
obj.destroyed = true; | |||
@@ -33,11 +33,15 @@ const config = { | |||
'bosses', | |||
'auras' | |||
], | |||
atlasTextureDimensions: {}, | |||
atlasTextures: [ | |||
'tiles', | |||
'walls', | |||
'objects' | |||
], | |||
blockingTileIndices: [ | |||
6, 7, 54, 55, 62, 63, 154, 189, 190, 192, 193, 194, 195, 196, 197 | |||
], | |||
tileOpacities: { | |||
default: { | |||
default: 0.4, | |||
@@ -196,8 +200,6 @@ const config = { | |||
module.exports = { | |||
config, | |||
atlasTextureDimensions: {}, | |||
init: async function () { | |||
events.emit('onBeforeGetClientConfig', config); | |||
@@ -213,14 +215,43 @@ module.exports = { | |||
//The client needs to know this as well as the map loader | |||
calculateAtlasTextureDimensions: async function () { | |||
for (const tex of config.atlasTextures) { | |||
const { atlasTextures, atlasTextureDimensions } = config; | |||
for (const tex of atlasTextures) { | |||
if (atlasTextureDimensions[tex]) | |||
return; | |||
const path = tex.includes('.png') ? `../${tex}` : `../client/images/${tex}.png`; | |||
const dimensions = await imageSize(path); | |||
delete dimensions.type; | |||
this.atlasTextureDimensions[tex] = dimensions; | |||
atlasTextureDimensions[tex] = dimensions; | |||
} | |||
}, | |||
getTileIndexInAtlas: async function (spriteSheet, tileIndexInSource) { | |||
const { atlasTextures, atlasTextureDimensions } = config; | |||
//We need to perform this check because once mods start adding sheets to atlasTextures, | |||
// things get out of control. We need to fix this in the future as it will become screwy. | |||
if (Object.keys(atlasTextureDimensions).length !== atlasTextures) | |||
await this.calculateAtlasTextureDimensions(); | |||
const indexOfSheet = atlasTextures.indexOf(spriteSheet); | |||
let tileCountBeforeSheet = 0; | |||
for (let i = 0; i < indexOfSheet; i++) { | |||
const sheet = atlasTextures[i]; | |||
const { width, height } = atlasTextureDimensions[sheet]; | |||
tileCountBeforeSheet += ((width / 8) * (height / 8)); | |||
} | |||
const result = tileCountBeforeSheet + tileIndexInSource; | |||
return result; | |||
}, | |||
//Used to send to clients | |||
@@ -1,4 +1,12 @@ | |||
{ "backgroundcolor":"#32222e", | |||
"compressionlevel":-1, | |||
"editorsettings": | |||
{ | |||
"export": | |||
{ | |||
"target":"." | |||
} | |||
}, | |||
"height":200, | |||
"infinite":false, | |||
"layers":[ | |||
@@ -2339,7 +2347,7 @@ | |||
{ | |||
"name":"cpnPortal", | |||
"type":"string", | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}" | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}" | |||
}], | |||
"rotation":0, | |||
"type":"", | |||
@@ -2375,7 +2383,7 @@ | |||
{ | |||
"name":"cpnPortal", | |||
"type":"string", | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}" | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}" | |||
}], | |||
"rotation":0, | |||
"type":"", | |||
@@ -2393,7 +2401,7 @@ | |||
{ | |||
"name":"cpnPortal", | |||
"type":"string", | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}" | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}" | |||
}], | |||
"rotation":0, | |||
"type":"", | |||
@@ -2411,7 +2419,7 @@ | |||
{ | |||
"name":"cpnPortal", | |||
"type":"string", | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}" | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}" | |||
}], | |||
"rotation":0, | |||
"type":"", | |||
@@ -2429,7 +2437,7 @@ | |||
{ | |||
"name":"cpnPortal", | |||
"type":"string", | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}" | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}" | |||
}], | |||
"rotation":0, | |||
"type":"", | |||
@@ -2465,7 +2473,7 @@ | |||
{ | |||
"name":"cpnPortal", | |||
"type":"string", | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":86}}" | |||
"value":"{\"zone\":\"fjolarok\",\"pos\":{\"x\":71,\"y\":50}}" | |||
}], | |||
"rotation":0, | |||
"type":"", | |||
@@ -2500,7 +2508,7 @@ | |||
"value":"[{\"x\":16,\"y\":136}, {\"source\": \"radulos\", \"x\": 163, \"y\": 35}]" | |||
}], | |||
"renderorder":"right-down", | |||
"tiledversion":"1.2.5", | |||
"tiledversion":"1.3.3", | |||
"tileheight":8, | |||
"tilesets":[ | |||
{ | |||
@@ -1,27 +1,4 @@ | |||
module.exports = { | |||
estrid: [{ | |||
msg: '*polishes some vials*' | |||
}, { | |||
msg: 'Where did I put that flask?' | |||
}, { | |||
msg: '*coughs*' | |||
}, { | |||
msg: 'Hm...' | |||
}], | |||
priest: [{ | |||
msg: 'peace be with you' | |||
}, { | |||
msg: 'walk in the light' | |||
}], | |||
cow: [{ | |||
msg: '*moos*' | |||
}], | |||
goat: [{ | |||
msg: '*bleats*' | |||
}], | |||
pig: [{ | |||
msg: '*oinks*' | |||
}], | |||
hermit: [{ | |||
msg: '*strokes his beard*' | |||
}, { | |||
@@ -106,168 +106,5 @@ module.exports = { | |||
targetName: 'hermit' | |||
}] | |||
} | |||
}, | |||
estrid: { | |||
1: { | |||
msg: [{ | |||
msg: 'Is there anything I can help you with today?', | |||
options: [1.1, 1.3, 1.4, 1.5] | |||
}], | |||
options: { | |||
1.1: { | |||
msg: 'How long have you been working here?', | |||
goto: 2 | |||
}, | |||
1.3: { | |||
msg: 'I\'d like to browse your wares.', | |||
goto: 'tradeBuy' | |||
}, | |||
1.4: { | |||
msg: 'I have some items to sell', | |||
goto: 'tradeSell' | |||
}, | |||
1.5: { | |||
msg: 'I want to buy something back', | |||
goto: 'tradeBuyback' | |||
} | |||
} | |||
}, | |||
2: { | |||
msg: 'I haven\'t been working here long, but I was born and raised here by my mother. She ran the shop before me.', | |||
options: { | |||
2.1: { | |||
msg: 'Where is your mother now?', | |||
goto: '2-1' | |||
}, | |||
2.2: { | |||
msg: 'I\'d like to ask something else.', | |||
goto: 1 | |||
} | |||
} | |||
}, | |||
'2-1': { | |||
msg: 'A few months ago, she...took ill. She\'s been bedridden upstairs ever since.', | |||
options: { | |||
'2-1.1': { | |||
msg: 'I\'d like to ask something else.', | |||
goto: 1 | |||
} | |||
} | |||
}, | |||
tradeBuy: { | |||
cpn: 'trade', | |||
method: 'startBuy', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
}, | |||
tradeSell: { | |||
cpn: 'trade', | |||
method: 'startSell', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
}, | |||
tradeBuyback: { | |||
cpn: 'trade', | |||
method: 'startBuyback', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
} | |||
}, | |||
vikar: { | |||
1: { | |||
msg: [{ | |||
msg: 'How may I help you today?', | |||
options: [] | |||
}] | |||
} | |||
}, | |||
asvald: { | |||
1: { | |||
msg: [{ | |||
msg: 'Is there anything I can help you with today?', | |||
options: [1.1, 1.2, 1.3] | |||
}], | |||
options: { | |||
1.1: { | |||
msg: 'I would like to buy some runes', | |||
goto: 'tradeBuy' | |||
}, | |||
1.2: { | |||
msg: 'I have some items I would like to sell', | |||
goto: 'tradeSell' | |||
}, | |||
1.3: { | |||
msg: 'Could I see the items I sold to you?', | |||
goto: 'tradeBuyback' | |||
} | |||
} | |||
}, | |||
tradeBuy: { | |||
cpn: 'trade', | |||
method: 'startBuy', | |||
args: [{ | |||
targetName: 'asvald' | |||
}] | |||
}, | |||
tradeSell: { | |||
cpn: 'trade', | |||
method: 'startSell', | |||
args: [{ | |||
targetName: 'asvald' | |||
}] | |||
}, | |||
tradeBuyback: { | |||
cpn: 'trade', | |||
method: 'startBuyback', | |||
args: [{ | |||
targetName: 'asvald' | |||
}] | |||
} | |||
}, | |||
priest: { | |||
1: { | |||
msg: [{ | |||
msg: 'Is there anything I can help you with today?', | |||
options: [1.1, 1.2, 1.3] | |||
}], | |||
options: { | |||
1.1: { | |||
msg: 'I\'d like to browse your wares.', | |||
goto: 'tradeBuy' | |||
}, | |||
1.2: { | |||
msg: 'I have some items to sell', | |||
goto: 'tradeSell' | |||
}, | |||
1.3: { | |||
msg: 'I want to buy something back', | |||
goto: 'tradeBuyback' | |||
} | |||
} | |||
}, | |||
tradeBuy: { | |||
cpn: 'trade', | |||
method: 'startBuy', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
}, | |||
tradeSell: { | |||
cpn: 'trade', | |||
method: 'startSell', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
}, | |||
tradeBuyback: { | |||
cpn: 'trade', | |||
method: 'startBuyback', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
} | |||
} | |||
}; |
@@ -18,506 +18,12 @@ module.exports = { | |||
type: 'fish', | |||
quantity: [6, 12] | |||
}, | |||
shopestrid: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
shophermit: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'hermit' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
shopvikar: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'vikar' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
shopasvald: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'asvald' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
shoppriest: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
gas: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['c0c3cf', '80f643'], | |||
end: ['386646', '69696e'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 18, | |||
max: 64 | |||
}, | |||
end: { | |||
min: 8, | |||
max: 24 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 2, | |||
max: 6 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 4 | |||
} | |||
}, | |||
lifetime: { | |||
min: 4, | |||
max: 24 | |||
}, | |||
alpha: { | |||
start: 0.05, | |||
end: 0 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.02, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
blendMode: 'screen', | |||
spawnRect: { | |||
x: -80, | |||
y: -80, | |||
w: 160, | |||
h: 160 | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
greencandle: { | |||
components: { | |||
cpnLight: { | |||
simplify: function () { | |||
return { | |||
type: 'light', | |||
blueprint: { | |||
color: { | |||
start: ['80f643'], | |||
end: ['4ac441', '51fc9a', 'd07840'] | |||
}, | |||
lifetime: { | |||
min: 2, | |||
max: 6 | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
fountain: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['48edff', '3fa7dd'], | |||
end: ['3a71ba', '42548d'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 2, | |||
max: 5 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.8, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -10, | |||
y: -21, | |||
w: 20, | |||
h: 8 | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
alchgreenpot: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['80f643', '80f643'], | |||
end: ['4ac441', '4ac441'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.1, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -20, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
alchredpot: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['ff4252', 'ff4252'], | |||
end: ['a82841', 'a82841'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -28, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
'alchemy workbench': { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['ff4252', 'ff4252'], | |||
end: ['a82841', 'a82841'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -28, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
}; | |||
} | |||
}, | |||
cpnWorkbench: { | |||
type: 'alchemy' | |||
} | |||
} | |||
}, | |||
etchbench: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['ff4252', 'ff4252'], | |||
end: ['a82841', 'a82841'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -28, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
}; | |||
} | |||
}, | |||
cpnWorkbench: { | |||
type: 'etching' | |||
} | |||
} | |||
}, | |||
fireplace: { | |||
components: { | |||
cpnWorkbench: { | |||
type: 'cooking' | |||
} | |||
} | |||
}, | |||
'enchanting shrine': { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['48edff', 'fc66f7'], | |||
end: ['393268', '42548d'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -28, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
}; | |||
} | |||
}, | |||
cpnWorkbench: { | |||
type: 'enchanting' | |||
} | |||
} | |||
} | |||
}, | |||
mobs: { | |||
@@ -531,6 +37,7 @@ module.exports = { | |||
}, | |||
'crazed seagull': { | |||
level: 1, | |||
spawnCd: 50, | |||
rare: { | |||
count: 0 | |||
@@ -645,15 +152,6 @@ module.exports = { | |||
name: 'The Pincer King' | |||
} | |||
}, | |||
'mud crab': { | |||
level: 9 | |||
}, | |||
frog: { | |||
level: 8, | |||
rare: { | |||
name: 'The Muck Prince' | |||
} | |||
}, | |||
eagle: { | |||
level: 10, | |||
regular: { | |||
@@ -727,147 +225,6 @@ module.exports = { | |||
} | |||
} | |||
}, | |||
guard: { | |||
level: 20, | |||
attackable: false, | |||
walkDistance: 0, | |||
rare: { | |||
count: 0 | |||
} | |||
}, | |||
estrid: { | |||
level: 15, | |||
attackable: false, | |||
walkDistance: 5, | |||
rare: { | |||
count: 0 | |||
}, | |||
properties: { | |||
cpnTrade: { | |||
items: { | |||
min: 0, | |||
max: 0, | |||
extra: [{ | |||
name: 'Empty Vial', | |||
material: true, | |||
sprite: [0, 9], | |||
worth: 10, | |||
infinite: true | |||
}] | |||
}, | |||
faction: { | |||
id: 'fjolgard', | |||
tier: 5 | |||
}, | |||
markup: { | |||
buy: 0.25, | |||
sell: 2.5 | |||
} | |||
} | |||
} | |||
}, | |||
vikar: { | |||
level: 15, | |||
walkDistance: 0, | |||
attackable: false, | |||
rare: { | |||
count: 0 | |||
} | |||
}, | |||
luta: { | |||
level: 15, | |||
walkDistance: 0, | |||
attackable: false, | |||
rare: { | |||
count: 0 | |||
} | |||
}, | |||
asvald: { | |||
level: 15, | |||
walkDistance: 0, | |||
attackable: false, | |||
rare: { | |||
count: 0 | |||
}, | |||
properties: { | |||
cpnTrade: { | |||
items: { | |||
min: 0, | |||
max: 0, | |||
extra: [{ | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'magic missile', | |||
worth: 3 | |||
}, { | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'ice spear', | |||
worth: 3 | |||
}, { | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'smite', | |||
worth: 3 | |||
}, { | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'consecrate', | |||
worth: 3 | |||
}, { | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'slash', | |||
worth: 3 | |||
}, { | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'charge', | |||
worth: 3 | |||
}, { | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'flurry', | |||
worth: 3 | |||
}, { | |||
generate: true, | |||
spell: true, | |||
quality: 0, | |||
infinite: true, | |||
spellName: 'smokebomb', | |||
worth: 3 | |||
}] | |||
}, | |||
faction: { | |||
id: 'fjolgard', | |||
tier: 5 | |||
}, | |||
markup: { | |||
buy: 0.25, | |||
sell: 10 | |||
} | |||
} | |||
} | |||
}, | |||
rodriguez: { | |||
attackable: false, | |||
level: 10, | |||
@@ -896,66 +253,6 @@ module.exports = { | |||
count: 0 | |||
} | |||
}, | |||
priest: { | |||
level: 20, | |||
attackable: false, | |||
walkDistance: 0, | |||
rare: { | |||
count: 0 | |||
}, | |||
properties: { | |||
cpnTrade: { | |||
items: { | |||
min: 5, | |||
max: 10, | |||
extra: [{ | |||
type: 'skin', | |||
skinId: 'gaekatlan-druid', | |||
worth: 100, | |||
factions: [{ | |||
id: 'gaekatla', | |||
tier: 7 | |||
}] | |||
}, { | |||
worth: 100, | |||
infinite: true, | |||
generate: true, | |||
name: 'Cowl of Obscurity', | |||
level: [4, 13], | |||
quality: 4, | |||
noSpell: true, | |||
slot: 'head', | |||
sprite: [2, 0], | |||
spritesheet: '../../../images/legendaryItems.png', | |||
type: 'Silk Cowl', | |||
description: 'Imbued with the powers of Gaekatla herself.', | |||
stats: ['hpMax', 'hpMax', 'int', 'int'], | |||
effects: [{ | |||
type: 'healOnCrit', | |||
rolls: { | |||
i_chance: [20, 60], | |||
i_percentage: [3, 7] | |||
} | |||
}], | |||
factions: [{ | |||
id: 'gaekatla', | |||
tier: 7 | |||
}] | |||
}] | |||
}, | |||
faction: { | |||
id: 'gaekatla', | |||
tier: 5 | |||
}, | |||
markup: { | |||
buy: 0.25, | |||
sell: 10 | |||
} | |||
} | |||
} | |||
}, | |||
sundfehr: { | |||
level: 9, | |||
walkDistance: 0, | |||
@@ -5,10 +5,6 @@ let config = [ | |||
name: 'cave', | |||
path: 'config/maps' | |||
}, | |||
{ | |||
name: 'sewer', | |||
path: 'config/maps' | |||
}, | |||
{ | |||
name: 'fjolarok', | |||
path: 'config/maps' | |||
@@ -1,291 +0,0 @@ | |||
const { mobs: { rat: { level, faction, grantRep, regular: { drops } } } } = require('../zone'); | |||
const rewardGenerator = require('../../../../misc/rewardGenerator'); | |||
const rewardConfig = { | |||
regularKill: [{ | |||
name: 'Iron Bar', | |||
sprite: [0, 0], | |||
quality: 0, | |||
quantity: 5, | |||
chance: 10 | |||
}, { | |||
name: 'Cloth Scrap', | |||
sprite: [0, 1], | |||
quality: 0, | |||
quantity: 5, | |||
chance: 10 | |||
}, { | |||
name: 'Leather Scrap', | |||
sprite: [0, 7], | |||
quality: 0, | |||
quantity: 5, | |||
chance: 10 | |||
}, { | |||
name: 'Muddy Runestone', | |||
material: true, | |||
quality: 0, | |||
sprite: [6, 0], | |||
spritesheet: 'images/materials.png', | |||
chance: 1 | |||
}, { | |||
name: 'Epic Essence', | |||
material: true, | |||
sprite: [0, 5], | |||
quality: 3, | |||
chance: 1 | |||
}, { | |||
name: 'Rat Claw', | |||
material: true, | |||
sprite: [3, 0], | |||
spritesheet: 'images/materials.png', | |||
chance: 3 | |||
}] | |||
}; | |||
const descriptionStrings = { | |||
leadup: 'A bandit alchemist has been spotted in the sewer tunnels.', | |||
active: 'Rats are swarming towards the city.', | |||
success: 'Success: The rat invasion has been averted.', | |||
failure: 'Failure: The rats have made it to the city.', | |||
escapeCounter: 'Escapees: $ratEscapees$' | |||
}; | |||
const config = { | |||
cron: '0 */3 * * *', | |||
//cron: '* * * * *', | |||
idFirstSpawnPhase: 6, | |||
idFailPhase: 19, | |||
maxEscapees: 5, | |||
repeats: [5, 5, 3], | |||
//repeats: [1, 1, 1], | |||
rewardItemsPerKill: 3 | |||
}; | |||
const ratTargetPos = { | |||
x: 97, | |||
y: 87 | |||
}; | |||
const rat = { | |||
name: 'Bloodthirsty Rat', | |||
cell: 24, | |||
level, | |||
faction, | |||
grantRep, | |||
drops, | |||
hpMult: 3, | |||
pos: { | |||
x: 61, | |||
y: 62 | |||
}, | |||
originX: ratTargetPos.x, | |||
originY: ratTargetPos.y, | |||
maxChaseDistance: 1000, | |||
spells: [{ | |||
type: 'smokeBomb', | |||
radius: 1, | |||
duration: 20, | |||
range: 2, | |||
selfCast: 1, | |||
statMult: 1, | |||
damage: 0.125, | |||
element: 'poison', | |||
cdMax: 5, | |||
particles: { | |||
scale: { | |||
start: { | |||
min: 10, | |||
max: 25 | |||
}, | |||
end: { | |||
min: 10, | |||
max: 0 | |||
} | |||
}, | |||
opacity: { | |||
start: 0.3, | |||
end: 0 | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 2 | |||
}, | |||
speed: { | |||
start: 3, | |||
end: 0 | |||
}, | |||
color: { | |||
start: ['db5538', '4ac441'], | |||
end: ['953f36', '386646'] | |||
}, | |||
chance: 0.125, | |||
randomColor: true, | |||
randomScale: true, | |||
blendMode: 'screen', | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -10, | |||
y: -10, | |||
w: 20, | |||
h: 20 | |||
} | |||
} | |||
}], | |||
events: { | |||
afterMove: function () { | |||
const { obj: { x, y } } = this; | |||
if (x !== ratTargetPos.x || y !== ratTargetPos.y) | |||
return; | |||
const eventManager = this.obj.instance.events; | |||
const eventName = this.obj.event.config.name; | |||
eventManager.incrementEventVariable(eventName, 'ratEscapees', 1); | |||
const { active, escapeCounter } = descriptionStrings; | |||
const newDesc = `${active}<br /><br />${escapeCounter}`; | |||
eventManager.setEventDescription(eventName, newDesc); | |||
this.obj.destroyed = true; | |||
const totalEscapees = this.obj.event.variables.ratEscapees; | |||
if (totalEscapees >= config.maxEscapees) { | |||
const event = this.obj.event; | |||
event.currentPhase.end = true; | |||
event.nextPhase = config.idFailPhase; | |||
} | |||
}, | |||
afterDeath: function ({ source: { name } }) { | |||
const eventManager = this.obj.instance.events; | |||
const eventName = this.obj.event.config.name; | |||
const itemCount = config.rewardItemsPerKill; | |||
const rewards = rewardGenerator(itemCount, rewardConfig.regularKill); | |||
eventManager.addParticipantRewards(eventName, name, rewards); | |||
} | |||
} | |||
}; | |||
module.exports = { | |||
name: 'Plague of Rats', | |||
description: descriptionStrings.leadup, | |||
distance: -1, | |||
cron: config.cron, | |||
phases: [{ | |||
type: 'spawnMob', | |||
auto: true, | |||
mobs: [{ | |||
id: 'banditAlchemist', | |||
name: 'Bandit Alchemist', | |||
attackable: false, | |||
hpMult: 1, | |||
cell: 79, | |||
level: 15, | |||
pos: { | |||
x: 117, | |||
y: 62 | |||
} | |||
}] | |||
}, { | |||
type: 'moveMob', | |||
id: 'banditAlchemist', | |||
pos: { | |||
x: 64, | |||
y: 63 | |||
} | |||
}, { | |||
type: 'eventChain', | |||
config: [{ | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: 'I think I cracked it! The serum should finally be complete.', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: 'One taste of this and the rats\' hunger for blood will be instatiable.', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: '*pours a bubbling liquid into a rat nest*', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: 'Let\'s see how Fjolgard handles a rat infestation of the bloodthirsty variety.', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: '*laughs*', | |||
delay: 15 | |||
}] | |||
}, { | |||
type: 'moveMob', | |||
id: 'banditAlchemist', | |||
pos: { | |||
x: 117, | |||
y: 63 | |||
}, | |||
auto: true | |||
}, { | |||
type: 'despawnMob', | |||
id: 'banditAlchemist' | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.active, | |||
auto: true | |||
}, { | |||
type: 'spawnMob', | |||
mobs: [rat], | |||
auto: true | |||
}, { | |||
type: 'wait', | |||
ttl: 15 | |||
}, { | |||
type: 'goto', | |||
gotoPhaseIndex: config.idFirstSpawnPhase, | |||
repeats: config.repeats[0] | |||
}, { | |||
type: 'spawnMob', | |||
mobs: [rat], | |||
auto: true | |||
}, { | |||
type: 'wait', | |||
ttl: 7 | |||
}, { | |||
type: 'goto', | |||
gotoPhaseIndex: config.idFirstSpawnPhase + 3, | |||
repeats: config.repeats[1] | |||
}, { | |||
type: 'spawnMob', | |||
mobs: [rat], | |||
auto: true | |||
}, { | |||
type: 'wait', | |||
ttl: 3 | |||
}, { | |||
type: 'goto', | |||
gotoPhaseIndex: config.idFirstSpawnPhase + 6, | |||
repeats: config.repeats[2] | |||
}, { | |||
type: 'killAllMobs' | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.success, | |||
ttl: 100 | |||
}, { | |||
type: 'giveRewards' | |||
}, { | |||
type: 'end' | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.failure, | |||
ttl: 100 | |||
}] | |||
}; |
@@ -1,455 +0,0 @@ | |||
const balance = { | |||
rat: { | |||
clawChance: 3 | |||
}, | |||
giantRat: { | |||
clawChance: 5 | |||
}, | |||
enragedRat: { | |||
clawChance: 80 | |||
}, | |||
stinktooth: { | |||
runestoneChance: 10, | |||
recipeChance: 3, | |||
shankChance: 0.2 | |||
}, | |||
bandit: { | |||
keyChance: 1 | |||
}, | |||
direRat: { | |||
clawChance: 7 | |||
}, | |||
bera: { | |||
recipeChance: 3, | |||
keyChance: 3 | |||
} | |||
}; | |||
module.exports = { | |||
name: 'The Fjolgard Sewer', | |||
level: [11, 13], | |||
resources: { | |||
Stinkcap: { | |||
type: 'herb', | |||
max: 1 | |||
} | |||
}, | |||
mobs: { | |||
default: { | |||
faction: 'fjolgard', | |||
deathRep: -5 | |||
}, | |||
rat: { | |||
faction: 'fjolgard', | |||
grantRep: { | |||
fjolgard: 6 | |||
}, | |||
level: 11, | |||
regular: { | |||
drops: { | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: balance.rat.clawChance, | |||
name: 'Rat Claw', | |||
material: true, | |||
sprite: [3, 0], | |||
spritesheet: 'images/materials.png' | |||
}] | |||
} | |||
}, | |||
rare: { | |||
name: 'Enraged Rat', | |||
cell: 24, | |||
drops: { | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: balance.enragedRat.clawChance, | |||
name: 'Rat Claw', | |||
material: true, | |||
sprite: [3, 0], | |||
spritesheet: 'images/materials.png' | |||
}] | |||
} | |||
} | |||
}, | |||
'giant rat': { | |||
faction: 'fjolgard', | |||
grantRep: { | |||
fjolgard: 6 | |||
}, | |||
level: 12, | |||
regular: { | |||
hpMult: 2, | |||
dmgMult: 1.2, | |||
drops: { | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: balance.giantRat.clawChance, | |||
name: 'Rat Claw', | |||
material: true, | |||
sprite: [3, 0], | |||
spritesheet: 'images/materials.png' | |||
}] | |||
} | |||
}, | |||
rare: { | |||
count: 0 | |||
} | |||
}, | |||
stinktooth: { | |||
faction: 'hostile', | |||
grantRep: { | |||
fjolgard: 15 | |||
}, | |||
level: 13, | |||
spawnCd: 1714, | |||
regular: { | |||
hpMult: 10, | |||
dmgMult: 3, | |||
drops: { | |||
rolls: 3, | |||
chance: 100, | |||
noRandom: true, | |||
alsoRandom: true, | |||
magicFind: [2000, 125], | |||
blueprints: [{ | |||
chance: balance.stinktooth.shankChance, | |||
name: 'Putrid Shank', | |||
level: 13, | |||
quality: 4, | |||
slot: 'oneHanded', | |||
type: 'Dagger', | |||
spritesheet: '../../../images/legendaryItems.png', | |||
sprite: [0, 1], | |||
implicitStat: { | |||
stat: 'lifeOnHit', | |||
value: [5, 20] | |||
}, | |||
effects: [{ | |||
type: 'castSpellOnHit', | |||
rolls: { | |||
i_chance: [5, 20], | |||
spell: 'smokeBomb' | |||
} | |||
}] | |||
}, { | |||
chance: balance.stinktooth.recipeChance, | |||
type: 'recipe', | |||
name: 'Recipe: Rune of Whirlwind', | |||
profession: 'etching', | |||
teaches: 'runeWhirlwind' | |||
}, { | |||
chance: balance.stinktooth.runestoneChance, | |||
name: 'Muddy Runestone', | |||
material: true, | |||
sprite: [6, 0], | |||
spritesheet: 'images/materials.png' | |||
}] | |||
} | |||
}, | |||
rare: { | |||
count: 0 | |||
}, | |||
spells: [{ | |||
type: 'melee', | |||
statMult: 1, | |||
damage: 0.08 | |||
}, { | |||
type: 'whirlwind', | |||
range: 2, | |||
damage: 0.2, | |||
cdMax: 40 | |||
}, { | |||
type: 'summonSkeleton', | |||
killMinionsOnDeath: false, | |||
killMinionsBeforeSummon: false, | |||
minionsDieOnAggroClear: true, | |||
needLos: false, | |||
sheetName: 'mobs', | |||
cdMax: 60, | |||
positions: [[30, 30], [40, 30], [30, 40], [40, 40]], | |||
summonTemplates: [{ | |||
name: 'Biter Rat', | |||
cell: 16, | |||
hpPercent: 20, | |||
dmgPercent: 0.1, | |||
basicSpell: 'melee' | |||
}, { | |||
name: 'Spitter Rat', | |||
cell: 24, | |||
hpPercent: 10, | |||
dmgPercent: 0.175, | |||
basicSpell: 'projectile' | |||
}] | |||
}, { | |||
type: 'charge', | |||
castOnEnd: 1, | |||
cdMax: 50, | |||
targetRandom: true, | |||
damage: 0.3 | |||
}, { | |||
type: 'fireblast', | |||
range: 2, | |||
damage: 0.001, | |||
pushback: 2, | |||
procCast: true | |||
}] | |||
}, | |||
bandit: { | |||
faction: 'hostile', | |||
grantRep: { | |||
fjolgard: 18 | |||
}, | |||
level: 11, | |||
regular: { | |||
drops: { | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: balance.bandit.keyChance, | |||
type: 'key', | |||
noSalvage: true, | |||
name: 'Rusted Key', | |||
keyId: 'rustedSewer', | |||
singleUse: true, | |||
sprite: [12, 1], | |||
quantity: 1 | |||
}] | |||
} | |||
}, | |||
rare: { | |||
name: 'Cutthroat' | |||
} | |||
}, | |||
'dire rat': { | |||
level: 13, | |||
walkDistance: 0, | |||
faction: 'hostile', | |||
noQuest: true, | |||
grantRep: { | |||
fjolgard: 22 | |||
}, | |||
regular: { | |||
hpMult: 5, | |||
dmgMult: 1.2, | |||
drops: { | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: balance.direRat.clawChance, | |||
name: 'Rat Claw', | |||
material: true, | |||
sprite: [3, 0], | |||
spritesheet: 'images/materials.png' | |||
}] | |||
} | |||
}, | |||
rare: { | |||
count: 0 | |||
} | |||
}, | |||
'bera the blade': { | |||
faction: 'hostile', | |||
grantRep: { | |||
fjolgard: 25 | |||
}, | |||
level: 14, | |||
walkDistance: 0, | |||
regular: { | |||
hpMult: 3, | |||
dmgMult: 1.5, | |||
drops: { | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: balance.bera.recipeChance, | |||
type: 'recipe', | |||
name: 'Recipe: Rune of Ambush', | |||
profession: 'etching', | |||
teaches: 'runeAmbush' | |||
}, { | |||
chance: balance.bera.keyChance, | |||
type: 'key', | |||
noSalvage: true, | |||
name: 'Rusted Key', | |||
keyId: 'rustedSewer', | |||
singleUse: true, | |||
sprite: [12, 1], | |||
quantity: 1 | |||
}] | |||
} | |||
}, | |||
rare: { | |||
count: 0 | |||
} | |||
} | |||
}, | |||
objects: { | |||
'mudfish school': { | |||
max: 9, | |||
type: 'fish', | |||
quantity: [6, 12] | |||
}, | |||
sewerdoor: { | |||
properties: { | |||
cpnDoor: { | |||
autoClose: 171, | |||
locked: true, | |||
key: 'rustedSewer', | |||
destroyKey: true | |||
} | |||
} | |||
}, | |||
bubbles: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['51fc9a', '80f643'], | |||
end: ['386646', '44cb95'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 8 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 4 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 2, | |||
max: 6 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 4 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 3 | |||
}, | |||
alpha: { | |||
start: 0.5, | |||
end: 0 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
blendMode: 'screen', | |||
spawnRect: { | |||
x: -40, | |||
y: -40, | |||
w: 80, | |||
h: 80 | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
gas: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['c0c3cf', '80f643'], | |||
end: ['386646', '69696e'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 18, | |||
max: 64 | |||
}, | |||
end: { | |||
min: 8, | |||
max: 24 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 2, | |||
max: 6 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 4 | |||
} | |||
}, | |||
lifetime: { | |||
min: 4, | |||
max: 24 | |||
}, | |||
alpha: { | |||
start: 0.05, | |||
end: 0 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.02, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
blendMode: 'screen', | |||
spawnRect: { | |||
x: -80, | |||
y: -80, | |||
w: 160, | |||
h: 160 | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}; |
@@ -1,6 +1,6 @@ | |||
module.exports = { | |||
qualities: [ | |||
2000, | |||
0, | |||
350, | |||
80, | |||
17, | |||
@@ -18,7 +18,7 @@ module.exports = { | |||
}, | |||
onAfterGetZone: function (zone, config) { | |||
if (zone !== 'fjolarok') | |||
if (zone !== 'fjolgard') | |||
return; | |||
let newRunes = [{ | |||
@@ -275,10 +275,12 @@ module.exports = { | |||
}, | |||
performMove: function (action) { | |||
let data = action.data; | |||
let physics = this.instance.physics; | |||
const { x: xOld, y: yOld, syncer, aggro, mob, instance: { physics } } = this; | |||
if (!action.force) { | |||
const { maxDistance = 1, force, data } = action; | |||
const { x: xNew, y: yNew } = data; | |||
if (!force) { | |||
if (physics.isTileBlocking(data.x, data.y)) | |||
return true; | |||
@@ -290,10 +292,8 @@ module.exports = { | |||
return true; | |||
} | |||
let maxDistance = action.maxDistance || 1; | |||
let deltaX = Math.abs(this.x - data.x); | |||
let deltaY = Math.abs(this.y - data.y); | |||
let deltaX = Math.abs(xOld - xNew); | |||
let deltaY = Math.abs(yOld - yNew); | |||
if ( | |||
( | |||
(deltaX > maxDistance) || | |||
@@ -308,27 +308,33 @@ module.exports = { | |||
} | |||
//Don't allow mob overlap during combat | |||
if ((this.mob) && (this.mob.target)) { | |||
if (physics.addObject(this, data.x, data.y)) { | |||
physics.removeObject(this, this.x, this.y); | |||
if (mob && mob.target) { | |||
this.x = xNew; | |||
this.y = yNew; | |||
if (physics.addObject(this, xNew, yNew)) | |||
physics.removeObject(this, xOld, yOld); | |||
else { | |||
this.x = xOld; | |||
this.y = yOld; | |||
this.x = data.x; | |||
this.y = data.y; | |||
} else | |||
return false; | |||
} | |||
} else { | |||
physics.removeObject(this, this.x, this.y, data.x, data.y); | |||
physics.addObject(this, data.x, data.y, this.x, this.y); | |||
this.x = data.x; | |||
this.y = data.y; | |||
physics.removeObject(this, xOld, yOld, xNew, yNew); | |||
this.x = xNew; | |||
this.y = yNew; | |||
physics.addObject(this, xNew, yNew, xOld, yOld); | |||
} | |||
let syncer = this.syncer; | |||
//We can't use xNew and yNew because addObject could have changed the position (like entering a building interior with stairs) | |||
syncer.o.x = this.x; | |||
syncer.o.y = this.y; | |||
if (this.aggro) | |||
this.aggro.move(); | |||
if (aggro) | |||
aggro.move(); | |||
this.fireEvent('afterMove'); | |||
@@ -796,9 +796,9 @@ | |||
} | |||
}, | |||
"engine.io-client": { | |||
"version": "3.5.0", | |||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", | |||
"integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", | |||
"version": "3.5.1", | |||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.1.tgz", | |||
"integrity": "sha512-oVu9kBkGbcggulyVF0kz6BV3ganqUeqXvD79WOFKa+11oK692w1NyFkuEj4xrkFRpZhn92QOqTk4RQq5LiBXbQ==", | |||
"requires": { | |||
"component-emitter": "~1.3.0", | |||
"component-inherit": "0.0.3", | |||
@@ -3020,9 +3020,9 @@ | |||
} | |||
}, | |||
"ws": { | |||
"version": "7.4.3", | |||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", | |||
"integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==" | |||
"version": "7.4.4", | |||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", | |||
"integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==" | |||
}, | |||
"xmlhttprequest-ssl": { | |||
"version": "1.5.5", | |||
@@ -300,7 +300,7 @@ module.exports = { | |||
}, | |||
getOffsetCellPos: function (sheetName, cell) { | |||
const { atlasTextureDimensions, config: { atlasTextures } } = clientConfig; | |||
const { config: { atlasTextureDimensions, atlasTextures } } = clientConfig; | |||
const indexInAtlas = atlasTextures.indexOf(sheetName); | |||
let offset = 0; | |||
@@ -378,11 +378,8 @@ module.exports = { | |||
if (layerName.indexOf('walls') > -1) | |||
this.collisionMap[x][y] = 1; | |||
else if (layerName === 'tiles' && sheetName === 'tiles') { | |||
//Check for water and water-like tiles | |||
if ([6, 7, 54, 55, 62, 63, 154, 189, 190, 192, 193, 194, 195, 196, 197].includes(offsetCell)) | |||
this.collisionMap[x][y] = 1; | |||
} | |||
else if (clientConfig.config.blockingTileIndices.includes(offsetCell)) | |||
this.collisionMap[x][y] = 1; | |||
}, | |||
object: function (layerName, cell) { | |||
@@ -459,6 +456,7 @@ module.exports = { | |||
} | |||
} else if (layerName === 'hiddenRooms') { | |||
blueprint.fog = (cell.properties || {}).fog; | |||
blueprint.interior = (cell.properties || {}).interior; | |||
blueprint.discoverable = (cell.properties || {}).discoverable; | |||
blueprint.layer = ~~((cell.properties || {}).layer || 0); | |||
@@ -114,14 +114,18 @@ module.exports = { | |||
obj.collisionEnter(c); | |||
} | |||
} else { | |||
//If a callback returns true, it means we collide | |||
//If a callback returns true, it means we collide | |||
if (c.collisionEnter(obj)) | |||
return; | |||
obj.collisionEnter(c); | |||
} | |||
} | |||
cell.push(obj); | |||
//Perhaps a collisionEvent caused us to move somewhere else, in which case, we don't push to the cell | |||
// as we assume that the collisionEvent handled it for us | |||
if (obj.x === x && obj.y === y) | |||
cell.push(obj); | |||
return true; | |||
}, | |||