Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
.env
|
||||
.env.*
|
||||
*.log
|
||||
Logs/
|
||||
@@ -0,0 +1,23 @@
|
||||
const { TASK_TYPES } = require("../Data/constants");
|
||||
const { TASK_TYPE_DEAL_DAMAGE } = TASK_TYPES;
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
const { players } = Game;
|
||||
|
||||
function globalAttackCallback(data) {
|
||||
const { typeID, entityTypeID, authorID, damage } = data;
|
||||
|
||||
const player = players[authorID];
|
||||
if (!player) return;
|
||||
|
||||
/*
|
||||
* only quest is updated here
|
||||
* if player is unavailable, it means that they have logged out: damage without player being online???
|
||||
* conclusion: only overtime damage can cause this issue
|
||||
*/
|
||||
|
||||
const toBeAdjusted = [{ taskCategoryID: TASK_TYPE_DEAL_DAMAGE, typeID, entityTypeID }];
|
||||
Object.values(player.quests.list).forEach(quest => quest.adjustTask(toBeAdjusted, damage));
|
||||
};
|
||||
|
||||
module.exports = globalAttackCallback;
|
||||
@@ -0,0 +1,253 @@
|
||||
const Reward = require("../Game/Reward");
|
||||
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
const admiralTable = require("../Data/admiralTable");
|
||||
|
||||
const leaderboards = require("../Data/leaderboard");
|
||||
const { TASK_TYPES, TYPES } = require("../Data/constants");
|
||||
|
||||
const rewardCallback = require("./reward");
|
||||
|
||||
|
||||
const { TASK_TYPE_DESTROY } = TASK_TYPES;
|
||||
const { ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_MONSTER } = TYPES;
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
const { players } = Game;
|
||||
|
||||
const percentages = [0.40, 0.25, 0.15, 0.10, 0.07, 0.03];
|
||||
const amounts = [1, 3, 5, 10, 20, 25, 35];
|
||||
const totalAmount = amounts.reduce((acc, cur) => acc + cur, 0);
|
||||
|
||||
let aa = new Array(amounts.length);
|
||||
setTimeout(async () => {
|
||||
const q = await execute("SELECT * FROM leaderboard_1 ORDER BY amount DESC LIMIT 1099");
|
||||
const batches = [];
|
||||
|
||||
const values = q.data;
|
||||
let totalSize = 0;
|
||||
|
||||
for (let i = 0; i < percentages.length; i++) { // this loop is for slicing based on percentages
|
||||
if (values.length - totalAmount < 0) {
|
||||
batches.push([]);
|
||||
continue;
|
||||
};
|
||||
|
||||
const batchSize = (values.length - totalAmount) * percentages[i];
|
||||
const batch = values.slice(totalSize, totalSize + batchSize);
|
||||
batches.push(batch);
|
||||
|
||||
totalSize += batchSize;
|
||||
};
|
||||
|
||||
let rest = values.slice(totalSize, totalSize + totalAmount);
|
||||
//require("../Miscellaneous/winston").error(`Total length: ${values.length} (these are from DB, raw)`)
|
||||
|
||||
// require("../Miscellaneous/winston").error(`Rest length: ${rest.length} (these are based on fixed amounts instead of percentages)`)
|
||||
|
||||
let x = 0;
|
||||
|
||||
for (let i = 0; i < amounts.length; i++) {
|
||||
const batchSize = amounts[i];
|
||||
const batch = rest.slice(totalAmount - (totalAmount - x), totalAmount - (totalAmount - x - batchSize));
|
||||
// require("../Miscellaneous/winston").error(`amounts array index: ${i} | slicing between: ${totalAmount - (totalAmount - x)} - ${totalAmount - (totalAmount - x - batchSize)}`)
|
||||
aa[amounts.length - 1 - i] = batch;
|
||||
|
||||
x += batchSize;
|
||||
};
|
||||
|
||||
batches.push(...aa);
|
||||
|
||||
const thresholds = new Array(batches.length);
|
||||
|
||||
for (let j = 0; j < batches.length; j++) {
|
||||
const batch = batches[j];
|
||||
const lastElemWithValue = batch.filter(e => e.id);
|
||||
// require("../Miscellaneous/winston").error(`Batch ${j}: length is ${batch.length}, last elem with value: ${lastElemWithValue[lastElemWithValue.length - 1]?.id} ${lastElemWithValue[lastElemWithValue.length - 1]?.amount}`)
|
||||
|
||||
thresholds[j] = lastElemWithValue[lastElemWithValue.length - 1] ? lastElemWithValue[lastElemWithValue.length - 1].amount : 0;
|
||||
|
||||
for (let k = 0; k < batch.length; k++)
|
||||
db.execute("UPDATE playerdata SET globalrank = ? WHERE playerID = ?", [j, batch[k].id]);
|
||||
};
|
||||
|
||||
global.thresholds = thresholds;
|
||||
|
||||
// require("../Miscellaneous/winston").error(thresholds);
|
||||
}, 2000)
|
||||
|
||||
const tracker = new DoubleLinkedList();
|
||||
|
||||
function globalDieCallback(source) {
|
||||
const { typeID, entityTypeID, parentEntityTypeID, isAdmiral, authorID, authorTypeID } = source;
|
||||
const id = `${typeID}_${isAdmiral ? entityTypeID : parentEntityTypeID}`;
|
||||
const ids = {
|
||||
typeID,
|
||||
parentEntityTypeID
|
||||
};
|
||||
console.trace("death", source);
|
||||
const player = players[authorID];
|
||||
|
||||
switch (typeID) {
|
||||
case 1:
|
||||
leaderboards[4].updatePosition(authorID, 1);
|
||||
break;
|
||||
case 2:
|
||||
leaderboards[5].updatePosition(authorID, 1);
|
||||
break;
|
||||
case 3:
|
||||
leaderboards[6].updatePosition(authorID, 1);
|
||||
break;
|
||||
};
|
||||
|
||||
if (authorTypeID === ENTITY_TYPE_PLAYER && typeID === ENTITY_TYPE_PLAYER) {
|
||||
const targetPlayer = players[source.id];
|
||||
|
||||
const keyAuthor = `${player.id}-${targetPlayer.id}`;
|
||||
const killCountAuthor = tracker.getElement(keyAuthor);
|
||||
if (killCountAuthor === 2) return console.info("reached threshold");
|
||||
|
||||
const keyTarget = `${targetPlayer.id}-${player.id}`;
|
||||
const killCountTarget = tracker.getElement(keyTarget);
|
||||
if (killCountTarget) tracker.accumulateItem(keyTarget, -1);
|
||||
|
||||
tracker.accumulateItem(keyAuthor, 1);
|
||||
|
||||
const amount = 10;
|
||||
|
||||
const reward = new Reward({
|
||||
distributionType: 3,
|
||||
baseReward: [
|
||||
{
|
||||
categoryID: 6,
|
||||
id: 4,
|
||||
amountMinimum: amount
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
reward.dynamic(rewards => rewardCallback({ isRegular: true, authorID, source, rewards }));
|
||||
|
||||
const battlePointsTarget = targetPlayer.statusPoints[3] && targetPlayer.statusPoints[3].amount;
|
||||
console.info(battlePointsTarget, amount, "sp")
|
||||
if (battlePointsTarget >= amount) {
|
||||
const reward_2 = new Reward({
|
||||
distributionType: 3,
|
||||
baseReward: [
|
||||
{
|
||||
categoryID: 6,
|
||||
id: 4,
|
||||
amountMinimum: amount * -1
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
reward_2.dynamic(rewards => rewardCallback({ isRegular: true, authorID: source.id, source, rewards }));
|
||||
};
|
||||
};
|
||||
|
||||
if (typeID === ENTITY_TYPE_NPC) {
|
||||
|
||||
} else if (typeID === ENTITY_TYPE_MONSTER) {
|
||||
|
||||
};
|
||||
|
||||
const toBeAdjusted = [{
|
||||
taskCategoryID: TASK_TYPE_DESTROY,
|
||||
typeID,
|
||||
entityTypeID
|
||||
}];
|
||||
|
||||
for (questID in player.quests.list) player.quests.list[questID].adjustTask(toBeAdjusted, 1);
|
||||
|
||||
if (this.reward) {
|
||||
const playerInventory = player.inventory.getPosessions();
|
||||
|
||||
switch (this.reward.distributionType) {
|
||||
case 1:
|
||||
this.reward.multiple({
|
||||
playerInventory,
|
||||
damage: this.damageReceived,
|
||||
maximum: this.totalDamageReceived,
|
||||
lastShotAuthorID: authorID
|
||||
}, (ids, rewards) => this.rewardCallback({ isRegular: true, authorID: ids.id, source, rewards }));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
this.reward.single(1, playerInventory, rewards => this.rewardCallback({ isRegular: true, authorID, source, rewards }));
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
const playerInventory = player.inventory.getPosessions();
|
||||
|
||||
if (isAdmiral) return admiralTable.descreaseAdmiralCount(id);
|
||||
|
||||
const isAdmiralSpawnReady = parentEntityTypeID ? admiralTable.increaseCounter(ids) : false;
|
||||
return isAdmiralSpawnReady;
|
||||
};
|
||||
|
||||
module.exports = globalDieCallback;
|
||||
|
||||
//const globalPoints = Math.floor(player.statusPoints[0] / 10000 + player.statusPoints[1] / 2000 + player.statusPoints[2] / 2000);
|
||||
//if (globalPoints) leaderboards[0].updatePosition(authorID, globalPoints, true);
|
||||
/*
|
||||
|
||||
let aa = new Array(amounts.length);
|
||||
setTimeout(async () => {
|
||||
const q = await execute("SELECT * FROM leaderboard_1 ORDER BY amount DESC");
|
||||
const batches = [];
|
||||
|
||||
const values = q.data;
|
||||
let totalSize = 0;
|
||||
|
||||
for (let i = 0; i < percentages.length; i++) { // this loop is for slicing based on percentages
|
||||
if (values.length - totalAmount < 0) {
|
||||
batches.push([]);
|
||||
continue;
|
||||
};
|
||||
|
||||
const batchSize = (values.length - totalAmount) * percentages[i];
|
||||
const batch = values.slice(totalSize, totalSize + batchSize);
|
||||
batches.push(batch);
|
||||
|
||||
totalSize += batchSize;
|
||||
};
|
||||
|
||||
let rest = values.slice(totalSize, totalSize + totalAmount);
|
||||
require("../Miscellaneous/winston").error(`Total length: ${values.length} (these are from DB, raw)`)
|
||||
|
||||
require("../Miscellaneous/winston").error(`Rest length: ${rest.length} (these are based on fixed amounts instead of percentages)`)
|
||||
|
||||
let x = 0;
|
||||
|
||||
for (let i = 0; i < amounts.length; i++) {
|
||||
const batchSize = amounts[i];
|
||||
const batch = rest.slice(totalAmount - (totalAmount - x), totalAmount - (totalAmount - x - batchSize));
|
||||
require("../Miscellaneous/winston").error(`amounts array index: ${i} | slicing between: ${totalAmount - (totalAmount - x)} - ${totalAmount - (totalAmount - x - batchSize)}`)
|
||||
aa[amounts.length - 1 - i] = batch;
|
||||
|
||||
x += batchSize;
|
||||
};
|
||||
|
||||
batches.push(...aa);
|
||||
|
||||
const thresholds = new Array(batches.length);
|
||||
|
||||
for (let j = 0; j < batches.length; j++) {
|
||||
const batch = batches[j];
|
||||
const lastElemWithValue = batch.filter(e => e.id);
|
||||
require("../Miscellaneous/winston").error(`Batch ${j}: length is ${batch.length}, last elem with value: ${lastElemWithValue[lastElemWithValue.length - 1]?.id} ${lastElemWithValue[lastElemWithValue.length - 1]?.amount}`)
|
||||
|
||||
thresholds[j] = lastElemWithValue[lastElemWithValue.length - 1] ? lastElemWithValue[lastElemWithValue.length - 1].amount : 0;
|
||||
|
||||
for (let k = 0; k < batch.length; k++)
|
||||
db.execute("UPDATE playerdata SET globalrank = ? WHERE playerID = ?", [j, batch[k].id]);
|
||||
};
|
||||
|
||||
global.thresholds = thresholds;
|
||||
|
||||
require("../Miscellaneous/winston").error(thresholds);
|
||||
}, 2000)
|
||||
*/
|
||||
@@ -0,0 +1,183 @@
|
||||
const RewardPacket = require("../Packets/Reward");
|
||||
const DesignChangedPacket = require("../Packets/DesignChanged");
|
||||
const LevelUpPacket = require("../Packets/LevelUp");
|
||||
const GuildDataEconomyDonatedPacket = require("../Packets/GuildDataEconomyDonated");
|
||||
|
||||
const StatisticsWorker = global.StatisticsWorker;
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
const { players } = Game;
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const leaderboards = require("../Data/leaderboard");
|
||||
const eliteRewards = require("../Data/eliteRewards");
|
||||
|
||||
const [playerEconomy, guildEconomy] = require("../Data/batchSave");
|
||||
|
||||
const { CATEGORIES } = constants;
|
||||
const { CATEGORY_ECONOMY, CATEGORY_DESIGN, CATEGORY_STATUS, CATEGORY_RANKING_POINTS } = CATEGORIES;
|
||||
|
||||
const statusMappedToLeaderboard = {
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3
|
||||
};
|
||||
|
||||
const EXP_GUILD_ALLOWED_SOURCES = {
|
||||
2: true,
|
||||
3: true
|
||||
};
|
||||
|
||||
async function rewardCallback(reward) {
|
||||
if (!reward) return;
|
||||
|
||||
const player = players[reward.authorID];
|
||||
if (!player) return;
|
||||
|
||||
const initializedPacket = RewardPacket.init(reward.isRegular, reward.rewards.length);
|
||||
const initializedGuildPacket = RewardPacket.init(true, 1);
|
||||
let designRewardID = 0;
|
||||
|
||||
reward.rewards.forEach(r => {
|
||||
if (r.categoryID === CATEGORY_STATUS) {
|
||||
if (player.guild.id && r.id === 1 && EXP_GUILD_ALLOWED_SOURCES[reward.source.typeID]) {
|
||||
const newLevel = player.guild.levelUpdate(r.amount);
|
||||
|
||||
guildEconomy.accumulateItem(`${player.guild.id}-${r.categoryID}-${r.id}`, r.amount);
|
||||
|
||||
const guildExperienceReward = {
|
||||
categoryID: 9,
|
||||
id: r.id,
|
||||
amount: r.amount
|
||||
};
|
||||
|
||||
initializedGuildPacket.update(guildExperienceReward);
|
||||
const guildRewardPacket = initializedGuildPacket.get();
|
||||
|
||||
const topic = Game.getTopic(player.guild.topic);
|
||||
topic.push(guildRewardPacket);
|
||||
|
||||
if (newLevel !== -1) {
|
||||
const levelUp = {
|
||||
level: newLevel,
|
||||
statusLevelID: 4,
|
||||
id: player.id
|
||||
};
|
||||
|
||||
const levelUpPacket = LevelUpPacket(levelUp);
|
||||
topic.push(levelUpPacket);
|
||||
|
||||
/*player.emit("levelUp", {
|
||||
level: newLevel,
|
||||
statusLevelID: 4,
|
||||
id: player.id
|
||||
});*/
|
||||
};
|
||||
};
|
||||
|
||||
const leaderboardID = statusMappedToLeaderboard[r.id];
|
||||
const leaderboard = leaderboards[leaderboardID];
|
||||
if (leaderboard) leaderboard.updatePosition(player.id, r.amount);
|
||||
|
||||
player.updateLevels(r.id, r.amount);
|
||||
}// else if (r.categoryID === CATEGORY_RANKING_POINTS) player.updateRankingPoints(r.id, r.amount);
|
||||
else player.inventory.refreshSingle(r);
|
||||
|
||||
if (r.categoryID === CATEGORY_ECONOMY) {
|
||||
if (player.guild.id) {
|
||||
let guildTax = 0;
|
||||
|
||||
switch (r.id) {
|
||||
case 1:
|
||||
guildTax = player.guild.taxRates[0];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
guildTax = player.guild.taxRates[1];
|
||||
break;
|
||||
};
|
||||
|
||||
const guildTaxAmount = Math.floor(r.amount * guildTax);
|
||||
const playerRewardAmount = r.amount - guildTaxAmount;
|
||||
console.info("guild tax reward", guildTaxAmount, r.categoryID, r.id);
|
||||
player.guild.bank.depositTax({
|
||||
categoryID: r.categoryID,
|
||||
id: r.id,
|
||||
amount: guildTaxAmount
|
||||
});
|
||||
|
||||
const guildEconomyDonated = {
|
||||
id: r.id,
|
||||
amount: guildTaxAmount
|
||||
};
|
||||
|
||||
const guildEconomyDonatedPacket = GuildDataEconomyDonatedPacket(guildEconomyDonated);
|
||||
|
||||
const topic = Game.getTopic(player.guild.topic);
|
||||
topic.push(guildEconomyDonatedPacket);
|
||||
|
||||
if (guildTax) guildEconomy.accumulateItem(`${player.guild.id}-${r.categoryID}-${r.id}`, guildTaxAmount);
|
||||
|
||||
r.amount -= guildTaxAmount;
|
||||
};
|
||||
};
|
||||
|
||||
if (r.categoryID === CATEGORY_DESIGN && !designRewardID) designRewardID = r.id;
|
||||
console.info("reward amount", r.amount)
|
||||
playerEconomy.accumulateItem(`${player.id}-${r.categoryID}-${r.id}`, r.amount);
|
||||
initializedPacket.update(r);
|
||||
|
||||
const now = new Date();
|
||||
|
||||
StatisticsWorker.postMessage({
|
||||
id: 1,
|
||||
data: {
|
||||
timestamp: now,
|
||||
playerID: player.id,
|
||||
categoryID: r.categoryID,
|
||||
id: r.id,
|
||||
amount: r.amount,
|
||||
source: {
|
||||
typeID: reward.source.typeID,
|
||||
entityTypeID: reward.source.entityTypeID,
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const packet = initializedPacket.get();
|
||||
player.map.registerIndividualNetworkPacket(player.id, packet);
|
||||
|
||||
if (designRewardID) {
|
||||
player.textureID = designRewardID;
|
||||
|
||||
const designReward = {
|
||||
typeID: player.typeID,
|
||||
id: player.id,
|
||||
designID: designRewardID
|
||||
};
|
||||
|
||||
const packet = DesignChangedPacket(designReward);
|
||||
player.map.registerNetworkPacket(player.id, packet);
|
||||
|
||||
if (designRewardID === 5) {
|
||||
for (let i = 1, length = player.eliteLevel; i < length; i++) {
|
||||
const currentEliteLevelRewards = eliteRewards[i];
|
||||
if (currentEliteLevelRewards) {
|
||||
currentEliteLevelRewards.single(0, player.inventory.getPosessions(), rewards => player.rewardCallback({ isRegular: true, authorID: player.id, source: {}, rewards }));
|
||||
|
||||
player.inventory.resourceSharedLimiter.cannon.quantity.maximum += 2;
|
||||
};
|
||||
};
|
||||
|
||||
const ePropertyChanged = {
|
||||
typeID: 5,
|
||||
value: player.inventory.resourceSharedLimiter.cannon.quantity.maximum
|
||||
};
|
||||
|
||||
player.emit("propertyChanged", ePropertyChanged, player.id);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = rewardCallback;
|
||||
@@ -0,0 +1,191 @@
|
||||
const GAME_TICKRATE = 1000 / 30;
|
||||
const INACTIVITY_THRESHOLD_MS = 5 * 60000;
|
||||
|
||||
let looseAccumulator = 0;
|
||||
let accumulator = 0;
|
||||
let difference = 0;
|
||||
let n = performance.now();
|
||||
let nn = Date.now();
|
||||
let batch = {};
|
||||
let isLooseIteration = false;
|
||||
|
||||
const packet = require("./Packets/packet");
|
||||
|
||||
const Game = require("./Game/Game");
|
||||
|
||||
const getFixedDate = require("./Utility/getFixedDate");
|
||||
|
||||
const packetRelevancies = require("./Data/packetRelevancies");
|
||||
const constants = require("./Data/constants");
|
||||
const { TIMING_EVENTS_BY_ID } = constants;
|
||||
|
||||
function gameLoop() {
|
||||
difference = performance.now() - n;
|
||||
accumulator += difference;
|
||||
n = performance.now();
|
||||
nn = Date.now();
|
||||
|
||||
if (accumulator > GAME_TICKRATE) {
|
||||
Game.maps.forEach(map => {
|
||||
map.eventAuthors.forEach(id => {
|
||||
const currentFlush = map.eventBuffer[id];
|
||||
const entity = map.getEntityFromSubgroup(currentFlush.target);
|
||||
if (entity) entity.emit("attacking", currentFlush.data, currentFlush.author);
|
||||
});
|
||||
|
||||
if (looseAccumulator === 15) {
|
||||
looseAccumulator = 0;
|
||||
isLooseIteration = true;
|
||||
};
|
||||
|
||||
map.players.forEach(player => {
|
||||
if (isLooseIteration) {
|
||||
player.isInactive = player.lastInteractionTimestamp + INACTIVITY_THRESHOLD_MS < n;
|
||||
|
||||
player.refreshVisibilities(false);
|
||||
|
||||
if (player.isInactive) player.setIndependentEvent(4, 0);
|
||||
};
|
||||
|
||||
if (player.hasUnexpectedlyLeft) return;
|
||||
|
||||
player.flush(1);
|
||||
|
||||
batch.packet = null;
|
||||
batch.offset = 0;
|
||||
|
||||
const localPacketsLength = map.networkPacketsLocal.length;
|
||||
const individual = map.networkPacketsInvidiual[player.id];
|
||||
|
||||
if (!localPacketsLength && individual)
|
||||
for (let i = 0, length = individual.length; i < length; i++) batch = packet(individual[i].packet, batch, player.id, individual[i].metadata);
|
||||
|
||||
if (localPacketsLength)
|
||||
for (let i = 0, j = 0; i < localPacketsLength; i++) {
|
||||
const p = map.networkPacketsLocal[i];
|
||||
const relevancyRequirement = packetRelevancies[p.header];
|
||||
if (relevancyRequirement(player, p)) batch = packet(p.packet, batch, player.id, p.metadata);
|
||||
|
||||
if (individual)
|
||||
while (j < individual.length && (i === individual[j].index || i === localPacketsLength - 1)) {
|
||||
batch = packet(individual[j].packet, batch, player.id, individual[j].metadata);
|
||||
j++;
|
||||
};
|
||||
};
|
||||
|
||||
for (let i = 0, length = map.testPackets.length; i < length; i++) {
|
||||
const p = map.testPackets[i];
|
||||
const relevancyRequirement = packetRelevancies[p.header];
|
||||
if (relevancyRequirement(player, p)) batch = packet(p.packet, batch, player.id, p.metadata);
|
||||
};
|
||||
|
||||
for (let i = 0, length = map.networkPacketsGlobal.length; i < length; i++) {
|
||||
const p = map.networkPacketsGlobal[i];
|
||||
batch = packet(p.packet, batch, player.id, p.metadata);
|
||||
};
|
||||
|
||||
if (player.topics) {
|
||||
player.topics.each((key, value) => {
|
||||
for (let i = 0, length = value.length; i < length; i++) {
|
||||
const p = value[i];
|
||||
batch = packet(p, batch, player.id);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
if (batch.offset) {
|
||||
const packet = batch.packet.slice(0, batch.offset);
|
||||
const session = socket.sockets[player.sessionID];
|
||||
session.send(packet, true, true);
|
||||
};
|
||||
|
||||
if (individual) individual.length = 0;
|
||||
});
|
||||
|
||||
map.testPackets.length = 0;
|
||||
map.networkPacketsGlobal.length = 0;
|
||||
|
||||
map.eventAuthors.length = 0;
|
||||
map.eventBuffer = {};
|
||||
|
||||
map.packetCount = map.networkPacketsLocal.length;
|
||||
map.networkPacketsLocal.length = 0;
|
||||
|
||||
if (isLooseIteration) {
|
||||
map.towers.forEach(tower => {
|
||||
if (!tower.searchedTarget) return;
|
||||
|
||||
tower.initializeAttackWithCannon(tower.searchedTarget);
|
||||
tower.searchedTarget = null;
|
||||
});
|
||||
|
||||
map.lookingForTarget.forEach(e => e.initializeAttackWithCannon(e.searchedTarget));
|
||||
map.lookingForTarget.length = 0;
|
||||
|
||||
map.dirtyChunks.forEach(c => {
|
||||
c.map.clear();
|
||||
c.head = null;
|
||||
c.tail = null;
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
Game.packets.each((_, value) => {
|
||||
if (value.length > 0) console.info("Topic length", value.length, "All topics length", Game.packets.map.size)
|
||||
value.length = 0
|
||||
});
|
||||
|
||||
const closest = Game.events.peek();
|
||||
if (closest && n >= closest.value)
|
||||
Game.events.forEach(entry => {
|
||||
if (n < entry.value) return false;
|
||||
|
||||
const event = Game.events.poll();
|
||||
const { id, entity, index, getLocalization, data, isSingle } = event.obj;
|
||||
const mapID = getLocalization();
|
||||
const map = Game.maps[mapID - 1];
|
||||
const entityInstance = map.getEntityFromSubgroup(entity.id);
|
||||
|
||||
const eventExists = entityInstance && entityInstance.events[id] && entityInstance.events[id][index];
|
||||
const isCancelled = eventExists && entityInstance.events[id][index].isCancelled;
|
||||
|
||||
if (isSingle && eventExists) entityInstance.events[id][index].isOver = true;
|
||||
|
||||
if (!isCancelled && eventExists) {
|
||||
const methodName = TIMING_EVENTS_BY_ID[id];
|
||||
entityInstance[methodName](data);
|
||||
};
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const closestFixed = Game.eventsFixed.peek();
|
||||
if (closestFixed && nn >= closestFixed.value)
|
||||
Game.eventsFixed.forEach(entry => {
|
||||
if (nn < entry.value) return false;
|
||||
|
||||
const event = Game.eventsFixed.poll();
|
||||
|
||||
const { fn, arguments, next } = event.obj;
|
||||
|
||||
fn(...arguments);
|
||||
|
||||
if (next) {
|
||||
const nextRunDate = getFixedDate(30);
|
||||
next(nextRunDate, event.obj);
|
||||
};
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
isLooseIteration = false;
|
||||
looseAccumulator++;
|
||||
|
||||
accumulator -= GAME_TICKRATE;
|
||||
};
|
||||
|
||||
if (difference < GAME_TICKRATE - 32) setTimeout(() => gameLoop());
|
||||
else setImmediate(() => gameLoop());
|
||||
};
|
||||
|
||||
module.exports = gameLoop;
|
||||
@@ -0,0 +1,35 @@
|
||||
const AdmiralCounter = require("../Game/AdmiralCounter");
|
||||
|
||||
const admiralTable = new AdmiralCounter({
|
||||
2: {
|
||||
2: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
4: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
6: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
8: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
10: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
12: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
14: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
16: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
18: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
20: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
22: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
24: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
26: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
28: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
30: { childToBeKilledCount: 2, maximumAdmiralCount: 2 }
|
||||
},
|
||||
3: {
|
||||
2: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
4: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
6: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
8: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
10: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
12: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
14: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
16: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
18: { childToBeKilledCount: 2, maximumAdmiralCount: 2 },
|
||||
20: { childToBeKilledCount: 2, maximumAdmiralCount: 2 }
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = admiralTable;
|
||||
@@ -0,0 +1,15 @@
|
||||
const ammunitions = {
|
||||
1: { damage: 1 },
|
||||
2: { damage: 2 },
|
||||
3: { damage: 3 },
|
||||
4: { damage: 4 },
|
||||
5: { damage: 5 },
|
||||
6: { damage: 6 },
|
||||
7: { damage: 7, overtimeHealthEffectID: 1 },
|
||||
8: { damage: 8, chanceToTerminateMovement: 20 },
|
||||
9: { damage: -9 },
|
||||
10: { damage: -10 },
|
||||
11: { damage: 11 }
|
||||
};
|
||||
|
||||
module.exports = ammunitions;
|
||||
@@ -0,0 +1,5 @@
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
const saveLists = [new DoubleLinkedList(), new DoubleLinkedList()];
|
||||
|
||||
module.exports = saveLists;
|
||||
@@ -0,0 +1,34 @@
|
||||
const cannons = {
|
||||
1: {
|
||||
range: 150, damage: 1, reload: 6500, scatter: 0
|
||||
},
|
||||
2: {
|
||||
range: 200, damage: 3, reload: 5750, scatter: 0
|
||||
},
|
||||
3: {
|
||||
range: 250, damage: 5, reload: 5000, scatter: 0
|
||||
},
|
||||
4: {
|
||||
range: 300, damage: 8, reload: 4500, scatter: 0
|
||||
},
|
||||
5: {
|
||||
range: 350, damage: 11, reload: 4000, scatter: 0
|
||||
},
|
||||
6: {
|
||||
range: 400, damage: 10, reload: 4000, scatter: 0
|
||||
},
|
||||
7: {
|
||||
range: 400, damage: 11, reload: 3500, scatter: 0
|
||||
},
|
||||
8: {
|
||||
range: 450, damage: 11, reload: 3500, scatter: 0
|
||||
},
|
||||
9: {
|
||||
range: 350, damage: 4, reload: 1000, scatter: 0
|
||||
},
|
||||
10: {
|
||||
range: 500, damage: 15, reload: 3000, scatter: 0
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = cannons;
|
||||
@@ -0,0 +1,70 @@
|
||||
const cannonsSchemeNPC = {
|
||||
1: [
|
||||
[1, 20]
|
||||
],
|
||||
2: [
|
||||
[1, 1],
|
||||
[4, 2],
|
||||
[8, 3],
|
||||
[10, 400]
|
||||
],
|
||||
3: [
|
||||
[10, 30]
|
||||
],
|
||||
4: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 300]
|
||||
],
|
||||
5: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 3]
|
||||
],
|
||||
6: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 300]
|
||||
],
|
||||
7: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 3]
|
||||
],
|
||||
8: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 300]
|
||||
],
|
||||
9: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 30]
|
||||
],
|
||||
10: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 30]
|
||||
],
|
||||
11: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 3]
|
||||
],
|
||||
12: [
|
||||
[1, 10],
|
||||
[4, 5],
|
||||
[8, 15],
|
||||
[10, 30]
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = cannonsSchemeNPC;
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
MAXIMUM VALUES, DO NOT CHANGE AT ALL
|
||||
*/
|
||||
const MAX_SAFE_INTEGER = Math.pow(2, 32) - 1;
|
||||
const MAX_LEVEL_ELITE = 18;
|
||||
const MAX_GUILD_TAX_RATE_GOLD = 30;
|
||||
const MAX_GUILD_TAX_RATE_EMERALD = 10;
|
||||
|
||||
/*
|
||||
TYPE OF ENTITES
|
||||
*/
|
||||
const ENTITY_TYPE_PLAYER = 1;
|
||||
const ENTITY_TYPE_NPC = 2;
|
||||
const ENTITY_TYPE_MONSTER = 3;
|
||||
const ENTITY_TYPE_TOWER = 4;
|
||||
const ENTITY_TYPE_COLLECTABLE = 5;
|
||||
|
||||
/*
|
||||
ECONOMY
|
||||
*/
|
||||
const ECONOMY_GOLD = 1;
|
||||
const ECONOMY_EMERALD = 2;
|
||||
|
||||
/*
|
||||
UNATTACKABLE ENTITIES
|
||||
*/
|
||||
const UNATTACKABLE_ENTITY_TYPES = [ENTITY_TYPE_COLLECTABLE];
|
||||
|
||||
/*
|
||||
EVENTS
|
||||
*/
|
||||
const EVENT_DISCONNECT = "disconnect";
|
||||
const EVENT_DISCONNECT_CANCEL = "disconnectCancel"
|
||||
const EVENT_DISCONNECT_IDLE = "disconnectIdle"
|
||||
const EVENT_RESPAWN = "respawn";
|
||||
const EVENT_MAP_CHANGED = "mapChanged";
|
||||
const EVENT_PROPERTY_CHANGED = "propertyChanged";
|
||||
const EVENT_ATTACK_STARTED = "attackStarted";
|
||||
const EVENT_ATTACKING = "attacking";
|
||||
const EVENT_DIE = "die";
|
||||
const EVENT_LEVEL_UP = "levelUp";
|
||||
const EVENT_SPEED_UPDATE = "speedUpdate";
|
||||
const EVENT_ATTACK_ABORTED_BY_USER = "attackAbortedByUser";
|
||||
const EVENT_STOP_MOVEMENT = "stopMovement";
|
||||
const EVENT_CANCEL_STATE_CHANGE_TIMEOUT = "cancelStateChangeTimeout";
|
||||
const EVENT_ABLE_TO_JUMP = "ableToJump";
|
||||
const EVENT_UNABLE_TO_JUMP = "unableToJump";
|
||||
const EVENT_LEVEL_IS_LOW = "levelIsLow";
|
||||
const EVENT_UPDATE_VISIBILITY = "updateVisibility";
|
||||
const EVENT_DESTINATION_REACHED = "destinationReached";
|
||||
const EVENT_DESTINATION_NEW = "newDestination";
|
||||
const EVENT_SEEK_TARGET = "seekTarget";
|
||||
const EVENT_ADD_EVENTS = "addEvents";
|
||||
const EVENT_READY = "ready";
|
||||
const EVENT_DISTRIBUTE_REWARD = "distributeReward";
|
||||
const EVENT_DAMAGE = "damage";
|
||||
const EVENT_ITEM_USED = "itemUsed";
|
||||
const EVENT_CANNONS_USED = "cannonsUsed";
|
||||
const EVENT_HARPOONERS_USED = "harpoonersUsed";
|
||||
const EVENT_QUEST_DONE = "questDone";
|
||||
const EVENT_EFFECT_ADD = "effectAdd";
|
||||
const EVENT_DESINATION_NEW = "newDestination";
|
||||
const EVENT_ATTACK_BAR = "attackBar";
|
||||
const EVENT_PATH_READY = "scheduledPathReady"
|
||||
const EVENT_SPAWN_ADMIRAL = "admiralSpawnNotification";
|
||||
const EVENT_HEALTH_CHANGED = "healthChanged";
|
||||
const EVENT_HEALTH_CHANGED_AUTHORLESS = "healthChangedAuthorless";
|
||||
const EVENT_GUILD_ISLAND_CAPTURE = "guildIslandCapture";
|
||||
const EVENT_GUILD_ISLAND_TOWER_BUILT = "guildDataIslandTowerBuilt";
|
||||
const EVENT_GUILD_ISLAND_TOWER_HEALTH_CHANGED = "guildDataIslandTowerHealthChanged";
|
||||
|
||||
/*
|
||||
BEHAVIOUR
|
||||
*/
|
||||
const BEHAVIOUR_AGGRESSIVE = "aggressive";
|
||||
const BEHAVIOUR_STOP_IF_ATTACKED = "stopIfAttacked";
|
||||
|
||||
/*
|
||||
MODE OF MAPS
|
||||
*/
|
||||
const MAP_MODE_PVE = "PvE";
|
||||
const MAP_MODE_PVP = "PvP";
|
||||
|
||||
/*
|
||||
LOGOUT TIMEOUTS
|
||||
*/
|
||||
const LOGOUT_TIME_BASIC = 30 * 1000;
|
||||
const LOGOUT_TIME_PREMIUM = 10 * 1000;
|
||||
const LOGOUT_TIME_IDLE = 10 * 60 * 10000;
|
||||
|
||||
/*
|
||||
BASE VALUES FOR CALCULATIONS
|
||||
*/
|
||||
const BASE_REPAIR_AMOUNT_MINIMUM = 1000;
|
||||
const BASE_REPAIR_AMOUNT_MAXIMUM = 2000;
|
||||
const BASE_SPEED_PLAYER_REPAIR = 1500;
|
||||
const BASE_SPEED_PLAYER = 100;
|
||||
|
||||
/*
|
||||
TASKS
|
||||
*/
|
||||
const TASK_TYPE_DEAL_DAMAGE = 1;
|
||||
const TASK_TYPE_DESTROY = 2;
|
||||
|
||||
/*
|
||||
TIMING EVENTS
|
||||
*/
|
||||
const TIMING_EVENT_CANNON_ATTACK = 1;
|
||||
const TIMING_EVENT_HARPOONER_ATTACK = 2;
|
||||
const TIMING_EVENT_REPAIR = 3;
|
||||
const TIMING_EVENT_DISCONNECT = 4;
|
||||
const TIMING_EVENT_DISCONNECT_IDLE = 5;
|
||||
const TIMING_EVENT_MAP_CHANGE = 6;
|
||||
const TIMING_EVENT_MOVE = 7;
|
||||
const TIMING_EVENT_RESPAWN = 8;
|
||||
const TIMING_EVENT_OVERTIME_DECREASE_HITPOINT = 9;
|
||||
const TIMING_EVENT_OVERTIME_INCREASE_HITPOINT = 10;
|
||||
const TIMING_EVENT_DIE = 11;
|
||||
const TIMING_EVENT_OVERTIME_DECREASE_HITPOINT_2 = 12;
|
||||
|
||||
/*
|
||||
ACQUIRABLE ITEM CATEGORIES
|
||||
*/
|
||||
const CATEGORY_ECONOMY = 1;
|
||||
const CATEGORY_ITEM = 2;
|
||||
const CATEGORY_AMMUNITION = 3;
|
||||
const CATEGORY_HARPOON = 4;
|
||||
const CATEGORY_DESIGN = 5;
|
||||
const CATEGORY_STATUS = 6;
|
||||
const CATEGORY_CANNON = 7;
|
||||
const CATEGORY_HARPOONER = 8;
|
||||
const CATEGORY_SAIL = 9;
|
||||
const CATEGORY_PIRATE = 10;
|
||||
const CATEGORY_RANKING_POINTS = 11;
|
||||
|
||||
/*
|
||||
QUEST STATUS
|
||||
*/
|
||||
const QUEST_STATE_ACTIVE = 0;
|
||||
const QUEST_STATE_COMPLETED = 0;
|
||||
|
||||
/*
|
||||
PROPERTY ALTERING CATEGORIES
|
||||
*/
|
||||
const AFFECT_CANNON_DAMAGE = 1;
|
||||
const AFFECT_HARPOONER_DAMAGE = 2;
|
||||
const AFFECT_PROTECTION = 3;
|
||||
const AFFECT_SAILING_SPEED = 4;
|
||||
const AFFECT_HITPOINT = 5;
|
||||
|
||||
/*
|
||||
GUILD PERMISSION BITS
|
||||
*/
|
||||
const GUILD_PERMISSION_KICK = 1;
|
||||
const GUILD_PERMISSION_ACCEPT_JOIN_REQUEST = 2;
|
||||
const GUILD_PERMISSION_EDIT_IDENTITY = 4;
|
||||
const GUILD_PERMISSION_EDIT_TAX = 8;
|
||||
const GUILD_PERMISSION_EDIT_ISLAND = 16;
|
||||
|
||||
/*
|
||||
LEADERBOARD TYPES
|
||||
*/
|
||||
const LEADERBOARD_EXPERIENCE_POINTS = 1;
|
||||
const LEADERBOARD_NPC_SINK = 2;
|
||||
const LEADERBOARD_MONSTER_SINK = 3;
|
||||
const LEADERBOARD_PLAYER_SINK = 4;
|
||||
const LEADERBOARD_ELITE_POINTS = 5;
|
||||
const LEADERBOARD_FISHING_POINTS = 6;
|
||||
|
||||
/*
|
||||
GUILD COOLDOWN TIMES
|
||||
*/
|
||||
const GUILD_COOLDOWN_EDIT_TAX_RATES = 60000;
|
||||
const GUILD_COOLDOWN_EDIT_TAG = 60000;
|
||||
const GUILD_COOLDOWN_EDIT_NAME = 60000;
|
||||
const GUILD_COOLDOWN_EDIT_DESCRIPTION = 60000;
|
||||
|
||||
const COLLECTIBLE_CURRENCIES_BY_ID = {
|
||||
1: "gold",
|
||||
2: "emerald",
|
||||
3: "draconicScales",
|
||||
4: "corsaireCoins"
|
||||
};
|
||||
|
||||
const COLLECTIBLE_CURRENCIES_BY_NAME = {
|
||||
gold: 1,
|
||||
emerald: 2,
|
||||
draconicScales: 3,
|
||||
corsaireCoins: 4
|
||||
};
|
||||
|
||||
const ENTITY_TYPES_BY_ID = {
|
||||
1: ENTITY_TYPE_PLAYER,
|
||||
2: ENTITY_TYPE_NPC,
|
||||
3: ENTITY_TYPE_MONSTER,
|
||||
4: ENTITY_TYPE_TOWER,
|
||||
5: ENTITY_TYPE_COLLECTABLE
|
||||
};
|
||||
|
||||
const TIMING_EVENTS_BY_ID = {
|
||||
1: "attackWithCannon",
|
||||
2: "attackWithHarpooner",
|
||||
3: "repair",
|
||||
4: "disconnect",
|
||||
5: "disconnect",
|
||||
6: "isChangeMapAvailable",
|
||||
7: "move",
|
||||
8: "respawn",
|
||||
9: "overtimeDecreaseCurrentHitpoint",
|
||||
10: "overtimeIncreaseCurrentHitpoint",
|
||||
11: "die",
|
||||
12: "overtimeDecreaseCurrentHitpoint",
|
||||
13: "overtimeDecreaseCurrentHitpoint",
|
||||
14: "respawn",
|
||||
15: "changeMap",
|
||||
16: "abortAttack",
|
||||
17: "effectRemove",
|
||||
97: "attackWithCannon",
|
||||
98: "attackWithCannon",
|
||||
99: "attackWithCannon",
|
||||
100: "attackWithCannon",
|
||||
101: "attackWithCannon",
|
||||
102: "attackWithCannon",
|
||||
103: "attackWithCannon",
|
||||
104: "attackWithCannon",
|
||||
105: "attackWithCannon",
|
||||
106: "attackWithCannon",
|
||||
197: "attackWithHarpooner",
|
||||
198: "attackWithHarpooner",
|
||||
199: "attackWithHarpooner",
|
||||
200: "attackWithHarpooner",
|
||||
201: "attackWithHarpooner",
|
||||
202: "attackWithHarpooner",
|
||||
299: "changeSpeed",
|
||||
300: "changeSpeed",
|
||||
999: "refreshVisibilities"
|
||||
};
|
||||
|
||||
const EVENTS_BY_TIMING_EVENTS_ID = {
|
||||
1: EVENT_ATTACKING,
|
||||
2: EVENT_ATTACKING
|
||||
};
|
||||
|
||||
const EFFECTS_TERMINATE_MOVEMENT = [1];
|
||||
|
||||
module.exports.MAXIMUM = {
|
||||
MAX_SAFE_INTEGER, MAX_LEVEL_ELITE, MAX_GUILD_TAX_RATE_GOLD, MAX_GUILD_TAX_RATE_EMERALD
|
||||
};
|
||||
|
||||
module.exports.TYPES = {
|
||||
ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_MONSTER, ENTITY_TYPE_TOWER, ENTITY_TYPE_COLLECTABLE
|
||||
};
|
||||
|
||||
module.exports.UNATTACKABLE_ENTITY_TYPES = UNATTACKABLE_ENTITY_TYPES;
|
||||
|
||||
module.exports.EVENTS = {
|
||||
EVENT_DISCONNECT, EVENT_DISCONNECT_CANCEL, EVENT_DISCONNECT_IDLE, EVENT_RESPAWN, EVENT_MAP_CHANGED, EVENT_PROPERTY_CHANGED, EVENT_ATTACK_STARTED, EVENT_ATTACKING, EVENT_DIE, EVENT_SPEED_UPDATE,
|
||||
EVENT_ATTACK_ABORTED_BY_USER, EVENT_STOP_MOVEMENT, EVENT_CANCEL_STATE_CHANGE_TIMEOUT,
|
||||
EVENT_ABLE_TO_JUMP, EVENT_UNABLE_TO_JUMP, EVENT_LEVEL_IS_LOW, EVENT_UPDATE_VISIBILITY, EVENT_DESTINATION_REACHED,
|
||||
EVENT_DESTINATION_NEW, EVENT_SEEK_TARGET, EVENT_LEVEL_UP, EVENT_ADD_EVENTS, EVENT_READY, EVENT_DISTRIBUTE_REWARD,
|
||||
EVENT_DAMAGE, EVENT_ITEM_USED, EVENT_CANNONS_USED, EVENT_HARPOONERS_USED, EVENT_QUEST_DONE, EVENT_EFFECT_ADD, EVENT_DESINATION_NEW, EVENT_ATTACK_BAR, EVENT_PATH_READY, EVENT_SPAWN_ADMIRAL,
|
||||
EVENT_HEALTH_CHANGED, EVENT_HEALTH_CHANGED_AUTHORLESS, EVENT_GUILD_ISLAND_CAPTURE, EVENT_GUILD_ISLAND_TOWER_BUILT, EVENT_GUILD_ISLAND_TOWER_HEALTH_CHANGED
|
||||
};
|
||||
|
||||
module.exports.BEHAVIOURS = {
|
||||
BEHAVIOUR_AGGRESSIVE, BEHAVIOUR_STOP_IF_ATTACKED
|
||||
};
|
||||
|
||||
module.exports.MAP_MODES = {
|
||||
MAP_MODE_PVE, MAP_MODE_PVP
|
||||
};
|
||||
|
||||
module.exports.LOGOUT_TIMES = {
|
||||
LOGOUT_TIME_BASIC, LOGOUT_TIME_PREMIUM, LOGOUT_TIME_IDLE
|
||||
};
|
||||
|
||||
module.exports.BASE = {
|
||||
BASE_REPAIR_AMOUNT_MINIMUM, BASE_REPAIR_AMOUNT_MAXIMUM, BASE_SPEED_PLAYER_REPAIR, BASE_SPEED_PLAYER
|
||||
};
|
||||
|
||||
module.exports.TASK_TYPES = {
|
||||
TASK_TYPE_DEAL_DAMAGE, TASK_TYPE_DESTROY
|
||||
};
|
||||
|
||||
module.exports.TIMING_EVENTS = {
|
||||
TIMING_EVENT_CANNON_ATTACK, TIMING_EVENT_HARPOONER_ATTACK, TIMING_EVENT_REPAIR, TIMING_EVENT_DISCONNECT, TIMING_EVENT_DISCONNECT_IDLE, TIMING_EVENT_MAP_CHANGE, TIMING_EVENT_MOVE, TIMING_EVENT_RESPAWN,
|
||||
TIMING_EVENT_OVERTIME_DECREASE_HITPOINT, TIMING_EVENT_OVERTIME_INCREASE_HITPOINT, TIMING_EVENT_OVERTIME_DECREASE_HITPOINT_2
|
||||
};
|
||||
|
||||
module.exports.CATEGORIES = {
|
||||
CATEGORY_ECONOMY, CATEGORY_ITEM, CATEGORY_AMMUNITION, CATEGORY_HARPOON, CATEGORY_DESIGN, CATEGORY_STATUS, CATEGORY_CANNON, CATEGORY_HARPOONER, CATEGORY_SAIL, CATEGORY_PIRATE, CATEGORY_RANKING_POINTS
|
||||
};
|
||||
|
||||
module.exports.AFFECTS = {
|
||||
AFFECT_CANNON_DAMAGE, AFFECT_HARPOONER_DAMAGE, AFFECT_PROTECTION, AFFECT_SAILING_SPEED, AFFECT_HITPOINT
|
||||
};
|
||||
|
||||
module.exports.GUILD_PERMISSIONS = {
|
||||
GUILD_PERMISSION_KICK, GUILD_PERMISSION_ACCEPT_JOIN_REQUEST, GUILD_PERMISSION_EDIT_IDENTITY, GUILD_PERMISSION_EDIT_TAX, GUILD_PERMISSION_EDIT_ISLAND
|
||||
};
|
||||
|
||||
module.exports.GUILD_COOLDOWNS = {
|
||||
GUILD_COOLDOWN_EDIT_TAX_RATES, GUILD_COOLDOWN_EDIT_TAG, GUILD_COOLDOWN_EDIT_NAME, GUILD_COOLDOWN_EDIT_DESCRIPTION
|
||||
};
|
||||
|
||||
module.exports.COLLECTIBLE_CURRENCIES_BY_ID = COLLECTIBLE_CURRENCIES_BY_ID;
|
||||
|
||||
module.exports.COLLECTIBLE_CURRENCIES_BY_NAME = COLLECTIBLE_CURRENCIES_BY_NAME;
|
||||
|
||||
module.exports.ENTITY_TYPES_BY_ID = ENTITY_TYPES_BY_ID;
|
||||
|
||||
module.exports.TIMING_EVENTS_BY_ID = TIMING_EVENTS_BY_ID;
|
||||
|
||||
module.exports.EVENTS_BY_TIMING_EVENTS_ID = EVENTS_BY_TIMING_EVENTS_ID;
|
||||
|
||||
module.exports.EFFECTS_TERMINATE_MOVEMENT = EFFECTS_TERMINATE_MOVEMENT;
|
||||
@@ -0,0 +1,62 @@
|
||||
const DESIGNS = {
|
||||
1: true,
|
||||
2: true,
|
||||
3: true,
|
||||
4: true,
|
||||
5: true,
|
||||
6: true,
|
||||
7: true,
|
||||
8: true,
|
||||
9: true,
|
||||
10: true,
|
||||
11: true,
|
||||
12: true,
|
||||
13: true,
|
||||
14: true,
|
||||
15: true,
|
||||
16: true,
|
||||
17: true,
|
||||
18: true,
|
||||
19: true,
|
||||
20: true,
|
||||
21: true,
|
||||
22: true,
|
||||
23: true,
|
||||
24: true,
|
||||
25: true,
|
||||
26: true,
|
||||
27: true,
|
||||
28: true,
|
||||
29: true,
|
||||
30: true,
|
||||
31: true,
|
||||
32: true,
|
||||
33: true,
|
||||
34: true,
|
||||
35: true,
|
||||
36: true,
|
||||
37: true,
|
||||
38: true,
|
||||
39: true,
|
||||
40: true,
|
||||
41: true,
|
||||
42: true,
|
||||
43: true,
|
||||
44: true,
|
||||
45: true,
|
||||
46: true,
|
||||
47: true,
|
||||
48: true,
|
||||
49: true,
|
||||
50: true,
|
||||
51: true,
|
||||
52: true,
|
||||
53: true,
|
||||
54: true,
|
||||
55: true,
|
||||
56: true,
|
||||
57: true,
|
||||
58: true
|
||||
};
|
||||
|
||||
module.exports = DESIGNS;
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
const effects = {
|
||||
1: [1],
|
||||
2: [2],
|
||||
3: [3],
|
||||
4: [4],
|
||||
5: [5],
|
||||
6: [6],
|
||||
7: [7],
|
||||
8: [8]
|
||||
};
|
||||
|
||||
/*
|
||||
Keys are the effectID's. Arrays bounded to them contain itemID's.
|
||||
This is used to activate the items bounded to the effects.
|
||||
It looks redundant: arrays are used so multiple items can be bounded to one effect.
|
||||
*/
|
||||
|
||||
module.exports = effects;
|
||||
@@ -0,0 +1,151 @@
|
||||
const Reward = require("../Game/Reward");
|
||||
|
||||
const eliteRewards = [
|
||||
0,
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
6: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
7: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
8: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
9: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
10: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
11: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
12: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
13: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
14: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
15: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
16: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
17: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
18: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
19: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
20: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
21: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
22: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
5: {
|
||||
23: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
module.exports = eliteRewards;
|
||||
@@ -0,0 +1,77 @@
|
||||
const entityGroup = {
|
||||
id: 1,
|
||||
mapIDs: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
||||
entitySettings: {
|
||||
1: {
|
||||
speed: 600,
|
||||
respawnTime: 0,
|
||||
size: 128
|
||||
},
|
||||
2: {
|
||||
speed: 400,
|
||||
respawnTime: 60,
|
||||
size: 96
|
||||
},
|
||||
3: {
|
||||
speed: 120,
|
||||
respawnTime: 30,
|
||||
size: 64
|
||||
}
|
||||
},
|
||||
structure: [
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 113,
|
||||
groupMemberTypeID: 1
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 113,
|
||||
groupMemberTypeID: 1
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 114,
|
||||
groupMemberTypeID: 2
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 114,
|
||||
groupMemberTypeID: 2
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 114,
|
||||
groupMemberTypeID: 2
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 115,
|
||||
groupMemberTypeID: 3
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 115,
|
||||
groupMemberTypeID: 3
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 115,
|
||||
groupMemberTypeID: 3
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 115,
|
||||
groupMemberTypeID: 3
|
||||
},
|
||||
{
|
||||
typeID: 2,
|
||||
entityTypeID: 115,
|
||||
groupMemberTypeID: 3
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const entityGroups = [entityGroup];
|
||||
|
||||
module.exports = entityGroups;
|
||||
@@ -0,0 +1,22 @@
|
||||
const harpooners = {
|
||||
1: {
|
||||
range: 150 / 4, damage: 1 / 4, reload: 6500 / 4
|
||||
},
|
||||
2: {
|
||||
range: 200 / 4, damage: 3 / 4, reload: 5750 / 4
|
||||
},
|
||||
3: {
|
||||
range: 250 / 4, damage: 5 / 4, reload: 5000 / 4
|
||||
},
|
||||
4: {
|
||||
range: 300 / 4, damage: 8 / 4, reload: 4500 / 4
|
||||
},
|
||||
5: {
|
||||
range: 350 / 4, damage: 11 / 4, reload: 4000 / 4
|
||||
},
|
||||
6: {
|
||||
range: 400 / 4, damage: 10 / 4, reload: 4000 / 4
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = harpooners;
|
||||
@@ -0,0 +1,12 @@
|
||||
const harpoons = {
|
||||
1: { damage: 50 },
|
||||
2: { damage: 250 },
|
||||
3: { damage: 500 },
|
||||
4: { damage: 750 },
|
||||
5: { damage: 1250 },
|
||||
6: { damage: 1500 },
|
||||
7: { damage: 2000 },
|
||||
8: { damage: 2500 }
|
||||
};
|
||||
|
||||
module.exports = harpoons;
|
||||
@@ -0,0 +1,33 @@
|
||||
const constants = require("../Data/constants");
|
||||
const { AFFECTS, MAP_MODES, TYPES } = constants;
|
||||
const { AFFECT_CANNON_DAMAGE, AFFECT_HARPOONER_DAMAGE, AFFECT_PROTECTION, AFFECT_SAILING_SPEED, AFFECT_HITPOINT } = AFFECTS;
|
||||
const { MAP_MODE_PVE, MAP_MODE_PVP } = MAP_MODES;
|
||||
const { ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_TOWER } = TYPES;
|
||||
|
||||
const itemTable = {
|
||||
1: { scale: 1.2, isSelf: true, isDurable: true, affects: [2], range: 0, cooldown: 0, statusID: 1, useOnImpact: false },
|
||||
2: { scale: 1.2, isSelf: true, isDurable: true, affects: [2], range: 0, cooldown: 0, statusID: 2, useOnImpact: false },
|
||||
3: { scale: 1.2, isSelf: true, isDurable: true, affects: [2], range: 0, cooldown: 0, statusID: 3, useOnImpact: false },
|
||||
4: { scale: 1.2, isSelf: true, isDurable: true, affects: [2], range: 0, cooldown: 0, statusID: 4, useOnImpact: false },
|
||||
5: { scale: 0.2, isSelf: true, isDurable: true, affects: [AFFECT_PROTECTION], range: 0, cooldown: 0, statusID: 5, useOnImpact: false },
|
||||
6: { scale: 0.2, isSelf: true, isDurable: true, affects: [AFFECT_PROTECTION], range: 0, cooldown: 0, statusID: 6, useOnImpact: false },
|
||||
7: { scale: 0.2, isSelf: true, isDurable: true, affects: [AFFECT_PROTECTION], range: 0, cooldown: 0, statusID: 7, useOnImpact: false },
|
||||
8: { scale: 0.2, isSelf: true, isDurable: true, affects: [AFFECT_PROTECTION], range: 0, cooldown: 0, statusID: 8, useOnImpact: false },
|
||||
9: { scale: -1000, isSelf: false, isDurable: false, affects: [AFFECT_HITPOINT], range: 700, cooldown: 2000, statusID: 0, useOnImpact: true, allowedTargetTypes: {
|
||||
"PvE": [ENTITY_TYPE_NPC, ENTITY_TYPE_TOWER],
|
||||
"PvP": [ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_TOWER]
|
||||
}},
|
||||
10: { scale: -25000, isSelf: false, isDurable: false, affects: [AFFECT_HITPOINT], range: 700, cooldown: 2000, statusID: 0, useOnImpact: true, allowedTargetTypes: {
|
||||
"PvE": [ENTITY_TYPE_NPC, ENTITY_TYPE_TOWER],
|
||||
"PvP": [ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_TOWER]
|
||||
}},
|
||||
11: { scale: 10000, isSelf: true, isDurable: false, affects: [AFFECT_HITPOINT], range: 0, cooldown: 10000, statusID: 0, useOnImpact: true },
|
||||
12: { scale: 15000, isSelf: true, isDurable: false, affects: [AFFECT_HITPOINT], range: 0, cooldown: 10000, statusID: 0, useOnImpact: true },
|
||||
13: { scale: 16000, isSelf: true, isDurable: false, affects: [AFFECT_HITPOINT], range: 0, cooldown: 10000, statusID: 0, useOnImpact: true },
|
||||
14: { scale: 17000, isSelf: true, isDurable: false, affects: [AFFECT_HITPOINT], range: 0, cooldown: 10000, statusID: 0, useOnImpact: true },
|
||||
15: { scale: 35000, isSelf: true, isDurable: false, affects: [AFFECT_HITPOINT], range: 0, cooldown: 10000, statusID: 0, useOnImpact: true },
|
||||
16: { scale: -12, isSelf: true, isDurable: false, affects: [AFFECT_SAILING_SPEED], range: 0, cooldown: 15000, time: 5000, statusID: 16, iid: 299, useOnImpact: true },
|
||||
17: { scale: -25, isSelf: true, isDurable: false, affects: [AFFECT_SAILING_SPEED], range: 0, cooldown: 30000, time: 10000, statusID: 17, iid: 300, useOnImpact: true }
|
||||
};
|
||||
|
||||
module.exports = itemTable;
|
||||
@@ -0,0 +1,20 @@
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
const leaderboards = [];
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const leaderboard = {
|
||||
cache: {},
|
||||
list: new DoubleLinkedList(),
|
||||
size: 0,
|
||||
entriesNEW: {},
|
||||
updatePosition(id, amount, isAbsolute) {
|
||||
if (isAbsolute) this.list.addItem(id, amount);
|
||||
else this.list.accumulateItem(id, amount);
|
||||
}
|
||||
};
|
||||
|
||||
leaderboards.push(leaderboard);
|
||||
};
|
||||
|
||||
module.exports = leaderboards;
|
||||
@@ -0,0 +1,162 @@
|
||||
const Reward = require("../Game/Reward");
|
||||
|
||||
const levelRewards = [
|
||||
0,
|
||||
0,
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
2: {
|
||||
5: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
1: { amountMinimum: 42, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
1: { amountMinimum: 42, chance: 100 }
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
module.exports = levelRewards;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
const { MAXIMUM } = require("./constants");
|
||||
const { MAX_LEVEL_ELITE } = MAXIMUM;
|
||||
|
||||
const a = 1;
|
||||
const b = -1;
|
||||
|
||||
const levels = {
|
||||
pirateLevel(points) {
|
||||
const c = points * 2 / 250 * -1;
|
||||
|
||||
const discriminant = Math.pow(b, 2) - 4 * a * c;
|
||||
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a);
|
||||
const currentLevel = Math.floor(x1);
|
||||
|
||||
return currentLevel;
|
||||
},
|
||||
eliteLevel(points) {
|
||||
const c = points * 2 / 750 * -1;
|
||||
const discriminant = Math.pow(b, 2) - 4 * a * c;
|
||||
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a);
|
||||
const currentLevel = Math.floor(x1);
|
||||
|
||||
return Math.min(currentLevel, MAX_LEVEL_ELITE);
|
||||
},
|
||||
fishingLevel(points) {
|
||||
const c = points * 2 / 525 * -1;
|
||||
|
||||
const discriminant = Math.pow(b, 2) - 4 * a * c;
|
||||
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a);
|
||||
const currentLevel = Math.floor(x1);
|
||||
|
||||
return currentLevel;
|
||||
},
|
||||
guildLevel(points) {
|
||||
const c = points * 2 / 20 * -1;
|
||||
|
||||
const discriminant = Math.pow(b, 2) - 4 * a * c;
|
||||
const x1 = (-b + Math.sqrt(discriminant)) / (2 * a);
|
||||
const currentLevel = Math.floor(x1);
|
||||
|
||||
return currentLevel;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = levels;
|
||||
@@ -0,0 +1,31 @@
|
||||
const loginRewards = {
|
||||
7: {
|
||||
1: {
|
||||
3: {
|
||||
5: { amountMinimum: 1500, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 2, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 2, chance: 100 }
|
||||
}
|
||||
},
|
||||
2: {
|
||||
3: {
|
||||
5: { amountMinimum: 1500, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
3: { amountMinimum: 2, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
3: { amountMinimum: 2, chance: 100 }
|
||||
}
|
||||
},
|
||||
3: {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = loginRewards;
|
||||
@@ -0,0 +1,9 @@
|
||||
const maps = [
|
||||
[1, 6, 11, 16],
|
||||
[2, 7, 12, 17],
|
||||
[3, 8, 13, 18],
|
||||
[4, 9, 14, 19],
|
||||
[5, 10, 15, 20]
|
||||
];
|
||||
|
||||
module.exports = maps;
|
||||
+436
@@ -0,0 +1,436 @@
|
||||
const constans = require("./constants");
|
||||
const entityGroups = require("./entityGroups");
|
||||
|
||||
const { MAP_MODES } = constans;
|
||||
|
||||
const { MAP_MODE_PVE, MAP_MODE_PVP } = MAP_MODES;
|
||||
|
||||
const MAP_NEIGHBOUR_DIRECTION_LEFT = 1;
|
||||
const MAP_NEIGHBOUR_DIRECTION_RIGHT = 2;
|
||||
const MAP_NEIGHBOUR_DIRECTION_TOP = 3;
|
||||
const MAP_NEIGHBOUR_DIRECTION_BOTTOM = 4;
|
||||
|
||||
const map_1 = {
|
||||
name: "1/1",
|
||||
level: 1,
|
||||
id: 1,
|
||||
type: MAP_MODE_PVE,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(1)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 5, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 2, align: "y", threshold: 96 }
|
||||
],
|
||||
collision: {}
|
||||
};
|
||||
|
||||
const map_2 = {
|
||||
name: "1/2",
|
||||
level: 1,
|
||||
id: 2,
|
||||
type: MAP_MODE_PVE,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(2)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 1, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 6, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 3, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_3 = {
|
||||
name: "2/1",
|
||||
level: 10,
|
||||
id: 3,
|
||||
type: MAP_MODE_PVE,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(3)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 7, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 2, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 4, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_4 = {
|
||||
name: "2/2",
|
||||
level: 10,
|
||||
id: 4,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(4)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 8, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 3, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_5 = {
|
||||
name: "3/1",
|
||||
level: 25,
|
||||
id: 5,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(5)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 1, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 9, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 6, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_6 = {
|
||||
name: "3/2",
|
||||
level: 25,
|
||||
id: 6,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(6)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 2, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 10, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 5, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 7, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_7 = {
|
||||
name: "4/1",
|
||||
level: 50,
|
||||
id: 7,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(7)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 3, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 11, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 6, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 8, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_8 = {
|
||||
name: "4/2",
|
||||
level: 50,
|
||||
id: 8,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(8)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 4, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 12, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 7, align: "y", threshold: 96 },
|
||||
]
|
||||
}
|
||||
|
||||
const map_9 = {
|
||||
name: "5/1",
|
||||
level: 75,
|
||||
id: 9,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(9)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 5, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 13, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 10, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_10 = {
|
||||
name: "5/2",
|
||||
level: 75,
|
||||
id: 10,
|
||||
type: MAP_MODE_PVE,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(10)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 6, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 14, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 9, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 11, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_11 = {
|
||||
name: "6/1",
|
||||
level: 100,
|
||||
id: 11,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(11)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 7, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 15, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 10, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 12, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_12 = {
|
||||
name: "6/2",
|
||||
level: 1,
|
||||
id: 12,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(12)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 8, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 16, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 11, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_13 = {
|
||||
name: "7/1",
|
||||
level: 1,
|
||||
id: 13,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(13)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 9, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 17, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 14, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_14 = {
|
||||
name: "7/2",
|
||||
level: 1,
|
||||
id: 14,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(14)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 10, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 18, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 13, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 15, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_15 = {
|
||||
name: "8/1",
|
||||
level: 1,
|
||||
id: 15,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(15)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 11, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 19, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 14, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 16, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_16 = {
|
||||
name: "8/2",
|
||||
level: 1,
|
||||
id: 16,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(16)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 12, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_RIGHT, id: 20, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 15, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_17 = {
|
||||
name: "9/1",
|
||||
level: 1,
|
||||
id: 17,
|
||||
type: MAP_MODE_PVE,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(17)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 13, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 18, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_18 = {
|
||||
name: "9/2",
|
||||
level: 1,
|
||||
id: 18,
|
||||
type: MAP_MODE_PVE,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(18)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 14, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 17, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 19, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_19 = {
|
||||
name: "10/1",
|
||||
level: 1,
|
||||
id: 19,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(19)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 15, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 18, align: "y", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_BOTTOM, id: 20, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
const map_20 = {
|
||||
name: "10/2",
|
||||
level: 1,
|
||||
id: 20,
|
||||
type: MAP_MODE_PVP,
|
||||
size: {
|
||||
width: 6000,
|
||||
height: 4500,
|
||||
chunk: {
|
||||
width: 1200,
|
||||
height: 900
|
||||
}
|
||||
},
|
||||
entityGroups: entityGroups.filter(e => e.mapIDs.includes(20)),
|
||||
neighbours: [
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_LEFT, id: 16, align: "x", threshold: 96 },
|
||||
{ directionIndex: MAP_NEIGHBOUR_DIRECTION_TOP, id: 19, align: "y", threshold: 96 }
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = [map_1, map_2, map_3, map_4, map_5, map_6, map_7, map_8, map_9, map_10, map_11, map_12, map_13, map_14, map_15, map_16, map_17, map_18, map_19, map_20];
|
||||
@@ -0,0 +1,7 @@
|
||||
const OVERTIME_HEALTH_EFFECTS = {
|
||||
1: { amount: 0.15, ticks: 10, time: 1000 },
|
||||
2: { amount: 0.15, ticks: 5, time: 500 },
|
||||
3: { amount: 0.15, ticks: 10, time: 100 }
|
||||
};
|
||||
|
||||
module.exports = OVERTIME_HEALTH_EFFECTS;
|
||||
@@ -0,0 +1,18 @@
|
||||
const Utility = require("../Game/Utility");
|
||||
|
||||
const packetRelevancies = {
|
||||
0: function (player, packet) {
|
||||
return player.inDistance[packet.authorID] || player.id === packet.authorID;
|
||||
},
|
||||
1: function (player, packet) {
|
||||
return Utility.IsInDistance(packet.position, player.position, player.sight + 60);
|
||||
},
|
||||
19: function (player) {
|
||||
return true;
|
||||
},
|
||||
59: function (player, packet) {
|
||||
return player.isAlive && (player.inDistance[packet.authorID] || player.id === packet.authorID);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = packetRelevancies;
|
||||
@@ -0,0 +1,71 @@
|
||||
const ReadyMessage = require("../Messages/ReadyMessage");
|
||||
const QuestMessage = require("../Messages/QuestMessage");
|
||||
const SpyMessage = require("../Messages/SpyMessage");
|
||||
const CauldronMessage = require("../Messages/CauldronMessage");
|
||||
const MovementMessage = require("../Messages/MovementMessage");
|
||||
const CloseMessage = require("../Messages/CloseMessage");
|
||||
const RepairMessage = require("../Messages/RepairMessage");
|
||||
const AttackStartMessage = require("../Messages/AttackStartMessage");
|
||||
const AttackAbortMessage = require("../Messages/AttackAbortMessage");
|
||||
const RespawnMessage = require("../Messages/RespawnMessage");
|
||||
const EquipmentChangeMessage = require("../Messages/EquipmentChangeMessage");
|
||||
const MapChangeMessage = require("../Messages/MapChangeMessage");
|
||||
const AmmunitionChangeMessage = require("../Messages/AmmunitionChangeMessage");
|
||||
const DesignChangeMessage = require("../Messages/DesignChangeMessage");
|
||||
const ItemUseMessage = require("../Messages/ItemUseMessage");
|
||||
const ItemBuyMessage = require("../Messages/ItemBuyMessage");
|
||||
const LeaderboardPageMessage = require("../Messages/LeaderboardPageMessage");
|
||||
const GuildMemberLeaveMessage = require("../Messages/GuildMemberLeaveMessage");
|
||||
const GuildMemberJoinMessage = require("../Messages/GuildMemberJoinMessage");
|
||||
const GuildJoinRequestSubmitMessage = require("../Messages/GuildJoinRequestSubmitMessage");
|
||||
const GuildDataMessage = require("../Messages/GuildDataMessage");
|
||||
const GuildDonateMessage = require("../Messages/GuildDonateMessage");
|
||||
const GuildDetailsTaxEditMessage = require("../Messages/GuildDetailsTaxEditMessage");
|
||||
const GuildDetailsIdentityEditMessage = require("../Messages/GuildDetailsIdentityEditMessage");
|
||||
const GuildIslandTowerRepairMessage = require("../Messages/GuildIslandTowerRepairMessage");
|
||||
const GuildIslandTowerBuildMessage = require("../Messages/GuildIslandTowerBuildMessage");
|
||||
const LeaderboardDataMessage = require("../Messages/LeaderboardDataMessage");
|
||||
const GuildSearchMessage = require("../Messages/GuildSearchMessage");
|
||||
const GuildJoinRequestRevokeMessage = require("../Messages/GuildJoinRequestRevokeMessage");
|
||||
const GuildCreateMessage = require("../Messages/GuildCreateMessage");
|
||||
|
||||
const packetTable = {
|
||||
1: { handler: ReadyMessage, expectedSize: 1, isAvailable: true },
|
||||
2: { handler: QuestMessage, expectedSize: 3, isAvailable: true },
|
||||
3: { handler: SpyMessage, expectedSize: 5, isAvailable: true },
|
||||
4: { handler: CauldronMessage, expectedSize: 3, isAvailable: true },
|
||||
5: { handler: MovementMessage, expectedSize: 5, isAvailable: true },
|
||||
6: { handler: CloseMessage, expectedSize: 1, isAvailable: true },
|
||||
7: { handler: RepairMessage, expectedSize: 1, isAvailable: true },
|
||||
8: { handler: AttackStartMessage, expectedSize: 6, isAvailable: true },
|
||||
9: { handler: AttackAbortMessage, expectedSize: 6, isAvailable: true },
|
||||
10: { handler: RespawnMessage, expectedSize: 1, isAvailable: true },
|
||||
11: { handler: EquipmentChangeMessage, expectedSize: 5, isAvailable: true },
|
||||
12: { handler: MapChangeMessage, expectedSize: 1, isAvailable: true },
|
||||
13: { handler: AmmunitionChangeMessage, expectedSize: 3, isAvailable: true },
|
||||
14: { handler: DesignChangeMessage, expectedSize: 3, isAvailable: true },
|
||||
15: { handler: ItemUseMessage, expectedSize: 7, isAvailable: true },
|
||||
16: { handler: ItemBuyMessage, expectedSize: 7, isAvailable: true },
|
||||
17: { handler: LeaderboardPageMessage, expectedSize: 4, isAvailable: true },
|
||||
18: { handler: GuildMemberLeaveMessage, expectedSize: 5, isAvailable: true },
|
||||
19: { handler: GuildMemberJoinMessage, expectedSize: 5, isAvailable: true },
|
||||
20: { handler: GuildJoinRequestSubmitMessage, expectedSize: 0, isAvailable: true },
|
||||
21: { handler: GuildDataMessage, expectedSize: 1, isAvailable: true },
|
||||
22: { handler: GuildDonateMessage, expectedSize: 6, isAvailable: true },
|
||||
23: { handler: GuildDetailsTaxEditMessage, expectedSize: 3, isAvailable: true },
|
||||
24: { handler: GuildDetailsIdentityEditMessage, expectedSize: 0, isAvailable: true },
|
||||
25: { handler: GuildIslandTowerRepairMessage, expectedSize: 6, isAvailable: true },
|
||||
26: { handler: GuildIslandTowerBuildMessage, expectedSize: 7, isAvailable: true },
|
||||
27: { handler: LeaderboardDataMessage, expectedSize: 1, isAvailable: true },
|
||||
28: { handler: GuildSearchMessage, expectedSize: 0, isAvailable: true },
|
||||
29: { handler: GuildJoinRequestRevokeMessage, expectedSize: 7, isAvailable: true },
|
||||
30: { handler: GuildCreateMessage, expectedSize: 0, isAvailable: true }
|
||||
};
|
||||
|
||||
module.exports = packetTable;
|
||||
|
||||
/*
|
||||
handler: the function that runs when packet is received
|
||||
expectedSize: size of the packet
|
||||
isAvailable: whether the server accepts this packet
|
||||
*/
|
||||
@@ -0,0 +1,8 @@
|
||||
const entries = {
|
||||
1: { name: "Corsaire Coins (200)", unit_amount: 1000, amount: 200 },
|
||||
2: { name: "Corsaire Coins (450)", unit_amount: 2000, amount: 450 },
|
||||
3: { name: "Corsaire Coins (1000)", unit_amount: 3000, amount: 1000 },
|
||||
4: { name: "Corsaire Coins (2000)", unit_amount: 4000, amount: 2000 }
|
||||
};
|
||||
|
||||
module.exports = entries;
|
||||
@@ -0,0 +1,30 @@
|
||||
module.exports.QUERY_FETCH_RESOURCES = "SELECT categoryID, itemID AS id, amount FROM resources WHERE playerID = ?";
|
||||
module.exports.QUERY_FETCH_PLAYER_DATA = "SELECT * FROM playerdata WHERE playerID = ?";
|
||||
module.exports.QUERY_FETCH_PLAYER_INFORMATION = "SELECT extensions.cannonSlots FROM extensions WHERE playerID = ?";
|
||||
module.exports.QUERY_FETCH_GUILD_INFORMATION = "SELECT guilds.id, guilds.tag FROM guilds JOIN guildmembers ON guilds.id = guildmembers.guildID WHERE guildmembers.playerID = ?";
|
||||
module.exports.QUERY_INSERT_DEFAULT_PLAYER = "INSERT INTO playerdata (playerID, x, y, mapID) VALUES (?, ?, ?, ?)";
|
||||
module.exports.QUERY_FETCH_EQUIPMENTS = "SELECT itemID AS id, categoryID, amountEquipped FROM equipments WHERE playerID = ?";
|
||||
module.exports.QUERY_FETCH_NPC_SPAWN = "SELECT npc.id, npc.parentEntityTypeId, npc.name, npc.speed, npc.minHealth, npc.maxHealth, spawn.amount, spawn.respawnTime, reward.categoryId, reward.itemId, reward.min, reward.max, reward.chance, reward.isLastShot FROM spawn INNER JOIN npc ON spawn.entityTypeId = npc.id LEFT JOIN reward ON spawn.id = reward.rewardId WHERE mapId IN (?, 0) AND typeId = ?";
|
||||
module.exports.QUERY_FETCH_MONSTER_SPAWN = "SELECT monster.id, monster.parentEntityTypeId, monster.name, monster.minHealth, monster.maxHealth, spawn.amount, spawn.respawnTime, reward.categoryId, reward.itemId, reward.min, reward.max, reward.chance, reward.isLastShot FROM spawn INNER JOIN monster ON spawn.entityTypeId = monster.id LEFT JOIN reward ON spawn.id = reward.rewardId WHERE mapId IN (?, 0) AND typeId = ?";
|
||||
module.exports.QUERY_FETCH_COLLECTABLE_SPAWN = "SELECT collectable.id, collectable.name, spawn.amount, spawn.respawnTime, reward.categoryId, reward.itemId, reward.min, reward.max, reward.chance FROM spawn INNER JOIN collectable ON spawn.entityTypeId = collectable.id LEFT JOIN reward ON spawn.id = reward.rewardId WHERE mapId = ? AND typeId = ?";
|
||||
module.exports.QUERY_FETCH_ACTIVE_QUESTS = "SELECT quests.questID, completedAmount, CAST(state AS UNSIGNED) AS state, questprogression.amount, questprogression.taskIndex, questprogression.taskType FROM quests LEFT JOIN questprogression ON quests.questID = questprogression.questID WHERE quests.playerID = ?";
|
||||
module.exports.QUERY_FETCH_STATUS = "SELECT statusID FROM status WHERE playerID = ? AND isActive = ?";
|
||||
module.exports.QUERY_FETCH_GUILD_REQUESTS = "SELECT guilds.id, guildrequests.playerID, guildrequests.message FROM guildrequests INNER JOIN guilds ON guildrequests.guildID = guilds.id";
|
||||
module.exports.QUERY_FETCH_GUILD_MEMBERS = "SELECT guilds.id, BINARY(guilds.name) AS name, BINARY(guilds.tag) AS tag, BINARY(guilds.description) AS description, guilds.createdBy, guilds.taxRateGold / 100 AS taxRateGold, guilds.taxRateEmerald / 100 AS taxRateEmerald, guilds.taxRatesEditedAt, guilds.tagEditedAt, guilds.nameEditedAt, guilds.descriptionEditedAt, guilds.descriptionEditedAt, guildmembers.playerID, guildmembers.permission FROM guildmembers INNER JOIN guilds ON guildmembers.guildID = guilds.id";
|
||||
module.exports.QUERY_FETCH_GUILD_ISLANDS = "SELECT * FROM guildislands";
|
||||
module.exports.QUERY_FETCH_GUILD_ISLAND_DAMAGES = "SELECT * FROM guildislanddamages";
|
||||
module.exports.QUERY_FETCH_GUILD_DIPLOMACIES = "SELECT guildID, targetGuildID, CONV(HEX(color), 16, 10) + 0 AS color FROM guilddiplomacy JOIN guilds ON guilddiplomacy.guildID = guilds.id";
|
||||
module.exports.QUERY_FETCH_GUILD_RESOURCES = "SELECT guildID, categoryID, itemID AS id, amount FROM guildresources";
|
||||
module.exports.QUERY_FETCH_GUILD_TOWERS = "SELECT gi.mapID, gt.id, gt.guildIslandID, gt.entityTypeID, gt.currentHitpoints, gt.positionX, gt.positionY FROM guildtowers gt INNER JOIN guildislands gi ON gt.guildIslandID = gi.id WHERE gi.mapID = ?";
|
||||
module.exports.QUERY_UPDATE_GUILD_ISLAND_TOWER_DEAD = "UPDATE guildtowers SET currentHitpoints = ? WHERE id = ?";
|
||||
module.exports.QUERY_UPDATE_GUILD_ISLAND_TOWER_UPGRADE = "UPDATE guildtowers SET entityTypeID = ?, currentHitpoints = ? WHERE id = ?";
|
||||
module.exports.QUERY_UPDATE_GUILD_ISLAND_TOWER_DEFAULT = "UPDATE guildtowers SET currentHitpoints = ? WHERE guildIslandID = ?";
|
||||
module.exports.QUERY_DELETE_GUILD_ISLAND_DAMAGES = "DELETE FROM guildislanddamages WHERE guildIslandID = ?";
|
||||
module.exports.QUERY_UPDATE_GUILD_ISLAND_AUTHOR = "UPDATE guildislands SET guildID = ? WHERE id = ?";
|
||||
module.exports.QUERY_UPDATE_GUILD_ISLAND_DAMAGES = "INSERT INTO guildislanddamages (damage, guildIslandID, guildID) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE damage = damage + ?";
|
||||
module.exports.QUERY_UPDATE_GUILD_RESOURCE_SINGLE = "INSERT INTO guildresources (guildID, categoryID, itemID, amount) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE amount = ?";
|
||||
module.exports.QUERY_UPDATE_PLAYER_RESOURCE_SINGLE = "INSERT INTO resources (playerID, categoryID, itemID, amount) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE amount = ?";
|
||||
module.exports.QUERY_UPDATE_GUILD_TAX_RATES = "UPDATE guilds SET taxRateGold = ?, taxRateEmerald = ? WHERE id = ?";
|
||||
module.exports.QUERY_DELETE_GUILD_MEMBER = "DELETE FROM guildmembers WHERE playerID = ? AND isLeader = ?";
|
||||
module.exports.QUERY_FETCH_GUILD_OFFLINE_MEMBERS = "SELECT accounts.id, username, itemID, amount FROM resources INNER JOIN accounts on resources.playerID = accounts.id WHERE categoryID = 6 AND itemID = 1 AND playerID IN (?)";
|
||||
module.exports.QUERY_FETCH_SHOP_ENTRIES = "SELECT * FROM shop";
|
||||
@@ -0,0 +1,97 @@
|
||||
const quest = {
|
||||
id: 1,
|
||||
maximumComplete: 1,
|
||||
minimumLevel: 1,
|
||||
maximumLevel: 99,
|
||||
task: {
|
||||
1: [
|
||||
{ typeID: 2, entityTypeID: 1, requiredAmount: 200, amount: 0 },
|
||||
{ typeID: 3, entityTypeID: 1, requiredAmount: 5000, amount: 0 },
|
||||
|
||||
],
|
||||
2: [
|
||||
{ typeID: 5, entityTypeID: 1, requiredAmount: 3, amount: 0 },
|
||||
{ typeID: 3, entityTypeID: 3, requiredAmount: 3, amount: 0 }
|
||||
]
|
||||
},
|
||||
reward: {
|
||||
3: {
|
||||
5: { amountMinimum: 69, chance: 100 }
|
||||
},
|
||||
5: {
|
||||
18: { amountMinimum: 1, chance: 100 }
|
||||
},
|
||||
6: {
|
||||
1: { amountMinimum: 100, chance: 100 }
|
||||
},
|
||||
7: {
|
||||
1: { amountMinimum: 3, chance: 100 }
|
||||
},
|
||||
8: {
|
||||
1: { amountMinimum: 3, chance: 100 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const quest2 = {
|
||||
id: 2,
|
||||
maximumComplete: 3,
|
||||
minimumLevel: 1,
|
||||
maximumLevel: 2,
|
||||
task: {
|
||||
1: [
|
||||
{ typeID: 2, entityTypeID: 3, requiredAmount: 2000, amount: 0 },
|
||||
{ typeID: 2, entityTypeID: 5, requiredAmount: 1500, amount: 0 },
|
||||
],
|
||||
2: [
|
||||
{ typeID: 5, entityTypeID: 1, requiredAmount: 10, amount: 0 },
|
||||
{ typeID: 3, entityTypeID: 3, requiredAmount: 3, amount: 0 }
|
||||
]
|
||||
},
|
||||
reward: {
|
||||
5: {
|
||||
6: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const quest3 = {
|
||||
id: 3,
|
||||
maximumComplete: 2,
|
||||
minimumLevel: 1,
|
||||
maximumLevel: 99,
|
||||
task: {
|
||||
2: [
|
||||
{ typeID: 5, entityTypeID: 1, requiredAmount: 10, amount: 0 }
|
||||
]
|
||||
},
|
||||
reward: {
|
||||
3: {
|
||||
1: { amountMinimum: 1000, chance: 100 },
|
||||
2: { amountMinimum: 2000, chance: 100 },
|
||||
5: { amountMinimum: 2700, chance: 100 },
|
||||
},
|
||||
5: {
|
||||
35: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const quest4 = {
|
||||
id: 4,
|
||||
maximumComplete: 1,
|
||||
minimumLevel: 4,
|
||||
maximumLevel: 4,
|
||||
task: {
|
||||
2: [
|
||||
{ typeID: 5, entityTypeID: 1, requiredAmount: 3, amount: 0 }
|
||||
]
|
||||
},
|
||||
reward: {
|
||||
5: {
|
||||
34: { amountMinimum: 1, chance: 100 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = [quest, quest2, quest3, quest4];
|
||||
@@ -0,0 +1,49 @@
|
||||
const schemeTower = {
|
||||
1: {
|
||||
maximumHitpoints: 5000,
|
||||
},
|
||||
2: {
|
||||
maximumHitpoints: 15000,
|
||||
},
|
||||
3: {
|
||||
maximumHitpoints: 25000,
|
||||
},
|
||||
4: {
|
||||
maximumHitpoints: 35000,
|
||||
},
|
||||
5: {
|
||||
maximumHitpoints: 45000,
|
||||
},
|
||||
6: {
|
||||
maximumHitpoints: 55000,
|
||||
},
|
||||
7: {
|
||||
maximumHitpoints: 65000,
|
||||
},
|
||||
8: {
|
||||
maximumHitpoints: 75000,
|
||||
},
|
||||
9: {
|
||||
maximumHitpoints: 85000,
|
||||
},
|
||||
10: {
|
||||
maximumHitpoints: 95000,
|
||||
},
|
||||
11: {
|
||||
maximumHitpoints: 105000,
|
||||
},
|
||||
12: {
|
||||
maximumHitpoints: 115000,
|
||||
},
|
||||
13: {
|
||||
maximumHitpoints: 125000,
|
||||
},
|
||||
14: {
|
||||
maximumHitpoints: 135000,
|
||||
},
|
||||
15: {
|
||||
maximumHitpoints: 145000,
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = schemeTower;
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
const constants = require("./constants");
|
||||
const { CATEGORIES } = constants;
|
||||
const { CATEGORY_ITEM, CATEGORY_AMMUNITION, CATEGORY_HARPOON, CATEGORY_DESIGN, CATEGORY_CANNON, CATEGORY_HARPOONER } = CATEGORIES;
|
||||
|
||||
const SHOP = [];
|
||||
|
||||
const SHOP_ENTRIES_BUY = {
|
||||
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
SHOP,
|
||||
SHOP_ENTRIES_BUY
|
||||
};
|
||||
/*
|
||||
const SHOP = {
|
||||
[CATEGORY_ITEM]: {
|
||||
|
||||
},
|
||||
[CATEGORY_AMMUNITION]: {
|
||||
1: {
|
||||
currency: 1,
|
||||
unitPrice: 21,
|
||||
maximumPlayerAmount: 1500,
|
||||
maximumGlobalAmount: 2500,
|
||||
shopCategoryID: 4,
|
||||
isAvailable: () => true
|
||||
},
|
||||
2: {
|
||||
currency: 1,
|
||||
unitPrice: 21,
|
||||
maximumPlayerAmount: 3000,
|
||||
maximumGlobalAmount: 1000,
|
||||
shopCategoryID: 4,
|
||||
isAvailable: () => true
|
||||
},
|
||||
},
|
||||
[CATEGORY_HARPOON]: {
|
||||
1: {
|
||||
currency: 1,
|
||||
unitPrice: 21,
|
||||
maximumPlayerAmount: 2000,
|
||||
shopCategoryID: 6,
|
||||
isAvailable: () => true
|
||||
},
|
||||
2: {
|
||||
currency: 1,
|
||||
unitPrice: 21,
|
||||
maximumPlayerAmount: 2000,
|
||||
shopCategoryID: 6,
|
||||
isAvailable: () => true
|
||||
},
|
||||
3: {
|
||||
currency: 1,
|
||||
unitPrice: 21,
|
||||
maximumPlayerAmount: 2000,
|
||||
shopCategoryID: 6,
|
||||
isAvailable: () => true
|
||||
},
|
||||
4: {
|
||||
currency: 1,
|
||||
unitPrice: 21,
|
||||
maximumPlayerAmount: 2000,
|
||||
shopCategoryID: 6,
|
||||
isAvailable: () => true
|
||||
}
|
||||
},
|
||||
[CATEGORY_DESIGN]: {
|
||||
2: {
|
||||
currency: 1,
|
||||
unitPrice: 200_000,
|
||||
maximumPlayerAmount: 1,
|
||||
shopCategoryID: 2,
|
||||
isAvailable: (player) => !player.inventory.hasDesign(2)
|
||||
},
|
||||
3: {
|
||||
currency: 1,
|
||||
unitPrice: 400_000,
|
||||
maximumPlayerAmount: 1,
|
||||
shopCategoryID: 2,
|
||||
isAvailable: (player) => !player.inventory.hasDesign(3)
|
||||
},
|
||||
4: {
|
||||
currency: 1,
|
||||
unitPrice: 600_000,
|
||||
maximumPlayerAmount: 1,
|
||||
shopCategoryID: 2,
|
||||
isAvailable: (player) => !player.inventory.hasDesign(4)
|
||||
},
|
||||
5: {
|
||||
currency: 2,
|
||||
unitPrice: 60_000,
|
||||
maximumPlayerAmount: 1,
|
||||
shopCategoryID: 2,
|
||||
isAvailable: (player) => !player.inventory.hasDesign(5)
|
||||
}
|
||||
},
|
||||
[CATEGORY_CANNON]: {
|
||||
1: {
|
||||
currency: 1,
|
||||
unitPrice: 1_000_000,
|
||||
maximumPlayerAmount: 10,
|
||||
shopCategoryID: 3,
|
||||
isAvailable: () => true
|
||||
},
|
||||
2: {
|
||||
currency: 1,
|
||||
unitPrice: 1_000_000,
|
||||
maximumPlayerAmount: 10,
|
||||
shopCategoryID: 3,
|
||||
isAvailable: () => true
|
||||
},
|
||||
},
|
||||
[CATEGORY_HARPOONER]: {
|
||||
1: {
|
||||
currency: 1,
|
||||
unitPrice: 1_000_000,
|
||||
maximumPlayerAmount: 10,
|
||||
shopCategoryID: 5,
|
||||
isAvailable: () => true
|
||||
}
|
||||
}
|
||||
};*/
|
||||
@@ -0,0 +1,12 @@
|
||||
const startRewards = [
|
||||
{ categoryID: 1, id: 1, amount: 100_000 },
|
||||
{ categoryID: 1, id: 2, amount: 1_000 },
|
||||
{ categoryID: 7, id: 1, amount: 20 },
|
||||
{ categoryID: 8, id: 1, amount: 10 },
|
||||
{ categoryID: 3, id: 1, amount: 2_000 },
|
||||
{ categoryID: 4, id: 1, amount: 1_000 },
|
||||
// { categoryID: 7, id: 10, amount: 50 },
|
||||
// { categoryID: 3, id: 3, amount: 99999999 },
|
||||
];
|
||||
|
||||
module.exports = startRewards;
|
||||
@@ -0,0 +1,16 @@
|
||||
const STRIPE_IP_COLLECTION = {
|
||||
"3.18.12.63": true,
|
||||
"3.130.192.231": true,
|
||||
"13.235.14.237": true,
|
||||
"13.235.122.149": true,
|
||||
"18.211.135.69": true,
|
||||
"35.154.171.200": true,
|
||||
"52.15.183.38": true,
|
||||
"54.88.130.119": true,
|
||||
"54.88.130.237": true,
|
||||
"54.187.174.169": true,
|
||||
"54.187.205.235": true,
|
||||
"54.187.216.72": true
|
||||
};
|
||||
|
||||
module.exports = STRIPE_IP_COLLECTION;
|
||||
@@ -0,0 +1,42 @@
|
||||
const Game = require("../Game/Game");
|
||||
const { players } = Game;
|
||||
|
||||
const extractBehaviourFeatures = require("../Utility/extractBehaviourFeatures");
|
||||
|
||||
// const NORMAL_CLOSE_CODES = [0, 1, 2];
|
||||
|
||||
module.exports = function (ws, code, message) {
|
||||
if (code === 1337) return;
|
||||
|
||||
const player = players[ws.accountID];
|
||||
if (!player) {
|
||||
ws.isInterrupted = true;
|
||||
delete socket.sockets[ws.id];
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
const playerActions = global.actions[player.id];
|
||||
const features = extractBehaviourFeatures(playerActions);
|
||||
|
||||
delete global.actions[player.id];
|
||||
|
||||
if (features) {
|
||||
features.playerID = player.id;
|
||||
features.timestampStart = new Date(ws.connectedAt);
|
||||
|
||||
global.StatisticsWorker.postMessage({
|
||||
id: 2,
|
||||
data: features
|
||||
});
|
||||
};
|
||||
|
||||
player.inDistance = {};
|
||||
player.isAvailable = false;
|
||||
player.hasUnexpectedlyLeft = true;
|
||||
|
||||
const individualPackets = player.map.networkPacketsInvidiual[player.id];
|
||||
if (individualPackets) individualPackets.length = 0;
|
||||
console.info("close event", performance.now());
|
||||
delete socket.sockets[ws.id];
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = function (ws) {
|
||||
console.info("Drain?");
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
const packetTable = require("../Data/packets");
|
||||
const { players } = require("../Game/Game");
|
||||
|
||||
const actions = [];
|
||||
global.actions = actions;
|
||||
|
||||
const { MongoClient } = require("mongodb");
|
||||
let db = null;
|
||||
|
||||
async function connect() {
|
||||
const client = new MongoClient("mongodb://localhost:27017");
|
||||
await client.connect({
|
||||
maxPoolSize: 100,
|
||||
minPoolSize: 20,
|
||||
maxConnecting: 20,
|
||||
waitQueueTimeoutMS: 0,
|
||||
socketTimeoutMS: 30000,
|
||||
connectTimeoutMS: 10000,
|
||||
retryWrites: false,
|
||||
directConnection: true
|
||||
});
|
||||
|
||||
return client.db('test');
|
||||
};
|
||||
|
||||
(async function() {
|
||||
db = await connect();
|
||||
global.mongodb = db;
|
||||
|
||||
return;
|
||||
setInterval(async () => {
|
||||
const t = [];
|
||||
for (let i = 0; i < 32; i++) {
|
||||
t.push({
|
||||
playerID: 1,
|
||||
timestamp: new Date(),
|
||||
opcode: Math.floor(Math.random() * 120)
|
||||
});
|
||||
};
|
||||
|
||||
const bulkOps = t.map(doc => ({
|
||||
insertOne: doc
|
||||
}));
|
||||
|
||||
const n = performance.now();
|
||||
await db.collection("Actions").insertMany(t, {
|
||||
ordered: false,
|
||||
writeConcern: { w: 0 },
|
||||
bypassDocumentValidation: true
|
||||
});
|
||||
|
||||
/* await db.collection('Actions').bulkWrite(bulkOps, {
|
||||
ordered: false,
|
||||
writeConcern: { w: 0 },
|
||||
bypassDocumentValidation: true
|
||||
});*/
|
||||
}, 500);
|
||||
})();
|
||||
|
||||
module.exports = async function (ws, message, isBinary) {
|
||||
const player = players[ws.accountID];
|
||||
if (!player) return;
|
||||
|
||||
const isLimitReached = ws.limiter.update();
|
||||
if (isLimitReached) return ws.close();
|
||||
|
||||
if (player.isInactive) return; // this shouldn't happen, though
|
||||
|
||||
const view = new DataView(message);
|
||||
const packetID = view.getUint8(0);
|
||||
const packet = packetTable[packetID];
|
||||
|
||||
if (packetID !== 1 && !player.isAvailable) return;
|
||||
if (!packet) return;
|
||||
if (packet.expectedSize && packet.expectedSize !== view.byteLength) return console.info("expectedSize", view.byteLength, packet.expectedSize);
|
||||
if (!packet.isAvailable) return;
|
||||
|
||||
player.lastInteractionTimestamp = performance.now();
|
||||
|
||||
const metadata = packet.handler(ws, view, player) ?? [];
|
||||
|
||||
if (packetID > 1) {
|
||||
const action = {
|
||||
ts: new Date(),
|
||||
pid: player.id,
|
||||
opcode: packetID,
|
||||
metadata
|
||||
};
|
||||
|
||||
const relevantContext = global.actionContext.getElement(player.id).save(2000);
|
||||
relevantContext.push(action);
|
||||
console.info("asd", relevantContext.length)
|
||||
};
|
||||
};
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
const Limiter = require("../Game/PacketLimiter");
|
||||
const EquipmentLimiter = require("../Game/EquipmentLimiter");
|
||||
const Inventory = require("../Game/Inventory");
|
||||
const Player = require("../Game/Player");
|
||||
const Game = require("../Game/Game");
|
||||
|
||||
const playerHandlers = require("../Handlers/Player");
|
||||
|
||||
const EnvironmentSetupPacket = require("../Packets/EnvironmentSetup");
|
||||
|
||||
const rewardCallback = require("../Callbacks/reward");
|
||||
const attackCallback = require("../Callbacks/attack");
|
||||
|
||||
const baseStarterRewards = require("../Data/startRewards");
|
||||
|
||||
const { maps, players, guilds } = Game;
|
||||
|
||||
const queries = require("../Data/queries");
|
||||
const { QUERY_FETCH_PLAYER_DATA, QUERY_FETCH_PLAYER_INFORMATION, QUERY_FETCH_GUILD_INFORMATION, QUERY_INSERT_DEFAULT_PLAYER, QUERY_FETCH_ACTIVE_QUESTS, QUERY_FETCH_STATUS, QUERY_FETCH_RESOURCES, QUERY_FETCH_EQUIPMENTS } = queries;
|
||||
|
||||
const playerEvents = Object.keys(playerHandlers);
|
||||
|
||||
const PlayerBehaviour = require("../Game/PlayerBehaviour");
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
global.actionContext = new DoubleLinkedList();
|
||||
|
||||
module.exports = async function (ws) {
|
||||
console.info("Before open for", ws.username, Game.state, ws.isAdmin);
|
||||
|
||||
if (Game.state === 0 && !ws.isAdmin) return ws.end(1337);
|
||||
|
||||
ws.connectedAt = Date.now();
|
||||
let abruptDisconnectTest = performance.now();
|
||||
|
||||
const ID = ws.accountID;
|
||||
const sessionID = Date.now().toString(36) + Math.random().toString(36).substring(2) + "!" + ID;
|
||||
|
||||
socket.sockets[sessionID] = ws;
|
||||
|
||||
ws.id = sessionID;
|
||||
ws.limiter = new Limiter({
|
||||
countThreshold: 10,
|
||||
timeThreshold: 1000
|
||||
});
|
||||
|
||||
const alreadyConnectedPlayer = players[ID];
|
||||
if (alreadyConnectedPlayer) {
|
||||
const player = alreadyConnectedPlayer;
|
||||
|
||||
const alreadyConnectedSoket = socket.sockets[player.sessionID];
|
||||
if (alreadyConnectedSoket) alreadyConnectedSoket.end(1);
|
||||
|
||||
player.sessionID = sessionID;
|
||||
ws.subscribe(player.map.id.toString());
|
||||
|
||||
player.map.removePlayer(player.id);
|
||||
player.map.addPlayer(player);
|
||||
|
||||
player.isUnderDisconnect = false;
|
||||
|
||||
player.cancelDisconnect();
|
||||
|
||||
const packet = EnvironmentSetupPacket(player.map.id);
|
||||
return ws.send(packet, true, true);
|
||||
};
|
||||
|
||||
const playerBehaviour = new PlayerBehaviour({
|
||||
size: 16,
|
||||
buffer: global.playerBehaviourContext,
|
||||
id: ID
|
||||
});
|
||||
|
||||
global.actionContext.addItem(ID, playerBehaviour);
|
||||
|
||||
const generalQuery = execute(QUERY_FETCH_PLAYER_DATA, [ID]);
|
||||
const informationQuery = execute(QUERY_FETCH_PLAYER_INFORMATION, [ID]);
|
||||
const guildQuery = execute(QUERY_FETCH_GUILD_INFORMATION, [ID]);
|
||||
const questQuery = execute(QUERY_FETCH_ACTIVE_QUESTS, [ID]);
|
||||
const statusQuery = execute(QUERY_FETCH_STATUS, [ID, true]);
|
||||
const resourcesQuery = execute(QUERY_FETCH_RESOURCES, [ID]);
|
||||
const equipmentsQuery = execute(QUERY_FETCH_EQUIPMENTS, [ID]);
|
||||
|
||||
const res = await Promise.all([generalQuery, informationQuery, guildQuery, questQuery, statusQuery, resourcesQuery, equipmentsQuery]);
|
||||
const hasError = res.some(q => q.error !== undefined);
|
||||
|
||||
if (hasError) return;
|
||||
if (ws.isInterrupted) return;
|
||||
|
||||
let isAlive = false;
|
||||
let map = null;
|
||||
let hitpoints = {
|
||||
current: 0,
|
||||
maximum: 0
|
||||
};
|
||||
let position = {};
|
||||
let attemptToDoubleLog = false;
|
||||
let maximumCannonRange = 0;
|
||||
let maximumHarpoonerRange = 0;
|
||||
let textureID = 0;
|
||||
let sight = 0;
|
||||
let speed = 0;
|
||||
let globalRank = 0;
|
||||
let levelRank = 0;
|
||||
|
||||
const playerFromDB = res[0].data[0];
|
||||
const guildFromDB = res[2].data[0];
|
||||
const questFromDB = res[3].data;
|
||||
const statusFromDB = res[4].data;
|
||||
const resourcesFromDB = res[5].data;
|
||||
const equipmentsFromDB = res[6].data;
|
||||
|
||||
const statusPoints = [];
|
||||
|
||||
const guildID = guildFromDB ? guildFromDB.id : 0;
|
||||
const guild = guilds[guildID];
|
||||
|
||||
const selectedAmmunition = playerFromDB ? playerFromDB.selectedAmmunitionID : 0;
|
||||
const selectedHarpoon = playerFromDB ? playerFromDB.selectedHarpoonID : 0;
|
||||
|
||||
const resourceSharedLimiter = {
|
||||
cannon: new EquipmentLimiter({
|
||||
maximum: 50
|
||||
}),
|
||||
harpooner: new EquipmentLimiter({
|
||||
maximum: 25
|
||||
}),
|
||||
sail: new EquipmentLimiter({
|
||||
maximum: 5
|
||||
}),
|
||||
pirate: new EquipmentLimiter({
|
||||
maximum: 250
|
||||
})
|
||||
};
|
||||
|
||||
const inventory = new Inventory({
|
||||
resourceSharedLimiter
|
||||
});
|
||||
|
||||
resourcesFromDB.forEach(r => {
|
||||
switch (r.categoryID) {
|
||||
case 6:
|
||||
statusPoints.push(r);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 8:
|
||||
const equipment = equipmentsFromDB.find(e => e.id === r.id && e.categoryID === r.categoryID);
|
||||
r.amountEquipped = equipment ? equipment.amountEquipped : 0;
|
||||
break;
|
||||
};
|
||||
|
||||
inventory.refreshSingle(r);
|
||||
});
|
||||
|
||||
inventory.setSelectedAmmunition(selectedAmmunition);
|
||||
inventory.setSelectedHarpoon(selectedHarpoon);
|
||||
|
||||
if (!attemptToDoubleLog) {
|
||||
const playerHasData = playerFromDB ? true : false;
|
||||
if (!playerHasData) {
|
||||
const startingMap = maps.find(m => m.id === 1);
|
||||
const { x, y } = startingMap.grid.randomNode(10);
|
||||
|
||||
textureID = 1;
|
||||
sight = 450;
|
||||
speed = 250;
|
||||
globalRank = 1;
|
||||
levelRank = 1;
|
||||
isAlive = true;
|
||||
map = startingMap;
|
||||
position = {
|
||||
x,
|
||||
y
|
||||
};
|
||||
hitpoints = {
|
||||
current: 10000,
|
||||
maximum: 50000
|
||||
};
|
||||
|
||||
baseStarterRewards.forEach(r => inventory.refreshSingle(r));
|
||||
|
||||
await execute(QUERY_INSERT_DEFAULT_PLAYER, [ID, x, y, 1]);
|
||||
} else {
|
||||
textureID = playerFromDB.designID;
|
||||
sight = playerFromDB.sight;
|
||||
speed = playerFromDB.speed;
|
||||
globalRank = playerFromDB.globalRank;
|
||||
levelRank = playerFromDB.levelRank;
|
||||
isAlive = !!hitpoints;
|
||||
map = maps.find(m => m.id === playerFromDB.mapID);
|
||||
|
||||
position = {
|
||||
x: playerFromDB.x,
|
||||
y: playerFromDB.y
|
||||
};
|
||||
|
||||
hitpoints = {
|
||||
current: playerFromDB.currentHitpoints,
|
||||
maximum: playerFromDB.maximumHitpoints
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const player = new Player({
|
||||
id: ID,
|
||||
sessionID: ws.id,
|
||||
name: ws.username,
|
||||
textureID,
|
||||
map,
|
||||
guild,
|
||||
positionX: position.x,
|
||||
positionY: position.y,
|
||||
isAlive,
|
||||
hitpoints,
|
||||
maximumCannonRange,
|
||||
maximumHarpoonerRange,
|
||||
sight,
|
||||
speed,
|
||||
globalRank,
|
||||
levelRank,
|
||||
inventory,
|
||||
statusPoints,
|
||||
activeStatusEffects: statusFromDB,
|
||||
quests: questFromDB,
|
||||
rewardCallback,
|
||||
attackCallback
|
||||
});
|
||||
|
||||
player.setDesign(textureID);
|
||||
|
||||
ws.subscribe(player.map.id.toString());
|
||||
|
||||
playerEvents.forEach(e => player.on(e, playerHandlers[e]));
|
||||
players[ID] = player;
|
||||
|
||||
const packet = EnvironmentSetupPacket(player.map.id);
|
||||
ws.send(packet, true, true);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
const crypto = require("crypto");
|
||||
|
||||
const redis = require("../Miscellaneous/redis");
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
|
||||
const HTTP_BAD_REQUEST = "403";
|
||||
|
||||
function badRequest(res) {
|
||||
res.cork(() => res.isAborted ? null : res
|
||||
.writeStatus(HTTP_BAD_REQUEST)
|
||||
.end());
|
||||
};
|
||||
|
||||
module.exports = async function (res, req, context) {
|
||||
const cookieHeader = req.getHeader("cookie");
|
||||
const cookie = cookieHeader.split("ps=")[1];
|
||||
|
||||
res.onAborted(() => {
|
||||
res.isAborted = true;
|
||||
console.info("ABORTED!!!");
|
||||
});
|
||||
|
||||
if (!cookie) {
|
||||
console.info("upgrade 1")
|
||||
return badRequest(res);
|
||||
};
|
||||
|
||||
const secWebSocketKey = req.getHeader("sec-websocket-key");
|
||||
const secWebSocketProtocol = req.getHeader("sec-websocket-protocol");
|
||||
const secWebSocketExtensions = req.getHeader("sec-websocket-extensions");
|
||||
|
||||
const userAgent = req.getHeader("user-agent");
|
||||
const ip = req.getHeader("x-forwarded-for");
|
||||
|
||||
const formattedCookie = cookie.split(".")[0].slice(4);
|
||||
const cookieInDB = await redis.get(`sess:${formattedCookie}`);
|
||||
if (!cookieInDB) {
|
||||
console.info("upgrade 2")
|
||||
|
||||
return badRequest(res);
|
||||
};
|
||||
|
||||
const parsedCookie = JSON.parse(cookieInDB);
|
||||
if (!parsedCookie.accountID) {
|
||||
console.info("upgrade 3")
|
||||
|
||||
return badRequest(res);
|
||||
};
|
||||
|
||||
const hash = crypto.createHash("md5")
|
||||
.update(userAgent)
|
||||
.update(ip)
|
||||
.digest("hex");
|
||||
|
||||
// if (hash !== parsedCookie.fingerprint) return badRequest(res);
|
||||
const { accountID, username, isAdmin } = parsedCookie;
|
||||
console.info("Before upgrading connection for", username);
|
||||
res.cork(() => res.upgrade({ accountID, username, cookie, isAdmin },
|
||||
secWebSocketKey,
|
||||
secWebSocketProtocol,
|
||||
secWebSocketExtensions,
|
||||
context));
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
class AdmiralCounter {
|
||||
constructor(configuration) {
|
||||
this.entities = Object.entries(configuration);
|
||||
this.admiralCounters = {};
|
||||
}
|
||||
|
||||
increaseCounter(ids) {
|
||||
const { typeID, parentEntityTypeID } = ids;
|
||||
const id = `${typeID}_${parentEntityTypeID}`;
|
||||
|
||||
if (!this.admiralCounters[id]) this.admiralCounters[id] = { childKilledCount: 1, admiralAliveCount: 0 };
|
||||
else this.admiralCounters[id].childKilledCount++;
|
||||
|
||||
const thresholds = this.entities.find(e => parseInt(e[0]) === typeID);
|
||||
if (!thresholds) return;
|
||||
|
||||
const threshold = thresholds[1][parentEntityTypeID];
|
||||
if (!threshold) return;
|
||||
|
||||
const counter = this.admiralCounters[id];
|
||||
if (counter.childKilledCount === threshold.childToBeKilledCount) {
|
||||
counter.childKilledCount = 0;
|
||||
|
||||
if (counter.admiralAliveCount < threshold.maximumAdmiralCount) {
|
||||
counter.admiralAliveCount++;
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
descreaseAdmiralCount(id) {
|
||||
const counter = this.admiralCounters[id];
|
||||
if (counter) counter.admiralAliveCount--;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = AdmiralCounter;
|
||||
@@ -0,0 +1,71 @@
|
||||
const EventEmitter = require("events");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { CATEGORIES } = constants;
|
||||
const { CATEGORY_CANNON } = CATEGORIES;
|
||||
|
||||
const eventsMapped = {
|
||||
1: 97,
|
||||
2: 98,
|
||||
3: 99,
|
||||
4: 100,
|
||||
5: 101,
|
||||
6: 102,
|
||||
7: 103,
|
||||
8: 104,
|
||||
9: 105,
|
||||
10: 106
|
||||
};
|
||||
|
||||
class Cannon extends EventEmitter {
|
||||
constructor(info) {
|
||||
super();
|
||||
|
||||
this.id = info.id;
|
||||
this.categoryID = CATEGORY_CANNON;
|
||||
this.eventID = eventsMapped[this.id];
|
||||
this.damage = info.damage;
|
||||
this.range = info.range;
|
||||
this.reload = info.reload;
|
||||
this.scatter = info.scatter;
|
||||
this.amount = info.amount;
|
||||
this.amountEquipped = info.amountEquipped || 0;
|
||||
this.lastShotTickTimestamp = 0;
|
||||
this.resourceSharedLimiter = info.resourceSharedLimiter;;
|
||||
};
|
||||
|
||||
equip(quantity) {
|
||||
if (this.amountEquipped + quantity > this.amount) return
|
||||
|
||||
const isTypeQunatityExhausted = this.resourceSharedLimiter.count(quantity);
|
||||
if (!isTypeQunatityExhausted) return
|
||||
|
||||
|
||||
this.amountEquipped += quantity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unequip(quantity) {
|
||||
if (this.amountEquipped - quantity < 0) return
|
||||
|
||||
const isTypeQunatityExhausted = this.resourceSharedLimiter.count(-quantity);
|
||||
if (!isTypeQunatityExhausted) return
|
||||
|
||||
|
||||
this.amountEquipped -= quantity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
return {
|
||||
typeID: 1,
|
||||
id: this.id,
|
||||
amount: this.amount,
|
||||
amountEquipped: this.amountEquipped
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Cannon;
|
||||
@@ -0,0 +1,70 @@
|
||||
const Entity = require("./Entity");
|
||||
|
||||
const Reward = require("./Reward");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
|
||||
const { TYPES, TIMING_EVENTS } = constants;
|
||||
const { ENTITY_TYPE_COLLECTABLE } = TYPES;
|
||||
const { TIMING_EVENT_RESPAWN } = TIMING_EVENTS;
|
||||
|
||||
class Collectable extends Entity {
|
||||
constructor(info) {
|
||||
super({
|
||||
type: ENTITY_TYPE_COLLECTABLE,
|
||||
id: info.id,
|
||||
map: info.map,
|
||||
isAlive: true,
|
||||
position: info.position
|
||||
});
|
||||
|
||||
this.respawnTime = 10000;
|
||||
this.entityTypeID = info.entityTypeID;
|
||||
this.dieCallback = info.dieCallback;
|
||||
this.rewardCallback = info.rewardCallback;
|
||||
this.reward = new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: info.rewardEntries.reduce((result, entry) => {
|
||||
if (result[entry.categoryId]) result[entry.categoryId][entry.itemId] = {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance
|
||||
};
|
||||
else result[entry.categoryId] = {
|
||||
[entry.itemId]: {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
}, {})
|
||||
});
|
||||
|
||||
this.map.collectables.set(this.id, this);
|
||||
}
|
||||
|
||||
die(deathEvent) {
|
||||
super.die();
|
||||
|
||||
const die = {
|
||||
typeID: this.typeID,
|
||||
entityTypeID: this.entityTypeID,
|
||||
id: this.id,
|
||||
authorID: deathEvent.authorID,
|
||||
targetEntityTypeID: this.entityTypeID
|
||||
};
|
||||
|
||||
this.dieCallback(die);
|
||||
|
||||
this.setSingleEvent(TIMING_EVENT_RESPAWN, this.respawnTime);
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
const { position, type, typeID, entityTypeID, id } = this;
|
||||
return { position, type, typeID, entityTypeID, id };
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Collectable;
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
const EventScheduler = require("./EventScheduler");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { TYPES, TIMING_EVENTS } = constants;
|
||||
const { ENTITY_TYPE_PLAYER } = TYPES;
|
||||
const { TIMING_EVENT_RESPAWN } = TIMING_EVENTS;
|
||||
|
||||
class Entity extends EventScheduler {
|
||||
constructor(info) {
|
||||
const id = info.type === ENTITY_TYPE_PLAYER ? info.id : Entity.composeID(info.type - 1, info.id);
|
||||
super(info.type, id, info.map.id);
|
||||
|
||||
this.isAlive = info.isAlive ?? true;
|
||||
this.chunk = null;
|
||||
this.type = null;
|
||||
this.map = info.map;
|
||||
this.typeID = info.type;
|
||||
this.id = id;
|
||||
|
||||
const { map } = this;
|
||||
|
||||
if (info.position) {
|
||||
const cell = info.map.grid.getCell(info.position.x, info.position.y);
|
||||
const coordinate = info.map.grid.getCoords(cell.xw, cell.xh)
|
||||
|
||||
this.position = coordinate;
|
||||
} else this.position = map.grid.randomNode(10);
|
||||
|
||||
if (info.type !== ENTITY_TYPE_PLAYER) {
|
||||
const currentChunk = this.getCurrentChunk();
|
||||
map[currentChunk].addItem(id, this);
|
||||
|
||||
this.chunk = currentChunk;
|
||||
};
|
||||
};
|
||||
|
||||
static composeID(entityType, entityID) {
|
||||
return ((entityType & 0xFF) << 24) | (entityID & 0xFFFFFF);
|
||||
}
|
||||
|
||||
static getType(compositeID) {
|
||||
return (compositeID >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
static getID(compositeID) {
|
||||
return compositeID & 0xFFFFFF;
|
||||
}
|
||||
|
||||
getCurrentChunk() {
|
||||
return `chunk_${Math.floor(this.position.x / (10 * 120))}-${Math.floor(this.position.y / (10 * 90))}`;
|
||||
}
|
||||
|
||||
removeFromChunk() {
|
||||
if (!this.chunk) return;
|
||||
|
||||
this.map[this.chunk].removeItem(this.id);
|
||||
|
||||
this.chunk = null;
|
||||
}
|
||||
|
||||
updateChunk() {
|
||||
const { chunk, map, id } = this;
|
||||
|
||||
const currentChunk = this.getCurrentChunk();
|
||||
if (chunk !== currentChunk) {
|
||||
this.removeFromChunk();
|
||||
|
||||
map[currentChunk].addItem(id, this);
|
||||
this.chunk = currentChunk;
|
||||
|
||||
this.chunkIndex = [Math.floor(this.position.x / (10 * 120)), Math.floor(this.position.y / (10 * 90))];
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
die() {
|
||||
if (!this.isAlive) return;
|
||||
|
||||
this.isAlive = false;
|
||||
|
||||
this.removeFromChunk();
|
||||
|
||||
const players = this.map.getLivingEntities([ENTITY_TYPE_PLAYER]);
|
||||
players.forEach(p => delete p.inDistance[this.id]); // this is not efficient, should update
|
||||
}
|
||||
|
||||
respawn(respawn = {}) {
|
||||
if (this.isAlive) return;
|
||||
|
||||
this.isAlive = true;
|
||||
this.position = respawn.position ? respawn.position : this.map.grid.randomNode(10);
|
||||
|
||||
this.updateChunk();
|
||||
|
||||
this.removeEvent(TIMING_EVENT_RESPAWN);
|
||||
}
|
||||
|
||||
markActive() {
|
||||
this.map[`chunkTest_${Math.floor(this.position.x / (10 * 120))}-${Math.floor(this.position.y / (10 * 90))}`].addItem(this.id, this);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Entity;
|
||||
@@ -0,0 +1,96 @@
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
class EntityGroup {
|
||||
constructor(info) {
|
||||
this.id = info.id;
|
||||
this.mapIDs = Array.isArray(info.mapIDs) ? info.mapIDs : [];
|
||||
this.structure = info.structure;
|
||||
this.entitySettings = info.entitySettings;
|
||||
this.members = new DoubleLinkedList();
|
||||
this.leaders = new DoubleLinkedList();
|
||||
this.isSummonPossible = true;
|
||||
this.predefinedArea = info.predefinedArea ? info.predefinedArea : {
|
||||
x: 1000,
|
||||
y: 1000,
|
||||
width: 750,
|
||||
height: 750
|
||||
};
|
||||
};
|
||||
|
||||
setAreaPosition(position) {
|
||||
this.predefinedArea.x = position.x;
|
||||
this.predefinedArea.y = position.y;
|
||||
}
|
||||
|
||||
getEntitySetting(groupMemberTypeID) {
|
||||
const entitySetting = this.entitySettings[groupMemberTypeID];
|
||||
|
||||
return entitySetting ? entitySetting : {
|
||||
speed: 600,
|
||||
respawnTime: 0
|
||||
};
|
||||
}
|
||||
|
||||
cancelRespawn() {
|
||||
if (!this.isSummonPossible) return;
|
||||
|
||||
this.members.each((key, value) => {
|
||||
value.cancelEvent(8);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
this.isSummonPossible = false;
|
||||
}
|
||||
|
||||
default() {
|
||||
this.members.clearEach(() => null);
|
||||
this.leaders.clearEach(() => null);
|
||||
|
||||
this.isSummonPossible = true;
|
||||
}
|
||||
|
||||
isDefeated() {
|
||||
let deadEntitiesCount = 0;
|
||||
|
||||
this.members.each((key, value) => {
|
||||
if (!value.isAlive) deadEntitiesCount++;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
this.leaders.each((key, value) => {
|
||||
if (!value.isAlive) deadEntitiesCount++;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return this.structure.length === deadEntitiesCount || (this.members.isEmpty() && this.leaders.isEmpty());
|
||||
}
|
||||
|
||||
addMember(entity) {
|
||||
this.members.addItem(entity.id, entity);
|
||||
}
|
||||
|
||||
addLeader(entity) {
|
||||
this.leaders.addItem(entity.id, entity);
|
||||
}
|
||||
|
||||
isLeader(id) {
|
||||
return this.leaders.hasElement(id);
|
||||
}
|
||||
|
||||
isLeaderAlive() {
|
||||
let isAlive = false;
|
||||
|
||||
this.leaders.each(function (key, value) {
|
||||
isAlive = value.isAlive;
|
||||
|
||||
return !value.isAlive;
|
||||
});
|
||||
|
||||
return isAlive;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = EntityGroup;
|
||||
@@ -0,0 +1,20 @@
|
||||
class EquipmentLimiter {
|
||||
constructor(info) {
|
||||
this.quantity = {
|
||||
current: 0,
|
||||
maximum: info.maximum
|
||||
};
|
||||
};
|
||||
|
||||
count(quantity) {
|
||||
|
||||
if (!quantity) return false;
|
||||
if (this.quantity.current + quantity > this.quantity.maximum || this.quantity.current + quantity < 0) return false;
|
||||
|
||||
this.quantity.current += quantity;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = EquipmentLimiter;
|
||||
@@ -0,0 +1,154 @@
|
||||
const EventEmitter = require("events");
|
||||
|
||||
const Game = require("./Game");
|
||||
|
||||
class EventScheduler extends EventEmitter {
|
||||
constructor(typeID, id, mapID) {
|
||||
super();
|
||||
|
||||
this.entity = {
|
||||
typeID,
|
||||
id
|
||||
};
|
||||
|
||||
this.events = {};
|
||||
this.mapID = mapID;
|
||||
};
|
||||
|
||||
setLocalization(mapID) {
|
||||
this.mapID = mapID;
|
||||
}
|
||||
|
||||
getLocalization = () => this.mapID;
|
||||
|
||||
setReusableSingleEvent(id, time, data) {
|
||||
if (!this.events[id] || this.events[id][0].isOver) {
|
||||
const previousTick = this.getEventTick(id);
|
||||
const relativeTo = previousTick ? previousTick : performance.now();
|
||||
const tickAt = relativeTo + (time > 0 ? time : 0);
|
||||
|
||||
const event = {
|
||||
id,
|
||||
entity: this.entity,
|
||||
isCancelled: false,
|
||||
index: 0,
|
||||
tickAt,
|
||||
data,
|
||||
getLocalization: this.getLocalization,
|
||||
isSingle: true,
|
||||
isOver: false
|
||||
};
|
||||
|
||||
Game.events.add(tickAt, event);
|
||||
this.events[id] = [event];
|
||||
} else {
|
||||
this.events[id][0].isCancelled = false;
|
||||
};
|
||||
}
|
||||
|
||||
setSingleEvent(id, time, data) {
|
||||
const tickAt = performance.now() + time;
|
||||
|
||||
const event = {
|
||||
id,
|
||||
entity: this.entity,
|
||||
isCancelled: false,
|
||||
index: 0,
|
||||
tickAt,
|
||||
data,
|
||||
getLocalization: this.getLocalization
|
||||
};
|
||||
|
||||
if (!this.events[id]) {
|
||||
Game.events.add(tickAt, event);
|
||||
this.events[id] = [event];
|
||||
};
|
||||
}
|
||||
|
||||
setEventTest2(id, time, data) {
|
||||
const previousTick = this.getEventTick(id);
|
||||
const relativeTo = previousTick ? previousTick : performance.now();
|
||||
const tickAt = relativeTo + time;
|
||||
|
||||
const event = {
|
||||
id,
|
||||
entity: this.entity,
|
||||
isCancelled: false,
|
||||
index: this.events[id] ? this.events[id].length - 1 : 0,
|
||||
tickAt,
|
||||
data,
|
||||
getLocalization: this.getLocalization
|
||||
};
|
||||
|
||||
Game.events.add(tickAt, event);
|
||||
this.events[id] = [event];
|
||||
}
|
||||
|
||||
setEvent(id, time, data) {
|
||||
const previousTick = this.getEventTick(id);
|
||||
const relativeTo = previousTick ? previousTick : performance.now();
|
||||
const tickAt = relativeTo + time;
|
||||
|
||||
const event = {
|
||||
id,
|
||||
entity: this.entity,
|
||||
isCancelled: false,
|
||||
index: this.events[id] ? this.events[id].length - 1 : 0,
|
||||
tickAt,
|
||||
data,
|
||||
getLocalization: this.getLocalization
|
||||
};
|
||||
|
||||
Game.events.add(tickAt, event);
|
||||
|
||||
if (this.events[id]) this.events[id].push(event);
|
||||
else this.events[id] = [event];
|
||||
}
|
||||
|
||||
setIndependentEvent(id, time, data) {
|
||||
const tickAt = performance.now() + time;
|
||||
|
||||
const event = {
|
||||
id,
|
||||
entity: this.entity,
|
||||
isCancelled: false,
|
||||
index: this.events[id] ? this.events[id].length : 0,
|
||||
tickAt,
|
||||
data,
|
||||
getLocalization: this.getLocalization
|
||||
};
|
||||
|
||||
Game.events.add(tickAt, event);
|
||||
|
||||
if (this.events[id]) this.events[id].push(event);
|
||||
else this.events[id] = [event];
|
||||
}
|
||||
|
||||
getEventTick(id, index) {
|
||||
const event = this.events[id];
|
||||
if (!event) return 0;
|
||||
|
||||
if (!index) index = event.length - 1;
|
||||
|
||||
const element = event[index];
|
||||
return element && !element.isOver && element.tickAt;
|
||||
}
|
||||
|
||||
removeEvent(id) {
|
||||
delete this.events[id];
|
||||
}
|
||||
|
||||
cancelEvent(id, index) {
|
||||
if (this.events[id]) {
|
||||
if (index == null) {
|
||||
for (let i = 0; i < this.events[id].length; i++) this.events[id][i].isCancelled = true;
|
||||
} else if (index === -1) {
|
||||
for (let i = 0; i < this.events[id].length - 1; i++) this.events[id][i].isCancelled = true;
|
||||
} else {
|
||||
this.events[id][index].isCancelled = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = EventScheduler;
|
||||
@@ -0,0 +1,43 @@
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
const PQ = require("../Utility/Q");
|
||||
|
||||
const maps = [];
|
||||
for (let i = 0; i < 20; i++) maps.push(require(`../Maps/Map_${i + 1}`));
|
||||
|
||||
const game = {
|
||||
players: {},
|
||||
packets: new DoubleLinkedList(),
|
||||
guilds: {},
|
||||
guildIslands: [],
|
||||
events: new PQ(),
|
||||
eventsFixed: new PQ(),
|
||||
maps,
|
||||
getTopic: function (topicName) {
|
||||
const topic = this.packets.getElement(topicName);
|
||||
if (topic) return topic;
|
||||
|
||||
return this.packets.addItem(topicName, []);
|
||||
},
|
||||
addPlayer: function (player) {
|
||||
this.players[player.id] = player;
|
||||
},
|
||||
/* testDestroyAll: function() {
|
||||
this.maps.forEach(map => map.npcs.forEach(npc => npc))
|
||||
},*/
|
||||
disconnectAll: function() {
|
||||
this.state = 0;
|
||||
|
||||
this.maps.forEach(map => map.players.forEach(player => player.disconnect(() => {
|
||||
socket.sockets[player.sessionID].end(2);
|
||||
delete this.players[player.id];
|
||||
delete socket.sockets[player.sessionID];
|
||||
}, true)))
|
||||
|
||||
// socket.us_listen_socket_close(this.listeningServer);
|
||||
// this.listeningServer = null;
|
||||
},
|
||||
listeningServer: null,
|
||||
state: 1
|
||||
};
|
||||
|
||||
module.exports = game;
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
class Grid {
|
||||
constructor(config) {
|
||||
this.width = config.width;
|
||||
this.height = config.height;
|
||||
this.collision = config.collision ?? {};
|
||||
}
|
||||
|
||||
getCoords(tileX, tileY) {
|
||||
const x = 0 + tileX * 24 + !(tileY % 2) * (24 / 2)
|
||||
const y = 0 + tileY * (18 / 2);
|
||||
|
||||
return {
|
||||
x,
|
||||
y
|
||||
};
|
||||
}
|
||||
|
||||
getCell(roundedX, roundedY) {
|
||||
const x = roundedX - 0;
|
||||
const y = roundedY - 0;
|
||||
const isEven = ((Math.floor(y / 9) * 9 - 9) / 18) % 1 !== 0;
|
||||
const xw = isEven ? Math.floor(x / 24) : Math.floor((x + 12) / 24);
|
||||
const xh = Math.floor(y / (18 / 2));
|
||||
|
||||
return {
|
||||
xw,
|
||||
xh
|
||||
};
|
||||
}
|
||||
|
||||
isValidNode(node) {
|
||||
const blockedTile = this.collision[`${node.xw}-${node.xh}`];
|
||||
return (!blockedTile && node.xw > 0 && node.xh > 0 && node.xw < this.width / 24 && node.xh < (this.height * 2) / 18);
|
||||
}
|
||||
|
||||
randomNodeNear(nearToX, nearToY, radius) {
|
||||
let r = radius * Math.sqrt(Math.random());
|
||||
let a = 2 * Math.PI * Math.random();
|
||||
|
||||
let nearX = Math.round(r * Math.cos(a) + nearToX);
|
||||
let nearY = Math.round(r * Math.sin(a) + nearToY);
|
||||
|
||||
let triedNode = this.getCell(nearX, nearY);
|
||||
|
||||
const { xw, xh } = triedNode;
|
||||
let { x, y } = this.getCoords(xw, xh);
|
||||
|
||||
while (!this.isValidNode(triedNode) || (x === nearToX && y === nearToY) || Math.pow(x - nearToX, 2) + Math.pow(y - nearToY, 2) < Math.pow(500, 2)) {
|
||||
r = radius * Math.sqrt(Math.random());
|
||||
a = 2 * Math.PI * Math.random();
|
||||
nearX = Math.round(r * Math.cos(a) + nearToX);
|
||||
nearY = Math.round(r * Math.sin(a) + nearToY);
|
||||
|
||||
triedNode = this.getCell(nearX, nearY);
|
||||
|
||||
const { xw, xh } = triedNode;
|
||||
const coords = this.getCoords(xw, xh);
|
||||
x = coords.x;
|
||||
y = coords.y;
|
||||
};
|
||||
|
||||
return {
|
||||
x,
|
||||
y
|
||||
};
|
||||
}
|
||||
|
||||
randomNodeInArea(area) {
|
||||
let xw = 0;
|
||||
let xh = 0;
|
||||
|
||||
const minimumWidth = area.x - area.width / 2;
|
||||
const maximumWidth = area.x + area.width / 2;
|
||||
const minimumHeight = area.y - area.height / 2;
|
||||
const maximumHeight = area.y + area.height / 2;
|
||||
|
||||
do {
|
||||
xw = Math.floor((Math.random() * (maximumWidth - minimumWidth + 1) + minimumWidth) / 24);
|
||||
xh = Math.floor((Math.random() * (maximumHeight - minimumHeight + 1) + minimumHeight) / (18 / 2));
|
||||
} while (!this.isValidNode({ xw, xh }));
|
||||
|
||||
return this.getCoords(xw, xh);
|
||||
}
|
||||
|
||||
randomNode(percentage) {
|
||||
let xw = 0;
|
||||
let xh = 0;
|
||||
|
||||
if (!percentage) percentage = 0;
|
||||
percentage /= 200;
|
||||
|
||||
const minimumWidth = 0 + this.width * percentage;
|
||||
const maximumWidth = this.width * (1 - percentage);
|
||||
const minimumHeight = 0 + this.height * percentage;
|
||||
const maximumHeight = this.height * (1 - percentage);
|
||||
|
||||
do {
|
||||
xw = Math.floor((Math.random() * (maximumWidth - minimumWidth + 1) + minimumWidth) / 24);
|
||||
xh = Math.floor((Math.random() * (maximumHeight - minimumHeight + 1) + minimumHeight) / (18 / 2));
|
||||
} while (!this.isValidNode({ xw, xh }));
|
||||
|
||||
return this.getCoords(xw, xh);
|
||||
}
|
||||
|
||||
getNodeNeighbours(node) {
|
||||
const { xw, xh } = node;
|
||||
let rightdown, leftdown, rightup, leftup;
|
||||
|
||||
if (node.xh % 2 !== 0) {
|
||||
rightdown = { xw, xh: xh + 1 };
|
||||
leftdown = { xw: xw - 1, xh: xh + 1 };
|
||||
rightup = { xw, xh: xh - 1 };
|
||||
leftup = { xw: xw - 1, xh: xh - 1 };
|
||||
} else {
|
||||
rightdown = { xw: xw + 1, xh: xh + 1 };
|
||||
leftdown = { xw: xw, xh: xh + 1 };
|
||||
rightup = { xw: xw + 1, xh: xh - 1 };
|
||||
leftup = { xw: xw, xh: xh - 1 };
|
||||
};
|
||||
|
||||
return [rightup, leftup, leftdown, rightdown];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Grid;
|
||||
@@ -0,0 +1,15 @@
|
||||
const Grid = require("./Grid");
|
||||
const data = require("../Data/maps");
|
||||
const grids = [];
|
||||
|
||||
data.forEach(d => {
|
||||
const grid = new Grid({
|
||||
width: d.size.width,
|
||||
height: d.size.height,
|
||||
collision: d.collision
|
||||
});
|
||||
|
||||
grids.push(grid);
|
||||
})
|
||||
|
||||
module.exports = grids;
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
const InventoryList = require("./InventoryList");
|
||||
|
||||
const LEVELS = require("../Data/levels");
|
||||
|
||||
const GUILD_MAXIMUM_REQUESTS_SIZE = 100;
|
||||
|
||||
const ECONOMY = [1, 2];
|
||||
|
||||
const globalRequests = {};
|
||||
|
||||
class Guild {
|
||||
constructor(info) {
|
||||
this.id = info.id;
|
||||
this.topic = `g${info.id}`;
|
||||
this.name = info.name;
|
||||
this.tag = info.tag;
|
||||
this.description = info.description;
|
||||
this.ownerID = info.ownerID;
|
||||
this.bank = new GuildBank();
|
||||
this.taxRates = info.taxRates;
|
||||
this.experiencePoints = info.experiencePoints ?? 0;
|
||||
this.level = LEVELS.guildLevel(this.experiencePoints) ?? 1;
|
||||
this.size = 0;
|
||||
this.sizeMaximum = 30;
|
||||
this.members = [];
|
||||
this.requests = [];
|
||||
this.diplomacy = {};
|
||||
this.timestamps = info.timestamps;
|
||||
|
||||
this.bank.depositTax({
|
||||
categoryID: 1,
|
||||
id: 1,
|
||||
amount: 0
|
||||
});
|
||||
|
||||
this.bank.depositTax({
|
||||
categoryID: 1,
|
||||
id: 2,
|
||||
amount: 0
|
||||
});
|
||||
};
|
||||
|
||||
levelUpdate(amount) {
|
||||
this.experiencePoints += amount;
|
||||
|
||||
if (this.level >= 25) return -1;
|
||||
|
||||
const newLevel = LEVELS.guildLevel(this.experiencePoints);
|
||||
if (newLevel !== this.level) {
|
||||
if (newLevel > 25) this.level = 25;
|
||||
else this.level = newLevel;
|
||||
|
||||
return this.level;
|
||||
};
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
isLeader(playerID) {
|
||||
return this.ownerID === playerID;
|
||||
}
|
||||
|
||||
hasRequested(playerID) {
|
||||
const isRequestPresent = this.requests.some(r => r.playerID === playerID);
|
||||
|
||||
return isRequestPresent;
|
||||
}
|
||||
|
||||
setPermission(playerID, permission) {
|
||||
const member = this.members.find(m => m.id === playerID);
|
||||
if (!member) return;
|
||||
|
||||
member.permission = permission;
|
||||
}
|
||||
|
||||
addRequest(playerID, message) {
|
||||
//if (this.requestSize >= GUILD_MAXIMUM_REQUESTS_SIZE) return;
|
||||
if (globalRequests[playerID] >= 8) return;
|
||||
console.info(globalRequests[playerID], "request count for player", playerID);
|
||||
|
||||
if (globalRequests[playerID]) globalRequests[playerID]++;
|
||||
else globalRequests[playerID] = 1;
|
||||
|
||||
const request = {
|
||||
playerID,
|
||||
message
|
||||
};
|
||||
|
||||
this.requests.push(request);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
removeRequest(playerID) {
|
||||
if (globalRequests[playerID]) globalRequests[playerID]--;
|
||||
|
||||
this.requests = this.requests.filter(r => r.playerID !== playerID);
|
||||
}
|
||||
|
||||
isMember(playerID) {
|
||||
return this.members.find(member => member.id === playerID) ? true : false;
|
||||
}
|
||||
|
||||
getMember(playerID) {
|
||||
return this.members.find(member => member.id === playerID);
|
||||
}
|
||||
|
||||
addMember(member) {
|
||||
if (this.size >= this.sizeMaximum) return;
|
||||
if (this.isMember(member.id)) return;
|
||||
|
||||
this.removeRequest(member.id);
|
||||
this.members.push(member);
|
||||
|
||||
delete globalRequests[member.id];
|
||||
|
||||
this.size++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
removeMember(playerID) {
|
||||
this.size--;
|
||||
this.members = this.members.filter(member => member.id !== playerID);
|
||||
}
|
||||
};
|
||||
|
||||
class GuildBank {
|
||||
constructor(info) {
|
||||
this.list = new InventoryList();
|
||||
this.deposits = [];
|
||||
};
|
||||
|
||||
depositTax(resource) {
|
||||
if (!ECONOMY.includes(resource.id)) return;
|
||||
|
||||
this.list.accumulateItem(resource.id, resource);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Guild;
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
|
||||
const { TIMING_EVENTS } = constants;
|
||||
const { TIMING_EVENT_RESPAWN } = TIMING_EVENTS;
|
||||
|
||||
const Game = require("./Game");
|
||||
const { guilds } = Game;
|
||||
|
||||
class GuildIsland {
|
||||
constructor(info) {
|
||||
this.id = info.id;
|
||||
this.locationMapID = info.locationMapID;
|
||||
this.index = info.index;
|
||||
this.position = info.position;
|
||||
this.guild = info.guild;
|
||||
this.towers = [];
|
||||
this.totalDamageReceived = new DoubleLinkedList();
|
||||
};
|
||||
|
||||
updateReceivedDamage(guildID, damage) {
|
||||
this.totalDamageReceived.accumulateItem(guildID, damage);
|
||||
}
|
||||
|
||||
getTower(id) {
|
||||
const tower = this.towers.find(t => t.id === id);
|
||||
return tower;
|
||||
}
|
||||
|
||||
addTower(tower) {
|
||||
this.towers.push(tower);
|
||||
}
|
||||
|
||||
updateOwnership() {
|
||||
const towersAlive = this.towers.filter(t => t.hp.current);
|
||||
if (towersAlive.length) return null;
|
||||
|
||||
let maximumDamage = 0;
|
||||
let newGuildID = 0;
|
||||
|
||||
this.totalDamageReceived.clearEach((guildID, damage) => {
|
||||
if (damage > maximumDamage) {
|
||||
maximumDamage = damage;
|
||||
newGuildID = guildID;
|
||||
};
|
||||
});
|
||||
|
||||
this.towers.forEach(tower => tower.setEvent(TIMING_EVENT_RESPAWN, 10000));
|
||||
|
||||
const topicOld = this.guild.topic;
|
||||
|
||||
const guildOld = guilds[this.guild.id];
|
||||
const guildNew = newGuildID ? guilds[newGuildID] : { tag: new ArrayBuffer(0), id: 0 };
|
||||
|
||||
const topicNew = guildNew.topic;
|
||||
|
||||
this.guild = guildNew;
|
||||
|
||||
return {
|
||||
guildOld,
|
||||
guildNew,
|
||||
topicOld,
|
||||
topicNew
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = GuildIsland;
|
||||
@@ -0,0 +1,63 @@
|
||||
const EventEmitter = require("events");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { CATEGORIES } = constants;
|
||||
const { CATEGORY_HARPOONER } = CATEGORIES;
|
||||
|
||||
const eventsMapped = {
|
||||
1: 197,
|
||||
2: 198,
|
||||
3: 199,
|
||||
4: 200,
|
||||
5: 201,
|
||||
6: 202
|
||||
};
|
||||
|
||||
class Harpooner extends EventEmitter {
|
||||
constructor(info) {
|
||||
super();
|
||||
|
||||
this.id = info.id;
|
||||
this.categoryID = CATEGORY_HARPOONER;
|
||||
this.eventID = eventsMapped[this.id];
|
||||
this.damage = info.damage;
|
||||
this.range = info.range;
|
||||
this.reload = info.reload;
|
||||
this.amount = info.amount;
|
||||
this.amountEquipped = info.amountEquipped || 0;
|
||||
this.resourceSharedLimiter = info.resourceSharedLimiter;
|
||||
}
|
||||
|
||||
equip(quantity) {
|
||||
if (this.amountEquipped + quantity > this.amount) return
|
||||
|
||||
const isTypeQunatityExhausted = this.resourceSharedLimiter.count(quantity);
|
||||
if (!isTypeQunatityExhausted) return
|
||||
|
||||
this.amountEquipped += quantity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unequip(quantity) {
|
||||
if (this.amountEquipped - quantity < 0) return
|
||||
|
||||
const isTypeQunatityExhausted = this.resourceSharedLimiter.count(-quantity);
|
||||
if (!isTypeQunatityExhausted) return
|
||||
|
||||
this.amountEquipped -= quantity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
return {
|
||||
typeID: 2,
|
||||
id: this.id,
|
||||
amount: this.amount,
|
||||
amountEquipped: this.amountEquipped
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Harpooner;
|
||||
@@ -0,0 +1,303 @@
|
||||
const Item = require("./Item");
|
||||
const Cannon = require("./Cannon");
|
||||
const Harpooner = require("./Harpooner");
|
||||
const InventoryList = require("../Game/InventoryList");
|
||||
|
||||
const schemeCannons = require("../Data/cannons");
|
||||
const schemeHarpooners = require("../Data/harpooners");
|
||||
const ammunitions = require("../Data/ammunitions");
|
||||
const harpoons = require("../Data/harpoons");
|
||||
const itemTable = require("../Data/items");
|
||||
const constants = require("../Data/constants");
|
||||
|
||||
const { CATEGORIES, AFFECTS } = constants;
|
||||
const { CATEGORY_ITEM, CATEGORY_AMMUNITION, CATEGORY_HARPOON, CATEGORY_DESIGN, CATEGORY_ECONOMY, CATEGORY_STATUS, CATEGORY_CANNON, CATEGORY_HARPOONER } = CATEGORIES;
|
||||
const { AFFECT_CANNON_DAMAGE, AFFECT_HARPOONER_DAMAGE, AFFECT_PROTECTION, AFFECT_SAILING_SPEED, AFFECT_HITPOINT } = AFFECTS;
|
||||
|
||||
const AFFECT_TABLE = {
|
||||
1: [1, 2, 3, 4],
|
||||
[AFFECT_PROTECTION]: [5, 6, 7, 8]
|
||||
};
|
||||
|
||||
class Inventory {
|
||||
constructor(info) {
|
||||
this.selectedAmmunition = 0;
|
||||
this.selectedHarpoon = 0;
|
||||
|
||||
this.resourceSharedLimiter = info.resourceSharedLimiter;
|
||||
|
||||
this[CATEGORY_AMMUNITION] = new InventoryList();
|
||||
this[CATEGORY_HARPOON] = new InventoryList();
|
||||
this[CATEGORY_ITEM] = new InventoryList();
|
||||
this[CATEGORY_STATUS] = new InventoryList();
|
||||
this[CATEGORY_ECONOMY] = new InventoryList();
|
||||
this[CATEGORY_DESIGN] = new InventoryList();
|
||||
this[CATEGORY_CANNON] = new InventoryList();
|
||||
this[CATEGORY_HARPOONER] = new InventoryList();
|
||||
|
||||
this[CATEGORY_DESIGN].addItem(1, {
|
||||
categoryID: CATEGORY_DESIGN,
|
||||
id: 1,
|
||||
amount: 1
|
||||
}); // default design
|
||||
}
|
||||
|
||||
refreshSingle(element) {
|
||||
switch (element.categoryID) {
|
||||
case CATEGORY_ECONOMY:
|
||||
|
||||
this[CATEGORY_ECONOMY].accumulateItem(element.id, element);
|
||||
break;
|
||||
case CATEGORY_ITEM:
|
||||
const inventoryItem = this[CATEGORY_ITEM].getElement(element.id);
|
||||
if (!inventoryItem) return this.addItem(element);
|
||||
|
||||
inventoryItem.amount += element.amount;
|
||||
break;
|
||||
case CATEGORY_AMMUNITION:
|
||||
if (!element.amount) return;
|
||||
|
||||
this[CATEGORY_AMMUNITION].accumulateItem(element.id, element);
|
||||
|
||||
if (!this.selectedAmmunition) this.setSelectedAmmunition(element.id);
|
||||
break;
|
||||
case CATEGORY_HARPOON:
|
||||
if (!element.amount) return;
|
||||
|
||||
this[CATEGORY_HARPOON].accumulateItem(element.id, element);
|
||||
|
||||
if (!this.selectedHarpoon) this.setSelectedHarpoon(element.id);
|
||||
break;
|
||||
case CATEGORY_DESIGN:
|
||||
this[CATEGORY_DESIGN].accumulateItem(element.id, element);
|
||||
break;
|
||||
case CATEGORY_STATUS:
|
||||
this[CATEGORY_STATUS].accumulateItem(element.id, element);
|
||||
break;
|
||||
case CATEGORY_CANNON:
|
||||
let cannon = this[CATEGORY_CANNON].getElement(element.id);
|
||||
if (!cannon) {
|
||||
const schemeCannon = schemeCannons[element.id];
|
||||
if (!schemeCannon) return;
|
||||
|
||||
cannon = new Cannon({
|
||||
id: element.id,
|
||||
damage: schemeCannon.damage,
|
||||
range: schemeCannon.range,
|
||||
reload: schemeCannon.reload,
|
||||
scatter: schemeCannon.scatter,
|
||||
amount: element.amount,
|
||||
amountEquipped: element.amountEquipped,
|
||||
resourceSharedLimiter: this.resourceSharedLimiter.cannon
|
||||
});
|
||||
|
||||
this[CATEGORY_CANNON].accumulateItem(cannon.id, cannon);
|
||||
} else this[CATEGORY_CANNON].accumulateItem(cannon.id, element);
|
||||
break;
|
||||
case CATEGORY_HARPOONER:
|
||||
let harpooner = this[CATEGORY_HARPOONER].getElement(element.id);
|
||||
if (!harpooner) {
|
||||
const schemeHarpooner = schemeHarpooners[element.id];
|
||||
if (!schemeHarpooner) return;
|
||||
|
||||
harpooner = new Harpooner({
|
||||
id: element.id,
|
||||
damage: schemeHarpooner.damage,
|
||||
range: schemeHarpooner.range,
|
||||
reload: schemeHarpooner.reload,
|
||||
scatter: schemeHarpooner.scatter,
|
||||
amount: element.amount,
|
||||
amountEquipped: element.amountEquipped,
|
||||
resourceSharedLimiter: this.resourceSharedLimiter.harpooner
|
||||
});
|
||||
|
||||
this[CATEGORY_HARPOONER].accumulateItem(harpooner.id, harpooner);
|
||||
} else this[CATEGORY_HARPOONER].accumulateItem(element.id, element);
|
||||
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
use(categoryID, id, amount) {
|
||||
const category = this[categoryID];
|
||||
if (!category) return
|
||||
|
||||
const item = category[id];
|
||||
if (!item || !item.amount) return
|
||||
|
||||
const itemScheme = itemTable[id];
|
||||
if (!itemScheme && categoryID === CATEGORY_ITEM) return
|
||||
|
||||
let removedCount = -1;
|
||||
|
||||
if (itemScheme.isDurable) item.isActive = true;
|
||||
else {
|
||||
if (categoryID === CATEGORY_ITEM) item.nextUseAt = performance.now() + itemScheme.cooldown;
|
||||
removedCount = itemScheme.useOnImpact || categoryID !== CATEGORY_ITEM ? Math.min(amount, item.amount) : 0;
|
||||
|
||||
item.amount -= removedCount;
|
||||
item.usedAmount = removedCount;
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
disable(id) {
|
||||
const item = this[CATEGORY_ITEM].getElement(id);
|
||||
if (!item) return;
|
||||
|
||||
item.isActive = false;
|
||||
}
|
||||
|
||||
alterAmount(categoryID, id, amount) {
|
||||
const category = this[categoryID];
|
||||
if (!category) return
|
||||
|
||||
const element = category.getElement(id);
|
||||
if (!element || !element.amount) return
|
||||
|
||||
const usedAmount = Math.min(amount, element.amount);
|
||||
element.amount -= usedAmount;
|
||||
|
||||
return usedAmount;
|
||||
}
|
||||
|
||||
setSelectedAmmunition(id) {
|
||||
if (!id) return this.selectedAmmunition = 0;
|
||||
|
||||
if (!ammunitions[id]) return;
|
||||
|
||||
const ammunition = this[CATEGORY_AMMUNITION].hasElement(id);
|
||||
if (!ammunition) return this.selectedAmmunition = 0;
|
||||
|
||||
console.info(ammunition)
|
||||
|
||||
return this.selectedAmmunition = id;
|
||||
}
|
||||
|
||||
getSelectedAmmunition() {
|
||||
const selectedAmmunition = this[CATEGORY_AMMUNITION].getElement(this.selectedAmmunition);
|
||||
if (!selectedAmmunition) return;
|
||||
|
||||
return selectedAmmunition;
|
||||
}
|
||||
|
||||
setSelectedHarpoon(id) {
|
||||
if (!id) return this.selectedHarpoon = 0;
|
||||
|
||||
if (!harpoons[id]) return;
|
||||
|
||||
const harpoon = this[CATEGORY_HARPOON].hasElement(id);
|
||||
if (!harpoon) return this.selectedHarpoon = 0;
|
||||
|
||||
return this.selectedHarpoon = id;
|
||||
}
|
||||
|
||||
getSeletedHarpoon() {
|
||||
const selectedHarpoon = this[CATEGORY_HARPOON].getElement(this.selectedHarpoon);
|
||||
if (!selectedHarpoon) return;
|
||||
|
||||
return selectedHarpoon;
|
||||
}
|
||||
|
||||
addItem(entry) {
|
||||
const itemScheme = itemTable[entry.id];
|
||||
if (!itemScheme) return;
|
||||
if (!entry.amount) return;
|
||||
|
||||
const { time, scale, affects } = itemScheme;
|
||||
|
||||
const item = new Item({
|
||||
id: entry.id,
|
||||
amount: entry.amount,
|
||||
time,
|
||||
scale,
|
||||
affects
|
||||
});
|
||||
|
||||
this[CATEGORY_ITEM].addItem(item.id, item);
|
||||
}
|
||||
|
||||
getItemsByAffect(affect) {
|
||||
const items = Object.values(this[CATEGORY_ITEM]);
|
||||
return items.filter(item => item.affects.includes(affect));
|
||||
}
|
||||
|
||||
useAvailableItemsByAffect(affect, cb) {
|
||||
AFFECT_TABLE[affect]
|
||||
.forEach(itemID => {
|
||||
const item = this[CATEGORY_ITEM].getElement(itemID);
|
||||
if (item && item.isActive && item.amount) {
|
||||
const use = item.use();
|
||||
cb(use.scale, itemID, use.statusID, use.isStatusActive);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getPosessions() {
|
||||
return {
|
||||
[CATEGORY_AMMUNITION]: this[CATEGORY_AMMUNITION],
|
||||
[CATEGORY_HARPOON]: this[CATEGORY_HARPOON],
|
||||
[CATEGORY_DESIGN]: this[CATEGORY_DESIGN]
|
||||
};
|
||||
}
|
||||
|
||||
getDesignIDs() {
|
||||
const designIDs = [];
|
||||
this[CATEGORY_DESIGN].each(key => designIDs.push(key));
|
||||
|
||||
return designIDs;
|
||||
}
|
||||
|
||||
getEconomy() {
|
||||
const economy = [];
|
||||
this[CATEGORY_ECONOMY].each((key, value) => economy.push(value));
|
||||
|
||||
return economy;
|
||||
}
|
||||
|
||||
getAmmunitions() {
|
||||
const ammunitions = [];
|
||||
this[CATEGORY_AMMUNITION].each((key, value) => ammunitions.push(value));
|
||||
|
||||
return ammunitions;
|
||||
}
|
||||
|
||||
getHarpoons() {
|
||||
const harpoons = [];
|
||||
this[CATEGORY_HARPOON].each((key, value) => harpoons.push(value));
|
||||
|
||||
return harpoons;
|
||||
}
|
||||
|
||||
getItems() {
|
||||
const items = [];
|
||||
this[CATEGORY_ITEM].each((key, value) => items.push(value));
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
getCannons() {
|
||||
const cannons = [];
|
||||
this[CATEGORY_CANNON].each((key, value) => cannons.push(value));
|
||||
|
||||
return cannons;
|
||||
}
|
||||
|
||||
getHarpooners() {
|
||||
const harpooners = [];
|
||||
this[CATEGORY_HARPOONER].each((key, value) => harpooners.push(value));
|
||||
|
||||
return harpooners;
|
||||
}
|
||||
|
||||
hasDesign(id) {
|
||||
return this[CATEGORY_DESIGN].hasElement(id);
|
||||
}
|
||||
|
||||
getItem(id) {
|
||||
return this[CATEGORY_ITEM].getElement(id);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Inventory;
|
||||
@@ -0,0 +1,41 @@
|
||||
const LinkedList = require("../Utility/LinkedList");
|
||||
|
||||
class LinkedListNode {
|
||||
constructor(key, value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.next = null;
|
||||
};
|
||||
};
|
||||
|
||||
class InventoryList extends LinkedList {
|
||||
constructor() {
|
||||
super();
|
||||
};
|
||||
|
||||
accumulateItem(key, value) {
|
||||
const entry = this.map.get(key);
|
||||
const updatedValue = entry ? entry.value.amount + value.amount : value;
|
||||
|
||||
if (entry) entry.value.amount = updatedValue;
|
||||
else this.addItem(key, updatedValue); /// eeeh
|
||||
}
|
||||
|
||||
addItem(key, value) {
|
||||
if (!key || !value || !value.categoryID || !value.id || isNaN(value.amount)) return console.info("addItem", key, value);
|
||||
|
||||
const temp = new LinkedListNode(key, value);
|
||||
|
||||
if (!this.head) {
|
||||
this.head = temp;
|
||||
this.tail = temp;
|
||||
} else {
|
||||
this.tail.next = temp;
|
||||
this.tail = temp;
|
||||
};
|
||||
|
||||
this.map.set(key, temp);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = InventoryList;
|
||||
@@ -0,0 +1,61 @@
|
||||
const itemTable = require("../Data/items");
|
||||
const constants = require("../Data/constants");
|
||||
|
||||
const { CATEGORIES } = constants;
|
||||
const { CATEGORY_ITEM } = CATEGORIES;
|
||||
|
||||
class Item {
|
||||
constructor(config) {
|
||||
this.categoryID = CATEGORY_ITEM;
|
||||
this.id = config.id;
|
||||
this.amount = config.amount;
|
||||
this.affects = config.affects;
|
||||
this.scale = config.scale;
|
||||
this.isDurable = config.isDurable;
|
||||
this.time = config.time;
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
use(amount) {
|
||||
const itemScheme = itemTable[this.id];
|
||||
if (!itemScheme) return;
|
||||
|
||||
if (!this.amount) return global.logger.send({ event: "itemUsage", reason: "item amount is 0" });
|
||||
|
||||
if (!amount) amount = 1;
|
||||
|
||||
const removedAmount = Math.min(amount, this.amount);
|
||||
this.amount -= removedAmount;
|
||||
|
||||
if (!this.amount && itemScheme.isDurable) this.isActive = false;
|
||||
|
||||
return {
|
||||
scale: this.scale,
|
||||
statusID: itemScheme.statusID,
|
||||
isStatusActive: this.amount && itemScheme.isDurable
|
||||
};
|
||||
}
|
||||
|
||||
activate(amount) {
|
||||
const itemScheme = itemTable[this.id];
|
||||
if (!itemScheme) return;
|
||||
if (!this.amount) return;
|
||||
|
||||
if (!amount) amount = 1;
|
||||
|
||||
const removedAmount = itemScheme.useOnImpact ? Math.min(amount, this.amount) : 0;
|
||||
|
||||
if (itemScheme.isDurable) this.isActive = true;
|
||||
else {
|
||||
this.nextUseAt = performance.now() + itemScheme.cooldown;
|
||||
this.amount -= removedAmount;
|
||||
};
|
||||
|
||||
return {
|
||||
...this,
|
||||
removedAmount
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Item;
|
||||
+511
@@ -0,0 +1,511 @@
|
||||
const EventEmitter = require("events");
|
||||
|
||||
const Utility = require("./Utility");
|
||||
const Reward = require("./Reward");
|
||||
const EntityGroup = require("./EntityGroup");
|
||||
|
||||
const mapMatrix = require("../Data/mapMatrix");
|
||||
const constants = require("../Data/constants");
|
||||
|
||||
const DoubleLinkedList = require("../Utility/DoubleLinkedList");
|
||||
|
||||
const grids = require("./Grids");
|
||||
|
||||
const { TYPES, EVENTS } = constants;
|
||||
const { ENTITY_TYPE_PLAYER } = TYPES;
|
||||
const { EVENT_SPAWN_ADMIRAL } = EVENTS;
|
||||
|
||||
const COORDINATE_REGIONS = ["A", "B", "C"];
|
||||
const COORDINATE_SECTIONS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
|
||||
|
||||
function getType(compositeID) {
|
||||
return (compositeID >> 24) & 0xFF;
|
||||
};
|
||||
|
||||
class GameMap extends EventEmitter {
|
||||
constructor(config) {
|
||||
super();
|
||||
|
||||
this.w = config.size.width;
|
||||
this.h = config.size.height;
|
||||
this.cw = config.size.chunk.width;
|
||||
this.ch = config.size.chunk.height;
|
||||
this.chunkRows = config.size.height / config.size.chunk.height;
|
||||
this.chunkColumns = config.size.width / config.size.chunk.width;
|
||||
this.name = config.name;
|
||||
this.type = config.type;
|
||||
this.id = config.id;
|
||||
this.neighbours = config.neighbours;
|
||||
this.collision = config.collision ?? {};
|
||||
this.level = config.level;
|
||||
this.spawnedEntitiesTotalCount = 0;
|
||||
this.availableIdentifiers = [];
|
||||
this.players = new Map();
|
||||
this.npcs = new Map();
|
||||
this.monsters = new Map();
|
||||
this.towers = new Map();
|
||||
this.collectables = new Map();
|
||||
this.entities = [this.players, this.npcs, this.monsters, this.towers, this.collectables];
|
||||
this.entityGroups = Array.isArray(config.entityGroups) ? config.entityGroups.map(e => new EntityGroup(e)) : [];
|
||||
this.chunksData = [];
|
||||
this.networkPacketsGlobal = [];
|
||||
this.networkPacketsLocal = [];
|
||||
this.networkPacketsInvidiual = {};
|
||||
this.testPackets = [];
|
||||
this.eventBuffer = {};
|
||||
this.eventAuthors = [];
|
||||
|
||||
this.lookingForTarget = [];
|
||||
|
||||
this.dirtyChunks = [];
|
||||
|
||||
this.grid = grids[this.id - 1];
|
||||
|
||||
for (let i = 0; i < this.chunkColumns; i++) {
|
||||
for (let j = 0; j < this.chunkRows; j++) {
|
||||
const m = new DoubleLinkedList();
|
||||
|
||||
this[`chunkTest_${i}-${j}`] = m;
|
||||
this[`chunk_${i}-${j}`] = new DoubleLinkedList();
|
||||
|
||||
this.dirtyChunks.push(m);
|
||||
|
||||
this.chunksData.push({
|
||||
x: i * config.size.chunk.width,
|
||||
y: j * config.size.chunk.height,
|
||||
name: `chunk_${i}-${j}`,
|
||||
nameTest: `chunkTest_${i}-${j}`
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
registerIndividualNetworkPacket(identifier, packets, metadata = []) {
|
||||
if (!(packets instanceof Array)) packets = [packets];
|
||||
if (!(metadata instanceof Array)) metadata = [metadata];
|
||||
|
||||
if (!this.networkPacketsInvidiual[identifier]) this.networkPacketsInvidiual[identifier] = [];
|
||||
|
||||
packets.forEach(packet => this.networkPacketsInvidiual[identifier].push({
|
||||
index: this.networkPacketsLocal.length,
|
||||
packet,
|
||||
metadata
|
||||
}));
|
||||
}
|
||||
|
||||
registerGlobalNetworkPacket(identifier, packets) {
|
||||
if (!(packets instanceof Array)) packets = [packets];
|
||||
const entity = this.getEntityFromSubgroup(identifier);
|
||||
|
||||
packets.forEach(p => this.networkPacketsGlobal.push({
|
||||
position: entity.position,
|
||||
authorID: entity.id,
|
||||
packet: p
|
||||
}));
|
||||
}
|
||||
|
||||
registerNetworkPacket(identifier, packets, headers = []) {
|
||||
if (!(packets instanceof Array)) packets = [packets];
|
||||
|
||||
const entity = this.getEntityFromSubgroup(identifier);
|
||||
|
||||
if (headers[0] === 59) packets.forEach((p, i) => this.testPackets.push({ position: entity.position, authorID: entity.id, packet: p, header: headers[i] ? headers[i] : 59 }));
|
||||
else packets.forEach((p, i) => this.networkPacketsLocal.push({ position: entity.position, authorID: entity.id, packet: p, header: headers[i] ? headers[i] : 0 }));
|
||||
}
|
||||
|
||||
getNeighbourMapsInRadius(radius) {
|
||||
const maps = [];
|
||||
|
||||
for (let i = 0; i < mapMatrix.length; i++) {
|
||||
const mapRow = mapMatrix[i];
|
||||
|
||||
for (let j = 0; j < mapRow.length; j++) {
|
||||
if (this.id === mapRow[j]) {
|
||||
const startFromColumn = Math.max(0, j - radius);
|
||||
const startFromRow = Math.max(0, i - radius);
|
||||
const rawColumnBoundary = (j + radius) + 1;
|
||||
const rawRowBoundary = (i + radius) + 1;
|
||||
|
||||
const columnBoundary = rawColumnBoundary > mapRow.length - 1 ? mapRow.length : rawColumnBoundary;
|
||||
const rowBoundary = rawRowBoundary > mapMatrix.length - 1 ? mapMatrix.length : rawRowBoundary;
|
||||
|
||||
for (let l = startFromRow; l < rowBoundary; l++)
|
||||
for (let k = startFromColumn; k < columnBoundary; k++)
|
||||
maps.push(mapMatrix[l][k]);
|
||||
break;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
return maps;
|
||||
}
|
||||
|
||||
spawnEntityGroups(cb, NPC, attackCallback, dieCallback) {
|
||||
for (let i = 0, length = this.entityGroups.length; i < length; i++) {
|
||||
const group = this.entityGroups[i];
|
||||
if (!group.isDefeated()) continue;
|
||||
|
||||
group.default();
|
||||
|
||||
const randomPosition = this.grid.randomNode(20);
|
||||
group.setAreaPosition(randomPosition);
|
||||
|
||||
for (let i = 0, length = group.structure.length; i < length; i++) {
|
||||
const { typeID, entityTypeID, groupMemberTypeID } = group.structure[i];
|
||||
|
||||
const entityTypeSetting = group.getEntitySetting(groupMemberTypeID);
|
||||
|
||||
const preSelectedDestinationNode = this.grid.randomNodeInArea(group.predefinedArea);
|
||||
const spawnData = {
|
||||
id: this.spawnedEntitiesTotalCount,
|
||||
speed: entityTypeSetting.speed,
|
||||
respawnTime: entityTypeSetting.respawnTime,
|
||||
size: entityTypeSetting.size,
|
||||
entityTypeID,
|
||||
parentEntityTypeID: 0,
|
||||
maximumHitpoint: 4500,
|
||||
currentHitpoint: 4500,
|
||||
map: this,
|
||||
group,
|
||||
position: preSelectedDestinationNode,
|
||||
attackCallback,
|
||||
dieCallback
|
||||
};
|
||||
|
||||
const spawn = this.registerNonPlayerCharacter(spawnData, NPC);
|
||||
|
||||
if (groupMemberTypeID === 1) group.addLeader(spawn.npc);
|
||||
else group.addMember(spawn.npc);
|
||||
|
||||
cb(this, spawn);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
setupNonPlayerCharacterSpawn(spawn, cb, globalDieCallback, globalRewardCallback, globalAttackCallback, NPC) {
|
||||
spawn.forEach(data => {
|
||||
const { id, parentEntityTypeId, speed, minHealth, maxHealth, amount, respawnTime, rewardEntries } = data;
|
||||
if (!parentEntityTypeId) return;
|
||||
|
||||
const admiralNPC = spawn.find(parent => parent.id === parentEntityTypeId);
|
||||
|
||||
const reward = new Reward({
|
||||
distributionType: 1,
|
||||
baseReward: rewardEntries.reduce((result, entry) => {
|
||||
if (result[entry.categoryId]) result[entry.categoryId][entry.itemId] = {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance,
|
||||
lastShot: entry.isLastShot
|
||||
};
|
||||
else result[entry.categoryId] = {
|
||||
[entry.itemId]: {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance,
|
||||
lastShot: entry.isLastShot
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
}, {})
|
||||
});
|
||||
|
||||
for (let i = 0; i < amount; i++) {
|
||||
const spareID = this.availableIdentifiers.pop();
|
||||
const hitpoint = Math.floor(Math.random() * (maxHealth - minHealth + 1) + minHealth);
|
||||
|
||||
const npcSpawnData = {
|
||||
id: spareID ? spareID : this.spawnedEntitiesTotalCount,
|
||||
speed,
|
||||
respawnTime,
|
||||
reward,
|
||||
entityTypeID: id,
|
||||
parentEntityTypeID: parentEntityTypeId,
|
||||
maximumHitpoint: hitpoint,
|
||||
currentHitpoint: hitpoint,
|
||||
map: this,
|
||||
behaviour: {
|
||||
stopIfAttacked: true,
|
||||
aggressive: false
|
||||
},
|
||||
attackCallback: globalAttackCallback,
|
||||
rewardCallback: globalRewardCallback
|
||||
};
|
||||
|
||||
const newSpawn = this.registerNonPlayerCharacter(npcSpawnData, NPC);
|
||||
newSpawn.npc.dieCallback = data => {
|
||||
const isAdmiralReady = globalDieCallback.call(newSpawn.npc, data);
|
||||
if (isAdmiralReady && admiralNPC) {
|
||||
const { id, speed, minHealth, maxHealth, rewardEntries } = admiralNPC;
|
||||
|
||||
const reward = new Reward({
|
||||
distributionType: 1,
|
||||
baseReward: rewardEntries.reduce((result, entry) => {
|
||||
if (result[entry.categoryId]) result[entry.categoryId][entry.itemId] = {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance,
|
||||
lastShot: entry.isLastShot
|
||||
};
|
||||
else result[entry.categoryId] = {
|
||||
[entry.itemId]: {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance,
|
||||
lastShot: entry.isLastShot
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
}, {})
|
||||
});
|
||||
|
||||
const spareID = this.availableIdentifiers.pop();
|
||||
const hitpoint = Math.floor(Math.random() * (maxHealth - minHealth + 1) + minHealth);
|
||||
|
||||
const admiralSpawnData = {
|
||||
id: spareID ? spareID : this.spawnedEntitiesTotalCount,
|
||||
isAdmiral: true,
|
||||
speed,
|
||||
reward,
|
||||
size: 192,
|
||||
entityTypeID: id,
|
||||
position: newSpawn.npc.position,
|
||||
maximumHitpoint: hitpoint,
|
||||
currentHitpoint: hitpoint,
|
||||
map: this,
|
||||
behaviour: {
|
||||
stopIfAttacked: true,
|
||||
aggressive: false
|
||||
},
|
||||
dieCallback: globalDieCallback,
|
||||
attackCallback: globalAttackCallback,
|
||||
rewardCallback: globalRewardCallback
|
||||
};
|
||||
|
||||
const emittingTo = this.getNeighbourMapsInRadius(1);
|
||||
const notificationData = {
|
||||
mapID: this.id,
|
||||
typeID: newSpawn.npc.typeID,
|
||||
entityTypeID: newSpawn.npc.entityTypeID
|
||||
};
|
||||
|
||||
this.emit(EVENT_SPAWN_ADMIRAL, notificationData, emittingTo);
|
||||
|
||||
const admiral = this.registerNonPlayerCharacter(admiralSpawnData, NPC);
|
||||
cb(this, admiral);
|
||||
};
|
||||
};
|
||||
|
||||
cb(this, newSpawn);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
registerNonPlayerCharacter(data, NPC) {
|
||||
const npc = new NPC(data);
|
||||
|
||||
if (!this.availableIdentifiers.length) this.spawnedEntitiesTotalCount++;
|
||||
|
||||
const npcSpawnProperties = npc.getProperties();
|
||||
|
||||
return { npc, npcSpawnProperties };
|
||||
}
|
||||
|
||||
setupMonsterSpawn(spawn, cb, globalDieCallback, globalRewardCallback, globalAttackCallback, Monster) {
|
||||
spawn.forEach(data => {
|
||||
const { id, parentEntityTypeId, minHealth, maxHealth, amount, respawnTime, rewardEntries } = data;
|
||||
if (!parentEntityTypeId) return;
|
||||
|
||||
const bossMonster = spawn.find(b => b.id === parentEntityTypeId);
|
||||
|
||||
const reward = new Reward({
|
||||
distributionType: 1,
|
||||
baseReward: rewardEntries.reduce((result, entry) => {
|
||||
if (result[entry.categoryId]) result[entry.categoryId][entry.itemId] = {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance
|
||||
};
|
||||
else result[entry.categoryId] = {
|
||||
[entry.itemId]: {
|
||||
amountMinimum: entry.min,
|
||||
amountMaximum: entry.max,
|
||||
chance: entry.chance
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
}, {})
|
||||
});
|
||||
|
||||
for (let i = 0; i < amount; i++) {
|
||||
const spareID = this.availableIdentifiers.pop();
|
||||
const hitpoint = Math.floor(Math.random() * (maxHealth - minHealth + 1) + minHealth);
|
||||
|
||||
const monsterData = {
|
||||
id: spareID ? spareID : this.spawnedEntitiesTotalCount,
|
||||
entityTypeID: id,
|
||||
reward,
|
||||
respawnTime,
|
||||
parentEntityTypeID: parentEntityTypeId,
|
||||
maximumHitpoint: hitpoint,
|
||||
currentHitpoint: hitpoint,
|
||||
map: this,
|
||||
rewardCallback: globalRewardCallback,
|
||||
attackCallback: globalAttackCallback
|
||||
};
|
||||
|
||||
const newSpawn = this.registerMonster(monsterData, Monster);
|
||||
newSpawn.monster.dieCallback = data => {
|
||||
const isAdmiralReady = globalDieCallback.call(newSpawn.monster, data);
|
||||
|
||||
if (isAdmiralReady && bossMonster) {
|
||||
const { id, minHealth, maxHealth, reward } = bossMonster;
|
||||
const spareID = this.availableIdentifiers.pop();
|
||||
const hitpoint = Math.floor(Math.random() * (maxHealth - minHealth + 1) + minHealth);
|
||||
|
||||
const emittingTo = this.getNeighbourMapsInRadius(1);
|
||||
const notificationData = { mapID: this.id, typeID: newSpawn.monster.typeID, entityTypeID: newSpawn.monster.entityTypeID };
|
||||
this.emit(EVENT_SPAWN_ADMIRAL, notificationData, emittingTo);
|
||||
|
||||
const bossSpawnData = {
|
||||
id: spareID ? spareID : this.spawnedEntitiesTotalCount,
|
||||
isAdmiral: true,
|
||||
reward,
|
||||
entityTypeID: id,
|
||||
position: newSpawn.monster.position,
|
||||
maximumHitpoint: hitpoint,
|
||||
currentHitpoint: hitpoint,
|
||||
map: this,
|
||||
dieCallback: globalDieCallback,
|
||||
attackCallback: globalAttackCallback,
|
||||
rewardCallback: globalRewardCallback
|
||||
};
|
||||
|
||||
const boss = this.registerMonster(bossSpawnData, Monster);
|
||||
cb(boss);
|
||||
};
|
||||
};
|
||||
|
||||
cb(newSpawn);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
registerMonster(data, Monster) {
|
||||
const monster = new Monster(data);
|
||||
|
||||
if (!this.availableIdentifiers.length) this.spawnedEntitiesTotalCount++;
|
||||
|
||||
const monsterSpawnProperties = monster.getProperties();
|
||||
|
||||
return { monster, monsterSpawnProperties };
|
||||
}
|
||||
|
||||
registerCollectable(spawn, cb, globalDieCallback, globalRewardCallback, Collectable) {
|
||||
spawn.forEach(data => {
|
||||
const { id, amount, respawnTime, reward } = data;
|
||||
const newCollectables = [];
|
||||
|
||||
for (let i = 0; i < amount; i++) {
|
||||
const collectable = new Collectable({
|
||||
id: this.spawnedEntitiesTotalCount++,
|
||||
entityTypeID: id,
|
||||
respawnTime,
|
||||
map: this,
|
||||
rewardEntries: reward,
|
||||
dieCallback: globalDieCallback,
|
||||
rewardCallback: globalRewardCallback
|
||||
});
|
||||
|
||||
const collectableSpawnProperties = collectable.getProperties();
|
||||
newCollectables.push({ collectable, collectableSpawnProperties });
|
||||
};
|
||||
|
||||
cb(newCollectables);
|
||||
});
|
||||
}
|
||||
|
||||
removeNPC(id) {
|
||||
this.npcs.delete(id);
|
||||
this.availableIdentifiers.push(id);
|
||||
}
|
||||
|
||||
removeMonster(id) {
|
||||
this.monsters.delete(id);
|
||||
this.availableIdentifiers.push(id);
|
||||
}
|
||||
|
||||
getNearbyPlayersGameID(objectToCheck) {
|
||||
const nearbyPlayers = [];
|
||||
|
||||
this.players.forEach(player => {
|
||||
if (!player.hasUnexpectedlyLeft &&
|
||||
player.isAlive &&
|
||||
Math.pow(objectToCheck.position.x - player.position.x, 2) + Math.pow(objectToCheck.position.y - player.position.y, 2) < Math.pow(player.sight, 2))
|
||||
nearbyPlayers.push(player.id);
|
||||
});
|
||||
|
||||
return nearbyPlayers;
|
||||
}
|
||||
|
||||
getLivingEntities(types) {
|
||||
if (!types.length) return;
|
||||
|
||||
const aliveEntities = [];
|
||||
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
const type = types[i];
|
||||
const entityGroup = this.entities[type - 1];
|
||||
entityGroup.forEach(entity => entity.isAlive ? aliveEntities.push(entity) : null);
|
||||
};
|
||||
|
||||
return aliveEntities;
|
||||
}
|
||||
|
||||
getAliveEntityFromSubgroup(type, id) {
|
||||
const subgroup = this.entities[type - 1];
|
||||
if (!subgroup) return null;
|
||||
|
||||
const entity = subgroup.get(+id);
|
||||
return entity && entity.isAlive ? entity : null;
|
||||
}
|
||||
|
||||
getEntityFromSubgroup(id) {
|
||||
const type = getType(id);
|
||||
|
||||
return this.entities[type] ? this.entities[type].get(id) : null;
|
||||
}
|
||||
|
||||
removePlayer = id => this.players.delete(id);
|
||||
|
||||
addPlayer = player => this.players.set(player.id, player);
|
||||
|
||||
switchPlayer(player, map) {
|
||||
this.removePlayer(player.id);
|
||||
|
||||
map.players.set(player.id, player);
|
||||
player.map = map;
|
||||
}
|
||||
|
||||
getGameCoordinates(spiedEntity) {
|
||||
const vertical = Math.round(spiedEntity.position.y / 100) - 1;
|
||||
const verticalRest = vertical % COORDINATE_SECTIONS.length;
|
||||
const index = Math.floor(Math.max(vertical, 0) / COORDINATE_SECTIONS.length);
|
||||
|
||||
const letters = `${COORDINATE_REGIONS[index]}${COORDINATE_SECTIONS[verticalRest]}`;
|
||||
const numbers = Math.floor(spiedEntity.position.x / 100) < 10 ? `0${Math.floor(spiedEntity.position.x / 100)}` : Math.floor(spiedEntity.position.x / 100);
|
||||
|
||||
return {
|
||||
name: spiedEntity.name,
|
||||
id: spiedEntity.id,
|
||||
map: spiedEntity.map.name,
|
||||
x: numbers,
|
||||
y: letters
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = GameMap;
|
||||
@@ -0,0 +1,7 @@
|
||||
const maps = [];
|
||||
for (let i = 0; i < 19; i++) {
|
||||
const map = require(`../Maps/Map_${i + 1}`);
|
||||
maps.push(map);
|
||||
};
|
||||
|
||||
module.exports = maps;
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
const Entity = require("./Entity");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { TYPES, EVENTS, TIMING_EVENTS, MAXIMUM } = constants;
|
||||
const { ENTITY_TYPE_PLAYER, ENTITY_TYPE_MONSTER } = TYPES;
|
||||
const { TIMING_EVENT_RESPAWN } = TIMING_EVENTS;
|
||||
const { EVENT_DIE } = EVENTS;
|
||||
const { MAX_SAFE_INTEGER } = MAXIMUM;
|
||||
|
||||
class Monster extends Entity {
|
||||
constructor(info) {
|
||||
super({
|
||||
type: ENTITY_TYPE_MONSTER,
|
||||
id: info.id,
|
||||
map: info.map,
|
||||
isAlive: true,
|
||||
position: info.position
|
||||
});
|
||||
|
||||
this.hp = {
|
||||
maximum: info.maximumHitpoint,
|
||||
current: info.currentHitpoint
|
||||
};
|
||||
|
||||
this.entityTypeID = info.entityTypeID;
|
||||
this.parentEntityTypeID = info.parentEntityTypeID ?? 0;
|
||||
this.respawnTime = info.respawnTime * 1000;
|
||||
this.isAdmiral = info.isAdmiral ?? false;
|
||||
this.attackers = [];
|
||||
this.totalDamageReceived = 0;
|
||||
this.damageReceived = {};
|
||||
this.dieCallback = info.dieCallback;
|
||||
this.attackCallback = info.attackCallback;
|
||||
this.rewardCallback = info.rewardCallback;
|
||||
this.reward = info.reward;
|
||||
|
||||
const { map, id } = this;
|
||||
|
||||
map.monsters.set(id, this);
|
||||
|
||||
const nearbyPlayersID = map.getNearbyPlayersGameID(this);
|
||||
for (let i = 0; i < nearbyPlayersID.length; i++) {
|
||||
const player = map.getEntityFromSubgroup(nearbyPlayersID[i]);
|
||||
player.inDistance[id] = 0x01;
|
||||
};
|
||||
}
|
||||
|
||||
increaseCurrentHitpoint(amount) {
|
||||
this.hp.current = Math.min(this.hp.maximum, this.hp.current + amount);
|
||||
return this.hp.current;
|
||||
}
|
||||
|
||||
decreaseCurrentHitpoint(amount, author) {
|
||||
const { authorID } = author;
|
||||
|
||||
amount = Math.abs(amount);
|
||||
|
||||
const damage = Math.min(this.hp.current, amount);
|
||||
|
||||
if (this.damageReceived[authorID]) this.damageReceived[authorID] += damage;
|
||||
else this.damageReceived[authorID] = damage;
|
||||
|
||||
this.totalDamageReceived += damage;
|
||||
this.hp.current -= damage;
|
||||
|
||||
this.attackCallback({
|
||||
typeID: this.typeID,
|
||||
entityTypeID: this.entityTypeID,
|
||||
authorID,
|
||||
damage
|
||||
});
|
||||
|
||||
const isOverSafeLimit = this.totalDamageReceived >= MAX_SAFE_INTEGER;
|
||||
if (isOverSafeLimit) this.totalDamageReceived = MAX_SAFE_INTEGER;
|
||||
|
||||
if (this.hp.current === 0 || isOverSafeLimit) {
|
||||
const die = {
|
||||
typeID: this.typeID,
|
||||
lastShotTypeID: 1,
|
||||
lastShotID: author.authorID,
|
||||
targetID: this.id,
|
||||
targetEntityTypeID: this.entityTypeID
|
||||
};
|
||||
|
||||
this.die(die);
|
||||
// this.setSingleEvent(11, 0, die);
|
||||
};
|
||||
|
||||
return {
|
||||
normalizedAmount: damage,
|
||||
currentHitpoint: this.hp.current
|
||||
};
|
||||
}
|
||||
|
||||
isAtMaximumHitpoints() {
|
||||
return this.hp.current === this.hp.maximum;
|
||||
}
|
||||
|
||||
die(die) {
|
||||
super.die();
|
||||
|
||||
this.hp.current = 0;
|
||||
|
||||
this.attackers.forEach(data => {
|
||||
const attackerEntity = this.map.getEntityFromSubgroup(data.attackerEntry);
|
||||
attackerEntity.wipeAttack();
|
||||
});
|
||||
|
||||
this.attackers.length = 0;
|
||||
|
||||
this.emit(EVENT_DIE, die, this.id);
|
||||
|
||||
const admiralProperties = {
|
||||
typeID: this.typeID,
|
||||
entityTypeID: this.entityTypeID,
|
||||
parentEntityTypeID: this.parentEntityTypeID,
|
||||
id: this.id,
|
||||
isAdmiral: this.isAdmiral,
|
||||
authorID: die.lastShotID
|
||||
};
|
||||
|
||||
this.dieCallback(admiralProperties);
|
||||
|
||||
this.removeEvent(11);
|
||||
|
||||
this.damageReceived = {};
|
||||
|
||||
if (!this.isAdmiral) this.setEvent(TIMING_EVENT_RESPAWN, this.respawnTime);
|
||||
}
|
||||
|
||||
respawn() {
|
||||
if (this.isAlive) return;
|
||||
|
||||
this.hp.current = this.hp.maximum;
|
||||
|
||||
super.respawn();
|
||||
|
||||
this.removeEvent(TIMING_EVENT_RESPAWN);
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
const { position, id, hp, type, typeID, isAdmiral, entityTypeID } = this;
|
||||
return { position, id, hp, type, typeID, isAdmiral, entityTypeID };
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Monster;
|
||||
+763
@@ -0,0 +1,763 @@
|
||||
const Entity = require("./Entity");
|
||||
const Utility = require("./Utility");
|
||||
const Cannon = require("./Cannon");
|
||||
|
||||
const ammunitions = require("../Data/ammunitions");
|
||||
const cannonsToEquip = require("../Data/cannonsSchemeNPC");
|
||||
const cannons = require("../Data/cannons");
|
||||
const OVERTIME_HEALTH_EFFECTS = require("../Data/overtime");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
|
||||
const { EVENTS, TIMING_EVENTS, TYPES, EFFECTS_TERMINATE_MOVEMENT, EVENTS_BY_TIMING_EVENTS_ID, AFFECTS, MAXIMUM } = constants;
|
||||
const { EVENT_DESTINATION_REACHED, EVENT_DESTINATION_NEW, EVENT_SPEED_UPDATE, EVENT_ATTACK_STARTED, EVENT_DIE, EVENT_ATTACKING, EVENT_EFFECT_ADD, EVENT_ATTACK_BAR, EVENT_HEALTH_CHANGED } = EVENTS;
|
||||
const { ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC } = TYPES;
|
||||
const { TIMING_EVENT_MOVE, TIMING_EVENT_RESPAWN } = TIMING_EVENTS;
|
||||
const { AFFECT_CANNON_DAMAGE } = AFFECTS;
|
||||
const { MAX_SAFE_INTEGER } = MAXIMUM;
|
||||
|
||||
const EVENTS_REMOVE_DEATH_NPC = [9, 11, 17];
|
||||
|
||||
class NPC extends Entity {
|
||||
constructor(info) {
|
||||
super({
|
||||
type: ENTITY_TYPE_NPC,
|
||||
id: info.id,
|
||||
map: info.map,
|
||||
isAlive: true,
|
||||
position: info.position
|
||||
});
|
||||
|
||||
this.hp = {
|
||||
maximum: info.maximumHitpoint,
|
||||
current: info.currentHitpoint
|
||||
};
|
||||
|
||||
this.entityTypeID = info.entityTypeID ?? 0;
|
||||
this.parentEntityTypeID = info.parentEntityTypeID ?? 0;
|
||||
this.respawnTime = info.respawnTime * 1000;
|
||||
this.isAdmiral = info.isAdmiral ?? false;
|
||||
this.speed = info.speed;
|
||||
this.size = info.size ?? 128;
|
||||
this.cannonRange = 0;
|
||||
this.i = 0;
|
||||
this.path = [];
|
||||
this.attackers = [];
|
||||
this.isSpeedLowered = false;
|
||||
this.damageReceived = {};
|
||||
// this.behaviour = info.behaviour;
|
||||
this.totalDamageReceived = 0;
|
||||
this.lastShotTickTimestamp = 0;
|
||||
this.activeOvertimeHealthEffects = {};
|
||||
this.rewardCallback = info.rewardCallback;
|
||||
this.attackCallback = info.attackCallback;
|
||||
this.dieCallback = info.dieCallback;
|
||||
this.reward = info.reward;
|
||||
this.activeEffects = {};
|
||||
this.cannonTypesOutOfRange = [];
|
||||
this.searchedTarget = null;
|
||||
this.group = info.group;
|
||||
this.inventory = {
|
||||
cannons: [],
|
||||
selectedAmmunition: 1,
|
||||
equipCannons() {
|
||||
const cannonsEquippedScheme = cannonsToEquip[info.entityTypeID];
|
||||
if (!cannonsEquippedScheme) return;
|
||||
|
||||
for (let i = 0, length = cannonsEquippedScheme.length; i < length; i++) {
|
||||
const [id, amount] = cannonsEquippedScheme[i];
|
||||
const cannonScheme = cannons[id];
|
||||
|
||||
if (this.cannonRange < cannonScheme.range) this.cannonRange = cannonScheme.range;
|
||||
|
||||
const cannon = new Cannon({
|
||||
id,
|
||||
damage: cannonScheme.damage,
|
||||
range: cannonScheme.range,
|
||||
reload: cannonScheme.reload,
|
||||
scatter: 0,
|
||||
amount,
|
||||
amountEquipped: amount
|
||||
});
|
||||
|
||||
this.cannons.push(cannon);
|
||||
};
|
||||
},
|
||||
getCannons() {
|
||||
return this.cannons;
|
||||
}
|
||||
};
|
||||
|
||||
this.inventory.equipCannons();
|
||||
|
||||
const cannonsStatistics = this.getCannonsStatistics();
|
||||
this.cannonRange = cannonsStatistics.maximumCannonRange;
|
||||
this.cannonTypesInUse = cannonsStatistics.distinctCannonTypes;
|
||||
|
||||
this.behaviour = {
|
||||
stopIfAttacked: !!this.parentEntityTypeID,
|
||||
attackIfAttacked: true,
|
||||
isAggressive: !this.parentEntityTypeID,
|
||||
isChaser: false,
|
||||
stopAttackIfAbandoned: true,
|
||||
selectTargetType: 1
|
||||
};
|
||||
|
||||
this.behaviourBits = Object.values(this.behaviour).reduce((prev, cur, i) => cur ? prev + Math.pow(2, i) : prev + 0, 0);
|
||||
|
||||
if (this.behaviour.isChaser) {
|
||||
this.chasingBehaviour = {
|
||||
range: 750,
|
||||
recalculateAccumulator: 0,
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
target: null
|
||||
};
|
||||
};
|
||||
|
||||
const { map, id } = this;
|
||||
|
||||
map.npcs.set(id, this);
|
||||
|
||||
const nearbyPlayersID = map.getNearbyPlayersGameID(this);
|
||||
for (let i = 0; i < nearbyPlayersID.length; i++) {
|
||||
const player = map.getEntityFromSubgroup(nearbyPlayersID[i]);
|
||||
player.inDistance[id] = true;
|
||||
};
|
||||
}
|
||||
|
||||
trackOvertimeHealthEffect(id) {
|
||||
const effect = this.activeOvertimeHealthEffects[id];
|
||||
if (effect) effect.count++;
|
||||
else this.activeOvertimeHealthEffects[id] = { count: 0 };
|
||||
|
||||
return effect ? effect.count : 0;
|
||||
}
|
||||
|
||||
overtimeDecreaseCurrentHitpoint(event) {
|
||||
if (!event) return;
|
||||
|
||||
const { id, authorTypeID, authorID, damage, iid } = event;
|
||||
const { amount, ticks, time } = OVERTIME_HEALTH_EFFECTS[id];
|
||||
|
||||
const decrease = this.decreaseCurrentHitpoint(damage * amount, {
|
||||
authorTypeID,
|
||||
authorID
|
||||
});
|
||||
|
||||
if (!decrease) return;
|
||||
|
||||
const currentCount = this.trackOvertimeHealthEffect(id);
|
||||
if (currentCount < ticks && decrease.currentHitpoint) this.setIndependentEvent(iid, time, event);
|
||||
else delete this.activeOvertimeHealthEffects[id];
|
||||
|
||||
const data = {
|
||||
targetTypeID: this.typeID,
|
||||
targetID: this.id,
|
||||
amount: decrease.normalizedAmount,
|
||||
type: 0,
|
||||
isCritical: false
|
||||
};
|
||||
|
||||
this.emit(EVENT_HEALTH_CHANGED, data, this.id);
|
||||
}
|
||||
|
||||
effectAdd(effectID) {
|
||||
this.activeEffects[effectID] = true;
|
||||
|
||||
this.emit(EVENT_EFFECT_ADD, {
|
||||
typeID: this.typeID,
|
||||
id: this.id,
|
||||
effectID
|
||||
});
|
||||
}
|
||||
|
||||
effectRemove(effectID) {
|
||||
this.activeEffects[effectID] = false;
|
||||
|
||||
this.emit(EVENT_EFFECT_ADD, {
|
||||
typeID: this.typeID,
|
||||
id: this.id,
|
||||
effectID
|
||||
});
|
||||
|
||||
this.removeEvent(17);
|
||||
|
||||
switch (effectID) {
|
||||
case 1:
|
||||
this.determineMovementState();
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
isAtMaximumHitpoints() {
|
||||
return this.hp.current === this.hp.maximum;
|
||||
}
|
||||
|
||||
increaseCurrentHitpoint(amount) {
|
||||
amount = Math.abs(amount);
|
||||
|
||||
const normalizedAmount = this.hp.current + amount > this.hp.maximum ? amount - (this.hp.current + amount - this.hp.maximum) : amount;
|
||||
|
||||
this.hp.current += normalizedAmount;
|
||||
|
||||
if (this.isSpeedLowered && !this.isHPBelowPercentageThreshold(35)) {
|
||||
this.changeSpeed(-this.speed / 2);
|
||||
this.isSpeedLowered = false;
|
||||
};
|
||||
|
||||
return {
|
||||
normalizedAmount,
|
||||
currentHitpoint: this.hp.current
|
||||
};
|
||||
}
|
||||
|
||||
decreaseCurrentHitpoint(amount, author) {
|
||||
if (this.hp.current === 0) return;
|
||||
|
||||
const { authorID } = author;
|
||||
|
||||
amount = Math.abs(amount);
|
||||
|
||||
const damage = Math.min(this.hp.current, amount);
|
||||
|
||||
if (this.damageReceived[authorID]) this.damageReceived[authorID] += damage;
|
||||
else this.damageReceived[authorID] = damage
|
||||
|
||||
this.totalDamageReceived += damage;
|
||||
this.hp.current -= damage;
|
||||
|
||||
if (!this.isSpeedLowered && this.isHPBelowPercentageThreshold(35)) {
|
||||
this.changeSpeed(this.speed);
|
||||
this.isSpeedLowered = true;
|
||||
};
|
||||
|
||||
this.attackCallback({
|
||||
typeID: this.typeID,
|
||||
entityTypeID: this.entityTypeID,
|
||||
authorID,
|
||||
damage
|
||||
});
|
||||
|
||||
const isOverSafeLimit = this.totalDamageReceived >= MAX_SAFE_INTEGER;
|
||||
if (isOverSafeLimit) this.totalDamageReceived = MAX_SAFE_INTEGER;
|
||||
|
||||
if (this.hp.current === 0 || isOverSafeLimit) {
|
||||
const die = {
|
||||
typeID: this.typeID,
|
||||
lastShotTypeID: 1,
|
||||
lastShotID: author.authorID,
|
||||
targetID: this.id,
|
||||
targetEntityTypeID: this.entityTypeID
|
||||
};
|
||||
|
||||
this.die(die);
|
||||
//this.setSingleEvent(11, 0, die);
|
||||
};
|
||||
|
||||
return {
|
||||
normalizedAmount: damage,
|
||||
currentHitpoint: this.hp.current
|
||||
};
|
||||
}
|
||||
|
||||
isHPBelowPercentageThreshold(percentageThreshold) {
|
||||
return this.hp.maximum / 100 * percentageThreshold > this.hp.current;
|
||||
}
|
||||
|
||||
changeSpeed(speed) {
|
||||
this.speed += speed;
|
||||
|
||||
const eSpeed = {
|
||||
typeID: this.typeID,
|
||||
id: this.id,
|
||||
speed: this.speed
|
||||
};
|
||||
|
||||
this.emit(EVENT_SPEED_UPDATE, eSpeed, this.id);
|
||||
}
|
||||
|
||||
die(eventDeath) {
|
||||
if (!this.isAlive) return;
|
||||
|
||||
this.emit(EVENT_DIE, eventDeath, this.id);
|
||||
|
||||
super.die();
|
||||
|
||||
this.hp.current = 0;
|
||||
|
||||
this.attackers.forEach(data => {
|
||||
const attackerEntity = this.map.getEntityFromSubgroup(data.attackerEntry);
|
||||
attackerEntity.wipeAttack();
|
||||
});
|
||||
|
||||
this.attackers.length = 0;
|
||||
|
||||
if (this.group)
|
||||
this.emit("miniMapDotRemoveTest", {
|
||||
id: this.id
|
||||
});
|
||||
|
||||
const admiralProperties = {
|
||||
typeID: this.typeID,
|
||||
entityTypeID: this.entityTypeID,
|
||||
parentEntityTypeID: this.parentEntityTypeID,
|
||||
id: this.id,
|
||||
isAdmiral: this.isAdmiral,
|
||||
authorID: eventDeath.lastShotID
|
||||
};
|
||||
|
||||
this.stopMovement();
|
||||
this.abortAttack();
|
||||
this.untrackChasedTarget();
|
||||
|
||||
this.dieCallback(admiralProperties);
|
||||
|
||||
this.damageReceived = {};
|
||||
this.activeEffects = {};
|
||||
|
||||
EVENTS_REMOVE_DEATH_NPC.forEach(e => this.removeEvent(e));
|
||||
|
||||
if (this.group && !this.group.isLeaderAlive()) this.group.cancelRespawn();
|
||||
|
||||
if ((!this.isAdmiral && !this.group) || (this.group && !this.group.isLeader(this.id) && this.group.isLeaderAlive())) this.setEvent(TIMING_EVENT_RESPAWN, this.respawnTime);
|
||||
}
|
||||
|
||||
respawn() {
|
||||
if (this.isAlive) return;
|
||||
|
||||
this.hp.current = this.hp.maximum;
|
||||
this.isSpeedLowered = false;
|
||||
this.speed /= 2;
|
||||
|
||||
super.respawn({
|
||||
position: this.group ? this.map.grid.randomNodeInArea(this.group.predefinedArea) : null
|
||||
});
|
||||
|
||||
this.determineMovementState();
|
||||
|
||||
this.removeEvent(TIMING_EVENT_RESPAWN);
|
||||
}
|
||||
|
||||
haltMovement() {
|
||||
this.isDirty = this.isDirty || this.isMoving && this.behaviour.stopIfAttacked;
|
||||
}
|
||||
|
||||
stopMovement() {
|
||||
this.isDirty = false;
|
||||
this.isMoving = false;
|
||||
this.path = [];
|
||||
this.i = 0;
|
||||
this.removeEvent(TIMING_EVENT_MOVE);
|
||||
}
|
||||
|
||||
startMovement(path) {
|
||||
if (!Array.isArray(path)) return;
|
||||
if (!path.length) return;
|
||||
|
||||
this.path = path;
|
||||
|
||||
this.isMoving = true;
|
||||
|
||||
const [x, y] = this.path[this.i];
|
||||
this.emit(EVENT_DESTINATION_NEW, {
|
||||
id: this.id,
|
||||
typeID: this.typeID,
|
||||
mapID: this.map.id,
|
||||
x,
|
||||
y
|
||||
});
|
||||
|
||||
this.setEvent(TIMING_EVENT_MOVE, this.speed);
|
||||
}
|
||||
|
||||
getCannonsStatistics() {
|
||||
const cannons = this.inventory.getCannons();
|
||||
|
||||
let maximumCannonRange = 0;
|
||||
|
||||
const distinctCannonTypes = cannons.reduce((acc, cur) => {
|
||||
const alreadyAddedType = acc.find(c => c.id === cur.id);
|
||||
if (!alreadyAddedType && cur.amountEquipped) {
|
||||
if (cur.range > maximumCannonRange) maximumCannonRange = cur.range;
|
||||
|
||||
acc.push({
|
||||
id: cur.id,
|
||||
reload: cur.reload,
|
||||
range: cur.range,
|
||||
eventID: cur.eventID
|
||||
});
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return {
|
||||
distinctCannonTypes,
|
||||
maximumCannonRange
|
||||
};
|
||||
}
|
||||
|
||||
calculateCannonDamage(id) {
|
||||
const { inventory } = this;
|
||||
const ammunitionScheme = ammunitions[inventory.selectedAmmunition];
|
||||
let scaleSum = 1;
|
||||
let damage = 0;
|
||||
let k = 0;
|
||||
|
||||
/*inventory.useAvailableItemsByAffect(AFFECT_CANNON_DAMAGE, (scale, itemID, statusID, isStatusActive) => {
|
||||
scaleSum += scale;
|
||||
|
||||
if (!isStatusActive) this.disableStatusEffect(statusID);
|
||||
});*/
|
||||
|
||||
const cannons = this.inventory.getCannons();
|
||||
for (let i = 0, length = cannons.length; i < length; i++) {
|
||||
const cannonType = cannons[i];
|
||||
|
||||
for (let j = 0, length = cannonType.amountEquipped; j < length; j++) {
|
||||
const minimum = ((cannonType.damage * ((100 + cannonType.scatter) / 100) + 1) * scaleSum) - 1;
|
||||
const maximum = cannonType.damage * scaleSum;
|
||||
|
||||
if (id === cannonType.id) {
|
||||
damage += Math.ceil((Math.random() * (maximum - minimum) + minimum) * ammunitionScheme.damage);
|
||||
k += cannonType.amountEquipped;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
usedCannonCount: k,
|
||||
value: damage,
|
||||
critical: false,
|
||||
usedAmmunition: ammunitionScheme
|
||||
};
|
||||
}
|
||||
|
||||
initializeAttackWithCannon(target) {
|
||||
if (!target) return;
|
||||
|
||||
if (this.isAttacking()) return false;
|
||||
|
||||
const isInShootingRange = Utility.IsInDistance(this.position, target.position, this.cannonRange);
|
||||
if (!isInShootingRange) return false;
|
||||
if (target.id === this.id) return false;
|
||||
if (!target.isAlive) return false;
|
||||
if (this.hp.current <= 0) return false;
|
||||
if (!this.behaviour.attackIfAttacked) return false;
|
||||
if (!this.inventory.getCannons().some(c => c.amountEquipped)) return false;
|
||||
|
||||
|
||||
if (target.typeID === ENTITY_TYPE_PLAYER) target.cancelDisconnect();
|
||||
|
||||
this.haltMovement();
|
||||
|
||||
this.target = target;
|
||||
this.searchedTarget = null;
|
||||
|
||||
const attackerInformation = {
|
||||
attackerEntry: this.id
|
||||
};
|
||||
|
||||
target.attackers.push(attackerInformation);
|
||||
|
||||
const eventAttackBar = {
|
||||
authorID: this.id,
|
||||
authorTypeID: this.typeID,
|
||||
isTarget: false,
|
||||
source: 1
|
||||
};
|
||||
|
||||
target.emit(EVENT_ATTACK_BAR, eventAttackBar);
|
||||
|
||||
this.cannonTypesInUse.forEach(c => this.setReusableSingleEvent(c.eventID, 0, { id: c.id }));
|
||||
}
|
||||
|
||||
flush(timingEvent) {
|
||||
const currentFlush = this.map.eventBuffer[this.id];
|
||||
if (!currentFlush) return
|
||||
|
||||
this.emit("attacking", currentFlush.data, currentFlush.author);
|
||||
|
||||
delete this.map.eventBuffer[this.id];
|
||||
}
|
||||
|
||||
attackWithCannon(event) {
|
||||
if (!this.isAlive) return;
|
||||
|
||||
const { target } = this;
|
||||
|
||||
const cannonType = this.cannonTypesInUse.find(c => c.id === event.id);
|
||||
|
||||
const recheckShootingRange = Utility.IsInDistance(this.position, target.position, cannonType.range);
|
||||
if (!recheckShootingRange) {
|
||||
this.cannonTypesOutOfRange.push({
|
||||
cannonID: cannonType.id,
|
||||
lastFiredAtTimestamp: performance.now(),
|
||||
event
|
||||
});
|
||||
|
||||
if (this.cannonRange === cannonType.range) {
|
||||
this.abortAttack();
|
||||
} else {
|
||||
this.setReusableSingleEvent(cannonType.eventID, cannonType.reload, event);
|
||||
};
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (this.behaviour.stopAttackIfAbandoned && !this.behaviour.isAggressive && !this.attackers.length) return this.abortAttack();
|
||||
|
||||
if (target.isRepairing()) target.cancelRepair();
|
||||
|
||||
const damage = this.calculateCannonDamage(cannonType.id);
|
||||
|
||||
this.lastShotTickTimestamp = performance.now();
|
||||
|
||||
const healthUpdate = target.decreaseCurrentHitpoint(damage.value, {
|
||||
authorTypeID: this.typeID,
|
||||
authorID: this.id
|
||||
});
|
||||
|
||||
// this.emit("attacking", attacking, this.id);
|
||||
|
||||
const flushBuffer = this.map.eventBuffer[this.id];
|
||||
if (flushBuffer) flushBuffer.data.amount += healthUpdate.normalizedAmount;
|
||||
else {
|
||||
const attacking = {
|
||||
authorID: this.id,
|
||||
authorTypeID: this.typeID,
|
||||
targetID: target.id,
|
||||
targetTypeID: target.typeID,
|
||||
amount: healthUpdate.normalizedAmount,
|
||||
isCritical: damage.critical
|
||||
};
|
||||
|
||||
this.map.eventBuffer[this.id] = {
|
||||
data: attacking,
|
||||
author: target.id,
|
||||
target: this.id
|
||||
};
|
||||
|
||||
this.map.eventAuthors.push(this.id);
|
||||
|
||||
/*this.flushBuffer[EVENT_ATTACKING] = {
|
||||
data: attacking,
|
||||
author: target.id
|
||||
};*/
|
||||
};
|
||||
|
||||
//this.flush(1);
|
||||
|
||||
if (healthUpdate.currentHitpoint <= 0) return;
|
||||
|
||||
this.setReusableSingleEvent(cannonType.eventID, cannonType.reload, event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wipeAttack() {
|
||||
const { target } = this;
|
||||
if (!target) return
|
||||
|
||||
this.target = null;
|
||||
|
||||
this.untrackChasedTarget();
|
||||
this.stopMovement();
|
||||
this.determineMovementState();
|
||||
|
||||
this.cannonTypesInUse.forEach(c => this.cancelEvent(c.eventID));
|
||||
}
|
||||
|
||||
abortAttack() {
|
||||
const { target } = this;
|
||||
if (!target) return;
|
||||
|
||||
if (this.behaviour.isChaser) {
|
||||
this.chasingBehaviour.target = this.target;
|
||||
this.chasingBehaviour.position = Object.assign({}, this.target.position);
|
||||
};
|
||||
|
||||
this.target.attackers = target.attackers.filter(data => data.attackerEntry !== this.id);
|
||||
this.target = null;
|
||||
|
||||
this.cannonTypesInUse.forEach(c => this.cancelEvent(c.eventID));
|
||||
|
||||
this.determineMovementState();
|
||||
|
||||
this.markActive();
|
||||
|
||||
this.map.lookingForTarget.push(this);
|
||||
}
|
||||
|
||||
determineMovementState(preSelectedDestinationNode) {
|
||||
if (!this.isAlive) return;
|
||||
if (this.isDirty) return;
|
||||
if (this.isMoving) return;
|
||||
if (this.behaviour.stopIfAttacked && this.isAttacking()) return;
|
||||
if (!this.behaviour.stopIfAttacked && this.isMoving) return;
|
||||
if (this.behaviour.stopIfAttacked && this.attackers.length) return;
|
||||
if (EFFECTS_TERMINATE_MOVEMENT.some(effect => this.activeEffects[effect])) return;
|
||||
|
||||
if (!this.isDirty && this.chasingBehaviour && this.chasingBehaviour.target
|
||||
&& this.chasingBehaviour.position.x === this.position.x && this.chasingBehaviour.position.y === this.position.y) {
|
||||
this.chasingBehaviour.position = {
|
||||
x: this.chasingBehaviour.target.position.x,
|
||||
y: this.chasingBehaviour.target.position.y
|
||||
};
|
||||
};
|
||||
|
||||
let destination = null;
|
||||
if (this.group) destination = this.map.grid.randomNodeInArea(this.group.predefinedArea);
|
||||
else if (preSelectedDestinationNode) destination = preSelectedDestinationNode;
|
||||
else if (this.chasingBehaviour && this.chasingBehaviour.target) destination = this.chasingBehaviour.position;
|
||||
else destination = null;
|
||||
|
||||
const predefinedArea = this.group ? this.group.predefinedArea : null;
|
||||
this.emit(EVENT_DESTINATION_REACHED, {
|
||||
destination,
|
||||
predefinedArea
|
||||
});
|
||||
}
|
||||
|
||||
seekEnemy(searchedTarget) {
|
||||
if (!searchedTarget) return;
|
||||
if (!this.isAlive) return;
|
||||
if (this.isAttacking()) return;
|
||||
if (!this.behaviour.isAggressive) return;
|
||||
|
||||
if (!this.searchedTarget) return this.searchedTarget = searchedTarget;
|
||||
|
||||
let isBehaviourSatisfied = false;
|
||||
|
||||
switch (this.behaviour.selectTargetType) {
|
||||
case 1:
|
||||
isBehaviourSatisfied = searchedTarget.hp.current - this.searchedTarget.hp.current <= 0;
|
||||
break;
|
||||
case 2:
|
||||
isBehaviourSatisfied = this.searchedTarget.hp.current - searchedTarget.hp.current <= 0;
|
||||
break;
|
||||
case 3:
|
||||
isBehaviourSatisfied = searchedTarget.hp.maximum - this.searchedTarget.hp.maximum < 0;
|
||||
break;
|
||||
case 4:
|
||||
isBehaviourSatisfied = this.searchedTarget.hp.maximum - searchedTarget.hp.maximum < 0;
|
||||
break;
|
||||
case 5:
|
||||
isBehaviourSatisfied = searchedTarget.speed - this.searchedTarget.speed < 0;
|
||||
break;
|
||||
case 6:
|
||||
isBehaviourSatisfied = this.searchedTarget.speed - searchedTarget.speed < 0;
|
||||
break;
|
||||
case 7:
|
||||
isBehaviourSatisfied = searchedTarget.sight - this.searchedTarget.sight < 0;
|
||||
break;
|
||||
case 8:
|
||||
isBehaviourSatisfied = this.searchedTarget.sight - searchedTarget.sight < 0;
|
||||
break;
|
||||
};
|
||||
|
||||
const isInShootingRange = Utility.IsInDistance(this.position, searchedTarget.position, this.cannonRange);
|
||||
const isSelectedInShootingRange = Utility.IsInDistance(this.position, this.searchedTarget.position, this.cannonRange);
|
||||
if ((isBehaviourSatisfied && isInShootingRange) || !isSelectedInShootingRange) return this.searchedTarget = searchedTarget;
|
||||
}
|
||||
|
||||
trackChasedTarget() {
|
||||
if (!this.chasingBehaviour || !this.chasingBehaviour.target) return;
|
||||
|
||||
const isInRange = Utility.IsInDistance(this.position, this.chasingBehaviour.target.position, this.chasingBehaviour.range);
|
||||
if (!isInRange) {
|
||||
this.untrackChasedTarget();
|
||||
|
||||
return this.isDirty = true;
|
||||
};
|
||||
|
||||
const chasedPosition = this.chasingBehaviour.target.position;
|
||||
if (chasedPosition.x === this.chasingBehaviour.position.x && chasedPosition.y === this.chasingBehaviour.position.y) return;
|
||||
|
||||
const threshold = Math.ceil((1000 - this.speed) / 150);
|
||||
if (++this.chasingBehaviour.recalculateAccumulator === threshold) {
|
||||
const isChasedTargetInShootingRange = Utility.IsInDistance(this.chasingBehaviour.position, chasedPosition, this.range);
|
||||
if (!isChasedTargetInShootingRange) {
|
||||
this.chasingBehaviour.position = {
|
||||
x: chasedPosition.x,
|
||||
y: chasedPosition.y
|
||||
};
|
||||
|
||||
this.isDirty = true;
|
||||
};
|
||||
|
||||
this.chasingBehaviour.recalculateAccumulator = 0;
|
||||
};
|
||||
}
|
||||
|
||||
untrackChasedTarget() {
|
||||
if (!this.chasingBehaviour || !this.chasingBehaviour.target) return;
|
||||
|
||||
this.chasingBehaviour.target = null;
|
||||
this.chasingBehaviour.position.x = 0;
|
||||
this.chasingBehaviour.position.y = 0;
|
||||
this.chasingBehaviour.recalculateAccumulator = 0;
|
||||
}
|
||||
|
||||
move() {
|
||||
const { position } = this;
|
||||
const [currentX, currentY] = this.path[this.i];
|
||||
|
||||
const dx = currentX - position.x;
|
||||
const dy = currentY - position.y;
|
||||
|
||||
position.x += dx;
|
||||
position.y += dy;
|
||||
|
||||
if (this.group) {
|
||||
this.emit("miniMapMove", {
|
||||
id: this.id,
|
||||
position: this.position
|
||||
});
|
||||
};
|
||||
|
||||
this.updateChunk();
|
||||
|
||||
this.markActive();
|
||||
|
||||
if (this.searchedTarget) {
|
||||
const target = this.map.getEntityFromSubgroup(this.searchedTarget.id);
|
||||
if (target) this.initializeAttackWithCannon(target);
|
||||
else this.searchedTarget = null;
|
||||
};
|
||||
|
||||
if (this.i === this.path.length - 1 || this.isDirty) {
|
||||
this.untrackChasedTarget();
|
||||
this.stopMovement();
|
||||
this.determineMovementState();
|
||||
} else {
|
||||
const nextNode = this.path[++this.i];
|
||||
const [x, y] = nextNode;
|
||||
|
||||
this.trackChasedTarget();
|
||||
|
||||
this.setEvent(TIMING_EVENT_MOVE, this.speed);
|
||||
|
||||
this.emit(EVENT_DESTINATION_NEW, {
|
||||
id: this.id,
|
||||
typeID: this.typeID,
|
||||
x,
|
||||
y
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
isAttacking = () => this.target ? true : false;
|
||||
|
||||
getProperties() {
|
||||
const { hp, position, id, speed, size, typeID, entityTypeID, path, behaviourBits, isMoving, isAdmiral, isSpeedLowered } = this;
|
||||
const activeEffectsBits = Object.values(this.activeEffects).reduce((prev, cur, i) => cur ? prev + Math.pow(2, i) : prev + 0, 0);
|
||||
|
||||
return { hp, position, id, speed, size, typeID, entityTypeID, path: path.length ? path[this.i] : [], behaviourBits, activeEffectsBits, isMoving, isAdmiral, isSpeedLowered };
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = NPC;
|
||||
@@ -0,0 +1,21 @@
|
||||
class Limiter {
|
||||
constructor(info) {
|
||||
this.countThreshold = info.countThreshold;
|
||||
this.timeThreshold = info.timeThreshold;
|
||||
this.nextTime = 0;
|
||||
this.time = 0;
|
||||
this.count = 0;
|
||||
};
|
||||
|
||||
update() {
|
||||
if (performance.now() - this.time < this.timeThreshold) this.count++;
|
||||
else {
|
||||
this.time = performance.now();
|
||||
this.count = 1;
|
||||
};
|
||||
|
||||
return this.count > this.countThreshold;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Limiter;
|
||||
+1536
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
class PlayerBehaviour {
|
||||
constructor(info) {
|
||||
this.id = info.id;
|
||||
this.size = info.size ?? 32;
|
||||
this.entries = Array.from({ length: this.size }, () => ({ isProcessed: false, timestamp: Infinity, entry: {}, id: 0 }));
|
||||
this.accumulator = 0;
|
||||
this.buffer = info.buffer;
|
||||
};
|
||||
|
||||
add(entry) {
|
||||
const index = this.accumulator++ % this.size;
|
||||
this.entries[index] = {
|
||||
timestamp: Date.now(),
|
||||
entry,
|
||||
id: this.id,
|
||||
isProcessed: false
|
||||
};
|
||||
}
|
||||
|
||||
save(threshold) {
|
||||
const now = Date.now();
|
||||
|
||||
for (let i = 0; i < this.size; i++) {
|
||||
const entry = this.entries[i];
|
||||
if (entry.id === 0) continue;
|
||||
if (entry.isProcessed) continue;
|
||||
if (entry.timestamp + threshold < now) continue;
|
||||
|
||||
entry.isProcessed = true;
|
||||
// require("../Miscellaneous/winston").error(entry)
|
||||
|
||||
this.buffer.push(entry.entry); // entry.entry
|
||||
};
|
||||
|
||||
return this.buffer;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = PlayerBehaviour;
|
||||
@@ -0,0 +1,68 @@
|
||||
const EventEmitter = require("events");
|
||||
const Reward = require("./Reward");
|
||||
|
||||
class Quest extends EventEmitter {
|
||||
constructor(configuration, doneCallback) {
|
||||
super();
|
||||
|
||||
this.id = configuration.id;
|
||||
this.currentComplete = configuration.completedAmount;
|
||||
this.maximumComplete = configuration.maximumComplete;
|
||||
this.task = JSON.parse(JSON.stringify(configuration.task));
|
||||
this.reward = new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: configuration.reward
|
||||
});
|
||||
this.name = configuration.name || "Quest without a name";
|
||||
this.finishedCount = 0;
|
||||
this.totalTasksCount = Object.values(this.task).reduce((pre, cur) => pre + cur.length, 0);
|
||||
this.doneCallback = doneCallback;
|
||||
}
|
||||
|
||||
adjustTask(events, amount) {
|
||||
events.forEach(type => {
|
||||
const tasksInCategory = this.task[type.taskCategoryID];
|
||||
if (!tasksInCategory) return
|
||||
|
||||
tasksInCategory.forEach(task => {
|
||||
if (task.isFinished) return
|
||||
|
||||
if (type.typeID === task.typeID && type.entityTypeID === task.entityTypeID) {
|
||||
task.amount += amount;
|
||||
|
||||
|
||||
|
||||
if (task.requiredAmount <= task.amount) {
|
||||
task.amount = task.requiredAmount;
|
||||
task.isFinished = true;
|
||||
this.finishedCount++;
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
if (this.finishedCount === this.totalTasksCount) {
|
||||
|
||||
this.doneCallback(this.id);
|
||||
};
|
||||
}
|
||||
|
||||
getTasks() {
|
||||
const taskTypes = Object.keys(this.task);
|
||||
return taskTypes.map(t => this.task[t]);
|
||||
}
|
||||
|
||||
getProgression() {
|
||||
const taskTypes = Object.keys(this.task);
|
||||
const progression = [];
|
||||
taskTypes.forEach(t => this.task[t].forEach((c, i) => {
|
||||
progression.push({ amount: c.amount, index: i, type: t });
|
||||
}));
|
||||
|
||||
return progression;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Quest;
|
||||
@@ -0,0 +1,86 @@
|
||||
const Quest = require("./Quest");
|
||||
const quests = require("../Data/quests");
|
||||
|
||||
class Quests {
|
||||
constructor(quests, doneCallback) {
|
||||
let currentQuest = quests[0];
|
||||
|
||||
this.completedQuests = [];
|
||||
this.finishedQuests = {};
|
||||
this.completedQuestsAmount = {};
|
||||
this.doneCallback = doneCallback;
|
||||
this.list = {};
|
||||
this.list = quests.reduce((result, entry) => {
|
||||
this.completedQuestsAmount[entry.questID] = entry.completedAmount;
|
||||
if (entry.state === 1 || entry.state === 2) return result;
|
||||
|
||||
const quest = this.add(entry.questID);
|
||||
if (!quest) return result;
|
||||
|
||||
if (currentQuest.id !== entry.questID) {
|
||||
result[entry.questID] = quest;
|
||||
currentQuest = quest;
|
||||
};
|
||||
|
||||
if (!entry.amount) return result;
|
||||
|
||||
const { typeID, entityTypeID } = result[entry.questID].task[entry.taskType][entry.taskIndex];
|
||||
currentQuest.adjustTask([{
|
||||
taskCategoryID: entry.taskType,
|
||||
typeID,
|
||||
entityTypeID
|
||||
}], entry.amount);
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
};
|
||||
|
||||
add(id) {
|
||||
const questScheme = quests[id - 1];
|
||||
if (!questScheme) return;
|
||||
|
||||
const isAcceptable = !questScheme.maximumComplete || questScheme.maximumComplete && questScheme.maximumComplete > this.completedQuestsAmount[id] || !this.completedQuestsAmount[id] && questScheme.maximumComplete;
|
||||
if (!isAcceptable) return;
|
||||
|
||||
const isQuestAlreadyAdded = this.list[id];
|
||||
if (isQuestAlreadyAdded) return true;
|
||||
|
||||
const quest = new Quest(questScheme, completedQuestID => {
|
||||
if (questScheme.maximumComplete !== -1) this.completedQuests.push(completedQuestID);
|
||||
|
||||
if (!this.completedQuestsAmount[completedQuestID]) this.completedQuestsAmount[completedQuestID] = 1;
|
||||
else this.completedQuestsAmount[completedQuestID]++;
|
||||
|
||||
const completedQuest = this.list[completedQuestID];
|
||||
this.doneCallback(completedQuest);
|
||||
this.remove(completedQuestID);
|
||||
});
|
||||
|
||||
delete this.finishedQuests[id];
|
||||
|
||||
this.list[id] = quest;
|
||||
|
||||
return quest;
|
||||
}
|
||||
|
||||
remove(id) {
|
||||
const questScheme = quests[id - 1];
|
||||
if (!questScheme) return;
|
||||
|
||||
this.finishedQuests[id] = true;
|
||||
delete this.list[id];
|
||||
}
|
||||
|
||||
has(id) {
|
||||
return this.list[id] ? true : false;
|
||||
}
|
||||
|
||||
refresh(pirateLevel) {
|
||||
for (const id in this.list) {
|
||||
const questScheme = quests[+id - 1];
|
||||
if (questScheme.maximumLevel < pirateLevel || questScheme.minimumLevel > pirateLevel) this.remove(+id);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Quests;
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
const Utility = require("./Utility");
|
||||
const chooseWeightedRandom = require("../Utility/chooseWeightedRandom");
|
||||
|
||||
const uniqueCategories = {
|
||||
5: true
|
||||
};
|
||||
|
||||
const uniqueItems = {};
|
||||
|
||||
class Reward {
|
||||
constructor(information) {
|
||||
this.distributionType = information.distributionType;
|
||||
this.baseReward = information.baseReward;
|
||||
this.damage = information.damage;
|
||||
};
|
||||
|
||||
single(amountOfItems, inventory, cb) {
|
||||
if (this.distributionType !== 2) return;
|
||||
|
||||
const { baseReward } = this;
|
||||
const allRewardCategories = Object.entries(baseReward);
|
||||
|
||||
const rewardChances = [];
|
||||
const rewardEntries = [];
|
||||
const finalRewards = [];
|
||||
|
||||
allRewardCategories.forEach(category => {
|
||||
const [categoryName, categoryEntries] = category;
|
||||
|
||||
Object.entries(categoryEntries).forEach(subEntry => {
|
||||
const [id, entry] = subEntry;
|
||||
|
||||
const isUniqueReward = uniqueCategories[categoryName] || uniqueItems[categoryName] && uniqueItems[categoryName][id];
|
||||
const isAlreadyAcquired = inventory[categoryName] && inventory[categoryName].hasElement(+id);
|
||||
if (isUniqueReward && isAlreadyAcquired) return;
|
||||
|
||||
const rewardEntry = {
|
||||
categoryID: +categoryName,
|
||||
id: +id,
|
||||
amount: entry.amountMaximum ? Utility.RandomIntegerBetween(entry.amountMinimum, entry.amountMaximum) : entry.amountMinimum,
|
||||
index: rewardEntries.length
|
||||
};
|
||||
|
||||
if (entry.chance === 100) finalRewards.push(rewardEntry);
|
||||
else {
|
||||
rewardChances.push(entry.chance);
|
||||
rewardEntries.push(rewardEntry);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const batchedRewards = {};
|
||||
if (rewardEntries.length) {
|
||||
for (let i = 0; i < amountOfItems; i++) {
|
||||
const chosenItem = chooseWeightedRandom(rewardEntries, rewardChances);
|
||||
|
||||
const batchedEntryIndex = batchedRewards[`${chosenItem.categoryID}-${chosenItem.id}`];
|
||||
if (batchedEntryIndex !== undefined) {
|
||||
finalRewards[batchedEntryIndex].amount += chosenItem.amount;
|
||||
} else {
|
||||
batchedRewards[`${chosenItem.categoryID}-${chosenItem.id}`] = finalRewards.length;
|
||||
finalRewards.push(Object.assign({}, chosenItem));
|
||||
|
||||
const isUniqueReward = uniqueCategories[chosenItem.categoryID] || uniqueItems[chosenItem.categoryID] && uniqueItems[chosenItem.categoryID][chosenItem.id];
|
||||
if (isUniqueReward) rewardChances[chosenItem.index] = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cb(finalRewards);
|
||||
}
|
||||
|
||||
multiple(data, cb) {
|
||||
if (this.distributionType !== 1) return;
|
||||
|
||||
const { baseReward } = this;
|
||||
const { playerInventory, damage, maximum, lastShotAuthorID } = data;
|
||||
const allRewardCategories = Object.keys(baseReward);
|
||||
const totalParticipants = Object.keys(damage);
|
||||
|
||||
totalParticipants.forEach(playerID => {
|
||||
playerID = parseInt(playerID);
|
||||
|
||||
const rewards = [];
|
||||
|
||||
allRewardCategories.forEach(category => {
|
||||
category = parseInt(category);
|
||||
|
||||
const rew = baseReward[category];
|
||||
|
||||
Object.keys(rew).forEach(id => {
|
||||
id = parseInt(id);
|
||||
|
||||
const isUniqueReward = uniqueCategories[category] || uniqueItems[category] && uniqueItems[category][id];
|
||||
const isAlreadyAcquired = playerInventory[category] && playerInventory[category].hasElement(id);
|
||||
if (isUniqueReward && isAlreadyAcquired) return;
|
||||
|
||||
if (rew[id].lastShot && playerID !== lastShotAuthorID) return;
|
||||
if (rew[id].chance < Math.random() * 100) return;
|
||||
|
||||
const baseRewardAmount = rew[id].amountMaximum ? Utility.RandomIntegerBetween(rew[id].amountMinimum, rew[id].amountMaximum) : rew[id].amountMinimum;
|
||||
const amountOfReward = rew[id].lastShot ? baseRewardAmount : Math.round(baseRewardAmount / 100 * (damage[playerID] / maximum * 100));
|
||||
if (amountOfReward <= 0) return;
|
||||
|
||||
rewards.push({
|
||||
categoryID: category,
|
||||
id,
|
||||
amount: amountOfReward
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cb({ id: playerID }, rewards);
|
||||
});
|
||||
}
|
||||
|
||||
dynamic(cb) {
|
||||
if (this.distributionType !== 3) return;
|
||||
|
||||
for (let i = 0, length = this.baseReward.length; i < length; i++) {
|
||||
const reward = this.baseReward[i];
|
||||
|
||||
const baseRewardAmount = reward.amountMaximum ? Utility.RandomIntegerBetween(reward.amountMinimum, reward.amountMaximum) : reward.amountMinimum;
|
||||
|
||||
cb([{
|
||||
categoryID: reward.categoryID,
|
||||
id: reward.id,
|
||||
amount: baseRewardAmount
|
||||
}]);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Reward;
|
||||
@@ -0,0 +1,15 @@
|
||||
class SessionManager {
|
||||
constructor() {
|
||||
this.startAt = Date.now();
|
||||
this.endedAt = null;
|
||||
this.currency = [0, 0, 0, 0]
|
||||
this.kills = [0, 0, 0, 0, 0];
|
||||
this.experience = 0;
|
||||
this.sunkCount = 0;
|
||||
this.packets = {};
|
||||
this.ammunition = {};
|
||||
this.harpoon = {};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = SessionManager;
|
||||
+552
@@ -0,0 +1,552 @@
|
||||
const Entity = require("./Entity");
|
||||
const Utility = require("./Utility");
|
||||
|
||||
const OVERTIME_HEALTH_EFFECTS = require("../Data/overtime");
|
||||
const GUILD_TOWER_SCHEME = require("../Data/schemeTower");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { EVENTS, TIMING_EVENTS, TYPES, MAXIMUM } = constants;
|
||||
|
||||
const { ENTITY_TYPE_TOWER, ENTITY_TYPE_PLAYER } = TYPES;
|
||||
const { EVENT_ATTACK_STARTED, EVENT_DIE, EVENT_CANNONS_USED, EVENT_ATTACK_BAR, EVENT_HEALTH_CHANGED, EVENT_HEALTH_CHANGED_AUTHORLESS, EVENT_GUILD_ISLAND_CAPTURE } = EVENTS;
|
||||
const { TIMING_EVENT_RESPAWN, TIMING_EVENT_REPAIR } = TIMING_EVENTS;
|
||||
const { MAX_SAFE_INTEGER } = MAXIMUM;
|
||||
|
||||
const EVENTS_REMOVE_DEATH_TOWER = [9, 11, 12, 13, 17];
|
||||
|
||||
class Tower extends Entity {
|
||||
constructor(info) {
|
||||
super({
|
||||
type: ENTITY_TYPE_TOWER,
|
||||
id: info.id,
|
||||
map: info.map,
|
||||
isAlive: info.currentHitpoint !== 0,
|
||||
position: info.position
|
||||
});
|
||||
|
||||
if (!info.island) return;
|
||||
|
||||
this.order = info.order;
|
||||
|
||||
this.behaviour = {
|
||||
attackIfAttacked: true,
|
||||
isAggressive: true,
|
||||
stopAttackIfAbandoned: false,
|
||||
selectTargetType: 1
|
||||
};
|
||||
|
||||
this.hp = {
|
||||
maximum: info.maximumHitpoint,
|
||||
current: info.currentHitpoint
|
||||
};
|
||||
|
||||
this.island = info.island;
|
||||
|
||||
this.island.addTower(this);
|
||||
|
||||
this.entityTypeID = info.entityTypeID;
|
||||
|
||||
this.attackers = [];
|
||||
this.totalDamageReceived = 0;
|
||||
this.damageReceived = {};
|
||||
|
||||
this.activeEffects = {};
|
||||
this.activeOvertimeHealthEffects = {};
|
||||
|
||||
this.dieCallback = info.dieCallback;
|
||||
this.attackCallback = info.attackCallback;
|
||||
|
||||
this.map.towers.set(this.id, this);
|
||||
setTimeout(() => {
|
||||
// this.repair();
|
||||
}, 15000);
|
||||
};
|
||||
|
||||
trackOvertimeHealthEffect(id) {
|
||||
const effect = this.activeOvertimeHealthEffects[id];
|
||||
if (effect) effect.count++;
|
||||
else this.activeOvertimeHealthEffects[id] = { count: 0 };
|
||||
|
||||
return effect ? effect.count : 0;
|
||||
}
|
||||
|
||||
overtimeDecreaseCurrentHitpoint(event) {
|
||||
if (!event) return;
|
||||
|
||||
const { id, authorTypeID, authorID, authorGuildID, damage, iid } = event;
|
||||
const { amount, ticks, time } = OVERTIME_HEALTH_EFFECTS[id];
|
||||
|
||||
const decrease = this.decreaseCurrentHitpoint(damage * amount, {
|
||||
authorTypeID,
|
||||
authorID,
|
||||
authorGuildID
|
||||
});
|
||||
|
||||
if (!decrease) return;
|
||||
|
||||
const currentCount = this.trackOvertimeHealthEffect(id);
|
||||
if (currentCount < ticks && decrease.currentHitpoint) this.setIndependentEvent(iid, time, event);
|
||||
else delete this.activeOvertimeHealthEffects[id];
|
||||
|
||||
const data = {
|
||||
targetTypeID: this.typeID,
|
||||
targetID: this.id,
|
||||
amount: decrease.normalizedAmount,
|
||||
type: 0,
|
||||
isCritical: false
|
||||
};
|
||||
|
||||
this.emit(EVENT_HEALTH_CHANGED_AUTHORLESS, data, this.id);
|
||||
}
|
||||
|
||||
initializeRepair() {
|
||||
if (!this.isAlive) return;
|
||||
if (this.isAtMaximumHitpoints()) return;
|
||||
|
||||
this.repair();
|
||||
}
|
||||
|
||||
repair(doneIndex) {
|
||||
const { id } = this;
|
||||
|
||||
this.lastRepairTickTimestamp = performance.now();
|
||||
|
||||
const healthUpdate = this.increaseCurrentHitpoint(Math.floor(Math.random() * 2000));
|
||||
if (!healthUpdate.normalizedAmount) return console.info("aaa", healthUpdate);
|
||||
|
||||
const eventRepair = {
|
||||
targetTypeID: ENTITY_TYPE_TOWER,
|
||||
targetID: id,
|
||||
amount: healthUpdate.normalizedAmount,
|
||||
type: 1,
|
||||
isCritical: false,
|
||||
};
|
||||
|
||||
this.emit(EVENT_HEALTH_CHANGED_AUTHORLESS, eventRepair, this.id);
|
||||
|
||||
if (healthUpdate.currentHitpoint >= this.hp.maximum) return this.cancelRepair();
|
||||
|
||||
this.setReusableSingleEvent(TIMING_EVENT_REPAIR, 1000, doneIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cancelRepair() {
|
||||
if (!this.isRepairing()) return;
|
||||
|
||||
this.cancelEvent(TIMING_EVENT_REPAIR);
|
||||
}
|
||||
|
||||
increaseCurrentHitpoint(amount) {
|
||||
amount = Math.abs(amount);
|
||||
|
||||
const normalizedAmount = this.hp.current + amount <= this.hp.maximum ? amount : this.hp.maximum - this.hp.current;
|
||||
this.hp.current += normalizedAmount;
|
||||
|
||||
const eventGuildIslandTowerHealthChanged = {
|
||||
id: this.id,
|
||||
entityTypeID: this.entityTypeID,
|
||||
guildIslandID: this.island.id,
|
||||
currentHitpoint: this.hp.current
|
||||
};
|
||||
|
||||
this.emit("guildDataIslandTowerHealthChanged", eventGuildIslandTowerHealthChanged, this.id);
|
||||
|
||||
return {
|
||||
normalizedAmount,
|
||||
currentHitpoint: this.hp.current
|
||||
};
|
||||
}
|
||||
|
||||
decreaseCurrentHitpoint(amount, author) {
|
||||
if (this.hp.current === 0) return console.info("couldn't decrease tower");
|
||||
|
||||
const { authorID, authorGuildID } = author;
|
||||
amount = Math.abs(amount);
|
||||
|
||||
const damage = Math.min(this.hp.current, amount);
|
||||
|
||||
this.island.updateReceivedDamage(authorGuildID, damage);
|
||||
|
||||
if (this.damageReceived[authorID]) this.damageReceived[authorID] += damage;
|
||||
else this.damageReceived[authorID] = damage;
|
||||
|
||||
this.totalDamageReceived += damage;
|
||||
this.hp.current -= damage;
|
||||
|
||||
this.attackCallback({
|
||||
typeID: this.typeID,
|
||||
entityTypeID: this.entityTypeID,
|
||||
authorID,
|
||||
damage
|
||||
});
|
||||
|
||||
const eventGuildIslandDamage = {
|
||||
guildIslandID: this.island.id,
|
||||
guildID: authorGuildID,
|
||||
damage
|
||||
};
|
||||
|
||||
const eventGuildIslandTowerHealthChanged = {
|
||||
id: this.id,
|
||||
entityTypeID: this.entityTypeID,
|
||||
guildIslandID: this.island.id,
|
||||
currentHitpoint: this.hp.current
|
||||
};
|
||||
|
||||
this.emit("guildDataIslandTowerHealthChanged", eventGuildIslandTowerHealthChanged, this.id);
|
||||
|
||||
this.emit("_", eventGuildIslandDamage);
|
||||
|
||||
const isOverSafeLimit = this.totalDamageReceived >= MAX_SAFE_INTEGER;
|
||||
if (isOverSafeLimit) this.totalDamageReceived = MAX_SAFE_INTEGER;
|
||||
|
||||
if (this.hp.current === 0 || isOverSafeLimit) {
|
||||
const death = {
|
||||
typeID: this.typeID,
|
||||
lastShotTypeID: 1,
|
||||
lastShotID: author.authorID,
|
||||
targetID: this.id
|
||||
};
|
||||
|
||||
this.die(death);
|
||||
// this.setSingleEvent(11, 0, die);
|
||||
};
|
||||
|
||||
return {
|
||||
normalizedAmount: damage,
|
||||
currentHitpoint: this.hp.current
|
||||
};
|
||||
}
|
||||
|
||||
initializeAttackWithCannon(selectedTarget) {
|
||||
//const cannonsStatistics = this.getCannonsStatistics();
|
||||
const isInShootingRange = Utility.IsInDistance(this.position, selectedTarget.position, 600);
|
||||
|
||||
//if (UNATTACKABLE_ENTITY_TYPES.includes(selectedTarget.typeID)) return
|
||||
if (!isInShootingRange) return
|
||||
//if (this.isRepairing()) return
|
||||
// if (selectedTarget.typeID === ENTITY_TYPE_PLAYER && this.map.type === MAP_PVE) return
|
||||
|
||||
//const selected = this.inventory.getSelectedAmmunition();
|
||||
//if (!selected || selected.amount <= 0) return
|
||||
//if (ammunitions[this.inventory.selectedAmmunition].damage < 0 && selectedTarget.isAtMaximumHitpoints()) return
|
||||
|
||||
if (this.target && this.target.id === selectedTarget.id) return
|
||||
if (selectedTarget.id === this.id) return
|
||||
if (!selectedTarget.isAlive) return
|
||||
if (!this.isAlive) return
|
||||
//if (selectedTarget.typeID === ENTITY_TYPE_PLAYER && selectedTarget.guild.id === this.island.guild.id) return
|
||||
//if (!this.inventory.getCannons().some(c => c.amountEquipped)) return
|
||||
|
||||
if (this.target && this.target.id !== selectedTarget.id) this.abortAttack();
|
||||
|
||||
const attackInformation = {
|
||||
attackerEntry: this.id
|
||||
};
|
||||
|
||||
selectedTarget.attackers.push(attackInformation);
|
||||
|
||||
this.target = selectedTarget;
|
||||
this.searchedTarget = null;
|
||||
|
||||
/*if (selectedTarget.typeID === ENTITY_TYPE_PLAYER) {
|
||||
const attacking = {
|
||||
authorID: this.id,
|
||||
authorTypeID: this.typeID,
|
||||
};
|
||||
|
||||
selectedTarget.emit(EVENT_ATTACK_STARTED, attacking, this.id);
|
||||
};*/
|
||||
|
||||
const attacking = {
|
||||
authorID: this.id,
|
||||
authorTypeID: this.typeID,
|
||||
isTarget: false,
|
||||
source: 1
|
||||
};
|
||||
|
||||
this.emit(EVENT_ATTACK_STARTED, attacking, this.id);
|
||||
selectedTarget.emit(EVENT_ATTACK_BAR, attacking, selectedTarget.id);
|
||||
|
||||
this.setReusableSingleEvent(97, 0, { id: 1 });
|
||||
//this.cannonTypesInUse = cannonsStatistics.distinctCannonTypes;
|
||||
//this.maximumCannonRange = cannonsStatistics.maximumCannonRange;
|
||||
|
||||
//cannonsStatistics.distinctCannonTypes.forEach(c => this.setReusableSingleEvent(c.eventID, 0, { id: c.id }));
|
||||
}
|
||||
|
||||
attackWithCannon(event) {
|
||||
const { target } = this;
|
||||
if (!target) return;
|
||||
|
||||
const recheckShootingRange = Utility.IsInDistance(this.position, target.position, 600);
|
||||
if (!recheckShootingRange) {
|
||||
this.abortAttack();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.lastShotTickTimestamp = performance.now();
|
||||
|
||||
const attackingAnimation = {
|
||||
authorSessionID: this.sessionID,
|
||||
authorID: this.id,
|
||||
authorTypeID: this.typeID,
|
||||
targetID: target.id,
|
||||
targetTypeID: target.typeID,
|
||||
entityTypeID: target.entityTypeID
|
||||
};
|
||||
|
||||
const damage = //this.calculateCannonDamage(1);
|
||||
{ value: 2000 }
|
||||
const attacking = {
|
||||
authorID: this.id,
|
||||
authorTypeID: this.typeID,
|
||||
targetID: target.id,
|
||||
targetTypeID: target.typeID,
|
||||
amount: 0,
|
||||
isCritical: false,
|
||||
type: damage.value <= 0
|
||||
};
|
||||
|
||||
const healthUpdate = damage.value >= 0 ?
|
||||
target.decreaseCurrentHitpoint(damage.value, {
|
||||
authorTypeID: this.typeID,
|
||||
authorID: this.id,
|
||||
authorGuildID: this.guild ? this.guild.id : 0
|
||||
}) : target.increaseCurrentHitpoint(damage.value);
|
||||
|
||||
attacking.amount = healthUpdate.normalizedAmount;
|
||||
attacking.isCritical = damage.critical;
|
||||
|
||||
const flushBuffer = this.map.eventBuffer[this.id];
|
||||
if (flushBuffer) flushBuffer.data[0].amount += attacking.amount;
|
||||
else {
|
||||
const data = [attacking, attackingAnimation];
|
||||
this.map.eventBuffer[this.id] = {
|
||||
data,
|
||||
author: target.id,
|
||||
target: this.id
|
||||
};
|
||||
|
||||
this.map.eventAuthors.push(this.id);
|
||||
};
|
||||
|
||||
this.emit(EVENT_CANNONS_USED, {
|
||||
amount: damage.usedCannonCount,
|
||||
type: 0
|
||||
}, this.id);
|
||||
|
||||
if (healthUpdate.currentHitpoint <= 0) return;
|
||||
|
||||
this.setReusableSingleEvent(97, 2000, event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
removeTarget() {
|
||||
if (!this.target) return;
|
||||
|
||||
this.target.attackers = this.target.attackers.filter(data => data.attackerEntry !== this.id);
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
wipeAttack() {
|
||||
const { target } = this;
|
||||
if (!target) return;
|
||||
|
||||
this.cancelEvent(97);
|
||||
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
abortAttack() {
|
||||
if (!this.isAttacking()) return;
|
||||
if (!this.target) return;
|
||||
|
||||
this.cancelEvent(97);
|
||||
|
||||
const target = {
|
||||
id: this.target.id,
|
||||
typeID: this.target.typeID
|
||||
};
|
||||
|
||||
this.removeTarget();
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
die(eventDeath) {
|
||||
super.die();
|
||||
console.trace("tower death")
|
||||
this.hp.current = 0;
|
||||
|
||||
this.attackers.forEach(data => {
|
||||
const attackerEntity = this.map.getEntityFromSubgroup(data.attackerEntry);
|
||||
attackerEntity.wipeAttack();
|
||||
});
|
||||
|
||||
this.attackers.length = 0;
|
||||
|
||||
const eventGuildIslandTowerDestroyed = {
|
||||
id: this.id,
|
||||
entityTypeID: this.entityTypeID,
|
||||
guildIslandID: this.island.id
|
||||
};
|
||||
|
||||
this.emit(EVENT_DIE, [eventDeath, eventGuildIslandTowerDestroyed], this.id);
|
||||
|
||||
const admiralProperties = {
|
||||
typeID: this.typeID,
|
||||
entityTypeID: this.entityTypeID,
|
||||
parentEntityTypeID: this.parentEntityTypeID,
|
||||
id: this.id,
|
||||
isAdmiral: this.isAdmiral,
|
||||
authorID: eventDeath.lastShotID
|
||||
};
|
||||
|
||||
this.abortAttack();
|
||||
|
||||
this.dieCallback(admiralProperties);
|
||||
|
||||
EVENTS_REMOVE_DEATH_TOWER.forEach(e => this.removeEvent(e));
|
||||
|
||||
const updatedOwnership = this.island.updateOwnership();
|
||||
if (updatedOwnership) {
|
||||
const { guildOld, guildNew, topicOld, topicNew } = updatedOwnership;
|
||||
|
||||
const eventGuildIslandCaptured = {
|
||||
topicOld: topicOld,
|
||||
topicNew: topicNew,
|
||||
packet: {
|
||||
tag: guildNew.tag
|
||||
},
|
||||
database: {
|
||||
newID: guildNew.id,
|
||||
oldID: guildOld.id
|
||||
}
|
||||
};
|
||||
|
||||
this.emit(EVENT_GUILD_ISLAND_CAPTURE, eventGuildIslandCaptured, this.id);
|
||||
};
|
||||
|
||||
this.damageReceived = {};
|
||||
}
|
||||
|
||||
seekEnemy(searchedTarget) {
|
||||
if (!searchedTarget) return;
|
||||
if (!this.isAlive) return;
|
||||
if (this.isAttacking()) return;
|
||||
if (!this.behaviour.isAggressive) return;
|
||||
if (this.island.guild.id && this.island.guild.id === searchedTarget.guild.id) return;
|
||||
|
||||
if (!this.searchedTarget) return this.searchedTarget = searchedTarget;
|
||||
|
||||
let isBehaviourSatisfied = false;
|
||||
|
||||
switch (this.behaviour.selectTargetType) {
|
||||
case 1:
|
||||
isBehaviourSatisfied = searchedTarget.hp.current - this.searchedTarget.hp.current <= 0;
|
||||
break;
|
||||
case 2:
|
||||
isBehaviourSatisfied = this.searchedTarget.hp.current - searchedTarget.hp.current <= 0;
|
||||
break;
|
||||
case 3:
|
||||
isBehaviourSatisfied = searchedTarget.hp.maximum - this.searchedTarget.hp.maximum < 0;
|
||||
break;
|
||||
case 4:
|
||||
isBehaviourSatisfied = this.searchedTarget.hp.maximum - searchedTarget.hp.maximum < 0;
|
||||
break;
|
||||
case 5:
|
||||
isBehaviourSatisfied = searchedTarget.speed - this.searchedTarget.speed < 0;
|
||||
break;
|
||||
case 6:
|
||||
isBehaviourSatisfied = this.searchedTarget.speed - searchedTarget.speed < 0;
|
||||
break;
|
||||
case 7:
|
||||
isBehaviourSatisfied = searchedTarget.sight - this.searchedTarget.sight < 0;
|
||||
break;
|
||||
case 8:
|
||||
isBehaviourSatisfied = this.searchedTarget.sight - searchedTarget.sight < 0;
|
||||
break;
|
||||
};
|
||||
|
||||
const isInShootingRange = Utility.IsInDistance(this.position, searchedTarget.position, 600);
|
||||
const isSelectedInShootingRange = Utility.IsInDistance(this.position, this.searchedTarget.position, 600);
|
||||
if ((isBehaviourSatisfied && isInShootingRange) || !isSelectedInShootingRange) return this.searchedTarget = searchedTarget;
|
||||
}
|
||||
|
||||
respawn() {
|
||||
if (this.isAlive) return;
|
||||
|
||||
this.entityTypeID = 1;
|
||||
|
||||
const baseScheme = GUILD_TOWER_SCHEME[this.entityTypeID];
|
||||
if (!baseScheme) return;
|
||||
|
||||
this.hp.maximum = baseScheme.maximumHitpoints;
|
||||
this.hp.current = this.hp.maximum;
|
||||
|
||||
super.respawn({
|
||||
position: this.position
|
||||
});
|
||||
|
||||
const eventTowerBuild = {
|
||||
id: this.id,
|
||||
entityTypeID: this.entityTypeID,
|
||||
guildIslandID: this.island.id
|
||||
};
|
||||
|
||||
this.emit("guildDataIslandTowerBuilt", eventTowerBuild, this.id);
|
||||
console.info("guildDataIslandTowerBuilt")
|
||||
|
||||
this.markActive();
|
||||
}
|
||||
|
||||
upgrade(entityTypeID) {
|
||||
if (!this.isAlive) {
|
||||
this.respawn();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const baseScheme = GUILD_TOWER_SCHEME[entityTypeID];
|
||||
if (!baseScheme) return false;
|
||||
|
||||
const isNextLevelTarget = entityTypeID - this.entityTypeID === 1;
|
||||
if (isNextLevelTarget) {
|
||||
this.hp.maximum = baseScheme.maximumHitpoints;
|
||||
this.hp.current = baseScheme.maximumHitpoints;
|
||||
this.entityTypeID = entityTypeID;
|
||||
} else return false;
|
||||
|
||||
const eventTowerBuild = {
|
||||
id: this.id,
|
||||
entityTypeID: this.entityTypeID,
|
||||
guildIslandID: this.island.id
|
||||
};
|
||||
|
||||
this.emit("guildDataIslandTowerBuilt", eventTowerBuild, this.id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
const { position, id, hp, type, typeID, isAdmiral, entityTypeID, island, order } = this;
|
||||
return { position, id, hp, type, typeID, isAdmiral, entityTypeID, guildTag: island.guild ? island.guild.tag : null, order };
|
||||
}
|
||||
|
||||
isAtMaximumHitpoints() {
|
||||
return this.hp.current === this.hp.maximum;
|
||||
}
|
||||
|
||||
isRepairing() {
|
||||
const event = this.events[TIMING_EVENT_REPAIR];
|
||||
return event && !event[event.length - 1].isCancelled ? true : false;
|
||||
}
|
||||
|
||||
isAttacking = () => this.target ? true : false;
|
||||
};
|
||||
|
||||
module.exports = Tower;
|
||||
@@ -0,0 +1,11 @@
|
||||
class Utility {
|
||||
static RandomIntegerBetween(minimum, maximum) {
|
||||
return Math.floor(Math.random() * (maximum - minimum + 1) + minimum);
|
||||
}
|
||||
|
||||
static IsInDistance(basePoint, targetPoint, range) {
|
||||
return (basePoint.x - targetPoint.x) ** 2 + (basePoint.y - targetPoint.y) ** 2 <= range ** 2;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Utility;
|
||||
@@ -0,0 +1,54 @@
|
||||
const EntityNewPacket = require("../Packets/EntityNew");
|
||||
const EntityDiePacket = require("../Packets/EntityDie");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { TYPES, EVENTS } = constants;
|
||||
const { ENTITY_TYPE_COLLECTABLE } = TYPES;
|
||||
const { EVENT_DIE } = EVENTS;
|
||||
|
||||
const queries = require("../Data/queries");
|
||||
const { QUERY_FETCH_COLLECTABLE_SPAWN } = queries;
|
||||
|
||||
const Collectable = require("../Game/Collectable");
|
||||
|
||||
module.exports = async function (map, globalDieCallback, globalRewardCallback) {
|
||||
const databaseSpawn = await execute(QUERY_FETCH_COLLECTABLE_SPAWN, [map.id, ENTITY_TYPE_COLLECTABLE]);
|
||||
if (databaseSpawn.error) return process.exit();
|
||||
|
||||
const spawns = databaseSpawn.data.reduce((spawns, spawn) => {
|
||||
if (!spawns[spawn.id]) {
|
||||
spawns[spawn.id] = {
|
||||
id: spawn.id,
|
||||
amount: spawn.amount,
|
||||
respawnTime: spawn.respawnTime,
|
||||
reward: []
|
||||
};
|
||||
};
|
||||
|
||||
if (spawn.categoryId) spawns[spawn.id].reward.push({
|
||||
categoryId: spawn.categoryId,
|
||||
itemId: spawn.itemId,
|
||||
min: spawn.min,
|
||||
max: spawn.max,
|
||||
chance: spawn.chance,
|
||||
isLastShot: spawn.isLastShot
|
||||
});
|
||||
|
||||
return spawns;
|
||||
}, {});
|
||||
|
||||
const convertedSpawn = Object.values(spawns);
|
||||
map.registerCollectable(convertedSpawn, collectablesData => {
|
||||
collectablesData.forEach(data => {
|
||||
const { collectable, collectableSpawnProperties } = data;
|
||||
|
||||
const packet = EntityNewPacket.collectable(collectableSpawnProperties);
|
||||
map.registerNetworkPacket(collectable.id, packet);
|
||||
|
||||
collectable.on(EVENT_DIE, function (data, author) {
|
||||
const packet = EntityDiePacket(data);
|
||||
map.registerNetworkPacket(author, packet, [1]);
|
||||
});
|
||||
});
|
||||
}, globalDieCallback, globalRewardCallback, Collectable);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
const AdmiralSpawnedPacket = require("../Packets/AdmiralSpawned");
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
const { maps } = Game;
|
||||
|
||||
const nonPlayerCharacterHandler = require("./NPC");
|
||||
const monsterHandler = require("./Monster");
|
||||
const collectableHandler = require("./Collectable");
|
||||
const towerHandler = require("./Tower");
|
||||
|
||||
const die = require("../Callbacks/die");
|
||||
const reward = require("../Callbacks/reward");
|
||||
const attack = require("../Callbacks/attack");
|
||||
|
||||
maps.forEach(map => {
|
||||
nonPlayerCharacterHandler(map, die, reward, attack);
|
||||
collectableHandler(map, die, reward);
|
||||
monsterHandler(map, die, reward, attack);
|
||||
towerHandler(map, die, reward, attack);
|
||||
|
||||
map.on("admiralSpawnNotification", function (data, emittingTo) {
|
||||
const packet = AdmiralSpawnedPacket(data);
|
||||
|
||||
emittingTo.forEach(mapID => app.publish(mapID.toString(), packet, true, true));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
const EntityNewPacket = require("../Packets/EntityNew");
|
||||
const AttackInformationPacket = require("../Packets/AttackInformation");
|
||||
const AttackerInformationPacket = require("../Packets/AttackerInformation");
|
||||
const EntityDiePacket = require("../Packets/EntityDie");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { TYPES, EVENTS } = constants;
|
||||
const { ENTITY_TYPE_MONSTER } = TYPES;
|
||||
const { EVENT_ATTACK_STARTED, EVENT_DIE, EVENT_ATTACK_BAR } = EVENTS;
|
||||
|
||||
const queries = require("../Data/queries");
|
||||
const { QUERY_FETCH_MONSTER_SPAWN } = queries;
|
||||
|
||||
const Monster = require("../Game/Monster");
|
||||
|
||||
module.exports = async function (map, globalDieCallback, globalRewardCallback, globalAttackCallback) {
|
||||
const databaseSpawn = await execute(QUERY_FETCH_MONSTER_SPAWN, [map.id, ENTITY_TYPE_MONSTER]);
|
||||
if (databaseSpawn.error) return process.exit();
|
||||
|
||||
const spawns = databaseSpawn.data.reduce((spawns, spawn) => {
|
||||
if (!spawns[spawn.id]) {
|
||||
spawns[spawn.id] = {
|
||||
id: spawn.id,
|
||||
parentEntityTypeId: spawn.parentEntityTypeId,
|
||||
name: spawn.name,
|
||||
minHealth: spawn.minHealth,
|
||||
maxHealth: spawn.maxHealth,
|
||||
amount: spawn.amount,
|
||||
respawnTime: spawn.respawnTime,
|
||||
rewardEntries: []
|
||||
};
|
||||
};
|
||||
|
||||
if (spawn.categoryId) spawns[spawn.id].rewardEntries.push({
|
||||
categoryId: spawn.categoryId,
|
||||
itemId: spawn.itemId,
|
||||
min: spawn.min,
|
||||
max: spawn.max,
|
||||
chance: spawn.chance,
|
||||
isLastShot: spawn.isLastShot
|
||||
});
|
||||
|
||||
return spawns;
|
||||
}, {});
|
||||
|
||||
const convertedSpawn = Object.values(spawns);
|
||||
map.setupMonsterSpawn(convertedSpawn,
|
||||
function monsterNetworkListeners (data) {
|
||||
const { monster, monsterSpawnProperties } = data;
|
||||
|
||||
const packet = EntityNewPacket.monster(monsterSpawnProperties);
|
||||
map.registerNetworkPacket(monster.id, packet);
|
||||
|
||||
monster.on(EVENT_ATTACK_STARTED, function (data, author) {
|
||||
const packet = AttackInformationPacket(data);
|
||||
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
});
|
||||
|
||||
monster.on(EVENT_ATTACK_BAR, function (data, author) {
|
||||
const packet = AttackerInformationPacket(data);
|
||||
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
});
|
||||
|
||||
monster.on(EVENT_DIE, function (data, author) {
|
||||
const packet = EntityDiePacket(data);
|
||||
|
||||
this.map.registerNetworkPacket(author, packet, [1]);
|
||||
});
|
||||
}, globalDieCallback, globalRewardCallback, globalAttackCallback, Monster);
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
const getFixedDate = require("../Utility/getFixedDate");
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
const { maps } = Game;
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { TYPES } = constants;
|
||||
const { ENTITY_TYPE_NPC } = TYPES;
|
||||
|
||||
const queries = require("../Data/queries");
|
||||
const { QUERY_FETCH_NPC_SPAWN } = queries;
|
||||
|
||||
const NPCWorker = global.NPCWorker;
|
||||
|
||||
const NPC = require("../Game/NPC");
|
||||
|
||||
const nonPlayerCharacterNetworkListeners = require("./NPCTest");
|
||||
|
||||
const messages = {
|
||||
1: function (data) {
|
||||
const { path, id, mapID } = data;
|
||||
const map = maps[mapID - 1];
|
||||
const npc = map.npcs.get(id);
|
||||
if (!npc) return;
|
||||
|
||||
npc.startMovement(path);
|
||||
}
|
||||
};
|
||||
|
||||
NPCWorker.on("message", data => {
|
||||
const { messageId } = data;
|
||||
const event = messages[messageId];
|
||||
if (event) event(data);
|
||||
});
|
||||
|
||||
module.exports = async function (map, globalDieCallback, globalRewardCallback, globalAttackCallback) {
|
||||
const databaseSpawn = await execute(QUERY_FETCH_NPC_SPAWN, [map.id, ENTITY_TYPE_NPC]);
|
||||
if (databaseSpawn.error) return process.exit();
|
||||
|
||||
const spawns = databaseSpawn.data.reduce((spawns, spawn) => {
|
||||
if (!spawns[spawn.id]) {
|
||||
spawns[spawn.id] = {
|
||||
id: spawn.id,
|
||||
parentEntityTypeId: spawn.parentEntityTypeId,
|
||||
name: spawn.name,
|
||||
speed: spawn.speed,
|
||||
minHealth: spawn.minHealth,
|
||||
maxHealth: spawn.maxHealth,
|
||||
amount: spawn.amount,
|
||||
respawnTime: spawn.respawnTime,
|
||||
rewardEntries: []
|
||||
};
|
||||
};
|
||||
|
||||
if (spawn.categoryId) spawns[spawn.id].rewardEntries.push({
|
||||
categoryId: spawn.categoryId,
|
||||
itemId: spawn.itemId,
|
||||
min: spawn.min,
|
||||
max: spawn.max,
|
||||
chance: spawn.chance,
|
||||
isLastShot: spawn.isLastShot
|
||||
});
|
||||
|
||||
return spawns;
|
||||
}, {});
|
||||
|
||||
const convertedSpawn = Object.values(spawns);
|
||||
map.setupNonPlayerCharacterSpawn(convertedSpawn, nonPlayerCharacterNetworkListeners, globalDieCallback, globalRewardCallback, globalAttackCallback, NPC);
|
||||
|
||||
const event = {
|
||||
fn: map.spawnEntityGroups.bind(map),
|
||||
arguments: [nonPlayerCharacterNetworkListeners, NPC, globalAttackCallback, globalDieCallback],
|
||||
next: Game.eventsFixed.add.bind(Game.eventsFixed)
|
||||
};
|
||||
|
||||
const nextRunDate = getFixedDate(30);
|
||||
Game.eventsFixed.add(nextRunDate, event);
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
const EntityNewPacket = require("../Packets/EntityNew");
|
||||
const AttackInformationPacket = require("../Packets/AttackInformation");
|
||||
const AttackerInformationPacket = require("../Packets/AttackerInformation");
|
||||
const AttackingPacket = require("../Packets/Attacking");
|
||||
const AttackAbortedPacket = require("../Packets/AttackAborted");
|
||||
const EntityDiePacket = require("../Packets/EntityDie");
|
||||
const EffectAddPacket = require("../Packets/EffectAdd");
|
||||
const EntitySpeedChangedPacket = require("../Packets/EntitySpeedChanged");
|
||||
const HealthChangedPacket = require("../Packets/HealthChanged");
|
||||
const HealthChangedAuthorlessPacket = require("../Packets/HealthChangedAuthorless");
|
||||
const MiniMapEntryMovePacket = require("../Packets/MiniMapEntryMove");
|
||||
const MiniMapEntryRemovePacket = require("../Packets/MiniMapEntryRemove");
|
||||
|
||||
const NewDestinationNodePacket = require("../Packets/NewDestinationNode");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { EVENTS } = constants;
|
||||
const { EVENT_DESTINATION_REACHED, EVENT_ATTACK_STARTED, EVENT_HEALTH_CHANGED, EVENT_ATTACKING, EVENT_ATTACK_ABORTED_BY_USER, EVENT_DIE, EVENT_SPEED_UPDATE, EVENT_DESTINATION_NEW, EVENT_EFFECT_ADD, EVENT_ATTACK_BAR } = EVENTS;
|
||||
|
||||
module.exports = function nonPlayerCharacterNetworkListeners(map, data) {
|
||||
const { npc, npcSpawnProperties } = data;
|
||||
|
||||
const packet = EntityNewPacket.npc(npcSpawnProperties);
|
||||
map.registerNetworkPacket(npc.id, packet.buffer ? packet.buffer : packet);
|
||||
|
||||
npc.on(EVENT_DESTINATION_REACHED, function (data) {
|
||||
NPCWorker.postMessage({
|
||||
position: npc.position,
|
||||
mapID: map.id,
|
||||
id: npc.id,
|
||||
preSelectedDestinationNode: data.destination,
|
||||
predefinedArea: data.predefinedArea
|
||||
});
|
||||
});
|
||||
|
||||
npc.on(EVENT_ATTACK_STARTED, function (data, author) {
|
||||
const packet = AttackInformationPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
});
|
||||
|
||||
npc.on(EVENT_ATTACK_BAR, function (data, author) {
|
||||
const packet = AttackerInformationPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
});
|
||||
|
||||
npc.on(EVENT_HEALTH_CHANGED, function (data, author) {
|
||||
const packet = HealthChangedAuthorlessPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
});
|
||||
|
||||
npc.on(EVENT_ATTACKING, function (data, author) {
|
||||
const _packet = AttackingPacket(data);
|
||||
const packet = HealthChangedPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, [_packet, packet], [1, 1]);
|
||||
});
|
||||
|
||||
npc.on(EVENT_ATTACK_ABORTED_BY_USER, function (data, author) {
|
||||
const packet = AttackAbortedPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
});
|
||||
|
||||
npc.on(EVENT_DIE, function (data, author) {
|
||||
const packet = EntityDiePacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet, [1]);
|
||||
});
|
||||
|
||||
npc.on(EVENT_SPEED_UPDATE, function (data, author) {
|
||||
const packet = EntitySpeedChangedPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
});
|
||||
|
||||
npc.on(EVENT_DESTINATION_NEW, function (data, author) {
|
||||
const packet = NewDestinationNodePacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet, [59]);
|
||||
});
|
||||
|
||||
npc.on(EVENT_EFFECT_ADD, function (data, author) {
|
||||
const packet = EffectAddPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
});
|
||||
|
||||
npc.on("miniMapDotRemoveTest", function (data, author) {
|
||||
const packet = MiniMapEntryRemovePacket(data);
|
||||
this.map.registerGlobalNetworkPacket(this.id, packet);
|
||||
});
|
||||
|
||||
npc.on("miniMapMove", function (data, author) {
|
||||
const packet = MiniMapEntryMovePacket(data);
|
||||
this.map.registerGlobalNetworkPacket(this.id, packet);
|
||||
});
|
||||
|
||||
npc.determineMovementState();
|
||||
};
|
||||
@@ -0,0 +1,364 @@
|
||||
const MainPlayerPacket = require("../Packets/MainPlayer");
|
||||
const QuestRemovalPacket = require("../Packets/QuestRemoval");
|
||||
const EntityOldPacket = require("../Packets/EntityOld");
|
||||
const EntityNewPacket = require("../Packets/EntityNew");
|
||||
const AmmunitionUsedPacket = require("../Packets/AmmunitionUsed");
|
||||
const LogoutCancelledPacket = require("../Packets/LogoutCancelled");
|
||||
const PropertyChangedPacket = require("../Packets/PropertyChanged")
|
||||
const AttackInformationPacket = require("../Packets/AttackInformation");
|
||||
const AttackingPacket = require("../Packets/Attacking");
|
||||
const AttackAbortedPacket = require("../Packets/AttackAborted");
|
||||
const AttackerInformationPacket = require("../Packets/AttackerInformation");
|
||||
const EntityDiePacket = require("../Packets/EntityDie");
|
||||
const RespawnConfirmationPacket = require("../Packets/RespawnConfirmation");
|
||||
const EffectAddPacket = require("../Packets/EffectAdd");
|
||||
const EntitySpeedChangedPacket = require("../Packets/EntitySpeedChanged");
|
||||
const MapChangeStatePacket = require("../Packets/MapChangeState");
|
||||
const MapChangedPacket = require("../Packets/MapChanged");
|
||||
const LevelUpPacket = require("../Packets/LevelUp");
|
||||
const LevelIsLowPacket = require("../Packets/LevelIsLow");
|
||||
const HealthChangedAuthorlessPacket = require("../Packets/HealthChangedAuthorless");
|
||||
const HealthChangedPacket = require("../Packets/HealthChanged");
|
||||
const ItemUsedPacket = require("../Packets/ItemUsed");
|
||||
const NewDestinationNodePacket = require("../Packets/NewDestinationNode");
|
||||
const GuildMemberNewPacket = require("../Packets/GuildMemberNew");
|
||||
const GuildDataMemberChangedPacket = require("../Packets/GuildDataMemberChanged");
|
||||
|
||||
const quests = require("../Data/quests");
|
||||
const constants = require("../Data/constants");
|
||||
|
||||
const Game = require("../Game/Game");
|
||||
|
||||
const { players } = Game;
|
||||
|
||||
const { TYPES } = constants;
|
||||
const { ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_MONSTER, ENTITY_TYPE_TOWER, ENTITY_TYPE_COLLECTABLE } = TYPES;
|
||||
|
||||
const [playerEconomy] = require("../Data/batchSave");
|
||||
|
||||
// just to add "lag"
|
||||
function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
global.PlayerWorker.on("message", data => {
|
||||
const player = players[data.id];
|
||||
if (!player) return;
|
||||
if (player.map.id !== data.mapId) return;
|
||||
if (player.movementRequestTracker !== data.movementRequestTracker) return;
|
||||
|
||||
player.startingPosition = data.startingPosition;
|
||||
|
||||
const { path } = data;
|
||||
|
||||
if (!player.isMoving) player.initializeMovement(path, data);
|
||||
else player.emit("scheduledPathReady", path, data);
|
||||
});
|
||||
|
||||
module.exports.scheduledPathReady = function (path, data) {
|
||||
if (path.length) {
|
||||
this.data = data;
|
||||
this.scheduledPath = path;
|
||||
this.isNewPathReady = true;
|
||||
} else this.isLastNodeFinished = true;
|
||||
|
||||
if (this.isNewPathReady && this.isLastNodeFinished) {
|
||||
this.isNewPathReady = false;
|
||||
this.isLastNodeFinished = false;
|
||||
|
||||
this.initializeMovement(this.scheduledPath, this.data);
|
||||
this.scheduledPath = [];
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.updateVisibility = function (entityProps) {
|
||||
const { type, data } = entityProps;
|
||||
let packet = null;
|
||||
|
||||
if (type) {
|
||||
switch (data.typeID) {
|
||||
case ENTITY_TYPE_PLAYER: packet = EntityNewPacket.player(data); break;
|
||||
case ENTITY_TYPE_NPC: packet = EntityNewPacket.npc(data); break;
|
||||
case ENTITY_TYPE_MONSTER: packet = EntityNewPacket.monster(data); break;
|
||||
case ENTITY_TYPE_TOWER: packet = EntityNewPacket.tower(data); break;
|
||||
case ENTITY_TYPE_COLLECTABLE: packet = EntityNewPacket.collectable(data); break;
|
||||
};
|
||||
} else packet = EntityOldPacket(data);
|
||||
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet.buffer ? packet.buffer : packet, [packet.metadata]);
|
||||
};
|
||||
|
||||
module.exports.disconnect = async function (data) {
|
||||
const { map, position, hp, inventory, textureID } = this;
|
||||
const playerID = this.id;
|
||||
|
||||
const oldEntityID = {
|
||||
typeID: this.typeID,
|
||||
id: playerID
|
||||
};
|
||||
|
||||
const packet = EntityOldPacket(oldEntityID);
|
||||
this.map.registerNetworkPacket(this.id, packet, [1]);
|
||||
|
||||
if (this.guild.id) {
|
||||
const guildDataMemberChanged = {
|
||||
id: playerID,
|
||||
isOnline: false
|
||||
};
|
||||
|
||||
const packet = GuildDataMemberChangedPacket(guildDataMemberChanged);
|
||||
|
||||
const topic = Game.getTopic(this.guild.topic);
|
||||
topic.push(packet);
|
||||
};
|
||||
|
||||
this.isUnderDisconnect = true;
|
||||
|
||||
if (!this.hasUnexpectedlyLeft) {
|
||||
socket.sockets[this.sessionID].end();
|
||||
delete socket.sockets[this.sessionID];
|
||||
console.info("player handler end socket", performance.now());
|
||||
};
|
||||
|
||||
const ammunition = inventory.getAmmunitions();
|
||||
const harpoon = inventory.getHarpoons();
|
||||
const economy = inventory.getEconomy();
|
||||
const items = inventory.getItems();
|
||||
const designs = inventory.getDesignIDs();
|
||||
const cannons = inventory.getCannons();
|
||||
const harpooners = inventory.getHarpooners();
|
||||
|
||||
const resources = [];
|
||||
const equipments = [];
|
||||
|
||||
economy.forEach(e => {
|
||||
playerEconomy.removeItem(`${playerID}-1-${e.id}`);
|
||||
resources.push([playerID, 1, e.id, e.amount])
|
||||
});
|
||||
|
||||
items.forEach(i => {
|
||||
playerEconomy.removeItem(`${playerID}-2-${i.id}`);
|
||||
resources.push([playerID, 2, i.id, i.amount])
|
||||
});
|
||||
|
||||
ammunition.forEach(a => {
|
||||
playerEconomy.removeItem(`${playerID}-3-${a.id}`);
|
||||
resources.push([playerID, 3, a.id, a.amount])
|
||||
});
|
||||
|
||||
harpoon.forEach(h => {
|
||||
playerEconomy.removeItem(`${playerID}-4-${h.id}`);
|
||||
resources.push([playerID, 4, h.id, h.amount])
|
||||
});
|
||||
|
||||
designs.forEach(id => {
|
||||
playerEconomy.removeItem(`${playerID}-5-${id}`);
|
||||
resources.push([playerID, 5, id, 1])
|
||||
});
|
||||
|
||||
this.statusPoints.forEach(s => {
|
||||
playerEconomy.removeItem(`${playerID}-6-${s.id}`);
|
||||
resources.push([playerID, 6, s.id, s.amount])
|
||||
});
|
||||
|
||||
cannons.forEach(c => {
|
||||
playerEconomy.removeItem(`${playerID}-7-${c.id}`);
|
||||
|
||||
resources.push([playerID, 7, c.id, c.amount]);
|
||||
equipments.push([playerID, 7, c.id, c.amountEquipped]);
|
||||
});
|
||||
|
||||
harpooners.forEach(h => {
|
||||
playerEconomy.removeItem(`${playerID}-8-${h.id}`);
|
||||
|
||||
resources.push([playerID, 8, h.id, h.amount]);
|
||||
equipments.push([playerID, 8, h.id, h.amountEquipped])
|
||||
});
|
||||
|
||||
// await delay(20000)
|
||||
Object.entries(this.quests.completedQuestsAmount).forEach(async q => {
|
||||
const [questID, amount] = q;
|
||||
const questScheme = quests[questID - 1];
|
||||
const isQuestAccepted = this.quests.has(questID);
|
||||
let questState = 0;
|
||||
|
||||
if (questScheme.maximumComplete && amount >= questScheme.maximumComplete) questState = 2;
|
||||
else if (!isQuestAccepted) questState = 1;
|
||||
else questState = 0;
|
||||
|
||||
await execute("INSERT INTO quests (playerID, questID, state, completedAmount) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE state = ?, completedAmount = ?", [playerID, questID, questState, amount, questState, amount]);
|
||||
|
||||
if (isQuestAccepted) {
|
||||
const quest = this.quests.list[questID];
|
||||
const questProgression = quest.getProgression();
|
||||
questProgression.forEach(async progression => {
|
||||
const { amount, index, type } = progression;
|
||||
await execute("INSERT INTO questprogression (playerID, questID, amount, taskIndex, taskType) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE amount = ?", [playerID, quest.id, amount, index, type, amount]);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
const finishedQuestsID = Object.keys(this.quests.finishedQuests);
|
||||
if (finishedQuestsID.length) await execute("UPDATE questprogression SET amount = 0 WHERE questID IN (?)", finishedQuestsID);
|
||||
|
||||
await execute("UPDATE playerdata SET mapID = ?, x = ?, y = ?, currentHitpoints = ?, maximumHitpoints = ?, selectedAmmunitionID = ?, selectedHarpoonID = ?, designID = ? WHERE playerID = ?", [map.id, position.x, position.y, hp.current, hp.maximum, inventory.selectedAmmunition, inventory.selectedHarpoon, textureID, playerID]);
|
||||
|
||||
if (resources.length) await query(`INSERT INTO resources (playerID, categoryID, itemID, amount) VALUES ? ON DUPLICATE KEY UPDATE amount = VALUES(amount)`, [resources]);
|
||||
|
||||
const activeStatusEffects = this.activeStatusEffects.length ? this.activeStatusEffects : [0];
|
||||
const activeStatusEffectsFormatted = this.activeStatusEffects.map(e => [playerID, e, 1]);
|
||||
if (this.activeStatusEffects.length) await query(`INSERT INTO status (playerID, statusID, isActive) VALUES ? ON DUPLICATE KEY UPDATE isActive = ?`, [activeStatusEffectsFormatted, 1]);
|
||||
await query(`UPDATE status SET isActive = ? WHERE statusID NOT IN (?) AND playerID = ?`, [0, activeStatusEffects, playerID]);
|
||||
|
||||
if (equipments.length) await query("INSERT INTO equipments (playerID, categoryID, itemID, amountEquipped) VALUES ? ON DUPLICATE KEY UPDATE amountEquipped = VALUES(amountEquipped)", [equipments]);
|
||||
|
||||
if (this.isUnderDisconnect) {
|
||||
console.info("player handler disconnect finished", performance.now());
|
||||
delete players[playerID];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.disconnectCancel = function() {
|
||||
const packet = LogoutCancelledPacket();
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.healthChangedAuthorless = function (data, author) {
|
||||
const packet = HealthChangedAuthorlessPacket(data);
|
||||
this.map.registerNetworkPacket(author, packet);
|
||||
};
|
||||
|
||||
module.exports.attackBar = function (data, author) {
|
||||
const packet = AttackerInformationPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.attackStarted = function (data, author) {
|
||||
const packet = AttackInformationPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.attacking = function (data, author) {
|
||||
const packet = AttackingPacket(data[1]);
|
||||
const packett = HealthChangedPacket(data[0]);
|
||||
this.map.registerNetworkPacket(author, [packet, packett], [1, 1]);
|
||||
};
|
||||
|
||||
module.exports.attackAbortedByUser = function (data, author) {
|
||||
const packet = AttackAbortedPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.die = function (data, author) {
|
||||
const packet = EntityDiePacket(data);
|
||||
this.map.registerNetworkPacket(author, packet, [19]);
|
||||
};
|
||||
|
||||
module.exports.propertyChanged = function (data, author) {
|
||||
const packet = PropertyChangedPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
};
|
||||
|
||||
module.exports.ableToJump = function (data) {
|
||||
const packet = MapChangeStatePacket(data);
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.unableToJump = function (data) {
|
||||
const packet = MapChangeStatePacket(data);
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.mapChanged = function (data, author) {
|
||||
const selfSwitchPacket = MapChangedPacket(data.newMap);
|
||||
this.map.registerIndividualNetworkPacket(this.id, selfSwitchPacket);
|
||||
|
||||
const socketInstance = socket.sockets[this.sessionID];
|
||||
socketInstance.unsubscribe(data.oldMap.id.toString());
|
||||
socketInstance.subscribe(data.newMap.mapID.toString());
|
||||
};
|
||||
|
||||
module.exports.levelUp = function (data) {
|
||||
if (this.guild.id) {
|
||||
const guildDataMemberChanged = {
|
||||
id: this.id,
|
||||
level: data.level
|
||||
};
|
||||
|
||||
const guildDataMemberChangedPacket = GuildDataMemberChangedPacket(guildDataMemberChanged);
|
||||
|
||||
const topic = Game.getTopic(this.guild.topic);
|
||||
topic.push(guildDataMemberChangedPacket);
|
||||
}
|
||||
|
||||
const packet = LevelUpPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.levelIsLow = function() {
|
||||
const packet = LevelIsLowPacket();
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.questDone = async function (data) {
|
||||
await execute("INSERT INTO quests (playerID, questID, state, completedAmount) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE state = ?, completedAmount = completedAmount + 1", [this.id, data.questID, 1, 1, 1]);
|
||||
|
||||
const packet = QuestRemovalPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.testOvertimeDamage = function (data, author) {
|
||||
const packet = HealthChangedPacket(data);
|
||||
this.map.registerNetworkPacket(author, packet);
|
||||
};
|
||||
|
||||
module.exports.itemUsed = function (data, author) {
|
||||
const packet = ItemUsedPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
};
|
||||
|
||||
module.exports.cannonsUsed = function (data, author) {
|
||||
const packet = AmmunitionUsedPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
};
|
||||
|
||||
module.exports.harpoonersUsed = function (data, author) {
|
||||
const packet = AmmunitionUsedPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
};
|
||||
|
||||
module.exports.newDestination = function (data) {
|
||||
const packet = NewDestinationNodePacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.effectAdd = function (data) {
|
||||
const packet = EffectAddPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.speedUpdate = function (data) {
|
||||
const packet = EntitySpeedChangedPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet);
|
||||
};
|
||||
|
||||
module.exports.respawn = async function() {
|
||||
const respawnPacket = RespawnConfirmationPacket();
|
||||
this.map.registerIndividualNetworkPacket(this.id, respawnPacket);
|
||||
|
||||
const respawningPlayerProperties = this.getProperties();
|
||||
const respawningPlayerPacket = MainPlayerPacket(respawningPlayerProperties);
|
||||
const packetsToSend = [respawningPlayerPacket];
|
||||
|
||||
if (this.guild && this.guild.id) {
|
||||
const guildProperties = {
|
||||
playerID: this.id,
|
||||
tag: this.guild.tag
|
||||
};
|
||||
|
||||
const guildPacket = GuildMemberNewPacket(guildProperties);
|
||||
packetsToSend.push(guildPacket);
|
||||
};
|
||||
|
||||
this.map.registerIndividualNetworkPacket(this.id, packetsToSend);
|
||||
};
|
||||
@@ -0,0 +1,184 @@
|
||||
const EntityNewPacket = require("../Packets/EntityNew");
|
||||
const EntityDiePacket = require("../Packets/EntityDie");
|
||||
const GuildIslandCapturedPacket = require("../Packets/GuildIslandCaptured");
|
||||
const AttackInformationPacket = require("../Packets/AttackInformation");
|
||||
const AttackerInformationPacket = require("../Packets/AttackerInformation");
|
||||
const AttackingPacket = require("../Packets/Attacking");
|
||||
const HealthChangedPacket = require("../Packets/HealthChanged");
|
||||
const HealthChangedAuthorlessPacket = require("../Packets/HealthChangedAuthorless");
|
||||
const GuildDataIslandTowerDestroyedPacket = require("../Packets/GuildDataIslandTowerDestroyed");
|
||||
const GuildDataIslandTowerHealthChangedPacket = require("../Packets/GuildDataIslandTowerHealthChanged");
|
||||
const GuildDataIslandCapturedPacket = require("../Packets/GuildDataIslandCaptured");
|
||||
const GuildDataIslandTowerBuiltPacket = require("../Packets/GuildDataIslandTowerBuilt");
|
||||
const WorldMapGuildEntryPacket = require("../Packets/WorldMapGuildEntry");
|
||||
const GuildDataIslandPacket = require("../Packets/GuildDataIsland");
|
||||
const GuildDataIslandTowerPacket = require("../Packets/GuildDataIslandTower");
|
||||
|
||||
const schemeTower = require("../Data/schemeTower");
|
||||
const queries = require("../Data/queries");
|
||||
const { QUERY_FETCH_GUILD_TOWERS, QUERY_UPDATE_GUILD_ISLAND_TOWER_DEAD, QUERY_UPDATE_GUILD_ISLAND_TOWER_UPGRADE, QUERY_UPDATE_GUILD_ISLAND_TOWER_DEFAULT, QUERY_DELETE_GUILD_ISLAND_DAMAGES, QUERY_UPDATE_GUILD_ISLAND_AUTHOR, QUERY_UPDATE_GUILD_ISLAND_DAMAGES } = queries;
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { EVENTS } = constants;
|
||||
const { EVENT_ATTACK_STARTED, EVENT_HEALTH_CHANGED_AUTHORLESS, EVENT_ATTACKING, EVENT_ATTACK_BAR, EVENT_DIE, EVENT_GUILD_ISLAND_CAPTURE, EVENT_GUILD_ISLAND_TOWER_BUILT, EVENT_GUILD_ISLAND_TOWER_HEALTH_CHANGED } = EVENTS;
|
||||
|
||||
const Tower = require("../Game/Tower");
|
||||
const Game = require("../Game/Game");
|
||||
const { guildIslands } = Game;
|
||||
|
||||
module.exports = async function (map, globalDieCallback, globalRewardCallback, globalAttackCallback) {
|
||||
const towers = await execute(QUERY_FETCH_GUILD_TOWERS, [map.id]);
|
||||
if (towers.error) return process.exit();
|
||||
|
||||
const allTowersMap = towers.data;
|
||||
if (!allTowersMap) return;
|
||||
|
||||
let order = 0;
|
||||
let previousIslandID = 0;
|
||||
|
||||
for (let i = 0, length = allTowersMap.length; i < length; i++) {
|
||||
const tower = allTowersMap[i];
|
||||
const scheme = schemeTower[tower.entityTypeID];
|
||||
if (!scheme) continue;
|
||||
|
||||
const island = guildIslands.find(island => island.id === tower.guildIslandID);
|
||||
|
||||
if (previousIslandID !== island.id) order = 0;
|
||||
previousIslandID = island.id;
|
||||
|
||||
const towerInstance = new Tower({
|
||||
id: tower.id,
|
||||
order: ++order,
|
||||
entityTypeID: tower.entityTypeID,
|
||||
island,
|
||||
map,
|
||||
maximumHitpoint: scheme.maximumHitpoints,
|
||||
currentHitpoint: tower.currentHitpoints,
|
||||
position: {
|
||||
x: tower.positionX,
|
||||
y: tower.positionY
|
||||
},
|
||||
attackCallback: globalAttackCallback,
|
||||
dieCallback: globalDieCallback
|
||||
});
|
||||
|
||||
const packet = EntityNewPacket.tower(towerInstance.getProperties());
|
||||
map.registerNetworkPacket(towerInstance.id, packet);
|
||||
|
||||
towerInstance.on(EVENT_DIE, function (data, id) {
|
||||
const packet = EntityDiePacket(data[0]);
|
||||
this.map.registerNetworkPacket(id, [packet], [1]);
|
||||
|
||||
const packet_2 = GuildDataIslandTowerDestroyedPacket(data[1]);
|
||||
const topic = Game.getTopic(this.island.guild.topic);
|
||||
topic.push(packet_2);
|
||||
|
||||
execute(QUERY_UPDATE_GUILD_ISLAND_TOWER_DEAD, [0, Tower.getID(this.id)]);
|
||||
});
|
||||
|
||||
towerInstance.on(EVENT_ATTACK_STARTED, function (data, author) {
|
||||
const packet = AttackInformationPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
});
|
||||
|
||||
towerInstance.on(EVENT_HEALTH_CHANGED_AUTHORLESS, function (data, author) {
|
||||
const packet = HealthChangedAuthorlessPacket(data);
|
||||
this.map.registerNetworkPacket(this.id, packet, [1]);
|
||||
});
|
||||
|
||||
towerInstance.on(EVENT_ATTACK_BAR, function (data, author) {
|
||||
const packet = AttackerInformationPacket(data);
|
||||
this.map.registerIndividualNetworkPacket(author, packet);
|
||||
});
|
||||
|
||||
towerInstance.on(EVENT_ATTACKING, function (data, author) {
|
||||
const packet = AttackingPacket(data[1]);
|
||||
const packet_2 = HealthChangedPacket(data[0]);
|
||||
this.map.registerNetworkPacket(author, [packet, packet_2], [1, 1]);
|
||||
});
|
||||
|
||||
towerInstance.on("_", function (data) {
|
||||
execute(QUERY_UPDATE_GUILD_ISLAND_DAMAGES, [data.damage, data.guildIslandID, data.guildID, data.damage]);
|
||||
});
|
||||
|
||||
towerInstance.on(EVENT_GUILD_ISLAND_TOWER_HEALTH_CHANGED, function (data, author) {
|
||||
const topic = Game.getTopic(this.island.guild.topic);
|
||||
const packet = GuildDataIslandTowerHealthChangedPacket(data);
|
||||
topic.push(packet);
|
||||
});
|
||||
|
||||
towerInstance.on(EVENT_GUILD_ISLAND_TOWER_BUILT, async function (data, author) {
|
||||
await execute(QUERY_UPDATE_GUILD_ISLAND_TOWER_UPGRADE, [this.entityTypeID, this.hp.maximum, Tower.getID(this.id)]);
|
||||
|
||||
const topic = Game.getTopic(this.island.guild.topic);
|
||||
const packet = GuildDataIslandTowerBuiltPacket(data);
|
||||
topic.push(packet);
|
||||
});
|
||||
|
||||
towerInstance.on(EVENT_GUILD_ISLAND_CAPTURE, async function (data, author) {
|
||||
const packet = GuildIslandCapturedPacket(data.packet);
|
||||
app.publish(this.map.id.toString(), packet, true, true);
|
||||
|
||||
if (data.database.oldID !== data.database.newID) {
|
||||
const packet_2 = GuildDataIslandCapturedPacket({
|
||||
guildIslandID: this.island.id
|
||||
});
|
||||
|
||||
const topicOld = Game.getTopic(data.topicOld);
|
||||
topicOld.push(packet_2);
|
||||
|
||||
const islandProperties = {
|
||||
id: this.island.id,
|
||||
mapID: this.map.id,
|
||||
position: this.island.position,
|
||||
};
|
||||
|
||||
const guildDataIslandPacket = GuildDataIslandPacket(islandProperties);
|
||||
|
||||
const topicNew = Game.getTopic(data.topicNew);
|
||||
topicNew.push(guildDataIslandPacket);
|
||||
|
||||
this.island.towers.forEach(tower => {
|
||||
const towerProperties = {
|
||||
entityTypeID: tower.entityTypeID,
|
||||
id: tower.id,
|
||||
guildIslandID: island.id,
|
||||
order: tower.order,
|
||||
hp: {
|
||||
current: tower.hp.current
|
||||
},
|
||||
isRepairing: tower.isRepairing()
|
||||
};
|
||||
|
||||
const guildDataIslandTowerPacket = GuildDataIslandTowerPacket(towerProperties);
|
||||
topicNew.push(guildDataIslandTowerPacket);
|
||||
});
|
||||
};
|
||||
|
||||
const packet_3 = WorldMapGuildEntryPacket({
|
||||
locationMapID: this.map.id,
|
||||
index: this.island.index,
|
||||
bufferTag: this.island.guild.tag
|
||||
});
|
||||
|
||||
Game.maps.forEach(map => app.publish(map.id.toString(), packet_3, true, true));
|
||||
|
||||
let dbConnection = null;
|
||||
|
||||
try {
|
||||
dbConnection = await db.getConnection();
|
||||
await dbConnection.beginTransaction();
|
||||
|
||||
await execute(QUERY_UPDATE_GUILD_ISLAND_TOWER_DEFAULT, [5000, this.island.id]);
|
||||
await execute(QUERY_DELETE_GUILD_ISLAND_DAMAGES, [this.island.id]);
|
||||
await execute(QUERY_UPDATE_GUILD_ISLAND_AUTHOR, [data.database.newID, this.island.id]);
|
||||
|
||||
await dbConnection.commit();
|
||||
} catch {
|
||||
await dbConnection.rollback();
|
||||
} finally {
|
||||
dbConnection.release();
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[0]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[9]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[10]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[11]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[12]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[13]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[14]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[15]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[16]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require("../Game/Map");
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[17]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require("../Game/Map");
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[18]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require("../Game/Map");
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[1]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require("../Game/Map");
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[19]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[2]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[3]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[4]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,5 @@
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[5]);
|
||||
|
||||
module.exports = map;
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[6]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[7]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
const GameMap = require('../Game/Map');
|
||||
const data = require("../Data/maps");
|
||||
const map = new GameMap(data[8]);
|
||||
|
||||
module.exports = map
|
||||
@@ -0,0 +1,34 @@
|
||||
const AmmunitionChangeConfirmationPacket = require("../Packets/AmmunitionChangeConfirmation");
|
||||
|
||||
module.exports = function (ws, message, player) {
|
||||
const type = message.getUint8(1);
|
||||
const id = message.getUint8(2);
|
||||
|
||||
let selectedAmmunitionConfirmedID = 0;
|
||||
let selectedAmmunitionTypeConfirmedID = 0;
|
||||
|
||||
switch (type) {
|
||||
case 1:
|
||||
selectedAmmunitionConfirmedID = player.inventory.setSelectedAmmunition(id);
|
||||
selectedAmmunitionTypeConfirmedID = 1;
|
||||
break;
|
||||
case 2:
|
||||
selectedAmmunitionConfirmedID = player.inventory.setSelectedHarpoon(id);
|
||||
selectedAmmunitionTypeConfirmedID = 2;
|
||||
break;
|
||||
};
|
||||
|
||||
const ammunitionChangeConfirmation = {
|
||||
typeID: selectedAmmunitionTypeConfirmedID,
|
||||
id: selectedAmmunitionConfirmedID
|
||||
};
|
||||
|
||||
console.info(ammunitionChangeConfirmation);
|
||||
|
||||
const ammunitionChangeConfirmationPacket = AmmunitionChangeConfirmationPacket(ammunitionChangeConfirmation);
|
||||
player.map.registerIndividualNetworkPacket(player.id, ammunitionChangeConfirmationPacket);
|
||||
|
||||
const metadata = [player.hp.current / player.hp.maximum, player.isAttacking(), 0, player.inventory.selectedAmmunition];
|
||||
|
||||
return metadata;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
const AttackAbortedPacket = require("../Packets/AttackAborted");
|
||||
|
||||
const constants = require("../Data/constants");
|
||||
const { TYPES } = constants;
|
||||
const { ENTITY_TYPE_NPC } = TYPES;
|
||||
|
||||
module.exports = function (ws, message, player) {
|
||||
const targetIdentifier = player.abortAttack();
|
||||
if (!targetIdentifier) return;
|
||||
|
||||
const packet = AttackAbortedPacket({
|
||||
authorID: targetIdentifier.id,
|
||||
authorTypeID: targetIdentifier.typeID,
|
||||
targetID: player.id,
|
||||
targetTypeID: player.typeID
|
||||
});
|
||||
|
||||
player.map.registerIndividualNetworkPacket(player.id, packet);
|
||||
|
||||
if (targetIdentifier.typeID === ENTITY_TYPE_NPC) {
|
||||
const target = player.map.getEntityFromSubgroup(targetIdentifier.id);
|
||||
if (!target) return;
|
||||
|
||||
target.determineMovementState();
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const constants = require("../Data/constants");
|
||||
const { TYPES } = constants;
|
||||
const { ENTITY_TYPE_MONSTER } = TYPES;
|
||||
|
||||
module.exports = function (ws, message, player) {
|
||||
const typeID = message.getUint8(1);
|
||||
const id = message.getUint32(2);
|
||||
|
||||
const wasAttacking = player.isAttacking();
|
||||
|
||||
const target = player.map.getEntityFromSubgroup(id);
|
||||
if (target) {
|
||||
if (typeID === ENTITY_TYPE_MONSTER) player.initializeAttackWithHarpooner(target);
|
||||
else player.initializeAttackWithCannon(target);
|
||||
};
|
||||
|
||||
return [player.inventory.selectedAmmunition, player.hp.current / player.hp.maximum, player.position.x, player.position.y, wasAttacking, 0 - 0];
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
const Reward = require("../Game/Reward");
|
||||
|
||||
const rewardCallback = require("../Callbacks/reward");
|
||||
|
||||
const MojoRemovedPacket = require("../Packets/MojoRemoved");
|
||||
|
||||
const MAXIMUM_BATCH_COUNT_MOJO = 1000;
|
||||
|
||||
const cauldronReward = new Reward({
|
||||
distributionType: 2,
|
||||
baseReward: {
|
||||
3: {
|
||||
5: { amountMinimum: 1500, chance: 16.5 },
|
||||
6: { amountMinimum: 1500, chance: 16.5 },
|
||||
7: { amountMinimum: 1500, chance: 16.5 },
|
||||
8: { amountMinimum: 1500, chance: 16.5 },
|
||||
9: { amountMinimum: 1500, chance: 16.5 }
|
||||
},
|
||||
7: {
|
||||
10: { amountMinimum: 1, amountMaximum: 5, chance: 16.5 }
|
||||
},
|
||||
5: {
|
||||
17: { amountMinimum: 1, chance: 20 }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function (ws, message, player) {
|
||||
let amount = message.getUint16(1);
|
||||
if (!amount) return
|
||||
|
||||
if (amount > MAXIMUM_BATCH_COUNT_MOJO) amount = MAXIMUM_BATCH_COUNT_MOJO;
|
||||
|
||||
const usedAmount = player.inventory.alterAmount(1, 3, amount);
|
||||
if (!usedAmount) return
|
||||
|
||||
const playerInventory = player.inventory.getPosessions();
|
||||
cauldronReward.single(usedAmount, playerInventory, rewards => rewardCallback({ isRegular: false, authorID: player.id, rewards, source: {} }));
|
||||
|
||||
const packet = MojoRemovedPacket(usedAmount);
|
||||
player.map.registerIndividualNetworkPacket(player.id, packet);
|
||||
|
||||
// global.test.accumulateItem(`${player.id}-1-3`, -amount);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
const packet_255 = require("../Packets/packet_255");
|
||||
|
||||
module.exports = function (ws, message, player) {
|
||||
if (player.isBeingAttacked()) return;
|
||||
|
||||
const shouldCancelDisconnect = player.isDisconnecting() && !player.hasUnexpectedlyLeft;
|
||||
const packet = packet_255({ packetId: 12, isSuccessful: shouldCancelDisconnect });
|
||||
player.map.registerIndividualNetworkPacket(player.id, packet);
|
||||
|
||||
if (shouldCancelDisconnect) return player.cancelDisconnect();
|
||||
|
||||
player.regularDisconnect();
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user