@@ -32,7 +32,8 @@ var components = [ | |||
'reputation', | |||
'serverActions', | |||
'social', | |||
'passives' | |||
'passives', | |||
'sound' | |||
].map(function (c) { | |||
return 'js/components/' + c; | |||
}); | |||
@@ -0,0 +1,18 @@ | |||
define([ | |||
'js/sound/sound' | |||
], function ( | |||
sound | |||
) { | |||
return { | |||
type: 'sound', | |||
sound: null, | |||
volume: 0, | |||
init: function () { | |||
var obj = this.obj; | |||
console.log(this.obj); | |||
sound.addSound(obj.zoneId, this.sound, this.volume, obj.x, obj.y, obj.width, obj.height, obj.area); | |||
} | |||
}; | |||
}); |
@@ -3,10 +3,6 @@ define([ | |||
], function ( | |||
pathfinder | |||
) { | |||
var sqrt = Math.sqrt.bind(Math); | |||
var ceil = Math.ceil.bind(Math); | |||
var random = Math.random.bind(Math); | |||
return { | |||
graph: null, | |||
@@ -28,233 +24,6 @@ define([ | |||
}); | |||
}, | |||
addRegion: function (obj) { | |||
var lowX = obj.x; | |||
var lowY = obj.y; | |||
var highX = lowX + obj.width; | |||
var highY = lowY + obj.height; | |||
var cells = this.cells; | |||
for (var i = lowX; i <= highX; i++) { | |||
var row = cells[i]; | |||
for (var j = lowY; j <= highY; j++) { | |||
row[j].push(obj); | |||
} | |||
} | |||
}, | |||
addObject: function (obj, x, y, fromX, fromY) { | |||
var row = this.cells[x]; | |||
if (!row) | |||
return; | |||
var cell = row[y]; | |||
if (!cell) | |||
return; | |||
var cLen = cell.length; | |||
for (var i = 0; i < cLen; i++) { | |||
var c = cell[i]; | |||
//If we have fromX and fromY, check if the target cell doesn't contain the same obj (like a notice area) | |||
if ((c.width) && (fromX)) { | |||
if ((fromX < c.x) || (fromY < c.y) || (fromX >= c.x + c.width) || (fromY >= c.y + c.height)) { | |||
c.collisionEnter(obj); | |||
obj.collisionEnter(c); | |||
} | |||
} else { | |||
c.collisionEnter(obj); | |||
obj.collisionEnter(c); | |||
} | |||
} | |||
cell.push(obj); | |||
return true; | |||
}, | |||
removeObject: function (obj, x, y, toX, toY) { | |||
var row = this.cells[x]; | |||
if (!row) | |||
return; | |||
var cell = row[y]; | |||
if (!cell) | |||
return; | |||
var oId = obj.id; | |||
var cLen = cell.length; | |||
for (var i = 0; i < cLen; i++) { | |||
var c = cell[i]; | |||
if (c.id != oId) { | |||
//If we have toX and toY, check if the target cell doesn't contain the same obj (like a notice area) | |||
if ((c.width) && (toX)) { | |||
if ((toX < c.x) || (toY < c.y) || (toX >= c.x + c.width) || (toY >= c.y + c.height)) { | |||
c.collisionExit(obj); | |||
obj.collisionExit(c); | |||
} | |||
} else { | |||
c.collisionExit(obj); | |||
obj.collisionExit(c); | |||
} | |||
} else { | |||
cell.splice(i, 1); | |||
i--; | |||
cLen--; | |||
} | |||
} | |||
}, | |||
isValid: function (x, y) { | |||
var row = this.cells[x]; | |||
if ((!row) || (row.length <= y) || (!this.graph.grid[x][y])) | |||
return false; | |||
else | |||
return true; | |||
}, | |||
getCell: function (x, y) { | |||
var row = this.cells[x]; | |||
if (!row) | |||
return []; | |||
var cell = row[y]; | |||
if (!cell) | |||
return []; | |||
return cell; | |||
}, | |||
getArea: function (x1, y1, x2, y2, filter) { | |||
var width = this.width; | |||
var height = this.height; | |||
x1 = ~~x1; | |||
y1 = ~~y1; | |||
x2 = ~~x2; | |||
y2 = ~~y2; | |||
if (x1 < 0) | |||
x1 = 0; | |||
else if (x2 >= width) | |||
x2 = width - 1; | |||
if (y1 < 0) | |||
y1 = 0; | |||
else if (y2 >= height) | |||
y2 = height - 1; | |||
var cells = this.cells; | |||
var grid = this.graph.grid; | |||
var result = []; | |||
for (var i = x1; i <= x2; i++) { | |||
var row = cells[i]; | |||
var gridRow = grid[i]; | |||
for (var j = y1; j <= y2; j++) { | |||
if (!gridRow[j]) | |||
continue; | |||
var cell = row[j]; | |||
var cLen = cell.length; | |||
for (var k = 0; k < cLen; k++) { | |||
var c = cell[k]; | |||
if (filter) { | |||
if (filter(c)) | |||
result.push(c); | |||
} else | |||
result.push(c); | |||
} | |||
} | |||
} | |||
return result; | |||
}, | |||
getOpenCellInArea: function (x1, y1, x2, y2) { | |||
var width = this.width; | |||
var height = this.height; | |||
x1 = ~~x1; | |||
y1 = ~~y1; | |||
x2 = ~~x2; | |||
y2 = ~~y2; | |||
if (x1 < 0) | |||
x1 = 0; | |||
else if (x2 >= width) | |||
x2 = width - 1; | |||
if (y1 < 0) | |||
y1 = 0; | |||
else if (y2 >= height) | |||
y2 = height - 1; | |||
var cells = this.cells; | |||
var grid = this.graph.grid; | |||
var result = []; | |||
for (var i = x1; i <= x2; i++) { | |||
var row = cells[i]; | |||
var gridRow = grid[i]; | |||
for (var j = y1; j <= y2; j++) { | |||
if (!gridRow[j]) | |||
continue; | |||
var cell = row[j]; | |||
if (cell.length == 0) { | |||
return { | |||
x: i, | |||
y: j | |||
}; | |||
} | |||
} | |||
} | |||
return result; | |||
}, | |||
getPath: function (from, to) { | |||
var graph = this.graph; | |||
var grid = graph.grid; | |||
if (!to) { | |||
to = { | |||
x: ~~(random() * grid.length), | |||
y: ~~(random() * grid[0].length) | |||
}; | |||
} | |||
var fromX = ~~from.x; | |||
var fromY = ~~from.y; | |||
if ((!grid[fromX]) || (grid[fromX].length <= fromY) || (fromX < 0) || (fromY < 0)) | |||
return []; | |||
var toX = ~~to.x; | |||
var toY = ~~to.y; | |||
if ((!grid[toX]) || (grid[toX].length <= toY) || (toX < 0) || (toY < 0)) | |||
return []; | |||
var path = pathfinder.astar.search(graph, { | |||
x: fromX, | |||
y: fromY | |||
}, { | |||
x: toX, | |||
y: toY | |||
}, { | |||
closest: true | |||
}); | |||
return path; | |||
}, | |||
isTileBlocking: function (x, y, mob, obj) { | |||
if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height)) | |||
return true; | |||
@@ -266,177 +35,30 @@ define([ | |||
return ((!node) || (node.weight == 0)); | |||
}, | |||
isCellOpen: function (x, y) { | |||
if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height)) | |||
return true; | |||
return (this.cells[x][y].length == 0); | |||
}, | |||
hasLos: function (fromX, fromY, toX, toY) { | |||
if ((fromX < 0) || (fromY < 0) || (fromX >= this.width) | (fromY >= this.height) || (toX < 0) || (toY < 0) || (toX >= this.width) | (toY >= this.height)) | |||
return false; | |||
var graphGrid = this.graph.grid; | |||
if ((!graphGrid[fromX][fromY]) || (!graphGrid[toX][toY])) | |||
return false; | |||
var dx = toX - fromX; | |||
var dy = toY - fromY; | |||
var distance = sqrt((dx * dx) + (dy * dy)); | |||
dx /= distance; | |||
dy /= distance; | |||
fromX += 0.5; | |||
fromY += 0.5; | |||
distance = ceil(distance); | |||
var x = 0; | |||
var y = 0; | |||
for (var i = 0; i < distance; i++) { | |||
fromX += dx; | |||
fromY += dy; | |||
x = ~~fromX; | |||
y = ~~fromY; | |||
if (!graphGrid[x][y]) | |||
return false; | |||
else if ((x == toX) && (y == toY)) | |||
return true; | |||
} | |||
return true; | |||
}, | |||
getClosestPos: function (fromX, fromY, toX, toY, target) { | |||
var tried = {}; | |||
var hasLos = this.hasLos.bind(this, toX, toY); | |||
var width = this.width; | |||
var height = this.height; | |||
var collisionMap = this.collisionMap; | |||
var cells = this.cells; | |||
var reverseX = (fromX > toX); | |||
var reverseY = (fromY > toY); | |||
for (var c = 1; c <= 10; c++) { | |||
var x1 = toX - c; | |||
var y1 = toY - c; | |||
var x2 = toX + c; | |||
var y2 = toY + c; | |||
var lowX, lowY, highX, highY, incX, incY; | |||
if (reverseX) { | |||
incX = -1; | |||
lowX = x2; | |||
highX = x1 - 1; | |||
} else { | |||
incX = 1; | |||
lowX = x1; | |||
highX = x2 + 1; | |||
} | |||
if (reverseY) { | |||
incY = -1; | |||
lowY = y2; | |||
highY = y1 - 1; | |||
} else { | |||
incY = 1; | |||
lowY = y1; | |||
highY = y2 + 1; | |||
} | |||
for (var i = lowX; i != highX; i += incX) { | |||
if ((i < 0) || (i >= width)) | |||
continue; | |||
var row = collisionMap[i]; | |||
var cellRow = cells[i]; | |||
var t = tried[i]; | |||
if (!t) { | |||
t = tried[i] = {}; | |||
} | |||
for (var j = lowY; j != highY; j += incY) { | |||
if (t[j]) | |||
continue; | |||
t[j] = 1; | |||
if ( | |||
((i == toX) && (j == toY)) || | |||
((j < 0) || (j >= height)) || | |||
(row[j]) | |||
) | |||
continue; | |||
var cell = cellRow[j]; | |||
var cLen = cell.length; | |||
var blocking = false; | |||
for (var k = 0; k < cLen; k++) { | |||
var aggro = cell[k].aggro; | |||
if (aggro) { | |||
blocking = aggro.list.some(a => a.obj == target); | |||
if (blocking) | |||
break; | |||
} | |||
} | |||
if (blocking) | |||
continue; | |||
else if (!hasLos(i, j)) | |||
continue; | |||
return { | |||
x: i, | |||
y: j | |||
}; | |||
} | |||
} | |||
} | |||
}, | |||
mobsCollide: function (x, y, obj) { | |||
if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height)) | |||
return true; | |||
var cell = this.cells[x][y]; | |||
var cLen = cell.length; | |||
isInPolygon: function (x, y, verts) { | |||
var inside = false; | |||
if (cLen == 1) | |||
return false; | |||
var vLen = verts.length; | |||
for (var i = 0, j = vLen - 1; i < vLen; j = i++) { | |||
var vi = verts[i]; | |||
var vj = verts[j]; | |||
var found = false; | |||
for (var i = 0; i < cLen; i++) { | |||
var c = cell[i]; | |||
if (c.aggro) { | |||
if ((!found) && (c == obj)) | |||
found = true; | |||
else | |||
return true; | |||
} | |||
} | |||
var xi = vi[0]; | |||
var yi = vi[1]; | |||
var xj = vj[0]; | |||
var yj = vj[1]; | |||
return false; | |||
}, | |||
var doesIntersect = ( | |||
((yi > y) != (yj > y)) && | |||
(x < ((((xj - xi) * (y - yi)) / (yj - yi)) + xi)) | |||
); | |||
setCollision: function (x, y, collides) { | |||
var node = this.graph.grid[x][y]; | |||
if (!node) { | |||
var grid = this.graph.grid; | |||
node = grid[x][y] = new pathfinder.gridNode(x, y, collides ? 0 : 1); | |||
if (doesIntersect) | |||
inside = !inside | |||
} | |||
node.weight = collides ? 0 : 1; | |||
return inside; | |||
} | |||
}; | |||
}); |
@@ -133,7 +133,7 @@ define([ | |||
var components = template.components || []; | |||
delete template.components; | |||
var syncTypes = ['portrait']; | |||
var syncTypes = ['portrait', 'area']; | |||
for (var p in template) { | |||
var value = template[p]; | |||
@@ -183,7 +183,7 @@ define([ | |||
events.emit('onGetPlayer', obj); | |||
window.player = obj; | |||
sound.init(obj.zoneName); | |||
sound.init(obj.zoneId); | |||
renderer.setPosition({ | |||
x: (obj.x - (renderer.width / (scale * 2))) * scale, | |||
@@ -1,49 +1,65 @@ | |||
define([ | |||
'howler' | |||
'howler', | |||
'js/misc/physics' | |||
], function ( | |||
howler | |||
howler, | |||
physics | |||
) { | |||
return { | |||
sounds: [], | |||
init: function (zone) { | |||
this.unload(); | |||
if (zone != 'fjolarok') | |||
return; | |||
this.addSound('fire.ogg', 123, 123); | |||
this.addSound('stream.ogg', 107, 69); | |||
this.addSound('wind.ogg', 176, 104); | |||
init: function (zoneId) { | |||
this.unload(zoneId); | |||
}, | |||
unload: function () { | |||
unload: function (zoneId) { | |||
this.sounds.forEach(function (s) { | |||
if (s.sound) | |||
if ((s.sound) && (s.zoneId != zoneId)) | |||
s.sound.unload(); | |||
}); | |||
this.sounds = []; | |||
this.sounds.spliceWhere(function (s) { | |||
return (s.zoneId != zoneId); | |||
}); | |||
}, | |||
update: function (x, y) { | |||
this.sounds.forEach(function (s) { | |||
var dx = Math.abs(s.x - x); | |||
if (dx > 10) { | |||
if (s.sound) | |||
s.sound.volume(0); | |||
return; | |||
} | |||
var dy = Math.abs(s.y - y); | |||
if (dy > 10) { | |||
if (s.sound) | |||
s.sound.volume(0); | |||
return; | |||
} | |||
var volume = 1; | |||
if (!s.w) { | |||
var dx = Math.abs(s.x - x); | |||
if (dx > 10) { | |||
if (s.sound) | |||
s.sound.volume(0); | |||
return; | |||
} | |||
var dy = Math.abs(s.y - y); | |||
if (dy > 10) { | |||
if (s.sound) | |||
s.sound.volume(0); | |||
return; | |||
} | |||
var dist = 10 - Math.max(dx, dy); | |||
dist = (dist * dist) / 100; | |||
var volume = 0.3 * dist; | |||
var dist = 10 - Math.max(dx, dy); | |||
dist = (dist * dist) / 100; | |||
volume = 0.3 * dist; | |||
} else { | |||
if (!s.area) { | |||
var inside = (!((x < s.x) || (y < s.y) || (x >= s.x + s.w) || (y >= s.y + s.h))); | |||
if (!inside) { | |||
if (s.sound) | |||
s.sound.volume(0); | |||
return; | |||
} | |||
} else { | |||
var inside = physics.isInPolygon(x, y, s.area); | |||
if (!inside) { | |||
if (s.sound) | |||
s.sound.volume(0); | |||
return; | |||
} | |||
} | |||
} | |||
if (!s.sound) { | |||
s.sound = new Howl({ | |||
@@ -54,16 +70,21 @@ define([ | |||
}); | |||
} | |||
s.sound.volume(volume); | |||
s.sound.volume(volume * s.volume); | |||
}); | |||
}, | |||
addSound: function (file, x, y) { | |||
addSound: function (zoneId, file, volume, x, y, w, h, area) { | |||
var sound = { | |||
file: file, | |||
x: x, | |||
y: y, | |||
sound: null | |||
w: w, | |||
h: h, | |||
volume: volume, | |||
area: area, | |||
sound: null, | |||
zoneId: zoneId | |||
}; | |||
this.sounds.push(sound); | |||
@@ -0,0 +1,20 @@ | |||
define([ | |||
], function ( | |||
) { | |||
return { | |||
type: 'sound', | |||
sound: null, | |||
volume: 0, | |||
simplify: function () { | |||
return { | |||
type: 'sound', | |||
sound: this.sound, | |||
volume: this.volume | |||
}; | |||
} | |||
}; | |||
}); |
@@ -3,14 +3,40 @@ module.exports = { | |||
level: [18, 20], | |||
mobs: { | |||
acowlyte: { | |||
level: 20, | |||
default: { | |||
faction: 'hostile', | |||
grantRep: { | |||
gaekatla: 15 | |||
}, | |||
patrol: [ | |||
[25, 90], | |||
[67, 90] | |||
], | |||
faction: 'hostile' | |||
regular: { | |||
hpMult: 4, | |||
dmgMult: 2.2, | |||
drops: { | |||
chance: 45, | |||
rolls: 1, | |||
magicFind: 500 | |||
} | |||
}, | |||
rare: { | |||
hpMult: 7, | |||
dmgMult: 3, | |||
drops: { | |||
chance: 100, | |||
rolls: 1, | |||
magicFind: 2000 | |||
} | |||
} | |||
}, | |||
snekk: { | |||
level: 15 | |||
}, | |||
snekkboss: { | |||
level: 15, | |||
} | |||
} | |||
}; |
@@ -104,7 +104,7 @@ define([ | |||
o = this; | |||
} | |||
var syncTypes = ['portrait']; | |||
var syncTypes = ['portrait', 'area']; | |||
for (var p in o) { | |||
var value = o[p]; | |||
@@ -112,7 +112,6 @@ define([ | |||
continue; | |||
var type = typeof (value); | |||
//build component | |||
if (type == 'object') { | |||
if (value.type) { | |||
@@ -88,6 +88,9 @@ define([ | |||
obj.height = l.height; | |||
} | |||
if (l.area) | |||
obj.area = l.area; | |||
//Add components (certain ones need to happen first) | |||
//TODO: Clean this part up | |||
var properties = extend(true, {}, l.properties); | |||
@@ -134,7 +137,7 @@ define([ | |||
if ((this.physics) && (!obj.dead)) { | |||
if (!obj.width) | |||
this.physics.addObject(obj, obj.x, obj.y); | |||
this.physics.addObject(obj, obj.x, obj.y); | |||
else | |||
this.physics.addRegion(obj); | |||
} | |||
@@ -385,7 +385,36 @@ define([ | |||
} else if (layerName == 'hiddenRooms') { | |||
this.hiddenRooms.push(blueprint); | |||
} else if (!clientObj) { | |||
if (!mapFile.properties.isRandom) | |||
if (cell.polyline) { | |||
var lowX = this.size.w; | |||
var lowY = this.size.h; | |||
var highX = 0; | |||
var highY = 0; | |||
blueprint.area = cell.polyline.map(function (v) { | |||
var x = ~~((v.x + cell.x) / mapScale); | |||
var y = ~~((v.y + cell.y) / mapScale); | |||
if (x < lowX) | |||
lowX = x; | |||
if (x > highX) | |||
highX = x; | |||
if (y < lowY) | |||
lowY = y; | |||
if (y > highY) | |||
highY = y; | |||
return [x, y]; | |||
}); | |||
blueprint.x = lowX; | |||
blueprint.y = lowY; | |||
blueprint.width = (highX - lowX); | |||
blueprint.height = (highY - lowY); | |||
spawners.register(blueprint, blueprint.spawnCd || mapFile.properties.spawnCd); | |||
} else if (!mapFile.properties.isRandom) | |||
spawners.register(blueprint, blueprint.spawnCd || mapFile.properties.spawnCd); | |||
else { | |||
var room = this.rooms.find(function (r) { | |||
@@ -399,6 +428,38 @@ define([ | |||
room.objects.push(blueprint); | |||
} | |||
} else { | |||
if (cell.polyline) { | |||
var lowX = this.size.w; | |||
var lowY = this.size.h; | |||
var highX = 0; | |||
var highY = 0; | |||
blueprint.area = cell.polyline.map(function (v) { | |||
var x = ~~((v.x + cell.x) / mapScale); | |||
var y = ~~((v.y + cell.y) / mapScale); | |||
if (x < lowX) | |||
lowX = x; | |||
if (x > highX) | |||
highX = x; | |||
if (y < lowY) | |||
lowY = y; | |||
if (y > highY) | |||
highY = y; | |||
return [x, y]; | |||
}); | |||
blueprint.x = lowX; | |||
blueprint.y = lowY; | |||
blueprint.width = (highX - lowX); | |||
blueprint.height = (highY - lowY); | |||
} else if (cell.width) { | |||
blueprint.width = cell.width / mapScale; | |||
blueprint.height = cell.height / mapScale; | |||
} | |||
var obj = objects.buildObjects([blueprint], true).getSimple(true); | |||
this.objBlueprints.push(obj); | |||
} | |||
@@ -106,7 +106,12 @@ define([ | |||
//If we have fromX and fromY, check if the target cell doesn't contain the same obj (like a notice area) | |||
if ((c.width) && (fromX)) { | |||
if ((fromX < c.x) || (fromY < c.y) || (fromX >= c.x + c.width) || (fromY >= c.y + c.height)) { | |||
if (c.area) { | |||
if ((this.isInPolygon(x, y, c.area)) && (!this.isInPolygon(fromX, fromY, c.area))) { | |||
c.collisionEnter(obj); | |||
obj.collisionEnter(c); | |||
} | |||
} else if ((fromX < c.x) || (fromY < c.y) || (fromX >= c.x + c.width) || (fromY >= c.y + c.height)) { | |||
c.collisionEnter(obj); | |||
obj.collisionEnter(c); | |||
} | |||
@@ -140,7 +145,12 @@ define([ | |||
if (c.id != oId) { | |||
//If we have toX and toY, check if the target cell doesn't contain the same obj (like a notice area) | |||
if ((c.width) && (toX)) { | |||
if ((toX < c.x) || (toY < c.y) || (toX >= c.x + c.width) || (toY >= c.y + c.height)) { | |||
if (c.area) { | |||
if ((this.isInPolygon(x, y, c.area)) && (!this.isInPolygon(toX, toY, c.area))) { | |||
c.collisionExit(obj); | |||
obj.collisionExit(c); | |||
} | |||
} else if ((toX < c.x) || (toY < c.y) || (toX >= c.x + c.width) || (toY >= c.y + c.height)) { | |||
c.collisionExit(obj); | |||
obj.collisionExit(c); | |||
} | |||
@@ -509,6 +519,31 @@ define([ | |||
grid[x][y].weight = collides ? 0 : 1; | |||
pathfinder.astar.cleanNode(grid[x][y]); | |||
} | |||
}, | |||
isInPolygon: function (x, y, verts) { | |||
var inside = false; | |||
var vLen = verts.length; | |||
for (var i = 0, j = vLen - 1; i < vLen; j = i++) { | |||
var vi = verts[i]; | |||
var vj = verts[j]; | |||
var xi = vi[0]; | |||
var yi = vi[1]; | |||
var xj = vj[0]; | |||
var yj = vj[1]; | |||
var doesIntersect = ( | |||
((yi > y) != (yj > y)) && | |||
(x < ((((xj - xi) * (y - yi)) / (yj - yi)) + xi)) | |||
); | |||
if (doesIntersect) | |||
inside = !inside | |||
} | |||
return inside; | |||
} | |||
}; | |||
}); |