diff --git a/.vscode/settings.json b/.vscode/settings.json index 13ad47b..e01df6a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,13 @@ { - "editor.defaultFormatter": "esbenp.prettier-vscode", - "prettier.configPath": "./node/.prettierrc", - "prettier.printWidth": 120, - "prettier.tabWidth": 2, - "prettier.trailingComma": "all", - "prettier.semi": true, - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - } + "editor.defaultFormatter": "esbenp.prettier-vscode", + "prettier.configPath": "./node/.prettierrc", + "prettier.printWidth": 120, + "prettier.tabWidth": 2, + "prettier.trailingComma": "all", + "prettier.semi": true, + "prettier.useTabs": true, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } } diff --git a/node/.prettierrc b/node/.prettierrc index e39f1de..0bb2a47 100644 --- a/node/.prettierrc +++ b/node/.prettierrc @@ -5,5 +5,6 @@ "trailingComma": "all", "semi": true, "bracketSpacing": true, - "arrowParens": "always" + "arrowParens": "always", + "useTabs": true } \ No newline at end of file diff --git a/node/precompute.js b/node/precompute.js index 13b73ec..e086d1c 100644 --- a/node/precompute.js +++ b/node/precompute.js @@ -13,23 +13,23 @@ var server_auth = "123456"; var base_url = variables.base_url; appengine_call("reload_server", { keyword: variables.keyword }, function (result) { - if (result.failed) { - return; - } - G = result.game; - var start = new Date(); - for (var mname in G.maps) { - if (G.maps[mname].ignore) { - continue; - } - var cstart = new Date(); - server_bfs(mname); - console.log("Precomputed: " + mname + " in " + mssince(cstart) + "ms"); - } - console.log("Done: " + mssince(start) + "ms"); - var precomputed = {}; - precomputed.version = G.version; - precomputed.amap_data = amap_data; - precomputed.smap_data = smap_data; - fs.writeFileSync(variables.data_path, "var precomputed=" + JSON.stringify(precomputed) + ";"); + if (result.failed) { + return; + } + G = result.game; + var start = new Date(); + for (var mname in G.maps) { + if (G.maps[mname].ignore) { + continue; + } + var cstart = new Date(); + server_bfs(mname); + console.log("Precomputed: " + mname + " in " + mssince(cstart) + "ms"); + } + console.log("Done: " + mssince(start) + "ms"); + var precomputed = {}; + precomputed.version = G.version; + precomputed.amap_data = amap_data; + precomputed.smap_data = smap_data; + fs.writeFileSync(variables.data_path, "var precomputed=" + JSON.stringify(precomputed) + ";"); }); diff --git a/node/server.js b/node/server.js index 28bf769..9efe6d6 100644 --- a/node/server.js +++ b/node/server.js @@ -3,25 +3,25 @@ var is_server = 1; var is_code = 0; var is_pvp = false; var server = { - started: false, - live: false, - last_update: false, - shutdown: false, // shutdown start - stopped: false, // shutdown end - s: {}, + started: false, + live: false, + last_update: false, + shutdown: false, // shutdown start + stopped: false, // shutdown end + s: {}, }; var variables = require("./variables"); var is_sdk = variables.is_sdk; var app = require("http").createServer(http_handler); //var io=require('socket.io')(app,{pingInterval:2400,pingTimeout:6000}); var io = require("socket.io")(app, { - pingInterval: 4000, - pingTimeout: 12000, - cors: { - origin: false, - methods: "GET,HEAD,PUT,PATCH,POST,DELETE", - credentials: true, - }, + pingInterval: 4000, + pingTimeout: 12000, + cors: { + origin: false, + methods: "GET,HEAD,PUT,PATCH,POST,DELETE", + credentials: true, + }, }); // default is 25000 to 60000 var fs = require("fs"); var url = require("url"); @@ -75,145 +75,145 @@ var P = {}; // the game var S = {}; // server data var T = {}; // cosmetics var E = { - schedule: { - time_offset: 0, - dailies: [13, 20], - nightlies: [23], - night: false, - }, + schedule: { + time_offset: 0, + dailies: [13, 20], + nightlies: [23], + night: false, + }, }; // server event data var TX = []; var TXX = {}; // all inter account transactions [07/02/22] var B = { - ext_vision: 120, // extends the vision by 120 each at all 4 directions -- obsolete now - u_boundary: 70, // when an entity moves 70px in x or y, triggers .u || xy_u_logic, last_u - u_vision: 65, // when an entity moves 65px in x or y, new entities in the new area are sent || xy_upush_logic, last_upush - vision: [700, 500], - max_vision: 1000, // used at the 'attack'/'heal'-'nothtere' logic - dist: 400, - sell_dist: 400, - door_dist: 112, - // game dynamics - transporter_dist: 160, - rip_time: 12, - hlevel_loss: 3, - heal_multiplier: 1, - arena_limit: 1, - use_pack_golds: true, - v: true, - dps_heal_mult: 1.8, // originally 1.6 - dps_tank_mult: 0.25, // originally 0.25 - game_loop_log_edge: 60, - instance_loop_log_edge: 120, - m_outgoing_gmult: 0.12, - start_map: "main", - free_last_hits: false, - pause_instances: true, - global_drops: true, + ext_vision: 120, // extends the vision by 120 each at all 4 directions -- obsolete now + u_boundary: 70, // when an entity moves 70px in x or y, triggers .u || xy_u_logic, last_u + u_vision: 65, // when an entity moves 65px in x or y, new entities in the new area are sent || xy_upush_logic, last_upush + vision: [700, 500], + max_vision: 1000, // used at the 'attack'/'heal'-'nothtere' logic + dist: 400, + sell_dist: 400, + door_dist: 112, + // game dynamics + transporter_dist: 160, + rip_time: 12, + hlevel_loss: 3, + heal_multiplier: 1, + arena_limit: 1, + use_pack_golds: true, + v: true, + dps_heal_mult: 1.8, // originally 1.6 + dps_tank_mult: 0.25, // originally 0.25 + game_loop_log_edge: 60, + instance_loop_log_edge: 120, + m_outgoing_gmult: 0.12, + start_map: "main", + free_last_hits: false, + pause_instances: true, + global_drops: true, }; var CC = { - auth: 2, - move: 1.5, - players: 12, - secondhands: 16, - friend: 24, - send_updates: 12, - cruise: 10, - random_look: 10, - equip: 3, - unequip: 6, - tracker: 50, - ccreport: 3, + auth: 2, + move: 1.5, + players: 12, + secondhands: 16, + friend: 24, + send_updates: 12, + cruise: 10, + random_look: 10, + equip: 3, + unequip: 6, + tracker: 50, + ccreport: 3, }; var limits = { - calls: 200, // 4 seconds - party: 9, - party_max: 10, + calls: 200, // 4 seconds + party: 9, + party_max: 10, }; var W = { - // warning flags - chest: {}, + // warning flags + chest: {}, }; var mode = { - range_test: 0, - path_checks: 1, // when pursuing a player, monsters check each x/y-line, stop pursuit on collision [14/08/16] - //#IDEA: Disable when low performance is detected [14/08/16] - upush_test: 0, // stops monsters to test the "push" logic - random_attacks: 0, // a test mode that makes monsters attack nearby players randomly [11/09/16] - aggro: 1, - dpvpblock: 1, // double-sided pvp blocks - novi: 1, // no vision improvement - added as a patch [12/01/17] - xyinf: 0, //sends xy data for corrections - log_pvp: 1, //sends pvp data to appengine, mainly to see whether people leech xp from sub-characters [03/02/17] - drop_all: 0, - red_zone: 1, - lcorrection: 1, - enforce_smap: 1, - rbugs: 0, - noxy: 0, - nopush: 0, - freeze_latest: 0, // to test latest_calls numbers - log_all: 0, // logs all incoming websocket calls - friendly_fire: 1, - fast_mlevels: 0, - map_respawns: 0, // respawn at maps or where they define [05/12/18] - low49_20xglobal: 1, // 20x global drops for <=49 level players [03/02/19] - low49_200xgoo: 1, - pve_safe_magiports: 1, - instant_monster_attacks: 1, // #TODO: Consider dynamically sending target data instantly too - drm_check: 1, - all_roam: 0, - all_smart: 1, - prevent_external: 0, // for "test" / "hardcore" - pvp_level_gap: 0, // have to be within 10 level to attack + range_test: 0, + path_checks: 1, // when pursuing a player, monsters check each x/y-line, stop pursuit on collision [14/08/16] + //#IDEA: Disable when low performance is detected [14/08/16] + upush_test: 0, // stops monsters to test the "push" logic + random_attacks: 0, // a test mode that makes monsters attack nearby players randomly [11/09/16] + aggro: 1, + dpvpblock: 1, // double-sided pvp blocks + novi: 1, // no vision improvement - added as a patch [12/01/17] + xyinf: 0, //sends xy data for corrections + log_pvp: 1, //sends pvp data to appengine, mainly to see whether people leech xp from sub-characters [03/02/17] + drop_all: 0, + red_zone: 1, + lcorrection: 1, + enforce_smap: 1, + rbugs: 0, + noxy: 0, + nopush: 0, + freeze_latest: 0, // to test latest_calls numbers + log_all: 0, // logs all incoming websocket calls + friendly_fire: 1, + fast_mlevels: 0, + map_respawns: 0, // respawn at maps or where they define [05/12/18] + low49_20xglobal: 1, // 20x global drops for <=49 level players [03/02/19] + low49_200xgoo: 1, + pve_safe_magiports: 1, + instant_monster_attacks: 1, // #TODO: Consider dynamically sending target data instantly too + drm_check: 1, + all_roam: 0, + all_smart: 1, + prevent_external: 0, // for "test" / "hardcore" + pvp_level_gap: 0, // have to be within 10 level to attack }; var events = { - // SEASONS - holidayseason: false, - lunarnewyear: false, - valentines: false, - pinkgoo: 0, // every N minutes - 60 - snowman: 20 * 60, // 1200 normally - 60 - at sprocess_game_data - egghunt: 0, // every N minutes - 60 - // RANDOM - halloween: true, - goblin: false, - goldenbat: 160000, - cutebee: 960000, - hide_and_seek: 0, - // DAILIES - goobrawl: false, - crabxx: false, - abtesting: false, - // NIGHTLIES - icegolem: false, - franky: false, + // SEASONS + holidayseason: false, + lunarnewyear: false, + valentines: false, + pinkgoo: 0, // every N minutes - 60 + snowman: 20 * 60, // 1200 normally - 60 - at sprocess_game_data + egghunt: 0, // every N minutes - 60 + // RANDOM + halloween: true, + goblin: false, + goldenbat: 160000, + cutebee: 960000, + hide_and_seek: 0, + // DAILIES + goobrawl: false, + crabxx: false, + abtesting: false, + // NIGHTLIES + icegolem: false, + franky: false, }; var dailies = ["crabxx", "goobrawl", "abtesting"]; shuffle(dailies); var nightlies = ["icegolem", "franky"]; shuffle(nightlies); if (events.holidayseason) { - events.snowman = 60; + events.snowman = 60; } if (events.valentines) { - events.pinkgoo = 60; + events.pinkgoo = 60; } TIMEO = { - EU: 1, - US: -5, - ASIA: 7, + EU: 1, + US: -5, + ASIA: 7, }; var perfc = { - game_loop: {}, - instance_loop: {}, - instance_delay: {}, - cps: 0, - sxyu: 0, - game_loops: 0, - instance_loops: 0, - roams: 0, - roam_ops: 0, + game_loop: {}, + instance_loop: {}, + instance_delay: {}, + cps: 0, + sxyu: 0, + game_loops: 0, + instance_loops: 0, + roams: 0, + roam_ops: 0, }; var instances = {}; // at first there were no instances, monsters were global @@ -231,12771 +231,12771 @@ port = process.argv[process.argv.length - 1]; E.schedule.time_offset = TIMEO[region]; if (server_name == "HARDCORE") { - gameplay = "hardcore"; - xpm = 2188; - // luckm=500; - goldm = 12000; - is_pvp = true; - variables.ip_limit = 1; - variables.character_limit = 1; - B.rip_time = -1; - B.heal_multiplier = 0.6; - B.sell_dist = 9999999 + 1000; - events.golden_bat = 8000; - B.arena_limit = 1; - B.v = false; - B.free_last_hits = true; - mode.prevent_external = 1; - mode.pvp_level_gap = 1; + gameplay = "hardcore"; + xpm = 2188; + // luckm=500; + goldm = 12000; + is_pvp = true; + variables.ip_limit = 1; + variables.character_limit = 1; + B.rip_time = -1; + B.heal_multiplier = 0.6; + B.sell_dist = 9999999 + 1000; + events.golden_bat = 8000; + B.arena_limit = 1; + B.v = false; + B.free_last_hits = true; + mode.prevent_external = 1; + mode.pvp_level_gap = 1; } else if (server_name == "TEST") { - gameplay = "test"; - is_pvp = true; - variables.ip_limit = 5; - variables.character_limit = 5; - B.rip_time = -1; - B.sell_dist = 9999999 + 1000; - B.arena_limit = 1; - B.rbugs = 1; - mode.prevent_external = 1; + gameplay = "test"; + is_pvp = true; + variables.ip_limit = 5; + variables.character_limit = 5; + B.rip_time = -1; + B.sell_dist = 9999999 + 1000; + B.arena_limit = 1; + B.rbugs = 1; + mode.prevent_external = 1; } else if (server_name == "DUNGEON") { - gameplay = "dungeon"; - is_pvp = true; - variables.ip_limit = 1; - variables.character_limit = 1; - B.start_map = observer_map = "d_e"; - B.free_last_hits = true; - B.pause_instances = false; - for (var name in events) { - if (is_number(events[name])) { - events[name] = (events[name] !== 0 && 9999999999) || 0; - } else { - events[name] = false; - } - } + gameplay = "dungeon"; + is_pvp = true; + variables.ip_limit = 1; + variables.character_limit = 1; + B.start_map = observer_map = "d_e"; + B.free_last_hits = true; + B.pause_instances = false; + for (var name in events) { + if (is_number(events[name])) { + events[name] = (events[name] !== 0 && 9999999999) || 0; + } else { + events[name] = false; + } + } } else { - gameplay = "normal"; + gameplay = "normal"; } // B.u_boundary=12; B.u_vision=12; B["vision"]=[320,270] // B.vision[0]*=100; B.vision[1]*=100; if (server_name.startsWith("PVP") || server_name.startsWith("HARDCORE")) { - is_pvp = true; - // variables.ip_limit=2+1; - // variables.character_limit=1+1; - luckm *= 1.15; - xpm *= 1.2; - goldm *= 1.25; + is_pvp = true; + // variables.ip_limit=2+1; + // variables.character_limit=1+1; + luckm *= 1.15; + xpm *= 1.2; + goldm *= 1.25; } // var secure_app=require('https').createServer(s_options,https_handler); #GTODO: Implement secure communications at one point function init_game() { - appengine_call( - "create_server", - { - keyword: variables.keyword, - port: port, - region: region, - name: server_name, - pvp: is_pvp || "", - gameplay: gameplay, - }, - function (result) { - try { - if (result.exists) { - return [console.log("Server Exists!"), process.exit()]; - } - // console.log(result); - server_id = result.id; - server_auth = result.auth; - server_name = result.name; - server_log("Server Live: " + server_name + " " + server_id, 1); - server_log("Node Version: " + process.version, 1); - //server_log("Socket.IO Version: "+require("socket.io").version); - S = result.data; - G = result.game; - server_log("Game Version: " + G.version, 1); - D = result.dynamics; - if (S) { - init_server_data(S); - } - if (G) { - sprocess_game_data(); - } - try { - app.listen(port); - init_io(); - // app2.listen(parseInt(port)+40); - } catch (e) { - server.exists = true; - server.started = true; - log_trace("port", e); - return shutdown_routine(); - } - //create_instance("main2","main2"); - var start = new Date(); - if (gameplay == "normal" || gameplay == "hardcore" || gameplay == "test") { - create_instance("main"); - create_instance("tunnel"); - create_instance("cave"); - create_instance("tavern"); - create_instance("resort_e"); - create_instance("resort"); - create_instance("mansion"); - create_instance("woffice"); - create_instance("halloween"); - create_instance("spookytown"); - if (gameplay == "normal") { - create_instance("test"); - } - create_instance("arena", "arena", { pvp: 1 }); - create_instance("bank"); - create_instance("bank_b"); - create_instance("bank_u"); - //create_instance("batcave","batcave"); - create_instance("winterland"); - create_instance("winter_inn"); - create_instance("winter_inn_rooms"); - create_instance("winter_cave"); - create_instance("winter_cove"); - create_instance("desertland"); - create_instance("level1"); - create_instance("level2"); - create_instance("level2n"); - create_instance("level2e"); - create_instance("level2s"); - create_instance("level2w"); - create_instance("level3"); - create_instance("level4"); - create_instance("cyberland"); - create_instance("hut"); - create_instance("mtunnel"); - create_instance("ship0"); - create_instance("goobrawl"); - create_instance("d_e"); - create_instance("d_g"); - create_instance("d_b1"); - create_instance("d_a1"); - create_instance("d_a2"); - server_bfs("crypt"); - server_bfs("winter_instance"); - server_bfs("tomb"); - server_bfs("dungeon0"); - server_bfs("cgallery"); - } else if (gameplay == "dungeon") { - for (var name in G.maps) { - if (G.maps[name].world == "dungeon") { - create_instance(name); - } - } - } - create_instance("jail"); - console.log("Calculations took: " + mssince(start) + "ms"); - shuffle(hiding_places); - init_tavern(); - init_server(); - server.started = true; - server.live = true; - server.last_update = new Date(); - } catch (e) { - log_trace("init", e); - } - }, - function () { - console.log("#X init_game's create_server call failed"); - }, - ); + appengine_call( + "create_server", + { + keyword: variables.keyword, + port: port, + region: region, + name: server_name, + pvp: is_pvp || "", + gameplay: gameplay, + }, + function (result) { + try { + if (result.exists) { + return [console.log("Server Exists!"), process.exit()]; + } + // console.log(result); + server_id = result.id; + server_auth = result.auth; + server_name = result.name; + server_log("Server Live: " + server_name + " " + server_id, 1); + server_log("Node Version: " + process.version, 1); + //server_log("Socket.IO Version: "+require("socket.io").version); + S = result.data; + G = result.game; + server_log("Game Version: " + G.version, 1); + D = result.dynamics; + if (S) { + init_server_data(S); + } + if (G) { + sprocess_game_data(); + } + try { + app.listen(port); + init_io(); + // app2.listen(parseInt(port)+40); + } catch (e) { + server.exists = true; + server.started = true; + log_trace("port", e); + return shutdown_routine(); + } + //create_instance("main2","main2"); + var start = new Date(); + if (gameplay == "normal" || gameplay == "hardcore" || gameplay == "test") { + create_instance("main"); + create_instance("tunnel"); + create_instance("cave"); + create_instance("tavern"); + create_instance("resort_e"); + create_instance("resort"); + create_instance("mansion"); + create_instance("woffice"); + create_instance("halloween"); + create_instance("spookytown"); + if (gameplay == "normal") { + create_instance("test"); + } + create_instance("arena", "arena", { pvp: 1 }); + create_instance("bank"); + create_instance("bank_b"); + create_instance("bank_u"); + //create_instance("batcave","batcave"); + create_instance("winterland"); + create_instance("winter_inn"); + create_instance("winter_inn_rooms"); + create_instance("winter_cave"); + create_instance("winter_cove"); + create_instance("desertland"); + create_instance("level1"); + create_instance("level2"); + create_instance("level2n"); + create_instance("level2e"); + create_instance("level2s"); + create_instance("level2w"); + create_instance("level3"); + create_instance("level4"); + create_instance("cyberland"); + create_instance("hut"); + create_instance("mtunnel"); + create_instance("ship0"); + create_instance("goobrawl"); + create_instance("d_e"); + create_instance("d_g"); + create_instance("d_b1"); + create_instance("d_a1"); + create_instance("d_a2"); + server_bfs("crypt"); + server_bfs("winter_instance"); + server_bfs("tomb"); + server_bfs("dungeon0"); + server_bfs("cgallery"); + } else if (gameplay == "dungeon") { + for (var name in G.maps) { + if (G.maps[name].world == "dungeon") { + create_instance(name); + } + } + } + create_instance("jail"); + console.log("Calculations took: " + mssince(start) + "ms"); + shuffle(hiding_places); + init_tavern(); + init_server(); + server.started = true; + server.live = true; + server.last_update = new Date(); + } catch (e) { + log_trace("init", e); + } + }, + function () { + console.log("#X init_game's create_server call failed"); + }, + ); } init_game(); function reload_server(to_broadcast, change) { - appengine_call( - "reload_server", - { keyword: variables.keyword, port: port, region: region, pvp: is_pvp || "", gameplay: gameplay }, - function (result) { - try { - G = result.game; - D = result.dynamics; - sprocess_game_data(); - prop_cache = {}; - if (to_broadcast) { - broadcast("reloaded", { change: change || "" }); - } - } catch (e) { - broadcast("notice", { message: "Server Live Reload Failed" }); - log_trace("#X live_reload", e); - } - }, - function () { - // setTimeout(init_game,1000); - server_log("#X live_reload failed", 1); - broadcast("notice", { message: "Live Reload Failed" }); - }, - ); + appengine_call( + "reload_server", + { keyword: variables.keyword, port: port, region: region, pvp: is_pvp || "", gameplay: gameplay }, + function (result) { + try { + G = result.game; + D = result.dynamics; + sprocess_game_data(); + prop_cache = {}; + if (to_broadcast) { + broadcast("reloaded", { change: change || "" }); + } + } catch (e) { + broadcast("notice", { message: "Server Live Reload Failed" }); + log_trace("#X live_reload", e); + } + }, + function () { + // setTimeout(init_game,1000); + server_log("#X live_reload failed", 1); + broadcast("notice", { message: "Live Reload Failed" }); + }, + ); } function decode_http_data(data) { - // #TODO: Find a better way to transfer data [28/09/17] - var result = decodeURIComponent(data) - .replace_all("u'", "'") - .replace_all("'", '"') - .replace_all("+", " ") - .replace_all("%2B", "+"); - return result; + // #TODO: Find a better way to transfer data [28/09/17] + var result = decodeURIComponent(data) + .replace_all("u'", "'") + .replace_all("'", '"') + .replace_all("+", " ") + .replace_all("%2B", "+"); + return result; } function parse_http_json(data) { - var result = decode_http_data(data); - var json = {}; - try { - json = JSON.parse(result); - } catch (e) { - console.log("\n" + data); - log_trace("parse_http_json", e); - } - return json; + var result = decode_http_data(data); + var json = {}; + try { + json = JSON.parse(result); + } catch (e) { + console.log("\n" + data); + log_trace("parse_http_json", e); + } + return json; } function http_handler(request, response) { - var body = []; - request - .on("error", function (err) { - server_log("http_err: " + err, 1); - }) - .on("data", function (chunk) { - body.push(chunk); - }) - .on("end", function () { - body = Buffer.concat(body).toString(); - try { - // server_log("http_handle's end"); - var url_parts = url.parse(request.url, true); - var args = url_parts.query; - var output = ""; - //console.log(body); - (body || "").split("&").forEach(function (pv) { - var pvp = pv.split("="); - args[pvp[0]] = pvp[1]; - }); - if (args.checkin) { - // console.log(JSON.stringify(args)); - // safe_search(request,"::1"); - // console.log(JSON.stringify(request.headers)); - console.log("start"); - req = request; - console.log(req.connection ? req.connection.remoteAddress : null); - console.log(req.socket ? req.socket.remoteAddress : null); - console.log(req.connection && req.connection.socket ? req.connection.socket.remoteAddress : null); - console.log(req.info ? req.info.remoteAddress : null); - var ip = getClientIp(request); - var id = id_to_id[args.id]; - // console.log(ip); - if (players[id] && players[id].ipass == args.ipass) { - players[id].last_ip = ip; - players[id].last_ipass = new Date(); - // server_log("ipass for "+players[id].name); - } - } - if (args.spass != variables.access_master) { - response.writeHead(200); - response.end(output); - return; - } - if (args.aevent == "shutdown") { - shutdown_routine(); - } - if (args.aevent == "cupdate") { - var id = id_to_id[args.id]; - server_log("cupdate for " + args.id + " socket.id: " + id); - if (players[id]) { - var player = players[id]; - player.cash = args.cash; - if (args.ncash && args.ncash != "0") { - player.socket.emit("game_log", { message: "Received " + args.ncash + " shells", color: colors.cash }); - } + var body = []; + request + .on("error", function (err) { + server_log("http_err: " + err, 1); + }) + .on("data", function (chunk) { + body.push(chunk); + }) + .on("end", function () { + body = Buffer.concat(body).toString(); + try { + // server_log("http_handle's end"); + var url_parts = url.parse(request.url, true); + var args = url_parts.query; + var output = ""; + //console.log(body); + (body || "").split("&").forEach(function (pv) { + var pvp = pv.split("="); + args[pvp[0]] = pvp[1]; + }); + if (args.checkin) { + // console.log(JSON.stringify(args)); + // safe_search(request,"::1"); + // console.log(JSON.stringify(request.headers)); + console.log("start"); + req = request; + console.log(req.connection ? req.connection.remoteAddress : null); + console.log(req.socket ? req.socket.remoteAddress : null); + console.log(req.connection && req.connection.socket ? req.connection.socket.remoteAddress : null); + console.log(req.info ? req.info.remoteAddress : null); + var ip = getClientIp(request); + var id = id_to_id[args.id]; + // console.log(ip); + if (players[id] && players[id].ipass == args.ipass) { + players[id].last_ip = ip; + players[id].last_ipass = new Date(); + // server_log("ipass for "+players[id].name); + } + } + if (args.spass != variables.access_master) { + response.writeHead(200); + response.end(output); + return; + } + if (args.aevent == "shutdown") { + shutdown_routine(); + } + if (args.aevent == "cupdate") { + var id = id_to_id[args.id]; + server_log("cupdate for " + args.id + " socket.id: " + id); + if (players[id]) { + var player = players[id]; + player.cash = args.cash; + if (args.ncash && args.ncash != "0") { + player.socket.emit("game_log", { message: "Received " + args.ncash + " shells", color: colors.cash }); + } - resend(player, "reopen+nc"); - } - output = "yes"; - } - if (args.aevent == "new_friend") { - var id = id_to_id[args.id]; - server_log("new_friend for " + args.id + " socket.id: " + id, 1); - if (players[id]) { - var player = players[id]; - player.friends = parse_http_json(args.friends); - player.socket.emit("friend", { event: "new", name: args.name, friends: player.friends }); - resend(player, "redata"); - } - output = "yes"; - } - if (args.aevent == "lost_friend") { - var id = id_to_id[args.id]; - server_log("lost_friend for " + args.id + " socket.id: " + id, 1); - if (players[id]) { - var player = players[id]; - player.friends = parse_http_json(args.friends); - player.socket.emit("friend", { event: "lost", friends: player.friends }); // ,name:args.name - resend(player, "redata"); - } - output = "yes"; - } - if (args.aevent == "eval") { - var data = parse_http_json(args.data); - try { - eval(decode_http_data(args.code)); - } catch (e) { - console.log("\n" + args.code); - log_trace("chttp_eval", e); - } - output = JSON.stringify(output); - } - response.writeHead(200); - response.end(output); - } catch (e) { - log_trace("chttp_err", e); - } - }); + resend(player, "reopen+nc"); + } + output = "yes"; + } + if (args.aevent == "new_friend") { + var id = id_to_id[args.id]; + server_log("new_friend for " + args.id + " socket.id: " + id, 1); + if (players[id]) { + var player = players[id]; + player.friends = parse_http_json(args.friends); + player.socket.emit("friend", { event: "new", name: args.name, friends: player.friends }); + resend(player, "redata"); + } + output = "yes"; + } + if (args.aevent == "lost_friend") { + var id = id_to_id[args.id]; + server_log("lost_friend for " + args.id + " socket.id: " + id, 1); + if (players[id]) { + var player = players[id]; + player.friends = parse_http_json(args.friends); + player.socket.emit("friend", { event: "lost", friends: player.friends }); // ,name:args.name + resend(player, "redata"); + } + output = "yes"; + } + if (args.aevent == "eval") { + var data = parse_http_json(args.data); + try { + eval(decode_http_data(args.code)); + } catch (e) { + console.log("\n" + args.code); + log_trace("chttp_eval", e); + } + output = JSON.stringify(output); + } + response.writeHead(200); + response.end(output); + } catch (e) { + log_trace("chttp_err", e); + } + }); } function player_to_server(player, place) { - var char = {}; - for (prop in player) { - if ( - !in_arr(prop, [ - "auth", - "last_sync", - "socket", - "character", - "last_upush", - "push", - "last", - "last_u", - "width", - "height", - "u", - ]) - ) { - char[prop] = player[prop]; - } - } - if (place == "sync" && !Object.keys(player.q).length && player.type != "merchant") { - delete char.p; - } // ~20KB - too much [19/11/18] - return char; + var char = {}; + for (prop in player) { + if ( + !in_arr(prop, [ + "auth", + "last_sync", + "socket", + "character", + "last_upush", + "push", + "last", + "last_u", + "width", + "height", + "u", + ]) + ) { + char[prop] = player[prop]; + } + } + if (place == "sync" && !Object.keys(player.q).length && player.type != "merchant") { + delete char.p; + } // ~20KB - too much [19/11/18] + return char; } function player_to_client(player, stranger) { - var data = {}; - [ - "hp", - "max_hp", - "mp", - "max_mp", - "xp", - "attack", - "heal", - "frequency", - "speed", - "range", - "armor", - "resistance", - "level", - "party", - "rip", - "npc", - "allow", - "code", - "afk", - "target", - "focus", - "role", - "s", - "c", - "q", - "b", - "age", - "pdps", - "id", - "x", - "y", - "moving", - "going_x", - "going_y", - "abs", - "move_num", - "angle", - "cid", - "guild", - "team", - ].forEach(function (p) { - // removed "vx","vy" - if (player[p] !== undefined) { - data[p] = player[p]; - } - }); - ["stand"].forEach(function (p) { - if (player.p && player.p[p] !== undefined) { - data[p] = player.p[p]; - } - }); - if (player.afk == "code") { - data.controller = player.controller; - } - if (player.rip && ((player.tcx && player.tcx.gravestone) || player.cx.gravestone)) { - player.rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone; - } - data.skin = player.tskin || player.skin; - data.cx = player.tcx || player.cx; - data.slots = player.cslots; - if (player.tp) { - data.tp = true; - } - data.ctype = player.type; - data.owner = (!player.private && player.owner) || ""; + var data = {}; + [ + "hp", + "max_hp", + "mp", + "max_mp", + "xp", + "attack", + "heal", + "frequency", + "speed", + "range", + "armor", + "resistance", + "level", + "party", + "rip", + "npc", + "allow", + "code", + "afk", + "target", + "focus", + "role", + "s", + "c", + "q", + "b", + "age", + "pdps", + "id", + "x", + "y", + "moving", + "going_x", + "going_y", + "abs", + "move_num", + "angle", + "cid", + "guild", + "team", + ].forEach(function (p) { + // removed "vx","vy" + if (player[p] !== undefined) { + data[p] = player[p]; + } + }); + ["stand"].forEach(function (p) { + if (player.p && player.p[p] !== undefined) { + data[p] = player.p[p]; + } + }); + if (player.afk == "code") { + data.controller = player.controller; + } + if (player.rip && ((player.tcx && player.tcx.gravestone) || player.cx.gravestone)) { + player.rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone; + } + data.skin = player.tskin || player.skin; + data.cx = player.tcx || player.cx; + data.slots = player.cslots; + if (player.tp) { + data.tp = true; + } + data.ctype = player.type; + data.owner = (!player.private && player.owner) || ""; - if (player.is_npc) { - // data.id="$"+data.id; - data.name = player.name; - if (player.direction !== undefined) { - data.direction = player.direction; - data.npc = player.npc; - } - } + if (player.is_npc) { + // data.id="$"+data.id; + data.name = player.name; + if (player.direction !== undefined) { + data.direction = player.direction; + data.npc = player.npc; + } + } - if (!stranger) { - [ - "int", - "str", - "dex", - "vit", - "for", - "mp_cost", - "mp_reduction", - "max_xp", - "goldm", - "xpm", - "luckm", - "map", - "in", - "isize", - "esize", - "gold", - "cash", - "targets", - "m", - "evasion", - "miss", - "reflection", - "lifesteal", - "manasteal", - "rpiercing", - "apiercing", - "crit", - "critdamage", - "dreturn", - "tax", - "xrange", - "pnresistance", - "firesistance", - "fzresistance", - "phresistance", - "stresistance", - "incdmgamp", - "stun", - "blast", - "explosion", - "courage", - "mcourage", - "pcourage", - "fear", - ].forEach(function (p) { - //"vision", - data[p] = player[p]; - }); - data.items = player.citems; - if (player.user !== undefined) { - data.user = player.cuser; - if (player.user) { - data.user.gold = player.user.gold; - } - } - if (player.socket) { - data.cc = get_call_cost(player); - } - } - return data; + if (!stranger) { + [ + "int", + "str", + "dex", + "vit", + "for", + "mp_cost", + "mp_reduction", + "max_xp", + "goldm", + "xpm", + "luckm", + "map", + "in", + "isize", + "esize", + "gold", + "cash", + "targets", + "m", + "evasion", + "miss", + "reflection", + "lifesteal", + "manasteal", + "rpiercing", + "apiercing", + "crit", + "critdamage", + "dreturn", + "tax", + "xrange", + "pnresistance", + "firesistance", + "fzresistance", + "phresistance", + "stresistance", + "incdmgamp", + "stun", + "blast", + "explosion", + "courage", + "mcourage", + "pcourage", + "fear", + ].forEach(function (p) { + //"vision", + data[p] = player[p]; + }); + data.items = player.citems; + if (player.user !== undefined) { + data.user = player.cuser; + if (player.user) { + data.user.gold = player.user.gold; + } + } + if (player.socket) { + data.cc = get_call_cost(player); + } + } + return data; } function monster_to_client(monster, events) { - var data = {}; - var def = G.monsters[monster.type]; - [ - "speed", - "hp", - "mp", - "max_mp", - "attack", - "xp", - "frequency", - "armor", - "resistance", - "1hp", - "skin", - "cooperative", - "drops", - ].forEach(function (p) { - //same array as game.js adopt_soft_properties - if (p in monster && monster[p] != def[p]) { - data[p] = monster[p]; - } - }); - if (monster.max_hp != def.hp) { - data.max_hp = monster.max_hp; - } - [ - "id", - "x", - "y", - "moving", - "going_x", - "going_y", - "abs", - "move_num", - "angle", - "type", - "cid", - "target", - "focus", - "s", - ].forEach(function (p) { - // removed "vx","vy" from both datasets [16/04/18] - if (monster[p] !== undefined) { - data[p] = monster[p]; - } - }); - if (monster.level > 1) { - data.level = monster.level; - } - if (monster.pet) { - data.pet = true; - data.owner = monster.owner; - data.name = monster.name; - } - if (monster.trap) { - data.trap = true; - data.owner = monster.owner; - } + var data = {}; + var def = G.monsters[monster.type]; + [ + "speed", + "hp", + "mp", + "max_mp", + "attack", + "xp", + "frequency", + "armor", + "resistance", + "1hp", + "skin", + "cooperative", + "drops", + ].forEach(function (p) { + //same array as game.js adopt_soft_properties + if (p in monster && monster[p] != def[p]) { + data[p] = monster[p]; + } + }); + if (monster.max_hp != def.hp) { + data.max_hp = monster.max_hp; + } + [ + "id", + "x", + "y", + "moving", + "going_x", + "going_y", + "abs", + "move_num", + "angle", + "type", + "cid", + "target", + "focus", + "s", + ].forEach(function (p) { + // removed "vx","vy" from both datasets [16/04/18] + if (monster[p] !== undefined) { + data[p] = monster[p]; + } + }); + if (monster.level > 1) { + data.level = monster.level; + } + if (monster.pet) { + data.pet = true; + data.owner = monster.owner; + data.name = monster.name; + } + if (monster.trap) { + data.trap = true; + data.owner = monster.owner; + } - if (events && events.length) { - data.events = events; - } - return data; + if (events && events.length) { + data.events = events; + } + return data; } function player_to_summary(player) { - var summary = { - skin: player.tskin || player.skin, - level: player.level, - type: player.type, - x: player.x, - y: player.y, - in: player.in, - map: player.map, - name: player.name, - hp: player.hp, - max_hp: player.max_hp, - }; - if (player.rip) { - summary.rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone || true; - } - summary.cx = player.tcx || player.cx || {}; - return summary; + var summary = { + skin: player.tskin || player.skin, + level: player.level, + type: player.type, + x: player.x, + y: player.y, + in: player.in, + map: player.map, + name: player.name, + hp: player.hp, + max_hp: player.max_hp, + }; + if (player.rip) { + summary.rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone || true; + } + summary.cx = player.tcx || player.cx || {}; + return summary; } function save_state(player) { - player.state = { - map: player.map, - in: player.in, - hp: player.hp, - mp: player.mp, - s: player.s, - x: player.x, - y: player.y, - restored: false, - }; + player.state = { + map: player.map, + in: player.in, + hp: player.hp, + mp: player.mp, + s: player.s, + x: player.x, + y: player.y, + restored: false, + }; } function clean_slate(player) { - restore_state(player); - save_state(player); - player.hp = player.max_hp; - player.mp = player.max_mp; - player.s = {}; - player.c = {}; + restore_state(player); + save_state(player); + player.hp = player.max_hp; + player.mp = player.max_mp; + player.s = {}; + player.c = {}; } function restore_state(player, dc) { - if (player.state && !player.state.restored) { - player.hp = player.state.hp; - player.mp = player.state.mp; - player.s = player.state.s; - player.rip = false; - if (player.state.rip) { - player.rip = true; - } - if (dc) { - player.map = player.state.map; - player.in = player.state.in; - player.x = player.state.x; - player.y = player.state.y; - } - player.state.restored = true; - } - delete player.team; - delete player.duel; + if (player.state && !player.state.restored) { + player.hp = player.state.hp; + player.mp = player.state.mp; + player.s = player.state.s; + player.rip = false; + if (player.state.rip) { + player.rip = true; + } + if (dc) { + player.map = player.state.map; + player.in = player.state.in; + player.x = player.state.x; + player.y = player.state.y; + } + player.state.restored = true; + } + delete player.team; + delete player.duel; } function party_to_client(oname) { - var list = parties[oname]; - var party = {}; - var output = 0; - var length = 0; - var newbies = 0; - var odps = {}; - var c = {}; - var add = 36000; - calculate_party(oname); - list.forEach(function (name) { - var player = players[name_to_id[name]]; - var dps_multiplier = 1; - if (!player) { - return; - } - if (player.type == "merchant") { - return; - } - if (player.type == "priest") { - dps_multiplier = 1.36; - } - length += 1; - if (player.level < 60) { - newbies += 1; - } - output += player.pdps * dps_multiplier + add; - odps[player.owner] = (odps[player.owner] || 0) + player.pdps * dps_multiplier + add; - c[player.owner] = (c[player.owner] || 0) + 1; - }); - list.forEach(function (name) { - var player = players[name_to_id[name]]; - var dps_multiplier = 1; - if (!player) { - return; - } - if (player.type == "priest") { - dps_multiplier = 1.36; - } - player.share = 0; - if (!output) { - // to handle an all merchant party - player.share = 1.0 / (max(1, list.length) + EPS); - } else if (player.type != "merchant") { - player.share = odps[player.owner] / (c[player.owner] + EPS) / (output + EPS); - player.share = min(1, player.share); - } - player.party_length = length; - player.party_gold = 5; - player.party_luck = newbies * 10; - player.party_xp = [0, 0, 10, 16, 20, 24, 25, 30, 36, 40, 40, 40, 40][length] || 0; - party[name] = { - skin: player.tskin || player.skin, - level: player.level, - type: player.type, - x: player.x, - y: player.y, - in: player.in, - map: player.map, - share: player.share, - pdps: player.pdps, - l: length, - xp: player.party_xp, - luck: player.party_luck, - gold: player.party_gold, - }; - if (player.rip) { - party[name].rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone || true; - } - if (Object.keys(player.tcx || player.cx).length) { - party[name].cx = player.tcx || player.cx; - } - }); - // git test - return party; + var list = parties[oname]; + var party = {}; + var output = 0; + var length = 0; + var newbies = 0; + var odps = {}; + var c = {}; + var add = 36000; + calculate_party(oname); + list.forEach(function (name) { + var player = players[name_to_id[name]]; + var dps_multiplier = 1; + if (!player) { + return; + } + if (player.type == "merchant") { + return; + } + if (player.type == "priest") { + dps_multiplier = 1.36; + } + length += 1; + if (player.level < 60) { + newbies += 1; + } + output += player.pdps * dps_multiplier + add; + odps[player.owner] = (odps[player.owner] || 0) + player.pdps * dps_multiplier + add; + c[player.owner] = (c[player.owner] || 0) + 1; + }); + list.forEach(function (name) { + var player = players[name_to_id[name]]; + var dps_multiplier = 1; + if (!player) { + return; + } + if (player.type == "priest") { + dps_multiplier = 1.36; + } + player.share = 0; + if (!output) { + // to handle an all merchant party + player.share = 1.0 / (max(1, list.length) + EPS); + } else if (player.type != "merchant") { + player.share = odps[player.owner] / (c[player.owner] + EPS) / (output + EPS); + player.share = min(1, player.share); + } + player.party_length = length; + player.party_gold = 5; + player.party_luck = newbies * 10; + player.party_xp = [0, 0, 10, 16, 20, 24, 25, 30, 36, 40, 40, 40, 40][length] || 0; + party[name] = { + skin: player.tskin || player.skin, + level: player.level, + type: player.type, + x: player.x, + y: player.y, + in: player.in, + map: player.map, + share: player.share, + pdps: player.pdps, + l: length, + xp: player.party_xp, + luck: player.party_luck, + gold: player.party_gold, + }; + if (player.rip) { + party[name].rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone || true; + } + if (Object.keys(player.tcx || player.cx).length) { + party[name].cx = player.tcx || player.cx; + } + }); + // git test + return party; } function send_party_update(oname) { - var party = party_to_client(oname); - parties[oname].forEach(function (name) { - var player = players[name_to_id[name]]; - if (!player) { - console.log("#X party player not found: " + oname); - leave_party(oname, { name: name }); - return; - } - players[name_to_id[name]].socket.emit("party_update", { list: parties[oname], party: party }); - }); + var party = party_to_client(oname); + parties[oname].forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + console.log("#X party player not found: " + oname); + leave_party(oname, { name: name }); + return; + } + players[name_to_id[name]].socket.emit("party_update", { list: parties[oname], party: party }); + }); } function calculate_party(oname) { - var list = parties[oname]; - list.forEach(function (name) { - var player = players[name_to_id[name]]; - if (!player) { - return; - } - if (player.type == "merchant") { - player.party_weight = 0; - } - player.party_weight = 20; - }); + var list = parties[oname]; + list.forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + return; + } + if (player.type == "merchant") { + player.party_weight = 0; + } + player.party_weight = 20; + }); } var stat_to_attr = { - str: "str", - int: "int", - dex: "dex", - vit: "vit", - for: "for", - armor: "armor", - resistance: "resistance", - pnresistance: "pnresistance", - firesistance: "firesistance", - fzresistance: "fzresistance", - phresistance: "phresistance", - stresistance: "stresistance", - incdmgamp: "incdmgamp", - stun: "stun", - blast: "blast", - explosion: "explosion", - evasion: "evasion", - cuteness: "cuteness", - bling: "bling", - dreturn: "dreturn", - reflection: "reflection", - crit: "crit", - critdamage: "critdamage", - miss: "miss", - avoidance: "avoidance", - hp: "max_hp", - mp: "max_mp", - speed: "speed", - lifesteal: "lifesteal", - manasteal: "manasteal", - apiercing: "apiercing", - rpiercing: "rpiercing", - output: "output", - attack: "a_attack", - mp_cost: "a_mp_cost", - mp_reduction: "mp_reduction", - xp: "xxp", - luck: "xluck", - gold: "xgold", - range: "range", - courage: "courage", - mcourage: "mcourage", - pcourage: "pcourage", + str: "str", + int: "int", + dex: "dex", + vit: "vit", + for: "for", + armor: "armor", + resistance: "resistance", + pnresistance: "pnresistance", + firesistance: "firesistance", + fzresistance: "fzresistance", + phresistance: "phresistance", + stresistance: "stresistance", + incdmgamp: "incdmgamp", + stun: "stun", + blast: "blast", + explosion: "explosion", + evasion: "evasion", + cuteness: "cuteness", + bling: "bling", + dreturn: "dreturn", + reflection: "reflection", + crit: "crit", + critdamage: "critdamage", + miss: "miss", + avoidance: "avoidance", + hp: "max_hp", + mp: "max_mp", + speed: "speed", + lifesteal: "lifesteal", + manasteal: "manasteal", + apiercing: "apiercing", + rpiercing: "rpiercing", + output: "output", + attack: "a_attack", + mp_cost: "a_mp_cost", + mp_reduction: "mp_reduction", + xp: "xxp", + luck: "xluck", + gold: "xgold", + range: "range", + courage: "courage", + mcourage: "mcourage", + pcourage: "pcourage", }; function apply_stats(player, prop, args) { - for (var stat in prop) { - if (args && args.no_range && stat == "range") { - continue; - } - if (stat_to_attr[stat]) { - player[stat_to_attr[stat]] = player[stat_to_attr[stat]] + prop[stat]; - } else if (stat == "frequency") { - player.frequency += prop[stat] / 100.0; - } - } + for (var stat in prop) { + if (args && args.no_range && stat == "range") { + continue; + } + if (stat_to_attr[stat]) { + player[stat_to_attr[stat]] = player[stat_to_attr[stat]] + prop[stat]; + } else if (stat == "frequency") { + player.frequency += prop[stat] / 100.0; + } + } } function calculate_player_stats(player) { - if (player.is_npc) { - return; - } - player.max_xp = G.levels[player.level + ""]; - var level_up = false; - while (player.xp >= player.max_xp) { - level_up = true; - player.xp -= player.max_xp; - player.level++; - player.max_xp = G.levels[player.level + ""]; - player.hp = 0; - achievement_logic_level(player); - } - if (level_up) { - if (player.level >= 80) { - realm_broadcast("server_message", { message: player.name + " is now level " + player.level, color: "#968CFA" }); - } else if (player.level >= 70) { - broadcast("server_message", { message: player.name + " is now level " + player.level + "!", color: "#968CFA" }); - } - xy_emit(player, "ui", { type: "level_up", name: player.name }); - } - // disappearing_text(player.socket,player,"LEVEL UP!",{xy:1,size:"huge",color:"#724A8F"}); - if (player.xp < 0) { - player.xp = 0; - player.warnings = (player.warnings || 0) + 1; - if (player.warnings == 2) { - console.log("'Your monster!' logged out ->"); - player.socket.emit("ui_log", "You monster!"); - player.socket.disconnect(); - return; - } - } - var class_def = G.classes[player.type]; - var item_attack = 0; - var the_date = new Date(); - if (!class_def) { - class_def = G.classes.merchant; - } // when npc's get resend't - player.range = class_def.range; - player.max_hp = class_def.hp; - player.max_mp = class_def.mp; - ["stealth"].forEach(function (prop) { - player[prop] = false; - }); - [ - "a_mp_cost", - "a_attack", - "stones", - "lifesteal", - "manasteal", - "incdmgamp", - "mp_reduction", - "stun", - "blast", - "explosion", - "cuteness", - "bling", - ].forEach(function (prop) { - player[prop] = 0; - }); - [ - "speed", - "attack", - "frequency", - "mp_cost", - "armor", - "resistance", - "apiercing", - "rpiercing", - "a", - "aura", - "evasion", - "miss", - "reflection", - "crit", - "critdamage", - "dreturn", - "computer", - "xxp", - "xluck", - "xgold", - "output", - "courage", - "mcourage", - "pcourage", - "pnresistance", - "firesistance", - "fzresistance", - "phresistance", - "stresistance", - ].forEach(function (prop) { - player[prop] = class_def[prop] || 0; - }); - if (!player.a) { - player.a = {}; - } //abilities - if (!player.aura) { - player.aura = {}; - } - if (player.paura) { - for (var id in player.paura) { - player.aura[id] = player.paura[id]; - } - } - for (stat in class_def.stats) { - player[stat] = class_def.stats[stat] + player.level * class_def.lstats[stat]; - if (player.level > 40) { - player[stat] += (player.level - 40) * class_def.lstats[stat]; - } - if (player.level > 55) { - player[stat] += (player.level - 55) * class_def.lstats[stat]; - } - if (player.level > 65) { - player[stat] += (player.level - 65) * class_def.lstats[stat]; - } - if (player.level > 80) { - player[stat] -= (player.level - 80) * class_def.lstats[stat]; - } - player[stat] = floor(player[stat]); - } - if (!player.slots) { - player.slots = {}; - } - // players.citems[27]=null; //- to reproduce the bug - while (player.items.length > 42 && !player.items[player.items.length - 1]) { - player.items.splice(player.items.length - 1); - } - while (player.citems.length > 42 && !player.citems[player.citems.length - 1]) { - player.citems.splice(player.citems.length - 1); - } - player.isize = 42; - player.sets = {}; - player.esize = player.isize - player.items.length; - player.xpm = player.goldm = player.luckm = 1; - for (var i = 0; i < player.items.length; i++) { - var current = player.items[i]; - if (!current) { - player.esize++; - } else if (current.name == "supercomputer") { - player.tracker = player.computer = player.supercomputer = true; - } else if (current.name == "computer") { - player.computer = true; - } else if (current.name == "tracker") { - player.tracker = true; - } else if (current.expires) { - // &&!current.ex = ex=elixier - if (the_date > current.expires) { - player.items[i] = null; - player.citems[i] = null; - } else if (G.items[current.name].gain) { - var prop = calculate_item_properties(current); - player.stones++; - player["x" + G.items[current.name].gain] = prop[G.items[current.name].gain]; - } - } - } - player.monster_stats = {}; - if (player.tracker) { - for (var name in G.monsters) { - var mx = max( - (player.p.stats.monsters[name] || 0) + (player.p.stats.monsters_diff[name] || 0), - (player.max_stats.monsters[name] || [0, 0])[0], - ); - if (!mx || !G.monsters[name].achievements) { - continue; - } - G.monsters[name].achievements.forEach(function (def) { - if (mx < def[0]) { - return; - } - if (def[1] == "stat") { - player.monster_stats[def[2]] = (player.monster_stats[def[2]] || 0) + def[3]; - } - }); - } - } - character_slots.forEach(function (slot) { - var current = player.slots[slot]; - if (!current) { - return; - } - var def = G.items[current.name]; - if (!def) { - console.log("#X Undefined item: " + current.name + " (" + slot + ")"); - return; - } - var prop = calculate_item_properties(current, { class: player.type, map: player.map }); - if (prop.class && !prop.class.includes(player.type)) { - return; - } + if (player.is_npc) { + return; + } + player.max_xp = G.levels[player.level + ""]; + var level_up = false; + while (player.xp >= player.max_xp) { + level_up = true; + player.xp -= player.max_xp; + player.level++; + player.max_xp = G.levels[player.level + ""]; + player.hp = 0; + achievement_logic_level(player); + } + if (level_up) { + if (player.level >= 80) { + realm_broadcast("server_message", { message: player.name + " is now level " + player.level, color: "#968CFA" }); + } else if (player.level >= 70) { + broadcast("server_message", { message: player.name + " is now level " + player.level + "!", color: "#968CFA" }); + } + xy_emit(player, "ui", { type: "level_up", name: player.name }); + } + // disappearing_text(player.socket,player,"LEVEL UP!",{xy:1,size:"huge",color:"#724A8F"}); + if (player.xp < 0) { + player.xp = 0; + player.warnings = (player.warnings || 0) + 1; + if (player.warnings == 2) { + console.log("'Your monster!' logged out ->"); + player.socket.emit("ui_log", "You monster!"); + player.socket.disconnect(); + return; + } + } + var class_def = G.classes[player.type]; + var item_attack = 0; + var the_date = new Date(); + if (!class_def) { + class_def = G.classes.merchant; + } // when npc's get resend't + player.range = class_def.range; + player.max_hp = class_def.hp; + player.max_mp = class_def.mp; + ["stealth"].forEach(function (prop) { + player[prop] = false; + }); + [ + "a_mp_cost", + "a_attack", + "stones", + "lifesteal", + "manasteal", + "incdmgamp", + "mp_reduction", + "stun", + "blast", + "explosion", + "cuteness", + "bling", + ].forEach(function (prop) { + player[prop] = 0; + }); + [ + "speed", + "attack", + "frequency", + "mp_cost", + "armor", + "resistance", + "apiercing", + "rpiercing", + "a", + "aura", + "evasion", + "miss", + "reflection", + "crit", + "critdamage", + "dreturn", + "computer", + "xxp", + "xluck", + "xgold", + "output", + "courage", + "mcourage", + "pcourage", + "pnresistance", + "firesistance", + "fzresistance", + "phresistance", + "stresistance", + ].forEach(function (prop) { + player[prop] = class_def[prop] || 0; + }); + if (!player.a) { + player.a = {}; + } //abilities + if (!player.aura) { + player.aura = {}; + } + if (player.paura) { + for (var id in player.paura) { + player.aura[id] = player.paura[id]; + } + } + for (stat in class_def.stats) { + player[stat] = class_def.stats[stat] + player.level * class_def.lstats[stat]; + if (player.level > 40) { + player[stat] += (player.level - 40) * class_def.lstats[stat]; + } + if (player.level > 55) { + player[stat] += (player.level - 55) * class_def.lstats[stat]; + } + if (player.level > 65) { + player[stat] += (player.level - 65) * class_def.lstats[stat]; + } + if (player.level > 80) { + player[stat] -= (player.level - 80) * class_def.lstats[stat]; + } + player[stat] = floor(player[stat]); + } + if (!player.slots) { + player.slots = {}; + } + // players.citems[27]=null; //- to reproduce the bug + while (player.items.length > 42 && !player.items[player.items.length - 1]) { + player.items.splice(player.items.length - 1); + } + while (player.citems.length > 42 && !player.citems[player.citems.length - 1]) { + player.citems.splice(player.citems.length - 1); + } + player.isize = 42; + player.sets = {}; + player.esize = player.isize - player.items.length; + player.xpm = player.goldm = player.luckm = 1; + for (var i = 0; i < player.items.length; i++) { + var current = player.items[i]; + if (!current) { + player.esize++; + } else if (current.name == "supercomputer") { + player.tracker = player.computer = player.supercomputer = true; + } else if (current.name == "computer") { + player.computer = true; + } else if (current.name == "tracker") { + player.tracker = true; + } else if (current.expires) { + // &&!current.ex = ex=elixier + if (the_date > current.expires) { + player.items[i] = null; + player.citems[i] = null; + } else if (G.items[current.name].gain) { + var prop = calculate_item_properties(current); + player.stones++; + player["x" + G.items[current.name].gain] = prop[G.items[current.name].gain]; + } + } + } + player.monster_stats = {}; + if (player.tracker) { + for (var name in G.monsters) { + var mx = max( + (player.p.stats.monsters[name] || 0) + (player.p.stats.monsters_diff[name] || 0), + (player.max_stats.monsters[name] || [0, 0])[0], + ); + if (!mx || !G.monsters[name].achievements) { + continue; + } + G.monsters[name].achievements.forEach(function (def) { + if (mx < def[0]) { + return; + } + if (def[1] == "stat") { + player.monster_stats[def[2]] = (player.monster_stats[def[2]] || 0) + def[3]; + } + }); + } + } + character_slots.forEach(function (slot) { + var current = player.slots[slot]; + if (!current) { + return; + } + var def = G.items[current.name]; + if (!def) { + console.log("#X Undefined item: " + current.name + " (" + slot + ")"); + return; + } + var prop = calculate_item_properties(current, { class: player.type, map: player.map }); + if (prop.class && !prop.class.includes(player.type)) { + return; + } - apply_stats(player, prop, { no_range: slot == "offhand" && def.type == "weapon" }); - if (prop.attack) { - if (slot == "offhand") { - item_attack += prop.attack * 0.7; - } else { - item_attack += prop.attack; - } - } - if (def.ability) { - player.a[def.ability] = { - attr0: ((player.a[def.ability] && player.a[def.ability].attr0) || 0) + prop.attr0, - attr1: ((player.a[def.ability] && player.a[def.ability].attr1) || 0) + prop.attr1, - }; - } - if (def.aura) { - player.aura[def.aura] = { attr0: prop.attr0 || 0, attr1: prop.attr1 || 0 }; - } - if (slot == "mainhand") { - apply_stats(player, class_def.doublehand[def.wtype] || class_def.mainhand[def.wtype] || {}); - } - if (slot == "offhand") { - apply_stats( - player, - class_def.offhand[def.wtype] || - class_def.offhand[def.type] || { - no_range: !player.slots.mainhand, - }, - ); - } - if (prop.set) { - player.sets[def.set] = (player.sets[def.set] || 0) + 1; - } - }); - for (var set in player.sets) { - var prop = G.sets[set] && G.sets[set][player.sets[set]]; - if (!prop) { - continue; - } - apply_stats(player, prop); - } - for (var condition in player.s) { - var prop = G.conditions[condition]; - apply_stats(player, player.s[condition]); - if (!prop) { - continue; - } - apply_stats(player, prop); - } - apply_stats(player, player.monster_stats); - if ( - player.slots.mainhand && - player.slots.offhand && - G.items[player.slots.mainhand.name].wtype == "stars" && - G.items[player.slots.offhand.name].wtype != "stars" - ) { - item_attack /= 3.0; - } - if (player.slots.cape && player.slots.cape.name == "stealthcape") { - player.stealth = true; - } - item_attack = max(item_attack, 5); - if (player.type == "paladin") { - player.attack += item_attack * (player.str / 20.0 + player.int / 40.0); - } else { - player.attack += item_attack * (player[class_def.main_stat] / 20.0); - } - player.attack += player.a_attack; - if (player.type == "priest") { - player.attack *= 1.6; - } - if (player.type == "warrior") { - player.courage += round(player.str / 30); - } - if (player.type == "priest") { - player.mcourage += round(player.int / 30); - } - if (player.type == "paladin") { - player.pcourage += round(player.str / 30 + player.int / 30); - } - //console.log(player.speed) - //player.speed+=player.dex/20.0+player.str/40.0+min(player.level,40)/4.0+max(0,min(player.level-40,20))/5.0+max(0,player.level-60)/7.0 - player.speed += - min(player.dex, 256) / 32.0 + - min(player.str, 256) / 64.0 + - min(player.level, 40) / 10.0 + - max(0, min(player.level - 40, 20)) / 15.0 + - max(0, min(86, player.level) - 60) / 16.0; + apply_stats(player, prop, { no_range: slot == "offhand" && def.type == "weapon" }); + if (prop.attack) { + if (slot == "offhand") { + item_attack += prop.attack * 0.7; + } else { + item_attack += prop.attack; + } + } + if (def.ability) { + player.a[def.ability] = { + attr0: ((player.a[def.ability] && player.a[def.ability].attr0) || 0) + prop.attr0, + attr1: ((player.a[def.ability] && player.a[def.ability].attr1) || 0) + prop.attr1, + }; + } + if (def.aura) { + player.aura[def.aura] = { attr0: prop.attr0 || 0, attr1: prop.attr1 || 0 }; + } + if (slot == "mainhand") { + apply_stats(player, class_def.doublehand[def.wtype] || class_def.mainhand[def.wtype] || {}); + } + if (slot == "offhand") { + apply_stats( + player, + class_def.offhand[def.wtype] || + class_def.offhand[def.type] || { + no_range: !player.slots.mainhand, + }, + ); + } + if (prop.set) { + player.sets[def.set] = (player.sets[def.set] || 0) + 1; + } + }); + for (var set in player.sets) { + var prop = G.sets[set] && G.sets[set][player.sets[set]]; + if (!prop) { + continue; + } + apply_stats(player, prop); + } + for (var condition in player.s) { + var prop = G.conditions[condition]; + apply_stats(player, player.s[condition]); + if (!prop) { + continue; + } + apply_stats(player, prop); + } + apply_stats(player, player.monster_stats); + if ( + player.slots.mainhand && + player.slots.offhand && + G.items[player.slots.mainhand.name].wtype == "stars" && + G.items[player.slots.offhand.name].wtype != "stars" + ) { + item_attack /= 3.0; + } + if (player.slots.cape && player.slots.cape.name == "stealthcape") { + player.stealth = true; + } + item_attack = max(item_attack, 5); + if (player.type == "paladin") { + player.attack += item_attack * (player.str / 20.0 + player.int / 40.0); + } else { + player.attack += item_attack * (player[class_def.main_stat] / 20.0); + } + player.attack += player.a_attack; + if (player.type == "priest") { + player.attack *= 1.6; + } + if (player.type == "warrior") { + player.courage += round(player.str / 30); + } + if (player.type == "priest") { + player.mcourage += round(player.int / 30); + } + if (player.type == "paladin") { + player.pcourage += round(player.str / 30 + player.int / 30); + } + //console.log(player.speed) + //player.speed+=player.dex/20.0+player.str/40.0+min(player.level,40)/4.0+max(0,min(player.level-40,20))/5.0+max(0,player.level-60)/7.0 + player.speed += + min(player.dex, 256) / 32.0 + + min(player.str, 256) / 64.0 + + min(player.level, 40) / 10.0 + + max(0, min(player.level - 40, 20)) / 15.0 + + max(0, min(86, player.level) - 60) / 16.0; - player.aggro_diff = player.bling / 100 - player.cuteness / 100; + player.aggro_diff = player.bling / 100 - player.cuteness / 100; - //console.log(player.speed) - //player.max_hp+=player.str*5+player.vit*player.level/2; //player.str*10.0+player.level*10+player.vit*25 - player.max_hp += player.str * 21 + player.vit * (48 + player.level / 3.0); - player.max_hp = max(1, player.max_hp); - player.max_mp += player.int * 15.0 + player.level * 5; - //player.armor+=player.str/2.0; - player.armor += min(player.str, 160) + max(0, player.str - 160) * 0.25; - player.resistance += min(player.int, 180) + max(0, player.int - 180) * 0.25; - player.frequency += - min(player.level, 80) / 164.0 + - min(160, player.dex) / 640.0 + - max(player.dex - 160, 0) / 925.0 + - player.int / 1575.0; // 120 635 1275 is the original mix - // player.frequency=9000; - player.attack_ms = round(1000.0 / player.frequency); - if (player.last_attack_ms && player.attack_ms != player.last_attack_ms) { - // server_log("skill_timeout_correction: "+player.last_attack_ms+" to "+player.attack_ms+" timeout: "+(player.attack_ms-mssince(player.last.attack))) - player.socket.emit("skill_timeout", { - name: "attack", - ms: player.attack_ms - mssince(player.last.attack), - reason: "attack_ms", - }); - } - player.last_attack_ms = player.attack_ms; - player.mp_cost += - min(player.level, 80) * (player.mp_cost / 10.0) + - player.a_mp_cost + - player.crit * 1.25 + - player.lifesteal * 1.5 + - player.manasteal / 5.0; - if (player.damage_type == "physical") { - player.mp_cost += player.apiercing / 15.0; - } else { - player.mp_cost += player.rpiercing / 15.0; - } - player.mp_cost = max(1, player.mp_cost); - if (!player.hp && !player.rip) { - player.hp = player.max_hp; - player.mp = player.max_mp; - } // used for level-ups - if ((player.gold || 0) <= 0) { - player.gold = 0; - } - player.heal = 0; - if (player.type == "priest") { - player.heal = player.attack; - } - player.output = max(5, player.output); - if (player.output) { - player.attack = (player.attack * player.output) / 100.0; - } - if (player.s.damage_received) { - player.attack += (player.s.damage_received.amount * 4) / 100; - } - ["attack", "heal", "hp", "mp", "max_hp", "max_mp", "range", "mp_cost", "resistance", "armor"].forEach( - function (prop) { - player[prop] = round(player[prop]); - }, - ); - player.hp = max(0, min(player.hp, player.max_hp)); - player.mp = max(0, min(player.mp, player.max_mp)); + //console.log(player.speed) + //player.max_hp+=player.str*5+player.vit*player.level/2; //player.str*10.0+player.level*10+player.vit*25 + player.max_hp += player.str * 21 + player.vit * (48 + player.level / 3.0); + player.max_hp = max(1, player.max_hp); + player.max_mp += player.int * 15.0 + player.level * 5; + //player.armor+=player.str/2.0; + player.armor += min(player.str, 160) + max(0, player.str - 160) * 0.25; + player.resistance += min(player.int, 180) + max(0, player.int - 180) * 0.25; + player.frequency += + min(player.level, 80) / 164.0 + + min(160, player.dex) / 640.0 + + max(player.dex - 160, 0) / 925.0 + + player.int / 1575.0; // 120 635 1275 is the original mix + // player.frequency=9000; + player.attack_ms = round(1000.0 / player.frequency); + if (player.last_attack_ms && player.attack_ms != player.last_attack_ms) { + // server_log("skill_timeout_correction: "+player.last_attack_ms+" to "+player.attack_ms+" timeout: "+(player.attack_ms-mssince(player.last.attack))) + player.socket.emit("skill_timeout", { + name: "attack", + ms: player.attack_ms - mssince(player.last.attack), + reason: "attack_ms", + }); + } + player.last_attack_ms = player.attack_ms; + player.mp_cost += + min(player.level, 80) * (player.mp_cost / 10.0) + + player.a_mp_cost + + player.crit * 1.25 + + player.lifesteal * 1.5 + + player.manasteal / 5.0; + if (player.damage_type == "physical") { + player.mp_cost += player.apiercing / 15.0; + } else { + player.mp_cost += player.rpiercing / 15.0; + } + player.mp_cost = max(1, player.mp_cost); + if (!player.hp && !player.rip) { + player.hp = player.max_hp; + player.mp = player.max_mp; + } // used for level-ups + if ((player.gold || 0) <= 0) { + player.gold = 0; + } + player.heal = 0; + if (player.type == "priest") { + player.heal = player.attack; + } + player.output = max(5, player.output); + if (player.output) { + player.attack = (player.attack * player.output) / 100.0; + } + if (player.s.damage_received) { + player.attack += (player.s.damage_received.amount * 4) / 100; + } + ["attack", "heal", "hp", "mp", "max_hp", "max_mp", "range", "mp_cost", "resistance", "armor"].forEach( + function (prop) { + player[prop] = round(player[prop]); + }, + ); + player.hp = max(0, min(player.hp, player.max_hp)); + player.mp = max(0, min(player.mp, player.max_mp)); - if (player.party && parties[player.party]) { - if (player.party_xp) { - player.xxp += player.party_xp; - } - if (player.party_luck) { - player.xluck += player.party_luck; - } - if (player.party_gold) { - player.xgold += player.party_gold; - } - } + if (player.party && parties[player.party]) { + if (player.party_xp) { + player.xxp += player.party_xp; + } + if (player.party_luck) { + player.xluck += player.party_luck; + } + if (player.party_gold) { + player.xgold += player.party_gold; + } + } - if (goldm != 1) { - player.xgold += (goldm - 1) * 100.0; - player.xluck += (luckm - 1) * 100.0; - player.xxp += (xpm - 1) * 100.0; - } - player.luckm = 1 + player.xluck / 100.0; - player.xpm = 1 + player.xxp / 100.0; - player.goldm = 1 + player.xgold / 100.0; - ["luckm", "xpm", "goldm"].forEach(function (p) { - player[p] = max(0.01, player[p]); - }); + if (goldm != 1) { + player.xgold += (goldm - 1) * 100.0; + player.xluck += (luckm - 1) * 100.0; + player.xxp += (xpm - 1) * 100.0; + } + player.luckm = 1 + player.xluck / 100.0; + player.xpm = 1 + player.xxp / 100.0; + player.goldm = 1 + player.xgold / 100.0; + ["luckm", "xpm", "goldm"].forEach(function (p) { + player[p] = max(0.01, player[p]); + }); - if (player.tskin == "konami") { - player.range = 120; - player.frequency += 0.25; - player.goldm *= 0.25; - player.luckm += 0.25; - } + if (player.tskin == "konami") { + player.range = 120; + player.frequency += 0.25; + player.goldm *= 0.25; + player.luckm += 0.25; + } - if (player.s.invis) { - player.speed = max(player.speed * 0.6, player.speed - 25); - player.attack = round(player.attack * 1.25); - } - if (player.s.invincible) { - player.attack = round(player.attack * 0.45); - } - if (player.s.dash) { - player.speed = 500; - } - calculate_common_stats(player); + if (player.s.invis) { + player.speed = max(player.speed * 0.6, player.speed - 25); + player.attack = round(player.attack * 1.25); + } + if (player.s.invincible) { + player.attack = round(player.attack * 0.45); + } + if (player.s.dash) { + player.speed = 500; + } + calculate_common_stats(player); - player.tax = - (player.level > 80 && 0.01) || - (player.level > 80 && 0.012) || - (player.level > 70 && 0.02) || - (player.level > 60 && 0.025) || - (player.level > 50 && 0.03) || - (player.level > 20 && 0.04) || - 0.05; + player.tax = + (player.level > 80 && 0.01) || + (player.level > 80 && 0.012) || + (player.level > 70 && 0.02) || + (player.level > 60 && 0.025) || + (player.level > 50 && 0.03) || + (player.level > 20 && 0.04) || + 0.05; - var excess = max( - 0, - max(player.targets_p - player.courage, max(player.targets_m - player.mcourage, player.targets_u - player.pcourage)), - ); - var sredux = [0, 20, 40, 70, 80, 90, 100]; - player.speed -= sredux[min(sredux.length - 1, excess)]; - if (excess > 2) { - player.attack = round(player.attack * 0.2); - } else if (excess > 1) { - player.attack = round(player.attack * 0.4); - } else if (excess) { - player.attack = round(player.attack * 0.6); - } - player.fear = excess; + var excess = max( + 0, + max(player.targets_p - player.courage, max(player.targets_m - player.mcourage, player.targets_u - player.pcourage)), + ); + var sredux = [0, 20, 40, 70, 80, 90, 100]; + player.speed -= sredux[min(sredux.length - 1, excess)]; + if (excess > 2) { + player.attack = round(player.attack * 0.2); + } else if (excess > 1) { + player.attack = round(player.attack * 0.4); + } else if (excess) { + player.attack = round(player.attack * 0.6); + } + player.fear = excess; - if (player.map == "winterland") { - player.speed *= 0.95; - } + if (player.map == "winterland") { + player.speed *= 0.95; + } - if (player.p.stand || player.s.hardshell) { - player.speed = 10; - } - player.evasion = min(50, player.evasion); - player.reflection = min((player.s.reflection && 50) || 30, player.reflection); - player.speed = min(player.speed, player.cruise || 200000); - player.speed = round(max(5, player.speed)); - if (!player.gold && player.gold !== 0) { - player.gold = 0; - server_log("#X - GOLD BUG calculate", 1); - } - recalculate_vxy(player); - perfc.cps += 1; + if (player.p.stand || player.s.hardshell) { + player.speed = 10; + } + player.evasion = min(50, player.evasion); + player.reflection = min((player.s.reflection && 50) || 30, player.reflection); + player.speed = min(player.speed, player.cruise || 200000); + player.speed = round(max(5, player.speed)); + if (!player.gold && player.gold !== 0) { + player.gold = 0; + server_log("#X - GOLD BUG calculate", 1); + } + recalculate_vxy(player); + perfc.cps += 1; } function calculate_common_stats(entity) { - if (entity.s.poisoned) { - entity.frequency *= 0.9; - } - if (entity.s.frozen) { - entity.frequency *= 0.3; - entity.speed -= 40; - } - if (entity.speed < 1) { - entity.speed = 1; - } - if (entity.s.tangled) { - entity.speed = min(entity.speed, 24); - } + if (entity.s.poisoned) { + entity.frequency *= 0.9; + } + if (entity.s.frozen) { + entity.frequency *= 0.3; + entity.speed -= 40; + } + if (entity.speed < 1) { + entity.speed = 1; + } + if (entity.s.tangled) { + entity.speed = min(entity.speed, 24); + } } function calculate_monster_stats(monster) { - var def = G.monsters[monster.type]; - ["attack", "speed", "frequency", "armor", "resistance", "output", "incdmgamp", "avoidance"].forEach(function (p) { - monster[p] = def[p] || 0; - }); - monster.output = 100; - if (monster.target || monster.focus) { - monster.speed = G.monsters[monster.type].charge; - } - for (var name in monster.s) { - // if(G.conditions[name]) - // { - // for(var p in G.conditions[name]) - // if(p in monster) - // monster[p]+=G.conditions[name][p]; - // } + var def = G.monsters[monster.type]; + ["attack", "speed", "frequency", "armor", "resistance", "output", "incdmgamp", "avoidance"].forEach(function (p) { + monster[p] = def[p] || 0; + }); + monster.output = 100; + if (monster.target || monster.focus) { + monster.speed = G.monsters[monster.type].charge; + } + for (var name in monster.s) { + // if(G.conditions[name]) + // { + // for(var p in G.conditions[name]) + // if(p in monster) + // monster[p]+=G.conditions[name][p]; + // } - var prop = G.conditions[name]; - apply_stats(monster, monster.s[name]); - if (!prop) { - continue; - } - apply_stats(monster, prop); - } - monster.attack = round((monster.attack * monster.output) / 100.0); - if (monster.focus && instances[monster.in].monsters[monster.focus]) { - monster.speed = min(monster.speed, instances[monster.in].monsters[monster.focus].speed + 4); - } - if (monster.level > 1) { - var att_mult = 0.125; - var freq_mult = 0.034; - var speed_mult = 0.24; // speed originally 0.34 - var mlevel = min(monster.level, 12); - if (monster.map_def.grow) { - att_mult = 0.05; - freq_mult = 0.008; - speed_mult = 0.16; - } - monster.attack = parseInt(monster.attack * (1 + mlevel * att_mult)); - monster.speed += mlevel * speed_mult; - monster.frequency += mlevel * freq_mult; - } - if (E.schedule.night) { - monster.speed = ceil(monster.speed * 0.7); - } - calculate_common_stats(monster); - recalculate_vxy(monster); + var prop = G.conditions[name]; + apply_stats(monster, monster.s[name]); + if (!prop) { + continue; + } + apply_stats(monster, prop); + } + monster.attack = round((monster.attack * monster.output) / 100.0); + if (monster.focus && instances[monster.in].monsters[monster.focus]) { + monster.speed = min(monster.speed, instances[monster.in].monsters[monster.focus].speed + 4); + } + if (monster.level > 1) { + var att_mult = 0.125; + var freq_mult = 0.034; + var speed_mult = 0.24; // speed originally 0.34 + var mlevel = min(monster.level, 12); + if (monster.map_def.grow) { + att_mult = 0.05; + freq_mult = 0.008; + speed_mult = 0.16; + } + monster.attack = parseInt(monster.attack * (1 + mlevel * att_mult)); + monster.speed += mlevel * speed_mult; + monster.frequency += mlevel * freq_mult; + } + if (E.schedule.night) { + monster.speed = ceil(monster.speed * 0.7); + } + calculate_common_stats(monster); + recalculate_vxy(monster); } function ccms(monster) { - if (Object.keys(monster.s).length) { - calculate_monster_stats(monster); - } + if (Object.keys(monster.s).length) { + calculate_monster_stats(monster); + } } function can_equip_item(player, item, slot) { - var class_def = G.classes[player.type]; - if (!slot) { - slot = item.type; - } - if (slot == "offhand" && item.type != "weapon") { - slot = item.type; - } //Easiest solution to the slot:"offhand" challenge [15/11/16] - if (item.type == "tool") { - slot = "mainhand"; - } - if (item.type == "test" && slot != "test") { - return slot; - } - if ( - !in_arr(item.type, [ - "helmet", - "pants", - "chest", - "weapon", - "amulet", - "earring", - "shoes", - "gloves", - "ring", - "shield", - "belt", - "source", - "orb", - "quiver", - "cape", - "misc_offhand", - "tool", - ]) - ) { - return "no"; - } - if (slot != item.type && in_arr(item.type, ["shield", "source", "quiver", "misc_offhand"]) && slot != "offhand") { - return "no"; - } - if (in_arr(slot, ["weapon", "mainhand", "offhand", "tool"])) { - if ( - slot == "weapon" && - player.slots.mainhand && - class_def.mainhand[G.items[player.slots.mainhand.name].wtype] && - !player.slots.offhand && - class_def.offhand[item.wtype] - ) { - slot = "offhand"; - } else if ( - slot == "offhand" && - (!player.slots.mainhand || - (player.slots.mainhand && class_def.mainhand[G.items[player.slots.mainhand.name].wtype])) && - class_def.offhand[item.wtype] - ) { - slot = "offhand"; - } else if (slot == "weapon" || slot == "mainhand") { - if (class_def.doublehand[item.wtype] && !player.slots.offhand) { - } else if (!class_def.mainhand[item.wtype]) { - return "no"; - } - slot = "mainhand"; - } else { - return "no"; - } - } else if (item.type == "shield" || item.type == "misc_offhand") { - if ( - class_def.offhand[item.type] && - !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) - ) { - slot = "offhand"; - } else { - return "no"; - } - } else if (item.type == "quiver") { - if ( - class_def.offhand.quiver && - !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) - ) { - slot = "offhand"; - } else { - return "no"; - } - } else if (item.type == "source") { - if ( - class_def.offhand.source && - !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) - ) { - slot = "offhand"; - } else { - return "no"; - } - } else if (item.type == "ring") { - if (slot == "ring1" || slot == "ring2") { - } else if (!player.slots["ring1"]) { - slot = "ring1"; - } else { - slot = "ring2"; - } - } else if (item.type == "earring") { - if (slot == "earring1" || slot == "earring2") { - } else if (!player.slots["earring1"]) { - slot = "earring1"; - } else { - slot = "earring2"; - } - } else if (slot != item.type) { - return "no"; - } - return slot; + var class_def = G.classes[player.type]; + if (!slot) { + slot = item.type; + } + if (slot == "offhand" && item.type != "weapon") { + slot = item.type; + } //Easiest solution to the slot:"offhand" challenge [15/11/16] + if (item.type == "tool") { + slot = "mainhand"; + } + if (item.type == "test" && slot != "test") { + return slot; + } + if ( + !in_arr(item.type, [ + "helmet", + "pants", + "chest", + "weapon", + "amulet", + "earring", + "shoes", + "gloves", + "ring", + "shield", + "belt", + "source", + "orb", + "quiver", + "cape", + "misc_offhand", + "tool", + ]) + ) { + return "no"; + } + if (slot != item.type && in_arr(item.type, ["shield", "source", "quiver", "misc_offhand"]) && slot != "offhand") { + return "no"; + } + if (in_arr(slot, ["weapon", "mainhand", "offhand", "tool"])) { + if ( + slot == "weapon" && + player.slots.mainhand && + class_def.mainhand[G.items[player.slots.mainhand.name].wtype] && + !player.slots.offhand && + class_def.offhand[item.wtype] + ) { + slot = "offhand"; + } else if ( + slot == "offhand" && + (!player.slots.mainhand || + (player.slots.mainhand && class_def.mainhand[G.items[player.slots.mainhand.name].wtype])) && + class_def.offhand[item.wtype] + ) { + slot = "offhand"; + } else if (slot == "weapon" || slot == "mainhand") { + if (class_def.doublehand[item.wtype] && !player.slots.offhand) { + } else if (!class_def.mainhand[item.wtype]) { + return "no"; + } + slot = "mainhand"; + } else { + return "no"; + } + } else if (item.type == "shield" || item.type == "misc_offhand") { + if ( + class_def.offhand[item.type] && + !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) + ) { + slot = "offhand"; + } else { + return "no"; + } + } else if (item.type == "quiver") { + if ( + class_def.offhand.quiver && + !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) + ) { + slot = "offhand"; + } else { + return "no"; + } + } else if (item.type == "source") { + if ( + class_def.offhand.source && + !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) + ) { + slot = "offhand"; + } else { + return "no"; + } + } else if (item.type == "ring") { + if (slot == "ring1" || slot == "ring2") { + } else if (!player.slots["ring1"]) { + slot = "ring1"; + } else { + slot = "ring2"; + } + } else if (item.type == "earring") { + if (slot == "earring1" || slot == "earring2") { + } else if (!player.slots["earring1"]) { + slot = "earring1"; + } else { + slot = "earring2"; + } + } else if (slot != item.type) { + return "no"; + } + return slot; } function consume(player, num, quantity) { - var available = player.items[num].q || 1; - if (quantity > available) { - exception = not_enough_items; - } - if (available == quantity) { - player.items[num] = null; - player.esize++; - } else { - player.items[num].q -= quantity; - } - player.citems[num] = cache_item(player.items[num]); + var available = player.items[num].q || 1; + if (quantity > available) { + exception = not_enough_items; + } + if (available == quantity) { + player.items[num] = null; + player.esize++; + } else { + player.items[num].q -= quantity; + } + player.citems[num] = cache_item(player.items[num]); } function consume_one(player, num) { - consume(player, num, 1); + consume(player, num, 1); } function consume_one_by_id(player, id) { - for (var i = 0; i < player.items.length; i++) { - if (player.items[i] && player.items[i].name == id) { - consume_one(player, i); - return true; - } - } - return false; + for (var i = 0; i < player.items.length; i++) { + if (player.items[i] && player.items[i].name == id) { + consume_one(player, i); + return true; + } + } + return false; } function create_new_item(name, quantity) { - var new_item = { name: name }; - if (G.items[name].s) { - new_item.q = quantity || 1; - } - if (G.items[name].upgrade || G.items[name].compound) { - new_item.level = 0; - } - return new_item; + var new_item = { name: name }; + if (G.items[name].s) { + new_item.q = quantity || 1; + } + if (G.items[name].upgrade || G.items[name].compound) { + new_item.level = 0; + } + return new_item; } function aadd_item(player, new_item) { - // admin [24/06/23] - add_item(player, new_item, { announce: false }); - reopen(player, "u+cid+reopen"); + // admin [24/06/23] + add_item(player, new_item, { announce: false }); + reopen(player, "u+cid+reopen"); } function add_item(player, new_item, args) { - // # IMPORTANT: ALWAYS KEEP CAN_ADD_ITEM IN SYNC! - var done = false; - var num = 0; - var a = false; - if (!args) { - args = {}; - } - if (!new_item.name) { - new_item = create_new_item(new_item, args.q || 1); - } - if (args.p) { - new_item.p = args.p; - } - if ( - (args.m || args.r) && - ((G.items[new_item.name].upgrade && Math.random() < 1.0 / 500) || - (G.items[new_item.name].compound && Math.random() < 1.0 / 20000)) - ) { - new_item.p = "shiny"; - a = true; - } - if (G.items[new_item.name].s) { - for (var i = 0; i < player.items.length; i++) { - var item = player.items[i]; - if (can_stack(item, new_item)) { - if ((args.v && is_in_pvp(player, 1)) || new_item.v) { - item.v = new Date(); - } - if (new_item.m) { - item.m = new_item.m; - } - item.q = parseInt((new_item.q || 1) + (item.q || 1)); // to fix the side-effects of not including parseInt on 'sell'/'buy' [22/02/18] - player.citems[i] = cache_item(player.items[i]); - done = true; - num = i; - break; - } - } - } - if (!new_item.oo) { - new_item.oo = player.name; - } - if (!done) { - for (var i = 0; i < player.items.length; i++) { - var item = player.items[i]; - if (!item) { - if (args.v && is_in_pvp(player, 1)) { - new_item.v = new Date(); - } - player.items[i] = new_item; - player.citems[i] = cache_item(player.items[i]); - player.esize--; // esize-- needed for concurrent party chest operations [18/10/18] - done = true; - num = i; - break; - } - } - } - if (!done) { - player.items.push(new_item); - player.citems[player.items.length - 1] = cache_item(player.items[player.items.length - 1]); - num = player.items.length - 1; - player.esize--; - } - if ( - args.m && - player.s.mluck && - get_player(player.s.mluck.f) && - Math.random() <= 0.02 && - can_add_item(get_player(player.s.mluck.f), new_item.name) - ) { - var mr = get_player(player.s.mluck.f); - var item = create_new_item(new_item.name); - item.m = player.name; - if (new_item.data) { - item.data = new_item.data; - } // "cxjar" - var num = add_item(mr, item, { m: 1 }); - mr.socket.emit("game_log", { message: "Found " + item_to_phrase(item), color: "#64B867" }); - xy_emit(mr, "ui", { type: "+M", name: mr.name, item: cache_item(item), num: num, cevent: "mluck", event: "mluck" }); - resend(mr, "reopen+nc+inv"); - } - if (args.announce !== false && (a || (a_score[new_item.name] || 0) < G.items[new_item.name].a) && !player.stealth) { - var event = G.items[new_item.name].event; - var e_type = "server_found"; - var e_phrase = " found "; - if (!args.found) { - e_type = "server_received"; - e_phrase = " received "; - } - if (args.phrase) { - e_type = "server_received"; - e_phrase = " " + args.phrase.toLowerCase() + " "; - } - broadcast("server_message", { - message: player.name + e_phrase + item_to_phrase(new_item), - color: "#85C76B", - type: e_type, - item: cache_item(new_item), - name: player.name, - event: event, - }); - a_score[new_item.name] = (a_score[new_item.name] || 0) + 1.05; - } - if (args.log) { - player.socket.emit("game_response", { response: "add_item", item: new_item }); - } - return num; + // # IMPORTANT: ALWAYS KEEP CAN_ADD_ITEM IN SYNC! + var done = false; + var num = 0; + var a = false; + if (!args) { + args = {}; + } + if (!new_item.name) { + new_item = create_new_item(new_item, args.q || 1); + } + if (args.p) { + new_item.p = args.p; + } + if ( + (args.m || args.r) && + ((G.items[new_item.name].upgrade && Math.random() < 1.0 / 500) || + (G.items[new_item.name].compound && Math.random() < 1.0 / 20000)) + ) { + new_item.p = "shiny"; + a = true; + } + if (G.items[new_item.name].s) { + for (var i = 0; i < player.items.length; i++) { + var item = player.items[i]; + if (can_stack(item, new_item)) { + if ((args.v && is_in_pvp(player, 1)) || new_item.v) { + item.v = new Date(); + } + if (new_item.m) { + item.m = new_item.m; + } + item.q = parseInt((new_item.q || 1) + (item.q || 1)); // to fix the side-effects of not including parseInt on 'sell'/'buy' [22/02/18] + player.citems[i] = cache_item(player.items[i]); + done = true; + num = i; + break; + } + } + } + if (!new_item.oo) { + new_item.oo = player.name; + } + if (!done) { + for (var i = 0; i < player.items.length; i++) { + var item = player.items[i]; + if (!item) { + if (args.v && is_in_pvp(player, 1)) { + new_item.v = new Date(); + } + player.items[i] = new_item; + player.citems[i] = cache_item(player.items[i]); + player.esize--; // esize-- needed for concurrent party chest operations [18/10/18] + done = true; + num = i; + break; + } + } + } + if (!done) { + player.items.push(new_item); + player.citems[player.items.length - 1] = cache_item(player.items[player.items.length - 1]); + num = player.items.length - 1; + player.esize--; + } + if ( + args.m && + player.s.mluck && + get_player(player.s.mluck.f) && + Math.random() <= 0.02 && + can_add_item(get_player(player.s.mluck.f), new_item.name) + ) { + var mr = get_player(player.s.mluck.f); + var item = create_new_item(new_item.name); + item.m = player.name; + if (new_item.data) { + item.data = new_item.data; + } // "cxjar" + var num = add_item(mr, item, { m: 1 }); + mr.socket.emit("game_log", { message: "Found " + item_to_phrase(item), color: "#64B867" }); + xy_emit(mr, "ui", { type: "+M", name: mr.name, item: cache_item(item), num: num, cevent: "mluck", event: "mluck" }); + resend(mr, "reopen+nc+inv"); + } + if (args.announce !== false && (a || (a_score[new_item.name] || 0) < G.items[new_item.name].a) && !player.stealth) { + var event = G.items[new_item.name].event; + var e_type = "server_found"; + var e_phrase = " found "; + if (!args.found) { + e_type = "server_received"; + e_phrase = " received "; + } + if (args.phrase) { + e_type = "server_received"; + e_phrase = " " + args.phrase.toLowerCase() + " "; + } + broadcast("server_message", { + message: player.name + e_phrase + item_to_phrase(new_item), + color: "#85C76B", + type: e_type, + item: cache_item(new_item), + name: player.name, + event: event, + }); + a_score[new_item.name] = (a_score[new_item.name] || 0) + 1.05; + } + if (args.log) { + player.socket.emit("game_response", { response: "add_item", item: new_item }); + } + return num; } function list_to_pseudo_items(list, type, add) { - var items = []; - var ex = {}; - if (!type || type == "multi") { - list.forEach(function (el) { - if (G.items[el[1]].s) { - if (ex[el[1]]) { - ex[el[1]].q += max(1, el[0]); - } else { - ex[el[1]] = { name: el[1], q: max(1, el[0]) }; - items.push(ex[el[1]]); - } - } else { - items.push({ name: el[1], q: max(1, el[0]) }); - } - }); - } else if (type == "chest") { - list.forEach(function (name) { - if (G.items[name].s) { - if (ex[name]) { - ex[name].q += 1; - } else { - ex[name] = create_new_item(name); - items.push(ex[name]); - } - } else { - items.push(create_new_item(name)); - } - }); - (add || []).forEach(function (item) { - items.push(item); - }); - } - return items; + var items = []; + var ex = {}; + if (!type || type == "multi") { + list.forEach(function (el) { + if (G.items[el[1]].s) { + if (ex[el[1]]) { + ex[el[1]].q += max(1, el[0]); + } else { + ex[el[1]] = { name: el[1], q: max(1, el[0]) }; + items.push(ex[el[1]]); + } + } else { + items.push({ name: el[1], q: max(1, el[0]) }); + } + }); + } else if (type == "chest") { + list.forEach(function (name) { + if (G.items[name].s) { + if (ex[name]) { + ex[name].q += 1; + } else { + ex[name] = create_new_item(name); + items.push(ex[name]); + } + } else { + items.push(create_new_item(name)); + } + }); + (add || []).forEach(function (item) { + items.push(item); + }); + } + return items; } function bank_add_item(player, slot, new_item) { - var done = false; - if (!new_item.name) { - new_item = create_new_item(new_item); - } - if (new_item.q) { - for (var i = 0; i < player.user[slot].length; i++) { - var item = player.user[slot][i]; - if (can_stack(item, new_item)) { - item.q = (item.q || 1) + (new_item.q || 1); - player.cuser[slot][i] = cache_item(player.user[slot][i]); - done = true; - break; - } - } - } - if (!done) { - for (var i = 0; i < player.user[slot].length; i++) { - var item = player.user[slot][i]; - if (!item) { - player.user[slot][i] = new_item; - player.cuser[slot][i] = cache_item(player.user[slot][i]); - done = true; - break; - } - } - } - if (!done) { - player.user[slot].push(new_item); - player.cuser[slot][player.user[slot].length - 1] = cache_item(player.user[slot][player.user[slot].length - 1]); - } + var done = false; + if (!new_item.name) { + new_item = create_new_item(new_item); + } + if (new_item.q) { + for (var i = 0; i < player.user[slot].length; i++) { + var item = player.user[slot][i]; + if (can_stack(item, new_item)) { + item.q = (item.q || 1) + (new_item.q || 1); + player.cuser[slot][i] = cache_item(player.user[slot][i]); + done = true; + break; + } + } + } + if (!done) { + for (var i = 0; i < player.user[slot].length; i++) { + var item = player.user[slot][i]; + if (!item) { + player.user[slot][i] = new_item; + player.cuser[slot][i] = cache_item(player.user[slot][i]); + done = true; + break; + } + } + } + if (!done) { + player.user[slot].push(new_item); + player.cuser[slot][player.user[slot].length - 1] = cache_item(player.user[slot][player.user[slot].length - 1]); + } } function drop_item_logic(drop, def, pvp) { - var added = false; - if (def[1] == "shells") { - drop.cash += def[2]; - } else if (def[1] == "cxjar" || def[1] == "emotionjar") { - var item = { name: def[1], q: def[2], data: def[3] }; - if (pvp) { - item.v = new Date(); - } - for (var i = 0; i < drop.items.length; i++) { - if (can_stack(drop.items[i], item)) { - drop.items[i].q += def[2]; - added = true; - break; - } - } - if (!added) { - drop.items.push(item); - } - } else if (def[1] == "open") { - chest_exchange(drop, def[2]); - } else { - var item = create_new_item(def[1]); - if (pvp) { - item.v = new Date(); - } - for (var i = 0; i < drop.items.length; i++) { - if (can_stack(drop.items[i], item)) { - drop.items[i].q += 1; - added = true; - break; - } - } - if (!added) { - drop.items.push(item); - } - } + var added = false; + if (def[1] == "shells") { + drop.cash += def[2]; + } else if (def[1] == "cxjar" || def[1] == "emotionjar") { + var item = { name: def[1], q: def[2], data: def[3] }; + if (pvp) { + item.v = new Date(); + } + for (var i = 0; i < drop.items.length; i++) { + if (can_stack(drop.items[i], item)) { + drop.items[i].q += def[2]; + added = true; + break; + } + } + if (!added) { + drop.items.push(item); + } + } else if (def[1] == "open") { + chest_exchange(drop, def[2]); + } else { + var item = create_new_item(def[1]); + if (pvp) { + item.v = new Date(); + } + for (var i = 0; i < drop.items.length; i++) { + if (can_stack(drop.items[i], item)) { + drop.items[i].q += 1; + added = true; + break; + } + } + if (!added) { + drop.items.push(item); + } + } } function drop_one_thing(player, items, args) { - if (!args) { - args = {}; - } - var drop_id = randomStr(30); - var chest = args.chest || "chest1"; - if (!is_array(items)) { - items = [items]; - } - drop = chests[drop_id] = { items: [], cash: 0 }; - for (var i = 0; i < items.length; i++) { - if (is_string(items[i])) { - drop_item_logic(drop, [1, items[i]], is_in_pvp(player, 1)); - } - } - drop.gold = args.gold || 0; - drop.x = (args.x !== undefined && args.x) || player.x; - drop.y = (args.y !== undefined && args.y) || player.y; - drop.map = args.map || player.map; - drop.date = new Date(); - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.items.length, - chest: chest, - id: drop_id, - map: drop.map, - owners: [player.owner], - }); + if (!args) { + args = {}; + } + var drop_id = randomStr(30); + var chest = args.chest || "chest1"; + if (!is_array(items)) { + items = [items]; + } + drop = chests[drop_id] = { items: [], cash: 0 }; + for (var i = 0; i < items.length; i++) { + if (is_string(items[i])) { + drop_item_logic(drop, [1, items[i]], is_in_pvp(player, 1)); + } + } + drop.gold = args.gold || 0; + drop.x = (args.x !== undefined && args.x) || player.x; + drop.y = (args.y !== undefined && args.y) || player.y; + drop.map = args.map || player.map; + drop.date = new Date(); + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + map: drop.map, + owners: [player.owner], + }); } function drop_something(player, monster, share) { - if (monster.pet || monster.trap) { - return; - } - achievement_logic_monster_kill(player, monster); - share = (share === undefined && 1) || share || 0; - // console.log("share: "+share); - var drop_id = randomStr(30); - var drop; - var chest = "chest3"; - var hp_mult = 1; - var drop_norm = 1000; - var global_mult = monster.mult; - var monster_mult = monster.mult; // originally: G.maps[player.map] && G.maps[player.map].drop_norm [31/01/18] - var GOLD = D.monster_gold[monster.type]; - if (B.use_pack_golds && monster.gold) { - GOLD = monster.gold; - } - if (drop_norm) { - hp_mult = monster.max_hp / drop_norm; - } + if (monster.pet || monster.trap) { + return; + } + achievement_logic_monster_kill(player, monster); + share = (share === undefined && 1) || share || 0; + // console.log("share: "+share); + var drop_id = randomStr(30); + var drop; + var chest = "chest3"; + var hp_mult = 1; + var drop_norm = 1000; + var global_mult = monster.mult; + var monster_mult = monster.mult; // originally: G.maps[player.map] && G.maps[player.map].drop_norm [31/01/18] + var GOLD = D.monster_gold[monster.type]; + if (B.use_pack_golds && monster.gold) { + GOLD = monster.gold; + } + if (drop_norm) { + hp_mult = monster.max_hp / drop_norm; + } - drop = chests[drop_id] = { items: [], cash: 0 }; - drop.gold = - round(1 + GOLD * D.drops.gold.base * share + Math.random() * GOLD * D.drops.gold.random * share) * - monster.level * - monster.mult || 0; // previously 0.75 - if (monster.extra_gold) { - drop.egold = (drop.egold || 0) + max(0, monster.extra_gold); - } - if (monster.outgoing) { - drop.egold = - (drop.egold || 0) + - min( - monster.outgoing * B.m_outgoing_gmult, - G.monsters[monster.type].hp * 0.048 * (gameplay == "hardcore" ? 50 : 1), - ); - } - if (drop.egold) { - drop.egold *= share; - } - if (monster.difficulty === 0) { - drop.gold = drop.egold = 0; - } - drop.x = monster.x; - drop.y = monster.y; - drop.map = monster.map; - if (monster["global"]) { - drop.x = player.x; - drop.y = player.y; - drop.map = player.map; - } - // if(player.level<50 && mode.low49_20xglobal) global_mult=20; - Commented out after SpadarFaar discovered/used it [16/04/19] - // console.log(global_mult); - if (monster["1hp"]) { - global_mult *= 1000; - } - if (D.drops.maps.global_static && player.tskin != "konami" && B.global_drops) { - D.drops.maps.global_static.forEach(function (item) { - if (Math.random() / share / player.luckm / monster.luckx / global_mult < item[0] || mode.drop_all) { - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - if (D.drops.maps.global && player.tskin != "konami" && B.global_drops) { - D.drops.maps.global.forEach(function (item) { - if (Math.random() / share / player.luckm / hp_mult / monster.luckx / global_mult < item[0] || mode.drop_all) { - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - if (D.drops.maps[monster.map] && player.tskin != "konami") { - D.drops.maps[monster.map].forEach(function (item) { - if (Math.random() / share / player.luckm / hp_mult / monster.luckx < item[0] || mode.drop_all) { - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - // if(player.level<50 && monster.type=="goo" && mode.low49_200xgoo) monster_mult=200; - if (D.drops.monsters[monster.type] && player.tskin != "konami") { - D.drops.monsters[monster.type].forEach(function (item) { - if ( - ((!monster.temp || item[0] > 0.00001) && - Math.random() / share / player.luckm / monster.level / monster_mult < item[0]) || - mode.drop_all - ) { - // /hp_mult - removed [13/07/18] - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - if (monster.drops) { - monster.drops.forEach(function (item) { - if ( - ((!monster.temp || item[0] > 0.00001) && - Math.random() / share / player.luckm / monster.level / monster_mult < item[0]) || - mode.drop_all - ) { - // /hp_mult - removed [13/07/18] - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - if (player.tskin == "konami") { - D.drops.konami.forEach(function (item) { - if (Math.random() / share / player.luckm / monster.level < item[0] || mode.drop_all) { - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - if (player.p.first && !player.p.first_drop) { - player.p.first_drop = true; - drop.gold += 100000; - drop.items.push(create_new_item("ringsj")); - drop.items.push(create_new_item("ringsj")); - drop.items.push(create_new_item("ringsj")); - drop.items.push(create_new_item("hpbelt")); - drop.items.push(create_new_item("gem0")); - } - if (Math.random() < D.drops.gold.x10) { - drop.gold *= 10; - chest = "chest4"; - } // previously 12 - if (Math.random() < D.drops.gold.x50) { - drop.gold *= 50; - chest = "chest5"; - } // previously 200 - if (drop.items.length || drop.cash) { - chest = "chest6"; - } - drop.date = new Date(); - if (player.party) { - var owners = []; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && !owners.includes(current.owner)) { - owners.push(current.owner); - } - }); - party_emit( - player.party, - "drop", - { - x: drop.x, - y: drop.y, - items: drop.items.length, - chest: chest, - id: drop_id, - party: player.party, - map: drop.map, - owners: owners, - }, - { instance: player.in }, - ); - } else { - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.items.length, - chest: chest, - id: drop_id, - map: drop.map, - owners: [player.owner], - }); - } + drop = chests[drop_id] = { items: [], cash: 0 }; + drop.gold = + round(1 + GOLD * D.drops.gold.base * share + Math.random() * GOLD * D.drops.gold.random * share) * + monster.level * + monster.mult || 0; // previously 0.75 + if (monster.extra_gold) { + drop.egold = (drop.egold || 0) + max(0, monster.extra_gold); + } + if (monster.outgoing) { + drop.egold = + (drop.egold || 0) + + min( + monster.outgoing * B.m_outgoing_gmult, + G.monsters[monster.type].hp * 0.048 * (gameplay == "hardcore" ? 50 : 1), + ); + } + if (drop.egold) { + drop.egold *= share; + } + if (monster.difficulty === 0) { + drop.gold = drop.egold = 0; + } + drop.x = monster.x; + drop.y = monster.y; + drop.map = monster.map; + if (monster["global"]) { + drop.x = player.x; + drop.y = player.y; + drop.map = player.map; + } + // if(player.level<50 && mode.low49_20xglobal) global_mult=20; - Commented out after SpadarFaar discovered/used it [16/04/19] + // console.log(global_mult); + if (monster["1hp"]) { + global_mult *= 1000; + } + if (D.drops.maps.global_static && player.tskin != "konami" && B.global_drops) { + D.drops.maps.global_static.forEach(function (item) { + if (Math.random() / share / player.luckm / monster.luckx / global_mult < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (D.drops.maps.global && player.tskin != "konami" && B.global_drops) { + D.drops.maps.global.forEach(function (item) { + if (Math.random() / share / player.luckm / hp_mult / monster.luckx / global_mult < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (D.drops.maps[monster.map] && player.tskin != "konami") { + D.drops.maps[monster.map].forEach(function (item) { + if (Math.random() / share / player.luckm / hp_mult / monster.luckx < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + // if(player.level<50 && monster.type=="goo" && mode.low49_200xgoo) monster_mult=200; + if (D.drops.monsters[monster.type] && player.tskin != "konami") { + D.drops.monsters[monster.type].forEach(function (item) { + if ( + ((!monster.temp || item[0] > 0.00001) && + Math.random() / share / player.luckm / monster.level / monster_mult < item[0]) || + mode.drop_all + ) { + // /hp_mult - removed [13/07/18] + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (monster.drops) { + monster.drops.forEach(function (item) { + if ( + ((!monster.temp || item[0] > 0.00001) && + Math.random() / share / player.luckm / monster.level / monster_mult < item[0]) || + mode.drop_all + ) { + // /hp_mult - removed [13/07/18] + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (player.tskin == "konami") { + D.drops.konami.forEach(function (item) { + if (Math.random() / share / player.luckm / monster.level < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (player.p.first && !player.p.first_drop) { + player.p.first_drop = true; + drop.gold += 100000; + drop.items.push(create_new_item("ringsj")); + drop.items.push(create_new_item("ringsj")); + drop.items.push(create_new_item("ringsj")); + drop.items.push(create_new_item("hpbelt")); + drop.items.push(create_new_item("gem0")); + } + if (Math.random() < D.drops.gold.x10) { + drop.gold *= 10; + chest = "chest4"; + } // previously 12 + if (Math.random() < D.drops.gold.x50) { + drop.gold *= 50; + chest = "chest5"; + } // previously 200 + if (drop.items.length || drop.cash) { + chest = "chest6"; + } + drop.date = new Date(); + if (player.party) { + var owners = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && !owners.includes(current.owner)) { + owners.push(current.owner); + } + }); + party_emit( + player.party, + "drop", + { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + party: player.party, + map: drop.map, + owners: owners, + }, + { instance: player.in }, + ); + } else { + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + map: drop.map, + owners: [player.owner], + }); + } } function drop_something_hardcore(player, target) { - var drop_id = randomStr(30); - var drop; - drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0 }; - drop.gold = 99; - drop.x = target.x; - drop.y = target.y + 10; - drop.map = target.map; + var drop_id = randomStr(30); + var drop; + drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0 }; + drop.gold = 99; + drop.x = target.x; + drop.y = target.y + 10; + drop.map = target.map; - target.slots.elixir = target.cslots.elixir = null; + target.slots.elixir = target.cslots.elixir = null; - for (var name in target.slots) { - if (target.slots[name] && !target.slots[name].b) { - var prob = 0.1; - if (name == "mainhand" || name == "offhand") { - prob = 0.05; - } - if (Math.random() < prob) { - drop.pvp_items.push(target.slots[name]); - target.slots[name] = target.cslots[name] = null; - } - } - } + for (var name in target.slots) { + if (target.slots[name] && !target.slots[name].b) { + var prob = 0.1; + if (name == "mainhand" || name == "offhand") { + prob = 0.05; + } + if (Math.random() < prob) { + drop.pvp_items.push(target.slots[name]); + target.slots[name] = target.cslots[name] = null; + } + } + } - for (var i = 0; i < 42; i++) { - if (target.items[i]) { - if (Math.random() < 0.2) { - drop.pvp_items.push(target.items[i]); - target.items[i] = target.citems[i] = null; - } else if (Math.random() < 0.05) { - target.items[i] = target.citems[i] = null; - } - } - } + for (var i = 0; i < 42; i++) { + if (target.items[i]) { + if (Math.random() < 0.2) { + drop.pvp_items.push(target.items[i]); + target.items[i] = target.citems[i] = null; + } else if (Math.random() < 0.05) { + target.items[i] = target.citems[i] = null; + } + } + } - drop.date = new Date(); + drop.date = new Date(); - if (!drop.pvp_items.length) { - delete chests[drop_id]; - return; - } + if (!drop.pvp_items.length) { + delete chests[drop_id]; + return; + } - if (player.party) { - var owners = []; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && !owners.includes(current.owner)) { - owners.push(current.owner); - } - }); - party_emit( - player.party, - "drop", - { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - party: player.party, - map: target.map, - owners: owners, - }, - { instance: player.in }, - ); - } else { - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - map: target.map, - owners: [player.owner], - }); - } + if (player.party) { + var owners = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && !owners.includes(current.owner)) { + owners.push(current.owner); + } + }); + party_emit( + player.party, + "drop", + { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + party: player.party, + map: target.map, + owners: owners, + }, + { instance: player.in }, + ); + } else { + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + map: target.map, + owners: [player.owner], + }); + } } function drop_something_pvp(player, target) { - var drop_id = randomStr(30); - var drop; - drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0, pvp: true }; - drop.gold = 0; - drop.x = target.x; - drop.y = target.y + 10; - drop.map = target.map; + var drop_id = randomStr(30); + var drop; + drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0, pvp: true }; + drop.gold = 0; + drop.x = target.x; + drop.y = target.y + 10; + drop.map = target.map; - for (var name in target.slots) { - if (target.slots[name] && target.slots[name].v && !target.slots[name].b) { - var prob = 0.5; - if (name == "mainhand" || name == "offhand") { - prob = 0.3; - } - if (Math.random() < prob) { - drop.pvp_items.push(target.slots[name]); - target.slots[name] = target.cslots[name] = null; - } - } - } + for (var name in target.slots) { + if (target.slots[name] && target.slots[name].v && !target.slots[name].b) { + var prob = 0.5; + if (name == "mainhand" || name == "offhand") { + prob = 0.3; + } + if (Math.random() < prob) { + drop.pvp_items.push(target.slots[name]); + target.slots[name] = target.cslots[name] = null; + } + } + } - for (var i = 0; i < 42; i++) { - if (target.items[i] && target.items[i].v) { - var prob = (target.items[i].q && 0.5) || 0.8; - if (Math.random() < prob) { - drop.pvp_items.push(target.items[i]); - target.items[i] = target.citems[i] = null; - } - // else if(Math.random()<0.05) target.items[i]=target.citems[i]=null; - } - } + for (var i = 0; i < 42; i++) { + if (target.items[i] && target.items[i].v) { + var prob = (target.items[i].q && 0.5) || 0.8; + if (Math.random() < prob) { + drop.pvp_items.push(target.items[i]); + target.items[i] = target.citems[i] = null; + } + // else if(Math.random()<0.05) target.items[i]=target.citems[i]=null; + } + } - drop.date = new Date(); + drop.date = new Date(); - if (!drop.pvp_items.length) { - delete chests[drop_id]; - return; - } + if (!drop.pvp_items.length) { + delete chests[drop_id]; + return; + } - if (player.party) { - var owners = []; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && !owners.includes(current.owner)) { - owners.push(current.owner); - } - }); - party_emit( - player.party, - "drop", - { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - party: player.party, - map: target.map, - owners: owners, - }, - { instance: player.in }, - ); - } else { - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - map: target.map, - owners: [player.owner], - }); - } + if (player.party) { + var owners = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && !owners.includes(current.owner)) { + owners.push(current.owner); + } + }); + party_emit( + player.party, + "drop", + { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + party: player.party, + map: target.map, + owners: owners, + }, + { instance: player.in }, + ); + } else { + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + map: target.map, + owners: [player.owner], + }); + } } function monster_hunt_logic(player, monster) { - var target = monster; - if (!player.s.monsterhunt || player.s.monsterhunt.sn != region + " " + server_name) { - return; - } - if (player.s.monsterhunt.id == monster.type && player.s.monsterhunt.c) { - player.s.monsterhunt.c--; - } - if (target.level == 1 && player.s.monsterhunt.id == target.type && player.s.monsterhunt.dl) { - player.s.monsterhunt.dl = false; - get_monsters(player.s.monsterhunt.id).forEach(function (m) { - if (!player.s.monsterhunt.dl && m.level > 1) { - // && !m.target - it was possible to keep a high level monsters aggroed and abuse the system [21/07/23] - player.s.monsterhunt.dl = true; - level_monster(m, { delevel: true }); - } - }); - } + var target = monster; + if (!player.s.monsterhunt || player.s.monsterhunt.sn != region + " " + server_name) { + return; + } + if (player.s.monsterhunt.id == monster.type && player.s.monsterhunt.c) { + player.s.monsterhunt.c--; + } + if (target.level == 1 && player.s.monsterhunt.id == target.type && player.s.monsterhunt.dl) { + player.s.monsterhunt.dl = false; + get_monsters(player.s.monsterhunt.id).forEach(function (m) { + if (!player.s.monsterhunt.dl && m.level > 1) { + // && !m.target - it was possible to keep a high level monsters aggroed and abuse the system [21/07/23] + player.s.monsterhunt.dl = true; + level_monster(m, { delevel: true }); + } + }); + } } function calculate_monster_score(player, monster, share) { - var score = min(1, monster.mult * 2.2); - var divider = 1; - if (!share) { - share = 0; - } - if (monster.cooperative) { - divider = 2; - } - for (var id in players) { - var current = players[id]; - if (current.id == player.id) { - continue; - } - if (current.owner == player.owner && current.type == "merchant" && simple_distance(current, player) < 600) { - score -= 0.2 / divider; - } - if ( - current.party && - current.party == player.party && - current.type == "merchant" && - simple_distance(current, player) < 600 - ) { - score -= 0.1 / divider; - } else if ( - current.owner == player.owner && - current.party == player.party && - simple_distance(current, player) < 600 && - current.type != "merchant" - ) { - score += 0.3 / divider; - } else if ( - current.owner == player.owner && - current.party && - current.party == player.party && - current.type != "merchant" - ) { - score += 0.3 / divider; - } // originally 0.25 - } - if (simple_distance(player, monster) > 600 && share < 0.01) { - score -= 0.3 / divider; - } - if (player.type == "merchant" && player.party) { - score /= 2; - } - if (score < 0) { - score = 0; - } - if (gameplay == "hardcore") { - score *= 10000; - } - return score; + var score = min(1, monster.mult * 2.2); + var divider = 1; + if (!share) { + share = 0; + } + if (monster.cooperative) { + divider = 2; + } + for (var id in players) { + var current = players[id]; + if (current.id == player.id) { + continue; + } + if (current.owner == player.owner && current.type == "merchant" && simple_distance(current, player) < 600) { + score -= 0.2 / divider; + } + if ( + current.party && + current.party == player.party && + current.type == "merchant" && + simple_distance(current, player) < 600 + ) { + score -= 0.1 / divider; + } else if ( + current.owner == player.owner && + current.party == player.party && + simple_distance(current, player) < 600 && + current.type != "merchant" + ) { + score += 0.3 / divider; + } else if ( + current.owner == player.owner && + current.party && + current.party == player.party && + current.type != "merchant" + ) { + score += 0.3 / divider; + } // originally 0.25 + } + if (simple_distance(player, monster) > 600 && share < 0.01) { + score -= 0.3 / divider; + } + if (player.type == "merchant" && player.party) { + score /= 2; + } + if (score < 0) { + score = 0; + } + if (gameplay == "hardcore") { + score *= 10000; + } + return score; } function issue_monster_awards(monster) { - var total = 0.1; - for (var name in monster.points) { - var current = players[name_to_id[name]]; - if (current) { - // && current.map==monster.map - total += max(0, monster.points[name]); - } - } - for (var name in monster.points) { - var current = players[name_to_id[name]]; - var share = max(0, monster.points[name]) / total; - if (current && share > 0.0025) { - // && current.map==monster.map - if (monster.rbuff && G.conditions[monster.rbuff]) { - current.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; - } - if (monster.cbuff) { - for (var i = 0; i < monster.cbuff.length; i++) { - if (current.level <= monster.cbuff[i][0] && G.conditions[monster.cbuff[i][1]]) { - current.s[monster.cbuff[i][1]] = { ms: G.conditions[monster.cbuff[i][1]].duration }; - break; - } - } - } - if (G.monsters[monster.type]["1hp"]) { - drop_something(current, monster); - } else { - drop_something(current, monster, share); - } - var score = calculate_monster_score(current, monster, share); - current.p.stats.monsters[monster.type] = (current.p.stats.monsters[monster.type] || 0) + 1; - current.p.stats.monsters_diff[monster.type] = (current.p.stats.monsters_diff[monster.type] || 0) + (score - 1); - monster_hunt_logic(current, monster, share); - if (current.type == "merchant") { - continue; - } - current.xp += round(monster.xp * share * current.xpm); - if (current.t) { - current.t.xp += round(monster.xp * share * current.xpm); - } - delete current.s.coop; - resend(current, "u+cid"); - } - } + var total = 0.1; + for (var name in monster.points) { + var current = players[name_to_id[name]]; + if (current) { + // && current.map==monster.map + total += max(0, monster.points[name]); + } + } + for (var name in monster.points) { + var current = players[name_to_id[name]]; + var share = max(0, monster.points[name]) / total; + if (current && share > 0.0025) { + // && current.map==monster.map + if (monster.rbuff && G.conditions[monster.rbuff]) { + current.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; + } + if (monster.cbuff) { + for (var i = 0; i < monster.cbuff.length; i++) { + if (current.level <= monster.cbuff[i][0] && G.conditions[monster.cbuff[i][1]]) { + current.s[monster.cbuff[i][1]] = { ms: G.conditions[monster.cbuff[i][1]].duration }; + break; + } + } + } + if (G.monsters[monster.type]["1hp"]) { + drop_something(current, monster); + } else { + drop_something(current, monster, share); + } + var score = calculate_monster_score(current, monster, share); + current.p.stats.monsters[monster.type] = (current.p.stats.monsters[monster.type] || 0) + 1; + current.p.stats.monsters_diff[monster.type] = (current.p.stats.monsters_diff[monster.type] || 0) + (score - 1); + monster_hunt_logic(current, monster, share); + if (current.type == "merchant") { + continue; + } + current.xp += round(monster.xp * share * current.xpm); + if (current.t) { + current.t.xp += round(monster.xp * share * current.xpm); + } + delete current.s.coop; + resend(current, "u+cid"); + } + } } function issue_monster_award(monster) { - if (monster.cooperative) { - return issue_monster_awards(monster); - } - var player = players[name_to_id[monster.target]]; - if (!player) { - return; - } - // if(gameplay=="test" && player.level<80) player.level+=1; - stats.kills[monster.type]++; - drop_something(player, monster); - if (!player.party) { - if (monster.rbuff && G.conditions[monster.rbuff]) { - player.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; - } - if (monster.cbuff) { - for (var i = 0; i < monster.cbuff.length; i++) { - if (player.level <= monster.cbuff[i][0] && G.conditions[monster.cbuff[i][1]]) { - player.s[monster.cbuff[i][1]] = { ms: G.conditions[monster.cbuff[i][1]].duration }; - break; - } - } - } - var score = calculate_monster_score(player, monster); - player.p.stats.monsters[monster.type] = (player.p.stats.monsters[monster.type] || 0) + 1; - player.p.stats.monsters_diff[monster.type] = (player.p.stats.monsters_diff[monster.type] || 0) + (score - 1); - monster_hunt_logic(player, monster); - if (player.type == "merchant") { - return; - } - player.xp += monster.xp * player.xpm * monster.mult; - if (player.t) { - player.t.xp += monster.xp * player.xpm * monster.mult; - } - player.cid++; - player.u = true; - calculate_player_stats(player); - } else { - var xp = monster.xp; - // xp*=[1,1,0.70,0.5,0.4,0.32,0.26,0.24,0.24,0.24,0.24,0.24][parties[player.party].length]; - // original: [1,1,0.8,0.7,0.65,0.5,0.4,0.3,0.3,0.3,0.3,0.3] - // xp=round(xp); - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - var cxp = round(xp * current.xpm * current.share); - if (monster.rbuff && G.conditions[monster.rbuff]) { - current.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; - } - var score = calculate_monster_score(current, monster); - current.p.stats.monsters[monster.type] = (current.p.stats.monsters[monster.type] || 0) + 1; - current.p.stats.monsters_diff[monster.type] = (current.p.stats.monsters_diff[monster.type] || 0) + (score - 1); - monster_hunt_logic(current, monster); - if (current.type == "merchant") { - return; - } - current.xp += cxp * monster.mult; - if (current.t) { - current.t.xp += cxp * monster.mult; - } - current.cid++; - current.u = true; - calculate_player_stats(current); - if (current != player) { - current.socket.emit("player", player_to_client(current)); - } - // current.socket.emit("game_log",{message:xp+" XP",color:"#416F3A"}); - disappearing_text(current.socket, current, "+" + cxp, { - map: current.map, - color: "#753D8C", - size: "large", - nv: 1, - }); //party:player.party, - better send it to individuals and multiply on client with a setting [19/08/16] - }); - } + if (monster.cooperative) { + return issue_monster_awards(monster); + } + var player = players[name_to_id[monster.target]]; + if (!player) { + return; + } + // if(gameplay=="test" && player.level<80) player.level+=1; + stats.kills[monster.type]++; + drop_something(player, monster); + if (!player.party) { + if (monster.rbuff && G.conditions[monster.rbuff]) { + player.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; + } + if (monster.cbuff) { + for (var i = 0; i < monster.cbuff.length; i++) { + if (player.level <= monster.cbuff[i][0] && G.conditions[monster.cbuff[i][1]]) { + player.s[monster.cbuff[i][1]] = { ms: G.conditions[monster.cbuff[i][1]].duration }; + break; + } + } + } + var score = calculate_monster_score(player, monster); + player.p.stats.monsters[monster.type] = (player.p.stats.monsters[monster.type] || 0) + 1; + player.p.stats.monsters_diff[monster.type] = (player.p.stats.monsters_diff[monster.type] || 0) + (score - 1); + monster_hunt_logic(player, monster); + if (player.type == "merchant") { + return; + } + player.xp += monster.xp * player.xpm * monster.mult; + if (player.t) { + player.t.xp += monster.xp * player.xpm * monster.mult; + } + player.cid++; + player.u = true; + calculate_player_stats(player); + } else { + var xp = monster.xp; + // xp*=[1,1,0.70,0.5,0.4,0.32,0.26,0.24,0.24,0.24,0.24,0.24][parties[player.party].length]; + // original: [1,1,0.8,0.7,0.65,0.5,0.4,0.3,0.3,0.3,0.3,0.3] + // xp=round(xp); + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + var cxp = round(xp * current.xpm * current.share); + if (monster.rbuff && G.conditions[monster.rbuff]) { + current.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; + } + var score = calculate_monster_score(current, monster); + current.p.stats.monsters[monster.type] = (current.p.stats.monsters[monster.type] || 0) + 1; + current.p.stats.monsters_diff[monster.type] = (current.p.stats.monsters_diff[monster.type] || 0) + (score - 1); + monster_hunt_logic(current, monster); + if (current.type == "merchant") { + return; + } + current.xp += cxp * monster.mult; + if (current.t) { + current.t.xp += cxp * monster.mult; + } + current.cid++; + current.u = true; + calculate_player_stats(current); + if (current != player) { + current.socket.emit("player", player_to_client(current)); + } + // current.socket.emit("game_log",{message:xp+" XP",color:"#416F3A"}); + disappearing_text(current.socket, current, "+" + cxp, { + map: current.map, + color: "#753D8C", + size: "large", + nv: 1, + }); //party:player.party, - better send it to individuals and multiply on client with a setting [19/08/16] + }); + } } function kill_monster(attacker, target) { - if (target.dead) { - return; - } // [04/02/23] - var no_decrease = false; - if (attacker) { - if (!attacker.party) { - attacker.socket.emit("game_log", "You " + killed_message(target.type)); - } // tut("killagoo") on game.js - else { - party_emit(attacker.party, "game_log", attacker.name + " " + killed_message(target.type)); - } - if (B.free_last_hits && target.target && target.target != attacker.name) { - stop_pursuit(target, { force: true, cause: "kill_monster call" }); - } - if (!target.target) { - target_player(target, attacker, 1); - no_decrease = true; - } - } - issue_monster_award(target); - remove_monster(target, { no_decrease: no_decrease }); + if (target.dead) { + return; + } // [04/02/23] + var no_decrease = false; + if (attacker) { + if (!attacker.party) { + attacker.socket.emit("game_log", "You " + killed_message(target.type)); + } // tut("killagoo") on game.js + else { + party_emit(attacker.party, "game_log", attacker.name + " " + killed_message(target.type)); + } + if (B.free_last_hits && target.target && target.target != attacker.name) { + stop_pursuit(target, { force: true, cause: "kill_monster call" }); + } + if (!target.target) { + target_player(target, attacker, 1); + no_decrease = true; + } + } + issue_monster_award(target); + remove_monster(target, { no_decrease: no_decrease }); } function player_rip_logic(player) { - if (!player.rip && player.hp <= 0) { - defeat_player(player); - rip(player); - } + if (!player.rip && player.hp <= 0) { + defeat_player(player); + rip(player); + } } function pwn_routine(victor, target) { - issue_player_award(victor, target); - rip(target); - instance_emit(target.in, "server_message", { - message: victor.name + " pwned " + target.name, - color: (victor.team && colors[victor.team]) || "gray", - }); - if (target.map == "arena") { - xy_emit(npcs.pvp, "chat_log", { owner: npcs.pvp.name, message: victor.name + " pwned " + target.name, id: "pvp" }); - } + issue_player_award(victor, target); + rip(target); + instance_emit(target.in, "server_message", { + message: victor.name + " pwned " + target.name, + color: (victor.team && colors[victor.team]) || "gray", + }); + if (target.map == "arena") { + xy_emit(npcs.pvp, "chat_log", { owner: npcs.pvp.name, message: victor.name + " pwned " + target.name, id: "pvp" }); + } } function issue_player_award(attacker, target) { - if (attacker.map == "duelland") { - if (target.duel) { - duel_defeat(target); - } - return; - } - if (attacker.map == "abtesting" && attacker.team != target.team) { - if (E.abtesting) { - E.abtesting[attacker.team]++; - item_achievement_increment(attacker, attacker.slots.orb, "abtesting"); - // instance_emit(attacker.map,"game_event",{name:"ab_score",A:E.abtesting.A,B:E.abtesting.B,win:attacker.name,lose:target.name,color:attacker.team}); - broadcast_e(); - } - return; - } - var lost_xp = floor(min(max((target.max_xp * 0.01) / 10, (target.xp * 0.02) / 10), target.xp)); + if (attacker.map == "duelland") { + if (target.duel) { + duel_defeat(target); + } + return; + } + if (attacker.map == "abtesting" && attacker.team != target.team) { + if (E.abtesting) { + E.abtesting[attacker.team]++; + item_achievement_increment(attacker, attacker.slots.orb, "abtesting"); + // instance_emit(attacker.map,"game_event",{name:"ab_score",A:E.abtesting.A,B:E.abtesting.B,win:attacker.name,lose:target.name,color:attacker.team}); + broadcast_e(); + } + return; + } + var lost_xp = floor(min(max((target.max_xp * 0.01) / 10, (target.xp * 0.02) / 10), target.xp)); - var lost_gold = 100; - var gain_gold = 100; - if (target.level >= 10) { - lost_gold = 1000; - } - if (target.level >= 20) { - lost_gold = 5000; - } - if (target.level >= 30) { - lost_gold = 12500; - } - if (target.level >= 40) { - lost_gold = 25000; - } - if (target.level >= 50) { - lost_gold = 50000; - } - if (target.level >= 55) { - lost_gold = 75000; - } - if (target.level >= 60) { - lost_gold = 125000; - } - if (target.level >= 65) { - lost_gold = 250000; - } - if (target.level >= 70) { - lost_gold = 500000; - } - if (target.level >= 75) { - lost_gold = 1000000; - } + var lost_gold = 100; + var gain_gold = 100; + if (target.level >= 10) { + lost_gold = 1000; + } + if (target.level >= 20) { + lost_gold = 5000; + } + if (target.level >= 30) { + lost_gold = 12500; + } + if (target.level >= 40) { + lost_gold = 25000; + } + if (target.level >= 50) { + lost_gold = 50000; + } + if (target.level >= 55) { + lost_gold = 75000; + } + if (target.level >= 60) { + lost_gold = 125000; + } + if (target.level >= 65) { + lost_gold = 250000; + } + if (target.level >= 70) { + lost_gold = 500000; + } + if (target.level >= 75) { + lost_gold = 1000000; + } - lost_gold = min(lost_gold, max(attacker.gold, 10000) * 4) || 0; - lost_xp = round(min(lost_xp, max(attacker.xp / 15.0, 50000))) || 0; + lost_gold = min(lost_gold, max(attacker.gold, 10000) * 4) || 0; + lost_xp = round(min(lost_xp, max(attacker.xp / 15.0, 50000))) || 0; - if (G.maps[attacker.map].safe_pvp && !is_pvp) { - lost_gold = 0; - lost_xp = 0; - } + if (G.maps[attacker.map].safe_pvp && !is_pvp) { + lost_gold = 0; + lost_xp = 0; + } - if (gameplay == "hardcore") { - lost_gold = round(target.gold * 0.8); - lost_xp = target.xp; - for (var i = 1; i <= B.hlevel_loss; i++) { - if (target.level - i > 0) { - lost_xp += G.levels[target.level - i + ""]; - } - } - target.level = max(1, target.level - B.hlevel_loss); - } + if (gameplay == "hardcore") { + lost_gold = round(target.gold * 0.8); + lost_xp = target.xp; + for (var i = 1; i <= B.hlevel_loss; i++) { + if (target.level - i > 0) { + lost_xp += G.levels[target.level - i + ""]; + } + } + target.level = max(1, target.level - B.hlevel_loss); + } - lost_gold = min(target.gold, lost_gold); - gain_gold = round(lost_gold * 0.9); + lost_gold = min(target.gold, lost_gold); + gain_gold = round(lost_gold * 0.9); - if (target.type == "merchant") { - lost_xp = 0; - } - if (gameplay != "hardcore" && gameplay != "test" && is_same(attacker, target, 1)) { - lost_gold = gain_gold = 0; - lost_xp = 0; - } + if (target.type == "merchant") { + lost_xp = 0; + } + if (gameplay != "hardcore" && gameplay != "test" && is_same(attacker, target, 1)) { + lost_gold = gain_gold = 0; + lost_xp = 0; + } - if (!is_same(attacker, target, 1)) { - attacker.kills++; - } + if (!is_same(attacker, target, 1)) { + attacker.kills++; + } - if (mode.log_pvp) { - appengine_log("pvp", attacker.name + " pwned " + target.name + " For " + lost_gold + " Gold " + lost_xp + " XP"); - } - pwns[pend++ % 200] = [attacker.name, target.name]; + if (mode.log_pvp) { + appengine_log("pvp", attacker.name + " pwned " + target.name + " For " + lost_gold + " Gold " + lost_xp + " XP"); + } + pwns[pend++ % 200] = [attacker.name, target.name]; - target.socket.emit("game_log", { message: "Slain by " + attacker.name, color: "#F12F02" }); - var lost_shells = 0; - var psize = 1; - if (lost_xp) { - for (var i = 0; i < target.isize; i++) { - if (target.items[i] && target.items[i].name == "xptome") { - lost_shells = 2; - lost_xp = floor(lost_xp / 50); - consume_one(target, i); - target.to_reopen = true; - target.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); - break; - } - } - } + target.socket.emit("game_log", { message: "Slain by " + attacker.name, color: "#F12F02" }); + var lost_shells = 0; + var psize = 1; + if (lost_xp) { + for (var i = 0; i < target.isize; i++) { + if (target.items[i] && target.items[i].name == "xptome") { + lost_shells = 2; + lost_xp = floor(lost_xp / 50); + consume_one(target, i); + target.to_reopen = true; + target.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); + break; + } + } + } - target.gold -= lost_gold; - target.xp -= lost_xp; - if (target.xp < 0) { - target.xp = 0; - } - target.socket.emit("game_log", "Lost " + to_pretty_num(lost_gold) + " gold"); - target.socket.emit("game_log", "Lost " + to_pretty_num(lost_xp) + " experience"); - target.socket.emit("disappearing_text", { - message: "-" + lost_xp, - x: target.x, - y: target.y - 30, - args: { color: colors.party_xp }, - }); + target.gold -= lost_gold; + target.xp -= lost_xp; + if (target.xp < 0) { + target.xp = 0; + } + target.socket.emit("game_log", "Lost " + to_pretty_num(lost_gold) + " gold"); + target.socket.emit("game_log", "Lost " + to_pretty_num(lost_xp) + " experience"); + target.socket.emit("disappearing_text", { + message: "-" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); - if (gameplay == "hardcore") { - drop_something_hardcore(attacker, target); - } - if (is_in_pvp(target)) { - drop_something_pvp(attacker, target); - } + if (gameplay == "hardcore") { + drop_something_hardcore(attacker, target); + } + if (is_in_pvp(target)) { + drop_something_pvp(attacker, target); + } - if (!attacker.party) { - if (attacker.type == "merchant") { - lost_xp = 0; - } - attacker.gold += gain_gold; - attacker.xp += round(lost_xp * 0.95); - attacker.socket.emit("game_log", { message: "Pwned " + target.name, color: "#67C051" }); - attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); - attacker.socket.emit("disappearing_text", { - message: "+" + gain_gold, - x: target.x, - y: target.y - 40, - args: { color: "+gold", size: "large" }, - }); - attacker.socket.emit("game_log", "Gained " + to_pretty_num(round(lost_xp * 0.95)) + " experience"); - attacker.socket.emit("disappearing_text", { - message: "+" + lost_xp, - x: target.x, - y: target.y - 30, - args: { color: colors.party_xp }, - }); - if (lost_shells) { - add_shells(attacker, lost_shells, "xptome"); - } - } else { - var name = attacker.name; - lost_xp = floor((lost_xp * 0.92) / parties[attacker.party].length); - gain_gold = floor(gain_gold / parties[attacker.party].length); - if (lost_shells) { - add_shells(attacker, lost_shells, "xptome"); - } - // ,lost_shells=ceil(lost_shells/parties[attacker.party].length) - parties[attacker.party].forEach(function (a_name) { - var attacker = players[name_to_id[a_name]]; - attacker.gold += gain_gold; - if (attacker.type != "merchant") { - attacker.xp += lost_xp; - } - attacker.socket.emit("game_log", { message: name + " pwned " + target.name, color: "#67C051" }); - attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); - attacker.socket.emit("disappearing_text", { - message: "+" + gain_gold, - x: target.x, - y: target.y - 40, - args: { color: "+gold", size: "large" }, - }); - if (attacker.type != "merchant") { - attacker.socket.emit("game_log", "Gained " + to_pretty_num(lost_xp) + " experience"); - attacker.socket.emit("disappearing_text", { - message: "+" + lost_xp, - x: target.x, - y: target.y - 30, - args: { color: colors.party_xp }, - }); - } - }); - } + if (!attacker.party) { + if (attacker.type == "merchant") { + lost_xp = 0; + } + attacker.gold += gain_gold; + attacker.xp += round(lost_xp * 0.95); + attacker.socket.emit("game_log", { message: "Pwned " + target.name, color: "#67C051" }); + attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); + attacker.socket.emit("disappearing_text", { + message: "+" + gain_gold, + x: target.x, + y: target.y - 40, + args: { color: "+gold", size: "large" }, + }); + attacker.socket.emit("game_log", "Gained " + to_pretty_num(round(lost_xp * 0.95)) + " experience"); + attacker.socket.emit("disappearing_text", { + message: "+" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + if (lost_shells) { + add_shells(attacker, lost_shells, "xptome"); + } + } else { + var name = attacker.name; + lost_xp = floor((lost_xp * 0.92) / parties[attacker.party].length); + gain_gold = floor(gain_gold / parties[attacker.party].length); + if (lost_shells) { + add_shells(attacker, lost_shells, "xptome"); + } + // ,lost_shells=ceil(lost_shells/parties[attacker.party].length) + parties[attacker.party].forEach(function (a_name) { + var attacker = players[name_to_id[a_name]]; + attacker.gold += gain_gold; + if (attacker.type != "merchant") { + attacker.xp += lost_xp; + } + attacker.socket.emit("game_log", { message: name + " pwned " + target.name, color: "#67C051" }); + attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); + attacker.socket.emit("disappearing_text", { + message: "+" + gain_gold, + x: target.x, + y: target.y - 40, + args: { color: "+gold", size: "large" }, + }); + if (attacker.type != "merchant") { + attacker.socket.emit("game_log", "Gained " + to_pretty_num(lost_xp) + " experience"); + attacker.socket.emit("disappearing_text", { + message: "+" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + } + }); + } } function commence_attack(attacker, target, atype) { - var attack = attacker.attack; - var mp_cost = 0; - var info = { - apiercing: 0, - damage_type: attacker.damage_type, - heal: false, - lines: true, - positive: false, - first_attack: 0, - procs: false, - conditions: [], - }; // server projectile + var attack = attacker.attack; + var mp_cost = 0; + var info = { + apiercing: 0, + damage_type: attacker.damage_type, + heal: false, + lines: true, + positive: false, + first_attack: 0, + procs: false, + conditions: [], + }; // server projectile - if (!G.skills[atype].hostile) { - info.positive = true; - } + if (!G.skills[atype].hostile) { + info.positive = true; + } - // TARGET CHANGE - if (attacker.is_player) { - attacker.target = target.id; - } + // TARGET CHANGE + if (attacker.is_player) { + attacker.target = target.id; + } - // FAILURE SCENARIOS - if ( - attacker.type == "merchant" && - !G.skills[atype].merchant_use && - !(atype == "attack" && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].wtype == "dartgun") - ) { - attacker.socket.emit("game_response", { response: "attack_failed", id: target.id }); - return { failed: true, reason: "merchant", place: atype, id: target.id }; - } - if ( - mode.pvp_level_gap && - attacker.is_player && - target.is_player && - !info.positive && - abs(attacker.level - target.level) > 10 - ) { - attacker.socket.emit("game_response", { response: "attack_failed", id: target.id, reason: "level" }); - return { failed: true, reason: "level_gap", place: atype, id: target.id }; - } + // FAILURE SCENARIOS + if ( + attacker.type == "merchant" && + !G.skills[atype].merchant_use && + !(atype == "attack" && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].wtype == "dartgun") + ) { + attacker.socket.emit("game_response", { response: "attack_failed", id: target.id }); + return { failed: true, reason: "merchant", place: atype, id: target.id }; + } + if ( + mode.pvp_level_gap && + attacker.is_player && + target.is_player && + !info.positive && + abs(attacker.level - target.level) > 10 + ) { + attacker.socket.emit("game_response", { response: "attack_failed", id: target.id, reason: "level" }); + return { failed: true, reason: "level_gap", place: atype, id: target.id }; + } - var dist = distance(attacker, target); - var range = 0; - var def = { hid: attacker.id, source: atype, projectile: null }; + var dist = distance(attacker, target); + var range = 0; + var def = { hid: attacker.id, source: atype, projectile: null }; - // PROJECTILE LOGIC - if (attacker.is_monster) { - def.projectile = G.monsters[attacker.type].projectile || "stone"; - } - if (attacker.is_player && G.classes[attacker.type].projectile) { - def.projectile = G.classes[attacker.type].projectile; - } - if (attacker.projectile) { - def.projectile = attacker.projectile; - } - if (attacker.slots && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].projectile) { - def.projectile = G.items[attacker.slots.mainhand.name].projectile; - } - if (attacker.tskin == "konami") { - def.projectile = "stone_k"; - } - if ((atype != "attack" || !def.projectile || atype == "heal") && G.skills[atype].projectile) { - def.projectile = G.skills[atype].projectile; - } + // PROJECTILE LOGIC + if (attacker.is_monster) { + def.projectile = G.monsters[attacker.type].projectile || "stone"; + } + if (attacker.is_player && G.classes[attacker.type].projectile) { + def.projectile = G.classes[attacker.type].projectile; + } + if (attacker.projectile) { + def.projectile = attacker.projectile; + } + if (attacker.slots && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].projectile) { + def.projectile = G.items[attacker.slots.mainhand.name].projectile; + } + if (attacker.tskin == "konami") { + def.projectile = "stone_k"; + } + if ((atype != "attack" || !def.projectile || atype == "heal") && G.skills[atype].projectile) { + def.projectile = G.skills[atype].projectile; + } - // DAMAGE TYPE LOGIC - if (attacker.is_monster && G.monsters[attacker.type].damage_type) { - info.damage_type = G.monsters[attacker.type].damage_type; - } - if (attacker.is_player && G.classes[attacker.type].damage_type) { - info.damage_type = G.classes[attacker.type].damage_type; - } - if (attacker.is_player && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].damage_type) { - info.damage_type = G.items[attacker.slots.mainhand.name].damage_type; - } - if (atype != "attack" && G.skills[atype].damage_type) { - info.damage_type = G.skills[atype].damage_type; - } + // DAMAGE TYPE LOGIC + if (attacker.is_monster && G.monsters[attacker.type].damage_type) { + info.damage_type = G.monsters[attacker.type].damage_type; + } + if (attacker.is_player && G.classes[attacker.type].damage_type) { + info.damage_type = G.classes[attacker.type].damage_type; + } + if (attacker.is_player && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].damage_type) { + info.damage_type = G.items[attacker.slots.mainhand.name].damage_type; + } + if (atype != "attack" && G.skills[atype].damage_type) { + info.damage_type = G.skills[atype].damage_type; + } - // PROCS - if (!attacker.is_player || G.skills[atype].procs) { - info.procs = true; - } + // PROCS + if (!attacker.is_player || G.skills[atype].procs) { + info.procs = true; + } - // HEAL / POSITIVE - if ( - G.skills[atype].heal || - (attacker.is_player && attacker.slots.mainhand && attacker.slots.mainhand.name == "cupid") - ) { - info.heal = true; - info.positive = true; - } + // HEAL / POSITIVE + if ( + G.skills[atype].heal || + (attacker.is_player && attacker.slots.mainhand && attacker.slots.mainhand.name == "cupid") + ) { + info.heal = true; + info.positive = true; + } - if ((attacker.is_player && target.is_player && !is_in_pvp(target, true) && !info.positive) || target.npc) { - attacker.socket.emit("game_response", { response: "attack_failed", id: target.id }); - return { failed: true, reason: "no_pvp", place: atype, id: target.id }; - } + if ((attacker.is_player && target.is_player && !is_in_pvp(target, true) && !info.positive) || target.npc) { + attacker.socket.emit("game_response", { response: "attack_failed", id: target.id }); + return { failed: true, reason: "no_pvp", place: atype, id: target.id }; + } - // COMMON RANGE CHECKS - if (G.skills[atype].use_range) { - range = attacker.range; - } - if (G.skills[atype].range) { - range = G.skills[atype].range; - } + // COMMON RANGE CHECKS + if (G.skills[atype].use_range) { + range = attacker.range; + } + if (G.skills[atype].range) { + range = G.skills[atype].range; + } - // DAMAGE - if (G.skills[atype].damage) { - attack = G.skills[atype].damage; - } + // DAMAGE + if (G.skills[atype].damage) { + attack = G.skills[atype].damage; + } - // SKILL MP - if (G.skills[atype].mp) { - mp_cost = G.skills[atype].mp; - } + // SKILL MP + if (G.skills[atype].mp) { + mp_cost = G.skills[atype].mp; + } - if (attacker.is_player && range && !attacker.is_npc) { - if (dist > range + attacker.xrange) { - attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); - return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; - } - if (dist > range) { - attacker.xrange += range - dist; - } - } + if (attacker.is_player && range && !attacker.is_npc) { + if (dist > range + attacker.xrange) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + if (dist > range) { + attacker.xrange += range - dist; + } + } - if (info.procs && attacker.s.poisonous) { - info.conditions.push("poisoned"); - } + if (info.procs && attacker.s.poisonous) { + info.conditions.push("poisoned"); + } - if (atype == "heal") { - attack = attacker.heal || attacker.attack; - mp_cost = attacker.mp_cost; - } else if ( - attacker.is_player && - (atype == "attack" || atype == "3shot" || atype == "5shot" || atype == "cleave" || atype == "shadowstrike") - ) { - var mp_mult = 1; - var att_mult = 1; - if (atype == "3shot") { - mp_mult = 0; - att_mult = 0.7; - } - if (atype == "5shot") { - mp_mult = 0; - att_mult = 0.5; - } - if (atype == "cleave") { - mp_mult = 0.02; - info.lines = false; - att_mult = 0.1 + Math.random() * 0.8; - def.aoe = true; - attacker.first = true; - } - if (atype == "shadowstrike") { - mp_mult = 0; - info.lines = false; - att_mult = (0.2 + Math.random() * 1.2) * ((Math.random() < 0.05 && 12) || 1); - def.aoe = true; - } - if (attacker.mp < attacker.mp_cost * mp_mult) { - attacker.socket.emit("game_response", { response: "no_mp" }); - return { failed: true, reason: "no_mp", place: atype, id: target.id }; - } - mp_cost = parseInt(attacker.mp_cost * mp_mult); - attack = attacker.attack * att_mult; - } else if (atype == "piercingshot") { - info.apiercing = 500; - attack = attacker.attack * 0.75; - } else if (atype == "partyheal") { - if (attacker.level >= 80) { - attack = 800; - } else if (attacker.level >= 72) { - attack = 720; - } else if (attacker.level >= 60) { - attack = 600; - } else { - attack = 400; - } - } else if (atype == "selfheal") { - if (attacker.level >= 80) { - attack = 800; - } else if (attacker.level >= 72) { - attack = 720; - } else if (attacker.level >= 60) { - attack = 600; - } else { - attack = 400; - } - } else if (atype == "taunt") { - if ( - target.is_monster && - target.target && - get_player(target.target) && - is_same(attacker, get_player(target.target), 1) - ) { - stop_pursuit(target, { redirect: true, cause: "taunt redirect" }); - target_player(target, attacker); - } - } else if (atype == "curse") { - if (distance(attacker, target) > min(200, attacker.range * 5 + 20)) { - attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); - return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; - } - attack = 0; - info.conditions.push("cursed"); - } else if (atype == "burst") { - if (attacker.mp < 1) { - attacker.socket.emit("game_response", { response: "no_mp" }); - return { failed: true, reason: "no_mp", place: atype, id: target.id }; - } - attack = attacker.mp * G.skills.burst.ratio; - mp_cost = attacker.mp; - } else if (atype == "cburst") { - var mp_cutoff = attacker.next_mp; - var mp = attacker.next_mp; - if (atype == "cburst") { - mp = mp_cutoff = attacker.next_mp; - } - if (attacker.mp < mp_cutoff) { - attacker.socket.emit("game_response", { response: "no_mp" }); - return { failed: true, reason: "no_mp", place: atype, id: target.id }; - } - attack = mp * G.skills.cburst.ratio; - mp_cost = mp; - attacker.first = true; - } else if (atype == "purify") { - def.purify = true; - } else if (atype == "frostball") { - info.conditions.push("frozen"); - } else if (atype == "fireball") { - info.conditions.push("burned"); - } else if (atype == "supershot") { - if (attacker.in != target.in || distance(attacker, target, true) > 3 * attacker.range + 20) { - attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); - return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; - } - attack = attacker.attack * 1.5; - // if(attacker.slots.mainhand && attacker.slots.mainhand.name=="cupid") info.heal=info.positive=true,def.projectile=G.items.cupid.projectile; - } else if (atype == "snowball") { - if (target.is_monster && attacker.a.freeze && attacker.a.freeze.attr0) { - attack += 10 * attacker.a.freeze.attr0 * attacker.a.freeze.attr0; - } - if (target.is_monster || is_in_pvp(attacker, true)) { - info.conditions.push("frozen"); - } - } else if (atype == "quickpunch" || atype == "quickstab" || atype == "smash") { - attack = attacker.attack * G.skills[atype].damage_multiplier; - } else if (atype == "mentalburst") { - if ( - attacker.in != target.in || - distance(attacker, target, true) > - attacker.range * G.skills.mentalburst.range_multiplier + G.skills.mentalburst.range_bonus - ) { - attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); - return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; - } - attack = attacker.attack * G.skills[atype].damage_multiplier; - } else if (atype == "poisonarrow") { - info.conditions.push("poisoned"); - } else if (attacker.is_monster) { - var rng = parseInt(Math.random() * 100 - 50); - if (attacker.s.poisonous) { - info.conditions.push("poisoned"); - } - attacker.last.attack = future_ms(rng); - } + if (atype == "heal") { + attack = attacker.heal || attacker.attack; + mp_cost = attacker.mp_cost; + } else if ( + attacker.is_player && + (atype == "attack" || atype == "3shot" || atype == "5shot" || atype == "cleave" || atype == "shadowstrike") + ) { + var mp_mult = 1; + var att_mult = 1; + if (atype == "3shot") { + mp_mult = 0; + att_mult = 0.7; + } + if (atype == "5shot") { + mp_mult = 0; + att_mult = 0.5; + } + if (atype == "cleave") { + mp_mult = 0.02; + info.lines = false; + att_mult = 0.1 + Math.random() * 0.8; + def.aoe = true; + attacker.first = true; + } + if (atype == "shadowstrike") { + mp_mult = 0; + info.lines = false; + att_mult = (0.2 + Math.random() * 1.2) * ((Math.random() < 0.05 && 12) || 1); + def.aoe = true; + } + if (attacker.mp < attacker.mp_cost * mp_mult) { + attacker.socket.emit("game_response", { response: "no_mp" }); + return { failed: true, reason: "no_mp", place: atype, id: target.id }; + } + mp_cost = parseInt(attacker.mp_cost * mp_mult); + attack = attacker.attack * att_mult; + } else if (atype == "piercingshot") { + info.apiercing = 500; + attack = attacker.attack * 0.75; + } else if (atype == "partyheal") { + if (attacker.level >= 80) { + attack = 800; + } else if (attacker.level >= 72) { + attack = 720; + } else if (attacker.level >= 60) { + attack = 600; + } else { + attack = 400; + } + } else if (atype == "selfheal") { + if (attacker.level >= 80) { + attack = 800; + } else if (attacker.level >= 72) { + attack = 720; + } else if (attacker.level >= 60) { + attack = 600; + } else { + attack = 400; + } + } else if (atype == "taunt") { + if ( + target.is_monster && + target.target && + get_player(target.target) && + is_same(attacker, get_player(target.target), 1) + ) { + stop_pursuit(target, { redirect: true, cause: "taunt redirect" }); + target_player(target, attacker); + } + } else if (atype == "curse") { + if (distance(attacker, target) > min(200, attacker.range * 5 + 20)) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + attack = 0; + info.conditions.push("cursed"); + } else if (atype == "burst") { + if (attacker.mp < 1) { + attacker.socket.emit("game_response", { response: "no_mp" }); + return { failed: true, reason: "no_mp", place: atype, id: target.id }; + } + attack = attacker.mp * G.skills.burst.ratio; + mp_cost = attacker.mp; + } else if (atype == "cburst") { + var mp_cutoff = attacker.next_mp; + var mp = attacker.next_mp; + if (atype == "cburst") { + mp = mp_cutoff = attacker.next_mp; + } + if (attacker.mp < mp_cutoff) { + attacker.socket.emit("game_response", { response: "no_mp" }); + return { failed: true, reason: "no_mp", place: atype, id: target.id }; + } + attack = mp * G.skills.cburst.ratio; + mp_cost = mp; + attacker.first = true; + } else if (atype == "purify") { + def.purify = true; + } else if (atype == "frostball") { + info.conditions.push("frozen"); + } else if (atype == "fireball") { + info.conditions.push("burned"); + } else if (atype == "supershot") { + if (attacker.in != target.in || distance(attacker, target, true) > 3 * attacker.range + 20) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + attack = attacker.attack * 1.5; + // if(attacker.slots.mainhand && attacker.slots.mainhand.name=="cupid") info.heal=info.positive=true,def.projectile=G.items.cupid.projectile; + } else if (atype == "snowball") { + if (target.is_monster && attacker.a.freeze && attacker.a.freeze.attr0) { + attack += 10 * attacker.a.freeze.attr0 * attacker.a.freeze.attr0; + } + if (target.is_monster || is_in_pvp(attacker, true)) { + info.conditions.push("frozen"); + } + } else if (atype == "quickpunch" || atype == "quickstab" || atype == "smash") { + attack = attacker.attack * G.skills[atype].damage_multiplier; + } else if (atype == "mentalburst") { + if ( + attacker.in != target.in || + distance(attacker, target, true) > + attacker.range * G.skills.mentalburst.range_multiplier + G.skills.mentalburst.range_bonus + ) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + attack = attacker.attack * G.skills[atype].damage_multiplier; + } else if (atype == "poisonarrow") { + info.conditions.push("poisoned"); + } else if (attacker.is_monster) { + var rng = parseInt(Math.random() * 100 - 50); + if (attacker.s.poisonous) { + info.conditions.push("poisoned"); + } + attacker.last.attack = future_ms(rng); + } - if (atype != "attack" && target.immune && (!G.skills[atype] || !G.skills[atype].pierces_immunity)) { - disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: attacker.id }); - return { failed: true, reason: "skill_immune", place: atype, id: target.id }; - } + if (atype != "attack" && target.immune && (!G.skills[atype] || !G.skills[atype].pierces_immunity)) { + disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: attacker.id }); + return { failed: true, reason: "skill_immune", place: atype, id: target.id }; + } - if ( - (!info.positive && - ((attacker.is_player && G.maps[attacker.map].safe) || - (!mode.friendly_fire && - (!attacker.team || attacker.team != target.team) && - ((attacker.party && attacker.party == target.party) || - (attacker.guild && attacker.guild == target.guild))))) || - (attacker.map == "duelland" && (!attacker.duel || !target.duel)) - ) { - attacker.socket.emit("game_response", { response: "friendly", id: target.id }); - return { failed: true, reason: "friendly", place: atype, id: target.id }; - } + if ( + (!info.positive && + ((attacker.is_player && G.maps[attacker.map].safe) || + (!mode.friendly_fire && + (!attacker.team || attacker.team != target.team) && + ((attacker.party && attacker.party == target.party) || + (attacker.guild && attacker.guild == target.guild))))) || + (attacker.map == "duelland" && (!attacker.duel || !target.duel)) + ) { + attacker.socket.emit("game_response", { response: "friendly", id: target.id }); + return { failed: true, reason: "friendly", place: atype, id: target.id }; + } - direction_logic(attacker, target); + direction_logic(attacker, target); - if (mp_cost && attacker.first && !attacker.is_npc) { - consume_mp(attacker, mp_cost, atype != "attack" && atype != "heal" && target); - } - attacker.first = false; + if (mp_cost && attacker.first && !attacker.is_npc) { + consume_mp(attacker, mp_cost, atype != "attack" && atype != "heal" && target); + } + attacker.first = false; - if (attacker.is_player) { - attacker.c = {}; - attacker.to_resend = "u+cid"; - if (attacker.p && attacker.p.stand) { - attacker.p.stand = false; - } - step_out_of_invis(attacker); - if (attacker.type == "merchant") { - var gold = parseInt(attack * 2); - attack = parseInt(min(attack, attacker.gold / 2)); - attacker.gold = max(0, attacker.gold - gold); - attacker.to_resend += "+reopen"; - } - } + if (attacker.is_player) { + attacker.c = {}; + attacker.to_resend = "u+cid"; + if (attacker.p && attacker.p.stand) { + attacker.p.stand = false; + } + step_out_of_invis(attacker); + if (attacker.type == "merchant") { + var gold = parseInt(attack * 2); + attack = parseInt(min(attack, attacker.gold / 2)); + attacker.gold = max(0, attacker.gold - gold); + attacker.to_resend += "+reopen"; + } + } - if (info.procs) { - ["crit", "critdamage", "explosion", "blast", "lifesteal", "manasteal"].forEach(function (p) { - if (attacker[p]) { - info[p] = attacker[p]; - } - }); - } - ["apiercing", "rpiercing", "miss"].forEach(function (p) { - if (attacker[p]) { - info[p] = attacker[p]; - } - }); - if ( - info.procs && - attacker.a.freeze && - Math.random() < (attacker.a.freeze.attr0 * (G.maps[attacker.map].freeze_multiplier || 1)) / 100.0 - ) { - info.conditions.push("frozen"); - } - if ( - info.procs && - attacker.a.burn && - info.procs && - Math.random() < (attacker.a.burn.attr0 * (G.maps[attacker.map].burn_multiplier || 1)) / 100.0 - ) { - info.conditions.push("burned"); - } - if (info.procs && attacker.a.weave && info.procs) { - info.conditions.push("woven"); - } - if (info.procs && attacker.stun && Math.random() < attacker.stun / 100.0 && info.damage_type == "physical") { - info.conditions.push("stunned"); - } + if (info.procs) { + ["crit", "critdamage", "explosion", "blast", "lifesteal", "manasteal"].forEach(function (p) { + if (attacker[p]) { + info[p] = attacker[p]; + } + }); + } + ["apiercing", "rpiercing", "miss"].forEach(function (p) { + if (attacker[p]) { + info[p] = attacker[p]; + } + }); + if ( + info.procs && + attacker.a.freeze && + Math.random() < (attacker.a.freeze.attr0 * (G.maps[attacker.map].freeze_multiplier || 1)) / 100.0 + ) { + info.conditions.push("frozen"); + } + if ( + info.procs && + attacker.a.burn && + info.procs && + Math.random() < (attacker.a.burn.attr0 * (G.maps[attacker.map].burn_multiplier || 1)) / 100.0 + ) { + info.conditions.push("burned"); + } + if (info.procs && attacker.a.weave && info.procs) { + info.conditions.push("woven"); + } + if (info.procs && attacker.stun && Math.random() < attacker.stun / 100.0 && info.damage_type == "physical") { + info.conditions.push("stunned"); + } - var pid = randomStr(6); - var eta = 0; - info.first_attack = info.attack = attack; - info.def = def; - def.damage_type = info.damage_type; - def.pid = pid; - info.attacker = attacker; - info.target = target; - info.atype = atype; - projectiles[pid] = info; + var pid = randomStr(6); + var eta = 0; + info.first_attack = info.attack = attack; + info.def = def; + def.damage_type = info.damage_type; + def.pid = pid; + info.attacker = attacker; + info.target = target; + info.atype = atype; + projectiles[pid] = info; - var action = { - attacker: attacker.id, - target: target.id, - type: atype, - source: atype, - x: target.x, - y: target.y, - eta: 400, - m: target.m, - pid: pid, - }; + var action = { + attacker: attacker.id, + target: target.id, + type: atype, + source: atype, + x: target.x, + y: target.y, + eta: 400, + m: target.m, + pid: pid, + }; - if (def.projectile) { - action.projectile = def.projectile; - if (target.is_monster && G.monsters[target.type].escapist) { - var dampened = false; - for (var id in instances[target.in].monsters) { - var m = instances[target.in].monsters[id]; - if (m.type == "fieldgen0" && point_distance(target.x, target.y, m.x, m.y) < 300) { - target.s.dampened = { ms: 2000 }; - add_condition(target, "dampened", { ms: 2000 }); - dampened = true; - } - } - if (!dampened) { - port_monster(target, random_place(target.map)); - } - } - } + if (def.projectile) { + action.projectile = def.projectile; + if (target.is_monster && G.monsters[target.type].escapist) { + var dampened = false; + for (var id in instances[target.in].monsters) { + var m = instances[target.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(target.x, target.y, m.x, m.y) < 300) { + target.s.dampened = { ms: 2000 }; + add_condition(target, "dampened", { ms: 2000 }); + dampened = true; + } + } + if (!dampened) { + port_monster(target, random_place(target.map)); + } + } + } - if (!(G.projectiles[def.projectile] && G.projectiles[def.projectile].instant)) { - eta = (1000 * dist) / G.projectiles[def.projectile].speed; - } else { - action.instant = true; - } + if (!(G.projectiles[def.projectile] && G.projectiles[def.projectile].instant)) { + eta = (1000 * dist) / G.projectiles[def.projectile].speed; + } else { + action.instant = true; + } - info.eta = future_ms(eta); + info.eta = future_ms(eta); - if (info.heal) { - action.heal = attack; - } else if (attack) { - action.damage = attack; - } - if (info.positive) { - action.positive = true; - } - if (info.conditions.length) { - action.conditions = info.conditions; - } - info.action = action; - xy_emit(attacker, "action", action, target.id); + if (info.heal) { + action.heal = attack; + } else if (attack) { + action.damage = attack; + } + if (info.positive) { + action.positive = true; + } + if (info.conditions.length) { + action.conditions = info.conditions; + } + info.action = action; + xy_emit(attacker, "action", action, target.id); - if (!eta) { - projectiles_loop(); - } + if (!eta) { + projectiles_loop(); + } - action.response = "data"; - action.place = atype; + action.response = "data"; + action.place = atype; - return action; + return action; } function complete_attack(attacker, target, info) { - var defense = "armor"; - var pierce = "apiercing"; - var combo = 1; - var combo_m = 1; - var atype = info.atype; - var evade = false; - var first = true; - var attack = info.attack; - var o_attack; - var i_attack = info.attack; - var def = info.def; - if (G.monsters[attacker.type] && G.monsters[attacker.type].good) { - info.heal = info.positive = true; - } - var change = false; - var targets = [[target, "normal"]]; - var otarget = target; - var events = []; - info.action.map = attacker.map; - info.action.in = attacker.in; + var defense = "armor"; + var pierce = "apiercing"; + var combo = 1; + var combo_m = 1; + var atype = info.atype; + var evade = false; + var first = true; + var attack = info.attack; + var o_attack; + var i_attack = info.attack; + var def = info.def; + if (G.monsters[attacker.type] && G.monsters[attacker.type].good) { + info.heal = info.positive = true; + } + var change = false; + var targets = [[target, "normal"]]; + var otarget = target; + var events = []; + info.action.map = attacker.map; + info.action.in = attacker.in; - if (info.damage_type == "pure" || target === attacker) { - defense = "none_existent"; - pierce = "non_existent"; - info.apiercing = 0; - } else if (info.damage_type == "magical") { - defense = "resistance"; - pierce = "rpiercing"; - info.apiercing = 0; - } else { - info.damage_type = "physical"; - } + if (info.damage_type == "pure" || target === attacker) { + defense = "none_existent"; + pierce = "non_existent"; + info.apiercing = 0; + } else if (info.damage_type == "magical") { + defense = "resistance"; + pierce = "rpiercing"; + info.apiercing = 0; + } else { + info.damage_type = "physical"; + } - if (target.is_player && attack > 0 && !info.heal) { - add_pdps(target, attacker, attack * B.dps_tank_mult); // "tank" - if (attacker.cooperative) { - add_coop_points(attacker, target, attack * B.dps_tank_mult); - } - } - if (attacker.is_monster && attack > 0 && !info.heal) { - attacker.outgoing += min(target.hp, attack); - } + if (target.is_player && attack > 0 && !info.heal) { + add_pdps(target, attacker, attack * B.dps_tank_mult); // "tank" + if (attacker.cooperative) { + add_coop_points(attacker, target, attack * B.dps_tank_mult); + } + } + if (attacker.is_monster && attack > 0 && !info.heal) { + attacker.outgoing += min(target.hp, attack); + } - if ( - target.reflection && - defense == "resistance" && - Math.random() * 100 < target.reflection && - !info.heal && - attacker != target - ) { - var pid = randomStr(6); - var eta = 0; - var opid = info.action.pid; - // info.attack=ceil(attack*damage_multiplier(attacker.resistance||0))||1; - info.attack = ceil(attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); // A pure reflection was requested [29/03/22] - if (!info.action.instant) { - eta = (1000 * distance(target, attacker, true)) / G.projectiles[info.action.projectile].speed; - } - info.target = attacker; - info.attacker = target; - info.eta = future_ms(eta); - projectiles[pid] = info; - info.action.pid = pid; - info.action.target = attacker.id; - info.action.attacker = target.id; - info.action.x = attacker.x; - info.action.y = attacker.y; - info.action.reflect = info.attack; - info.action.m = attacker.m; + if ( + target.reflection && + defense == "resistance" && + Math.random() * 100 < target.reflection && + !info.heal && + attacker != target + ) { + var pid = randomStr(6); + var eta = 0; + var opid = info.action.pid; + // info.attack=ceil(attack*damage_multiplier(attacker.resistance||0))||1; + info.attack = ceil(attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); // A pure reflection was requested [29/03/22] + if (!info.action.instant) { + eta = (1000 * distance(target, attacker, true)) / G.projectiles[info.action.projectile].speed; + } + info.target = attacker; + info.attacker = target; + info.eta = future_ms(eta); + projectiles[pid] = info; + info.action.pid = pid; + info.action.target = attacker.id; + info.action.attacker = target.id; + info.action.x = attacker.x; + info.action.y = attacker.y; + info.action.reflect = info.attack; + info.action.m = attacker.m; - if ((attacker.is_player && target.is_monster) || info.reflections) { - info.reflections = (info.reflections || 0) + 1; - } - if (info.reflections >= 4 && attacker.is_player) { - item_achievement_increment(attacker, attacker.slots.chest, "reflector"); - } - if (info.reflections >= 4 && target.is_player) { - item_achievement_increment(target, target.slots.chest, "reflector"); - } + if ((attacker.is_player && target.is_monster) || info.reflections) { + info.reflections = (info.reflections || 0) + 1; + } + if (info.reflections >= 4 && attacker.is_player) { + item_achievement_increment(attacker, attacker.slots.chest, "reflector"); + } + if (info.reflections >= 4 && target.is_player) { + item_achievement_increment(target, target.slots.chest, "reflector"); + } - xy_emit( - target, - "hit", - { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, reflect: info.attack }, - attacker.id, - ); - return xy_emit(target, "action", info.action, attacker.id); - } + xy_emit( + target, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, reflect: info.attack }, + attacker.id, + ); + return xy_emit(target, "action", info.action, attacker.id); + } - if (attacker == target && !target.dead) { - } // reflect after dead fix [04/02/23] - else if (target.evasion && defense == "armor" && Math.random() * 100 < target.evasion) { - return xy_emit( - info.action, - "hit", - { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, evade: true }, - attacker.id, - ); - } else if ( - target.dc || - target.dead || - (attacker.miss && Math.random() * 100 < attacker.miss) || - (target.avoidance && Math.random() * 100 < target.avoidance) - ) { - return xy_emit( - info.action, - "hit", - { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, - attacker.id, - ); - } else if ( - target.m != info.action.m || - point_distance(target.x, target.y, info.action.x, info.action.y) > 72 * ((info.heal && 1.5) || 1) - ) { - return xy_emit( - info.action, - "hit", - { - pid: def.pid, - hid: attacker.id, - id: target.id, - damage: 0, - avoid: true, - x: info.action.x, - y: info.action.y, - map: info.attacker.map, - in: info.attacker.in, - }, - attacker.id, - ); - } + if (attacker == target && !target.dead) { + } // reflect after dead fix [04/02/23] + else if (target.evasion && defense == "armor" && Math.random() * 100 < target.evasion) { + return xy_emit( + info.action, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, evade: true }, + attacker.id, + ); + } else if ( + target.dc || + target.dead || + (attacker.miss && Math.random() * 100 < attacker.miss) || + (target.avoidance && Math.random() * 100 < target.avoidance) + ) { + return xy_emit( + info.action, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, + attacker.id, + ); + } else if ( + target.m != info.action.m || + point_distance(target.x, target.y, info.action.x, info.action.y) > 72 * ((info.heal && 1.5) || 1) + ) { + return xy_emit( + info.action, + "hit", + { + pid: def.pid, + hid: attacker.id, + id: target.id, + damage: 0, + avoid: true, + x: info.action.x, + y: info.action.y, + map: info.attacker.map, + in: info.attacker.in, + }, + attacker.id, + ); + } - if (info.positive || !info.procs) { - combo = 0; - } - if (combo && target.targets > 3) { - if (!target.last_combo || ssince(target.last_combo) > 5) { - target.combo = 1; - } - combo += target.combo; - target.last_combo = new Date(); - target.combo += 1; - def.mobbing = target.combo; - } - if ( - combo && - target.is_player && - instances[target.in].pmap[target.last_hash] && - Object.keys(instances[target.in].pmap[target.last_hash]).length > 1 - ) { - var combo_check = false; - for (var id in instances[target.in].pmap[target.last_hash]) { - var ntarget = instances[target.in].pmap[target.last_hash][id]; - if ( - target.id == ntarget.id || - is_invinc(ntarget) || - (!is_same(target, ntarget, 2) && !is_in_pvp(target)) || - attacker.id == ntarget.id - ) { - continue; - } //is_same(attacker,ntarget,1) - //!is_same(target,ntarget,1) && !is_in_pvp(target) to prevent people killing other people in non-pvp servers - //!is_same(target,ntarget,2) makes people kill others in coop fights - combo_check = true; - break; - } - if (combo_check) { - targets = []; - combo = 0; - for (var id in instances[target.in].pmap[target.last_hash]) { - var ntarget = instances[target.in].pmap[target.last_hash][id]; - if (is_invinc(ntarget) || attacker.id == ntarget.id || !is_same(target, ntarget, 2)) { - continue; - } //is_same(attacker,ntarget,1) - targets.push([ntarget, "stack"]); - if (!ntarget.last_combo || ssince(ntarget.last_combo) > 5) { - ntarget.combo = 1; - } - combo += ntarget.combo; - ntarget.last_combo = new Date(); - ntarget.combo += 1; - } - if (targets.length > 1) { - def.stacked = []; - targets.forEach(function (t) { - def.stacked.push(t[0].id); - }); - } - } - } + if (info.positive || !info.procs) { + combo = 0; + } + if (combo && target.targets > 3) { + if (!target.last_combo || ssince(target.last_combo) > 5) { + target.combo = 1; + } + combo += target.combo; + target.last_combo = new Date(); + target.combo += 1; + def.mobbing = target.combo; + } + if ( + combo && + target.is_player && + instances[target.in].pmap[target.last_hash] && + Object.keys(instances[target.in].pmap[target.last_hash]).length > 1 + ) { + var combo_check = false; + for (var id in instances[target.in].pmap[target.last_hash]) { + var ntarget = instances[target.in].pmap[target.last_hash][id]; + if ( + target.id == ntarget.id || + is_invinc(ntarget) || + (!is_same(target, ntarget, 2) && !is_in_pvp(target)) || + attacker.id == ntarget.id + ) { + continue; + } //is_same(attacker,ntarget,1) + //!is_same(target,ntarget,1) && !is_in_pvp(target) to prevent people killing other people in non-pvp servers + //!is_same(target,ntarget,2) makes people kill others in coop fights + combo_check = true; + break; + } + if (combo_check) { + targets = []; + combo = 0; + for (var id in instances[target.in].pmap[target.last_hash]) { + var ntarget = instances[target.in].pmap[target.last_hash][id]; + if (is_invinc(ntarget) || attacker.id == ntarget.id || !is_same(target, ntarget, 2)) { + continue; + } //is_same(attacker,ntarget,1) + targets.push([ntarget, "stack"]); + if (!ntarget.last_combo || ssince(ntarget.last_combo) > 5) { + ntarget.combo = 1; + } + combo += ntarget.combo; + ntarget.last_combo = new Date(); + ntarget.combo += 1; + } + if (targets.length > 1) { + def.stacked = []; + targets.forEach(function (t) { + def.stacked.push(t[0].id); + }); + } + } + } - if (combo > 10) { - combo_m = combo / 4.0; - } else if (combo > 1) { - combo_m = [1, 1.6, 1.62, 1.64, 1.7, 1.72, 1.75, 1.8, 1.9, 2, 2, 2, 2][combo]; - } - combo_m = min(combo_m, max(300 / attack, 1.2)); // previously 2.4 + if (combo > 10) { + combo_m = combo / 4.0; + } else if (combo > 1) { + combo_m = [1, 1.6, 1.62, 1.64, 1.7, 1.72, 1.75, 1.8, 1.9, 2, 2, 2, 2][combo]; + } + combo_m = min(combo_m, max(300 / attack, 1.2)); // previously 2.4 - if ( - !info.positive && - ((info.explosion && info.damage_type == "physical") || (info.blast && info.damage_type == "magical")) - ) { - var intensity = info.blast; - var defense = "resistance"; - if (info.damage_type == "physical") { - intensity = info.explosion; - defense = "armor"; - } - var radius = intensity / 3.6; - if (is_in_pvp(attacker) || attacker.is_monster) { - for (var id in instances[target.in].players) { - var target = instances[target.in].players[id]; - if (target.npc) { - continue; - } - if (target.id != otarget.id && distance(target, otarget) < radius) { - targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); - } - } - } - if (attacker.is_player) { - for (var id in instances[target.in].monsters) { - var target = instances[target.in].monsters[id]; - if (target.avoidance && Math.random() * 100 < target.avoidance) { - xy_emit( - info.action, - "hit", - { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, - attacker.id, - ); - continue; - } - if (target.id != otarget.id && distance(target, otarget) < radius) { - targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); - } - } - } - } + if ( + !info.positive && + ((info.explosion && info.damage_type == "physical") || (info.blast && info.damage_type == "magical")) + ) { + var intensity = info.blast; + var defense = "resistance"; + if (info.damage_type == "physical") { + intensity = info.explosion; + defense = "armor"; + } + var radius = intensity / 3.6; + if (is_in_pvp(attacker) || attacker.is_monster) { + for (var id in instances[target.in].players) { + var target = instances[target.in].players[id]; + if (target.npc) { + continue; + } + if (target.id != otarget.id && distance(target, otarget) < radius) { + targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); + } + } + } + if (attacker.is_player) { + for (var id in instances[target.in].monsters) { + var target = instances[target.in].monsters[id]; + if (target.avoidance && Math.random() * 100 < target.avoidance) { + xy_emit( + info.action, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, + attacker.id, + ); + continue; + } + if (target.id != otarget.id && distance(target, otarget) < radius) { + targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); + } + } + } + } - targets.forEach(function (target_def) { - delete def.splash; - delete def.dreturn; - delete def.kill; - delete def.unintentional; + targets.forEach(function (target_def) { + delete def.splash; + delete def.dreturn; + delete def.kill; + delete def.unintentional; - var target = target_def[0]; - if (target_def[1] != "normal") { - def.unintentional = true; - } - if (target_def[1] == "splash") { - def.splash = true; - } + var target = target_def[0]; + if (target_def[1] != "normal") { + def.unintentional = true; + } + if (target_def[1] == "splash") { + def.splash = true; + } - def.id = target.id; + def.id = target.id; - if (info.heal) { - if (first) { - o_attack = attack = -ceil( - B.heal_multiplier * - attack * - (0.9 + Math.random() * 0.2) * - damage_multiplier(((target[defense] || 0) - (attacker[pierce] || 0)) / 2.0), - ); - if (target.s.poisoned) { - attack = round(attack * 0.25); - } - } else { - attack = o_attack; - } - def.heal = -attack; - if (attacker.is_player && target.type == "ghost" && !target.s.healed) { - target.s.healed = { ms: 960 * 60 * 60 * 1000 }; - drop_one_thing(attacker, "essenceoflife", { x: target.x, y: target.y, chest: "chestp" }); - } - if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.is_player) { - var gold = -88; - target.gold -= gold; - def.goldsteal = gold; - } - } else { - if (evade) { - return; - } - target.hits++; - if (first) { - if (info.crit && Math.random() * 100 < info.crit) { - var cmult = 2 + (info.critdamage || 0) / 100.0; - def.crit = cmult; - attack *= cmult; - } - if (attacker.type == "rogue") { - var maxd = G.skills.stack.max; - // if(G.monsters[target.type] && G.monsters[target.type].stationary) maxd=9999999999; - target.s.stack = { ms: 10000, s: min(maxd, (target.s.stack && target.s.stack.s + 1) || 1) }; - attack += target.s.stack.s; - } - if (def.purify) { - for (var name in target.s) { - if ( - (G.conditions[name] && - (G.conditions[name].buff || G.conditions[name].debuff) && - !G.conditions[name].persistent) || - target.s[name].citizens - ) { - delete target.s[name]; - attack += 400; - info.first_attack += 400; - target.cid++; - target.u = true; - } - } - } - var dmg_mult = 1; - if (attacker.is_player && target["for"]) { - dmg_mult = damage_multiplier(target["for"] * 5); - } - i_attack = attack = ceil(combo_m * attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); - attack = - ceil( - attack * dmg_mult * damage_multiplier((target[defense] || 0) - (attacker[pierce] || 0) - info.apiercing), - ) || 0; + if (info.heal) { + if (first) { + o_attack = attack = -ceil( + B.heal_multiplier * + attack * + (0.9 + Math.random() * 0.2) * + damage_multiplier(((target[defense] || 0) - (attacker[pierce] || 0)) / 2.0), + ); + if (target.s.poisoned) { + attack = round(attack * 0.25); + } + } else { + attack = o_attack; + } + def.heal = -attack; + if (attacker.is_player && target.type == "ghost" && !target.s.healed) { + target.s.healed = { ms: 960 * 60 * 60 * 1000 }; + drop_one_thing(attacker, "essenceoflife", { x: target.x, y: target.y, chest: "chestp" }); + } + if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.is_player) { + var gold = -88; + target.gold -= gold; + def.goldsteal = gold; + } + } else { + if (evade) { + return; + } + target.hits++; + if (first) { + if (info.crit && Math.random() * 100 < info.crit) { + var cmult = 2 + (info.critdamage || 0) / 100.0; + def.crit = cmult; + attack *= cmult; + } + if (attacker.type == "rogue") { + var maxd = G.skills.stack.max; + // if(G.monsters[target.type] && G.monsters[target.type].stationary) maxd=9999999999; + target.s.stack = { ms: 10000, s: min(maxd, (target.s.stack && target.s.stack.s + 1) || 1) }; + attack += target.s.stack.s; + } + if (def.purify) { + for (var name in target.s) { + if ( + (G.conditions[name] && + (G.conditions[name].buff || G.conditions[name].debuff) && + !G.conditions[name].persistent) || + target.s[name].citizens + ) { + delete target.s[name]; + attack += 400; + info.first_attack += 400; + target.cid++; + target.u = true; + } + } + } + var dmg_mult = 1; + if (attacker.is_player && target["for"]) { + dmg_mult = damage_multiplier(target["for"] * 5); + } + i_attack = attack = ceil(combo_m * attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); + attack = + ceil( + attack * dmg_mult * damage_multiplier((target[defense] || 0) - (attacker[pierce] || 0) - info.apiercing), + ) || 0; - if (target.incdmgamp) { - attack = round((attack * (100 + target.incdmgamp)) / 100.0); - } + if (target.incdmgamp) { + attack = round((attack * (100 + target.incdmgamp)) / 100.0); + } - if (target["1hp"]) { - attack = (def.crit && 2) || 1; - } - if (attack > 0 && target.s.invincible) { - attack = 0; - } - if (attacker.tskin == "konami" && target.type != attacker.p.target_lock) { - attack = 0; - } + if (target["1hp"]) { + attack = (def.crit && 2) || 1; + } + if (attack > 0 && target.s.invincible) { + attack = 0; + } + if (attacker.tskin == "konami" && target.type != attacker.p.target_lock) { + attack = 0; + } - o_attack = attack = ceil(attack); + o_attack = attack = ceil(attack); - if (info.conditions.includes("frozen") && !target.immune && target.hp > attack) { - if (Math.random() < target.fzresistance / 100.0) { - xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); - } else { - add_condition(target, "frozen"); - disappearing_text(target.socket, target, "FREEZE!", { xy: 1, size: "huge", color: "freeze", nv: 1 }); //target.is_player&&"huge"||undefined - } - } + if (info.conditions.includes("frozen") && !target.immune && target.hp > attack) { + if (Math.random() < target.fzresistance / 100.0) { + xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); + } else { + add_condition(target, "frozen"); + disappearing_text(target.socket, target, "FREEZE!", { xy: 1, size: "huge", color: "freeze", nv: 1 }); //target.is_player&&"huge"||undefined + } + } - if (info.conditions.includes("burned") && !target.immune && target.hp > attack) { - add_condition(target, "burned", { - divider: attacker.a && attacker.a.burn && attacker.a.burn.unlimited && 1.5, - fid: attacker.id, - f: attacker.name || G.monsters[attacker.type].name, - attack: attack, - }); - } + if (info.conditions.includes("burned") && !target.immune && target.hp > attack) { + add_condition(target, "burned", { + divider: attacker.a && attacker.a.burn && attacker.a.burn.unlimited && 1.5, + fid: attacker.id, + f: attacker.name || G.monsters[attacker.type].name, + attack: attack, + }); + } - if (info.conditions.includes("woven") && !target.immune) { - add_condition(target, "woven"); - } + if (info.conditions.includes("woven") && !target.immune) { + add_condition(target, "woven"); + } - if ( - info.conditions.includes("stunned") && - target.hp > attack && - add_condition(target, "stunned", { duration: 2000 }) - ) { - disappearing_text(target.socket, target, "STUN!", { xy: 1, size: "huge", color: "stun", nv: 1 }); - } //target.is_player&&"huge"||undefined + if ( + info.conditions.includes("stunned") && + target.hp > attack && + add_condition(target, "stunned", { duration: 2000 }) + ) { + disappearing_text(target.socket, target, "STUN!", { xy: 1, size: "huge", color: "stun", nv: 1 }); + } //target.is_player&&"huge"||undefined - if (info.procs && target.a.putrid) { - add_condition(attacker, "poisoned"); - add_condition(attacker, "cursed"); - change = true; - } - info.conditions.forEach(function (c) { - if (["frozen", "burned", "woven", "stunned"].includes(c)) { - return; - } - if (target.hp > attack && !target.immune) { - add_condition(target, c); - } - }); - if (info.procs && attacker.a.sugarrush && Math.random() < 0.0025) { - def.trigger = "sugarrush"; - add_condition(attacker, "sugarrush"); - disappearing_text(attacker.socket, attacker, "SUGAR RUSH!", { xy: 1, size: "huge", color: "sugar", nv: 1 }); //target.is_player&&"huge"||undefined - } - if (attacker.s.invis) { - // && target.is_player - def.sneak = true; - disappearing_text(target.socket, target, "SNEAK!", { xy: 1, size: "huge", color: "sneak", nv: 1 }); - } - if (info.damage_type == "pure") { - attack = ceil(info.first_attack); - } - if (target["1hp"]) { - attack = (def.crit && 2) || 1; - } - } else { - attack = o_attack; - if (target_def[1] == "splash") { - attack = ceil(attack * target_def[2]); - } - if (target["1hp"]) { - def.damage = o_attack = attack = 1; - } - } - if (attack >= 1) { - target.last.attacked = new Date(); - if (target.is_monster && attacker.is_player) { - target.points[attacker.name] = (target.points[attacker.name] || 0) + 1; - } - } - if (atype == "deepfreeze" && add_condition(target, "deepfreezed")) { - def.deepfreeze = true; - } + if (info.procs && target.a.putrid) { + add_condition(attacker, "poisoned"); + add_condition(attacker, "cursed"); + change = true; + } + info.conditions.forEach(function (c) { + if (["frozen", "burned", "woven", "stunned"].includes(c)) { + return; + } + if (target.hp > attack && !target.immune) { + add_condition(target, c); + } + }); + if (info.procs && attacker.a.sugarrush && Math.random() < 0.0025) { + def.trigger = "sugarrush"; + add_condition(attacker, "sugarrush"); + disappearing_text(attacker.socket, attacker, "SUGAR RUSH!", { xy: 1, size: "huge", color: "sugar", nv: 1 }); //target.is_player&&"huge"||undefined + } + if (attacker.s.invis) { + // && target.is_player + def.sneak = true; + disappearing_text(target.socket, target, "SNEAK!", { xy: 1, size: "huge", color: "sneak", nv: 1 }); + } + if (info.damage_type == "pure") { + attack = ceil(info.first_attack); + } + if (target["1hp"]) { + attack = (def.crit && 2) || 1; + } + } else { + attack = o_attack; + if (target_def[1] == "splash") { + attack = ceil(attack * target_def[2]); + } + if (target["1hp"]) { + def.damage = o_attack = attack = 1; + } + } + if (attack >= 1) { + target.last.attacked = new Date(); + if (target.is_monster && attacker.is_player) { + target.points[attacker.name] = (target.points[attacker.name] || 0) + 1; + } + } + if (atype == "deepfreeze" && add_condition(target, "deepfreezed")) { + def.deepfreeze = true; + } - def.damage = attack; - if (info.lifesteal) { - var hp = ceil((min(attack, target.hp) * info.lifesteal) / 100.0); - attacker.hp = min(attacker.max_hp, attacker.hp + hp); - change = true; - if (hp) { - def.lifesteal = hp; - } - } - if (info.manasteal) { - var mp = ceil((min(attack, target.hp) * info.manasteal) / 100.0); - if (target.mp !== undefined) { - mp = min(target.mp, mp); - target.mp = max(0, target.mp - mp); - } else { - mp = 0; - } - if (attacker.mp !== undefined) { - attacker.mp = min(attacker.max_mp || 0, attacker.mp + mp); - change = true; - } - if (mp) { - def.manasteal = mp; - } - } - if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.gold) { - //var gold=min(target.gold,parseInt(ceil(target.level*target.level/10))); - var gold = parseInt(Math.random() * 12) + 1; - target.gold = max(0, target.gold - gold); - attacker.extra_gold = (attacker.extra_gold || 0) + gold; - def.goldsteal = gold; - } - } + def.damage = attack; + if (info.lifesteal) { + var hp = ceil((min(attack, target.hp) * info.lifesteal) / 100.0); + attacker.hp = min(attacker.max_hp, attacker.hp + hp); + change = true; + if (hp) { + def.lifesteal = hp; + } + } + if (info.manasteal) { + var mp = ceil((min(attack, target.hp) * info.manasteal) / 100.0); + if (target.mp !== undefined) { + mp = min(target.mp, mp); + target.mp = max(0, target.mp - mp); + } else { + mp = 0; + } + if (attacker.mp !== undefined) { + attacker.mp = min(attacker.max_mp || 0, attacker.mp + mp); + change = true; + } + if (mp) { + def.manasteal = mp; + } + } + if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.gold) { + //var gold=min(target.gold,parseInt(ceil(target.level*target.level/10))); + var gold = parseInt(Math.random() * 12) + 1; + target.gold = max(0, target.gold - gold); + attacker.extra_gold = (attacker.extra_gold || 0) + gold; + def.goldsteal = gold; + } + } - var original = target.hp; - if (target.s.mshield && target.mp > 200 && attack > 0) { - // console.log("HERE MPSHIELD!") - var max_mp = target.mp - 200; - var damage_per_mp = 1.5; - if (target.level > 99) { - damage_per_mp = 3; - } else if (target.level > 89) { - damage_per_mp = 2.4; - } else if (target.level > 79) { - damage_per_mp = 2; - } else if (target.level > 69) { - damage_per_mp = 1.75; - } - var max_hp = ceil(max_mp * damage_per_mp); - if (max_hp < attack) { - def.mp_damage = max_mp; - attack -= max_hp; - target.mp = 200; - } else { - def.mp_damage = parseInt(attack / damage_per_mp); - attack = 0; - target.mp -= def.mp_damage; - } - change = true; - } - target.hp = min(target.hp - attack, target.max_hp); // both for damage and heal - var net = original - max(0, target.hp); - if (target.hp <= 0) { - def.kill = true; - if (G.skills[atype].kill_buff) { - add_condition(attacker, G.skills[atype].kill_buff); - } - } + var original = target.hp; + if (target.s.mshield && target.mp > 200 && attack > 0) { + // console.log("HERE MPSHIELD!") + var max_mp = target.mp - 200; + var damage_per_mp = 1.5; + if (target.level > 99) { + damage_per_mp = 3; + } else if (target.level > 89) { + damage_per_mp = 2.4; + } else if (target.level > 79) { + damage_per_mp = 2; + } else if (target.level > 69) { + damage_per_mp = 1.75; + } + var max_hp = ceil(max_mp * damage_per_mp); + if (max_hp < attack) { + def.mp_damage = max_mp; + attack -= max_hp; + target.mp = 200; + } else { + def.mp_damage = parseInt(attack / damage_per_mp); + attack = 0; + target.mp -= def.mp_damage; + } + change = true; + } + target.hp = min(target.hp - attack, target.max_hp); // both for damage and heal + var net = original - max(0, target.hp); + if (target.hp <= 0) { + def.kill = true; + if (G.skills[atype].kill_buff) { + add_condition(attacker, G.skills[atype].kill_buff); + } + } - if ( - target.dreturn && - i_attack > 0 && - first && - attacker.range < 75 && - info.damage_type == "physical" && - !attacker["1hp"] - ) { - // dreturn happens at every hit - def.dreturn = ceil((i_attack * target.dreturn) / 100.0); - if (attacker.is_monster) { - attacker.u = true; - attacker.cid++; - } - attacker.hp = max(attacker.hp - def.dreturn, 0); - } + if ( + target.dreturn && + i_attack > 0 && + first && + attacker.range < 75 && + info.damage_type == "physical" && + !attacker["1hp"] + ) { + // dreturn happens at every hit + def.dreturn = ceil((i_attack * target.dreturn) / 100.0); + if (attacker.is_monster) { + attacker.u = true; + attacker.cid++; + } + attacker.hp = max(attacker.hp - def.dreturn, 0); + } - // if(target.is_player && net>0) target.s.damage_received={amount:target.s.damage_received&&(target.s.damage_received.amount+net)||net,ms:10000}; + // if(target.is_player && net>0) target.s.damage_received={amount:target.s.damage_received&&(target.s.damage_received.amount+net)||net,ms:10000}; - if (attacker.is_player) { - if (net > 0) { - // "attack" - if (attacker.t) { - attacker.t.mdamage += net; - } - add_pdps(attacker, target, net); - } // "heal" - else { - add_pdps(attacker, target, -net * B.dps_heal_mult); - } - var m = target; - var mnet = net; - if (net < 0 && target.s && target.s.coop) { - m = instances[attacker.in].monsters[target.s.coop.id]; - mnet = -net * B.dps_heal_mult; - if (m && m.attack < 120) { - mnet /= 100; - } // dirty fix - // console.log("coop heal: "+m) - } - if (target.master) { - m = instances[attacker.in].monsters[target.master]; - } - if (m && m.is_monster && m.cooperative) { - add_coop_points(m, attacker, mnet); - } - } + if (attacker.is_player) { + if (net > 0) { + // "attack" + if (attacker.t) { + attacker.t.mdamage += net; + } + add_pdps(attacker, target, net); + } // "heal" + else { + add_pdps(attacker, target, -net * B.dps_heal_mult); + } + var m = target; + var mnet = net; + if (net < 0 && target.s && target.s.coop) { + m = instances[attacker.in].monsters[target.s.coop.id]; + mnet = -net * B.dps_heal_mult; + if (m && m.attack < 120) { + mnet /= 100; + } // dirty fix + // console.log("coop heal: "+m) + } + if (target.master) { + m = instances[attacker.in].monsters[target.master]; + } + if (m && m.is_monster && m.cooperative) { + add_coop_points(m, attacker, mnet); + } + } - if (mode.instant_monster_attacks || attacker.is_player) { - xy_emit(target, "hit", def, attacker.id); - } // always sends the event to attacker.id - else { - events.push(["hit", def]); - } + if (mode.instant_monster_attacks || attacker.is_player) { + xy_emit(target, "hit", def, attacker.id); + } // always sends the event to attacker.id + else { + events.push(["hit", def]); + } - if (attacker.is_monster) { - //monster attacks player - achievement_logic_monster_hit(attacker, target, attack); - if (target.hp <= 0 && !target.rip) { - if (target.a && target.a.secondchance && Math.random() * 100 < target.a.secondchance.attr0) { - target.hp = target.max_hp; - disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); - } else { - if (target.s.block && get_player(target.s.block.f)) { - pwn_routine(get_player(target.s.block.f), target); - } else { - defeated_by_a_monster(attacker, target); - } - } - } - target.c = {}; - } else if (target.is_monster) { - //player attacks monster - achievement_logic_monster_damage(attacker, target, net); - target.u = true; - target.cid++; - ccms(target); - if (target.hp <= 0) { - if (atype == "mentalburst") { - attacker.mp += net; - } - achievement_logic_monster_last_hit(attacker, target); - kill_monster(attacker, target); - } else { - if (target.a.warp_on_hit && Math.random() < target.a.warp_on_hit.attr0 && !is_disabled(target)) { - var point = random_place(target.map); - transport_monster_to(target, target.in, target.map, point.x, point.y); - } - if (target.drop_on_hit) { - drop_something(attacker, target, 1); - } - if ( - !attacker.is_npc && - !target.target && - !evade && - atype != "shadowstrike" && - !G.monsters[target.type].passive - ) { - target_player(target, attacker); - } - } - } else if (target.is_player) { - //player attacks player - if (!info.positive && !target.s.invincible) { - if (mode.dpvpblock) { - attacker.socket.emit("eval", { code: "pvp_timeout(3600,1)" }); - attacker.s.block = { ms: 3600, f: (attacker.s.block && attacker.s.block.f) || target.name }; - change = true; - } - target.socket.emit("eval", { code: "pvp_timeout(3600)" }); - target.s.block = { ms: 3600, f: (target.s.block && target.s.block.f) || attacker.name }; - if (!is_same(target, attacker, 1)) { - target.s.block.f = attacker.name; - } - target.c = {}; - } + if (attacker.is_monster) { + //monster attacks player + achievement_logic_monster_hit(attacker, target, attack); + if (target.hp <= 0 && !target.rip) { + if (target.a && target.a.secondchance && Math.random() * 100 < target.a.secondchance.attr0) { + target.hp = target.max_hp; + disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); + } else { + if (target.s.block && get_player(target.s.block.f)) { + pwn_routine(get_player(target.s.block.f), target); + } else { + defeated_by_a_monster(attacker, target); + } + } + } + target.c = {}; + } else if (target.is_monster) { + //player attacks monster + achievement_logic_monster_damage(attacker, target, net); + target.u = true; + target.cid++; + ccms(target); + if (target.hp <= 0) { + if (atype == "mentalburst") { + attacker.mp += net; + } + achievement_logic_monster_last_hit(attacker, target); + kill_monster(attacker, target); + } else { + if (target.a.warp_on_hit && Math.random() < target.a.warp_on_hit.attr0 && !is_disabled(target)) { + var point = random_place(target.map); + transport_monster_to(target, target.in, target.map, point.x, point.y); + } + if (target.drop_on_hit) { + drop_something(attacker, target, 1); + } + if ( + !attacker.is_npc && + !target.target && + !evade && + atype != "shadowstrike" && + !G.monsters[target.type].passive + ) { + target_player(target, attacker); + } + } + } else if (target.is_player) { + //player attacks player + if (!info.positive && !target.s.invincible) { + if (mode.dpvpblock) { + attacker.socket.emit("eval", { code: "pvp_timeout(3600,1)" }); + attacker.s.block = { ms: 3600, f: (attacker.s.block && attacker.s.block.f) || target.name }; + change = true; + } + target.socket.emit("eval", { code: "pvp_timeout(3600)" }); + target.s.block = { ms: 3600, f: (target.s.block && target.s.block.f) || attacker.name }; + if (!is_same(target, attacker, 1)) { + target.s.block.f = attacker.name; + } + target.c = {}; + } - if (target.hp <= 0 && !target.rip) { - if (atype == "mentalburst") { - attacker.mp += net; - change = true; - } - if (target.a && target.a.secondchance && Math.random() * 100 < target.a.secondchance.attr0) { - target.hp = target.max_hp; - disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); - } else { - var victor = attacker; - if (target.s.block && get_player(target.s.block.f)) { - victor = get_player(target.s.block.f); - } - pwn_routine(victor, target); - } - } - } + if (target.hp <= 0 && !target.rip) { + if (atype == "mentalburst") { + attacker.mp += net; + change = true; + } + if (target.a && target.a.secondchance && Math.random() * 100 < target.a.secondchance.attr0) { + target.hp = target.max_hp; + disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); + } else { + var victor = attacker; + if (target.s.block && get_player(target.s.block.f)) { + victor = get_player(target.s.block.f); + } + pwn_routine(victor, target); + } + } + } - first = false; + first = false; - if (target.is_player) { - resend(target, "u+cid"); - } - }); + if (target.is_player) { + resend(target, "u+cid"); + } + }); - if (attacker.hp <= 0 && !attacker.dead && !attacker.rip) { - // dreturn - if (attacker.is_monster) { - kill_monster((attacker.target && get_player(attacker.target)) || target, attacker); - } // monster to player - else if (target.is_monster) { - defeated_by_a_monster(target, attacker); - change = true; - } // player to monster - else { - pwn_routine(target, attacker); - } // player to player - } + if (attacker.hp <= 0 && !attacker.dead && !attacker.rip) { + // dreturn + if (attacker.is_monster) { + kill_monster((attacker.target && get_player(attacker.target)) || target, attacker); + } // monster to player + else if (target.is_monster) { + defeated_by_a_monster(target, attacker); + change = true; + } // player to monster + else { + pwn_routine(target, attacker); + } // player to player + } - if ((change || attacker.to_resend) && attacker.is_player) { - resend(attacker, "u+cid"); - } - if (change && attacker.is_monster && !attacker.dead) { - attacker.u = true; - attacker.cid++; - ccms(attacker); - } + if ((change || attacker.to_resend) && attacker.is_player) { + resend(attacker, "u+cid"); + } + if (change && attacker.is_monster && !attacker.dead) { + attacker.u = true; + attacker.cid++; + ccms(attacker); + } } function target_player(monster, player, no_increase) { - // if(is_sdk) console.log("target_player: "+player.name+" "+(!no_increase)); - if (!no_increase && (monster.s.charmed || monster.peaceful)) { - return; - } - if (monster.dead || monster.pet || monster.trap) { - return; - } - monster.target = player.name; - if (!no_increase) { - increase_targets(player, monster); - } - delete monster.s.sleeping; - monster.last.attacked = new Date(); - monster.last_level = future_s(Math.random() * 100 - 50); - monster.ex = monster.x; - monster.ey = monster.y; - monster.moving = false; - monster.abs = true; - monster.u = true; - monster.cid++; - calculate_monster_stats(monster); + // if(is_sdk) console.log("target_player: "+player.name+" "+(!no_increase)); + if (!no_increase && (monster.s.charmed || monster.peaceful)) { + return; + } + if (monster.dead || monster.pet || monster.trap) { + return; + } + monster.target = player.name; + if (!no_increase) { + increase_targets(player, monster); + } + delete monster.s.sleeping; + monster.last.attacked = new Date(); + monster.last_level = future_s(Math.random() * 100 - 50); + monster.ex = monster.x; + monster.ey = monster.y; + monster.moving = false; + monster.abs = true; + monster.u = true; + monster.cid++; + calculate_monster_stats(monster); } function defeat_player(player) { - player.violations = (player.violations || 0) + 1; - if (player.s.block && player.s.block.f && !player.rip) { - var attacker = players[name_to_id[player.s.block.f]]; - if (attacker && attacker.name != player.name) { - issue_player_award(attacker, player); - instance_emit(attacker.in, "server_message", { - message: attacker.name + " defeated " + player.name, - color: "gray", - }); - if (player.map == "arena") { - xy_emit(npcs.pvp, "chat_log", { - owner: npcs.pvp.name, - message: attacker.name + " defeated " + player.name, - id: "pvp", - }); - } - rip(player); - } - } + player.violations = (player.violations || 0) + 1; + if (player.s.block && player.s.block.f && !player.rip) { + var attacker = players[name_to_id[player.s.block.f]]; + if (attacker && attacker.name != player.name) { + issue_player_award(attacker, player); + instance_emit(attacker.in, "server_message", { + message: attacker.name + " defeated " + player.name, + color: "gray", + }); + if (player.map == "arena") { + xy_emit(npcs.pvp, "chat_log", { + owner: npcs.pvp.name, + message: attacker.name + " defeated " + player.name, + id: "pvp", + }); + } + rip(player); + } + } } function duel_defeat(player) { - var info = instances[player.duel.id].info; - info.A.forEach(function (p) { - if (p.name == player.name) { - p.active = false; - } - }); - info.B.forEach(function (p) { - if (p.name == player.name) { - p.active = false; - } - }); - delete player.duel; - delete player.team; + var info = instances[player.duel.id].info; + info.A.forEach(function (p) { + if (p.name == player.name) { + p.active = false; + } + }); + info.B.forEach(function (p) { + if (p.name == player.name) { + p.active = false; + } + }); + delete player.duel; + delete player.team; } function resend(player, events) { - if (player.halt || player.is_npc) { - return; - } - if (!player.gold && player.gold !== 0) { - player.gold = 0; - server_log("#X - GOLD BUG resend", 1); - } - events = (events && events.split && events.split("+")) || []; - delete player.to_resend; - if (in_arr("u", events)) { - add_call_cost(call_modifier); - player.u = true; - } - if (in_arr("cid", events)) { - player.cid++; - } - // if(in_arr("inv",events)) no longer needed as both add_item and consume has .esize updates [18/10/18] - // { - // player.esize=0; - // for(var i=0;i 6000) { - var ms = max(0, -mssince(player.last.attack)) + 3200; - var EV = ""; - EV = "skill_timeout('attack'," + ms + "); "; - player.last.attack = future_ms(ms); - for (var i in player.last) { - if (G.skills[i] && player.last[i] > future_ms(-3000)) { - player.last[i] = future_ms(3200); - EV += "skill_timeout('" + i + "'," + (3200 + (G.skills[i].cooldown || 0)) + "); "; - } - } - } - if (effect) { - player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 812, 120000) }; - } else { - player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 3200, 120000) }; - } - player.socket.emit("new_map", { - name: instance.map, - in: name, - x: player.x, - y: player.y, - direction: direction, - effect: effect || 0, - info: instance.info, - m: player.m, - entities: send_all_xy(player, { raw: true }), - eval: EV, - }); - player.last.transport = new Date(); - resend(player, "u+cid"); + if (scatter) { + player.x += Math.random() * scatter - scatter / 2; + player.y += Math.random() * scatter - scatter / 2; + } + // server_log(effect); + if (effect) { + // server_log("here"); + player.tp = effect; //appear + setTimeout(function () { + try { + if (!check_player(player)) { + return; + } + player.tp = false; + } catch (e) { + log_trace("#X Critical-tp", e); + } + }, 500); + } + player.moving = false; + player.vx = player.vy = 0; + player.u = true; + player.cid++; + calculate_player_stats(player); + pmap_add(player); + add_call_cost(player, 8, "transport"); // New - to offset send_all_xy [26/01/20] + if (0 && !effect && mssince(player.last.transport) > 6000) { + var ms = max(0, -mssince(player.last.attack)) + 3200; + var EV = ""; + EV = "skill_timeout('attack'," + ms + "); "; + player.last.attack = future_ms(ms); + for (var i in player.last) { + if (G.skills[i] && player.last[i] > future_ms(-3000)) { + player.last[i] = future_ms(3200); + EV += "skill_timeout('" + i + "'," + (3200 + (G.skills[i].cooldown || 0)) + "); "; + } + } + } + if (effect) { + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 812, 120000) }; + } else { + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 3200, 120000) }; + } + player.socket.emit("new_map", { + name: instance.map, + in: name, + x: player.x, + y: player.y, + direction: direction, + effect: effect || 0, + info: instance.info, + m: player.m, + entities: send_all_xy(player, { raw: true }), + eval: EV, + }); + player.last.transport = new Date(); + resend(player, "u+cid"); } function add_shells(player, amount, reason, announce, override) { - if (gameplay == "hardcore" || gameplay == "test") { - return; - } - var phrase = "Received"; - if (reason == "xptome") { - phrase = "Earned"; - } - player.cash += amount; - player.socket.emit("game_log", { message: phrase + " " + to_pretty_num(amount) + " SHELLS", color: "green" }); - disappearing_text(player.socket, player, "+" + to_pretty_num(amount), { - color: colors.cash, - xy: 1, - s: "cash", - size: "huge", - }); - appengine_call( - "bill_user", - { auth: player.auth, amount: -parseInt(amount), reason: reason + "_drop", name: player.name, override: override }, - function (result) { - if (result.failed || !result.done) { - return; - } - player.cash = result.cash; - resend(player, "reopen"); - }, - ); - if (announce) { - broadcast("server_message", { - message: player.name + " found " + to_pretty_num(amount) + " shells", - color: "#85C76B", - type: "server_found", - shells: amount, - name: player.name, - }); - } + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + var phrase = "Received"; + if (reason == "xptome") { + phrase = "Earned"; + } + player.cash += amount; + player.socket.emit("game_log", { message: phrase + " " + to_pretty_num(amount) + " SHELLS", color: "green" }); + disappearing_text(player.socket, player, "+" + to_pretty_num(amount), { + color: colors.cash, + xy: 1, + s: "cash", + size: "huge", + }); + appengine_call( + "bill_user", + { auth: player.auth, amount: -parseInt(amount), reason: reason + "_drop", name: player.name, override: override }, + function (result) { + if (result.failed || !result.done) { + return; + } + player.cash = result.cash; + resend(player, "reopen"); + }, + ); + if (announce) { + broadcast("server_message", { + message: player.name + " found " + to_pretty_num(amount) + " shells", + color: "#85C76B", + type: "server_found", + shells: amount, + name: player.name, + }); + } } function is_socket_allowed(socket) { - var loose = 0; - for (var id in sockets) { - if (!players[id] && get_ip(sockets[id]) == get_ip(socket)) { - loose++; - } - } - if (loose > 5) { - return false; - } - return true; + var loose = 0; + for (var id in sockets) { + if (!players[id] && get_ip(sockets[id]) == get_ip(socket)) { + loose++; + } + } + if (loose > 5) { + return false; + } + return true; } function disconnect_old_sockets(socket) { - for (var id in sockets) { - if (id != socket.id && !players[id] && get_ip(sockets[id]) == get_ip(socket)) { - sockets[id].emit("disconnect_reason", "Too many loose connections from your network. Simply reload to play."); - if (sockets[id]) { - sockets[id].disconnect(); - } // emit can trigger a disconnect too, so this would throw an exception, bring down the server - } - } + for (var id in sockets) { + if (id != socket.id && !players[id] && get_ip(sockets[id]) == get_ip(socket)) { + sockets[id].emit("disconnect_reason", "Too many loose connections from your network. Simply reload to play."); + if (sockets[id]) { + sockets[id].disconnect(); + } // emit can trigger a disconnect too, so this would throw an exception, bring down the server + } + } } function init_io() { - io.on("connection", function (socket) { - if (socket.handshake.query.server_method) { - if (0 && socket.handshake.query.server_master == variables.server_master) { - // this was to make servers communicate with each other and disconnect overflows immediately [28/10/23] - if (socket.handshake.query.server_method == "players") { - socket.emit("players"); // decided to make the existing cron more aggressive [26/09/21] - } - } - socket.disconnect(); - return; - } - sockets[socket.id] = socket; - socket.total_calls = 0; - socket.calls = []; - socket.fs = {}; // function list - - if (!is_socket_allowed(socket)) { - disconnect_old_sockets(socket); - } - - var original_on = socket.on; - socket.on = function (method, f) { - // takes the "f" function, the function thats sent to socket.on, wraps it into a "g" function - var g = function (data) { - ls_method = method; - if (mode.log_all) { - console.log("'" + method + "': " + JSON.stringify(data)); - } - try { - var climit = limits.calls; - var name = "_observer"; - if (data === undefined) { - data = {}; - } // data normalisation [28/08/18] - socket.total_calls++; - add_call_cost(-1); - current_socket = socket; - call_modifier = { open_chest: 0.1, skill: 0.05, target: 0.5 }[method] || 1; - if (players[socket.id]) { - name = players[socket.id].name; - // if(players[socket.id].type=="merchant") climit=round(climit/3); - // Merchants are first class citizens now! [14/01/18] - } else { - climit = round(climit / 4); - } - if (method == "cm") { - var add = 1; - var len = data.message.length; - var mult = 1; - if (len > 100) { - add = 2; - } else if (len > 1000) { - add = 3; - } else if (len > 10000) { - add = 10; - } else if (len > 50000) { - add = 20; - } - // console.log("add: "+add+"len: "+len); - if (data.to.length > 1) { - mult = 0.8; - } - add_call_cost(add * data.to.length * mult, undefined, "cm_data"); - } else { - if (CC[method]) { - add_call_cost(CC[method] || 0); - } - } - if (get_call_cost() > climit && method != "disconnect") { - server_log(">>> LIMITDC " + name, 1); - socket.emit("limitdcreport", { calls: socket.calls, climit: climit, total: socket.total_calls }); - socket.emit("disconnect_reason", "limitdc"); - socket.disconnect(); - } else { - f(data); - } - } catch (e) { - try { - var climit = limits.calls; - add_call_cost(16); - log_trace("socket.on: " + method.substr(0, 200), e); - if (get_call_cost() > climit) { - server_log(">>> LIMITDC2 " + name, 1); - socket.emit("limitdcreport", { - calls: socket.calls, - climit: climit, - total: socket.total_calls, - method: method.substr(0, 200), - }); - socket.emit("disconnect_reason", "limitdc"); - socket.disconnect(); - } else { - try { - socket.emit("game_error", "ERROR!"); - } catch (e) {} - } - } catch (e) { - log_trace("limit_calls", e); - } - } - current_socket = false_socket; - }; - socket.fs[method] = f; - original_on.apply(socket, [method, g]); - }; - - var data = { - region: region, - name: server_name, - pvp: is_pvp, - gameplay: gameplay, - info: (instances[socket.first_map] && instances[socket.first_map].info) || {}, - }; - socket.first_map = socket.first_in = observer_map; - socket.first_x = observer_x; - socket.first_y = observer_y + 120; - socket.desktop = true; - if (socket.request && socket.request._query && socket.request._query.secret) { - for (var id in players) { - var player = players[id]; - if (player.secret == socket.request._query.secret) { - socket.player = player; - data.character = player_to_client(player); - data.character.id = data.character.name = player.name; - socket.first_map = player.map; - socket.first_in = player.in; - socket.first_x = player.x; - socket.first_y = player.y; - if (socket.request._query.desktop) { - socket.desktop = true; - socket.first_y += 120; - } else { - socket.desktop = false; - } - } - } - } - data.x = socket.first_x; - data.y = socket.first_y; - data.map = socket.first_map; - data.in = socket.first_in; - broadcast_e(true); - data.S = E; - socket.emit("welcome", data); - socket.on("send_updates", function () { - if (observers[socket.id]) { - send_all_xy(observers[socket.id]); - } - if (players[socket.id]) { - send_all_xy(players[socket.id]); - } - }); - socket.on("loaded", function (data) { - var observer = (observers[socket.id] = { - socket: socket, - x: socket.first_x, - y: socket.first_y, - // vision:[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision], - map: socket.first_map, - in: socket.first_in, - observer: 1, - id: "o" + socket.id, - s: {}, - }); - if (socket.player) { - observer.player = socket.player; - } - // observer.vision[0]=min(1000,observer.vision[0]); observer.vision[1]=min(700,observer.vision[1]); - observer.vision = B.vision; - // socket.emit("observing",{map:observer.map,x:observer.x,y:observer.y}); - resume_instance(instances[observer.in]); - instances[observer.in].observers[observer.id] = observer; - send_all_xy(observer); - }); - socket.on("o:home", function (data) { - var observer = observers[socket.id]; - if (!observer) { - return; - } - var player = observer.player; - if (!player || player.dc) { - return; - } - transport_observer_to(observer, player.in, player.map, player.x, player.y + ((socket.desktop && 120) || 0)); - }); - socket.on("o:command", function (data) { - var observer = observers[socket.id]; - if (!observer) { - return; - } - var player = observer.player; - if (!player || player.dc) { - return; - } - player.socket.emit("code_eval", data); - }); - socket.on("cm", function (data) { - var player = players[socket.id]; - var receivers = []; - if (!player || player.s.mute) { - return fail_response("muted"); - } - data.to.forEach(function (name) { - var p = players[name_to_id[name]]; - if (p) { - p.socket.emit("cm", { name: player.name, message: data.message || "" }); - receivers.push(name); - } - }); - success_response("data", { locals: [], receivers: receivers }); - }); - socket.on("say", function (data) { - var player = players[socket.id]; - var message = strip_string(data.message).substr(0, 1200); - if (!player || player.s.mute) { - return fail_response("muted"); - } - if (data.code && player.last_say && ssince(player.last_say) < 15) { - return fail_response("chat_slowdown"); - } - if (player.last_say && mssince(player.last_say) < 400) { - return fail_response("chat_slowdown"); - } - if (!message || !message.length) { - return fail_response("invalid"); - } - player.last_say = new Date(); - if (data.party) { - if (!player.party) { - return fail_response("not_in_a_party"); - } - party_emit(player.party, "partym", { owner: player.name, message: message, id: player.id, p: true }); - } else if (data.name) { - var target = get_player(data.name); - if (!target) { - player.socket.emit("pm", { - owner: player.name, - to: data.name, - message: message, - id: player.id, - xserver: true, - }); - appengine_call( - "log_chat", - { to: ["", data.name], type: "xprivate", message: message, fro: player.name, author: player.owner }, - function (result) { - if (result.failed && players[socket.id]) { - player.socket.emit("pm", { - owner: player.name, - to: data.name, - message: "(FAILED)", - id: player.id, - xserver: true, - }); - } - }, - ); - } else { - if (target.name == player.name) { - return fail_response("invalid"); - } - player.socket.emit("pm", { owner: player.name, to: data.name, message: message, id: player.id }); - target.socket.emit("pm", { owner: player.name, message: message, id: player.id }); - appengine_call("log_chat", { - to: [target.owner, target.name], - type: "private", - message: message, - fro: player.name, - author: player.owner, - }); - } - } else { - if (1) { - broadcast("chat_log", { owner: player.name, message: message, id: player.id, p: true }); - var owners = {}; - for (var id in players) { - var p = players[id]; - owners[p.owner] = owners[p.owner] || []; - owners[p.owner].push(p.name); - } - appengine_call("log_chat", { - to: Object.entries(owners), - type: "ambient", - message: message, - fro: player.name, - author: player.owner, - }); - } else { - xy_emit(player, "chat_log", { owner: player.name, message: message, id: player.id, p: true }); - } - appengine_call("log_chat", { type: "server", message: message, fro: player.name, author: player.owner }); - } - if (player.s.typing) { - delete player.s.typing; - resend(player, "u+cid+nc"); - } - success_response(); - }); - socket.on("ping_trig", function (data) { - socket.emit("ping_ack", data); - }); - socket.on("target", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - reduce_call_cost(); - player.target = data.id; - player.focus = data.xid; - var target = instances[player.in].monsters[data.id]; - if (!target) { - target = instances[player.in].players[data.id]; - } - if (!target) { - target = instances[player.in].players[NPC_prefix + data.id]; - } - if (!target) { - player.target = null; - } - var focus = instances[player.in].monsters[data.xid]; - if (!focus) { - focus = instances[player.in].players[data.xid]; - } - if (!focus) { - focus = instances[player.in].players[NPC_prefix + data.xid]; - } - if (!focus) { - player.focus = null; - } - if (focus && focus.screenshot) { - target.going_x = player.x; - target.going_y = player.y; - target.u = true; - start_moving_element(focus); - } - if (focus && player.map == "cgallery" && focus.npc) { - // target.going_x=player.x; - // target.going_y=player.y; - // target.u=true; - // start_moving_element(target); - if (!player.tcx) { - player.tcx = {}; - } - if (focus.ctype == "body") { - player.tskin = focus.skin; - } else if (player.cx.length > 5) { - return fail_response("invalid"); - } - player.tcx[focus.ctype] = focus.cx[focus.ctype]; - // player.tcx=target.cx; - resend(player, "u+cid"); - } - // server_log(player.name+" target: "+player.target+" focus: "+player.focus); - resend(player, "u+nc"); - success_response({}); - }); - socket.on("ureward", function (data) { - if (!player || player.user || !data.name || !G.docs.rewards[data.name]) { - return; - } - if (!player.verified || !player.auth_id) { - return socket.emit("game_response", "reward_notverified"); - } - if (!player.user.rewards) { - player.user.rewards = []; - } - if (player.user.rewards.includes(data.name)) { - return socket.emit("game_response", "reward_already"); - } - player.user.rewards.push(data.name); - exchange(player, G.docs.rewards[data.name], { name: "reward_" + data.name }); - socket.emit("game_response", { response: "reward_received", rewards: player.user.rewards }); - }); - socket.on("creward", function (data) { - if (!player || !data.name || !G.classes[player.type].rewards[data.name]) { - return; - } - if (!player.verified || !player.auth_id) { - return socket.emit("game_response", "reward_notverified"); - } - if (!player.p.rewards) { - player.p.rewards = []; - } - if (player.p.rewards.includes(data.name)) { - return socket.emit("game_response", "reward_already"); - } - player.p.rewards.push(data.name); - exchange(player, G.classes[player.type].rewards[data.name].reward, { name: "reward_" + data.name }); - socket.emit("game_response", { response: "reward_received", rewards: player.p.rewards }); - }); - socket.on("cx", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - // if(player.role!="gm") return socket.emit('game_log',"Cosmetics system is out of the test phase for now"); - // console.log(data); - var cx = player.cx; - var cxl = all_cx(player); - // if(!player.tcx) player.tcx=clone(player.cx); - if (data.slot && !data.name) { - if (data.slot == "back") { - delete cx.tail; - } // synced with render_cgallery - if (data.slot == "face") { - delete cx.makeup; - } - delete cx[data.slot]; - } else { - if (!T[data.name] || (!cxl[data.name] && player.role != "cx")) { - return fail_response("cx_not_found"); - } - if ((T[data.name] == "body" || T[data.name] == "armor" || T[data.name] == "character") && data.slot == "skin") { - player.skin = data.name; - } else if ((T[data.name] == "body" || T[data.name] == "armor") && data.slot == "upper") { - cx.upper = data.name; - } else if (cxtype_to_slot[T[data.name]] && cxtype_to_slot[T[data.name]] != "skin") { - cx[cxtype_to_slot[T[data.name]]] = data.name; - } - } - prune_cx(player.cx, player.skin); - resend(player, "u+cid"); - success_response(); - }); - socket.on("gm", function (data) { - var player = players[socket.id]; - if (!player || player.role != "gm") { - return; - } - var target = players[id_to_id[data.id]]; - var action = data.action; - if (action == "mute") { - if (!target) { - return socket.emit("game_log", "Player not found: " + data.id); - } - if (!target.s.mute) { - target.s.mute = { ms: 48 * 60 * 60 * 1000 }; - return socket.emit("game_chat", "Muted " + target.name); - } else { - target.s.mute = { ms: 0 }; - return socket.emit("game_chat", "Unmuted " + target.name); - } - } else if (action == "jail") { - if (!target) { - return socket.emit("game_log", "Player not found: " + data.id); - } - transport_player_to(target, "jail"); - } else if (action == "ban") { - appengine_call("ban_user", { name: data.id }, function (result) { - socket.emit("game_log", "Ban: " + ((result && result.result) || "No result")); - }); - } else if (action == "invincible") { - player.s.invincible = { ms: 24 * 60 * 60 * 1000 }; - resend(player, "u+cid"); - } else if (action == "jump") { - if (!target) { - return socket.emit("game_log", "Player not found: " + data.id); - } - var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); - if (spot) { - pulled = player; - pulled.s.magiport = { ms: 400 }; - pulled.s.magiport.x = spot.x; - pulled.s.magiport.y = spot.y; - pulled.s.magiport.f = target.name; - pulled.s.magiport.in = target.in; - pulled.s.magiport.map = target.map; - resend(pulled, "u+cid"); - } else { - return socket.emit("game_log", "No safe spot near: " + data.id); - } - } else if (action == "mjump") { - target = get_monsters(data.monster)[0]; - if (!target) { - return socket.emit("game_log", "Monster not found: " + data.monster); - } - var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); - if (spot) { - pulled = player; - pulled.s.magiport = { ms: 400 }; - pulled.s.magiport.x = spot.x; - pulled.s.magiport.y = spot.y; - pulled.s.magiport.f = target.name; - pulled.s.magiport.in = target.in; - pulled.s.magiport.map = target.map; - resend(pulled, "u+cid"); - } else { - return socket.emit("game_log", "No safe spot near: " + data.monster); - } - } else if (action == "jump_list") { - var ids = []; - for (var id in players) { - ids.push(players[id].name); - } - socket.emit("gm", { action: "jump_list", ids: ids }); - } else if (action == "server_info") { - var info = []; - for (var id in players) { - info.push({ name: players[id].name, owner: players[id].owner, ip: get_ip(players[id]) }); - } - //socket.emit("gm",{action:"server_info",info:info}); - } - }); - socket.on("monsterhunt", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (simple_distance(G.maps.main.ref.monsterhunter, player, true) > B.sell_dist) { - return fail_response("distance"); - } - var hunted = []; - for (var id in server.s) { - if (server.s[id].type == "monsterhunt") { - hunted.push(server.s[id].id); - } - } - if (player.s.monsterhunt && player.s.monsterhunt.c) { - return fail_response("monsterhunt_already"); - } else if (player.s.monsterhunt) { - delete server.s["monsterhunt_" + player.s.monsterhunt.id]; - delete player.s.monsterhunt; - add_item(player, "monstertoken", { log: true, q: (gameplay == "hardcore" && 100) || 1 }); - resend(player, "u+cid+reopen"); - return success_response({ completed: true }); - } - if (player.type == "merchant") { - return socket.emit("game_response", "monsterhunt_merchant"); - } - var mmax = -1; - var name = "goo"; - var count = 100; - var times = 0; - var the_hp = 0; - for (var id in instances) { - if (instances[id].name != id || !G.maps[id] || G.maps[id].irregular) { - continue; - } - for (var mid in instances[id].monsters) { - var monster = instances[id].monsters[mid]; - if (monster.level > mmax && !in_arr(monster.type, hunted) && !monster.target) { - // added the target condition [21/07/23] - name = monster.type; - mmax = monster.level; - the_hp = monster.max_hp / 1000.0; - } - } - } - for (var id in G.maps) { - if (G.maps[id].irregular || !G.maps[id].monsters) { - continue; - } - G.maps[id].monsters.forEach(function (p) { - if (p.type == name) { - times += p.count; - } - }); - } - // console.log(times); - count = max(1, min(500, parseInt((20 * 60 * max(1, times)) / the_hp / (G.monsters[name].respawn + 0.25)))); - if (gameplay == "hardcore") { - count = max(1, parseInt(count / 10)); - } - player.s.monsterhunt = { sn: region + " " + server_name, id: name, c: count, ms: 30 * 60 * 1000, dl: true }; - server.s["monsterhunt_" + name] = { name: player.name, id: name, ms: 20 * 60 * 1000, type: "monsterhunt" }; - player.hitchhikers.push(["game_response", "monsterhunt_started"]); - resend(player, "u+cid"); - success_response({ started: true }); - }); - socket.on("ccreport", function () { - socket.emit("ccreport", { calls: socket.calls, climit: limits.calls, total: socket.total_calls }); - }); - socket.on("tracker", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (!player.tracker) { - return; - } - var data = { - monsters: player.p.stats.monsters, - monsters_diff: player.p.stats.monsters_diff, - exchanges: player.p.stats.exchanges, - maps: D.drops.maps, - tables: {}, - max: player.max_stats, - }; // ,computer:false - function register_table(table) { - if (table) { - table.forEach(function (drop) { - if (drop[1] == "open" && !data.tables[drop[2]]) { - data.tables[drop[2]] = D.drops[drop[2]]; - register_table(D.drops[drop[2]]); - } - }); - } - } - if (player.computer || 1) { - // data.computer=true; - data.drops = D.drops.monsters; - } else { - data.drops = {}; - for (var name in data.monsters) { - if (data.monsters[name] >= 100 && D.drops.monsters[name]) { - data.drops[name] = D.drops.monsters[name]; - } - } - } - if (D.drops.maps.global) { - data.global = D.drops.maps.global; - register_table(data.global); - } - if (D.drops.maps.global_static) { - data.global_static = D.drops.maps.global_static; - register_table(data.global_static); - } - for (var name in data.drops) { - register_table(data.drops[name]); - } - for (var name in data.maps) { - register_table(data.maps[name]); - } - for (var name in G.items) { - if ( - G.items[name].e && - (player.computer || - 1 || - (player.p.stats.exchanges && player.p.stats.exchanges[name] && player.p.stats.exchanges[name] >= 100)) - ) { - if (G.items[name].upgrade || G.items[name].compound) { - for (var i = 0; i < 13; i++) { - if (D.drops[name + i]) { - data.tables[name + i] = D.drops[name + i]; - register_table(data.tables[name + i]); - } - } - } else if (D.drops[name]) { - data.tables[name] = D.drops[name]; - register_table(data.tables[name]); - } - } - } - socket.emit("tracker", data); - }); - socket.on("set_home", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (player.p.dt.last_homeset && hsince(player.p.dt.last_homeset) < 36) { - return fail_response("sh_time", { hours: 36 - hsince(player.p.dt.last_homeset) }); - } - player.p.dt.last_homeset = new Date(); - player.p.home = region + server_name; - delete player.s.hopsickness; - success_response("home_set", { home: player.p.home }); - }); - socket.on("code", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data.run) { - players[socket.id].code = true; - } else { - players[socket.id].code = false; - } - resend(players[socket.id], "u+cid"); - }); - socket.on("property", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data.typing) { - if (!player.s.typing || player.s.typing.ms < 3000) { - reduce_call_cost(); - } - player.s.typing = { ms: 4000 }; - resend(player, "u+cid+nc"); - } else { - var original = player.afk; - if (player.afk == "bot" || player.afk == "code") { - } else if (data.afk === true) { - player.afk = true; - } else if (data.afk === false) { - if (player.afk !== undefined) { - player.afk = false; - } - } - if (original !== player.afk) { - reduce_call_cost(); - resend(player, "u+cid+nc"); - } - } - }); - socket.on("cruise", function (speed) { - var player = players[socket.id]; - if (!player) { - return; - } - player.cruise = parseInt(speed); - resend(player, "u+cid"); - success_response("cruise", { speed: player.cruise }); - }); - socket.on("test", function (data) { - if (is_sdk) { - console.log(data.test); - } - socket.emit("test", { date: new Date() }); - }); - socket.on("blocker", function (data) { - if (data.type == "pvp") { - if (instances["arena"].allow) { - socket.emit("blocker", { type: "pvp", allow: 1 }); - } else { - socket.emit("blocker", { type: "pvp" }); - } - } - }); - socket.on("mail_take_item", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (mode.prevent_external) { - return socket.emit("game_response", { response: "not_in_this_server" }); - } - if (!player.esize) { - return socket.emit("game_response", "inv_size"); - } - appengine_call( - "take_item_from_mail", - { owner: player.owner, mid: data.id }, - function (result) { - var player = players[socket.id]; - if (result.failed) { - return socket.emit("game_response", { response: "mail_item_already_taken" }); - } - var item = JSON.parse(result.item); - add_item(player, item, { announce: false }); - resend(player, "reopen"); - socket.emit("game_response", { response: "mail_item_taken" }); - }, - function () { - socket.emit("game_response", { response: "mail_take_item_failed" }); - }, - ); - }); - socket.on("mail", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (mode.prevent_external) { - return fail_response("not_in_this_server"); - } - var item = null; - var retries = 1; - if (player.gold < 48000) { - return fail_response("gold_not_enough"); - } - player.gold -= 48000; - if (data.item && player.items[0] && player.items[0].name != "placeholder") { - if (player.items[0].l) { - return fail_response("item_locked"); - } - if (player.items[0].b || player.items[0].v) { - return fail_response("item_blocked"); - } - if (player.gold < 312000) { - return fail_response("gold_not_enough"); - } - player.gold -= 312000; - item = JSON.stringify(player.items[0]); - player.items[0] = player.citems[0] = null; - retries = 3; - } - appengine_call( - "send_mail", - { - fro: player.name, - to: data.to, - subject: data.subject || "", - message: data.message || "", - rid: randomStr(50), - retries: retries, - item: item, - }, - function (result) { - var player = players[socket.id]; - if (result.failed) { - if (player) { - socket.emit("game_response", { - response: "mail_failed", - to: data.to, - reason: result.reason, - cevent: "mail_failed", - }); - } - if (player && item && result.return && player.esize) { - var r = JSON.parse(item); - add_item(player, r); - resend(player, "reopen"); - } else { - console.log("#M unsent mail, lost item: " + item); - } - return; - } - if (player) { - socket.emit("game_response", { response: "mail_sent", to: data.to, cevent: "mail_sent" }); - } - }, - function () { - var player = players[socket.id]; - if (player) { - socket.emit("game_response", { response: "mail_failed", reason: "coms_failure" }); - } - if (item) { - console.log("#M unsent mail, lost item: " + item); - } - }, - ); - resend(player, "reopen"); - success_response("mail_sending", { sucess: false, in_progress: true, received: "unknown" }); - }); - socket.on("leave", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (!can_walk(player)) { - return fail_response("transport_failed"); - } - if ( - (0 && player.s.block) || - player.targets > 5 || - !(player.map == "jail" || player.map == "cyberland" || instances[player.in].solo) - ) { - return fail_response("cant_escape"); - } - transport_player_to(player, B.start_map); - success_response(); - }); - socket.on("transport", function (data) { - var player = players[socket.id]; - var can_reach = false; - if (!player) { - return; - } // this was missing, probably caused a production exception - should be added everywhere [04/09/16] - if (!can_walk(player) || player.map == "jail") { - return fail_response("transport_failed"); - } - var new_map = G.maps[data.to]; - var s = data.s || 0; - var the_door = null; - if (!new_map || !instances[data.to] || !instances[data.to].allow) { - return fail_response("cant_enter"); - } - if ((0 && player.s.block) || player.targets > 5) { - return fail_response("cant_escape"); - } - - (G.maps[player.map].doors || []).forEach(function (door) { - if ( - !can_reach && - door[4] == data.to && - s == (door[5] || 0) && - simple_distance( - { map: player.map, x: G.maps[player.map].spawns[door[6]][0], y: G.maps[player.map].spawns[door[6]][1] }, - player, - ) < B.door_dist - ) { - can_reach = "door"; - the_door = door; - } - }); - if ((player.map == "woffice" && gameplay == "hardcore") || player.role == "gm") { - can_reach = "transport"; - } - if ( - !can_reach && - G.maps[player.map].ref.transporter && - simple_distance(G.maps[player.map].ref.transporter, player) < B.transporter_dist && - G.npcs.transporter.places[data.to] === s - ) { - can_reach = "transport"; - } - if (can_reach == "transport" && player.s.dampened) { - return fail_response("transport_cant_dampened"); - } - if (!can_reach) { - return fail_response("transport_cant_reach"); - } - if (the_door && the_door[7] == "protected") { - var protected = false; - for (var x in instances[player.in].monsters || {}) { - if (instances[player.in].monsters[x].map_def.gatekeeper) { - protected = true; - } - } - if (protected) { - return fail_response("transport_cant_protection"); - } - } - if ( - the_door && - the_door[7] == "ulocked" && - G.maps[data.to].mount && - player.user && - !player.user.unlocked[data.to] - ) { - return fail_response("transport_cant_locked"); - } - - if (instances[data.to].mount && !player.user) { - if (player.mounting || player.unmounting) { - return fail_response("bank_opi"); - } - add_call_cost(32, undefined, "bank"); - player.mounting = new Date(); - player.mount_to = data.to; - player.mount_s = s; - sync_loop(); - return success_response({ success: false, in_progress: true }); - } else if (!instances[data.to].mount && player.user) { - // previously handled at transport_player_to - if (player.mounting || player.unmounting) { - return fail_response("bank_opi"); - } - add_call_cost(16, undefined, "bank"); - player.unmounting = new Date(); - player.unmount_to = data.to; - player.unmount_s = s; - sync_loop(); - return success_response({ success: false, in_progress: true }); - } else if (0 && instances[data.to].mount && player.user && data.to != player.map) { - // an easy prevention for the bank re-entry nuisance, bank can be re-entered physically but not digitally [03/11/16] - // commented out for bank_u / bank_b [06/05/20] - return fail_response("transport_failed"); - } else { - decay_s(player, 5200); - transport_player_to(player, data.to, s); - return success_response(); - } - }); - socket.on("enter", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (gameplay != "normal") { - return fail_response("transport_failed"); - } - if (!can_walk(player) || player.map == "jail") { - return fail_response("transport_failed"); - } - if (player.s.block || player.targets > 5) { - return fail_response("cant_escape"); - } - // if(player.role!="gm" && !(data.place==player.map && player.map=="cgallery")) - // { - // if(data.place!="resort" && !G.maps[player.map].ref.transporter || simple_distance(G.maps[player.map].ref.transporter,player)>80) return socket.emit("game_response","transport_cant_reach"); - // if(data.place=="resort" && player.map!="resort") return socket.emit("game_response","transport_cant_reach"); - // } - server_log(data); - var name = randomStr(24); - if (data.place == "resort" && 0) { - var name = "resort_" + data.name; - instance = instances[name] || create_instance(name, "resort_map"); - transport_player_to(player, name); - } else if (data.place == "duelland") { - if (instances[data.name] && instances[data.name].map == "duelland") { - transport_player_to(player, data.name); - instance_emit(data.name, "game_log", { message: player.name + " is spectating the duel", color: "gray" }); - } else { - return fail_response("cant_enter"); - } - } else if (data.place == "crypt" || data.place == "winter_instance") { - var f = "cave"; - var ref = G.maps.cave.spawns[2]; - var item = "cryptkey"; - if (data.place == "winter_instance") { - f = "winterland"; - ref = G.maps.winterland.spawns[5]; - item = "frozenkey"; - } - if (simple_distance(player, { in: f, map: f, x: ref[0], y: ref[1] }) > 120) { - return fail_response("transport_cant_reach"); - } - if (data.name && instances[data.name] && instances[data.name].map == data.place) { - transport_player_to(player, data.name); - } else { - if (!consume_one_by_id(player, item)) { - return fail_response("transport_cant_item"); - } - instance = create_instance(name, data.place); - transport_player_to(player, name); - } - resend(player, "u+cid+reopen"); - } else if (data.place == "dungeon0" && player.role == "gm") { - instance = create_instance(name, "dungeon0", { solo: player.id }); - transport_player_to(player, name); - } else if (data.place == "cgallery" && player.role == "gm") { - var point = null; - if (player.map == "cgallery") { - point = [player.x, player.y]; - } - instance = create_instance(name, "cgallery", { solo: player.id }); - transport_player_to(player, name, point); - var bodies = []; - var heads = []; - var hairs = []; - var wings = []; - var hats = []; - for (var s in G.sprites) { - var current = G.sprites[s]; - var matrix = current.matrix; - if (current.skip || current.rskip) { - continue; - } - if (!in_arr(current.type, ["body", "head", "hair", "s_wings", "hat"])) { - continue; - } - for (var i = 0; i < matrix.length; i++) { - for (var j = 0; j < matrix[i].length; j++) { - if (!matrix[i][j]) { - continue; - } - if (current.type == "body") { - bodies.push(matrix[i][j]); - } - if (current.type == "head") { - heads.push(matrix[i][j]); - } - if (current.type == "hair") { - hairs.push(matrix[i][j]); - } - if (current.type == "hat") { - hats.push(matrix[i][j]); - } - if (current.type == "s_wings") { - wings.push(matrix[i][j]); - } - T[matrix[i][j]] = current.type; - } - } - } - var xs = [-80, -40, 0, 40, 80]; - for (var n = 0; n < bodies.length; n++) { - var npc = create_npc( - { - name: bodies[n], - level: 1, - speed: 12, - hp: 1000, - skin: bodies[n], - cx: {}, - }, - { - position: [xs[n % xs.length], -parseInt(n / xs.length) * 40], - id: bodies[n], - }, - instance, - ); - npc.ctype = "body"; - npc.cx = { head: heads[n] }; - npc.id = "body: " + bodies[n]; - instance.players[NPC_prefix + npc.id] = npc; - } - for (var n = 0; n < heads.length; n++) { - var npc = create_npc( - { name: heads[n], level: 1, speed: 12, hp: 1000, skin: bodies[0], cx: {} }, - { position: [xs[n % xs.length] + 240, -parseInt(n / xs.length) * 40], id: heads[n] }, - instance, - ); - npc.ctype = "head"; - npc.cx = { head: heads[n] }; - npc.id = "head: " + heads[n]; - instance.players[NPC_prefix + npc.id] = npc; - } - for (var n = 0; n < hairs.length; n++) { - var npc = create_npc( - { name: hairs[n], level: 1, speed: 12, hp: 1000, skin: bodies[1], cx: {} }, - { position: [xs[n % xs.length] + 480, -parseInt(n / xs.length) * 40], id: hairs[n] }, - instance, - ); - npc.ctype = "hair"; - npc.cx = { head: heads[0], hair: hairs[n] }; - npc.id = "hair: " + hairs[n]; - instance.players[NPC_prefix + npc.id] = npc; - } - for (var n = 0; n < hats.length; n++) { - var npc = create_npc( - { name: hats[n], level: 1, speed: 12, hp: 1000, skin: bodies[2], cx: {} }, - { position: [xs[n % xs.length] + 720, -parseInt(n / xs.length) * 40], id: hats[n] }, - instance, - ); - npc.ctype = "hat"; - npc.cx = { head: heads[0], hat: hats[n] }; - npc.id = "hair: " + hats[n]; - instance.players[NPC_prefix + npc.id] = npc; - } - } else { - return fail_response("transport_cant_reach"); - } - success_response(); - }); - socket.on("town", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - // if(player.last.town && mssince(player.last.town)<1200) return; // bad ui experience [Unknown] - got reported and disabled [25/03/22] - if (!can_walk(player) || player.map == "jail") { - return fail_response("transport_failed"); - } - if ((0 && player.s.block) || player.targets > 5) { - return fail_response("cant_escape"); - } - player.c.town = { ms: min((player.c.town && player.c.town.ms) || 5000, 3000) }; - resend(player, "u+nc"); - success_response({ success: false, in_progress: true }); - }); - socket.on("respawn", function (data) { - var player = players[socket.id]; - if (!player || !player.rip) { - return fail_response("invalid"); - } - if (player.rip_time && ssince(player.rip_time) < B.rip_time) { - return fail_response("cant_respawn"); - } - - delete player.s.block; - player.hp = player.max_hp; - player.mp = round(player.max_mp / 2); - player.rip = false; - if (gameplay == "hardcore") { - reset_player(player, 1); - } - - if (data && data.safe) { - // gameplay=="hardcore" && - transport_player_to(player, "woffice", 0, 1); - } else { - var place = G.maps[player.map].on_death || G.maps[B.start_map].on_death || ["main", 0]; - transport_player_to(player, place[0], place[1], 1); - } - if (player.party) { - send_party_update(player.party); - } - invincible_logic(player); - resend(player, "u+cid"); - success_response("data", { place: "respawn", cevent: "respawn" }); - }); - socket.on("random_look", function (data) { - return socket.emit("game_log", "socket.emit('enter',{place:'cgallery'})"); - var player = players[socket.id]; - if (!player) { - return; - } - var bodies = []; - var heads = []; - var hairs = []; - var wings = []; - var hats = []; - if (player.rlooks == 25 && !is_sdk && player.role != "gm") { - return; - } - if (player.role == "gm") { - reduce_call_cost(40); - } - player.rlooks = (player.rlooks || 0) + 1; - for (var s in G.sprites) { - var current = G.sprites[s]; - var matrix = current.matrix; - if (current.skip || current.rskip) { - continue; - } - if (!in_arr(current.type, ["body", "head", "hair", "s_wings", "hat"])) { - continue; - } - for (var i = 0; i < matrix.length; i++) { - for (var j = 0; j < matrix[i].length; j++) { - if (!matrix[i][j]) { - continue; - } - if (current.type == "body") { - bodies.push(matrix[i][j]); - } - if (current.type == "head") { - heads.push(matrix[i][j]); - } - if (current.type == "hair") { - hairs.push(matrix[i][j]); - } - if (current.type == "hat") { - hats.push(matrix[i][j]); - } - if (current.type == "s_wings") { - wings.push(matrix[i][j]); - } - } - } - } - var head = "head"; - if (Math.random() < 0.1) { - head = random_one(heads); - } - player.tskin = random_one(bodies); - player.tcx = [head, random_one(hairs)]; - if (Math.random() < 0.08) { - player.tcx.push(random_one(wings)); - } - if (Math.random() < 0.4) { - player.tcx.push(random_one(hats)); - } - resend(player, "u+cid"); - }); - socket.on("unlock", function (data) { - return; // [27/06/18] - var player = players[socket.id]; - if (!player) { - return; - } - if (gameplay == "normal") { - return; - } // ? [27/06/18] - if (data.name == "code") { - var item = player.items[data.num || 0]; - if (!item || item.name != "computer" || item.charges === 0 || item.charges < 0 || player.unlocking_code) { - return; - } - if (!item.charges) { - item.charges = 2; - } - item.charges--; - player.citems[data.num || 0] = player.items[data.num || 0]; // explicitly linked to the actual item - player.unlocking_code = true; - appengine_call( - "user_operation", - { auth: player.auth, operation: "code_unlock", suffix: "/code_unlock/" + player.owner, retries: 4 }, - function (result) { - server_log("user_operation_code: " + JSON.stringify(result), 1); - if (result.failed) { - if (result.reason == "already") { - socket.emit( - "game_log", - "Your CODE slots are already unlocked. You can lend your Ancient Computer to a friend in need, that's why there are 2 charges :]", - ); - item.charges++; - } else { - socket.emit("game_log", "Unlock Failed. Email hello@adventure.land with a screenshot."); - } - return; - } - socket.emit("game_log", "CODE slots increased to 100!"); - resend(player, "reopen"); - }, - ); - } - }); - socket.on("dismantle", function (data) { - var player = players[socket.id]; - var check = true; - var items = []; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!player.computer && simple_distance(G.maps.main.ref.craftsman, player) > B.sell_dist) { - return fail_response("distance"); - } - // if(player.esize<=0) return socket.emit("game_response","inventory_full"); - var item = player.items[data.num]; - if (item && item.level && G.items[item.name].compound) { - var cost = min(50000000, calculate_item_value(item) * 10); - if (player.gold < cost) { - return fail_response("gold_not_enough"); - } - if (player.esize < 2) { - return fail_response("inv_size"); - } - if (G.items[item.name].type == "booster") { - return fail_response("dismantle_cant"); - } - player.gold -= cost; - consume_one(player, data.num); - add_item( - player, - { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, - { announce: false }, - ); - add_item( - player, - { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, - { announce: false }, - ); - add_item( - player, - { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, - { announce: false }, - ); - resend(player, "reopen+nc+inv"); - return success_response("dismantle", { name: item.name, level: item.level, cost: cost, cevent: true }); - } - if (!item || !G.dismantle[item.name]) { - return fail_response("dismantle_cant"); - } - if (item.l) { - return fail_response("item_locked"); - } - if (item.b) { - return fail_response("item_blocked"); - } - if (player.gold < G.dismantle[item.name].cost) { - return fail_response("gold_not_enough"); - } - if ( - !can_add_items(player, list_to_pseudo_items(G.dismantle[item.name].items), { - space: ((item.q || 1) == 1 && 1) || 0, - }) - ) { - return fail_response("inv_size"); - } - player.gold -= G.dismantle[item.name].cost; - consume_one(player, data.num); - G.dismantle[item.name].items.forEach(function (e) { - if (e[0] < 1 && Math.random() > e[0]) { - return; - } - add_item(player, e[1], { q: max(1, e[0]), p: item.p && !G.titles[item.p].misc && item.p }); - }); - resend(player, "reopen+nc+inv"); - success_response("dismantle", { name: item.name, cevent: true }); - }); - socket.on("craft", function (data) { - var player = players[socket.id]; - var check = true; - var items = []; - var quantity = {}; - var place = {}; - var space = false; - var p = {}; - var locked = false; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - data.items.forEach(function (x) { - if (!player.items[x[1]]) { - check = false; - } else if (player.items[x[1]].l || player.items[x[1]].b) { - check = false; - locked = true; - } else { - var name = player.items[x[1]].name; - if (player.items[x[1]].level) { - name += "+" + player.items[x[1]].level; - } - items.push(name); - quantity[player.items[x[1]].name] = player.items[x[1]].q || 1; // (quantity[player.items[x[1]].name]||0)+ removed this part, seems like a bad initial addition [17/06/18] - place[player.items[x[1]].name] = x[1]; - if (player.items[x[1]].p && !G.titles[player.items[x[1]].p].misc) { - p[player.items[x[1]].p] = player.items[x[1]].p; - } - } - }); - if (locked) { - return fail_response("item_locked"); - } - if (!check) { - return fail_response("no_item"); - } - //if(items.length<2) return socket.emit("game_response","craft_atleast2"); - items.sort(); - var key = items.join(","); - // console.log(key); - if (!D.craftmap[key]) { - return fail_response("craft_cant"); - } - var name = D.craftmap[key]; - var enough = true; - if ( - !player.computer && - !G.craft[name].quest && - simple_distance(get_npc_coords("craftsman"), player) > B.sell_dist - ) { - return fail_response("distance"); - } - if ( - !player.computer && - G.craft[name].quest && - simple_distance(get_npc_coords(G.craft[name].quest), player) > B.sell_dist - ) { - return fail_response("distance"); - } - if (player.gold < G.craft[name].cost) { - return fail_response("gold_not_enough"); - } - G.craft[name].items.forEach(function (i) { - if (quantity[i[1]] < i[0]) { - enough = false; - } - if (quantity[i[1]] == i[0]) { - space = true; - } - }); - if (!space && !can_add_item(player, create_new_item(name))) { - return fail_response("inventory_full"); - } - if (!enough) { - return fail_response("craft_cant_quantity"); - } - player.gold -= G.craft[name].cost; - G.craft[name].items.forEach(function (x) { - consume(player, place[x[1]], x[0]); - }); - var i = add_item(player, name, { r: 1, p: Object.keys(p).length && random_one(p) }); - resend(player, "reopen+nc+inv"); - success_response("craft", { num: i, name: name, cevent: true }); - }); - socket.on("exchange", function (data) { - var player = players[socket.id]; - var item = player.items[data.item_num]; - var def = G.items[item && item.name]; - var suffix = ""; - if (player.q.exchange) { - return fail_response("exchange_existing"); - } - if (def && (def.compound || def.upgrade)) { - suffix = item.level || 0; - } - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - G.maps.main.exchange.name = player.name; - if (!def || !def.e || item.q != data.q || !D.drops[item.name + suffix]) { - return fail_response("invalid"); - } - if (item.l) { - return fail_response("item_locked"); - } - if (!player.computer && !def.quest && simple_distance(G.maps.main.exchange, player) > B.sell_dist) { - return fail_response("distance"); - } - if (!player.computer && def.quest && simple_distance(G.quests[def.quest], player) > B.sell_dist) { - return fail_response("distance"); - } - if (player.esize <= 0 && !((item.q || 1) == 1)) { - return fail_response("inventory_full"); - } - if (def.e > 1 && item.q < def.e) { - return fail_response("exchange_notenough"); - } - player.p.stats.exchanges[item.name + suffix] = (player.p.stats.exchanges[item.name + suffix] || 0) + 1; - consume(player, data.item_num, def.e); - var num = add_item(player, "placeholder"); - var ms = 3000 + parseInt(Math.random() * 3000); - if (gameplay == "hardcore") { - ms = 400; - } - player.q.exchange = { ms: ms, len: ms, name: item.name, id: item.name + suffix, q: def.e, num: num }; - if (suffix) { - player.q.exchange.s = suffix; - } - if (item.v) { - player.q.exchange.v = item.v; - } - if (def.quest) { - player.q.exchange.qs = def.quest; - } - resend(player, "reopen+nc+inv"); - success_response({ success: false, in_progress: true, num: num }); - }); - socket.on("exchange_buy", function (data) { - //console.log(JSON.stringify(data)); - var player = players[socket.id]; - var item = player.items[data.num]; - var def = G.items[item && item.name]; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!def || def.type != "token") { - return fail_response("invalid"); - } - var npc = G.maps.main.ref[G.items[item.name].npc || item.name + "s"]; - var num = -1; - if (!G.tokens[item.name][data.name]) { - return fail_response("invalid"); - } - if (item.l) { - return fail_response("item_locked"); - } - npc.name = player.name; - if (item.q != data.q) { - return fail_response("safety_check"); - } - if (!player.computer && simple_distance(npc, player) > B.sell_dist) { - return fail_response("distance"); - } - if (item.q < G.tokens[item.name][data.name]) { - return fail_response("exchange_notenough"); - } - - if (G.tokens[item.name][data.name] < 1) { - var q = parseInt(1 / G.tokens[item.name][data.name]); - if (item.q != 1 && !can_add_item(player, { name: data.name, q: q })) { - return fail_response("inventory_full"); - } - consume(player, data.num, 1); - num = add_item(player, data.name, { q: q, announce: false, r: true }); - } else { - var name = data.name; - var idata = undefined; - if (name.search("-") != -1) { - idata = name.split("-")[1]; - name = name.split("-")[0]; - } - var new_item = create_new_item(name); - if (idata) { - new_item.data = idata; - } - if (item.q != G.tokens[item.name][data.name] && !can_add_item(player, new_item)) { - return fail_response("inventory_full"); - } - consume(player, data.num, G.tokens[item.name][data.name]); - num = add_item(player, new_item, { announce: false, r: true }); - } - - xy_emit(npc, "upgrade", { type: item.name + "s", success: 1 }); - resend(player, "reopen+nc+inv"); - success_response({ num: num, cevent: true }); - }); - socket.on("locksmith", function (data) { - var player = players[socket.id]; - var item = player.items[data.item_num]; - var def = G.items[item && item.name]; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!player.computer && simple_distance(G.maps.desertland.ref.locksmith, player) > B.sell_dist) { - return fail_response("distance"); - } - var item = player.items[data.num]; - if (!item) { - return fail_response("no_item"); - } - var def = G.items[item.name]; - if (in_arr(def.type, ["uscroll", "cscroll", "pscroll", "offering", "tome"])) { - return fail_response("locksmith_cant"); - } - if (data.operation == "unlock") { - if (!item.l) { - resend(player, "reopen+nc"); - return fail_response("locksmith_aunlocked", "locksmith", "already_unlocked"); - } - if (item.l == "s") { - if (player.gold < 250000) { - return fail_response("gold_not_enough"); - } - player.gold -= 250000; - item.ld = JSON.stringify(future_s(2 * 24 * 60 * 60)); - item.l = "u"; - socket.emit("game_response", "locksmith_unsealed"); - } else if (item.l == "u") { - var date = new Date(JSON.parse(item.ld)); - if (date < new Date()) { - delete item.l; - socket.emit("game_response", "locksmith_unseal_complete"); - } else { - resend(player, "reopen+nc"); - return success_response("locksmith_unsealing", { hours: -hsince(date), success: false, in_progress: true }); - } - } else { - if (player.gold < 250000) { - return fail_response("gold_not_enough"); - } - player.gold -= 250000; - delete item.l; - socket.emit("game_response", "locksmith_unlocked"); - } - } else if (data.operation == "lock") { - if (item.l) { - resend(player, "reopen+nc"); - return fail_response("locksmith_alocked", "locksmith", "already_locked"); - } - if (player.gold < 250000) { - return fail_response("gold_not_enough"); - } - player.gold -= 250000; - item.l = "l"; - socket.emit("game_response", "locksmith_locked"); - } else if (data.operation == "seal") { - if (player.gold < 250000) { - return fail_response("gold_not_enough"); - } - player.gold -= 250000; - item.l = "s"; - socket.emit("game_response", "locksmith_sealed"); - } - player.citems[data.num] = cache_item(player.items[data.num]); - resend(player, "reopen+nc"); - success_response(); - }); - socket.on("compound", function (data) { - try { - var player = players[socket.id]; - var item0 = player.items[data.items[0]]; - var item1 = player.items[data.items[1]]; - var item2 = player.items[data.items[2]]; - var scroll = player.items[data.scroll_num]; - var result; - var ex = ""; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - G.maps.main.compound.name = player.name; - if (player.q.compound) { - return fail_response("compound_in_progress", "compound", "in_progress"); - } - var offering = player.items[data.offering_num]; - if (offering && G.items[offering.name].type != "offering") { - return socket.emit("game_response", "compound_invalid_offering"); - } - if (!item0 || (item0.level || 0) != data.clevel) { - return fail_response("no_item"); - } - if (!player.computer && simple_distance(G.maps.main.compound, player) > B.sell_dist) { - return socket.emit("game_response", { response: "distance", place: "compound", failed: true }); - } - var def = G.items[item0.name]; - var scroll_def = G.items[scroll.name]; - var offering_def = offering && G.items[offering.name]; - var grade = calculate_item_grade(def, item0); - if (grade == 4) { - return socket.emit("game_response", { - response: "max_level", - level: item.level, - place: "compound", - failed: true, - }); - } - if ( - !( - item0.name == item1.name && - item1.name == item2.name && - (item0.level || 0) == (item1.level || 0) && - (item1.level || 0) == (item2.level || 0) - ) - ) { - return socket.emit("game_response", "compound_mismatch"); - } - if (!def.compound) { - return socket.emit("game_response", "compound_cant"); - } - if (scroll_def.type != "cscroll" || grade > scroll_def.grade) { - return socket.emit("game_response", "compound_incompatible_scroll"); - } - if (data.items[0] == data.items[1] || data.items[1] == data.items[2] || data.items[0] == data.items[2]) { - return socket.emit("game_response", { response: "misc_fail", place: "compound", failed: true }); - } - if (item0.l || item1.l || item2.l) { - return socket.emit("game_response", { response: "item_locked", place: "compound", failed: true }); - } - - if (!data.calculate) { - consume_one(player, data.scroll_num); - } - - var new_level = (item0.level || 0) + 1; - var probability = 1; - var oprobability = 1; - var result = 0; - var proc = 0; - var grace = 0; - var igrade = def.igrade; - var high = false; - var grace_bonus = 0; - if (item0.level >= 3) { - igrade = calculate_item_grade(def, { name: item0.name, level: item0.level - 2 }); - } - - delete player.p.c_item; - delete player.p.c_itemx; - delete player.p.c_roll; - - oprobability = probability = D.compounds[igrade][new_level]; - result = Math.random(); - server_log(result + " < " + probability); - - if (scroll_def.grade > grade) { - probability = probability * 1.1 + 0.001; - server_log("higher grade scroll " + result + " < " + probability); - if (!data.calculate) { - grace_bonus += 0.4; - } - high = scroll_def.grade - grade; - } - - if (offering) { - var increase = 0.5; - if (!data.calculate) { - consume_one(player, data.offering_num); - } - grace = 0.027 * ((item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0) + 0.5 + player.p.ograce); - - if (offering_def.grade > grade + 1) { - probability = probability * 1.64 + grace * 2; - high = true; - increase = 3; - } else if (offering_def.grade > grade) { - probability = probability * 1.48 + grace; - high = true; - increase = 1; - } else if (offering_def.grade == grade) { - probability = probability * 1.36 + min(30 * 0.027, grace); - } else if (offering_def.grade == grade - 1) { - probability = probability * 1.15 + min(25 * 0.019, grace) / max(item0.level - 2, 1); - increase = 0.2; - } else { - probability = probability * 1.08 + min(15 * 0.015, grace) / max(item0.level - 1, 1); - increase = 0.1; - } - - if (!data.calculate) { - item0.grace = (item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0); - grace_bonus += increase; - } - server_log("offering " + result + " < " + probability); - } else { - grace = 0.007 * ((item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0) + player.p.ograce); - probability = probability + min(25 * 0.007, grace) / max(item0.level - 1, 1); - if (!data.calculate) { - item0.grace = max(max(item0.grace || 0, item1.grace || 0), item2.grace || 0); - } - } - - if (!data.calculate) { - item0.grace = item0.grace / 6.4 + grace_bonus; - } - - if (def.type == "booster") { - probability = 0.9999999999999; - proc = offering && 0.12; - } else { - probability = min( - probability, - min(oprobability * (3 + ((high && high * 0.6) || 0)), oprobability + 0.2 + ((high && high * 0.05) || 0)), - ); - } - - if (gameplay == "test") { - result = 0; - } - - server_log(result + " < " + probability + " grace: " + grace); - - if (data.calculate) { - return success_response("compound_chance", { - calculate: true, - chance: probability, - item: cache_item(item0), - scroll: scroll.name, - offering: (offering && offering.name) || undefined, - grace: (item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0), - }); - } - - player.p.c_roll = result; - var len = 10000; - if (gameplay == "hardcore") { - len = 1200; - } - if (player.s.massproduction) { - len /= 2; - delete player.s.massproduction; - ex = "+u+cid"; - } - if (player.s.massproductionpp) { - len /= 10; - delete player.s.massproductionpp; - ex = "+u+cid"; - } - player.q.compound = { ms: len, len: len, num: data.items[0], nums: [] }; - player.items[data.items[0]] = { - name: "placeholder", - p: { - chance: probability, - name: item0.name, - level: item0.level, - scroll: scroll.name, - offering: offering && offering.name, - nums: [], - }, - }; - player.items[data.items[1]] = null; - player.items[data.items[2]] = null; - - if (result <= probability) { - if (offering) { - player.p.ograce = 0; - } else { - player.p.ograce *= 1 - new_level * 0.02; - } - item0.level = new_level; - if ((item0.p || item1.p || item2.p) != "legacy") { - item0.p = item0.p || item1.p || item2.p; - } else { - delete item0.p; - } - player.p.c_item = item0; - if (item0.oo != player.name) { - item0.o = player.name; - } - player.esize += 2; - if (parseInt(result * 10000) == parseInt(probability * 10000)) { - // && grade>=1 - add_achievement(player, "lucky"); - add_item_property(item0, "lucky"); - } - if (item1.v || item2.v) { - item0.v = item1.v || item2.v; - } - if (item0.name == "ctristone" && item0.level == 1 && Math.random() < 0.012) { - item0.name = "cdarktristone"; - } - if (def.type == "booster") { - var activate = false; - item0.extra = 0; - while (offering && proc && Math.random() < proc) { - item0.extra++; - item0.level += 1; - proc /= 2.0; - } - var seconds = 6 * 24 * 60 * 60; - [item0, item1, item2].forEach(function (i) { - if (i.expires) { - seconds -= ssince(i.expires); - activate = true; - } else { - seconds += def.days * 24 * 60 * 60; - } - }); - if (activate) { - item0.expires = future_s(seconds / 3); - } - } - } else { - if (offering) { - player.p.ograce += 0.4; - } - player.esize += 2; - player.p.c_item = null; - player.p.c_itemx = item0; - } - - player.citems[data.items[0]] = cache_item(player.items[data.items[0]]); - player.citems[data.items[1]] = cache_item(player.items[data.items[1]]); - player.citems[data.items[2]] = cache_item(player.items[data.items[2]]); - - resend(player, "reopen+nc+inv" + ex); - } catch (e) { - server_log("compound_e " + e); - return socket.emit("game_response", { response: "exception", place: "compound", failed: true }); - } - }); - socket.on("upgrade", function (data) { - try { - var player = players[socket.id]; - var item = player.items[data.item_num]; - var scroll = player.items[data.scroll_num]; - var offering = player.items[data.offering_num]; - var result; - var ex = ""; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (player.q.upgrade) { - return socket.emit("game_response", "upgrade_in_progress"); - } - G.maps.main.upgrade.name = player.name; - if (!player.computer && simple_distance(G.maps.main.upgrade, player) > B.sell_dist) { - return socket.emit("game_response", { response: "distance", place: "upgrade", failed: true }); - } - if (!item) { - return socket.emit("game_response", "upgrade_no_item"); - } - if ( - offering && - !( - (G.items[offering.name] && G.items[offering.name].type == "offering") || - (G.items[offering.name] && G.items[offering.name].offering !== undefined && !(item.level > 0)) - ) - ) { - return socket.emit("game_response", "upgrade_invalid_offering"); - } - if (!scroll && !offering) { - return socket.emit("game_response", "upgrade_no_scroll"); - } - if ((item.level || 0) != data.clevel) { - return socket.emit("game_response", "upgrade_mismatch"); - } - var item_def = G.items[item.name]; - var scroll_def = scroll && G.items[scroll.name]; - var offering_def = offering && G.items[offering.name]; - var grade = calculate_item_grade(item_def, item); - if (!item_def.upgrade) { - return socket.emit("game_response", "upgrade_cant"); - } - if ( - scroll && - (!in_arr(scroll_def.type, ["uscroll", "pscroll"]) || - (scroll_def.type == "uscroll" && !item_def.upgrade) || - (scroll_def.type == "pscroll" && !item_def.stat) || - grade > scroll_def.grade) - ) { - if (grade == 4 && scroll_def.type == "uscroll") { - return socket.emit("game_response", { - response: "max_level", - level: item.level, - place: "upgrade", - failed: true, - }); - } - return socket.emit("game_response", "upgrade_incompatible_scroll"); - } - - var new_level = (item.level || 0) + 1; - var probability = 1; - var oprobability = 1; - var grace = 0; - var high = false; - var ograde = calculate_item_grade(item_def, { name: item.name, level: 0 }); - var tmult = 1; - if (ograde == 1) { - tmult = 1.5; - } else if (ograde == 2) { - tmult = 2; - } - - delete player.p.u_item; - delete player.p.u_type; - delete player.p.u_itemx; - delete player.p.u_roll; - delete player.p.u_fail; - delete player.p.u_level; - - player.p.u_level = item.level || 0; - - if (!scroll) { - if (G.items[offering.name] && G.items[offering.name].offering !== undefined) { - var chance = 0.16; - var ms = 2000; - if ( - G.items[offering.name].offering > ograde || - (G.items[offering.name].offering == 2 && calculate_item_value(item) <= 20000000) - ) { - chance = 0.32; - } - chance *= [2.8, 1.6, 1][ograde]; - if (G.items[offering.name].offering < ograde) { - return socket.emit("game_response", "upgrade_invalid_offering"); - } - if (data.calculate) { - return success_response("upgrade_chance", { - calculate: true, - chance: chance, - offering: offering.name, - item: cache_item(item), - grace: item.grace || 0, - }); - } - var result = Math.random(); - - consume_one(player, data.offering_num); - player.p.u_type = "normal"; - player.p.u_roll = result; - if (player.s.massproduction) { - ms /= 2; - delete player.s.massproduction; - ex = "+u+cid"; - } - if (player.s.massproductionpp) { - ms /= 10; - delete player.s.massproductionpp; - ex = "+u+cid"; - } - player.q.upgrade = { ms: ms, len: ms, num: data.item_num, silent: true }; - player.items[data.item_num] = { - name: "placeholder", - p: { - chance: chance, - name: item.name, - level: item.level, - scroll: null, - offering: offering.name, - nums: [], - }, - }; - - if (result <= chance) { - item.p = "shiny"; - player.p.u_item = item; - } else { - player.p.u_item = item; - player.p.u_fail = true; - } - } else { - var ms = 1000; - if (data.calculate) { - return success_response("upgrade_chance", { - calculate: true, - chance: 1, - offering: offering.name, - item: cache_item(item), - grace: item.grace || 0, - }); - } - consume_one(player, data.offering_num); - item.grace = (item.grace || 0) + 0.5; - server_log("item.grace: " + item.grace); - player.p.u_type = "offering"; - player.p.u_item = item; - player.p.u_roll = 0.999999999999999; - if (player.s.massproduction) { - ms /= 2; - delete player.s.massproduction; - ex = "+u+cid"; - } - if (player.s.massproductionpp) { - ms /= 10; - delete player.s.massproductionpp; - ex = "+u+cid"; - } - player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; - player.items[data.item_num] = { - name: "placeholder", - p: { chance: 1, name: item.name, level: item.level, scroll: null, offering: offering.name, nums: [] }, - }; - } - } else if (scroll_def.type == "uscroll") { - if (item.l) { - return socket.emit("game_response", { response: "item_locked", place: "upgrade", failed: true }); - } - if (grade == 4) { - return socket.emit("game_response", { - response: "max_level", - level: item.level, - place: "upgrade", - failed: true, - }); - } - if (!data.calculate) { - consume_one(player, data.scroll_num); - } - oprobability = probability = D.upgrades[item_def.igrade][new_level]; - // grace=max(0,min(new_level+1, (item.grace||0) + min(3,player.p.ugrace[new_level]/3.0) +min(2,ugrace[new_level]/4.0) +item_def.igrace + player.p.ograce/2.0 )); - original [16/07/18] - grace = max( - 0, - min(new_level + 1, (item.grace || 0) + min(3, player.p.ugrace[new_level] / 4.5) + item_def.igrace) + - min(6, S.ugrace[new_level] / 3.0) + - player.p.ograce / 3.2, - ); - server_log( - "Grace num: " + - grace + - "\nItem: " + - (item.grace || 0) + - "\nPlayer: " + - min(3, player.p.ugrace[new_level] / 4.5) + - "\nDef: " + - item_def.igrace + - "\nOgrace:" + - player.p.ograce / 3.2 + - "\nS.ugrace: " + - min(6, S.ugrace[new_level] / 3.0), - ); - grace = (probability * grace) / new_level + grace / 1000.0; - server_log("Grace-prob: " + grace); - result = Math.random(); - server_log(result + " < " + probability); - - // if(!data.calculate && item.name=="throwingstars" && scroll_def.grade==2 && item.level==4) item.p="superfast"; - - if (scroll_def.grade > grade && new_level <= 10) { - probability = probability * 1.2 + 0.01; - high = true; - if (!data.calculate) { - item.grace = (item.grace || 0) + 0.4; - } - } - - if (offering) { - var increase = 0.4; - if (!data.calculate) { - consume_one(player, data.offering_num); - } - - if (offering_def.grade > grade + 1) { - probability = probability * 1.7 + grace * 4; - high = true; - increase = 3; - } else if (offering_def.grade > grade) { - probability = probability * 1.5 + grace * 1.2; - high = true; - increase = 1; - } else if (offering_def.grade == grade) { - probability = probability * 1.4 + grace; - } else if (offering_def.grade == grade - 1) { - probability = probability * 1.15 + grace / 3.2; - increase = 0.2; - } else { - probability = probability * 1.08 + grace / 4; - increase = 0.1; - } - - if (!data.calculate) { - item.grace = (item.grace || 0) + increase; - } // previously +1 [16/07/18] - } else { - grace = max(0, grace / 4.8 - 0.4 / ((new_level - 0.999) * (new_level - 0.999))); - probability += grace; // previously 12.0 // previously 9.0 [16/07/18] - } - - if (!data.calculate && Math.random() < 0.025) { - // Bonus grace - item.grace = (item.grace || 0) + 1; - } - - if (data.item_num == player.p.item_num && Math.random() < 0.6) { - // Added [29/10/17] - server_log("16 cheat"); - result = max(Math.random() / 10000.0, result * 0.975 - 0.012); - } - - if (high) { - probability = min(probability, min(oprobability + 0.36, oprobability * 3)); - } else { - probability = min(probability, min(oprobability + 0.24, oprobability * 2)); - } - server_log(result + " < " + probability + " grace: " + grace); - - if (data.calculate) { - return success_response("upgrade_chance", { - calculate: true, - chance: probability, - offering: (offering && offering.name) || undefined, - item: cache_item(item), - grace: item.grace || 0, - scroll: scroll.name, - }); - } - - if (gameplay == "test") { - result = 0; - } - // result=probability; - player.p.u_type = "normal"; - player.p.u_roll = result; - var ms = 500 * new_level * Math.sqrt(new_level) * tmult; - if (player.s.massproduction) { - ms /= 2; - delete player.s.massproduction; - ex = "+u+cid"; - } - if (player.s.massproductionpp) { - ms /= 10; - delete player.s.massproductionpp; - ex = "+u+cid"; - } - player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; - if (gameplay == "hardcore") { - player.q.upgrade.ms = player.q.upgrade.len = 500; - } - player.items[data.item_num] = { - name: "placeholder", - p: { - chance: probability, - name: item.name, - level: item.level, - scroll: scroll.name, - offering: offering && offering.name, - nums: [], - }, - }; - - //result=probability+EPS; - - if (result <= probability) { - // console.log("here"); - player.p.ugrace[new_level] = S.ugrace[new_level] = 0; - if (offering) { - player.p.ograce *= 0.25; - } else { - player.p.ograce *= 1 - new_level * 0.005; - } - item.level = new_level; - if (item.oo != player.name) { - item.o = player.name; - } - player.p.u_item = item; - if (parseInt(result * 10000) == parseInt(probability * 10000) && grade >= 1) { - add_item_property(item, "lucky"); - add_achievement(player, "lucky"); - } - } else { - player.p.ugrace[new_level - 1] += 1; - S.ugrace[new_level - 1] += 1; - player.p.ugrace[new_level] += 1; - S.ugrace[new_level] += 1; - if (new_level >= 8 && new_level <= 15) { - player.p.ugrace[new_level - 1] += 1; - S.ugrace[new_level - 1] += 1; - player.p.ugrace[new_level - 2] += 2 + ((offering && 1) || 0); - S.ugrace[new_level - 2] += 2; - player.p.ugrace[new_level - 3] += 2 + ((offering && 2) || 0); - S.ugrace[new_level - 3] += 3 + ((offering && 1) || 0); - } - if (offering) { - player.p.ograce += 0.6; - } // previously 1 [16/07/18] - if (scroll_def.grade != 3.6) { - player.p.u_itemx = item; - } else { - player.p.u_item = item; - player.p.u_fail = true; - } - if (parseInt(result * 10000) == parseInt(probability * 10000)) { - if (player.esize && grade >= 1) { - add_item(player, "essenceofgreed"); - } - add_achievement(player, "unlucky"); - } - } - } else if (scroll_def.type == "pscroll") { - var needed = [1, 10, 100, 1000, 9999, 9999, 9999]; - if (scroll.q < needed[grade]) { - return socket.emit("game_response", { response: "upgrade_scroll_q", q: needed[grade], h: scroll.q }); - } - if (!data.calculate) { - consume(player, data.scroll_num, needed[grade]); - } - - if (data.calculate) { - return success_response("upgrade_chance", { - calculate: true, - chance: 0.99999, - scroll: scroll.name, - item: cache_item(item), - grace: item.grace || 0, - }); - } - - if (offering) { - consume_one(player, data.offering_num); - item.grace = (item.grace || 0) + 1; - server_log("Graced up to " + item.grace); - } - - item.stat_type = scroll_def.stat; - player.p.u_roll = Math.random(); - - player.p.u_type = "stat"; - if (player.p.u_roll <= 0.99999 || offering) { - player.p.u_item = item; - } else { - player.p.u_itemx = item; - } - var ms = 2000 * tmult * tmult; - if (player.s.massproduction) { - ms /= 2; - delete player.s.massproduction; - ex = "+u+cid"; - } - if (player.s.massproductionpp) { - ms /= 10; - delete player.s.massproductionpp; - ex = "+u+cid"; - } - player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; - player.items[data.item_num] = { - name: "placeholder", - p: { - chance: 0.99999, - name: item.name, - level: item.level, - scroll: scroll.name, - offering: offering && offering.name, - nums: [], - }, - }; - } - - player.citems[data.item_num] = cache_item(player.items[data.item_num]); - - resend(player, "reopen+nc+inv" + ex); - } catch (e) { - server_log("upgrade_e " + e); - return socket.emit("game_response", { response: "exception", place: "upgrade", failed: true }); - } - }); - socket.on("equip", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - player.c = {}; - data.num = to_number(data.num); - if (data.num >= player.items.length) { - return fail_response("invalid"); - } - var item = player.items[data.num]; - if (!item) { - return fail_response("no_item"); - } - if (item.b) { - return fail_response("item_blocked"); - } - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - var def = G.items[item.name]; - var to_update = "reopen+u+cid"; - var resolve = { num: data.num }; - var resolve_type = "data"; - def.iname = item.name; //just for orb name checks [08/10/16] - if (!def) { - return fail_response("no_item"); - } - // if(is_sdk) server_log("Trying to equip "+JSON.stringify(data)); - - if (data.slot && get_trade_slots(player).includes(data.slot) && !data.consume) { - if (item.acl || item.v) { - return fail_response("item_locked"); - } - var slot = data.slot; - var price = round(min(99999999999, max(parseInt(data.price) || 1, 1))); - var minutes = 1; - data.q = max(1, parseInt(data.q) || 1); - if ((item.q || 1) < data.q) { - return fail_response("not_enough"); - } - if (data.giveaway) { - minutes = max( - 5, - min(600, min(parseInt(data.minutes) || 5, max(60, round((calculate_item_value(item) * data.q) / 40000)))), - ); - } - if (!price || data.giveaway) { - price = 1; - } - if (player.slots[slot]) { - return fail_response("slot_occuppied"); - } - if (def.s) { - player.slots[slot] = create_new_item(player.items[data.num].name, 1); - player.slots[slot].price = price; - player.slots[slot].q = data.q; - player.slots[slot].rid = randomStr(4); - if (player.items[data.num].data) { - player.slots[slot].data = player.items[data.num].data; - } - if (data.giveaway) { - player.slots[slot].giveaway = minutes; - player.slots[slot].list = []; - player.giveaway = true; - } - player.cslots[slot] = cache_item(player.slots[slot], true); - consume(player, data.num, data.q); - if (data.giveaway) { - socket.emit("game_log", "Listed " + data.q + " " + item_name(player.slots[slot]) + " to giveaway!"); - } else { - socket.emit( - "game_log", - "Listed " + data.q + " " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold each", - ); - } - } else { - player.items[data.num].price = price; - player.items[data.num].rid = randomStr(4); - player.slots[slot] = player.items[data.num]; - if (data.giveaway) { - player.slots[slot].giveaway = minutes; - player.slots[slot].list = []; - player.giveaway = true; - } - player.cslots[slot] = cache_item(player.slots[slot], true); - player.items[data.num] = null; - player.citems[data.num] = null; - if (data.giveaway) { - socket.emit("game_log", "Listed " + item_name(player.slots[slot]) + " to giveaway!"); - } else { - socket.emit( - "game_log", - "Listed " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold", - ); - } - } - resolve.slot = slot; - } else if (def.type == "elixir") { - if (item.l) { - return fail_response("item_locked"); - } - if (player.slots.elixir && G.items[player.slots.elixir.name].withdrawal) { - add_condition(player, G.items[player.slots.elixir.name].withdrawal); - } - if (player.slots.elixir && player.slots.elixir.name == item.name) { - player.slots.elixir.expires = future_s(def.duration * 60 * 60 - ssince(player.slots.elixir.expires) * 0.975); - } else { - player.slots.elixir = { name: item.name, expires: future_s(def.duration * 60 * 60), ex: true }; - } - if (G.items[item.name] && G.items[item.name].withdrawal && player.s.withdrawal) { - delete player.s.withdrawal; - } - player.cslots.elixir = cache_item(player.slots.elixir); - consume_one(player, data.num); - resolve_type = "elixir"; - } else if (item.name == "cxjar") { - if (!item.data || item.l) { - return fail_response("item_locked"); - } - player.p.acx[item.data] = (player.p.acx[item.data] || 0) + 1; - socket.emit("game_response", { response: "cx_new", acx: player.p.acx, name: item.data, from: "cxjar" }); - consume_one(player, data.num); - } else if (item.name == "emotionjar") { - if (!item.data || item.l) { - return fail_response("item_locked"); - } - player.p.emx[item.data] = (player.p.emx[item.data] || 0) + 1; - socket.emit("game_response", { - response: "emotion_new", - emx: player.p.emx, - name: item.data, - from: "emotionjar", - }); - consume_one(player, data.num); - } else if (def.type == "licence") { - if (item.l) { - return fail_response("item_locked"); - } - player.s.licenced = { ms: ((player.s.licenced && player.s.licenced.ms) || 0) + 7 * 60 * 1000 }; - consume_one(player, data.num); - } else if (def.type == "spawner") { - new_monster(player.in, { type: def.spawn, stype: "trap", x: player.x, y: player.y, owner: player.name }); - consume_one(player, data.num); - } else if (def.gives) { - if (player.last.potion && mssince(player.last.potion) < 0) { - return fail_response("not_ready"); - } - if (item.l) { - return fail_response("item_locked"); - } - var timeout = 2000; - var timeout_ui = null; - var xp = false; - consume_one(player, data.num); - (def.gives || []).forEach(function (p) { - var amount = p[1]; - if (player.s.poisoned) { - amount = round(amount / 2); - } - player[p[0]] = (player[p[0]] || 0) + amount; - player[p[0]] = max(1, player[p[0]]); - if (p[0] == "hp") { - if (amount > 0) { - disappearing_text(socket, player, "+" + amount, { color: "hp", xy: 1, s: "hp" }); - } else { - disappearing_text(socket, player, amount, { color: "hp", xy: 1, s: "hp" }); - } - } - if (p[0] == "mp") { - disappearing_text(socket, player, "+" + amount, { color: "mp", xy: 1, s: "mp" }); - } - if (p[0] == "xp") { - disappearing_text(socket, player, "+1M", { color: "1mxp", xy: 1, s: "xp", size: "large" }); - xp = true; - timeout_ui = 120; - timeout = 0.1; - } - }); - if (def.debuff) { - for (var c in player.s) { - if (G.conditions[c] && G.conditions[c].debuff && (!G.conditions[c].persistent || c == "hopsickness")) { - delete player.s[c]; - } - } - } - player.hp = min(player.hp, player.max_hp); - player.mp = min(player.mp, player.max_mp); - if (def.cooldown) { - timeout = def.cooldown; - } - player.last.potion = future_ms(timeout); - if (!xp) { - to_update = "u+cid+reopen+nc"; - } - socket.emit("eval", { code: "pot_timeout(" + (timeout_ui || timeout) + ")" }); - } else if (!data.consume) { - // console.log(data); - var slot = data.slot || def.type; - var existing; - var comp = can_equip_item(player, def, slot); - if (comp == "no") { - resend(player, "u+cid+reopen"); // if you drop something on an invalid slot, it stays there for a bit otherwise [29/08/22] - return fail_response("cant_equip"); - } - slot = comp; - existing = player.slots[slot]; - player.slots[slot] = player.items[data.num]; - player.cslots[slot] = cache_item(player.slots[slot]); - if (existing && existing.b) { - existing = null; - } - player.items[data.num] = existing; - player.citems[data.num] = cache_item(existing); - player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 120, 120000) }; - resolve.slot = slot; - } else { - return fail_response("cant_consume"); - } - - if (to_update) { - resend(player, to_update); - } - success_response(resolve_type, resolve); - }); - socket.on("misc_npc", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - var npc = players[data.npc]; - if (!npc || !npc.npc) { - return; - } - if (simple_distance(player, npc) > 1000) { - return socket.emit("game_response", "distance"); - } - if (data.npc == NPC_prefix + "Marven" && npc.misc == true) { - server_log("Here!"); - } - }); - socket.on("unequip", function (data) { - var player = players[socket.id]; - if (!player || !data.slot || !player.slots[data.slot]) { - return fail_response("invalid"); - } - if (data.slot == "elixir") { - return fail_response("cant"); - } - var item = player.slots[data.slot]; - var done = false; - if (in_arr(data.slot, trade_slots) && item.giveaway !== undefined) { - return fail_response("giveaway"); - } - // an oversight allowed .giveaway items to be equipped, so now they can be unequipped from regular slots [04/11/21] - if (player.esize <= 0 && !item.b) { - return fail_response("no_space"); - } - player.slots[data.slot] = null; - player.cslots[data.slot] = null; - player.c = {}; - if (!item.b) { - add_item(player, item, { announce: false }); - } - resend(player, "reopen+u+cid"); - success_response("data"); - }); - socket.on("secondhands", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (simple_distance(G.maps.main.ref.secondhands, player) > 500) { - return socket.emit("game_response", "distance"); - } - socket.emit("secondhands", csold); - }); - socket.on("lostandfound", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data == "info") { - return socket.emit("game_response", { response: "lostandfound_info", gold: S.gold }); - } - if (!player.donation) { - return socket.emit("game_response", "lostandfound_donate"); - } - if (simple_distance(G.maps.woffice.ref.lostandfound, player) > 500) { - return socket.emit("game_response", "distance"); - } - socket.emit("lostandfound", cfound); - }); - socket.on("split", function (data) { - var player = players[socket.id]; - var num = data.num; - var item = player.items[data.num]; - var quantity = min(max(parseInt(data.quantity) || 0, 1), (item && item.q) || 1); - if (!item) { - return fail_response("no_item"); - } - if (!G.items[item.name].s) { - return fail_response("invalid"); - } - quantity = min(quantity, G.items[item.name].s || 1); - if (!player.esize) { - return fail_response("cant_space"); - } - if (item.q == quantity) { - return success_response(); - } - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (item.l) { - return fail_response("item_locked"); - } - if (item.b) { - return fail_response("item_blocked"); - } - - consume(player, data.num, quantity); - - var new_item = cache_item(item); - if (item.q) { - new_item.q = quantity; - } - - for (var i = 0; i < player.isize; i++) { - if (!player.items[i]) { - player.items[i] = new_item; - player.citems[i] = cache_item(new_item); - break; - } - } - resend(player, "reopen"); - success_response(); - }); - socket.on("sell", function (data) { - var player = players[socket.id]; - var num = data.num; - var item = player.items[data.num]; - var quantity = min(max(parseInt(data.quantity) || 0, 1), (item && item.q) || 1); - var can_reach = false; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!item) { - return fail_response("no_item"); - } - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (item.l) { - return fail_response("item_locked"); - } - if (item.b) { - return fail_response("item_blocked"); - } - (G.maps[player.map].merchants || []).forEach(function (m) { - if (simple_distance(player, m) < B.sell_dist) { - can_reach = m; - } - }); - if (player.computer && !can_reach) { - can_reach = G.maps.main.merchants[0]; - } - if (!can_reach) { - return fail_response("distance"); - } - can_reach.name = player.name; - var value = calculate_item_value(item); - consume(player, data.num, quantity); - player.gold += value * quantity; - var new_item = cache_item(item); - if (item.q) { - new_item.q = quantity; - } - xy_emit(can_reach, "ui", { - type: "-$", - id: can_reach.id, - name: player.name, - item: new_item, - num: num, - event: "sell", - }); - resend(player, "reopen+nc+inv"); - success_response("gold_received", { gold: value * quantity, item: new_item, cevent: "sell" }); - secondhands_logic(item, quantity); // In the end, so if this fails, the "sell" still succeeds - otherwise it could cause a infinite gold loophole [10/07/18] - }); - socket.on("buy_shells", function (data) { - return socket.emit("game_log", "No longer possible"); - var player = players[socket.id]; - if (!player || player.user || gameplay == "hardcore" || gameplay == "test") { - return game_response("cant_in_bank"); - } - var gold = parseInt(data.gold) || 0; - if (gold < 1000000 || gold > player.gold) { - return socket.emit("game_response", "not_enough_gold"); - } - var shells = Math.floor(gold / G.multipliers.shells_to_gold); - player.gold -= gold; - socket.emit("game_log", "Gave " + to_pretty_num(gold) + " gold"); - appengine_call( - "bill_user", - { auth: player.auth, amount: -shells, reason: "buy_shells", name: player.name }, - function (result) { - server_log("buy_with_cash: " + JSON.stringify(result)); - if (result.failed || !result.done) { - socket.emit("game_log", "Purchase failed"); - return; - } - player.cash = result.cash; - socket.emit("game_log", "Received " + to_pretty_num(shells) + " shells"); - - resend(player, "reopen+nc"); - }, - ); - resend(player, "reopen+nc"); - }); - socket.on("buy_with_cash", function (data) { - var player = players[socket.id]; - var name = data.name; - var def = G.items[data.name]; - var quantity = min(max(parseInt(data.quantity) || 0, 1), (def && def.s) || 9999); - if (!player || player.user || gameplay == "hardcore" || gameplay == "test") { - return fail_response("cant_in_bank"); - } - var cost = def.cash * quantity; - if (!def.cash || def.ignore) { - return fail_response("invalid"); - } - if (def.p2w) { - return fail_response("invalid"); - } - if (!def.s) { - quantity = 1; - } - if (!can_add_item(player, create_new_item(name, quantity))) { - return fail_response("no_space"); - } - appengine_call( - "bill_user", - { auth: player.auth, amount: cost, reason: data.name, name: player.name }, - function (result) { - server_log("buy_with_cash: " + JSON.stringify(result)); - if (result.failed || !result.done) { - socket.emit("game_log", "Purchase failed"); - return; - } - player.cash = result.cash; - var new_item = create_new_item(name, quantity); - var done = false; - add_item(player, new_item, { announce: false }); - socket.emit("game_log", "Spent " + to_pretty_num(cost) + " shells"); - - resend(player, "reopen+nc+inv"); - }, - ); - success_response({ success: false, in_progress: true }); - }); - socket.on("sbuy", function (data) { - var player = players[socket.id]; - var done = false; - var npc = G.maps.main.ref.secondhands; - var c = S.sold; - var cc = csold; - var e = "+$p"; - var ev = "secondhands"; - var mult = 2; - var src = "scnd"; - if (data.f) { - npc = G.maps.woffice.ref.lostandfound; - c = S.found; - cc = cfound; - e = "+$f"; - ev = "lostandfound"; - mult = 4; - src = "lost"; - } - if (!player || player.user) { - return game_response("cant_in_bank"); - } - if (player.s.hopsickness && ev == "lostandfound") { - return fail_response("cant_when_sick", { goblin: true }); - } - if (simple_distance(npc, player) > 500) { - return socket.emit("game_response", "distance"); - } - for (var i = 0; i < c.length; i++) { - if (c[i].rid == data.rid) { - if (mult == 2 && G.items[c[i].name].cash) { - mult = 3; - } - var gold = calculate_item_value(c[i]) * mult * (c[i].q || 1); - var item = c[i]; - if (!can_add_item(player, c[i])) { - return disappearing_text(socket, player, "NO SPACE"); - } - if (gold > player.gold) { - return socket.emit("game_response", "buy_cost"); - } - player.gold -= gold; - item.src = src; - add_item(player, c[i], { announce: false }); - c.splice(i, 1); - cc.splice(i, 1); - socket.emit("game_log", "Spent " + to_pretty_num(gold) + " gold"); - resend(player, "reopen+nc+inv"); - socket.emit(ev, cc); - done = true; - xy_emit(npc, "ui", { type: e, id: npc.id, name: player.name, item: cache_item(item, 1) }); - break; - } - } - if (!done) { - return socket.emit("game_log", "Item gone"); - } - }); - socket.on("buy", function (data) { - var player = players[socket.id]; - var can_reach = false; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - var name = data.name; - var quantity = min(max(parseInt(data.quantity) || 0, 1), (G.items[name] && G.items[name].s) || 9999); - var cost = 0; - var added = false; - var done = false; - if (!can_buy[name] && gameplay != "test") { - return fail_response("buy_cant_npc"); - } - if (!can_add_item(player, create_new_item(name, quantity))) { - return fail_response("buy_cant_space"); - } - (G.maps[player.map].items[name] || []).forEach(function (l) { - if (simple_distance(player, l) < B.sell_dist) { - can_reach = l; - } - }); - if (player.computer && !can_reach) { - can_reach = G.maps.main.merchants[0]; - } - if (!can_reach) { - return fail_response("distance"); - } - can_reach.name = player.name; - var def = G.items[name]; - if (!def.s) { - quantity = 1; - } - cost = quantity * G.items[name].g; - if (G.items[name].p2w) { - cost *= G.inflation; - } - if (player.gold < cost) { - return fail_response("buy_cost"); - } - var new_item = create_new_item(name, quantity); - player.gold -= cost; - var num = add_item(player, new_item, { announce: false }); - xy_emit(can_reach, "ui", { - type: "+$", - id: can_reach.id, - name: player.name, - item: cache_item(new_item), - event: "buy", - }); - - resend(player, "reopen+nc+inv"); - success_response("buy_success", { cost: cost, num: num, name: name, q: quantity, cevent: "buy" }); - }); - socket.on("send", function (data) { - var player = players[socket.id]; - var receiver = players[name_to_id[data.name || ""]]; - var num; - var s_item; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!receiver || receiver.user) { - return fail_response("receiver_unavailable"); - } - if (distance(receiver, player, true) > B.dist || receiver.map != player.map) { - return fail_response("distance"); - } - if (data.num !== undefined) { - data.num = max(0, parseInt(data.num) || 0); - var item = player.items[data.num]; - if (!item) { - return fail_response("send_no_item"); - } - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (item.l || (item.acl && receiver.owner != player.owner)) { - return fail_response("item_locked"); - } - if (item.b) { - return fail_response("item_blocked"); - } - data.q = min(item.q || 1, max(1, parseInt(data.q || 1) || 1)); - if (!data.q) { - return fail_response("no_item"); - } - if (!can_add_item(receiver, create_new_item(item.name, data.q))) { - return fail_response("send_no_space"); - } - if ((item.q || 1) == data.q) { - player.items[data.num] = player.citems[data.num] = null; - player.esize++; - } else { - player.items[data.num].q -= data.q; - player.citems[data.num] = cache_item(player.items[data.num]); - } - - if (item.q) { - s_item = create_new_item(item.name, data.q); - if (item.v) { - s_item.v = item.v; - } - if (item.data) { - s_item.data = item.data; - } - num = add_item(receiver, s_item, { announce: false }); - } else { - s_item = item; - num = add_item(receiver, item, { announce: false }); - } - - if (receiver.owner != player.owner) { - item.src = "snd"; - } - - add_to_history(player, { name: "item", to: receiver.name, item: item.name, q: data.q, level: item.level }); - add_to_history(receiver, { name: "item", from: player.name, item: item.name, q: data.q, level: item.level }); - - xy_emit(player, "ui", { - type: "item_sent", - receiver: receiver.name, - sender: player.name, - item: cache_item(s_item, null, { q: data.q }), - num: num, - fnum: data.num, - event: true, - }); - - resend(player, "reopen+nc+inv"); - resend(receiver, "reopen+nc+inv"); - receiver.socket.emit("game_response", { - response: "item_received", - name: player.name, - item: item.name, - q: data.q, - num: num, - cevent: true, - }); - player.socket.emit("game_response", { - response: "item_sent", - name: receiver.name, - item: item.name, - q: data.q, - num: data.num, - cevent: true, - place: "send", - }); - } else if (data.gold !== undefined) { - data.gold = min(player.gold, max(1, parseInt(data.gold || 1) || 1)) || player.gold; - if (!data.gold) { - return fail_response("invalid"); - } - player.gold -= data.gold; - if (!is_same(player, receiver) && data.gold != 1) { - data.gold = parseInt(data.gold * 0.975); - } - receiver.gold += data.gold; - - add_to_history(player, { name: "gold", to: receiver.name, amount: data.gold }); - add_to_history(receiver, { name: "gold", from: player.name, amount: data.gold }); - - xy_emit(player, "ui", { - type: "gold_sent", - receiver: receiver.name, - sender: player.name, - gold: data.gold, - event: true, - }); - - resend(player, "reopen+nc+inv"); - resend(receiver, "reopen+nc+inv"); - receiver.socket.emit("game_response", { - response: "gold_received", - name: player.name, - gold: data.gold, - cevent: true, - }); - player.socket.emit("game_response", { - response: "gold_sent", - name: receiver.name, - gold: data.gold, - cevent: true, - place: "send", - }); - } else if (data.cx !== undefined) { - var cxl = map_cx(player); - var count = all_cx(player, 1); - if (!(cxl[data.cx] && player.p.acx[cxl[data.cx]] && count[data.cx] > 0)) { - return fail_response("send_no_cx"); - } - // count[cxl[data.cx]] before [28/01/22] - if (receiver.owner != player.owner) { - return fail_response("send_diff_owner"); - } - player.p.acx[cxl[data.cx]] -= 1; - if (!player.p.acx[cxl[data.cx]]) { - delete player.p.acx[cxl[data.cx]]; - } - receiver.p.acx[cxl[data.cx]] = (receiver.p.acx[cxl[data.cx]] || 0) + 1; - - xy_emit(player, "ui", { - type: "cx_sent", - receiver: receiver.name, - sender: player.name, - cx: data.cx, - event: true, - }); - - receiver.socket.emit("game_response", { - response: "cx_received", - name: player.name, - cx: data.cx, - acx: receiver.p.acx, - cevent: true, - }); - player.socket.emit("game_response", { - response: "cx_sent", - name: receiver.name, - cx: data.cx, - acx: player.p.acx, - cevent: true, - place: "send", - }); - } - }); - socket.on("donate", function (data) { - var player = players[socket.id]; - var XPX = 3.2; - if (!player || player.user) { - return game_response("cant_in_bank"); - } - var gold = max(1, min(parseInt(data.gold) || 0, 1000000000)); - if (gold > player.gold) { - return socket.emit("game_response", "gold_not_enough"); - } - if (gold >= 1000000) { - response = "thx"; - player.donation = true; - } else if (gold < 100000) { - response = "low"; - } else { - add_item(player, "gum"); - response = "gum"; - } - if (S.gold < 500000000) { - XPX = 4.8; - } else if (S.gold <= 1000000000) { - XPX = 4; - } - player.gold -= gold; - S.gold += gold; - if (gold >= 5000000) { - lstack(S.logs.donate, { name: player.name, gold: gold, xp: XPX }); - } - if (player.type == "merchant") { - player.xp += parseInt(gold * XPX); - } - resend(player, "reopen"); - socket.emit("game_response", { response: "donate_" + response, gold: gold, xprate: XPX }); - }); - socket.on("destroy", function (data) { - var player = players[socket.id]; - var add = "+nc+inv"; - data.num = max(0, parseInt(data.num) || 0); - if (!player.items[data.num]) { - return fail_response("no_item"); - } - var item = player.items[data.num]; - var name = player.items[data.num].name; - data.q = min(max(parseInt(data.q) || 0, 1), (item && item.q) || 1); - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (item.l) { - return fail_response("item_locked"); - } - if (item.b) { - return fail_response("item_blocked"); - } - if (item.level != 13) { - consume(player, data.num, data.q); - //player.items[data.num]=player.citems[data.num]=null; - } - if (data.statue) { - if (item.name == "shadowstone") { - add = "+u+cid"; - player.s.invis = { ms: 99999 }; - } - if (G.items[item.name].upgrade && Math.random() < 1.0 / ((gameplay == "hardcore" && 10000) || 1000000)) { - add = "+u+cid"; - item.level = 13; - player.items[data.num] = item; - player.citems[data.num] = cache_item(player.items[data.num]); - } - xy_emit(G.maps.spookytown.ref.poof, "upgrade", { type: "poof", success: 1 }); - } - resend(player, "reopen" + add); - success_response("destroyed", { name: name, place: "destroy", cevent: "destroy" }); - }); - socket.on("join_giveaway", function (data) { - var player = players[socket.id]; - var seller = players[id_to_id[data.id]]; - var num; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!in_arr(data.slot, trade_slots)) { - return fail_response("invalid"); - } - if (!seller || seller.npc || is_invis(seller)) { - return fail_response("seller_gone"); - } - if (seller.user) { - return fail_response("cant_in_bank"); - } - if (distance(seller, player, true) > B.dist || seller.map != player.map) { - return fail_response("distance"); - } - if (seller.id == player.id) { - return fail_response("hmm"); - } - var item = seller.slots[data.slot]; - if (!item || (data.rid && item.rid != data.rid)) { - return fail_response("item_gone"); - } - if (!item.giveaway) { - return fail_response("sneaky"); - } - if (!player.auth_id) { - return fail_response("need_auth"); - } - //if(item.list.includes(player.name)) return socket.emit("game_log","Already joined!"); - //if(player.type!="merchant") return socket.emit("game_log","Only merchants can join giveaways!"); - - //item.list.push(player.name); - - item.registry = item.registry || {}; - item.registry[player.auth_id] = player.name; - item.list = Object.values(item.registry); - - socket.emit("game_log", "Joined the giveaway!"); - seller.socket.emit("game_response", { - response: "giveaway_join", - name: player.name, - slot: data.slot, - cevent: true, - }); - - resend(player, "reopen"); - resend(seller, "reopen+u+cid"); - success_response({}); - }); - socket.on("trade_wishlist", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - data.q = min(9999, max(1, parseInt(data.q || 1) || 1)); - if (!in_arr(data.slot, trade_slots) || !G.items[data.name] || data.name == "placeholder") { - return fail_response("invalid"); - } - if (player.slots[data.slot] && !player.slots[data.slot].b) { - return fail_response("slot_occuppied"); - } - if (!get_trade_slots(player).includes(data.slot)) { - return fail_response("invalid"); - } - var item = { - name: data.name, - rid: randomStr(4), - price: round(min(99999999999, max(parseInt(data.price) || 1, 1))), - b: true, - }; - if (G.items[data.name].upgrade || G.items[data.name].compound) { - item.q = min(99, data.q); - item.level = round(min(12, max(parseInt(data.level) || 0, 0))); - } else { - item.q = data.q; - } - player.slots[data.slot] = item; - player.cslots[data.slot] = cache_item(player.slots[data.slot], true); - resend(player, "reopen+u+cid+nc"); - success_response({}); - }); - socket.on("trade_sell", function (data) { - // if(!is_sdk) return; - var player = players[socket.id]; - var buyer = players[id_to_id[data.id]]; - var num; - var actual = null; - var num = null; - data.q = max(1, parseInt(data.q || 1) || 1); - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!in_arr(data.slot, trade_slots)) { - return fail_response("invalid"); - } - if (!buyer || buyer.npc || is_invis(buyer)) { - return fail_response("buyer_gone"); - } - if (player.user) { - return fail_response("cant_in_bank"); - } - if (distance(buyer, player, true) > B.dist) { - return fail_response("distance"); - } - if (buyer.id == player.id) { - return fail_response("hmm"); - } - var item = buyer.slots[data.slot]; - if (!item || (data.rid && item.rid != data.rid)) { - return fail_response("item_gone"); - } - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (!item.b && !B.rbugs) { - return fail_response("sneaky"); - } - if ((item.q || 1) < data.q) { - return fail_response("dont_have_enough"); - } - if (item.price * data.q > buyer.gold) { - return fail_response("buyer_gold"); - } - for (var i = 0; i < player.isize; i++) { - if ( - player.items[i] && - player.items[i].name == item.name && - (player.items[i].q || 1) >= data.q && - player.items[i].level == item.level && - !player.items[i].l - ) { - actual = player.items[i]; - num = i; - break; - } - } - if (!actual) { - return fail_response("no_item"); - } - if (actual.b) { - return fail_response("item_blocked"); - } - if (!can_add_item(buyer, create_new_item(item.name, data.q))) { - return fail_response("trade_bspace"); - } - var price = item.price * data.q; - player.gold += round(price * (1 - player.tax)); - add_to_trade_history(player, "sell", buyer.name, cache_item(actual, true, { q: data.q }), price); - buyer.gold -= price; - add_to_trade_history(buyer, "buy", player.name, cache_item(actual, true, { q: data.q }), price); - S.gold += price - round(price * (1 - player.tax)); - - if ((item.q || 1) == data.q) { - buyer.slots[data.slot] = buyer.cslots[data.slot] = null; - } else { - buyer.slots[data.slot].q -= data.q; - buyer.cslots[data.slot] = cache_item(buyer.slots[data.slot], true); - } - - // up to here - - if ((player.items[i].q || 1) == data.q) { - player.items[num] = player.citems[num] = null; - } else { - player.items[num].q -= data.q; - player.citems[num] = cache_item(player.items[num]); - } - - if (G.items[item.name].s) { - bnum = add_item(buyer, create_new_item(item.name, data.q), { announce: false }); - } else { - bnum = add_item(buyer, actual, { announce: false }); - } - - if (player.type == "merchant") { - merchant_xp_logic(player, buyer, price, price - round(price * (1 - player.tax))); - } - if (buyer.type == "merchant") { - merchant_xp_logic(buyer, player, price, price - round(price * (1 - buyer.tax))); - } - - socket.emit( - "game_log", - "Sales tax " + to_pretty_num(price - round(price * (1 - player.tax))) + " gold [" + player.tax * 100 + "%]", - ); - socket.emit("game_log", "Received " + to_pretty_num(round(price * (1 - player.tax))) + " gold"); - buyer.socket.emit("game_log", "Spent " + to_pretty_num(price) + " gold"); - - xy_emit(buyer, "ui", { - type: "+$$", - seller: player.name, - buyer: buyer.name, - item: cache_item(actual, true, { q: data.q, price: item.price }), - slot: data.slot, - num: bnum, - snum: num, - }); - - resend(player, "reopen"); - resend(buyer, "reopen+u+cid"); - success_response({}); - }); - socket.on("trade_buy", function (data) { - var player = players[socket.id]; - var seller = players[id_to_id[data.id]]; - var num; - data.q = max(1, parseInt(data.q || 1) || 1); - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!in_arr(data.slot, trade_slots)) { - return fail_response("invalid"); - } - if (!seller || seller.npc || is_invis(seller)) { - return fail_response("seller_gone"); - } - if (seller.user) { - return fail_response("cant_in_bank"); - } - if (distance(seller, player, true) > B.dist || seller.map != player.map) { - return fail_response("distance"); - } - if (seller.id == player.id) { - return fail_response("hmm"); - } - var item = seller.slots[data.slot]; - if (!item || (data.rid && item.rid != data.rid)) { - return fail_response("item_gone"); - } - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (item.b || item.giveaway) { - return fail_response("sneaky"); - } - if (item.price * data.q > player.gold) { - return fail_response("gold_not_enough"); - } - if ((item.q || 1) < data.q) { - return fail_response("insufficient_q"); - } - if (!can_add_item(player, create_new_item(item.name, data.q))) { - return fail_response("no_space"); - } - var price = item.price * data.q; - player.gold -= price; - add_to_trade_history(player, "buy", seller.name, cache_item(seller.slots[data.slot], true, { q: data.q }), price); - seller.gold += round(price * (1 - seller.tax)); - add_to_trade_history( - seller, - "sell", - player.name, - cache_item(seller.slots[data.slot], true, { q: data.q }), - price, - ); - S.gold += price - round(price * (1 - seller.tax)); - - if ((item.q || 1) == data.q) { - seller.slots[data.slot] = seller.cslots[data.slot] = null; - } else { - seller.slots[data.slot].q -= data.q; - seller.cslots[data.slot] = cache_item(seller.slots[data.slot], true); - } - - if (item.q) { - num = add_item(player, create_new_item(item.name, data.q), { announce: false }); - } else { - num = add_item(player, item, { announce: false }); - } - - if (seller.owner != player.owner) { - item.src = "tb"; - } - - if (player.type == "merchant") { - merchant_xp_logic(player, seller, price, price - round(price * (1 - player.tax))); - } - if (seller.type == "merchant") { - merchant_xp_logic(seller, player, price, price - round(price * (1 - seller.tax))); - } - - socket.emit("game_log", "Spent " + to_pretty_num(price) + " gold"); - seller.socket.emit( - "game_log", - "Sales tax " + to_pretty_num(price - round(price * (1 - seller.tax))) + " gold [" + seller.tax * 100 + "%]", - ); - seller.socket.emit("game_log", "Received " + to_pretty_num(round(price * (1 - seller.tax))) + " gold"); - - xy_emit(seller, "ui", { - type: "+$$", - seller: seller.name, - buyer: player.name, - item: cache_item(item, true, { q: data.q }), - slot: data.slot, - num: data.num, - }); - - resend(player, "reopen"); - resend(seller, "reopen+u+cid"); - success_response({}); - }); - socket.on("trade_history", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - server_log("trade_history: " + player.name); - socket.emit("trade_history", player.p.trade_history || []); - }); - socket.on("merchant", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - var initial = player.p.stand; - server_log("merchant: " + player.name); - if (data.close || player.p.stand) { - player.p.stand = false; - for (var i = 0; i < player.items.length; i++) { - if (player.items[i] && player.items[i].b == "stand") { - delete player.items[i].b; - } - } - } - if (data.num !== undefined) { - var item = player.items[data.num]; - if (item && G.items[item.name].stand) { - player.p.stand = G.items[item.name].stand; - item.b = "stand"; - } else { - return fail_response("invalid"); - } - } - if (initial != player.p.stand) { - // All unneccessary causes of resend's should be patched [03/08/18] - reslot_player(player); - resend(player, "u+cid"); - } - success_response({}); - }); - socket.on("imove", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - data.a = max(0, parseInt(data.a) || 0); - data.b = max(0, parseInt(data.b) || 0); - if (data.a == data.b) { - return fail_response("invalid"); - } - var a = min(data.a, data.b); - var b = max(data.a, data.b); - var a_item; - if (!(b < player.isize || b < player.items.length)) { - return fail_response("invalid"); - } - // while(b>=player.items.length) player.items.push(null); [22/11/16] - if (player.items[a] && player.items[a].name == "placeholder") { - return fail_response("item_placeholder"); - } - if (player.items[b] && player.items[b].name == "placeholder") { - return fail_response("item_placeholder"); - } - if (can_stack(player.items[a], player.items[b])) { - player.items[data.a].q = (player.items[a].q || 1) + (player.items[b].q || 1); - player.items[data.b] = null; - player.esize++; - } else { - a_item = player.items[a]; - player.items[a] = player.items[b]; - player.items[b] = a_item; - } - player.citems[data.a] = cache_item(player.items[data.a]); - player.citems[data.b] = cache_item(player.items[data.b]); - resend(player, "reopen+nc+inv"); - return success_response("data"); - }); - socket.on("bank", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (!player.user || player.mounting || player.unmounting) { - return fail_response("bank_unavailable"); - } - var success = {}; - if (data.operation == "withdraw") { - var amount = max(0, min(parseInt(data.amount) || 0, player.user.gold)); - player.user.gold -= amount; - player.gold += amount; - success = { response: "bank_withdraw", gold: amount, cevent: true }; - } - if (data.operation == "deposit") { - var amount = max(0, min(parseInt(data.amount) || 0, player.gold)); - player.user.gold += amount; - player.gold -= amount; - success = { response: "bank_store", gold: amount, cevent: true }; - } - if (data.operation == "unlock") { - if (!bank_packs[data.pack]) { - return fail_response("invalid"); - } - var gold = bank_packs[data.pack][1]; - var shells = bank_packs[data.pack][2]; - if (!gold) { - return fail_response("gold_not_enough"); - } - if (player.user[data.pack]) { - return fail_response("invalid"); - } - if (data.gold) { - if (player.gold < gold) { - return fail_response("gold_not_enough"); - } - player.gold -= gold; - player.user[data.pack] = []; - player.cuser[data.pack] = []; - success = { response: "bank_new_pack", pack: data.pack, gold: gold, cevent: true }; - } else if (data.shells) { - if (player["unlocking_" + data.pack]) { - return fail_response("bank_opi"); - } - player["unlocking_" + data.pack] = true; - appengine_call( - "bill_user", - { - auth: player.auth, - amount: shells, - reason: data.pack, - name: player.name, - suffix: "/" + player.name + "/" + data.pack, - override: true, - }, - function (result) { - server_log("buy_with_cash: " + JSON.stringify(result)); - player["unlocking_" + data.pack] = false; - if (result.failed || !result.done) { - return socket.emit("game_log", "Purchase failed"); - } - player.cash = result.cash; - - player.user[data.pack] = []; - player.cuser[data.pack] = []; - game_response("bank_new_pack", { cevent: true, pack: data.pack, shells: shells }); - - resend(player, "reopen"); - }, - ); - success = { success: false, in_progress: true }; - } - } - if (data.operation == "move") { - //within a .itemsN - if (!player.user[data.pack] || bank_packs[data.pack][0] != player.map) { - return fail_response("invalid"); - } - server_log("storage move " + JSON.stringify(data)); - data.a = max(0, min(41, parseInt(data.a) || 0)); - data.b = max(0, min(41, parseInt(data.b) || 0)); - if (data.a == data.b) { - return fail_response("invalid"); - } - if (player.user[data.pack][data.a] && player.user[data.pack][data.a].name == "placeholder") { - return fail_response("item_placeholder"); - } - if (player.user[data.pack][data.b] && player.user[data.pack][data.b].name == "placeholder") { - return fail_response("item_placeholder"); - } - if (can_stack(player.user[data.pack][data.a], player.user[data.pack][data.b])) { - player.user[data.pack][data.b].q = - (player.user[data.pack][data.a].q || 1) + (player.user[data.pack][data.b].q || 1); - player.user[data.pack][data.a] = null; - } else { - var temp = player.user[data.pack][data.a]; - player.user[data.pack][data.a] = player.user[data.pack][data.b]; - player.user[data.pack][data.b] = temp; - } - player.cuser[data.pack][data.a] = cache_item(player.user[data.pack][data.a]); - player.cuser[data.pack][data.b] = cache_item(player.user[data.pack][data.b]); - } - if (data.operation == "swap") { - //between .items and a .itemsN - var operation = "swap"; - if (!player.user[data.pack] || bank_packs[data.pack][0] != player.map) { - return fail_response("invalid"); - } - data.str = parseInt(data.str); - data.inv = parseInt(data.inv); - if (data.inv == -1 || (!data.inv && data.inv !== 0)) { - operation = "pull"; - for (var i = 0; i < player.isize; i++) { - if (!player.items[i]) { - data.inv = i; - break; - } - } - // if(data.inv==-1) { socket.emit("game_log","Inventory is full"); return; } - } - if (data.str == -1 || (!data.str && data.str !== 0)) { - if (operation == "pull") { - return fail_response("invalid"); - } - operation = "store"; - for (var i = 0; i < 42; i++) { - if (!player.user[data.pack][i]) { - data.str = i; - break; - } - } - // if(data.str==-1) { socket.emit("game_log","Storage is full"); return; } - } - server_log("storage swap " + JSON.stringify(data)); - data.str = max(0, min(41, parseInt(data.str) || 0)); - data.inv = max(0, min(player.isize - 1, parseInt(data.inv) || 0)); - var bank_item = player.user[data.pack][data.str]; - var inv_item = player.items[data.inv]; - if (inv_item && inv_item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (inv_item && inv_item.b) { - return fail_response("item_blocked"); - } - if (inv_item) { - delete inv_item.m; - delete inv_item.v; - } - if (operation == "swap") { - player.user[data.pack][data.str] = inv_item; - player.items[data.inv] = bank_item; - player.cuser[data.pack][data.str] = cache_item(player.user[data.pack][data.str]); - player.citems[data.inv] = cache_item(player.items[data.inv]); - } else if (operation == "store" && inv_item) { - if (!can_add_item(player.user[data.pack], inv_item)) { - return fail_response("storage_full"); - } - player.items[data.inv] = player.citems[data.inv] = null; - bank_add_item(player, data.pack, inv_item); - } else if (operation == "pull" && bank_item) { - if (!can_add_item(player, bank_item)) { - return fail_response("inventory_full"); - } - player.user[data.pack][data.str] = player.cuser[data.pack][data.str] = null; - add_item(player, bank_item, { announce: false }); - } - } - if (!player.user.gold && player.user.gold !== 0) { - player.user.gold = 0; - server_log("#X - GOLD BUG bank", 1); - } - resend(player, "reopen"); - success_response(success); - }); - socket.on("throw", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - var item = player.items[data.num]; - if (item) { - if (item.name == "placeholder") { - return fail_response("item_placeholder"); - } - if (item.l) { - return fail_response("item_locked"); - } - if (item.b) { - return fail_response("item_blocked"); - } - var def = G.items[item.name]; - if (!def.throw) { - return fail_response("invalid"); - } - var x = parseFloat(data.x) || 0; - var y = parseFloat(data.y) || 0; - if (distance(player, { map: player.map, in: player.in, x: x, y: y }) > player.str * 3) { - fail_response("too_far"); - } - consume_one(player, data.num); - if (item.name == "confetti") { - player.thrilling = future_s(20); - xy_emit( - { map: player.map, in: player.in, x: x, y: y }, - "eval", - "confetti_shower({in:'" + player.in + "',map:'" + player.map + "',real_x:" + x + ",real_y:" + y + "})", - ); - } - if (item.name == "firecrackers") { - player.thrilling = future_s(200); - xy_emit( - { map: player.map, in: player.in, x: x, y: y }, - "eval", - "firecrackers({in:'" + player.in + "',map:'" + player.map + "',real_x:" + x + ",real_y:" + y + "})", - ); - for (var id in instances[player.in].monsters) { - var monster = instances[player.in].monsters[id]; - if (monster.target && distance({ map: player.map, in: player.in, x: x, y: y }, monster) < 64) { - stop_pursuit(monster, { force: true, cause: "firecrackers" }); - } - } - } - if (item.name == "whiteegg") { - xy_emit({ map: player.map, in: player.in, x: x, y: y }, "eval", "egg_splash(" + x + "," + y + ")"); - for (var id in instances[player.in].monsters) { - var monster = instances[player.in].monsters[id]; - if (!monster.target && distance({ map: player.map, in: player.in, x: x, y: y }, monster) < 32) { - target_player(monster, player); - } - } - } - if (item.name == "smoke") { - xy_emit({ map: player.map, in: player.in, x: x, y: y }, "eval", "assassin_smoke(" + x + "," + y + ");"); - } - resend(player, "reopen+nc+inv"); - success_response({}); - } else { - fail_response("no_item"); - } - }); - socket.on("poke", function (data) { - var player = players[socket.id]; - var level = 1; - if (!player || !player.slots.gloves || player.slots.gloves.name != "poker") { - return; - } - if (player.pokes >= 50) { - return socket.emit("game_log", "You are out of pokes!"); - } - player.pokes = (player.pokes || 0) + 1; - if (player.slots.gloves.level >= 10) { - level = 4; - } else if (player.slots.gloves.level == 9) { - level = 3; - } else if (player.slots.gloves.level == 8) { - level = 2; - } - xy_emit(player, "poke", { name: data.name, level: level, who: player.name }); - }); - socket.on("merge", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - var container = player.slots[data.container]; - var pet = player.slots[data.pet]; - if (!container || container.type != "container" || !pet || pet.type != "container") { - return socket.emit("game_response", "merge_mismatch"); - } - if (G.items[container.name].grade < G.items[pet.name].grade || container.data) { - return socket.emit("game_response", "merge_mismatch"); - } - container.data = G.items[pet.name].pet; - player.slots[data.pet] = null; - socket.emit("game_response", "merge_complete"); - resend(player, "reopen"); - }); - socket.on("activate", function (data) { - var player = players[socket.id]; - // console.log(JSON.stringify(data)); - if (!player) { - return; - } - if (data.slot) { - var item = player.slots[data.slot]; - if (!item || in_arr(data.slot, trade_slots)) { - return; - } - if (item.name == "etherealamulet") { - if (player.last_ethereal && mssince(player.last_ethereal) < 120) { - return socket.emit("game_response", "not_ready"); - } - player.last_ethereal = new Date(); - player.s.ethereal = { ms: 5000 }; - socket.emit("eval", { code: "skill_timeout('ethereal',120)" }); - return resend(player, "u+cid"); - } else if (item.name == "angelwings") { - if (player.tskin == "snow_angel") { - player.tskin = ""; - } else if (item.level >= 8 && (player.type == "mage" || player.type == "priest")) { - player.tskin = "snow_angel"; - } else { - return socket.emit("game_response", "nothing"); - } - } else if (item.name == "tristone") { - var on = true; - if (player.tskin || player.tactivations == 100) { - player.tskin = ""; - on = false; - } else if (item.level <= 1 && deduct_gender(player) == "female") { - player.tskin = random_one(["tf_green", "tf_pink"]); - } else if (item.level == 2 && deduct_gender(player) == "female") { - player.tskin = random_one(["tf_blue", "tf_purple"]); - } else if (deduct_gender(player) == "female") { - player.tskin = "tf_orange"; - } else if (item.level <= 1) { - player.tskin = random_one(["tm_gray", "tm_brown", "tm_white"]); - } else if (item.level == 2) { - player.tskin = random_one(["tm_green", "tm_yellow", "tm_purple"]); - } else { - player.tskin = random_one(["tm_blue", "tm_red"]); - } - player.tactivations = (player.tactivations || 0) + 1; - } else if (item.name == "darktristone") { - var on = true; - if (player.tskin || player.tactivations == 100) { - player.tskin = ""; - on = false; - } else if (item.level == 4 && deduct_gender(player) == "female") { - player.tskin = "mf_blue"; - } else if (deduct_gender(player) == "female") { - player.tskin = "mf_yellow"; - } else if (item.level == 4) { - player.tskin = "mm_blue"; - } else { - player.tskin = "mm_yellow"; - } - player.tactivations = (player.tactivations || 0) + 1; - } else { - return; - } - resend(player, "reopen+nc+inv+u+cid"); - } else { - var item = player.items[data.num]; - if (item) { - if (item.name == "frozenstone") { - consume_one(player, data.num); - } - if (item.name == "bkey") { - if (!player.user) { - return socket.emit("game_response", "only_in_bank"); - } - if (player.user.unlocked && player.user.unlocked.bank_b) { - return socket.emit("game_response", "already_unlocked"); - } - consume_one(player, data.num); - player.user.unlocked = player.user.unlocked || {}; - player.user.unlocked.bank_b = new Date(); - player.user.items8 = player.user.items8 || []; - socket.emit("game_response", "door_unlocked"); - } - if (item.name == "ukey") { - if (!player.user) { - return socket.emit("game_response", "only_in_bank"); - } - if (player.user.unlocked && player.user.unlocked.bank_u) { - return socket.emit("game_response", "already_unlocked"); - } - consume_one(player, data.num); - player.user.unlocked = player.user.unlocked || {}; - player.user.unlocked.bank_u = new Date(); - player.user.items24 = player.user.items24 || []; - socket.emit("game_response", "door_unlocked"); - } - if (item.name == "dkey") { - if (!player.user) { - return socket.emit("game_response", "only_in_bank"); - } - var found = false; - for (var i = 0; i < 48; i++) { - var pack = "items" + i; - if (!bank_packs[pack] || player.user[pack]) { - continue; - } - found = true; - player.user[pack] = []; - player.cuser[pack] = []; - break; - } - if (!found) { - return; - } - consume_one(player, data.num); - socket.emit("game_response", "bank_pack_unlocked"); - } - } - resend(player, "reopen+nc+inv"); - } - }); - socket.on("booster", function (data) { - var player = players[socket.id]; - var item = player.items[data.num]; - if (!player) { - return; - } - server_log("booster " + data.num + " " + data.action); - if (!item || !in_arr(item.name, booster_items)) { - return fail_response("invalid"); - } - if (data.action == "activate" && !item.expires) { - item.expires = new Date(); - item.expires.setDate(item.expires.getDate() + 30 + (item.level || 0) * 2); - //item.expires.setMinutes(item.expires.getMinutes()+2); - } else if (data.action == "shift") { - if (!in_arr(data.to, booster_items)) { - return fail_response("invalid"); - } - player.xpm = player.goldm = player.luckm = 1; - item.name = data.to; - player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 240, 120000) }; - } - // if(item.expires) item.p="legacy"; - player.citems[data.num] = cache_item(player.items[data.num]); - resend(player, "reopen"); - success_response({ name: player.items[data.num].name }); - }); - socket.on("convert", function (data) { - // There are a lot of routines that could give birth to an endgame bug - // This routine ended up being the one - // "stone" boosters were originally 1200 shells - // when they were discontinued, I let players exchange them for 3600 shells instead - // turns out 'buy_with_cash' didn't check whether an item has "ignore" or not - // so one could buy the stones for 1200 shells and sell them for 3600 - // unlocking infinite gold and shells [17/01/18] - var player = players[socket.id]; - var item = player.items[data.num]; - var amount = 3600; - server_log("stone " + data.num + " " + data.action); - if (!item || !in_arr(item.name, ["stoneofxp", "stoneofgold", "stoneofluck"])) { - return; - } - if (item.expires) { - amount = round(600 - (hsince(item.expires) * 100) / 24.0); - } - add_shells(player, amount, "convert"); - player.items[data.num] = null; - player.citems[data.num] = cache_item(player.items[data.num]); - resend(player, "reopen+cid"); - }); - socket.on("emotion", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (!data.name) { - data.name = random_one(Object.keys(player.p.emx)); - } - if (player.last.emotion && mssince(player.last.emotion) < 2000) { - return socket.emit("game_response", "emotion_cooldown"); - } - player.last.emotion = new Date(); - if (!G.emotions[data.name] || !player.p.emx[data.name]) { - return socket.emit("game_response", "emotion_cant"); - } - xy_emit(player, "emotion", { name: data.name, player: player.name }); - }); - socket.on("skill", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - var target = null; - var cool = true; - var resolve = { response: "data", place: data.name, success: true }; - var reject = null; - player.first = true; - G.skills.attack.cooldown = player.attack_ms; - server_log("skill " + JSON.stringify(data)); - if (is_disabled(player)) { - return fail_response("disabled", data.name); - } - if ((player.tskin == "konami" || is_silenced(player)) && data.name != "attack") { - return socket.emit("game_response", { response: "skill_cant_incapacitated", place: data.name, failed: true }); - } - if (!G.skills[data.name]) { - return socket.emit("game_response", { response: "no_skill", place: data.name, failed: true }); - } - if ( - G.skills[data.name].mp && - player.mp < (G.skills[data.name].mp * (100 - (player.mp_reduction || 0))) / 100.0 && - !(player.role == "gm" && data.name == "blink") - ) { - return socket.emit("game_response", { response: "no_mp", place: data.name, failed: true }); - } - if (G.skills[data.name].level && player.level < G.skills[data.name].level) { - return socket.emit("game_response", { response: "no_level", place: data.name, failed: true }); - } - if ( - (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) && - player.last[data.name] && - mssince(player.last[data.name]) < (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) - ) { - return socket.emit("game_response", { - response: "cooldown", - skill: data.name, - place: data.name, - id: data.id, - ms: (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) - mssince(player.last[data.name]), - failed: true, - }); - } - if ( - G.skills[data.name].share && - player.last[G.skills[data.name].share] && - mssince(player.last[G.skills[data.name].share]) < - G.skills[G.skills[data.name].share].cooldown * (G.skills[data.name].cooldown_multiplier || 1) - ) { - return socket.emit("game_response", { - response: "cooldown", - skill: data.name, - place: data.name, - failed: true, - id: data.id, - ms: - G.skills[G.skills[data.name].share].cooldown * (G.skills[data.name].cooldown_multiplier || 1) - - mssince(player.last[G.skills[data.name].share]), - }); - } - if (G.skills[data.name].class && !in_arr(player.type, G.skills[data.name].class) && player.role != "gm") { - return socket.emit("game_response", { response: "skill_cant_use", place: data.name, failed: true }); - } - if ( - G.skills[data.name].wtype && - !is_array(G.skills[data.name].wtype) && - (!player.slots.mainhand || G.items[player.slots.mainhand.name].wtype != G.skills[data.name].wtype) && - player.role != "gm" - ) { - return socket.emit("game_response", { response: "skill_cant_wtype", place: data.name, failed: true }); - } - if ( - is_array(G.skills[data.name].wtype) && - (!player.slots.mainhand || !in_arr(G.items[player.slots.mainhand.name].wtype, G.skills[data.name].wtype)) && - player.role != "gm" - ) { - return socket.emit("game_response", { response: "skill_cant_wtype", place: data.name, failed: true }); - } - if (G.skills[data.name].hostile && G.maps[player.map].safe) { - return socket.emit("game_response", { response: "skill_cant_safe", place: data.name, failed: true }); - } - if (G.skills[data.name].target) { - if ( - ("" + parseInt(data.id) === "" + data.id && G.skills[data.name].target == "player") || - ("" + parseInt(data.id) !== "" + data.id && G.skills[data.name].target == "monster") - ) { - return fail_response("invalid_target", data.name, { id: data.id }); - } - if (G.skills[data.name].target != "monster" && players[id_to_id[data.id]]) { - target = players[id_to_id[data.id]]; - if (G.skills[data.name].hostile && target.name == player.name) { - return socket.emit("game_response", { response: "no_target", place: data.name, failed: true }); - } - } else if (G.skills[data.name].target != "player" && instances[player.in].monsters[data.id]) { - target = instances[player.in].monsters[data.id]; - } else { - return socket.emit("disappear", { id: data.id, place: data.name, reason: "not_there" }); - } - - if ( - is_invis(target) || - (!G.skills[data.name].global && (target.map != player.map || distance(target, player, true) > B.max_vision)) - ) { - return socket.emit("disappear", { id: data.id, place: data.name, reason: "not_there" }); - } - } - if (G.skills[data.name].requirements) { - for (var requirement in G.skills[data.name].requirements) { - if (!player[requirement] || player[requirement] < G.skills[data.name].requirements[requirement]) { - return socket.emit("game_response", { - response: "skill_cant_requirements", - place: data.name, - failed: true, - }); - } - } - } - if (G.skills[data.name].slot) { - var found = false; - var charges = false; - G.skills[data.name].slot.forEach(function (p) { - if (player.slots[p[0]] && player.slots[p[0]].name == p[1]) { - if (G.items[player.slots[p[0]].name].charge) { - if ((player.slots[p[0]].charges || 0) < G.items[player.slots[p[0]].name].charge) { - charges = true; - return; - } - player.slots[p[0]].charges -= G.items[player.slots[p[0]].name].charge; - player.cslots[p[0]] = cache_item(player.slots[p[0]]); - } - found = true; - } - }); - if (charges) { - return socket.emit("game_response", { response: "skill_cant_charges", place: data.name, failed: true }); - } - if (!found) { - return socket.emit("game_response", { response: "skill_cant_slot", place: data.name, failed: true }); - } - } - if (player.s.invincible) { - delete player.s.invincible; - player.to_resend = "u+cid"; - } - if (data.name == "attack" || data.name == "heal") { - var attack = commence_attack(player, target, data.name); - if (!attack.failed) { - resolve = attack; - } else { - reject = attack; - cool = false; - } - } - if (data.name == "invis" && !player.s.invis) { - if (player.s.marked) { - return socket.emit("game_response", { response: "skill_cant_use", place: data.name, failed: true }); - } - player.s.invis = { ms: 999999999999999 }; - xy_emit(player, "disappear", { id: player.id, invis: true, reason: "invis" }); - player.to_resend = " "; - } - if (data.name == "pickpocket") { - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - - consume_mp(player, G.skills[data.name].mp); - player.c[data.name] = { - ms: - G.skills[data.name].duration_min + - Math.random() * (G.skills[data.name].duration_max - G.skills[data.name].duration_min), - target: target.name, - }; - player.to_resend = "u+cid"; - resolve = { response: "data", place: data.name, success: false, in_progress: true }; - } - if (data.name == "fishing" || data.name == "mining") { - var direction = 0; - var the_zone = null; - consume_mp(player, G.skills[data.name].mp); - (G.maps[player.map].zones || []).forEach(function (zone) { - if (zone.type != data.name || the_zone) { - return; - } - [ - [0, -24, 3], - [-24, 0, 1], - [24, 0, 2], - [0, 24, 0], - ].forEach(function (m) { - if (is_point_inside([player.x + m[0], player.y + m[1]], zone.polygon)) { - the_zone = zone; - direction = m[2]; - } - }); - }); - if (!the_zone || player.moving) { - xy_emit(player, "ui", { type: data.name + "_fail", name: player.name }); - reject = { response: "data", place: data.name }; - } else { - xy_emit(player, "ui", { type: data.name + "_start", name: player.name, direction: direction }); - player.c[data.name] = { - ms: - G.skills[data.name].duration_min + - Math.random() * (G.skills[data.name].duration_max - G.skills[data.name].duration_min), - drop: the_zone.drop, - }; - } - resolve = { response: "data", place: data.name, success: false, in_progress: true }; - player.to_resend = "u+cid"; - } - if (data.name == "light") { - consume_mp(player, G.skills[data.name].mp); - xy_emit(player, "light", { name: player.name }); - player.to_resend = "u+cid"; - } - if (data.name == "charge") { - player.s.charging = { ms: G.skills.charge.duration }; - player.to_resend = "u+cid"; - } - if (data.name == "dash") { - //console.log(data); - consume_mp(player, G.skills[data.name].mp); - var x = parseFloat(data.x) || 0; - var y = parseFloat(data.y) || 0; - var point = true; - if (point_distance(player.x, player.y, x, y) > 50) { - point = false; - player.socket.emit("game_response", "dash_failed"); - reject = { response: "data", place: data.name }; - } - if (point) { - var spot = safe_xy_nearby(player.map, x, y); - if (!spot) { - spot = safe_xy_nearby(player.map, player.x + (x - player.x) * 0.8, player.y + (y - player.y) * 0.8); - } - if (!spot) { - spot = safe_xy_nearby(player.map, player.x + (x - player.x) * 0.6, player.y + (y - player.y) * 0.6); - } - if (!spot || point_distance(player.x, player.y, spot.x, spot.y) < 10) { - point = false; - player.socket.emit("game_response", "dash_failed"); - reject = { response: "data", place: data.name }; - } - if (point) { - //console.log([player.x,player.y,spot.x,spot.y]); - player.s.dash = { ms: 1000 }; - player.speed = 500; - player.going_x = spot.x; - player.going_y = spot.y; - start_moving_element(player); - player.to_resend = "u+cid"; - player.socket.emit("eval", { code: "ui_move(" + spot.x + "," + spot.y + ")" }); - } - } - } - if (data.name == "hardshell" || data.name == "power" || data.name == "xpower") { - consume_mp(player, G.skills[data.name].mp); - player.s[G.skills[data.name].condition] = { - ms: G.skills[data.name].duration || G.conditions[G.skills[data.name].condition].duration, - }; - player.to_resend = "u+cid"; - } - if (data.name == "mshield") { - consume_mp(player, G.skills[data.name].mp); - if (player.s[G.skills[data.name].condition]) { - delete player.s[G.skills[data.name].condition]; - } else { - player.s[G.skills[data.name].condition] = { ms: 999999999 }; - } - player.to_resend = "u+cid"; - } - if ( - data.name == "mcourage" || - data.name == "mfrenzy" || - data.name == "massproduction" || - data.name == "massproductionpp" - ) { - consume_mp(player, G.skills[data.name].mp); - player.s[G.skills[data.name].condition] = { ms: G.conditions[G.skills[data.name].condition].duration }; - xy_emit(player, "ui", { type: data.name, name: player.name }); - player.to_resend = "u+cid"; - } - if (data.name == "throw") { - if (distance(player, target, true) > G.skills[data.name].range + player.level) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - if (target.s.invincible || target.immune) { - return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); - } - if (!player.items[data.num]) { - return socket.emit("game_response", { response: "skill_no_item", place: data.name, failed: true }); - } - var item = player.items[data.num]; - var r = "u+cid"; - var damage = 0; - if (item.name == "placeholder") { - return socket.emit("game_response", { response: "item_placeholder", place: data.name, failed: true }); - } - if (item.l) { - return socket.emit("game_response", { response: "item_locked", place: data.name, failed: true }); - } - if (item.b) { - return socket.emit("game_response", { response: "item_blocked", place: data.name, failed: true }); - } - var prop = calculate_item_properties(item); - var negative = false; - if (in_arr(item.name, G.skills.throw.negative)) { - negative = true; - } - G.skills.throw.nprop.forEach(function (p) { - if (prop[p]) { - negative = true; - } - }); - if (negative && target.is_player && !is_in_pvp(player)) { - return socket.emit("game_response", { response: "not_in_pvp", place: data.name, failed: true }); - } - consume_one(player, data.num); - if (item.name == "essenceoffire") { - add_condition(target, "eburn", { from: player }); - } else if (item.name == "essenceoflife") { - add_condition(target, "eheal", { from: player }); - } else if (prop.attack) { - damage = round(Math.random() * prop.attack * 15); - } else if (prop.armor) { - damage = round(Math.random() * prop.armor * 24); - } - if (damage) { - if (target["1hp"]) { - damage = 1; - } - target.hp = max(1, target.hp - damage); - disappearing_text({}, target, "-" + damage, { color: "red", xy: 1 }); - } - consume_mp(player, G.skills[data.name].mp, target); - xy_emit(player, "ui", { type: "throw", from: player.name, to: target.id, item: item.name }); - player.to_resend = "u+cid+reopen"; - if (target.is_player) { - resend(target, r); - } else { - target.u = true; - target.cid++; - } - } - if (data.name == "phaseout") { - if (!player.items[data.num] || player.items[data.num].name != "shadowstone") { - return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); - } - consume_one(player, data.num); - consume_mp(player, G.skills[data.name].mp); - player.s.phasedout = { ms: G.conditions.phasedout.duration }; - player.to_resend = "u+cid+reopen"; - } - if (data.name == "pcoat") { - if (!player.items[data.num] || player.items[data.num].name != "poison") { - return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); - } - consume_one(player, data.num); - consume_mp(player, G.skills[data.name].mp); - player.s.poisonous = { ms: G.skills.pcoat.cooldown }; - player.to_resend = "u+cid+reopen"; - } - if (data.name == "curse") { - //#TODO: last_curse variable + check for multiple curses - var attack = commence_attack(player, target, "curse"); - if (!attack.failed) { - resolve = attack; - add_pdps(player, null, player.attack / 2); - } else { - reject = attack; - cool = false; - } - } - if (data.name == "snowball") { - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - var found = false; - for (var i = player.isize - 1; i >= 0; i--) { - if (player.items[i] && player.items[i].name == "snowball") { - consume_one(player, i); - found = true; - break; - } - } - if (!found) { - return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); - } - var attack = commence_attack(player, target, "snowball"); - if (!attack.failed) { - resolve = attack; - } else { - reject = attack; - cool = false; - } - } - if (data.name == "entangle" || data.name == "tangle") { - if (data.name == "entangle") { - if (!player.items[data.num] || player.items[data.num].name != "essenceofnature") { - return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); - } - } - if (target.s.invincible) { - return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); - } - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - add_condition(target, "tangled", { ms: G.skills["entangle"].duration }); - target.abs = true; - target.moving = false; - xy_emit(player, "ui", { type: "entangle", from: player.name, to: target.id }); - consume_mp(player, G.skills[data.name].mp, target); - add_pdps(player, target, 4000); - if (data.name == "entangle") { - consume_one(player, data.num); - } - player.to_resend = "u+cid+reopen"; - if (target.is_monster) { - target.u = true; - target.cid++; - ccms(target); - } else { - resend(target, "u+cid"); - } - } - if (data.name == "4fingers") { - if (target.s.invincible) { - return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); - } - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - if (!is_in_pvp(player) && !is_same(player, target, 1)) { - return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); - } - add_condition(target, "fingered", { ms: G.skills["4fingers"].duration }); - add_condition(target, "stunned", { duration: G.skills["4fingers"].duration - 2000 }); - xy_emit(player, "ui", { type: "4fingers", from: player.name, to: target.name }); - consume_mp(player, G.skills[data.name].mp, target); - add_pdps(player, target, 1000); - resend(target, "u+cid"); - player.to_resend = "u+cid"; - } - if ( - [ - "quickpunch", - "quickstab", - "smash", - "mentalburst", - "purify", - "taunt", - "supershot", - "zapperzap", - "burst", - "piercingshot", - ].includes(data.name) - ) { - var attack = commence_attack(player, target, data.name); - if (!attack.failed) { - resolve = attack; - } else { - reject = attack; - cool = false; - } - } - if (data.name == "poisonarrow") { - if (!player.items[data.num] || player.items[data.num].name != "poison") { - return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); - } - - var attack = commence_attack(player, target, data.name); - if (!attack.failed) { - resolve = attack; - } else { - reject = attack; - cool = false; - } - - consume_one(player, data.num); - player.to_resend = "reopen"; - } - if (data.name == "revive") { - if (!player.items[data.num] || player.items[data.num].name != "essenceoflife") { - return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); - } - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - if (!target.rip) { - return socket.emit("game_response", { response: "target_alive", place: data.name, failed: true }); - } - - consume_one(player, data.num); - player.to_resend = "reopen"; - if (target.party == player.party) { - add_pdps(player, target, target.pdps / 5); - } - - if (target.hp != target.max_hp) { - reject = { response: "data", place: data.name, reason: "hp" }; - player.socket.emit("game_response", { response: "revive_failed", id: data.id }); - } else { - target.c.revival = { ms: 8000, f: player.name }; - } - resend(target, "u+cid"); - } - if (data.name == "cburst") { - var hit = {}; - var times = 0; - var attack = null; - var c_resolve = null; - consume_mp(player, G.skills[data.name].mp); - player.first_burst = true; - player.halt = true; - if (is_array(data.targets)) { - data.targets.forEach(function (t) { - // console.log(id); - var id = t[0]; - var mp = max(0, parseInt(t[1]) || 0); - if (player.mp < 20 || times > 16 || !mp) { - return; - } - times += 1; - var target = instances[player.in].monsters[id]; - if (!target) { - target = instances[player.in].players[id]; - } - if (!target || is_invinc(target) || target.name == player.name) { - return; - } - if (hit[id]) { - return; - } - hit[id] = true; - player.next_mp = mp; - attack = commence_attack(player, target, "cburst"); - if (!attack || !attack.projectile) { - return; - } - if (!c_resolve) { - c_resolve = attack; - attack.pids = [attack.pid]; - attack.targets = [attack.target]; - } else { - c_resolve.pids.push(attack.pid); - c_resolve.targets.push(attack.target); - } - }); - } - player.halt = false; - player.to_resend = "u+cid"; - if (!c_resolve) { - if (attack) { - reject = attack; - } - disappearing_text(player.socket, player, "NO HITS"); - } else { - resolve = c_resolve; - } - } - if (data.name == "partyheal") { - var targets = [player]; - var attack = null; - var hits = 0; - if (parties[player.party]) { - targets = []; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - targets.push(current); - }); - } - targets.forEach(function (target) { - attack = commence_attack(player, target, "partyheal"); - if (!attack.failed) { - hits = true; - } - }); - if (!hits) { - reject = attack; - } - } - if (data.name == "selfheal") { - var attack = commence_attack(player, player, data.name); - if (!attack.failed) { - resolve = attack; - } else { - reject = attack; - cool = false; - } - } - if (data.name == "darkblessing" || data.name == "warcry") { - consume_mp(player, G.skills[data.name].mp); - for (var id in instances[player.in].players) { - var target = instances[player.in].players[id]; - if ( - !target.npc && - distance(player, target, true) < G.skills[data.name].range && - (!(G.maps[player.map].pvp || is_pvp) || is_same(player, target, 1)) - ) { - target.s[G.skills[data.name].condition] = { ms: G.skills[data.name].duration, f: player.name }; - resend(target, "u+cid"); - add_pdps(player, target, 500); - } - } - xy_emit(player, "ui", { type: data.name }); - } - if (data.name == "3shot" || data.name == "5shot") { - player.halt = true; - var times = 0; - var hit = {}; - var reftarget = null; - var targets = 3; - var attack = null; - var c_resolve = null; - if (data.name == "5shot") { - targets = 5; - } - //console.log(data.ids); - if (is_array(data.ids)) { - data.ids.forEach(function (id) { - if (times >= targets) { - return; - } - times += 1; - var target = instances[player.in].monsters[id]; - if (!target) { - target = instances[player.in].players[id]; - } - if (!target || is_invinc(target) || target.name == player.name) { - attack = { failed: true, place: data.name, reason: "no_target" }; - return; - } - if (hit[id]) { - return; - } - hit[id] = true; - attack = commence_attack(player, target, data.name); - if (!attack || !attack.projectile) { - return; - } - if (!c_resolve) { - reftarget = target; - c_resolve = attack; - attack.pids = [attack.pid]; - attack.targets = [attack.target]; - } else { - c_resolve.pids.push(attack.pid); - c_resolve.targets.push(attack.target); - } - // if(times==1 && attack==null) times=40; - }); - } - consume_mp(player, G.skills[data.name].mp, reftarget); - player.halt = false; - player.to_resend = "u+cid"; - if (!c_resolve) { - if (attack) { - reject = attack; - } - disappearing_text(player.socket, player, "NO HITS"); - } else { - resolve = c_resolve; - } - } - if (data.name == "track") { - var list = []; - for (var id in instances[player.in].players) { - if (id == player.name) { - continue; - } - var target = instances[player.in].players[id]; - var current = { sound: "wmp" }; - if (target.npc) { - continue; - } - if (is_invis(target)) { - current.invis = true; - } - if (target.type == "rogue" || target.type == "ranger") { - current.sound = "rr"; - } else if (target.type == "priest" || target.type == "mage") { - current.sound = "pm"; - } - current.dist = distance(player, target); - if (current.dist < G.skills.track.range) { - list.push(current); - } - } - list.sort(function (a, b) { - return a.dist - b.dist; - }); - consume_mp(player, G.skills[data.name].mp); - xy_emit(player, "ui", { type: "track", name: player.name }); - socket.emit("track", list); - player.to_resend = "u+cid"; - } - if (data.name == "agitate") { - var ids = []; - for (var id in instances[player.in].monsters) { - var target = instances[player.in].monsters[id]; - if (target.target == player.name) { - ids.push(id); - continue; - } // why did the missing continue cause a disengage I have no idea [25/03/23] - if (target.target && !(get_player(target.target) && is_same(player, get_player(target.target), 1))) { - continue; - } - var dist = distance(player, target); - if (dist < G.skills.agitate.range) { - if (target.target) { - stop_pursuit(target, { redirect: true, cause: "agitate redirect" }); - } - target.cid += 1; - target.u = true; - target.last.attacked = new Date(); - target_player(target, player); - ids.push(id); - } - } - consume_mp(player, G.skills[data.name].mp, target); - xy_emit(player, "ui", { type: "agitate", name: player.name, ids: ids }); - player.to_resend = "u+cid"; - add_pdps(player, null, 1000); - } - if (data.name == "absorb") { - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - var ids = []; - if (is_same(player, target, 1)) { - consume_mp(player, G.skills[data.name].mp); - } else { - if (player.mp < G.skills[data.name].mp * 6 || player.level < 75) { - return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); - } - consume_mp(player, G.skills[data.name].mp * 6); - if (Math.random() < 0.95) { - consume_skill(player, data.name); - resend(player, "u+cid"); - return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); - } - } - for (var id in instances[player.in].monsters) { - var monster = instances[player.in].monsters[id]; - if (monster.target == target.name) { - stop_pursuit(monster, { redirect: true, cause: "absorb redirect" }); - monster.cid += 1; - monster.u = true; - target_player(monster, player); - ids.push(id); - } - } - xy_emit(player, "ui", { type: "absorb", name: player.name, from: data.id, ids: ids }); - player.to_resend = "u+cid"; - add_pdps(player, target, 1000); - } - if (data.name == "stomp") { - var ids = []; - var reftarget = null; - for (var id in instances[player.in].monsters) { - var target = instances[player.in].monsters[id]; - var dist = distance(player, target); - if (dist < G.skills.stomp.range) { - if (target.immune) { - player.hitchhikers.push(["game_response", { response: "skill_immune", skill: data.name }]); - player.to_resend = "nc"; - disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: player.id }); - } else if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { - ids.push(id); - add_pdps(player, target, 500); - } - } - } - if (is_in_pvp(player, 1)) { - for (var id in instances[player.in].players) { - var target = instances[player.in].players[id]; - var dist = distance(player, target); - if (dist < G.skills.stomp.range && !target.npc && !is_same(player, target, 1) && !target.s.invincible) { - if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { - reftarget = target; - resend(target, "u+cid"); - ids.push(id); - add_pdps(player, target, 5000); - } - } - } - } - consume_mp(player, G.skills[data.name].mp, reftarget); - xy_emit(player, "ui", { type: "stomp", name: player.name, ids: ids }); - player.to_resend = "u+cid"; - resolve = { response: "data", place: data.name, success: true, ids: ids }; - } - if (data.name == "scare") { - var ids = []; - consume_mp(player, G.skills[data.name].mp); - for (var id in instances[player.in].monsters) { - var target = instances[player.in].monsters[id]; - if (target.target == player.name) { - if (target.immune) { - player.hitchhikers.push(["game_response", { response: "skill_immune", skill: "scare" }]); - player.to_resend = "nc"; - disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: player.id }); - } else { - stop_pursuit(target, { force: true, cause: "scare" }); - target.abs = true; - target.moving = false; - ids.push(id); - } - } - } - xy_emit(player, "ui", { type: "scare", name: player.name, ids: ids }); - player.to_resend = "u+cid"; - } - if (data.name == "huntersmark") { - if (target.is_player && !is_in_pvp(player) && !is_same(player, target, 1)) { - return socket.emit("game_response", { response: "skill_cant_pve", place: data.name, failed: true }); - } - consume_mp(player, G.skills[data.name].mp, target); - add_condition(target, "marked"); - if (target.is_player) { - resend(target, "u+cid"); - } else { - target.cid++; - target.u = true; - } - xy_emit(player, "ui", { type: "huntersmark", name: player.name, id: target.id }); - player.to_resend = "u+cid"; - } - if (data.name == "charm") { - consume_mp(player, G.skills[data.name].mp); - if (Math.random() > 0.01) { - socket.emit("game_response", "charm_failed"); - xy_emit(player, "ui", { type: "charm", name: player.name, id: target.id, fail: true }); - } else { - target.cid++; - target.u = true; - target.s.charmed = { ms: G.conditions.charmed.duration }; - xy_emit(player, "ui", { type: "charm", name: player.name, id: target.id }); - } - player.to_resend = "u+cid"; - } - if (data.name == "cleave" || data.name == "shadowstrike") { - player.to_resend = "u+cid"; - if (data.name == "shadowstrike") { - if (!player.items[data.num] || player.items[data.num].name != "shadowstone") { - return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); - } - consume_one(player, data.num); - player.to_resend = "u+cid+reopen"; - } - player.halt = true; - var ids = []; - var reftarget = null; - var c_resolve = null; - for (var id in instances[player.in].monsters) { - var target = instances[player.in].monsters[id]; - var dist = distance(player, target); - if (dist < G.skills[data.name].range) { - attack = commence_attack(player, target, data.name); - if (!attack || !attack.projectile) { - continue; - } - ids.push(id); - if (!c_resolve) { - c_resolve = attack; - attack.pids = [attack.pid]; - attack.targets = [attack.target]; - } else { - c_resolve.pids.push(attack.pid); - c_resolve.targets.push(attack.target); - } - } - } - if (is_in_pvp(player, 1)) { - for (var id in instances[player.in].players) { - var target = instances[player.in].players[id]; - var dist = distance(player, target); - if (dist < G.skills[data.name].range && !target.npc && !is_same(player, target, 1)) { - attack = commence_attack(player, target, data.name); - if (!attack || !attack.projectile) { - continue; - } - ids.push(id); - if (!c_resolve) { - reftarget = target; - c_resolve = attack; - attack.pids = [attack.pid]; - attack.targets = [attack.target]; - } else { - c_resolve.pids.push(attack.pid); - c_resolve.targets.push(attack.target); - } - } - } - } - consume_mp(player, G.skills[data.name].mp, reftarget); - xy_emit(player, "ui", { type: data.name, name: player.name, ids: ids }); - player.halt = false; - if (c_resolve) { - resolve = c_resolve; - } - } - if (data.name == "magiport") { - var pported = false; - consume_mp(player, G.skills[data.name].mp); - if (!is_pvp && mode.pve_safe_magiports) { - if (!magiportations[player.name]) { - magiportations[player.name] = {}; - } - magiportations[player.name][target.name] = true; - target.socket.emit("magiport", { name: player.name }); - player.socket.emit("game_response", { response: "magiport_sent", id: data.id }); - xy_emit(player, "ui", { type: "magiport", name: player.name }); - } else { - if (is_same(player, target, 1) || !target.slots.helmet) { - ported = magiport_someone(target, player); - } - if (!ported) { - player.socket.emit("game_response", { response: "magiport_failed", id: data.id }); - } - } - player.to_resend = "u+cid"; - } - if (data.name == "blink") { - var x = parseFloat(data.x) || 0; - var y = parseFloat(data.y) || 0; - var spot = safe_xy_nearby(player.map, x, y); - if (!spot) { - return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); - } - player.s.blink = { ms: 200 }; - player.s.blink.in = player.in; - player.s.blink.map = player.map; - player.s.blink.x = spot.x; - player.s.blink.y = spot.y; - player.s.blink.d = 0; - if (in_arr(data.direction, [1, 2, 3])) { - player.s.blink.d = data.direction; - } - if (player.role != "gm") { - consume_mp(player, G.skills[data.name].mp); - } - // xy_emit(player,"ui",{type:"blinking",name:player.name}); - resend(player, "u+cid"); - // #TODO: Appear animation for non-self's [21/05/18] - } - if (data.name == "warp") { - var x = parseFloat(data.x) || 0; - var y = parseFloat(data.y) || 0; - var ins = data.in || "main"; - var instance = instances[ins]; - if (!instance) { - return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); - } - if (instance.mount != instances[player.in].mount) { - return player.socket.emit("game_response", { response: "cant_in_bank", place: data.name, failed: true }); - } - var spot = safe_xy_nearby(instance.name, x, y); - if (!spot) { - return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); - } - player.s.blink = { ms: 200 }; - player.s.blink.in = ins; - player.s.blink.map = instance.name; - player.s.blink.x = spot.x; - player.s.blink.y = spot.y; - player.s.blink.d = 0; - if (in_arr(data.direction, [1, 2, 3])) { - player.s.blink.d = data.direction; - } - if (player.role != "gm") { - consume_mp(player, G.skills[data.name].mp); - } - // xy_emit(player,"ui",{type:"blinking",name:player.name}); - player.to_resend = "u+cid"; - // #TODO: Appear animation for non-self's [21/05/18] - } - if (data.name == "mluck") { - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - consume_mp(player, G.skills[data.name].mp); - if ( - !target.s[G.skills[data.name].condition] || - !target.s[G.skills[data.name].condition].strong || - target.s[G.skills[data.name].condition].f == player.name - ) { - target.s[G.skills[data.name].condition] = { ms: G.conditions.mluck.duration, f: player.name }; - } - if (target.owner == player.owner) { - target.s[G.skills[data.name].condition].strong = true; - } - xy_emit(player, "ui", { type: "mluck", from: player.name, to: target.name }); - resend(target, "u+cid"); - resend(player, "u+cid"); - } - if (data.name == "rspeed") { - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - consume_mp(player, G.skills[data.name].mp); - target.s[G.skills[data.name].condition] = { ms: G.conditions.rspeed.duration, f: player.name }; - xy_emit(player, "ui", { type: "rspeed", from: player.name, to: target.name }); - resend(target, "u+cid"); - resend(player, "u+cid"); - if (player.party == target.party && player != target) { - add_pdps(player, target, 2000); - } - } - if (data.name == "reflection") { - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - consume_mp(player, G.skills[data.name].mp); - target.s[G.skills[data.name].condition] = { ms: G.conditions.reflection.duration, f: player.name }; - xy_emit(player, "ui", { type: "reflection", from: player.name, to: target.name }); - resend(target, "u+cid"); - resend(player, "u+cid"); - if (player.party == target.party && player != target) { - add_pdps(player, target, 4000); - } - } - if (data.name == "energize") { - if (distance(player, target, true) > G.skills[data.name].range) { - return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); - } - if (!player.mp) { - return socket.emit("game_response", { response: "no_mp", place: data.name, failed: true }); - } - var mp = parseInt(data.mp) || 10000; - mp = max(1, min(player.mp, mp)); - if (mp > target.max_mp - target.mp) { - mp = target.max_mp - target.mp; - } - target.mp += mp; - player.mp -= mp; - if (player.party == target.party && player != target) { - add_pdps(player, target, mp * 2); - } - disappearing_text(player.socket, player, "-" + mp, { color: "mana", xy: 1 }); - disappearing_text(target.socket, target, "+" + mp, { color: "mana", xy: 1 }); - target.s[G.skills[data.name].condition] = { ms: G.conditions[G.skills[data.name].condition].duration }; - xy_emit(player, "ui", { type: "energize", from: player.name, to: target.name }); - resend(target, "u+cid"); - resend(player, "u+cid"); - } - if (data.name == "alchemy") { - var gold = 0; - var rate = 0.8; - if (player.level >= 100) { - rate = 1.12; - } else if (player.level >= 90) { - rate = 1.1; - } else if (player.level >= 80) { - rate = 1.06; - } else if (player.level >= 70) { - rate = 1; - } else if (player.level >= 60) { - rate = 0.92; - } else if (player.level >= 50) { - rate = 0.86; - } - consume_mp(player, G.skills[data.name].mp); - xy_emit(player, "ui", { type: "alchemy", name: player.name }); - for (var i = 0; i < player.isize; i++) { - if (!player.items[i] || player.items[i].l) { - continue; - } - gold = calculate_item_value(player.items[i]); - consume_one(player, i); - break; - } - player.gold += gold * rate; - resend(player, "reopen"); - socket.emit("game_response", { response: "gold_received", gold: gold * rate }); - } - if (cool) { - consume_skill(player, data.name); - } - if (player.to_resend) { - resend(player, player.to_resend); - } - if (reject) { - if (!reject.response) { - reject.response = "data"; - } - socket.emit("game_response", reject); - } else if (resolve) { - socket.emit("game_response", resolve); - } - }); - socket.on("click", function (data) { - // You'll be missed 'click' method, the 'click' method was the first method on this server, it was used as an attack method up until [17/06/18] - at this date, there were 3 simple conditions left which checked for data.button=="right" - the game matured so that all interactions were handled client-side rather than processed server-side - socket.emit("game_log", "'click' method is deprecated."); - }); - socket.on("attack", function (data) { - return socket.fs.skill({ name: "attack", id: data.id }); - }); - socket.on("heal", function (data) { - return socket.fs.skill({ name: "heal", id: data.id }); - }); - socket.on("interaction", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data.type == "newyear_tree") { - var x = ""; - if ( - !G.maps[player.map].ref || - !G.maps[player.map].ref[data.type] || - distance(G.maps[player.map].ref[data.type], player, true) > 300 - ) { - return socket.emit("game_response", "distance"); - } - if (!player.s.holidayspirit && player.esize) { - add_item(player, "funtoken"); - x = "+reopen"; - } - add_condition(player, "holidayspirit"); - resend(player, "u+cid" + x); - } else if (["redorb", "blueorb", "greenorb", "yelloworb"].includes(data.type)) { - if ( - !G.maps[player.map].ref || - !G.maps[player.map].ref[data.type] || - distance(G.maps[player.map].ref[data.type], player, true) > 40 - ) { - return socket.emit("game_response", "distance"); - } - ["redorb", "blueorb", "greenorb", "yelloworb"].forEach(function (s) { - delete player.s[s]; - }); - add_condition(player, data.type); - resend(player, "u+cid"); - } else if (data == "the_lever") { - if (player.map != "resort_e") { - return socket.emit("game_response", "distance"); - } - player.s.magiport = { ms: 300 }; - player.s.magiport.x = player.x; - player.s.magiport.y = player.y; - player.s.magiport.f = player.name; - player.s.magiport.in = "resort"; - player.s.magiport.map = "resort"; - resend(player, "u+cid"); - } else if (data.type == "dailytask") { - if (!G.maps.main.ref.dailytask || distance(G.maps.main.ref.dailytask, player, true) > 150) { - return socket.emit("game_response", "distance"); - } - player.p.monsterhunt = { ms: 60 * 60 * 1000, m: "goo" }; - socket.emit("game_response", { response: "monsterhunt", monster: "goo" }); - } else if (data.key && player.konami) { - if (data.key == "B" && player.konami.length < 20) { - player.konami.push(data.key[0]); - } else if (data.key == "A") { - if (player.konami.join("") == "uuddlrlrB") { - player.tskin = "konami"; - resend(player, "u+cid"); - if (!player.p.target_lock || !G.monsters[player.p.target_lock] || hsince(player.p.dt.last_tl) > 15 * 24) { - var monsters = []; - for (var name in G.monsters) { - if (!G.monsters[name].special && !G.monsters[name].stationary && G.monsters[name].c) { - monsters.push(name); - } - } - player.p.target_lock = random_one(monsters); - player.p.dt.last_tl = new Date(); - } - socket.emit("game_response", { response: "target_lock", monster: player.p.target_lock }); - } else { - player.konami = []; - } - } - } - }); - socket.on("mreport", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - var a = 0; - var b = 0; - a = parseFloat(data.x) || 0; - b = parseFloat(data.y) || 0; - // console.log(JSON.stringify(data)); - socket.emit("game_log", "" + simple_distance({ x: player.x, y: player.y }, { x: a, y: b })); - }); - socket.on("move", function (data) { - // if(observers[socket.id]) observers[socket.id].x=data.x,observers[socket.id].y=data.y; Old observer code [17/02/17] - var player = players[socket.id]; - var current = -1; - var going = -1; - var actual = true; - var x = parseFloat(data.going_x) || 0; - var y = parseFloat(data.going_y) || 0; - if (data.pet) { - player = player.monster; - actual = false; - } - if (data.key && (!player.konami || player.konami.length < 20)) { - // console.log(data.key); - if (!player.konami) { - player.konami = []; - } - player.konami.push(data.key[0]); - } - if (player && player.m == data.m && can_walk(player) && (x != player.x || y != player.y)) { - // if(is_sdk) server_log("Player moving to: "+data.going_x+","+data.going_y); - - // player.x=data.x; player.y=data.y; [03/08/16] Seems like a really bad idea to update x/y based on what players provide - data.x = parseFloat(data.x) || 0; - data.y = parseFloat(data.y) || 0; - player.going_x = x; - player.going_y = y; - if (smap_data[player.map] != -1 && mode.enforce_smap) { - current = smap_data[player.map][rphash(data.x, data.y)]; - going = smap_data[player.map][rphash(player.going_x, player.going_y)]; - // server_log("current:"+current+" going:"+going+" real:"+smap_data[player.map][phash(player.x,player.y)]); - if (current === undefined || current >= 2 || going === undefined || going >= 2) { - server_log( - "#C cheater: " + - player.name + - " current: " + - current + - "[" + - data.x + - "," + - data.x + - "] going: " + - going + - "[" + - player.going_x + - "," + - player.going_y + - "] at " + - player.map, - 1, - ); - appengine_log("violation", "move_line: " + player.name + " afk: " + player.afk + " code: " + player.code); - player.socket.emit("game_log", "Line violation detected"); - player.socket.emit("game_log", "Make sure you only move with the built-in move function"); - defeat_player(player); - transport_player_to(player, "jail"); - return; - } - } - // player.check_x=player.going_x; player.check_y=player.going_y; player.checked_xy=false; - start_moving_element(player); - if ( - !player.pet && - simple_distance(player, { x: data.x, y: data.y }) > 132 && - current == 0 && - mode.lcorrection - ) { - // server_log("Correction sent"); - socket.emit("correction", { x: player.x, y: player.y }); - } - if (actual) { - var change = false; - for (var id in player.c) { - if (G.conditions[id] && !G.conditions[id].can_move) { - change = true; - delete player.c[id]; - } - } - if (change) { - resend(player, "u+cid"); - } - } - //if(smap_data[player.map][rphash(player.x,player.y)]===0) push_xyh(player,player.x,player.y); - //else push_xyh(player,data.x,data.y); - } - }); - socket.on("open_chest", function (data) { - var chest = chests[data.id]; - var player = players[socket.id]; - var reopen = false; - if (!player) { - return; - } - var r = { id: data.id, goldm: player.goldm, opener: player.name, items: [] }; - if (chest && simple_distance(chest, player) > 400) { - r.goldm = 1; - r.dry = true; - } - if (chest && msince(chest.date) > 8) { - r.goldm = 1; - r.stale = true; - } - if (player.map == "woffice" || G.maps[player.map].mount || is_invis(player)) { - return fail_response("loot_failed"); - } - try { - if (chest && !chest.pvp && chest.gold > 100000000 && !player.stealth) { - broadcast("server_message", { - message: player.name + " looted " + to_pretty_num(server_tax(round(chest.gold * r.goldm), true)) + " gold", - color: "gold", - type: "server_gold", - name: player.name, - }); - } - if (chest && player.owner && chest.owners && !in_arr(player.owner, chest.owners) && !W.chest[player.owner]) { - W.chest[player.owner] = new Date(); - server_log("SEVERE - Cross Loot from " + player.name + " not from " + chest.owners.toString()); - } - if (chest && !player.party) { - var all_items = chest.items.slice(0); - all_items.concat(chest.pvp_items); - if (!can_add_items(player, all_items)) { - return fail_response("loot_no_space"); - } - delete chests[data.id]; // The add_shells routine was at the top, so when inventory was full, attempting to open the chest gave shells infinitely, repeated lesson, always remove before adding, or exceptions [11/07/18] - if (chest && chest.cash) { - add_shells(player, chest.cash, "chest", true, "override"); - } - chest.items.forEach(function (item) { - item.src = "pvp"; - add_item(player, item, { found: 1, m: 1, v: B.v }); - reopen = true; - var ritem = cache_item(item); - ritem.looter = player.name; - r.items.push(ritem); - socket.emit("game_log", { message: "Found " + item_to_phrase(item), color: "#4BAEAA" }); - if (player.t) { - player.t.dgold += round(G.items[item.name].g * 0.6); - } - }); - (chest.pvp_items || []).forEach(function (item) { - item.v = new Date(); - if (can_add_item(player, item)) { - add_item(player, item, { found: 1, v: B.v }); - reopen = true; - var ritem = cache_item(item); - ritem.looter = player.name; - ritem.pvp_loot = true; - r.items.push(ritem); - socket.emit("game_log", { message: "Looted " + item_to_phrase(item), color: "#4BAEAA" }); - } else { - lostandfound_logic(item); - var ritem = cache_item(item); - ritem.looter = null; - ritem.lostandfound = true; - r.items.push(ritem); - socket.emit("game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); - } - }); - r.gold = round(chest.gold * r.goldm) + round(chest.egold || 0); - r.gold = server_tax(r.gold); - player.gold += r.gold; - if (player.t) { - player.t.cgold += r.gold; - } - if (r.gold) { - socket.emit("game_log", { message: to_pretty_num(r.gold) + " gold", color: "gold" }); - } - socket.emit("disappearing_text", { - message: "+" + r.gold, - x: chest.x, - y: chest.y - 10, - args: { color: "+gold", size: "large" }, - }); - if (!r.items.length) { - delete r.items; - } - resend(player, (reopen && "reopen+nc+inv") || ""); - socket.emit("chest_opened", r); - } else if (chest) { - // var gold=round(chest.gold/parties[player.party].length); - r.party = true; - var chest = chests[data.id]; - var reopen = {}; - delete chests[data.id]; - if (chest && chest.cash) { - add_shells(player, chest.cash, "chest", true, "override"); - } - chest.items.forEach(function (item) { - // console.log(item); - var pool = 0; - var can = {}; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && can_add_item(current, item)) { - pool += current.share; - can[name] = true; - } - }); - var pool_winner = Math.random() * pool; - var pool_current = 0; - var awarded = false; - parties[player.party].forEach(function (name) { - if (awarded) { - return; - } - var current = players[name_to_id[name]]; - if (current && can[name]) { - if (pool_winner <= pool_current + current.share) { - awarded = true; - add_item(current, item, { found: 1, m: 1, v: B.v }); - reopen[current.id] = true; - var ritem = cache_item(item); - ritem.looter = current.name; - r.items.push(ritem); - party_emit(player.party, "game_log", { - message: current.name + " found " + item_to_phrase(item), - color: "#4BAEAA", - }); - if (current.t) { - current.t.dgold += round(G.items[item.name].g * 0.6); - } - } else { - pool_current += current.share; - } - } - }); - if (!awarded) { - lostandfound_logic(item); - var ritem = cache_item(item); - ritem.looter = null; - ritem.lostandfound = true; - r.items.push(ritem); - party_emit(player.party, "game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); - } - }); - (chest.pvp_items || []).forEach(function (item) { - item.v = new Date(); - var pool = 0; - var can = {}; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && current.share && can_add_item(current, item)) { - pool += current.share; - can[name] = true; - } - }); - var pool_winner = Math.random() * pool; - var pool_current = 0; - var awarded = false; - parties[player.party].forEach(function (name) { - if (awarded) { - return; - } - var current = players[name_to_id[name]]; - if (current && can[name]) { - if (pool_winner <= pool_current + current.share) { - awarded = true; - add_item(current, item, { found: 1, v: B.v }); - reopen[current.id] = true; - var ritem = cache_item(item); - ritem.looter = current.name; - ritem.pvp_loot = true; - r.items.push(ritem); - party_emit(player.party, "game_log", { - message: current.name + " looted " + item_to_phrase(item), - color: "#4BAEAA", - }); - } else { - pool_current += current.share; - } - } - }); - if (!awarded) { - lostandfound_logic(item); - var ritem = cache_item(item); - ritem.looter = null; - ritem.pvp_loot = true; - ritem.lostandfound = true; - r.items.push(ritem); - party_emit(player.party, "game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); - } - }); - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - var cgold = - round(chest.gold * (current.share || 0) * r.goldm) + round((chest.egold || 0) * (current.share || 0)); - r.gold = cgold = server_tax(cgold); - current.gold += cgold; - if (current.t) { - current.t.cgold += cgold; - } - if (cgold) { - current.socket.emit("game_log", { message: to_pretty_num(cgold) + " gold", color: "gold" }); - } - if (current.in == player.in) { - current.socket.emit("disappearing_text", { - message: "+" + cgold, - x: chest.x, - y: chest.y - 10, - args: { color: "gold", size: "large" }, - }); - } - resend(current, (reopen[current.id] && "reopen+nc+inv") || ""); - current.socket.emit("chest_opened", r); - }); - } else { - socket.emit("chest_opened", { id: data.id, gone: true }); - } - } catch (e) { - delete chests[data.id]; // If this didn't exist, any exception would end up being a source for infinite gold and items - log_trace("#X chest_error", e); - return fail_response("error"); - } - }); - socket.on("auth", function (data) { - if (gameplay == "test" && data.passphrase != "potato salad") { - return socket.emit("game_log", "Wrong passphrase!"); - } - if (observers[socket.id] && observers[socket.id].auth_engaged) { - return socket.emit("game_log", "Authorization in progress."); - } - if (dc_players[data.character]) { - return socket.emit("game_log", "Authorization in progress."); - } - if (!server.live || !observers[socket.id] || players[socket.id]) { - return; - } - if (Object.keys(players).length >= max_players) { - socket.emit("game_error", "Can't accept more than " + max_players + " players at this time"); - return; - } - socket.observer_secret = randomStr(24); - observers[socket.id].auth_engaged = true; - appengine_call( - "start_character", - { - auth: data.user + "-" + data.auth, - secret: socket.observer_secret, - code_slot: data.code_slot, - character: data.character, - mode: gameplay, - ip: get_ip(socket), - suffix: "/" + data.character, - }, - function (result) { - if (observers[socket.id]) { - observers[socket.id].auth_engaged = false; - } - if (result.failed) { - socket.emit("game_error", "Failed: " + result.reason); - return; - } - // console.log(JSON.stringify(result)); - server_log("start_character: " + JSON.stringify(result.character.name), 1); - var player = { u: true, is_player: true, humanoid: true, secret: socket.observer_secret }; - for (prop in result.character) { - player[prop] = result.character[prop]; - } - if (!instances[player.map] || !instances[player.map].allow || instances[player.map].mount) { - var place = G.maps[player.map].on_exit || G.maps[B.start_map].on_exit || ["main", 0]; - player.map = player.in = place[0]; - player.x = G.maps[player.map].spawns[place[1]][0]; - player.y = G.maps[player.map].spawns[place[1]][1]; - } else { - player.in = player.map; - } - player.owner = data.user; - player.auth = data.user + "-" + data.auth; - player.last_sync = new Date(); - player.socket = socket; - player.max_stats = result.stats; - - if (data.bot == variables.bot_key) { - player.bot = true; - player.afk = "bot"; - } - if (data.no_html) { - player.afk = "code"; - try { - player.controller = (name_to_id[data.no_html] && data.no_html) || ""; - } catch (e) { - player.controller = ""; - } - } - if (!player.afk) { - player.afk = true; - } - if (gameplay == "test") { - player.name += parseInt(Math.random() * 10000); - } - player.real_id = player.id; - player.id = player.name; - - player.total_ips = 1; - player.width = 26; - player.height = 36; - player.damage_type = G.classes[player.type].damage_type; - player.xrange = 25; - player.red_zone = 0; - player.targets = player.targets_p = player.targets_m = player.targets_u = 0; - player.cid = 1; - player.hits = 0; - player.kills = 0; - player.m = 0; // map number - /* party variables*/ - player.pdps = 0; - player.party_length = 1; - player.party_luck = 0; - player.party_xp = 0; - player.party_gold = 0; - player.share = 0.1; - player.cx = player.cx || {}; - if (!player.s) { - player.s = {}; - } - player.t = { mdamage: 0, cgold: 0, dgold: 0, xp: 0, start: new Date() }; - player.hitchhikers = []; // socket events to be registered after a resend - player.last = { attack: future_ms(-1200), attacked: really_old }; - player.bets = {}; - player.base = dbase; - player.age = parseInt(ceil(hsince(new Date(player.created)) / 24.0)); - // player.vision=[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision]; - // player.vision[0]=min(1000,player.vision[0]); - // player.vision[1]=min(700,player.vision[1]); - player.vision = B.vision; - - if (!player.verified) { - player.s.notverified = { ms: 30 * 60 * 1000 }; - } else if (player.s.notverified) { - player.s.notverified = { ms: 100 }; - } - - if (player.guild) { - console.log(player.guild); - player.guild = player.guild.short; - } - - if (gameplay == "hardcore") { - reset_player(player); - } // || gameplay=="test" - - init_player(player); - if (!observers[socket.id]) { - // observer hang up before "auth" - server_log("Abrupt stop for " + result.character.name, 1); - if (gameplay != "hardcore" && gameplay != "test") { - dc_players[player.real_id] = player; - } - sync_loop(); - return; - } - try { - delete_observer(socket); - } catch (e) {} - - players[socket.id] = player; - resume_instance(instances[player.in]); - instances[player.in].players[player.id] = player; - pmap_add(player); - - name_to_id[player.name] = socket.id; - id_to_id[player.id] = socket.id; - - cache_player_items(player); - invincible_logic(player); - serverhop_logic(player); - calculate_player_stats(player); - - if (data.epl == "mas" && data.receipt) { - player.platform = "mas"; - verify_mas_receipt(player, data.receipt); - } else if (data.epl == "steam" && data.ticket) { - player.platform = "steam"; - verify_steam_ticket(player, data.ticket); - } else { - player.platform = "web"; - if (result.character.pid) { - player.auth_id = result.character.pid; - } // part of the new restriction system [02/05/19] - } - - if (mode.drm_check) { - if (result.character.drm && !player.auth_id) { - player.s.authfail = { ms: 900000 * 1000 }; - } else if (player.s.authfail) { - player.s.authfail = { ms: 100 }; - } - } - - if (!is_player_allowed(player)) { - socket.emit("disconnect_reason", "limits"); - socket.disconnect(); - } else { - var cdata = player_to_client(player); - player.ipass = cdata.ipass = randomStr(12); - player.last_ipass = new Date(); - player.last.attack = future_ms(-10000); - player.last.transport = future_ms(-10000); - cdata.home = player.p.home; - cdata.friends = player.friends; - cdata.acx = player.p.acx; - cdata.xcx = player.p.xcx; - cdata.emx = player.p.emx; - cdata.info = instances[player.in].info; - cdata.base_gold = D.base_gold; - broadcast_e(true); - cdata.s_info = E; - if (result.code) { - cdata.code = result.code; - cdata.code_slot = result.code_slot; - cdata.code_version = result.code_version; - } - cdata.entities = send_all_xy(player, { raw: true }); - socket.emit("start", cdata); - total_players++; - } - }, - function (err) { - server_log("start_character_failed: " + data.character + " reason: " + err, 1); - if (observers[socket.id]) { - observers[socket.id].auth_engaged = false; - } - }, - ); - }); - socket.on("use", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data.item == "hp" || data.item == "mp") { - if (player.last.potion && mssince(player.last.potion) < 0) { - return fail_response("not_ready"); - } - player.last.potion = future_ms(4000); - if (data.item == "hp") { - player.hp += 50; - disappearing_text(socket, player, "+50", { color: "green", xy: 1, s: "hp", nohp: 1 }); - } - if (data.item == "mp") { - player.mp += 100; - disappearing_text(socket, player, "+100", { color: "#006AA9", xy: 1, s: "mp", nomp: 1 }); - } - player.hp = min(player.hp, player.max_hp); - player.mp = min(player.mp, player.max_mp); - // calculate_player_stats(player); [22/11/16] - player.cid++; - player.u = true; - socket.emit("player", player_to_client(player)); - socket.emit("eval", { code: "pot_timeout(4000)" }); - } - success_response({}); - }); - socket.on("friend", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - server_log("friend: " + JSON.stringify(data)); - if (data.event == "request") { - var friend = players[name_to_id[data.name || ""]]; - if (!friend) { - return fail_response("friend_rleft"); - } - if (in_arr(friend.owner, player.friends) || friend.owner == player.owner) { - return success_response("friend_already"); - } - requests[player.name + "-" + friend.name] = { a: player.owner, b: friend.owner }; - friend.socket.emit("friend", { event: "request", name: player.name }); - return success_response("friend_rsent"); - } - if (data.event == "accept") { - if (!requests[data.name + "-" + player.name]) { - return fail_response("friend_expired"); - } - appengine_call( - "set_friends", - { user1: requests[data.name + "-" + player.name].a, user2: requests[data.name + "-" + player.name].b }, - function (result) { - var player = players[socket.id]; - if (result.failed) { - if (player) { - socket.emit("game_response", { response: "friend_failed", reason: result.reason }); - } - return; - } - //var friend=players[name_to_id[data.name||""]]; - //if(player) - //if(friend) friend.emit("friend",{event:"accepted",name:player.name}); - }, - function () { - var player = players[socket.id]; - if (player) { - socket.emit("game_response", { response: "friend_failed", reason: "coms failure" }); - } - }, - ); - requests[data.name + "-" + player.name] = false; - } - if (data.event == "unfriend") { - appengine_call( - "not_friends", - { user1: player.owner, user2: data.name }, - function (result) { - var player = players[socket.id]; - if (result.failed) { - if (player) { - socket.emit("game_response", { response: "unfriend_failed", reason: result.reason }); - } - return; - } - }, - function () { - var player = players[socket.id]; - if (player) { - socket.emit("game_response", { response: "unfriend_failed", reason: "coms failure" }); - } - }, - ); - } - success_response({ success: false, in_progress: true }); - }); - socket.on("duel", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data.event == "challenge") { - var invited = players[name_to_id[data.id || data.name]]; - if (!invited || invited.id == player.id) { - return socket.emit("game_log", "Invalid"); - } - if (invited.duel || player.duel) { - return socket.emit("game_log", "Already dueling"); - } - invited.socket.emit("duel", { event: "chellenge", name: player.name }); - socket.emit("game_response", { response: "challenge_sent", name: invited.name }); - invited.socket.emit("game_response", { response: "challenge_received", name: player.name }); - challenges[player.name] = invited.name; - } else if (data.event == "accept") { - var challenger = players[name_to_id[data.id || data.name]]; - if (!challenger || challenges[challenger.name] != player.name) { - return socket.emit("game_log", "Challenge expired"); - } - if (challenger.duel || player.duel) { - return socket.emit("game_log", "Already dueling"); - } - if (is_in_pvp(challenger) || is_in_pvp(player)) { - return socket.emit("game_log", "Can't start a duel if any of the parties are already in a pvp zone"); - } - delete challenges[challenger.name]; - challenger.socket.emit("game_response", { response: "challenge_accepted", name: player.name }); - var name = randomStr(20); - var a = []; - var b = []; - instance = create_instance(name, "duelland"); - instance.info = { - seconds: (is_sdk && 20) || 60, - active: false, - A: [player_to_summary(challenger)], - B: [player_to_summary(player)], - id: name, - }; - instance.info.A[0].active = true; - instance.info.B[0].active = true; - clean_slate(challenger); - transport_player_to(challenger, name, 1); - if (challenger.party) { - parties[challenger.party].forEach(function (p) { - a.push(p); - }); - } else { - a = [challenger.name]; - } - clean_slate(player); - transport_player_to(player, name, 2); - if (player.party) { - parties[player.party].forEach(function (p) { - b.push(p); - }); - } else { - b = [player.name]; - } - if (!E.duels) { - E.duels = {}; - } - var duel = { - challenger: challenger.name, - a: a, - vs: player.name, - b: b, - instance: name, - seconds: (is_sdk && 20) || 60, - active: false, - id: name, - }; - challenger.team = "A"; - challenger.duel = duel; - challenger.s.stunned = { ms: 120000 }; - resend(challenger, "u+cid"); - player.team = "B"; - player.duel = duel; - player.s.stunned = { ms: 120000 }; - resend(player, "u+cid"); - E.duels[name] = duel; - broadcast_e(); - a.forEach(function (p) { - if (p != challenger.name && get_player(p)) { - get_player(p).socket.emit("game_response", { - response: "duel_started", - challenger: challenger.name, - vs: player.name, - id: name, - }); - } - }); - b.forEach(function (p) { - if (p != player.name && get_player(p)) { - get_player(p).socket.emit("game_response", { - response: "duel_started", - challenger: challenger.name, - vs: player.name, - id: name, - }); - } - }); - } else if (data.event == "enter") { - if (is_in_pvp(player)) { - return socket.emit("game_log", "Can't join the duel from a pvp zone!"); - } - if (player.duel) { - return socket.emit("game_log", "Already in a duel!"); - } - if (!E.duels[data.id]) { - return socket.emit("game_log", "Duel expired"); - } - if (E.duels[data.id].active) { - return socket.emit("game_log", "Duel already started"); - } - if (!(E.duels[data.id].a.includes(player.name) || E.duels[data.id].b.includes(player.name))) { - return socket.emit("game_log", "Not your duel"); - } - clean_slate(player); - - if (E.duels[data.id].a.includes(player.name)) { - transport_player_to(player, data.id, 1); - player.team = "A"; - } else { - transport_player_to(player, data.id, 2); - player.team = "B"; - } - - player.duel = E.duels[data.id]; - if (player.duel.a.includes(player.name)) { - instances[data.id].info.A.push(player_to_summary(player)); - instances[data.id].info.A[instances[data.id].info.A.length - 1].active = true; - } else { - instances[data.id].info.B.push(player_to_summary(player)); - instances[data.id].info.B[instances[data.id].info.B.length - 1].active = true; - } - player.s.stunned = { ms: 120000 }; - - resend(player, "u+cid"); - instance_emit(data.id, "game_chat", { message: player.name + " joined the duel!" }); - } - }); - socket.on("party", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data.event == "invite") { - // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can send invites"); return; } - if (player.party && (parties[player.party].length >= limits.party_max || player.party_length >= limits.party)) { - return fail_response("party_full"); - } - var invited = players[name_to_id[data.id || data.name]]; - if (!invited || invited.id == player.id) { - return fail_response("invalid"); - } - if (player.party && player.party == invited.party) { - return success_response("already_in_party"); - } - // if(invited.party) { socket.emit("game_log",invited.name+" is already partying"); return; } - invited.socket.emit("invite", { name: player.name }); - socket.emit("game_log", "Invited " + invited.name + " to party"); - if (!invitations[player.name]) { - invitations[player.name] = {}; - } - invitations[player.name][invited.id] = 1; - } - if (data.event == "request") { - // if(player.party) { return; } - var invited = players[name_to_id[data.id || data.name]]; - if (!invited || invited.id == player.id) { - return fail_response("invalid"); - } - if (player.party && player.party == invited.party) { - return success_response("already_in_party"); - } - // if(!invited.party) { socket.emit("game_log",invited.name+" isn't partying"); return; } - invited.socket.emit("request", { name: player.name }); - socket.emit("game_log", "Requested to join " + invited.name + "'s party"); - if (!requests[player.name]) { - requests[player.name] = {}; - } - requests[player.name][invited.id] = 1; - } - if (data.event == "accept") { - var inviter = players[name_to_id[data.name]]; - // if(player.party) { socket.emit("party_update",{list:parties[player.party],party:party_to_client(player.party)}); socket.emit("game_log","Already partying"); return; } - // if(!inviter || (inviter.party && inviter.party!=inviter.name)) { socket.emit("game_log","Party was disbanded"); return; } - if (!inviter) { - return fail_response("player_gone", { name: data.name }); - } - if ( - inviter.party && - (parties[inviter.party].length >= limits.party_max || inviter.party_length >= limits.party) - ) { - return fail_response("party_full"); - } - if (!invitations[inviter.name] || !invitations[inviter.name][player.id]) { - return fail_response("invitation_expired"); - } - if (player.party && player.party == inviter.party) { - return success_response("already_in_party"); - } - if (player.party) { - leave_party(player.party, player); - socket.emit("party_update", {}); - socket.emit("game_log", "Left your current party"); - } - invitations[inviter.name][player.id] = 0; - if (!inviter.party) { - inviter.party = inviter.name; - parties[inviter.name] = [inviter.name]; - resend(inviter, "nc+u+cid"); - delete requests[inviter.name]; - if (!players[name_to_id[data.name]]) { - return fail_response("player_gone", { name: data.name }); - } // these repetitions are all because socket.emit's can cause in-line disconnects and disband parties right after creation [07/08/20] - } - player.party = inviter.party; - parties[inviter.party].push(player.name); - party_emit(player.party, "party_update", { - list: parties[inviter.party], - party: party_to_client(inviter.party), - message: - player.name + - " joined the party" + - ((inviter.party != inviter.name && " with " + inviter.name + "'s invite") || ""), - }); - resend(player, "nc+u+cid"); - delete requests[player.name]; - delete requests[inviter.name]; // optional - } - if (data.event == "raccept") { - var requester = players[name_to_id[data.name]]; - if (!requester) { - return fail_response("player_gone", { name: data.name }); - } - // if(requester.party) { socket.emit("game_log","Already partying"); return; } - if (player.party && (parties[player.party].length >= limits.party_max || player.party_length >= limits.party)) { - return fail_response("party_full"); - } - if (!requests[requester.name] || !requests[requester.name][player.id]) { - return fail_response("request_expired"); - } - if (player.party && player.party == requester.party) { - return success_response("already_in_party"); - } - if (requester.party) { - leave_party(requester.party, requester); - requester.socket.emit("party_update", {}); - requester.socket.emit("game_log", "Left the party"); - if (!players[name_to_id[data.name]]) { - return fail_response("player_gone", { name: data.name }); - } - } - requests[requester.name][player.id] = 0; - if (!player.party) { - player.party = player.name; - parties[player.name] = [player.name]; - resend(player, "nc+u+cid"); - delete requests[player.name]; - } - requester.party = player.party; - parties[player.party].push(requester.name); - party_emit(requester.party, "party_update", { - list: parties[player.party], - party: party_to_client(player.party), - message: requester.name + " joined the party", - }); - resend(requester, "nc+u+cid"); - delete requests[requester.name]; - delete requests[player.name]; // optional - } - if (data.event == "leave") { - if (!player.party) { - socket.emit("party_update", {}); - return success_response({}); - } - leave_party(player.party, player); - socket.emit("party_update", {}); - socket.emit("game_log", "Left the party"); - resend(player, "nc+u+cid"); - } - if (data.event == "kick") { - if (!player.party) { - return success_response({}); - } - // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can kick someone"); return; } - if (!in_arr(data.name, parties[player.party])) { - socket.emit("party_update", { list: parties[player.party], party: party_to_client(player.party) }); - return success_response({}); - } - if (parties[player.party].indexOf(player.name) > parties[player.party].indexOf(data.name)) { - return fail_response("cant_kick"); - } - var kicked = players[name_to_id[data.name]]; - if (!kicked) { - return fail_response("player_gone"); - } - leave_party(player.party, kicked); - if (player.party) { - party_emit(player.party, "game_log", player.name + " kicked " + kicked.name); - } - kicked.socket.emit("party_update", {}); - kicked.socket.emit("game_log", "You've been removed from the party"); - resend(kicked, "nc+u+cid"); - } - success_response({}); - }); - socket.on("magiport", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (magiportations[data.name] && magiportations[data.name][player.name] && get_player(data.name)) { - delete magiportations[data.name][player.name]; - if (instances[get_player(data.name).in].mount != instances[player.in].mount) { - return fail_response("cant_in_bank"); - } - if (!magiport_someone(player, get_player(data.name))) { - return fail_response("invalid"); - } - return success_response({}); - } else { - player.socket.emit("game_response", { response: "magiport_gone", name: data.name }); - return fail_response("inviter_gone"); - } - }); - socket.on("trade", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (data.event == "show" && player.p.trades != 1) { - player.p.trades = 1; - reslot_player(player); - resend(player, "nc+u+cid"); - } else if (data.event == "hide" && player.p.trades != null) { - player.p.trades = null; - reslot_player(player); - resend(player, "nc+u+cid"); - } - }); - socket.on("signup", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (!signups[player.name]) { - disappearing_text(false_socket, npcs.bean, "+1", { color: "#4D9A59", xy: 1 }); - } - signups[player.name] = true; - socket.emit("game_response", { response: "signed_up" }); - }); - socket.on("join", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (player.type == "merchant") { - return fail_response("no_merchants"); - } - if (player.s.hopsickness) { - return fail_response("cant_when_sick"); - } - if (player.user) { - return fail_response("cant_in_bank"); - } - if (data.name == "goobrawl" && events.goobrawl) { - if (player.map != "goobrawl") { - transport_player_to(player, "goobrawl"); - } - } else if (data.name == "crabxx" && events.crabxx) { - if (simple_distance(player, { map: "main", x: -1000, y: 1700 }) > 200) { - transport_player_to(player, "main", [-1000, 1700, 0, 40]); - } - } else if (data.name == "franky" && events.franky) { - if (simple_distance(player, { map: "level2w", x: -300, y: 150 }) > 200) { - transport_player_to(player, "level2w", [-300, 150, 0, 40]); - } - } else if (data.name == "icegolem" && events.icegolem) { - if (simple_distance(player, { map: "winterland", x: 820, y: 425 }) > 100) { - transport_player_to(player, "winterland", [820, 425, 0, 10]); - } - } else if (data.name == "abtesting" && E.abtesting) { - if (player.map != "abtesting" || !player.team) { - if ( - ssince(timers.abtesting_start) > 120 && - (!player.p.abtesting || player.p.abtesting[0] != E.abtesting.id) - ) { - return fail_response("join_too_late"); - } - - if (player.p.abtesting && player.p.abtesting[0] == E.abtesting.id) { - player.team = player.p.abtesting[1]; - } else { - player.team = random_one(["A", "B"]); - } - - player.p.abtesting = [E.abtesting.id, player.team]; - - player.team = player.p.abtesting[1]; // transport does a restore ... - save_state(player); - - if (player.team == "A") { - transport_player_to(player, "abtesting", 2); - } else if (player.team == "B") { - transport_player_to(player, "abtesting", 3); - } - - resend(player, "cid"); - } - } else { - return fail_response("cant_join"); - } - success_response(); - }); - socket.on("stop", function (data) { - var player = players[socket.id]; - var change = false; - if (!player) { - return; - } - if (!data || !data.action) { - if (player.s.invis) { - step_out_of_invis(player); - change = true; - } - if (Object.keys(player.c).length) { - player.c = {}; - change = true; - } - } else if (data.action == "invis") { - if (player.s.invis) { - step_out_of_invis(player); - change = true; - } - } else if (data.action == "channeling") { - if (Object.keys(player.c).length) { - player.c = {}; - change = true; - } - } else if (data.action == "teleport" || data.action == "town") { - if (player.c.town) { - delete player.c.town; - change = true; - } - } else if (data.action == "revival") { - if (player.c.revival) { - delete player.c.revival; - change = true; - } - } - if (change) { - resend(player, "u+cid"); - } - success_response(); - }); - socket.on("tarot", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - var npc = G.maps[player.map].ref.twitch; - if (!npc || simple_distance(npc, player) > 500) { - return socket.emit("game_response", "distance"); - } - for (var name in player.s) { - if (name.startsWith("tarot")) { - return socket.emit("game_response", "tarot_exists"); - } - } - }); - socket.on("bet", function (data) { - if (!instances.tavern) { - return; - } - var player = players[socket.id]; - if (!player || player.user || (player.map != "tavern" && !is_sdk)) { - return; - } - if (player.s.xshotted) { - return socket.emit("game_response", "bet_xshot"); - } - if (data.type == "roulette") { - if (!is_sdk) { - return; - } // Old routine [23/08/18] - if (!data.odds) { - data.odds = "black"; - } - data.odds = "" + data.odds; - data.gold = max(1, parseInt(data.gold) || 1); - if (!tavern.roulette.odds[data.odds]) { - return; - } - if (tavern.roulette.state != "bets") { - socket.emit("game_log", "Not taking bets yet!"); - return; - } - if (Object.keys(player.bets).length >= 5) { - socket.emit("game_log", "You already have 5 active bets"); - return; - } - if (player.gold < data.gold) { - socket.emit("game_log", "Not enough gold"); - return; - } - - socket.emit("game_log", { message: "Bet accepted on " + data.odds, color: "white" }); - socket.emit("game_log", { message: to_pretty_num(data.gold) + " gold", color: "gray" }); - - var rid = randomStr(10); - player.gold -= data.gold; - player.bets[rid] = { - id: rid, - type: "roulette", - odds: data.odds, - gold: data.gold, - pid: socket.id, - state: "bet", - }; - tavern.roulette.players[player.socket.id] = true; - io.to("roulette").emit("bet", { name: player.name, type: "roulette", odds: data.odds, gold: data.gold }); - socket.emit("tavern", player.bets[rid]); - resend(player, "reopen+nc"); - } - if (data.type == "dice") { - // server_log(JSON.stringify(data)); - var num = min(99.99, max(parseFloat(data.num) || 0, 0.01)); - var gold = max(10000, min(parseInt(data.gold) || 0, 100000000000)); - var odds = 100.0 / num; - var dir = "down"; - if (data.dir == "up") { - odds = 100.0 / (100 - num); - dir = "up"; - } - odds = min(odds, 10000); - odds = parseFloat(floor_f2(odds)); - var win = gold * odds; - var raw = win; - var edge = ceil(((gold * odds - gold) * house_edge()) / 100.0); - win = parseInt(win); - edge = parseInt(edge); - if (tavern.dice.state == "roll") { - return socket.emit("game_response", "tavern_too_late"); - } - if (tavern.dice.state != "bets") { - return socket.emit("game_response", "tavern_not_yet"); - } - if (gold > player.gold) { - return socket.emit("game_response", "gold_not_enough"); - } - if (win - edge - gold > (S.gold - house_debt()) * 0.4) { - return socket.emit("game_response", "tavern_gold_not_enough"); - } - if (Object.keys(player.bets).length) { - return socket.emit("game_response", "tavern_dice_exist"); - } - // if(Object.keys(player.bets).length>=5) return socket.emit("game_response","tavern_too_many_bets"); - // socket.emit("game_log",{"message":"Bet accepted on "+num.toFixed(2)+" "+dir.toUpperCase(),"color":"white"}); - socket.emit("game_log", { message: "Num: " + num.toFixed(2) + " " + dir.toUpperCase(), color: "white" }); - socket.emit("game_log", { message: "Bet: " + to_pretty_num(gold) + " gold", color: "gray" }); - - var rid = randomStr(10); - player.gold -= gold; - player.bets[rid] = { - id: rid, - type: "dice", - num: num, - gold: gold, - dir: dir, - pid: socket.id, - state: "bet", - win: win, - edge: edge, - odds: odds, - }; - tavern.dice.players[player.socket.id] = true; - instance_emit(tavern, "tavern", { - event: "bet", - name: player.name, - type: "dice", - num: num, - gold: gold, - dir: dir, - }); - resend(player, "reopen+nc"); - } - if (data.type == "slots") { - var gold = 1000000; - if (gold > player.gold) { - return socket.emit("game_response", "gold_not_enough"); - } - player.gold -= gold; - S.gold += gold; - player.q.slots = { ms: 3000 }; - xy_emit(player, "ui", { type: "slots", player: player.name }); - socket.emit("game_response", { response: "gold_use", gold: gold, game: data.type }); - resend(player, "u+cid+reopen+nc"); - } - }); - socket.on("tavern", function (data) { - if (data.event == "info") { - socket.emit("tavern", { event: "info", edge: house_edge(), max: parseInt((S.gold - house_debt()) * 0.4) }); - } - }); - socket.on("play", function (data) {}); - socket.on("pet", function (data) { - var player = players[socket.id]; - if (!player) { - return; - } - if (!player.monster) { - player.monster = new_monster(player.in, { - type: player.pet, - stype: "pet", - x: player.x, - y: player.y, - owner: player.name, - name: "Skimpy", - }); - } - }); - socket.on("whistle", function (data) { - if (!player.monster) { - return; - } - xy_emit(player.monster, "disappear", { id: player.monster.id }); - player.monster.x = player.x; - player.monster.y = player.y; - player.monster.map = player.map; - player.monster.in = player.in; - player.monster.u = true; - player.monster.cid++; - }); - socket.on("list_pvp", function (data) { - var plist = []; - for (var j = 1; j < 201; j++) { - if (pend - j < 0) { - break; - } - plist.push(pwns[(pend - j) % 200]); - } - socket.emit("pvp_list", { code: data && data.code, list: plist }); - }); - socket.on("players", function (data) { - var player = players[socket.id]; - var sdata = []; - if (!player) { - return; - } // || is_pvp - for (var id in players) { - var current = players[id]; - var mapn = current.map; - current.age = parseInt(ceil(hsince(new Date(current.created)) / 24.0)); - if (is_pvp) { - mapn = "Unknown"; - sdata.push({ - name: "Hidden", - map: mapn, - age: current.age, - level: current.level, - type: current.type, - afk: (current.afk && 1) || 0, - party: (current.party && "Hidden") || "", - kills: current.kills, - }); - } else if (!is_in_pvp(current)) { - sdata.push({ - name: current.name, - map: mapn, - age: current.age, - level: current.level, - type: current.type, - afk: (current.afk && 1) || 0, - party: current.party || "", - }); - } - } - socket.emit("players", sdata); - }); - socket.on("pets", function (data) { - var player = players[socket.id]; - var sdata = []; - if (!player) { - return; - } // || is_pvp - for (var id in player.p.pets || {}) { - sdata.push(player.p.pets[id]); - } - socket.emit("players", sdata); - }); - socket.on("harakiri", function (data) { - var player = players[socket.id]; - if (!player || player.rip) { - return; - } - defeat_player(player); - if (!player.rip) { - rip(player); - } - disappearing_text(player.socket, player, "SEPPUKU", { xy: 1, size: "huge", color: "#6F76A6" }); - resend(player, "u+cid"); - }); - socket.on("deepsea", function (data) { - return; - var player = players[socket.id]; - if (!player || player.rip || player.tskin == "deepsea") { - return; - } - player.tskin = "deepsea"; - disappearing_text(player.socket, player, "ROARRRRRRR", { xy: 1, size: "huge", color: "#60A975" }); - resend(player, "u+cid"); - }); - socket.on("blend", function (data) { - var player = players[socket.id]; - var min = 99999; - var x = null; - if (!player || player.rip) { - return; - } - for (var id in instances[player.in].monsters || {}) { - var m = instances[player.in].monsters[id]; - var c = distance(m, player, true); - if (c < min) { - min = c; - x = m; - } - } - if (x) { - var skin = G.monsters[x.type].skin || x.type; - if (player.tskin != skin) { - player.tskin = skin; - resend(player, "u+cid"); - } - } - }); - socket.on("legacify", function (data) { - var player = players[socket.id]; - if (!player || player.rip) { - return; - } - for (var i = 0; i < 42; i++) { - if (0 && player.items[i] && !player.items[i].p && ["fury", "starkillers"].includes(player.items[i].name)) { - player.items[i].p = "legacy"; - } - } - resend(player, "reopen"); - }); - socket.on("requested_ack", function (data) { - // send "requesting_ack", to verify someone is actually connected [03/08/16] - server_log("requested_ack" + JSON.stringify(data), 1); - }); - socket.on("disconnect", function () { - //#IMPORTANT: disconnect exceptions are fatal [07/08/16] - // console.log("disconnect!"); - var player = players[socket.id]; - var observer = observers[socket.id]; - try { - delete sockets[socket.id]; - } catch (e) {} - if (player) { - player.dc = true; - try { - defeat_player(player); - } catch (e) { - log_trace("#X DCERRORPVP", e); - } - // if(!server.live) { server_log('Ignoring the "disconnect" of '+player.name,1); return; } - - try { - if (player.moving && distance(player, { x: player.going_x, y: player.going_y }) < 800) { - player.x = player.going_x; - player.y = player.going_y; - } - } catch (e) { - log_trace("#X DCERRORM", e); - } - // to not get stuck on out-of-bounds corners - - try { - if (player.party) { - leave_party(player.party, player); - } - } catch (e) { - log_trace("#X DCERROR1", e); - } - - try { - xy_emit(player, "disappear", { id: player.id, reason: "disconnect" }); - } catch (e) { - log_trace("#X DCERROR2", e); - } - - server_log("Disconnected Player: " + socket.id + " " + player.name + " Calls: " + socket.total_calls, 1); - - try { - for (var bid in player.bets) { - player.gold += player.bets[bid].gold; - } - player.bets = {}; - } catch (e) { - log_trace("#X DCERRORBETS", e); - } - - try { - if (player.monster) { - remove_monster(player.monster); - } - } catch (e) { - log_trace("#X DCERRORPETS", e); - } - - try { - delete players[socket.id]; - delete instances[player.in].players[player.id]; - if (instances[player.in].solo == player.id) { - destroy_instance(player.in); - } - pmap_remove(player); - } catch (e) { - log_trace("#X DCERROR3 ", e); - } - - try { - restore_state(player, true); - } catch (e) { - log_trace("#X DCERRORrestore", e); - } - - try { - if (gameplay != "hardcore" && gameplay != "test") { - dc_players[player.real_id] = player; - sync_loop(); - } else { - save_player(player); - } - } catch (e) { - log_trace("#X DCERROR", e); - } - } - if (observer) { - server_log("Disconnected Observer: " + socket.id + " Calls: " + socket.total_calls); - try { - delete_observer(socket); - } catch (e) { - log_trace("#X DCERRORO ", e); - } - } - }); - socket.on("shutdown", function (data) { - if (data.pass != variables.access_master) { - return; - } - if (data.reason) { - broadcast("disconnect_reason", data.reason); - } - setTimeout(shutdown_routine, 10000); - }); - socket.on("notice", function (data) { - if (data.pass != variables.access_master) { - return; - } - broadcast("notice", { message: data.message }); - }); - socket.on("render", function (data) { - if (data.pass != variables.access_master) { - return; - } - var player = players[socket.id]; - var output = ""; - var json_output = undefined; - var window = null; - var after = ""; - try { - eval(data.code); - } catch (e) { - output = "Exception: " + e; - } - if (window) { - socket.emit("simple_eval", { - window: window, - after: after, - code: "for(var id in data.window) window[id]=data.window[id]; eval(data.after);", - }); - } else if (json_output !== undefined) { - socket.emit("simple_eval", { output: output, json_output: json_output, code: "show_json(data.json_output)" }); - } else { - socket.emit("simple_eval", { - output: output, - json_output: json_output, - code: 'show_modal("
"+data.output+"
")', - }); - } - resend(player, "reopen+u+cid"); - }); - socket.on("error", function (data) { - // socket.emit("error") brings server down [16/02/22] - }); - socket.on("eval", function (data) { - if (data.command) { - var player = players[socket.id]; - if (!player) { - return; - } - if (player.map != "cyberland" || player.rip) { - return player.socket.emit("game_log", "Not connected to the mainframe"); - } - if (data.command == "hello") { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "hi", - id: "mainframe", - }); - } else if (data.command == "give") { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "what?", - id: "mainframe", - }); - } else if (data.command.startsWith("swap")) { - var numbers = data.command.split(" "); - if (numbers.length != 3) { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "...", - id: "mainframe", - }); - } else { - var a = parseInt(numbers[1]); - var b = parseInt(numbers[2]); - if (0 <= a && a < 42 && 0 <= b && b < 42) { - if (a == player.p.item_num) { - player.p.item_num = b; - } else if (b == player.p.item_num) { - player.p.item_num = a; - } - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "done", - id: "mainframe", - }); - } else { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "ugh, ok", - id: "mainframe", - }); - } - } - } else if (data.command == "stop") { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "mechagnomes assemble", - id: "mainframe", - }); - get_monsters("mechagnome").forEach(function (m) { - if (m.target) { - stop_pursuit(m, { stop: 1, cause: "stop()" }); - } - //else m.irregular=3; - }); - } else if (data.command.startsWith("give") && data.command != "give spares") { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "no", - id: "mainframe", - }); - } else if (data.command == "secret web mode" && (player.p.steam_id || player.p.mas_auth_id)) { - player.p.secret_web_mode = true; - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "secret web mode unlocked", - id: "mainframe", - }); - } else if (data.command == "give spares") { - if (S.misc && S.misc.spares && S.misc.spares.length) { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "here you go", - id: "mainframe", - }); - drop_one_thing(player, S.misc.spares, { x: 1, y: -88 }); - S.misc.spares = []; - } else { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "come later", - id: "mainframe", - }); - } - } else { - if (!player.supercomputer) { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "UNAUTHORIZED COMMAND", - id: "mainframe", - }); - for (var id in instances.cyberland.monsters) { - var monster = instances.cyberland.monsters[id]; - if (!monster.target) { - target_player(monster, player); - } - } - } else { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "UNAUTHORIZED, COMRADE", - id: "mainframe", - }); - } - } - } - if (data.pass == variables.access_master) { - eval(data.code); - } - }); - }); + io.on("connection", function (socket) { + if (socket.handshake.query.server_method) { + if (0 && socket.handshake.query.server_master == variables.server_master) { + // this was to make servers communicate with each other and disconnect overflows immediately [28/10/23] + if (socket.handshake.query.server_method == "players") { + socket.emit("players"); // decided to make the existing cron more aggressive [26/09/21] + } + } + socket.disconnect(); + return; + } + sockets[socket.id] = socket; + socket.total_calls = 0; + socket.calls = []; + socket.fs = {}; // function list + + if (!is_socket_allowed(socket)) { + disconnect_old_sockets(socket); + } + + var original_on = socket.on; + socket.on = function (method, f) { + // takes the "f" function, the function thats sent to socket.on, wraps it into a "g" function + var g = function (data) { + ls_method = method; + if (mode.log_all) { + console.log("'" + method + "': " + JSON.stringify(data)); + } + try { + var climit = limits.calls; + var name = "_observer"; + if (data === undefined) { + data = {}; + } // data normalisation [28/08/18] + socket.total_calls++; + add_call_cost(-1); + current_socket = socket; + call_modifier = { open_chest: 0.1, skill: 0.05, target: 0.5 }[method] || 1; + if (players[socket.id]) { + name = players[socket.id].name; + // if(players[socket.id].type=="merchant") climit=round(climit/3); + // Merchants are first class citizens now! [14/01/18] + } else { + climit = round(climit / 4); + } + if (method == "cm") { + var add = 1; + var len = data.message.length; + var mult = 1; + if (len > 100) { + add = 2; + } else if (len > 1000) { + add = 3; + } else if (len > 10000) { + add = 10; + } else if (len > 50000) { + add = 20; + } + // console.log("add: "+add+"len: "+len); + if (data.to.length > 1) { + mult = 0.8; + } + add_call_cost(add * data.to.length * mult, undefined, "cm_data"); + } else { + if (CC[method]) { + add_call_cost(CC[method] || 0); + } + } + if (get_call_cost() > climit && method != "disconnect") { + server_log(">>> LIMITDC " + name, 1); + socket.emit("limitdcreport", { calls: socket.calls, climit: climit, total: socket.total_calls }); + socket.emit("disconnect_reason", "limitdc"); + socket.disconnect(); + } else { + f(data); + } + } catch (e) { + try { + var climit = limits.calls; + add_call_cost(16); + log_trace("socket.on: " + method.substr(0, 200), e); + if (get_call_cost() > climit) { + server_log(">>> LIMITDC2 " + name, 1); + socket.emit("limitdcreport", { + calls: socket.calls, + climit: climit, + total: socket.total_calls, + method: method.substr(0, 200), + }); + socket.emit("disconnect_reason", "limitdc"); + socket.disconnect(); + } else { + try { + socket.emit("game_error", "ERROR!"); + } catch (e) {} + } + } catch (e) { + log_trace("limit_calls", e); + } + } + current_socket = false_socket; + }; + socket.fs[method] = f; + original_on.apply(socket, [method, g]); + }; + + var data = { + region: region, + name: server_name, + pvp: is_pvp, + gameplay: gameplay, + info: (instances[socket.first_map] && instances[socket.first_map].info) || {}, + }; + socket.first_map = socket.first_in = observer_map; + socket.first_x = observer_x; + socket.first_y = observer_y + 120; + socket.desktop = true; + if (socket.request && socket.request._query && socket.request._query.secret) { + for (var id in players) { + var player = players[id]; + if (player.secret == socket.request._query.secret) { + socket.player = player; + data.character = player_to_client(player); + data.character.id = data.character.name = player.name; + socket.first_map = player.map; + socket.first_in = player.in; + socket.first_x = player.x; + socket.first_y = player.y; + if (socket.request._query.desktop) { + socket.desktop = true; + socket.first_y += 120; + } else { + socket.desktop = false; + } + } + } + } + data.x = socket.first_x; + data.y = socket.first_y; + data.map = socket.first_map; + data.in = socket.first_in; + broadcast_e(true); + data.S = E; + socket.emit("welcome", data); + socket.on("send_updates", function () { + if (observers[socket.id]) { + send_all_xy(observers[socket.id]); + } + if (players[socket.id]) { + send_all_xy(players[socket.id]); + } + }); + socket.on("loaded", function (data) { + var observer = (observers[socket.id] = { + socket: socket, + x: socket.first_x, + y: socket.first_y, + // vision:[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision], + map: socket.first_map, + in: socket.first_in, + observer: 1, + id: "o" + socket.id, + s: {}, + }); + if (socket.player) { + observer.player = socket.player; + } + // observer.vision[0]=min(1000,observer.vision[0]); observer.vision[1]=min(700,observer.vision[1]); + observer.vision = B.vision; + // socket.emit("observing",{map:observer.map,x:observer.x,y:observer.y}); + resume_instance(instances[observer.in]); + instances[observer.in].observers[observer.id] = observer; + send_all_xy(observer); + }); + socket.on("o:home", function (data) { + var observer = observers[socket.id]; + if (!observer) { + return; + } + var player = observer.player; + if (!player || player.dc) { + return; + } + transport_observer_to(observer, player.in, player.map, player.x, player.y + ((socket.desktop && 120) || 0)); + }); + socket.on("o:command", function (data) { + var observer = observers[socket.id]; + if (!observer) { + return; + } + var player = observer.player; + if (!player || player.dc) { + return; + } + player.socket.emit("code_eval", data); + }); + socket.on("cm", function (data) { + var player = players[socket.id]; + var receivers = []; + if (!player || player.s.mute) { + return fail_response("muted"); + } + data.to.forEach(function (name) { + var p = players[name_to_id[name]]; + if (p) { + p.socket.emit("cm", { name: player.name, message: data.message || "" }); + receivers.push(name); + } + }); + success_response("data", { locals: [], receivers: receivers }); + }); + socket.on("say", function (data) { + var player = players[socket.id]; + var message = strip_string(data.message).substr(0, 1200); + if (!player || player.s.mute) { + return fail_response("muted"); + } + if (data.code && player.last_say && ssince(player.last_say) < 15) { + return fail_response("chat_slowdown"); + } + if (player.last_say && mssince(player.last_say) < 400) { + return fail_response("chat_slowdown"); + } + if (!message || !message.length) { + return fail_response("invalid"); + } + player.last_say = new Date(); + if (data.party) { + if (!player.party) { + return fail_response("not_in_a_party"); + } + party_emit(player.party, "partym", { owner: player.name, message: message, id: player.id, p: true }); + } else if (data.name) { + var target = get_player(data.name); + if (!target) { + player.socket.emit("pm", { + owner: player.name, + to: data.name, + message: message, + id: player.id, + xserver: true, + }); + appengine_call( + "log_chat", + { to: ["", data.name], type: "xprivate", message: message, fro: player.name, author: player.owner }, + function (result) { + if (result.failed && players[socket.id]) { + player.socket.emit("pm", { + owner: player.name, + to: data.name, + message: "(FAILED)", + id: player.id, + xserver: true, + }); + } + }, + ); + } else { + if (target.name == player.name) { + return fail_response("invalid"); + } + player.socket.emit("pm", { owner: player.name, to: data.name, message: message, id: player.id }); + target.socket.emit("pm", { owner: player.name, message: message, id: player.id }); + appengine_call("log_chat", { + to: [target.owner, target.name], + type: "private", + message: message, + fro: player.name, + author: player.owner, + }); + } + } else { + if (1) { + broadcast("chat_log", { owner: player.name, message: message, id: player.id, p: true }); + var owners = {}; + for (var id in players) { + var p = players[id]; + owners[p.owner] = owners[p.owner] || []; + owners[p.owner].push(p.name); + } + appengine_call("log_chat", { + to: Object.entries(owners), + type: "ambient", + message: message, + fro: player.name, + author: player.owner, + }); + } else { + xy_emit(player, "chat_log", { owner: player.name, message: message, id: player.id, p: true }); + } + appengine_call("log_chat", { type: "server", message: message, fro: player.name, author: player.owner }); + } + if (player.s.typing) { + delete player.s.typing; + resend(player, "u+cid+nc"); + } + success_response(); + }); + socket.on("ping_trig", function (data) { + socket.emit("ping_ack", data); + }); + socket.on("target", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + reduce_call_cost(); + player.target = data.id; + player.focus = data.xid; + var target = instances[player.in].monsters[data.id]; + if (!target) { + target = instances[player.in].players[data.id]; + } + if (!target) { + target = instances[player.in].players[NPC_prefix + data.id]; + } + if (!target) { + player.target = null; + } + var focus = instances[player.in].monsters[data.xid]; + if (!focus) { + focus = instances[player.in].players[data.xid]; + } + if (!focus) { + focus = instances[player.in].players[NPC_prefix + data.xid]; + } + if (!focus) { + player.focus = null; + } + if (focus && focus.screenshot) { + target.going_x = player.x; + target.going_y = player.y; + target.u = true; + start_moving_element(focus); + } + if (focus && player.map == "cgallery" && focus.npc) { + // target.going_x=player.x; + // target.going_y=player.y; + // target.u=true; + // start_moving_element(target); + if (!player.tcx) { + player.tcx = {}; + } + if (focus.ctype == "body") { + player.tskin = focus.skin; + } else if (player.cx.length > 5) { + return fail_response("invalid"); + } + player.tcx[focus.ctype] = focus.cx[focus.ctype]; + // player.tcx=target.cx; + resend(player, "u+cid"); + } + // server_log(player.name+" target: "+player.target+" focus: "+player.focus); + resend(player, "u+nc"); + success_response({}); + }); + socket.on("ureward", function (data) { + if (!player || player.user || !data.name || !G.docs.rewards[data.name]) { + return; + } + if (!player.verified || !player.auth_id) { + return socket.emit("game_response", "reward_notverified"); + } + if (!player.user.rewards) { + player.user.rewards = []; + } + if (player.user.rewards.includes(data.name)) { + return socket.emit("game_response", "reward_already"); + } + player.user.rewards.push(data.name); + exchange(player, G.docs.rewards[data.name], { name: "reward_" + data.name }); + socket.emit("game_response", { response: "reward_received", rewards: player.user.rewards }); + }); + socket.on("creward", function (data) { + if (!player || !data.name || !G.classes[player.type].rewards[data.name]) { + return; + } + if (!player.verified || !player.auth_id) { + return socket.emit("game_response", "reward_notverified"); + } + if (!player.p.rewards) { + player.p.rewards = []; + } + if (player.p.rewards.includes(data.name)) { + return socket.emit("game_response", "reward_already"); + } + player.p.rewards.push(data.name); + exchange(player, G.classes[player.type].rewards[data.name].reward, { name: "reward_" + data.name }); + socket.emit("game_response", { response: "reward_received", rewards: player.p.rewards }); + }); + socket.on("cx", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + // if(player.role!="gm") return socket.emit('game_log',"Cosmetics system is out of the test phase for now"); + // console.log(data); + var cx = player.cx; + var cxl = all_cx(player); + // if(!player.tcx) player.tcx=clone(player.cx); + if (data.slot && !data.name) { + if (data.slot == "back") { + delete cx.tail; + } // synced with render_cgallery + if (data.slot == "face") { + delete cx.makeup; + } + delete cx[data.slot]; + } else { + if (!T[data.name] || (!cxl[data.name] && player.role != "cx")) { + return fail_response("cx_not_found"); + } + if ((T[data.name] == "body" || T[data.name] == "armor" || T[data.name] == "character") && data.slot == "skin") { + player.skin = data.name; + } else if ((T[data.name] == "body" || T[data.name] == "armor") && data.slot == "upper") { + cx.upper = data.name; + } else if (cxtype_to_slot[T[data.name]] && cxtype_to_slot[T[data.name]] != "skin") { + cx[cxtype_to_slot[T[data.name]]] = data.name; + } + } + prune_cx(player.cx, player.skin); + resend(player, "u+cid"); + success_response(); + }); + socket.on("gm", function (data) { + var player = players[socket.id]; + if (!player || player.role != "gm") { + return; + } + var target = players[id_to_id[data.id]]; + var action = data.action; + if (action == "mute") { + if (!target) { + return socket.emit("game_log", "Player not found: " + data.id); + } + if (!target.s.mute) { + target.s.mute = { ms: 48 * 60 * 60 * 1000 }; + return socket.emit("game_chat", "Muted " + target.name); + } else { + target.s.mute = { ms: 0 }; + return socket.emit("game_chat", "Unmuted " + target.name); + } + } else if (action == "jail") { + if (!target) { + return socket.emit("game_log", "Player not found: " + data.id); + } + transport_player_to(target, "jail"); + } else if (action == "ban") { + appengine_call("ban_user", { name: data.id }, function (result) { + socket.emit("game_log", "Ban: " + ((result && result.result) || "No result")); + }); + } else if (action == "invincible") { + player.s.invincible = { ms: 24 * 60 * 60 * 1000 }; + resend(player, "u+cid"); + } else if (action == "jump") { + if (!target) { + return socket.emit("game_log", "Player not found: " + data.id); + } + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (spot) { + pulled = player; + pulled.s.magiport = { ms: 400 }; + pulled.s.magiport.x = spot.x; + pulled.s.magiport.y = spot.y; + pulled.s.magiport.f = target.name; + pulled.s.magiport.in = target.in; + pulled.s.magiport.map = target.map; + resend(pulled, "u+cid"); + } else { + return socket.emit("game_log", "No safe spot near: " + data.id); + } + } else if (action == "mjump") { + target = get_monsters(data.monster)[0]; + if (!target) { + return socket.emit("game_log", "Monster not found: " + data.monster); + } + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (spot) { + pulled = player; + pulled.s.magiport = { ms: 400 }; + pulled.s.magiport.x = spot.x; + pulled.s.magiport.y = spot.y; + pulled.s.magiport.f = target.name; + pulled.s.magiport.in = target.in; + pulled.s.magiport.map = target.map; + resend(pulled, "u+cid"); + } else { + return socket.emit("game_log", "No safe spot near: " + data.monster); + } + } else if (action == "jump_list") { + var ids = []; + for (var id in players) { + ids.push(players[id].name); + } + socket.emit("gm", { action: "jump_list", ids: ids }); + } else if (action == "server_info") { + var info = []; + for (var id in players) { + info.push({ name: players[id].name, owner: players[id].owner, ip: get_ip(players[id]) }); + } + //socket.emit("gm",{action:"server_info",info:info}); + } + }); + socket.on("monsterhunt", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (simple_distance(G.maps.main.ref.monsterhunter, player, true) > B.sell_dist) { + return fail_response("distance"); + } + var hunted = []; + for (var id in server.s) { + if (server.s[id].type == "monsterhunt") { + hunted.push(server.s[id].id); + } + } + if (player.s.monsterhunt && player.s.monsterhunt.c) { + return fail_response("monsterhunt_already"); + } else if (player.s.monsterhunt) { + delete server.s["monsterhunt_" + player.s.monsterhunt.id]; + delete player.s.monsterhunt; + add_item(player, "monstertoken", { log: true, q: (gameplay == "hardcore" && 100) || 1 }); + resend(player, "u+cid+reopen"); + return success_response({ completed: true }); + } + if (player.type == "merchant") { + return socket.emit("game_response", "monsterhunt_merchant"); + } + var mmax = -1; + var name = "goo"; + var count = 100; + var times = 0; + var the_hp = 0; + for (var id in instances) { + if (instances[id].name != id || !G.maps[id] || G.maps[id].irregular) { + continue; + } + for (var mid in instances[id].monsters) { + var monster = instances[id].monsters[mid]; + if (monster.level > mmax && !in_arr(monster.type, hunted) && !monster.target) { + // added the target condition [21/07/23] + name = monster.type; + mmax = monster.level; + the_hp = monster.max_hp / 1000.0; + } + } + } + for (var id in G.maps) { + if (G.maps[id].irregular || !G.maps[id].monsters) { + continue; + } + G.maps[id].monsters.forEach(function (p) { + if (p.type == name) { + times += p.count; + } + }); + } + // console.log(times); + count = max(1, min(500, parseInt((20 * 60 * max(1, times)) / the_hp / (G.monsters[name].respawn + 0.25)))); + if (gameplay == "hardcore") { + count = max(1, parseInt(count / 10)); + } + player.s.monsterhunt = { sn: region + " " + server_name, id: name, c: count, ms: 30 * 60 * 1000, dl: true }; + server.s["monsterhunt_" + name] = { name: player.name, id: name, ms: 20 * 60 * 1000, type: "monsterhunt" }; + player.hitchhikers.push(["game_response", "monsterhunt_started"]); + resend(player, "u+cid"); + success_response({ started: true }); + }); + socket.on("ccreport", function () { + socket.emit("ccreport", { calls: socket.calls, climit: limits.calls, total: socket.total_calls }); + }); + socket.on("tracker", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!player.tracker) { + return; + } + var data = { + monsters: player.p.stats.monsters, + monsters_diff: player.p.stats.monsters_diff, + exchanges: player.p.stats.exchanges, + maps: D.drops.maps, + tables: {}, + max: player.max_stats, + }; // ,computer:false + function register_table(table) { + if (table) { + table.forEach(function (drop) { + if (drop[1] == "open" && !data.tables[drop[2]]) { + data.tables[drop[2]] = D.drops[drop[2]]; + register_table(D.drops[drop[2]]); + } + }); + } + } + if (player.computer || 1) { + // data.computer=true; + data.drops = D.drops.monsters; + } else { + data.drops = {}; + for (var name in data.monsters) { + if (data.monsters[name] >= 100 && D.drops.monsters[name]) { + data.drops[name] = D.drops.monsters[name]; + } + } + } + if (D.drops.maps.global) { + data.global = D.drops.maps.global; + register_table(data.global); + } + if (D.drops.maps.global_static) { + data.global_static = D.drops.maps.global_static; + register_table(data.global_static); + } + for (var name in data.drops) { + register_table(data.drops[name]); + } + for (var name in data.maps) { + register_table(data.maps[name]); + } + for (var name in G.items) { + if ( + G.items[name].e && + (player.computer || + 1 || + (player.p.stats.exchanges && player.p.stats.exchanges[name] && player.p.stats.exchanges[name] >= 100)) + ) { + if (G.items[name].upgrade || G.items[name].compound) { + for (var i = 0; i < 13; i++) { + if (D.drops[name + i]) { + data.tables[name + i] = D.drops[name + i]; + register_table(data.tables[name + i]); + } + } + } else if (D.drops[name]) { + data.tables[name] = D.drops[name]; + register_table(data.tables[name]); + } + } + } + socket.emit("tracker", data); + }); + socket.on("set_home", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (player.p.dt.last_homeset && hsince(player.p.dt.last_homeset) < 36) { + return fail_response("sh_time", { hours: 36 - hsince(player.p.dt.last_homeset) }); + } + player.p.dt.last_homeset = new Date(); + player.p.home = region + server_name; + delete player.s.hopsickness; + success_response("home_set", { home: player.p.home }); + }); + socket.on("code", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.run) { + players[socket.id].code = true; + } else { + players[socket.id].code = false; + } + resend(players[socket.id], "u+cid"); + }); + socket.on("property", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.typing) { + if (!player.s.typing || player.s.typing.ms < 3000) { + reduce_call_cost(); + } + player.s.typing = { ms: 4000 }; + resend(player, "u+cid+nc"); + } else { + var original = player.afk; + if (player.afk == "bot" || player.afk == "code") { + } else if (data.afk === true) { + player.afk = true; + } else if (data.afk === false) { + if (player.afk !== undefined) { + player.afk = false; + } + } + if (original !== player.afk) { + reduce_call_cost(); + resend(player, "u+cid+nc"); + } + } + }); + socket.on("cruise", function (speed) { + var player = players[socket.id]; + if (!player) { + return; + } + player.cruise = parseInt(speed); + resend(player, "u+cid"); + success_response("cruise", { speed: player.cruise }); + }); + socket.on("test", function (data) { + if (is_sdk) { + console.log(data.test); + } + socket.emit("test", { date: new Date() }); + }); + socket.on("blocker", function (data) { + if (data.type == "pvp") { + if (instances["arena"].allow) { + socket.emit("blocker", { type: "pvp", allow: 1 }); + } else { + socket.emit("blocker", { type: "pvp" }); + } + } + }); + socket.on("mail_take_item", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (mode.prevent_external) { + return socket.emit("game_response", { response: "not_in_this_server" }); + } + if (!player.esize) { + return socket.emit("game_response", "inv_size"); + } + appengine_call( + "take_item_from_mail", + { owner: player.owner, mid: data.id }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + return socket.emit("game_response", { response: "mail_item_already_taken" }); + } + var item = JSON.parse(result.item); + add_item(player, item, { announce: false }); + resend(player, "reopen"); + socket.emit("game_response", { response: "mail_item_taken" }); + }, + function () { + socket.emit("game_response", { response: "mail_take_item_failed" }); + }, + ); + }); + socket.on("mail", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (mode.prevent_external) { + return fail_response("not_in_this_server"); + } + var item = null; + var retries = 1; + if (player.gold < 48000) { + return fail_response("gold_not_enough"); + } + player.gold -= 48000; + if (data.item && player.items[0] && player.items[0].name != "placeholder") { + if (player.items[0].l) { + return fail_response("item_locked"); + } + if (player.items[0].b || player.items[0].v) { + return fail_response("item_blocked"); + } + if (player.gold < 312000) { + return fail_response("gold_not_enough"); + } + player.gold -= 312000; + item = JSON.stringify(player.items[0]); + player.items[0] = player.citems[0] = null; + retries = 3; + } + appengine_call( + "send_mail", + { + fro: player.name, + to: data.to, + subject: data.subject || "", + message: data.message || "", + rid: randomStr(50), + retries: retries, + item: item, + }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + if (player) { + socket.emit("game_response", { + response: "mail_failed", + to: data.to, + reason: result.reason, + cevent: "mail_failed", + }); + } + if (player && item && result.return && player.esize) { + var r = JSON.parse(item); + add_item(player, r); + resend(player, "reopen"); + } else { + console.log("#M unsent mail, lost item: " + item); + } + return; + } + if (player) { + socket.emit("game_response", { response: "mail_sent", to: data.to, cevent: "mail_sent" }); + } + }, + function () { + var player = players[socket.id]; + if (player) { + socket.emit("game_response", { response: "mail_failed", reason: "coms_failure" }); + } + if (item) { + console.log("#M unsent mail, lost item: " + item); + } + }, + ); + resend(player, "reopen"); + success_response("mail_sending", { sucess: false, in_progress: true, received: "unknown" }); + }); + socket.on("leave", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!can_walk(player)) { + return fail_response("transport_failed"); + } + if ( + (0 && player.s.block) || + player.targets > 5 || + !(player.map == "jail" || player.map == "cyberland" || instances[player.in].solo) + ) { + return fail_response("cant_escape"); + } + transport_player_to(player, B.start_map); + success_response(); + }); + socket.on("transport", function (data) { + var player = players[socket.id]; + var can_reach = false; + if (!player) { + return; + } // this was missing, probably caused a production exception - should be added everywhere [04/09/16] + if (!can_walk(player) || player.map == "jail") { + return fail_response("transport_failed"); + } + var new_map = G.maps[data.to]; + var s = data.s || 0; + var the_door = null; + if (!new_map || !instances[data.to] || !instances[data.to].allow) { + return fail_response("cant_enter"); + } + if ((0 && player.s.block) || player.targets > 5) { + return fail_response("cant_escape"); + } + + (G.maps[player.map].doors || []).forEach(function (door) { + if ( + !can_reach && + door[4] == data.to && + s == (door[5] || 0) && + simple_distance( + { map: player.map, x: G.maps[player.map].spawns[door[6]][0], y: G.maps[player.map].spawns[door[6]][1] }, + player, + ) < B.door_dist + ) { + can_reach = "door"; + the_door = door; + } + }); + if ((player.map == "woffice" && gameplay == "hardcore") || player.role == "gm") { + can_reach = "transport"; + } + if ( + !can_reach && + G.maps[player.map].ref.transporter && + simple_distance(G.maps[player.map].ref.transporter, player) < B.transporter_dist && + G.npcs.transporter.places[data.to] === s + ) { + can_reach = "transport"; + } + if (can_reach == "transport" && player.s.dampened) { + return fail_response("transport_cant_dampened"); + } + if (!can_reach) { + return fail_response("transport_cant_reach"); + } + if (the_door && the_door[7] == "protected") { + var protected = false; + for (var x in instances[player.in].monsters || {}) { + if (instances[player.in].monsters[x].map_def.gatekeeper) { + protected = true; + } + } + if (protected) { + return fail_response("transport_cant_protection"); + } + } + if ( + the_door && + the_door[7] == "ulocked" && + G.maps[data.to].mount && + player.user && + !player.user.unlocked[data.to] + ) { + return fail_response("transport_cant_locked"); + } + + if (instances[data.to].mount && !player.user) { + if (player.mounting || player.unmounting) { + return fail_response("bank_opi"); + } + add_call_cost(32, undefined, "bank"); + player.mounting = new Date(); + player.mount_to = data.to; + player.mount_s = s; + sync_loop(); + return success_response({ success: false, in_progress: true }); + } else if (!instances[data.to].mount && player.user) { + // previously handled at transport_player_to + if (player.mounting || player.unmounting) { + return fail_response("bank_opi"); + } + add_call_cost(16, undefined, "bank"); + player.unmounting = new Date(); + player.unmount_to = data.to; + player.unmount_s = s; + sync_loop(); + return success_response({ success: false, in_progress: true }); + } else if (0 && instances[data.to].mount && player.user && data.to != player.map) { + // an easy prevention for the bank re-entry nuisance, bank can be re-entered physically but not digitally [03/11/16] + // commented out for bank_u / bank_b [06/05/20] + return fail_response("transport_failed"); + } else { + decay_s(player, 5200); + transport_player_to(player, data.to, s); + return success_response(); + } + }); + socket.on("enter", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (gameplay != "normal") { + return fail_response("transport_failed"); + } + if (!can_walk(player) || player.map == "jail") { + return fail_response("transport_failed"); + } + if (player.s.block || player.targets > 5) { + return fail_response("cant_escape"); + } + // if(player.role!="gm" && !(data.place==player.map && player.map=="cgallery")) + // { + // if(data.place!="resort" && !G.maps[player.map].ref.transporter || simple_distance(G.maps[player.map].ref.transporter,player)>80) return socket.emit("game_response","transport_cant_reach"); + // if(data.place=="resort" && player.map!="resort") return socket.emit("game_response","transport_cant_reach"); + // } + server_log(data); + var name = randomStr(24); + if (data.place == "resort" && 0) { + var name = "resort_" + data.name; + instance = instances[name] || create_instance(name, "resort_map"); + transport_player_to(player, name); + } else if (data.place == "duelland") { + if (instances[data.name] && instances[data.name].map == "duelland") { + transport_player_to(player, data.name); + instance_emit(data.name, "game_log", { message: player.name + " is spectating the duel", color: "gray" }); + } else { + return fail_response("cant_enter"); + } + } else if (data.place == "crypt" || data.place == "winter_instance") { + var f = "cave"; + var ref = G.maps.cave.spawns[2]; + var item = "cryptkey"; + if (data.place == "winter_instance") { + f = "winterland"; + ref = G.maps.winterland.spawns[5]; + item = "frozenkey"; + } + if (simple_distance(player, { in: f, map: f, x: ref[0], y: ref[1] }) > 120) { + return fail_response("transport_cant_reach"); + } + if (data.name && instances[data.name] && instances[data.name].map == data.place) { + transport_player_to(player, data.name); + } else { + if (!consume_one_by_id(player, item)) { + return fail_response("transport_cant_item"); + } + instance = create_instance(name, data.place); + transport_player_to(player, name); + } + resend(player, "u+cid+reopen"); + } else if (data.place == "dungeon0" && player.role == "gm") { + instance = create_instance(name, "dungeon0", { solo: player.id }); + transport_player_to(player, name); + } else if (data.place == "cgallery" && player.role == "gm") { + var point = null; + if (player.map == "cgallery") { + point = [player.x, player.y]; + } + instance = create_instance(name, "cgallery", { solo: player.id }); + transport_player_to(player, name, point); + var bodies = []; + var heads = []; + var hairs = []; + var wings = []; + var hats = []; + for (var s in G.sprites) { + var current = G.sprites[s]; + var matrix = current.matrix; + if (current.skip || current.rskip) { + continue; + } + if (!in_arr(current.type, ["body", "head", "hair", "s_wings", "hat"])) { + continue; + } + for (var i = 0; i < matrix.length; i++) { + for (var j = 0; j < matrix[i].length; j++) { + if (!matrix[i][j]) { + continue; + } + if (current.type == "body") { + bodies.push(matrix[i][j]); + } + if (current.type == "head") { + heads.push(matrix[i][j]); + } + if (current.type == "hair") { + hairs.push(matrix[i][j]); + } + if (current.type == "hat") { + hats.push(matrix[i][j]); + } + if (current.type == "s_wings") { + wings.push(matrix[i][j]); + } + T[matrix[i][j]] = current.type; + } + } + } + var xs = [-80, -40, 0, 40, 80]; + for (var n = 0; n < bodies.length; n++) { + var npc = create_npc( + { + name: bodies[n], + level: 1, + speed: 12, + hp: 1000, + skin: bodies[n], + cx: {}, + }, + { + position: [xs[n % xs.length], -parseInt(n / xs.length) * 40], + id: bodies[n], + }, + instance, + ); + npc.ctype = "body"; + npc.cx = { head: heads[n] }; + npc.id = "body: " + bodies[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + for (var n = 0; n < heads.length; n++) { + var npc = create_npc( + { name: heads[n], level: 1, speed: 12, hp: 1000, skin: bodies[0], cx: {} }, + { position: [xs[n % xs.length] + 240, -parseInt(n / xs.length) * 40], id: heads[n] }, + instance, + ); + npc.ctype = "head"; + npc.cx = { head: heads[n] }; + npc.id = "head: " + heads[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + for (var n = 0; n < hairs.length; n++) { + var npc = create_npc( + { name: hairs[n], level: 1, speed: 12, hp: 1000, skin: bodies[1], cx: {} }, + { position: [xs[n % xs.length] + 480, -parseInt(n / xs.length) * 40], id: hairs[n] }, + instance, + ); + npc.ctype = "hair"; + npc.cx = { head: heads[0], hair: hairs[n] }; + npc.id = "hair: " + hairs[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + for (var n = 0; n < hats.length; n++) { + var npc = create_npc( + { name: hats[n], level: 1, speed: 12, hp: 1000, skin: bodies[2], cx: {} }, + { position: [xs[n % xs.length] + 720, -parseInt(n / xs.length) * 40], id: hats[n] }, + instance, + ); + npc.ctype = "hat"; + npc.cx = { head: heads[0], hat: hats[n] }; + npc.id = "hair: " + hats[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + } else { + return fail_response("transport_cant_reach"); + } + success_response(); + }); + socket.on("town", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + // if(player.last.town && mssince(player.last.town)<1200) return; // bad ui experience [Unknown] - got reported and disabled [25/03/22] + if (!can_walk(player) || player.map == "jail") { + return fail_response("transport_failed"); + } + if ((0 && player.s.block) || player.targets > 5) { + return fail_response("cant_escape"); + } + player.c.town = { ms: min((player.c.town && player.c.town.ms) || 5000, 3000) }; + resend(player, "u+nc"); + success_response({ success: false, in_progress: true }); + }); + socket.on("respawn", function (data) { + var player = players[socket.id]; + if (!player || !player.rip) { + return fail_response("invalid"); + } + if (player.rip_time && ssince(player.rip_time) < B.rip_time) { + return fail_response("cant_respawn"); + } + + delete player.s.block; + player.hp = player.max_hp; + player.mp = round(player.max_mp / 2); + player.rip = false; + if (gameplay == "hardcore") { + reset_player(player, 1); + } + + if (data && data.safe) { + // gameplay=="hardcore" && + transport_player_to(player, "woffice", 0, 1); + } else { + var place = G.maps[player.map].on_death || G.maps[B.start_map].on_death || ["main", 0]; + transport_player_to(player, place[0], place[1], 1); + } + if (player.party) { + send_party_update(player.party); + } + invincible_logic(player); + resend(player, "u+cid"); + success_response("data", { place: "respawn", cevent: "respawn" }); + }); + socket.on("random_look", function (data) { + return socket.emit("game_log", "socket.emit('enter',{place:'cgallery'})"); + var player = players[socket.id]; + if (!player) { + return; + } + var bodies = []; + var heads = []; + var hairs = []; + var wings = []; + var hats = []; + if (player.rlooks == 25 && !is_sdk && player.role != "gm") { + return; + } + if (player.role == "gm") { + reduce_call_cost(40); + } + player.rlooks = (player.rlooks || 0) + 1; + for (var s in G.sprites) { + var current = G.sprites[s]; + var matrix = current.matrix; + if (current.skip || current.rskip) { + continue; + } + if (!in_arr(current.type, ["body", "head", "hair", "s_wings", "hat"])) { + continue; + } + for (var i = 0; i < matrix.length; i++) { + for (var j = 0; j < matrix[i].length; j++) { + if (!matrix[i][j]) { + continue; + } + if (current.type == "body") { + bodies.push(matrix[i][j]); + } + if (current.type == "head") { + heads.push(matrix[i][j]); + } + if (current.type == "hair") { + hairs.push(matrix[i][j]); + } + if (current.type == "hat") { + hats.push(matrix[i][j]); + } + if (current.type == "s_wings") { + wings.push(matrix[i][j]); + } + } + } + } + var head = "head"; + if (Math.random() < 0.1) { + head = random_one(heads); + } + player.tskin = random_one(bodies); + player.tcx = [head, random_one(hairs)]; + if (Math.random() < 0.08) { + player.tcx.push(random_one(wings)); + } + if (Math.random() < 0.4) { + player.tcx.push(random_one(hats)); + } + resend(player, "u+cid"); + }); + socket.on("unlock", function (data) { + return; // [27/06/18] + var player = players[socket.id]; + if (!player) { + return; + } + if (gameplay == "normal") { + return; + } // ? [27/06/18] + if (data.name == "code") { + var item = player.items[data.num || 0]; + if (!item || item.name != "computer" || item.charges === 0 || item.charges < 0 || player.unlocking_code) { + return; + } + if (!item.charges) { + item.charges = 2; + } + item.charges--; + player.citems[data.num || 0] = player.items[data.num || 0]; // explicitly linked to the actual item + player.unlocking_code = true; + appengine_call( + "user_operation", + { auth: player.auth, operation: "code_unlock", suffix: "/code_unlock/" + player.owner, retries: 4 }, + function (result) { + server_log("user_operation_code: " + JSON.stringify(result), 1); + if (result.failed) { + if (result.reason == "already") { + socket.emit( + "game_log", + "Your CODE slots are already unlocked. You can lend your Ancient Computer to a friend in need, that's why there are 2 charges :]", + ); + item.charges++; + } else { + socket.emit("game_log", "Unlock Failed. Email hello@adventure.land with a screenshot."); + } + return; + } + socket.emit("game_log", "CODE slots increased to 100!"); + resend(player, "reopen"); + }, + ); + } + }); + socket.on("dismantle", function (data) { + var player = players[socket.id]; + var check = true; + var items = []; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!player.computer && simple_distance(G.maps.main.ref.craftsman, player) > B.sell_dist) { + return fail_response("distance"); + } + // if(player.esize<=0) return socket.emit("game_response","inventory_full"); + var item = player.items[data.num]; + if (item && item.level && G.items[item.name].compound) { + var cost = min(50000000, calculate_item_value(item) * 10); + if (player.gold < cost) { + return fail_response("gold_not_enough"); + } + if (player.esize < 2) { + return fail_response("inv_size"); + } + if (G.items[item.name].type == "booster") { + return fail_response("dismantle_cant"); + } + player.gold -= cost; + consume_one(player, data.num); + add_item( + player, + { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, + { announce: false }, + ); + add_item( + player, + { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, + { announce: false }, + ); + add_item( + player, + { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, + { announce: false }, + ); + resend(player, "reopen+nc+inv"); + return success_response("dismantle", { name: item.name, level: item.level, cost: cost, cevent: true }); + } + if (!item || !G.dismantle[item.name]) { + return fail_response("dismantle_cant"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + if (player.gold < G.dismantle[item.name].cost) { + return fail_response("gold_not_enough"); + } + if ( + !can_add_items(player, list_to_pseudo_items(G.dismantle[item.name].items), { + space: ((item.q || 1) == 1 && 1) || 0, + }) + ) { + return fail_response("inv_size"); + } + player.gold -= G.dismantle[item.name].cost; + consume_one(player, data.num); + G.dismantle[item.name].items.forEach(function (e) { + if (e[0] < 1 && Math.random() > e[0]) { + return; + } + add_item(player, e[1], { q: max(1, e[0]), p: item.p && !G.titles[item.p].misc && item.p }); + }); + resend(player, "reopen+nc+inv"); + success_response("dismantle", { name: item.name, cevent: true }); + }); + socket.on("craft", function (data) { + var player = players[socket.id]; + var check = true; + var items = []; + var quantity = {}; + var place = {}; + var space = false; + var p = {}; + var locked = false; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + data.items.forEach(function (x) { + if (!player.items[x[1]]) { + check = false; + } else if (player.items[x[1]].l || player.items[x[1]].b) { + check = false; + locked = true; + } else { + var name = player.items[x[1]].name; + if (player.items[x[1]].level) { + name += "+" + player.items[x[1]].level; + } + items.push(name); + quantity[player.items[x[1]].name] = player.items[x[1]].q || 1; // (quantity[player.items[x[1]].name]||0)+ removed this part, seems like a bad initial addition [17/06/18] + place[player.items[x[1]].name] = x[1]; + if (player.items[x[1]].p && !G.titles[player.items[x[1]].p].misc) { + p[player.items[x[1]].p] = player.items[x[1]].p; + } + } + }); + if (locked) { + return fail_response("item_locked"); + } + if (!check) { + return fail_response("no_item"); + } + //if(items.length<2) return socket.emit("game_response","craft_atleast2"); + items.sort(); + var key = items.join(","); + // console.log(key); + if (!D.craftmap[key]) { + return fail_response("craft_cant"); + } + var name = D.craftmap[key]; + var enough = true; + if ( + !player.computer && + !G.craft[name].quest && + simple_distance(get_npc_coords("craftsman"), player) > B.sell_dist + ) { + return fail_response("distance"); + } + if ( + !player.computer && + G.craft[name].quest && + simple_distance(get_npc_coords(G.craft[name].quest), player) > B.sell_dist + ) { + return fail_response("distance"); + } + if (player.gold < G.craft[name].cost) { + return fail_response("gold_not_enough"); + } + G.craft[name].items.forEach(function (i) { + if (quantity[i[1]] < i[0]) { + enough = false; + } + if (quantity[i[1]] == i[0]) { + space = true; + } + }); + if (!space && !can_add_item(player, create_new_item(name))) { + return fail_response("inventory_full"); + } + if (!enough) { + return fail_response("craft_cant_quantity"); + } + player.gold -= G.craft[name].cost; + G.craft[name].items.forEach(function (x) { + consume(player, place[x[1]], x[0]); + }); + var i = add_item(player, name, { r: 1, p: Object.keys(p).length && random_one(p) }); + resend(player, "reopen+nc+inv"); + success_response("craft", { num: i, name: name, cevent: true }); + }); + socket.on("exchange", function (data) { + var player = players[socket.id]; + var item = player.items[data.item_num]; + var def = G.items[item && item.name]; + var suffix = ""; + if (player.q.exchange) { + return fail_response("exchange_existing"); + } + if (def && (def.compound || def.upgrade)) { + suffix = item.level || 0; + } + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + G.maps.main.exchange.name = player.name; + if (!def || !def.e || item.q != data.q || !D.drops[item.name + suffix]) { + return fail_response("invalid"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (!player.computer && !def.quest && simple_distance(G.maps.main.exchange, player) > B.sell_dist) { + return fail_response("distance"); + } + if (!player.computer && def.quest && simple_distance(G.quests[def.quest], player) > B.sell_dist) { + return fail_response("distance"); + } + if (player.esize <= 0 && !((item.q || 1) == 1)) { + return fail_response("inventory_full"); + } + if (def.e > 1 && item.q < def.e) { + return fail_response("exchange_notenough"); + } + player.p.stats.exchanges[item.name + suffix] = (player.p.stats.exchanges[item.name + suffix] || 0) + 1; + consume(player, data.item_num, def.e); + var num = add_item(player, "placeholder"); + var ms = 3000 + parseInt(Math.random() * 3000); + if (gameplay == "hardcore") { + ms = 400; + } + player.q.exchange = { ms: ms, len: ms, name: item.name, id: item.name + suffix, q: def.e, num: num }; + if (suffix) { + player.q.exchange.s = suffix; + } + if (item.v) { + player.q.exchange.v = item.v; + } + if (def.quest) { + player.q.exchange.qs = def.quest; + } + resend(player, "reopen+nc+inv"); + success_response({ success: false, in_progress: true, num: num }); + }); + socket.on("exchange_buy", function (data) { + //console.log(JSON.stringify(data)); + var player = players[socket.id]; + var item = player.items[data.num]; + var def = G.items[item && item.name]; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!def || def.type != "token") { + return fail_response("invalid"); + } + var npc = G.maps.main.ref[G.items[item.name].npc || item.name + "s"]; + var num = -1; + if (!G.tokens[item.name][data.name]) { + return fail_response("invalid"); + } + if (item.l) { + return fail_response("item_locked"); + } + npc.name = player.name; + if (item.q != data.q) { + return fail_response("safety_check"); + } + if (!player.computer && simple_distance(npc, player) > B.sell_dist) { + return fail_response("distance"); + } + if (item.q < G.tokens[item.name][data.name]) { + return fail_response("exchange_notenough"); + } + + if (G.tokens[item.name][data.name] < 1) { + var q = parseInt(1 / G.tokens[item.name][data.name]); + if (item.q != 1 && !can_add_item(player, { name: data.name, q: q })) { + return fail_response("inventory_full"); + } + consume(player, data.num, 1); + num = add_item(player, data.name, { q: q, announce: false, r: true }); + } else { + var name = data.name; + var idata = undefined; + if (name.search("-") != -1) { + idata = name.split("-")[1]; + name = name.split("-")[0]; + } + var new_item = create_new_item(name); + if (idata) { + new_item.data = idata; + } + if (item.q != G.tokens[item.name][data.name] && !can_add_item(player, new_item)) { + return fail_response("inventory_full"); + } + consume(player, data.num, G.tokens[item.name][data.name]); + num = add_item(player, new_item, { announce: false, r: true }); + } + + xy_emit(npc, "upgrade", { type: item.name + "s", success: 1 }); + resend(player, "reopen+nc+inv"); + success_response({ num: num, cevent: true }); + }); + socket.on("locksmith", function (data) { + var player = players[socket.id]; + var item = player.items[data.item_num]; + var def = G.items[item && item.name]; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!player.computer && simple_distance(G.maps.desertland.ref.locksmith, player) > B.sell_dist) { + return fail_response("distance"); + } + var item = player.items[data.num]; + if (!item) { + return fail_response("no_item"); + } + var def = G.items[item.name]; + if (in_arr(def.type, ["uscroll", "cscroll", "pscroll", "offering", "tome"])) { + return fail_response("locksmith_cant"); + } + if (data.operation == "unlock") { + if (!item.l) { + resend(player, "reopen+nc"); + return fail_response("locksmith_aunlocked", "locksmith", "already_unlocked"); + } + if (item.l == "s") { + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; + item.ld = JSON.stringify(future_s(2 * 24 * 60 * 60)); + item.l = "u"; + socket.emit("game_response", "locksmith_unsealed"); + } else if (item.l == "u") { + var date = new Date(JSON.parse(item.ld)); + if (date < new Date()) { + delete item.l; + socket.emit("game_response", "locksmith_unseal_complete"); + } else { + resend(player, "reopen+nc"); + return success_response("locksmith_unsealing", { hours: -hsince(date), success: false, in_progress: true }); + } + } else { + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; + delete item.l; + socket.emit("game_response", "locksmith_unlocked"); + } + } else if (data.operation == "lock") { + if (item.l) { + resend(player, "reopen+nc"); + return fail_response("locksmith_alocked", "locksmith", "already_locked"); + } + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; + item.l = "l"; + socket.emit("game_response", "locksmith_locked"); + } else if (data.operation == "seal") { + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; + item.l = "s"; + socket.emit("game_response", "locksmith_sealed"); + } + player.citems[data.num] = cache_item(player.items[data.num]); + resend(player, "reopen+nc"); + success_response(); + }); + socket.on("compound", function (data) { + try { + var player = players[socket.id]; + var item0 = player.items[data.items[0]]; + var item1 = player.items[data.items[1]]; + var item2 = player.items[data.items[2]]; + var scroll = player.items[data.scroll_num]; + var result; + var ex = ""; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + G.maps.main.compound.name = player.name; + if (player.q.compound) { + return fail_response("compound_in_progress", "compound", "in_progress"); + } + var offering = player.items[data.offering_num]; + if (offering && G.items[offering.name].type != "offering") { + return socket.emit("game_response", "compound_invalid_offering"); + } + if (!item0 || (item0.level || 0) != data.clevel) { + return fail_response("no_item"); + } + if (!player.computer && simple_distance(G.maps.main.compound, player) > B.sell_dist) { + return socket.emit("game_response", { response: "distance", place: "compound", failed: true }); + } + var def = G.items[item0.name]; + var scroll_def = G.items[scroll.name]; + var offering_def = offering && G.items[offering.name]; + var grade = calculate_item_grade(def, item0); + if (grade == 4) { + return socket.emit("game_response", { + response: "max_level", + level: item.level, + place: "compound", + failed: true, + }); + } + if ( + !( + item0.name == item1.name && + item1.name == item2.name && + (item0.level || 0) == (item1.level || 0) && + (item1.level || 0) == (item2.level || 0) + ) + ) { + return socket.emit("game_response", "compound_mismatch"); + } + if (!def.compound) { + return socket.emit("game_response", "compound_cant"); + } + if (scroll_def.type != "cscroll" || grade > scroll_def.grade) { + return socket.emit("game_response", "compound_incompatible_scroll"); + } + if (data.items[0] == data.items[1] || data.items[1] == data.items[2] || data.items[0] == data.items[2]) { + return socket.emit("game_response", { response: "misc_fail", place: "compound", failed: true }); + } + if (item0.l || item1.l || item2.l) { + return socket.emit("game_response", { response: "item_locked", place: "compound", failed: true }); + } + + if (!data.calculate) { + consume_one(player, data.scroll_num); + } + + var new_level = (item0.level || 0) + 1; + var probability = 1; + var oprobability = 1; + var result = 0; + var proc = 0; + var grace = 0; + var igrade = def.igrade; + var high = false; + var grace_bonus = 0; + if (item0.level >= 3) { + igrade = calculate_item_grade(def, { name: item0.name, level: item0.level - 2 }); + } + + delete player.p.c_item; + delete player.p.c_itemx; + delete player.p.c_roll; + + oprobability = probability = D.compounds[igrade][new_level]; + result = Math.random(); + server_log(result + " < " + probability); + + if (scroll_def.grade > grade) { + probability = probability * 1.1 + 0.001; + server_log("higher grade scroll " + result + " < " + probability); + if (!data.calculate) { + grace_bonus += 0.4; + } + high = scroll_def.grade - grade; + } + + if (offering) { + var increase = 0.5; + if (!data.calculate) { + consume_one(player, data.offering_num); + } + grace = 0.027 * ((item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0) + 0.5 + player.p.ograce); + + if (offering_def.grade > grade + 1) { + probability = probability * 1.64 + grace * 2; + high = true; + increase = 3; + } else if (offering_def.grade > grade) { + probability = probability * 1.48 + grace; + high = true; + increase = 1; + } else if (offering_def.grade == grade) { + probability = probability * 1.36 + min(30 * 0.027, grace); + } else if (offering_def.grade == grade - 1) { + probability = probability * 1.15 + min(25 * 0.019, grace) / max(item0.level - 2, 1); + increase = 0.2; + } else { + probability = probability * 1.08 + min(15 * 0.015, grace) / max(item0.level - 1, 1); + increase = 0.1; + } + + if (!data.calculate) { + item0.grace = (item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0); + grace_bonus += increase; + } + server_log("offering " + result + " < " + probability); + } else { + grace = 0.007 * ((item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0) + player.p.ograce); + probability = probability + min(25 * 0.007, grace) / max(item0.level - 1, 1); + if (!data.calculate) { + item0.grace = max(max(item0.grace || 0, item1.grace || 0), item2.grace || 0); + } + } + + if (!data.calculate) { + item0.grace = item0.grace / 6.4 + grace_bonus; + } + + if (def.type == "booster") { + probability = 0.9999999999999; + proc = offering && 0.12; + } else { + probability = min( + probability, + min(oprobability * (3 + ((high && high * 0.6) || 0)), oprobability + 0.2 + ((high && high * 0.05) || 0)), + ); + } + + if (gameplay == "test") { + result = 0; + } + + server_log(result + " < " + probability + " grace: " + grace); + + if (data.calculate) { + return success_response("compound_chance", { + calculate: true, + chance: probability, + item: cache_item(item0), + scroll: scroll.name, + offering: (offering && offering.name) || undefined, + grace: (item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0), + }); + } + + player.p.c_roll = result; + var len = 10000; + if (gameplay == "hardcore") { + len = 1200; + } + if (player.s.massproduction) { + len /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + len /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.compound = { ms: len, len: len, num: data.items[0], nums: [] }; + player.items[data.items[0]] = { + name: "placeholder", + p: { + chance: probability, + name: item0.name, + level: item0.level, + scroll: scroll.name, + offering: offering && offering.name, + nums: [], + }, + }; + player.items[data.items[1]] = null; + player.items[data.items[2]] = null; + + if (result <= probability) { + if (offering) { + player.p.ograce = 0; + } else { + player.p.ograce *= 1 - new_level * 0.02; + } + item0.level = new_level; + if ((item0.p || item1.p || item2.p) != "legacy") { + item0.p = item0.p || item1.p || item2.p; + } else { + delete item0.p; + } + player.p.c_item = item0; + if (item0.oo != player.name) { + item0.o = player.name; + } + player.esize += 2; + if (parseInt(result * 10000) == parseInt(probability * 10000)) { + // && grade>=1 + add_achievement(player, "lucky"); + add_item_property(item0, "lucky"); + } + if (item1.v || item2.v) { + item0.v = item1.v || item2.v; + } + if (item0.name == "ctristone" && item0.level == 1 && Math.random() < 0.012) { + item0.name = "cdarktristone"; + } + if (def.type == "booster") { + var activate = false; + item0.extra = 0; + while (offering && proc && Math.random() < proc) { + item0.extra++; + item0.level += 1; + proc /= 2.0; + } + var seconds = 6 * 24 * 60 * 60; + [item0, item1, item2].forEach(function (i) { + if (i.expires) { + seconds -= ssince(i.expires); + activate = true; + } else { + seconds += def.days * 24 * 60 * 60; + } + }); + if (activate) { + item0.expires = future_s(seconds / 3); + } + } + } else { + if (offering) { + player.p.ograce += 0.4; + } + player.esize += 2; + player.p.c_item = null; + player.p.c_itemx = item0; + } + + player.citems[data.items[0]] = cache_item(player.items[data.items[0]]); + player.citems[data.items[1]] = cache_item(player.items[data.items[1]]); + player.citems[data.items[2]] = cache_item(player.items[data.items[2]]); + + resend(player, "reopen+nc+inv" + ex); + } catch (e) { + server_log("compound_e " + e); + return socket.emit("game_response", { response: "exception", place: "compound", failed: true }); + } + }); + socket.on("upgrade", function (data) { + try { + var player = players[socket.id]; + var item = player.items[data.item_num]; + var scroll = player.items[data.scroll_num]; + var offering = player.items[data.offering_num]; + var result; + var ex = ""; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (player.q.upgrade) { + return socket.emit("game_response", "upgrade_in_progress"); + } + G.maps.main.upgrade.name = player.name; + if (!player.computer && simple_distance(G.maps.main.upgrade, player) > B.sell_dist) { + return socket.emit("game_response", { response: "distance", place: "upgrade", failed: true }); + } + if (!item) { + return socket.emit("game_response", "upgrade_no_item"); + } + if ( + offering && + !( + (G.items[offering.name] && G.items[offering.name].type == "offering") || + (G.items[offering.name] && G.items[offering.name].offering !== undefined && !(item.level > 0)) + ) + ) { + return socket.emit("game_response", "upgrade_invalid_offering"); + } + if (!scroll && !offering) { + return socket.emit("game_response", "upgrade_no_scroll"); + } + if ((item.level || 0) != data.clevel) { + return socket.emit("game_response", "upgrade_mismatch"); + } + var item_def = G.items[item.name]; + var scroll_def = scroll && G.items[scroll.name]; + var offering_def = offering && G.items[offering.name]; + var grade = calculate_item_grade(item_def, item); + if (!item_def.upgrade) { + return socket.emit("game_response", "upgrade_cant"); + } + if ( + scroll && + (!in_arr(scroll_def.type, ["uscroll", "pscroll"]) || + (scroll_def.type == "uscroll" && !item_def.upgrade) || + (scroll_def.type == "pscroll" && !item_def.stat) || + grade > scroll_def.grade) + ) { + if (grade == 4 && scroll_def.type == "uscroll") { + return socket.emit("game_response", { + response: "max_level", + level: item.level, + place: "upgrade", + failed: true, + }); + } + return socket.emit("game_response", "upgrade_incompatible_scroll"); + } + + var new_level = (item.level || 0) + 1; + var probability = 1; + var oprobability = 1; + var grace = 0; + var high = false; + var ograde = calculate_item_grade(item_def, { name: item.name, level: 0 }); + var tmult = 1; + if (ograde == 1) { + tmult = 1.5; + } else if (ograde == 2) { + tmult = 2; + } + + delete player.p.u_item; + delete player.p.u_type; + delete player.p.u_itemx; + delete player.p.u_roll; + delete player.p.u_fail; + delete player.p.u_level; + + player.p.u_level = item.level || 0; + + if (!scroll) { + if (G.items[offering.name] && G.items[offering.name].offering !== undefined) { + var chance = 0.16; + var ms = 2000; + if ( + G.items[offering.name].offering > ograde || + (G.items[offering.name].offering == 2 && calculate_item_value(item) <= 20000000) + ) { + chance = 0.32; + } + chance *= [2.8, 1.6, 1][ograde]; + if (G.items[offering.name].offering < ograde) { + return socket.emit("game_response", "upgrade_invalid_offering"); + } + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: chance, + offering: offering.name, + item: cache_item(item), + grace: item.grace || 0, + }); + } + var result = Math.random(); + + consume_one(player, data.offering_num); + player.p.u_type = "normal"; + player.p.u_roll = result; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num, silent: true }; + player.items[data.item_num] = { + name: "placeholder", + p: { + chance: chance, + name: item.name, + level: item.level, + scroll: null, + offering: offering.name, + nums: [], + }, + }; + + if (result <= chance) { + item.p = "shiny"; + player.p.u_item = item; + } else { + player.p.u_item = item; + player.p.u_fail = true; + } + } else { + var ms = 1000; + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: 1, + offering: offering.name, + item: cache_item(item), + grace: item.grace || 0, + }); + } + consume_one(player, data.offering_num); + item.grace = (item.grace || 0) + 0.5; + server_log("item.grace: " + item.grace); + player.p.u_type = "offering"; + player.p.u_item = item; + player.p.u_roll = 0.999999999999999; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; + player.items[data.item_num] = { + name: "placeholder", + p: { chance: 1, name: item.name, level: item.level, scroll: null, offering: offering.name, nums: [] }, + }; + } + } else if (scroll_def.type == "uscroll") { + if (item.l) { + return socket.emit("game_response", { response: "item_locked", place: "upgrade", failed: true }); + } + if (grade == 4) { + return socket.emit("game_response", { + response: "max_level", + level: item.level, + place: "upgrade", + failed: true, + }); + } + if (!data.calculate) { + consume_one(player, data.scroll_num); + } + oprobability = probability = D.upgrades[item_def.igrade][new_level]; + // grace=max(0,min(new_level+1, (item.grace||0) + min(3,player.p.ugrace[new_level]/3.0) +min(2,ugrace[new_level]/4.0) +item_def.igrace + player.p.ograce/2.0 )); - original [16/07/18] + grace = max( + 0, + min(new_level + 1, (item.grace || 0) + min(3, player.p.ugrace[new_level] / 4.5) + item_def.igrace) + + min(6, S.ugrace[new_level] / 3.0) + + player.p.ograce / 3.2, + ); + server_log( + "Grace num: " + + grace + + "\nItem: " + + (item.grace || 0) + + "\nPlayer: " + + min(3, player.p.ugrace[new_level] / 4.5) + + "\nDef: " + + item_def.igrace + + "\nOgrace:" + + player.p.ograce / 3.2 + + "\nS.ugrace: " + + min(6, S.ugrace[new_level] / 3.0), + ); + grace = (probability * grace) / new_level + grace / 1000.0; + server_log("Grace-prob: " + grace); + result = Math.random(); + server_log(result + " < " + probability); + + // if(!data.calculate && item.name=="throwingstars" && scroll_def.grade==2 && item.level==4) item.p="superfast"; + + if (scroll_def.grade > grade && new_level <= 10) { + probability = probability * 1.2 + 0.01; + high = true; + if (!data.calculate) { + item.grace = (item.grace || 0) + 0.4; + } + } + + if (offering) { + var increase = 0.4; + if (!data.calculate) { + consume_one(player, data.offering_num); + } + + if (offering_def.grade > grade + 1) { + probability = probability * 1.7 + grace * 4; + high = true; + increase = 3; + } else if (offering_def.grade > grade) { + probability = probability * 1.5 + grace * 1.2; + high = true; + increase = 1; + } else if (offering_def.grade == grade) { + probability = probability * 1.4 + grace; + } else if (offering_def.grade == grade - 1) { + probability = probability * 1.15 + grace / 3.2; + increase = 0.2; + } else { + probability = probability * 1.08 + grace / 4; + increase = 0.1; + } + + if (!data.calculate) { + item.grace = (item.grace || 0) + increase; + } // previously +1 [16/07/18] + } else { + grace = max(0, grace / 4.8 - 0.4 / ((new_level - 0.999) * (new_level - 0.999))); + probability += grace; // previously 12.0 // previously 9.0 [16/07/18] + } + + if (!data.calculate && Math.random() < 0.025) { + // Bonus grace + item.grace = (item.grace || 0) + 1; + } + + if (data.item_num == player.p.item_num && Math.random() < 0.6) { + // Added [29/10/17] + server_log("16 cheat"); + result = max(Math.random() / 10000.0, result * 0.975 - 0.012); + } + + if (high) { + probability = min(probability, min(oprobability + 0.36, oprobability * 3)); + } else { + probability = min(probability, min(oprobability + 0.24, oprobability * 2)); + } + server_log(result + " < " + probability + " grace: " + grace); + + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: probability, + offering: (offering && offering.name) || undefined, + item: cache_item(item), + grace: item.grace || 0, + scroll: scroll.name, + }); + } + + if (gameplay == "test") { + result = 0; + } + // result=probability; + player.p.u_type = "normal"; + player.p.u_roll = result; + var ms = 500 * new_level * Math.sqrt(new_level) * tmult; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; + if (gameplay == "hardcore") { + player.q.upgrade.ms = player.q.upgrade.len = 500; + } + player.items[data.item_num] = { + name: "placeholder", + p: { + chance: probability, + name: item.name, + level: item.level, + scroll: scroll.name, + offering: offering && offering.name, + nums: [], + }, + }; + + //result=probability+EPS; + + if (result <= probability) { + // console.log("here"); + player.p.ugrace[new_level] = S.ugrace[new_level] = 0; + if (offering) { + player.p.ograce *= 0.25; + } else { + player.p.ograce *= 1 - new_level * 0.005; + } + item.level = new_level; + if (item.oo != player.name) { + item.o = player.name; + } + player.p.u_item = item; + if (parseInt(result * 10000) == parseInt(probability * 10000) && grade >= 1) { + add_item_property(item, "lucky"); + add_achievement(player, "lucky"); + } + } else { + player.p.ugrace[new_level - 1] += 1; + S.ugrace[new_level - 1] += 1; + player.p.ugrace[new_level] += 1; + S.ugrace[new_level] += 1; + if (new_level >= 8 && new_level <= 15) { + player.p.ugrace[new_level - 1] += 1; + S.ugrace[new_level - 1] += 1; + player.p.ugrace[new_level - 2] += 2 + ((offering && 1) || 0); + S.ugrace[new_level - 2] += 2; + player.p.ugrace[new_level - 3] += 2 + ((offering && 2) || 0); + S.ugrace[new_level - 3] += 3 + ((offering && 1) || 0); + } + if (offering) { + player.p.ograce += 0.6; + } // previously 1 [16/07/18] + if (scroll_def.grade != 3.6) { + player.p.u_itemx = item; + } else { + player.p.u_item = item; + player.p.u_fail = true; + } + if (parseInt(result * 10000) == parseInt(probability * 10000)) { + if (player.esize && grade >= 1) { + add_item(player, "essenceofgreed"); + } + add_achievement(player, "unlucky"); + } + } + } else if (scroll_def.type == "pscroll") { + var needed = [1, 10, 100, 1000, 9999, 9999, 9999]; + if (scroll.q < needed[grade]) { + return socket.emit("game_response", { response: "upgrade_scroll_q", q: needed[grade], h: scroll.q }); + } + if (!data.calculate) { + consume(player, data.scroll_num, needed[grade]); + } + + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: 0.99999, + scroll: scroll.name, + item: cache_item(item), + grace: item.grace || 0, + }); + } + + if (offering) { + consume_one(player, data.offering_num); + item.grace = (item.grace || 0) + 1; + server_log("Graced up to " + item.grace); + } + + item.stat_type = scroll_def.stat; + player.p.u_roll = Math.random(); + + player.p.u_type = "stat"; + if (player.p.u_roll <= 0.99999 || offering) { + player.p.u_item = item; + } else { + player.p.u_itemx = item; + } + var ms = 2000 * tmult * tmult; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; + player.items[data.item_num] = { + name: "placeholder", + p: { + chance: 0.99999, + name: item.name, + level: item.level, + scroll: scroll.name, + offering: offering && offering.name, + nums: [], + }, + }; + } + + player.citems[data.item_num] = cache_item(player.items[data.item_num]); + + resend(player, "reopen+nc+inv" + ex); + } catch (e) { + server_log("upgrade_e " + e); + return socket.emit("game_response", { response: "exception", place: "upgrade", failed: true }); + } + }); + socket.on("equip", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + player.c = {}; + data.num = to_number(data.num); + if (data.num >= player.items.length) { + return fail_response("invalid"); + } + var item = player.items[data.num]; + if (!item) { + return fail_response("no_item"); + } + if (item.b) { + return fail_response("item_blocked"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + var def = G.items[item.name]; + var to_update = "reopen+u+cid"; + var resolve = { num: data.num }; + var resolve_type = "data"; + def.iname = item.name; //just for orb name checks [08/10/16] + if (!def) { + return fail_response("no_item"); + } + // if(is_sdk) server_log("Trying to equip "+JSON.stringify(data)); + + if (data.slot && get_trade_slots(player).includes(data.slot) && !data.consume) { + if (item.acl || item.v) { + return fail_response("item_locked"); + } + var slot = data.slot; + var price = round(min(99999999999, max(parseInt(data.price) || 1, 1))); + var minutes = 1; + data.q = max(1, parseInt(data.q) || 1); + if ((item.q || 1) < data.q) { + return fail_response("not_enough"); + } + if (data.giveaway) { + minutes = max( + 5, + min(600, min(parseInt(data.minutes) || 5, max(60, round((calculate_item_value(item) * data.q) / 40000)))), + ); + } + if (!price || data.giveaway) { + price = 1; + } + if (player.slots[slot]) { + return fail_response("slot_occuppied"); + } + if (def.s) { + player.slots[slot] = create_new_item(player.items[data.num].name, 1); + player.slots[slot].price = price; + player.slots[slot].q = data.q; + player.slots[slot].rid = randomStr(4); + if (player.items[data.num].data) { + player.slots[slot].data = player.items[data.num].data; + } + if (data.giveaway) { + player.slots[slot].giveaway = minutes; + player.slots[slot].list = []; + player.giveaway = true; + } + player.cslots[slot] = cache_item(player.slots[slot], true); + consume(player, data.num, data.q); + if (data.giveaway) { + socket.emit("game_log", "Listed " + data.q + " " + item_name(player.slots[slot]) + " to giveaway!"); + } else { + socket.emit( + "game_log", + "Listed " + data.q + " " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold each", + ); + } + } else { + player.items[data.num].price = price; + player.items[data.num].rid = randomStr(4); + player.slots[slot] = player.items[data.num]; + if (data.giveaway) { + player.slots[slot].giveaway = minutes; + player.slots[slot].list = []; + player.giveaway = true; + } + player.cslots[slot] = cache_item(player.slots[slot], true); + player.items[data.num] = null; + player.citems[data.num] = null; + if (data.giveaway) { + socket.emit("game_log", "Listed " + item_name(player.slots[slot]) + " to giveaway!"); + } else { + socket.emit( + "game_log", + "Listed " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold", + ); + } + } + resolve.slot = slot; + } else if (def.type == "elixir") { + if (item.l) { + return fail_response("item_locked"); + } + if (player.slots.elixir && G.items[player.slots.elixir.name].withdrawal) { + add_condition(player, G.items[player.slots.elixir.name].withdrawal); + } + if (player.slots.elixir && player.slots.elixir.name == item.name) { + player.slots.elixir.expires = future_s(def.duration * 60 * 60 - ssince(player.slots.elixir.expires) * 0.975); + } else { + player.slots.elixir = { name: item.name, expires: future_s(def.duration * 60 * 60), ex: true }; + } + if (G.items[item.name] && G.items[item.name].withdrawal && player.s.withdrawal) { + delete player.s.withdrawal; + } + player.cslots.elixir = cache_item(player.slots.elixir); + consume_one(player, data.num); + resolve_type = "elixir"; + } else if (item.name == "cxjar") { + if (!item.data || item.l) { + return fail_response("item_locked"); + } + player.p.acx[item.data] = (player.p.acx[item.data] || 0) + 1; + socket.emit("game_response", { response: "cx_new", acx: player.p.acx, name: item.data, from: "cxjar" }); + consume_one(player, data.num); + } else if (item.name == "emotionjar") { + if (!item.data || item.l) { + return fail_response("item_locked"); + } + player.p.emx[item.data] = (player.p.emx[item.data] || 0) + 1; + socket.emit("game_response", { + response: "emotion_new", + emx: player.p.emx, + name: item.data, + from: "emotionjar", + }); + consume_one(player, data.num); + } else if (def.type == "licence") { + if (item.l) { + return fail_response("item_locked"); + } + player.s.licenced = { ms: ((player.s.licenced && player.s.licenced.ms) || 0) + 7 * 60 * 1000 }; + consume_one(player, data.num); + } else if (def.type == "spawner") { + new_monster(player.in, { type: def.spawn, stype: "trap", x: player.x, y: player.y, owner: player.name }); + consume_one(player, data.num); + } else if (def.gives) { + if (player.last.potion && mssince(player.last.potion) < 0) { + return fail_response("not_ready"); + } + if (item.l) { + return fail_response("item_locked"); + } + var timeout = 2000; + var timeout_ui = null; + var xp = false; + consume_one(player, data.num); + (def.gives || []).forEach(function (p) { + var amount = p[1]; + if (player.s.poisoned) { + amount = round(amount / 2); + } + player[p[0]] = (player[p[0]] || 0) + amount; + player[p[0]] = max(1, player[p[0]]); + if (p[0] == "hp") { + if (amount > 0) { + disappearing_text(socket, player, "+" + amount, { color: "hp", xy: 1, s: "hp" }); + } else { + disappearing_text(socket, player, amount, { color: "hp", xy: 1, s: "hp" }); + } + } + if (p[0] == "mp") { + disappearing_text(socket, player, "+" + amount, { color: "mp", xy: 1, s: "mp" }); + } + if (p[0] == "xp") { + disappearing_text(socket, player, "+1M", { color: "1mxp", xy: 1, s: "xp", size: "large" }); + xp = true; + timeout_ui = 120; + timeout = 0.1; + } + }); + if (def.debuff) { + for (var c in player.s) { + if (G.conditions[c] && G.conditions[c].debuff && (!G.conditions[c].persistent || c == "hopsickness")) { + delete player.s[c]; + } + } + } + player.hp = min(player.hp, player.max_hp); + player.mp = min(player.mp, player.max_mp); + if (def.cooldown) { + timeout = def.cooldown; + } + player.last.potion = future_ms(timeout); + if (!xp) { + to_update = "u+cid+reopen+nc"; + } + socket.emit("eval", { code: "pot_timeout(" + (timeout_ui || timeout) + ")" }); + } else if (!data.consume) { + // console.log(data); + var slot = data.slot || def.type; + var existing; + var comp = can_equip_item(player, def, slot); + if (comp == "no") { + resend(player, "u+cid+reopen"); // if you drop something on an invalid slot, it stays there for a bit otherwise [29/08/22] + return fail_response("cant_equip"); + } + slot = comp; + existing = player.slots[slot]; + player.slots[slot] = player.items[data.num]; + player.cslots[slot] = cache_item(player.slots[slot]); + if (existing && existing.b) { + existing = null; + } + player.items[data.num] = existing; + player.citems[data.num] = cache_item(existing); + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 120, 120000) }; + resolve.slot = slot; + } else { + return fail_response("cant_consume"); + } + + if (to_update) { + resend(player, to_update); + } + success_response(resolve_type, resolve); + }); + socket.on("misc_npc", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var npc = players[data.npc]; + if (!npc || !npc.npc) { + return; + } + if (simple_distance(player, npc) > 1000) { + return socket.emit("game_response", "distance"); + } + if (data.npc == NPC_prefix + "Marven" && npc.misc == true) { + server_log("Here!"); + } + }); + socket.on("unequip", function (data) { + var player = players[socket.id]; + if (!player || !data.slot || !player.slots[data.slot]) { + return fail_response("invalid"); + } + if (data.slot == "elixir") { + return fail_response("cant"); + } + var item = player.slots[data.slot]; + var done = false; + if (in_arr(data.slot, trade_slots) && item.giveaway !== undefined) { + return fail_response("giveaway"); + } + // an oversight allowed .giveaway items to be equipped, so now they can be unequipped from regular slots [04/11/21] + if (player.esize <= 0 && !item.b) { + return fail_response("no_space"); + } + player.slots[data.slot] = null; + player.cslots[data.slot] = null; + player.c = {}; + if (!item.b) { + add_item(player, item, { announce: false }); + } + resend(player, "reopen+u+cid"); + success_response("data"); + }); + socket.on("secondhands", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (simple_distance(G.maps.main.ref.secondhands, player) > 500) { + return socket.emit("game_response", "distance"); + } + socket.emit("secondhands", csold); + }); + socket.on("lostandfound", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data == "info") { + return socket.emit("game_response", { response: "lostandfound_info", gold: S.gold }); + } + if (!player.donation) { + return socket.emit("game_response", "lostandfound_donate"); + } + if (simple_distance(G.maps.woffice.ref.lostandfound, player) > 500) { + return socket.emit("game_response", "distance"); + } + socket.emit("lostandfound", cfound); + }); + socket.on("split", function (data) { + var player = players[socket.id]; + var num = data.num; + var item = player.items[data.num]; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (item && item.q) || 1); + if (!item) { + return fail_response("no_item"); + } + if (!G.items[item.name].s) { + return fail_response("invalid"); + } + quantity = min(quantity, G.items[item.name].s || 1); + if (!player.esize) { + return fail_response("cant_space"); + } + if (item.q == quantity) { + return success_response(); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + + consume(player, data.num, quantity); + + var new_item = cache_item(item); + if (item.q) { + new_item.q = quantity; + } + + for (var i = 0; i < player.isize; i++) { + if (!player.items[i]) { + player.items[i] = new_item; + player.citems[i] = cache_item(new_item); + break; + } + } + resend(player, "reopen"); + success_response(); + }); + socket.on("sell", function (data) { + var player = players[socket.id]; + var num = data.num; + var item = player.items[data.num]; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (item && item.q) || 1); + var can_reach = false; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!item) { + return fail_response("no_item"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + (G.maps[player.map].merchants || []).forEach(function (m) { + if (simple_distance(player, m) < B.sell_dist) { + can_reach = m; + } + }); + if (player.computer && !can_reach) { + can_reach = G.maps.main.merchants[0]; + } + if (!can_reach) { + return fail_response("distance"); + } + can_reach.name = player.name; + var value = calculate_item_value(item); + consume(player, data.num, quantity); + player.gold += value * quantity; + var new_item = cache_item(item); + if (item.q) { + new_item.q = quantity; + } + xy_emit(can_reach, "ui", { + type: "-$", + id: can_reach.id, + name: player.name, + item: new_item, + num: num, + event: "sell", + }); + resend(player, "reopen+nc+inv"); + success_response("gold_received", { gold: value * quantity, item: new_item, cevent: "sell" }); + secondhands_logic(item, quantity); // In the end, so if this fails, the "sell" still succeeds - otherwise it could cause a infinite gold loophole [10/07/18] + }); + socket.on("buy_shells", function (data) { + return socket.emit("game_log", "No longer possible"); + var player = players[socket.id]; + if (!player || player.user || gameplay == "hardcore" || gameplay == "test") { + return game_response("cant_in_bank"); + } + var gold = parseInt(data.gold) || 0; + if (gold < 1000000 || gold > player.gold) { + return socket.emit("game_response", "not_enough_gold"); + } + var shells = Math.floor(gold / G.multipliers.shells_to_gold); + player.gold -= gold; + socket.emit("game_log", "Gave " + to_pretty_num(gold) + " gold"); + appengine_call( + "bill_user", + { auth: player.auth, amount: -shells, reason: "buy_shells", name: player.name }, + function (result) { + server_log("buy_with_cash: " + JSON.stringify(result)); + if (result.failed || !result.done) { + socket.emit("game_log", "Purchase failed"); + return; + } + player.cash = result.cash; + socket.emit("game_log", "Received " + to_pretty_num(shells) + " shells"); + + resend(player, "reopen+nc"); + }, + ); + resend(player, "reopen+nc"); + }); + socket.on("buy_with_cash", function (data) { + var player = players[socket.id]; + var name = data.name; + var def = G.items[data.name]; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (def && def.s) || 9999); + if (!player || player.user || gameplay == "hardcore" || gameplay == "test") { + return fail_response("cant_in_bank"); + } + var cost = def.cash * quantity; + if (!def.cash || def.ignore) { + return fail_response("invalid"); + } + if (def.p2w) { + return fail_response("invalid"); + } + if (!def.s) { + quantity = 1; + } + if (!can_add_item(player, create_new_item(name, quantity))) { + return fail_response("no_space"); + } + appengine_call( + "bill_user", + { auth: player.auth, amount: cost, reason: data.name, name: player.name }, + function (result) { + server_log("buy_with_cash: " + JSON.stringify(result)); + if (result.failed || !result.done) { + socket.emit("game_log", "Purchase failed"); + return; + } + player.cash = result.cash; + var new_item = create_new_item(name, quantity); + var done = false; + add_item(player, new_item, { announce: false }); + socket.emit("game_log", "Spent " + to_pretty_num(cost) + " shells"); + + resend(player, "reopen+nc+inv"); + }, + ); + success_response({ success: false, in_progress: true }); + }); + socket.on("sbuy", function (data) { + var player = players[socket.id]; + var done = false; + var npc = G.maps.main.ref.secondhands; + var c = S.sold; + var cc = csold; + var e = "+$p"; + var ev = "secondhands"; + var mult = 2; + var src = "scnd"; + if (data.f) { + npc = G.maps.woffice.ref.lostandfound; + c = S.found; + cc = cfound; + e = "+$f"; + ev = "lostandfound"; + mult = 4; + src = "lost"; + } + if (!player || player.user) { + return game_response("cant_in_bank"); + } + if (player.s.hopsickness && ev == "lostandfound") { + return fail_response("cant_when_sick", { goblin: true }); + } + if (simple_distance(npc, player) > 500) { + return socket.emit("game_response", "distance"); + } + for (var i = 0; i < c.length; i++) { + if (c[i].rid == data.rid) { + if (mult == 2 && G.items[c[i].name].cash) { + mult = 3; + } + var gold = calculate_item_value(c[i]) * mult * (c[i].q || 1); + var item = c[i]; + if (!can_add_item(player, c[i])) { + return disappearing_text(socket, player, "NO SPACE"); + } + if (gold > player.gold) { + return socket.emit("game_response", "buy_cost"); + } + player.gold -= gold; + item.src = src; + add_item(player, c[i], { announce: false }); + c.splice(i, 1); + cc.splice(i, 1); + socket.emit("game_log", "Spent " + to_pretty_num(gold) + " gold"); + resend(player, "reopen+nc+inv"); + socket.emit(ev, cc); + done = true; + xy_emit(npc, "ui", { type: e, id: npc.id, name: player.name, item: cache_item(item, 1) }); + break; + } + } + if (!done) { + return socket.emit("game_log", "Item gone"); + } + }); + socket.on("buy", function (data) { + var player = players[socket.id]; + var can_reach = false; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + var name = data.name; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (G.items[name] && G.items[name].s) || 9999); + var cost = 0; + var added = false; + var done = false; + if (!can_buy[name] && gameplay != "test") { + return fail_response("buy_cant_npc"); + } + if (!can_add_item(player, create_new_item(name, quantity))) { + return fail_response("buy_cant_space"); + } + (G.maps[player.map].items[name] || []).forEach(function (l) { + if (simple_distance(player, l) < B.sell_dist) { + can_reach = l; + } + }); + if (player.computer && !can_reach) { + can_reach = G.maps.main.merchants[0]; + } + if (!can_reach) { + return fail_response("distance"); + } + can_reach.name = player.name; + var def = G.items[name]; + if (!def.s) { + quantity = 1; + } + cost = quantity * G.items[name].g; + if (G.items[name].p2w) { + cost *= G.inflation; + } + if (player.gold < cost) { + return fail_response("buy_cost"); + } + var new_item = create_new_item(name, quantity); + player.gold -= cost; + var num = add_item(player, new_item, { announce: false }); + xy_emit(can_reach, "ui", { + type: "+$", + id: can_reach.id, + name: player.name, + item: cache_item(new_item), + event: "buy", + }); + + resend(player, "reopen+nc+inv"); + success_response("buy_success", { cost: cost, num: num, name: name, q: quantity, cevent: "buy" }); + }); + socket.on("send", function (data) { + var player = players[socket.id]; + var receiver = players[name_to_id[data.name || ""]]; + var num; + var s_item; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!receiver || receiver.user) { + return fail_response("receiver_unavailable"); + } + if (distance(receiver, player, true) > B.dist || receiver.map != player.map) { + return fail_response("distance"); + } + if (data.num !== undefined) { + data.num = max(0, parseInt(data.num) || 0); + var item = player.items[data.num]; + if (!item) { + return fail_response("send_no_item"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l || (item.acl && receiver.owner != player.owner)) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + data.q = min(item.q || 1, max(1, parseInt(data.q || 1) || 1)); + if (!data.q) { + return fail_response("no_item"); + } + if (!can_add_item(receiver, create_new_item(item.name, data.q))) { + return fail_response("send_no_space"); + } + if ((item.q || 1) == data.q) { + player.items[data.num] = player.citems[data.num] = null; + player.esize++; + } else { + player.items[data.num].q -= data.q; + player.citems[data.num] = cache_item(player.items[data.num]); + } + + if (item.q) { + s_item = create_new_item(item.name, data.q); + if (item.v) { + s_item.v = item.v; + } + if (item.data) { + s_item.data = item.data; + } + num = add_item(receiver, s_item, { announce: false }); + } else { + s_item = item; + num = add_item(receiver, item, { announce: false }); + } + + if (receiver.owner != player.owner) { + item.src = "snd"; + } + + add_to_history(player, { name: "item", to: receiver.name, item: item.name, q: data.q, level: item.level }); + add_to_history(receiver, { name: "item", from: player.name, item: item.name, q: data.q, level: item.level }); + + xy_emit(player, "ui", { + type: "item_sent", + receiver: receiver.name, + sender: player.name, + item: cache_item(s_item, null, { q: data.q }), + num: num, + fnum: data.num, + event: true, + }); + + resend(player, "reopen+nc+inv"); + resend(receiver, "reopen+nc+inv"); + receiver.socket.emit("game_response", { + response: "item_received", + name: player.name, + item: item.name, + q: data.q, + num: num, + cevent: true, + }); + player.socket.emit("game_response", { + response: "item_sent", + name: receiver.name, + item: item.name, + q: data.q, + num: data.num, + cevent: true, + place: "send", + }); + } else if (data.gold !== undefined) { + data.gold = min(player.gold, max(1, parseInt(data.gold || 1) || 1)) || player.gold; + if (!data.gold) { + return fail_response("invalid"); + } + player.gold -= data.gold; + if (!is_same(player, receiver) && data.gold != 1) { + data.gold = parseInt(data.gold * 0.975); + } + receiver.gold += data.gold; + + add_to_history(player, { name: "gold", to: receiver.name, amount: data.gold }); + add_to_history(receiver, { name: "gold", from: player.name, amount: data.gold }); + + xy_emit(player, "ui", { + type: "gold_sent", + receiver: receiver.name, + sender: player.name, + gold: data.gold, + event: true, + }); + + resend(player, "reopen+nc+inv"); + resend(receiver, "reopen+nc+inv"); + receiver.socket.emit("game_response", { + response: "gold_received", + name: player.name, + gold: data.gold, + cevent: true, + }); + player.socket.emit("game_response", { + response: "gold_sent", + name: receiver.name, + gold: data.gold, + cevent: true, + place: "send", + }); + } else if (data.cx !== undefined) { + var cxl = map_cx(player); + var count = all_cx(player, 1); + if (!(cxl[data.cx] && player.p.acx[cxl[data.cx]] && count[data.cx] > 0)) { + return fail_response("send_no_cx"); + } + // count[cxl[data.cx]] before [28/01/22] + if (receiver.owner != player.owner) { + return fail_response("send_diff_owner"); + } + player.p.acx[cxl[data.cx]] -= 1; + if (!player.p.acx[cxl[data.cx]]) { + delete player.p.acx[cxl[data.cx]]; + } + receiver.p.acx[cxl[data.cx]] = (receiver.p.acx[cxl[data.cx]] || 0) + 1; + + xy_emit(player, "ui", { + type: "cx_sent", + receiver: receiver.name, + sender: player.name, + cx: data.cx, + event: true, + }); + + receiver.socket.emit("game_response", { + response: "cx_received", + name: player.name, + cx: data.cx, + acx: receiver.p.acx, + cevent: true, + }); + player.socket.emit("game_response", { + response: "cx_sent", + name: receiver.name, + cx: data.cx, + acx: player.p.acx, + cevent: true, + place: "send", + }); + } + }); + socket.on("donate", function (data) { + var player = players[socket.id]; + var XPX = 3.2; + if (!player || player.user) { + return game_response("cant_in_bank"); + } + var gold = max(1, min(parseInt(data.gold) || 0, 1000000000)); + if (gold > player.gold) { + return socket.emit("game_response", "gold_not_enough"); + } + if (gold >= 1000000) { + response = "thx"; + player.donation = true; + } else if (gold < 100000) { + response = "low"; + } else { + add_item(player, "gum"); + response = "gum"; + } + if (S.gold < 500000000) { + XPX = 4.8; + } else if (S.gold <= 1000000000) { + XPX = 4; + } + player.gold -= gold; + S.gold += gold; + if (gold >= 5000000) { + lstack(S.logs.donate, { name: player.name, gold: gold, xp: XPX }); + } + if (player.type == "merchant") { + player.xp += parseInt(gold * XPX); + } + resend(player, "reopen"); + socket.emit("game_response", { response: "donate_" + response, gold: gold, xprate: XPX }); + }); + socket.on("destroy", function (data) { + var player = players[socket.id]; + var add = "+nc+inv"; + data.num = max(0, parseInt(data.num) || 0); + if (!player.items[data.num]) { + return fail_response("no_item"); + } + var item = player.items[data.num]; + var name = player.items[data.num].name; + data.q = min(max(parseInt(data.q) || 0, 1), (item && item.q) || 1); + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + if (item.level != 13) { + consume(player, data.num, data.q); + //player.items[data.num]=player.citems[data.num]=null; + } + if (data.statue) { + if (item.name == "shadowstone") { + add = "+u+cid"; + player.s.invis = { ms: 99999 }; + } + if (G.items[item.name].upgrade && Math.random() < 1.0 / ((gameplay == "hardcore" && 10000) || 1000000)) { + add = "+u+cid"; + item.level = 13; + player.items[data.num] = item; + player.citems[data.num] = cache_item(player.items[data.num]); + } + xy_emit(G.maps.spookytown.ref.poof, "upgrade", { type: "poof", success: 1 }); + } + resend(player, "reopen" + add); + success_response("destroyed", { name: name, place: "destroy", cevent: "destroy" }); + }); + socket.on("join_giveaway", function (data) { + var player = players[socket.id]; + var seller = players[id_to_id[data.id]]; + var num; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!in_arr(data.slot, trade_slots)) { + return fail_response("invalid"); + } + if (!seller || seller.npc || is_invis(seller)) { + return fail_response("seller_gone"); + } + if (seller.user) { + return fail_response("cant_in_bank"); + } + if (distance(seller, player, true) > B.dist || seller.map != player.map) { + return fail_response("distance"); + } + if (seller.id == player.id) { + return fail_response("hmm"); + } + var item = seller.slots[data.slot]; + if (!item || (data.rid && item.rid != data.rid)) { + return fail_response("item_gone"); + } + if (!item.giveaway) { + return fail_response("sneaky"); + } + if (!player.auth_id) { + return fail_response("need_auth"); + } + //if(item.list.includes(player.name)) return socket.emit("game_log","Already joined!"); + //if(player.type!="merchant") return socket.emit("game_log","Only merchants can join giveaways!"); + + //item.list.push(player.name); + + item.registry = item.registry || {}; + item.registry[player.auth_id] = player.name; + item.list = Object.values(item.registry); + + socket.emit("game_log", "Joined the giveaway!"); + seller.socket.emit("game_response", { + response: "giveaway_join", + name: player.name, + slot: data.slot, + cevent: true, + }); + + resend(player, "reopen"); + resend(seller, "reopen+u+cid"); + success_response({}); + }); + socket.on("trade_wishlist", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + data.q = min(9999, max(1, parseInt(data.q || 1) || 1)); + if (!in_arr(data.slot, trade_slots) || !G.items[data.name] || data.name == "placeholder") { + return fail_response("invalid"); + } + if (player.slots[data.slot] && !player.slots[data.slot].b) { + return fail_response("slot_occuppied"); + } + if (!get_trade_slots(player).includes(data.slot)) { + return fail_response("invalid"); + } + var item = { + name: data.name, + rid: randomStr(4), + price: round(min(99999999999, max(parseInt(data.price) || 1, 1))), + b: true, + }; + if (G.items[data.name].upgrade || G.items[data.name].compound) { + item.q = min(99, data.q); + item.level = round(min(12, max(parseInt(data.level) || 0, 0))); + } else { + item.q = data.q; + } + player.slots[data.slot] = item; + player.cslots[data.slot] = cache_item(player.slots[data.slot], true); + resend(player, "reopen+u+cid+nc"); + success_response({}); + }); + socket.on("trade_sell", function (data) { + // if(!is_sdk) return; + var player = players[socket.id]; + var buyer = players[id_to_id[data.id]]; + var num; + var actual = null; + var num = null; + data.q = max(1, parseInt(data.q || 1) || 1); + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!in_arr(data.slot, trade_slots)) { + return fail_response("invalid"); + } + if (!buyer || buyer.npc || is_invis(buyer)) { + return fail_response("buyer_gone"); + } + if (player.user) { + return fail_response("cant_in_bank"); + } + if (distance(buyer, player, true) > B.dist) { + return fail_response("distance"); + } + if (buyer.id == player.id) { + return fail_response("hmm"); + } + var item = buyer.slots[data.slot]; + if (!item || (data.rid && item.rid != data.rid)) { + return fail_response("item_gone"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (!item.b && !B.rbugs) { + return fail_response("sneaky"); + } + if ((item.q || 1) < data.q) { + return fail_response("dont_have_enough"); + } + if (item.price * data.q > buyer.gold) { + return fail_response("buyer_gold"); + } + for (var i = 0; i < player.isize; i++) { + if ( + player.items[i] && + player.items[i].name == item.name && + (player.items[i].q || 1) >= data.q && + player.items[i].level == item.level && + !player.items[i].l + ) { + actual = player.items[i]; + num = i; + break; + } + } + if (!actual) { + return fail_response("no_item"); + } + if (actual.b) { + return fail_response("item_blocked"); + } + if (!can_add_item(buyer, create_new_item(item.name, data.q))) { + return fail_response("trade_bspace"); + } + var price = item.price * data.q; + player.gold += round(price * (1 - player.tax)); + add_to_trade_history(player, "sell", buyer.name, cache_item(actual, true, { q: data.q }), price); + buyer.gold -= price; + add_to_trade_history(buyer, "buy", player.name, cache_item(actual, true, { q: data.q }), price); + S.gold += price - round(price * (1 - player.tax)); + + if ((item.q || 1) == data.q) { + buyer.slots[data.slot] = buyer.cslots[data.slot] = null; + } else { + buyer.slots[data.slot].q -= data.q; + buyer.cslots[data.slot] = cache_item(buyer.slots[data.slot], true); + } + + // up to here + + if ((player.items[i].q || 1) == data.q) { + player.items[num] = player.citems[num] = null; + } else { + player.items[num].q -= data.q; + player.citems[num] = cache_item(player.items[num]); + } + + if (G.items[item.name].s) { + bnum = add_item(buyer, create_new_item(item.name, data.q), { announce: false }); + } else { + bnum = add_item(buyer, actual, { announce: false }); + } + + if (player.type == "merchant") { + merchant_xp_logic(player, buyer, price, price - round(price * (1 - player.tax))); + } + if (buyer.type == "merchant") { + merchant_xp_logic(buyer, player, price, price - round(price * (1 - buyer.tax))); + } + + socket.emit( + "game_log", + "Sales tax " + to_pretty_num(price - round(price * (1 - player.tax))) + " gold [" + player.tax * 100 + "%]", + ); + socket.emit("game_log", "Received " + to_pretty_num(round(price * (1 - player.tax))) + " gold"); + buyer.socket.emit("game_log", "Spent " + to_pretty_num(price) + " gold"); + + xy_emit(buyer, "ui", { + type: "+$$", + seller: player.name, + buyer: buyer.name, + item: cache_item(actual, true, { q: data.q, price: item.price }), + slot: data.slot, + num: bnum, + snum: num, + }); + + resend(player, "reopen"); + resend(buyer, "reopen+u+cid"); + success_response({}); + }); + socket.on("trade_buy", function (data) { + var player = players[socket.id]; + var seller = players[id_to_id[data.id]]; + var num; + data.q = max(1, parseInt(data.q || 1) || 1); + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!in_arr(data.slot, trade_slots)) { + return fail_response("invalid"); + } + if (!seller || seller.npc || is_invis(seller)) { + return fail_response("seller_gone"); + } + if (seller.user) { + return fail_response("cant_in_bank"); + } + if (distance(seller, player, true) > B.dist || seller.map != player.map) { + return fail_response("distance"); + } + if (seller.id == player.id) { + return fail_response("hmm"); + } + var item = seller.slots[data.slot]; + if (!item || (data.rid && item.rid != data.rid)) { + return fail_response("item_gone"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.b || item.giveaway) { + return fail_response("sneaky"); + } + if (item.price * data.q > player.gold) { + return fail_response("gold_not_enough"); + } + if ((item.q || 1) < data.q) { + return fail_response("insufficient_q"); + } + if (!can_add_item(player, create_new_item(item.name, data.q))) { + return fail_response("no_space"); + } + var price = item.price * data.q; + player.gold -= price; + add_to_trade_history(player, "buy", seller.name, cache_item(seller.slots[data.slot], true, { q: data.q }), price); + seller.gold += round(price * (1 - seller.tax)); + add_to_trade_history( + seller, + "sell", + player.name, + cache_item(seller.slots[data.slot], true, { q: data.q }), + price, + ); + S.gold += price - round(price * (1 - seller.tax)); + + if ((item.q || 1) == data.q) { + seller.slots[data.slot] = seller.cslots[data.slot] = null; + } else { + seller.slots[data.slot].q -= data.q; + seller.cslots[data.slot] = cache_item(seller.slots[data.slot], true); + } + + if (item.q) { + num = add_item(player, create_new_item(item.name, data.q), { announce: false }); + } else { + num = add_item(player, item, { announce: false }); + } + + if (seller.owner != player.owner) { + item.src = "tb"; + } + + if (player.type == "merchant") { + merchant_xp_logic(player, seller, price, price - round(price * (1 - player.tax))); + } + if (seller.type == "merchant") { + merchant_xp_logic(seller, player, price, price - round(price * (1 - seller.tax))); + } + + socket.emit("game_log", "Spent " + to_pretty_num(price) + " gold"); + seller.socket.emit( + "game_log", + "Sales tax " + to_pretty_num(price - round(price * (1 - seller.tax))) + " gold [" + seller.tax * 100 + "%]", + ); + seller.socket.emit("game_log", "Received " + to_pretty_num(round(price * (1 - seller.tax))) + " gold"); + + xy_emit(seller, "ui", { + type: "+$$", + seller: seller.name, + buyer: player.name, + item: cache_item(item, true, { q: data.q }), + slot: data.slot, + num: data.num, + }); + + resend(player, "reopen"); + resend(seller, "reopen+u+cid"); + success_response({}); + }); + socket.on("trade_history", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + server_log("trade_history: " + player.name); + socket.emit("trade_history", player.p.trade_history || []); + }); + socket.on("merchant", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var initial = player.p.stand; + server_log("merchant: " + player.name); + if (data.close || player.p.stand) { + player.p.stand = false; + for (var i = 0; i < player.items.length; i++) { + if (player.items[i] && player.items[i].b == "stand") { + delete player.items[i].b; + } + } + } + if (data.num !== undefined) { + var item = player.items[data.num]; + if (item && G.items[item.name].stand) { + player.p.stand = G.items[item.name].stand; + item.b = "stand"; + } else { + return fail_response("invalid"); + } + } + if (initial != player.p.stand) { + // All unneccessary causes of resend's should be patched [03/08/18] + reslot_player(player); + resend(player, "u+cid"); + } + success_response({}); + }); + socket.on("imove", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + data.a = max(0, parseInt(data.a) || 0); + data.b = max(0, parseInt(data.b) || 0); + if (data.a == data.b) { + return fail_response("invalid"); + } + var a = min(data.a, data.b); + var b = max(data.a, data.b); + var a_item; + if (!(b < player.isize || b < player.items.length)) { + return fail_response("invalid"); + } + // while(b>=player.items.length) player.items.push(null); [22/11/16] + if (player.items[a] && player.items[a].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (player.items[b] && player.items[b].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (can_stack(player.items[a], player.items[b])) { + player.items[data.a].q = (player.items[a].q || 1) + (player.items[b].q || 1); + player.items[data.b] = null; + player.esize++; + } else { + a_item = player.items[a]; + player.items[a] = player.items[b]; + player.items[b] = a_item; + } + player.citems[data.a] = cache_item(player.items[data.a]); + player.citems[data.b] = cache_item(player.items[data.b]); + resend(player, "reopen+nc+inv"); + return success_response("data"); + }); + socket.on("bank", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!player.user || player.mounting || player.unmounting) { + return fail_response("bank_unavailable"); + } + var success = {}; + if (data.operation == "withdraw") { + var amount = max(0, min(parseInt(data.amount) || 0, player.user.gold)); + player.user.gold -= amount; + player.gold += amount; + success = { response: "bank_withdraw", gold: amount, cevent: true }; + } + if (data.operation == "deposit") { + var amount = max(0, min(parseInt(data.amount) || 0, player.gold)); + player.user.gold += amount; + player.gold -= amount; + success = { response: "bank_store", gold: amount, cevent: true }; + } + if (data.operation == "unlock") { + if (!bank_packs[data.pack]) { + return fail_response("invalid"); + } + var gold = bank_packs[data.pack][1]; + var shells = bank_packs[data.pack][2]; + if (!gold) { + return fail_response("gold_not_enough"); + } + if (player.user[data.pack]) { + return fail_response("invalid"); + } + if (data.gold) { + if (player.gold < gold) { + return fail_response("gold_not_enough"); + } + player.gold -= gold; + player.user[data.pack] = []; + player.cuser[data.pack] = []; + success = { response: "bank_new_pack", pack: data.pack, gold: gold, cevent: true }; + } else if (data.shells) { + if (player["unlocking_" + data.pack]) { + return fail_response("bank_opi"); + } + player["unlocking_" + data.pack] = true; + appengine_call( + "bill_user", + { + auth: player.auth, + amount: shells, + reason: data.pack, + name: player.name, + suffix: "/" + player.name + "/" + data.pack, + override: true, + }, + function (result) { + server_log("buy_with_cash: " + JSON.stringify(result)); + player["unlocking_" + data.pack] = false; + if (result.failed || !result.done) { + return socket.emit("game_log", "Purchase failed"); + } + player.cash = result.cash; + + player.user[data.pack] = []; + player.cuser[data.pack] = []; + game_response("bank_new_pack", { cevent: true, pack: data.pack, shells: shells }); + + resend(player, "reopen"); + }, + ); + success = { success: false, in_progress: true }; + } + } + if (data.operation == "move") { + //within a .itemsN + if (!player.user[data.pack] || bank_packs[data.pack][0] != player.map) { + return fail_response("invalid"); + } + server_log("storage move " + JSON.stringify(data)); + data.a = max(0, min(41, parseInt(data.a) || 0)); + data.b = max(0, min(41, parseInt(data.b) || 0)); + if (data.a == data.b) { + return fail_response("invalid"); + } + if (player.user[data.pack][data.a] && player.user[data.pack][data.a].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (player.user[data.pack][data.b] && player.user[data.pack][data.b].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (can_stack(player.user[data.pack][data.a], player.user[data.pack][data.b])) { + player.user[data.pack][data.b].q = + (player.user[data.pack][data.a].q || 1) + (player.user[data.pack][data.b].q || 1); + player.user[data.pack][data.a] = null; + } else { + var temp = player.user[data.pack][data.a]; + player.user[data.pack][data.a] = player.user[data.pack][data.b]; + player.user[data.pack][data.b] = temp; + } + player.cuser[data.pack][data.a] = cache_item(player.user[data.pack][data.a]); + player.cuser[data.pack][data.b] = cache_item(player.user[data.pack][data.b]); + } + if (data.operation == "swap") { + //between .items and a .itemsN + var operation = "swap"; + if (!player.user[data.pack] || bank_packs[data.pack][0] != player.map) { + return fail_response("invalid"); + } + data.str = parseInt(data.str); + data.inv = parseInt(data.inv); + if (data.inv == -1 || (!data.inv && data.inv !== 0)) { + operation = "pull"; + for (var i = 0; i < player.isize; i++) { + if (!player.items[i]) { + data.inv = i; + break; + } + } + // if(data.inv==-1) { socket.emit("game_log","Inventory is full"); return; } + } + if (data.str == -1 || (!data.str && data.str !== 0)) { + if (operation == "pull") { + return fail_response("invalid"); + } + operation = "store"; + for (var i = 0; i < 42; i++) { + if (!player.user[data.pack][i]) { + data.str = i; + break; + } + } + // if(data.str==-1) { socket.emit("game_log","Storage is full"); return; } + } + server_log("storage swap " + JSON.stringify(data)); + data.str = max(0, min(41, parseInt(data.str) || 0)); + data.inv = max(0, min(player.isize - 1, parseInt(data.inv) || 0)); + var bank_item = player.user[data.pack][data.str]; + var inv_item = player.items[data.inv]; + if (inv_item && inv_item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (inv_item && inv_item.b) { + return fail_response("item_blocked"); + } + if (inv_item) { + delete inv_item.m; + delete inv_item.v; + } + if (operation == "swap") { + player.user[data.pack][data.str] = inv_item; + player.items[data.inv] = bank_item; + player.cuser[data.pack][data.str] = cache_item(player.user[data.pack][data.str]); + player.citems[data.inv] = cache_item(player.items[data.inv]); + } else if (operation == "store" && inv_item) { + if (!can_add_item(player.user[data.pack], inv_item)) { + return fail_response("storage_full"); + } + player.items[data.inv] = player.citems[data.inv] = null; + bank_add_item(player, data.pack, inv_item); + } else if (operation == "pull" && bank_item) { + if (!can_add_item(player, bank_item)) { + return fail_response("inventory_full"); + } + player.user[data.pack][data.str] = player.cuser[data.pack][data.str] = null; + add_item(player, bank_item, { announce: false }); + } + } + if (!player.user.gold && player.user.gold !== 0) { + player.user.gold = 0; + server_log("#X - GOLD BUG bank", 1); + } + resend(player, "reopen"); + success_response(success); + }); + socket.on("throw", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var item = player.items[data.num]; + if (item) { + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + var def = G.items[item.name]; + if (!def.throw) { + return fail_response("invalid"); + } + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + if (distance(player, { map: player.map, in: player.in, x: x, y: y }) > player.str * 3) { + fail_response("too_far"); + } + consume_one(player, data.num); + if (item.name == "confetti") { + player.thrilling = future_s(20); + xy_emit( + { map: player.map, in: player.in, x: x, y: y }, + "eval", + "confetti_shower({in:'" + player.in + "',map:'" + player.map + "',real_x:" + x + ",real_y:" + y + "})", + ); + } + if (item.name == "firecrackers") { + player.thrilling = future_s(200); + xy_emit( + { map: player.map, in: player.in, x: x, y: y }, + "eval", + "firecrackers({in:'" + player.in + "',map:'" + player.map + "',real_x:" + x + ",real_y:" + y + "})", + ); + for (var id in instances[player.in].monsters) { + var monster = instances[player.in].monsters[id]; + if (monster.target && distance({ map: player.map, in: player.in, x: x, y: y }, monster) < 64) { + stop_pursuit(monster, { force: true, cause: "firecrackers" }); + } + } + } + if (item.name == "whiteegg") { + xy_emit({ map: player.map, in: player.in, x: x, y: y }, "eval", "egg_splash(" + x + "," + y + ")"); + for (var id in instances[player.in].monsters) { + var monster = instances[player.in].monsters[id]; + if (!monster.target && distance({ map: player.map, in: player.in, x: x, y: y }, monster) < 32) { + target_player(monster, player); + } + } + } + if (item.name == "smoke") { + xy_emit({ map: player.map, in: player.in, x: x, y: y }, "eval", "assassin_smoke(" + x + "," + y + ");"); + } + resend(player, "reopen+nc+inv"); + success_response({}); + } else { + fail_response("no_item"); + } + }); + socket.on("poke", function (data) { + var player = players[socket.id]; + var level = 1; + if (!player || !player.slots.gloves || player.slots.gloves.name != "poker") { + return; + } + if (player.pokes >= 50) { + return socket.emit("game_log", "You are out of pokes!"); + } + player.pokes = (player.pokes || 0) + 1; + if (player.slots.gloves.level >= 10) { + level = 4; + } else if (player.slots.gloves.level == 9) { + level = 3; + } else if (player.slots.gloves.level == 8) { + level = 2; + } + xy_emit(player, "poke", { name: data.name, level: level, who: player.name }); + }); + socket.on("merge", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var container = player.slots[data.container]; + var pet = player.slots[data.pet]; + if (!container || container.type != "container" || !pet || pet.type != "container") { + return socket.emit("game_response", "merge_mismatch"); + } + if (G.items[container.name].grade < G.items[pet.name].grade || container.data) { + return socket.emit("game_response", "merge_mismatch"); + } + container.data = G.items[pet.name].pet; + player.slots[data.pet] = null; + socket.emit("game_response", "merge_complete"); + resend(player, "reopen"); + }); + socket.on("activate", function (data) { + var player = players[socket.id]; + // console.log(JSON.stringify(data)); + if (!player) { + return; + } + if (data.slot) { + var item = player.slots[data.slot]; + if (!item || in_arr(data.slot, trade_slots)) { + return; + } + if (item.name == "etherealamulet") { + if (player.last_ethereal && mssince(player.last_ethereal) < 120) { + return socket.emit("game_response", "not_ready"); + } + player.last_ethereal = new Date(); + player.s.ethereal = { ms: 5000 }; + socket.emit("eval", { code: "skill_timeout('ethereal',120)" }); + return resend(player, "u+cid"); + } else if (item.name == "angelwings") { + if (player.tskin == "snow_angel") { + player.tskin = ""; + } else if (item.level >= 8 && (player.type == "mage" || player.type == "priest")) { + player.tskin = "snow_angel"; + } else { + return socket.emit("game_response", "nothing"); + } + } else if (item.name == "tristone") { + var on = true; + if (player.tskin || player.tactivations == 100) { + player.tskin = ""; + on = false; + } else if (item.level <= 1 && deduct_gender(player) == "female") { + player.tskin = random_one(["tf_green", "tf_pink"]); + } else if (item.level == 2 && deduct_gender(player) == "female") { + player.tskin = random_one(["tf_blue", "tf_purple"]); + } else if (deduct_gender(player) == "female") { + player.tskin = "tf_orange"; + } else if (item.level <= 1) { + player.tskin = random_one(["tm_gray", "tm_brown", "tm_white"]); + } else if (item.level == 2) { + player.tskin = random_one(["tm_green", "tm_yellow", "tm_purple"]); + } else { + player.tskin = random_one(["tm_blue", "tm_red"]); + } + player.tactivations = (player.tactivations || 0) + 1; + } else if (item.name == "darktristone") { + var on = true; + if (player.tskin || player.tactivations == 100) { + player.tskin = ""; + on = false; + } else if (item.level == 4 && deduct_gender(player) == "female") { + player.tskin = "mf_blue"; + } else if (deduct_gender(player) == "female") { + player.tskin = "mf_yellow"; + } else if (item.level == 4) { + player.tskin = "mm_blue"; + } else { + player.tskin = "mm_yellow"; + } + player.tactivations = (player.tactivations || 0) + 1; + } else { + return; + } + resend(player, "reopen+nc+inv+u+cid"); + } else { + var item = player.items[data.num]; + if (item) { + if (item.name == "frozenstone") { + consume_one(player, data.num); + } + if (item.name == "bkey") { + if (!player.user) { + return socket.emit("game_response", "only_in_bank"); + } + if (player.user.unlocked && player.user.unlocked.bank_b) { + return socket.emit("game_response", "already_unlocked"); + } + consume_one(player, data.num); + player.user.unlocked = player.user.unlocked || {}; + player.user.unlocked.bank_b = new Date(); + player.user.items8 = player.user.items8 || []; + socket.emit("game_response", "door_unlocked"); + } + if (item.name == "ukey") { + if (!player.user) { + return socket.emit("game_response", "only_in_bank"); + } + if (player.user.unlocked && player.user.unlocked.bank_u) { + return socket.emit("game_response", "already_unlocked"); + } + consume_one(player, data.num); + player.user.unlocked = player.user.unlocked || {}; + player.user.unlocked.bank_u = new Date(); + player.user.items24 = player.user.items24 || []; + socket.emit("game_response", "door_unlocked"); + } + if (item.name == "dkey") { + if (!player.user) { + return socket.emit("game_response", "only_in_bank"); + } + var found = false; + for (var i = 0; i < 48; i++) { + var pack = "items" + i; + if (!bank_packs[pack] || player.user[pack]) { + continue; + } + found = true; + player.user[pack] = []; + player.cuser[pack] = []; + break; + } + if (!found) { + return; + } + consume_one(player, data.num); + socket.emit("game_response", "bank_pack_unlocked"); + } + } + resend(player, "reopen+nc+inv"); + } + }); + socket.on("booster", function (data) { + var player = players[socket.id]; + var item = player.items[data.num]; + if (!player) { + return; + } + server_log("booster " + data.num + " " + data.action); + if (!item || !in_arr(item.name, booster_items)) { + return fail_response("invalid"); + } + if (data.action == "activate" && !item.expires) { + item.expires = new Date(); + item.expires.setDate(item.expires.getDate() + 30 + (item.level || 0) * 2); + //item.expires.setMinutes(item.expires.getMinutes()+2); + } else if (data.action == "shift") { + if (!in_arr(data.to, booster_items)) { + return fail_response("invalid"); + } + player.xpm = player.goldm = player.luckm = 1; + item.name = data.to; + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 240, 120000) }; + } + // if(item.expires) item.p="legacy"; + player.citems[data.num] = cache_item(player.items[data.num]); + resend(player, "reopen"); + success_response({ name: player.items[data.num].name }); + }); + socket.on("convert", function (data) { + // There are a lot of routines that could give birth to an endgame bug + // This routine ended up being the one + // "stone" boosters were originally 1200 shells + // when they were discontinued, I let players exchange them for 3600 shells instead + // turns out 'buy_with_cash' didn't check whether an item has "ignore" or not + // so one could buy the stones for 1200 shells and sell them for 3600 + // unlocking infinite gold and shells [17/01/18] + var player = players[socket.id]; + var item = player.items[data.num]; + var amount = 3600; + server_log("stone " + data.num + " " + data.action); + if (!item || !in_arr(item.name, ["stoneofxp", "stoneofgold", "stoneofluck"])) { + return; + } + if (item.expires) { + amount = round(600 - (hsince(item.expires) * 100) / 24.0); + } + add_shells(player, amount, "convert"); + player.items[data.num] = null; + player.citems[data.num] = cache_item(player.items[data.num]); + resend(player, "reopen+cid"); + }); + socket.on("emotion", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!data.name) { + data.name = random_one(Object.keys(player.p.emx)); + } + if (player.last.emotion && mssince(player.last.emotion) < 2000) { + return socket.emit("game_response", "emotion_cooldown"); + } + player.last.emotion = new Date(); + if (!G.emotions[data.name] || !player.p.emx[data.name]) { + return socket.emit("game_response", "emotion_cant"); + } + xy_emit(player, "emotion", { name: data.name, player: player.name }); + }); + socket.on("skill", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var target = null; + var cool = true; + var resolve = { response: "data", place: data.name, success: true }; + var reject = null; + player.first = true; + G.skills.attack.cooldown = player.attack_ms; + server_log("skill " + JSON.stringify(data)); + if (is_disabled(player)) { + return fail_response("disabled", data.name); + } + if ((player.tskin == "konami" || is_silenced(player)) && data.name != "attack") { + return socket.emit("game_response", { response: "skill_cant_incapacitated", place: data.name, failed: true }); + } + if (!G.skills[data.name]) { + return socket.emit("game_response", { response: "no_skill", place: data.name, failed: true }); + } + if ( + G.skills[data.name].mp && + player.mp < (G.skills[data.name].mp * (100 - (player.mp_reduction || 0))) / 100.0 && + !(player.role == "gm" && data.name == "blink") + ) { + return socket.emit("game_response", { response: "no_mp", place: data.name, failed: true }); + } + if (G.skills[data.name].level && player.level < G.skills[data.name].level) { + return socket.emit("game_response", { response: "no_level", place: data.name, failed: true }); + } + if ( + (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) && + player.last[data.name] && + mssince(player.last[data.name]) < (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) + ) { + return socket.emit("game_response", { + response: "cooldown", + skill: data.name, + place: data.name, + id: data.id, + ms: (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) - mssince(player.last[data.name]), + failed: true, + }); + } + if ( + G.skills[data.name].share && + player.last[G.skills[data.name].share] && + mssince(player.last[G.skills[data.name].share]) < + G.skills[G.skills[data.name].share].cooldown * (G.skills[data.name].cooldown_multiplier || 1) + ) { + return socket.emit("game_response", { + response: "cooldown", + skill: data.name, + place: data.name, + failed: true, + id: data.id, + ms: + G.skills[G.skills[data.name].share].cooldown * (G.skills[data.name].cooldown_multiplier || 1) - + mssince(player.last[G.skills[data.name].share]), + }); + } + if (G.skills[data.name].class && !in_arr(player.type, G.skills[data.name].class) && player.role != "gm") { + return socket.emit("game_response", { response: "skill_cant_use", place: data.name, failed: true }); + } + if ( + G.skills[data.name].wtype && + !is_array(G.skills[data.name].wtype) && + (!player.slots.mainhand || G.items[player.slots.mainhand.name].wtype != G.skills[data.name].wtype) && + player.role != "gm" + ) { + return socket.emit("game_response", { response: "skill_cant_wtype", place: data.name, failed: true }); + } + if ( + is_array(G.skills[data.name].wtype) && + (!player.slots.mainhand || !in_arr(G.items[player.slots.mainhand.name].wtype, G.skills[data.name].wtype)) && + player.role != "gm" + ) { + return socket.emit("game_response", { response: "skill_cant_wtype", place: data.name, failed: true }); + } + if (G.skills[data.name].hostile && G.maps[player.map].safe) { + return socket.emit("game_response", { response: "skill_cant_safe", place: data.name, failed: true }); + } + if (G.skills[data.name].target) { + if ( + ("" + parseInt(data.id) === "" + data.id && G.skills[data.name].target == "player") || + ("" + parseInt(data.id) !== "" + data.id && G.skills[data.name].target == "monster") + ) { + return fail_response("invalid_target", data.name, { id: data.id }); + } + if (G.skills[data.name].target != "monster" && players[id_to_id[data.id]]) { + target = players[id_to_id[data.id]]; + if (G.skills[data.name].hostile && target.name == player.name) { + return socket.emit("game_response", { response: "no_target", place: data.name, failed: true }); + } + } else if (G.skills[data.name].target != "player" && instances[player.in].monsters[data.id]) { + target = instances[player.in].monsters[data.id]; + } else { + return socket.emit("disappear", { id: data.id, place: data.name, reason: "not_there" }); + } + + if ( + is_invis(target) || + (!G.skills[data.name].global && (target.map != player.map || distance(target, player, true) > B.max_vision)) + ) { + return socket.emit("disappear", { id: data.id, place: data.name, reason: "not_there" }); + } + } + if (G.skills[data.name].requirements) { + for (var requirement in G.skills[data.name].requirements) { + if (!player[requirement] || player[requirement] < G.skills[data.name].requirements[requirement]) { + return socket.emit("game_response", { + response: "skill_cant_requirements", + place: data.name, + failed: true, + }); + } + } + } + if (G.skills[data.name].slot) { + var found = false; + var charges = false; + G.skills[data.name].slot.forEach(function (p) { + if (player.slots[p[0]] && player.slots[p[0]].name == p[1]) { + if (G.items[player.slots[p[0]].name].charge) { + if ((player.slots[p[0]].charges || 0) < G.items[player.slots[p[0]].name].charge) { + charges = true; + return; + } + player.slots[p[0]].charges -= G.items[player.slots[p[0]].name].charge; + player.cslots[p[0]] = cache_item(player.slots[p[0]]); + } + found = true; + } + }); + if (charges) { + return socket.emit("game_response", { response: "skill_cant_charges", place: data.name, failed: true }); + } + if (!found) { + return socket.emit("game_response", { response: "skill_cant_slot", place: data.name, failed: true }); + } + } + if (player.s.invincible) { + delete player.s.invincible; + player.to_resend = "u+cid"; + } + if (data.name == "attack" || data.name == "heal") { + var attack = commence_attack(player, target, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "invis" && !player.s.invis) { + if (player.s.marked) { + return socket.emit("game_response", { response: "skill_cant_use", place: data.name, failed: true }); + } + player.s.invis = { ms: 999999999999999 }; + xy_emit(player, "disappear", { id: player.id, invis: true, reason: "invis" }); + player.to_resend = " "; + } + if (data.name == "pickpocket") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + + consume_mp(player, G.skills[data.name].mp); + player.c[data.name] = { + ms: + G.skills[data.name].duration_min + + Math.random() * (G.skills[data.name].duration_max - G.skills[data.name].duration_min), + target: target.name, + }; + player.to_resend = "u+cid"; + resolve = { response: "data", place: data.name, success: false, in_progress: true }; + } + if (data.name == "fishing" || data.name == "mining") { + var direction = 0; + var the_zone = null; + consume_mp(player, G.skills[data.name].mp); + (G.maps[player.map].zones || []).forEach(function (zone) { + if (zone.type != data.name || the_zone) { + return; + } + [ + [0, -24, 3], + [-24, 0, 1], + [24, 0, 2], + [0, 24, 0], + ].forEach(function (m) { + if (is_point_inside([player.x + m[0], player.y + m[1]], zone.polygon)) { + the_zone = zone; + direction = m[2]; + } + }); + }); + if (!the_zone || player.moving) { + xy_emit(player, "ui", { type: data.name + "_fail", name: player.name }); + reject = { response: "data", place: data.name }; + } else { + xy_emit(player, "ui", { type: data.name + "_start", name: player.name, direction: direction }); + player.c[data.name] = { + ms: + G.skills[data.name].duration_min + + Math.random() * (G.skills[data.name].duration_max - G.skills[data.name].duration_min), + drop: the_zone.drop, + }; + } + resolve = { response: "data", place: data.name, success: false, in_progress: true }; + player.to_resend = "u+cid"; + } + if (data.name == "light") { + consume_mp(player, G.skills[data.name].mp); + xy_emit(player, "light", { name: player.name }); + player.to_resend = "u+cid"; + } + if (data.name == "charge") { + player.s.charging = { ms: G.skills.charge.duration }; + player.to_resend = "u+cid"; + } + if (data.name == "dash") { + //console.log(data); + consume_mp(player, G.skills[data.name].mp); + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + var point = true; + if (point_distance(player.x, player.y, x, y) > 50) { + point = false; + player.socket.emit("game_response", "dash_failed"); + reject = { response: "data", place: data.name }; + } + if (point) { + var spot = safe_xy_nearby(player.map, x, y); + if (!spot) { + spot = safe_xy_nearby(player.map, player.x + (x - player.x) * 0.8, player.y + (y - player.y) * 0.8); + } + if (!spot) { + spot = safe_xy_nearby(player.map, player.x + (x - player.x) * 0.6, player.y + (y - player.y) * 0.6); + } + if (!spot || point_distance(player.x, player.y, spot.x, spot.y) < 10) { + point = false; + player.socket.emit("game_response", "dash_failed"); + reject = { response: "data", place: data.name }; + } + if (point) { + //console.log([player.x,player.y,spot.x,spot.y]); + player.s.dash = { ms: 1000 }; + player.speed = 500; + player.going_x = spot.x; + player.going_y = spot.y; + start_moving_element(player); + player.to_resend = "u+cid"; + player.socket.emit("eval", { code: "ui_move(" + spot.x + "," + spot.y + ")" }); + } + } + } + if (data.name == "hardshell" || data.name == "power" || data.name == "xpower") { + consume_mp(player, G.skills[data.name].mp); + player.s[G.skills[data.name].condition] = { + ms: G.skills[data.name].duration || G.conditions[G.skills[data.name].condition].duration, + }; + player.to_resend = "u+cid"; + } + if (data.name == "mshield") { + consume_mp(player, G.skills[data.name].mp); + if (player.s[G.skills[data.name].condition]) { + delete player.s[G.skills[data.name].condition]; + } else { + player.s[G.skills[data.name].condition] = { ms: 999999999 }; + } + player.to_resend = "u+cid"; + } + if ( + data.name == "mcourage" || + data.name == "mfrenzy" || + data.name == "massproduction" || + data.name == "massproductionpp" + ) { + consume_mp(player, G.skills[data.name].mp); + player.s[G.skills[data.name].condition] = { ms: G.conditions[G.skills[data.name].condition].duration }; + xy_emit(player, "ui", { type: data.name, name: player.name }); + player.to_resend = "u+cid"; + } + if (data.name == "throw") { + if (distance(player, target, true) > G.skills[data.name].range + player.level) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (target.s.invincible || target.immune) { + return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); + } + if (!player.items[data.num]) { + return socket.emit("game_response", { response: "skill_no_item", place: data.name, failed: true }); + } + var item = player.items[data.num]; + var r = "u+cid"; + var damage = 0; + if (item.name == "placeholder") { + return socket.emit("game_response", { response: "item_placeholder", place: data.name, failed: true }); + } + if (item.l) { + return socket.emit("game_response", { response: "item_locked", place: data.name, failed: true }); + } + if (item.b) { + return socket.emit("game_response", { response: "item_blocked", place: data.name, failed: true }); + } + var prop = calculate_item_properties(item); + var negative = false; + if (in_arr(item.name, G.skills.throw.negative)) { + negative = true; + } + G.skills.throw.nprop.forEach(function (p) { + if (prop[p]) { + negative = true; + } + }); + if (negative && target.is_player && !is_in_pvp(player)) { + return socket.emit("game_response", { response: "not_in_pvp", place: data.name, failed: true }); + } + consume_one(player, data.num); + if (item.name == "essenceoffire") { + add_condition(target, "eburn", { from: player }); + } else if (item.name == "essenceoflife") { + add_condition(target, "eheal", { from: player }); + } else if (prop.attack) { + damage = round(Math.random() * prop.attack * 15); + } else if (prop.armor) { + damage = round(Math.random() * prop.armor * 24); + } + if (damage) { + if (target["1hp"]) { + damage = 1; + } + target.hp = max(1, target.hp - damage); + disappearing_text({}, target, "-" + damage, { color: "red", xy: 1 }); + } + consume_mp(player, G.skills[data.name].mp, target); + xy_emit(player, "ui", { type: "throw", from: player.name, to: target.id, item: item.name }); + player.to_resend = "u+cid+reopen"; + if (target.is_player) { + resend(target, r); + } else { + target.u = true; + target.cid++; + } + } + if (data.name == "phaseout") { + if (!player.items[data.num] || player.items[data.num].name != "shadowstone") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + consume_one(player, data.num); + consume_mp(player, G.skills[data.name].mp); + player.s.phasedout = { ms: G.conditions.phasedout.duration }; + player.to_resend = "u+cid+reopen"; + } + if (data.name == "pcoat") { + if (!player.items[data.num] || player.items[data.num].name != "poison") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + consume_one(player, data.num); + consume_mp(player, G.skills[data.name].mp); + player.s.poisonous = { ms: G.skills.pcoat.cooldown }; + player.to_resend = "u+cid+reopen"; + } + if (data.name == "curse") { + //#TODO: last_curse variable + check for multiple curses + var attack = commence_attack(player, target, "curse"); + if (!attack.failed) { + resolve = attack; + add_pdps(player, null, player.attack / 2); + } else { + reject = attack; + cool = false; + } + } + if (data.name == "snowball") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + var found = false; + for (var i = player.isize - 1; i >= 0; i--) { + if (player.items[i] && player.items[i].name == "snowball") { + consume_one(player, i); + found = true; + break; + } + } + if (!found) { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + var attack = commence_attack(player, target, "snowball"); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "entangle" || data.name == "tangle") { + if (data.name == "entangle") { + if (!player.items[data.num] || player.items[data.num].name != "essenceofnature") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + } + if (target.s.invincible) { + return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); + } + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + add_condition(target, "tangled", { ms: G.skills["entangle"].duration }); + target.abs = true; + target.moving = false; + xy_emit(player, "ui", { type: "entangle", from: player.name, to: target.id }); + consume_mp(player, G.skills[data.name].mp, target); + add_pdps(player, target, 4000); + if (data.name == "entangle") { + consume_one(player, data.num); + } + player.to_resend = "u+cid+reopen"; + if (target.is_monster) { + target.u = true; + target.cid++; + ccms(target); + } else { + resend(target, "u+cid"); + } + } + if (data.name == "4fingers") { + if (target.s.invincible) { + return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); + } + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (!is_in_pvp(player) && !is_same(player, target, 1)) { + return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); + } + add_condition(target, "fingered", { ms: G.skills["4fingers"].duration }); + add_condition(target, "stunned", { duration: G.skills["4fingers"].duration - 2000 }); + xy_emit(player, "ui", { type: "4fingers", from: player.name, to: target.name }); + consume_mp(player, G.skills[data.name].mp, target); + add_pdps(player, target, 1000); + resend(target, "u+cid"); + player.to_resend = "u+cid"; + } + if ( + [ + "quickpunch", + "quickstab", + "smash", + "mentalburst", + "purify", + "taunt", + "supershot", + "zapperzap", + "burst", + "piercingshot", + ].includes(data.name) + ) { + var attack = commence_attack(player, target, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "poisonarrow") { + if (!player.items[data.num] || player.items[data.num].name != "poison") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + + var attack = commence_attack(player, target, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + + consume_one(player, data.num); + player.to_resend = "reopen"; + } + if (data.name == "revive") { + if (!player.items[data.num] || player.items[data.num].name != "essenceoflife") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (!target.rip) { + return socket.emit("game_response", { response: "target_alive", place: data.name, failed: true }); + } + + consume_one(player, data.num); + player.to_resend = "reopen"; + if (target.party == player.party) { + add_pdps(player, target, target.pdps / 5); + } + + if (target.hp != target.max_hp) { + reject = { response: "data", place: data.name, reason: "hp" }; + player.socket.emit("game_response", { response: "revive_failed", id: data.id }); + } else { + target.c.revival = { ms: 8000, f: player.name }; + } + resend(target, "u+cid"); + } + if (data.name == "cburst") { + var hit = {}; + var times = 0; + var attack = null; + var c_resolve = null; + consume_mp(player, G.skills[data.name].mp); + player.first_burst = true; + player.halt = true; + if (is_array(data.targets)) { + data.targets.forEach(function (t) { + // console.log(id); + var id = t[0]; + var mp = max(0, parseInt(t[1]) || 0); + if (player.mp < 20 || times > 16 || !mp) { + return; + } + times += 1; + var target = instances[player.in].monsters[id]; + if (!target) { + target = instances[player.in].players[id]; + } + if (!target || is_invinc(target) || target.name == player.name) { + return; + } + if (hit[id]) { + return; + } + hit[id] = true; + player.next_mp = mp; + attack = commence_attack(player, target, "cburst"); + if (!attack || !attack.projectile) { + return; + } + if (!c_resolve) { + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { + c_resolve.pids.push(attack.pid); + c_resolve.targets.push(attack.target); + } + }); + } + player.halt = false; + player.to_resend = "u+cid"; + if (!c_resolve) { + if (attack) { + reject = attack; + } + disappearing_text(player.socket, player, "NO HITS"); + } else { + resolve = c_resolve; + } + } + if (data.name == "partyheal") { + var targets = [player]; + var attack = null; + var hits = 0; + if (parties[player.party]) { + targets = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + targets.push(current); + }); + } + targets.forEach(function (target) { + attack = commence_attack(player, target, "partyheal"); + if (!attack.failed) { + hits = true; + } + }); + if (!hits) { + reject = attack; + } + } + if (data.name == "selfheal") { + var attack = commence_attack(player, player, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "darkblessing" || data.name == "warcry") { + consume_mp(player, G.skills[data.name].mp); + for (var id in instances[player.in].players) { + var target = instances[player.in].players[id]; + if ( + !target.npc && + distance(player, target, true) < G.skills[data.name].range && + (!(G.maps[player.map].pvp || is_pvp) || is_same(player, target, 1)) + ) { + target.s[G.skills[data.name].condition] = { ms: G.skills[data.name].duration, f: player.name }; + resend(target, "u+cid"); + add_pdps(player, target, 500); + } + } + xy_emit(player, "ui", { type: data.name }); + } + if (data.name == "3shot" || data.name == "5shot") { + player.halt = true; + var times = 0; + var hit = {}; + var reftarget = null; + var targets = 3; + var attack = null; + var c_resolve = null; + if (data.name == "5shot") { + targets = 5; + } + //console.log(data.ids); + if (is_array(data.ids)) { + data.ids.forEach(function (id) { + if (times >= targets) { + return; + } + times += 1; + var target = instances[player.in].monsters[id]; + if (!target) { + target = instances[player.in].players[id]; + } + if (!target || is_invinc(target) || target.name == player.name) { + attack = { failed: true, place: data.name, reason: "no_target" }; + return; + } + if (hit[id]) { + return; + } + hit[id] = true; + attack = commence_attack(player, target, data.name); + if (!attack || !attack.projectile) { + return; + } + if (!c_resolve) { + reftarget = target; + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { + c_resolve.pids.push(attack.pid); + c_resolve.targets.push(attack.target); + } + // if(times==1 && attack==null) times=40; + }); + } + consume_mp(player, G.skills[data.name].mp, reftarget); + player.halt = false; + player.to_resend = "u+cid"; + if (!c_resolve) { + if (attack) { + reject = attack; + } + disappearing_text(player.socket, player, "NO HITS"); + } else { + resolve = c_resolve; + } + } + if (data.name == "track") { + var list = []; + for (var id in instances[player.in].players) { + if (id == player.name) { + continue; + } + var target = instances[player.in].players[id]; + var current = { sound: "wmp" }; + if (target.npc) { + continue; + } + if (is_invis(target)) { + current.invis = true; + } + if (target.type == "rogue" || target.type == "ranger") { + current.sound = "rr"; + } else if (target.type == "priest" || target.type == "mage") { + current.sound = "pm"; + } + current.dist = distance(player, target); + if (current.dist < G.skills.track.range) { + list.push(current); + } + } + list.sort(function (a, b) { + return a.dist - b.dist; + }); + consume_mp(player, G.skills[data.name].mp); + xy_emit(player, "ui", { type: "track", name: player.name }); + socket.emit("track", list); + player.to_resend = "u+cid"; + } + if (data.name == "agitate") { + var ids = []; + for (var id in instances[player.in].monsters) { + var target = instances[player.in].monsters[id]; + if (target.target == player.name) { + ids.push(id); + continue; + } // why did the missing continue cause a disengage I have no idea [25/03/23] + if (target.target && !(get_player(target.target) && is_same(player, get_player(target.target), 1))) { + continue; + } + var dist = distance(player, target); + if (dist < G.skills.agitate.range) { + if (target.target) { + stop_pursuit(target, { redirect: true, cause: "agitate redirect" }); + } + target.cid += 1; + target.u = true; + target.last.attacked = new Date(); + target_player(target, player); + ids.push(id); + } + } + consume_mp(player, G.skills[data.name].mp, target); + xy_emit(player, "ui", { type: "agitate", name: player.name, ids: ids }); + player.to_resend = "u+cid"; + add_pdps(player, null, 1000); + } + if (data.name == "absorb") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + var ids = []; + if (is_same(player, target, 1)) { + consume_mp(player, G.skills[data.name].mp); + } else { + if (player.mp < G.skills[data.name].mp * 6 || player.level < 75) { + return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp * 6); + if (Math.random() < 0.95) { + consume_skill(player, data.name); + resend(player, "u+cid"); + return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); + } + } + for (var id in instances[player.in].monsters) { + var monster = instances[player.in].monsters[id]; + if (monster.target == target.name) { + stop_pursuit(monster, { redirect: true, cause: "absorb redirect" }); + monster.cid += 1; + monster.u = true; + target_player(monster, player); + ids.push(id); + } + } + xy_emit(player, "ui", { type: "absorb", name: player.name, from: data.id, ids: ids }); + player.to_resend = "u+cid"; + add_pdps(player, target, 1000); + } + if (data.name == "stomp") { + var ids = []; + var reftarget = null; + for (var id in instances[player.in].monsters) { + var target = instances[player.in].monsters[id]; + var dist = distance(player, target); + if (dist < G.skills.stomp.range) { + if (target.immune) { + player.hitchhikers.push(["game_response", { response: "skill_immune", skill: data.name }]); + player.to_resend = "nc"; + disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: player.id }); + } else if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { + ids.push(id); + add_pdps(player, target, 500); + } + } + } + if (is_in_pvp(player, 1)) { + for (var id in instances[player.in].players) { + var target = instances[player.in].players[id]; + var dist = distance(player, target); + if (dist < G.skills.stomp.range && !target.npc && !is_same(player, target, 1) && !target.s.invincible) { + if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { + reftarget = target; + resend(target, "u+cid"); + ids.push(id); + add_pdps(player, target, 5000); + } + } + } + } + consume_mp(player, G.skills[data.name].mp, reftarget); + xy_emit(player, "ui", { type: "stomp", name: player.name, ids: ids }); + player.to_resend = "u+cid"; + resolve = { response: "data", place: data.name, success: true, ids: ids }; + } + if (data.name == "scare") { + var ids = []; + consume_mp(player, G.skills[data.name].mp); + for (var id in instances[player.in].monsters) { + var target = instances[player.in].monsters[id]; + if (target.target == player.name) { + if (target.immune) { + player.hitchhikers.push(["game_response", { response: "skill_immune", skill: "scare" }]); + player.to_resend = "nc"; + disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: player.id }); + } else { + stop_pursuit(target, { force: true, cause: "scare" }); + target.abs = true; + target.moving = false; + ids.push(id); + } + } + } + xy_emit(player, "ui", { type: "scare", name: player.name, ids: ids }); + player.to_resend = "u+cid"; + } + if (data.name == "huntersmark") { + if (target.is_player && !is_in_pvp(player) && !is_same(player, target, 1)) { + return socket.emit("game_response", { response: "skill_cant_pve", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp, target); + add_condition(target, "marked"); + if (target.is_player) { + resend(target, "u+cid"); + } else { + target.cid++; + target.u = true; + } + xy_emit(player, "ui", { type: "huntersmark", name: player.name, id: target.id }); + player.to_resend = "u+cid"; + } + if (data.name == "charm") { + consume_mp(player, G.skills[data.name].mp); + if (Math.random() > 0.01) { + socket.emit("game_response", "charm_failed"); + xy_emit(player, "ui", { type: "charm", name: player.name, id: target.id, fail: true }); + } else { + target.cid++; + target.u = true; + target.s.charmed = { ms: G.conditions.charmed.duration }; + xy_emit(player, "ui", { type: "charm", name: player.name, id: target.id }); + } + player.to_resend = "u+cid"; + } + if (data.name == "cleave" || data.name == "shadowstrike") { + player.to_resend = "u+cid"; + if (data.name == "shadowstrike") { + if (!player.items[data.num] || player.items[data.num].name != "shadowstone") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + consume_one(player, data.num); + player.to_resend = "u+cid+reopen"; + } + player.halt = true; + var ids = []; + var reftarget = null; + var c_resolve = null; + for (var id in instances[player.in].monsters) { + var target = instances[player.in].monsters[id]; + var dist = distance(player, target); + if (dist < G.skills[data.name].range) { + attack = commence_attack(player, target, data.name); + if (!attack || !attack.projectile) { + continue; + } + ids.push(id); + if (!c_resolve) { + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { + c_resolve.pids.push(attack.pid); + c_resolve.targets.push(attack.target); + } + } + } + if (is_in_pvp(player, 1)) { + for (var id in instances[player.in].players) { + var target = instances[player.in].players[id]; + var dist = distance(player, target); + if (dist < G.skills[data.name].range && !target.npc && !is_same(player, target, 1)) { + attack = commence_attack(player, target, data.name); + if (!attack || !attack.projectile) { + continue; + } + ids.push(id); + if (!c_resolve) { + reftarget = target; + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { + c_resolve.pids.push(attack.pid); + c_resolve.targets.push(attack.target); + } + } + } + } + consume_mp(player, G.skills[data.name].mp, reftarget); + xy_emit(player, "ui", { type: data.name, name: player.name, ids: ids }); + player.halt = false; + if (c_resolve) { + resolve = c_resolve; + } + } + if (data.name == "magiport") { + var pported = false; + consume_mp(player, G.skills[data.name].mp); + if (!is_pvp && mode.pve_safe_magiports) { + if (!magiportations[player.name]) { + magiportations[player.name] = {}; + } + magiportations[player.name][target.name] = true; + target.socket.emit("magiport", { name: player.name }); + player.socket.emit("game_response", { response: "magiport_sent", id: data.id }); + xy_emit(player, "ui", { type: "magiport", name: player.name }); + } else { + if (is_same(player, target, 1) || !target.slots.helmet) { + ported = magiport_someone(target, player); + } + if (!ported) { + player.socket.emit("game_response", { response: "magiport_failed", id: data.id }); + } + } + player.to_resend = "u+cid"; + } + if (data.name == "blink") { + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + var spot = safe_xy_nearby(player.map, x, y); + if (!spot) { + return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); + } + player.s.blink = { ms: 200 }; + player.s.blink.in = player.in; + player.s.blink.map = player.map; + player.s.blink.x = spot.x; + player.s.blink.y = spot.y; + player.s.blink.d = 0; + if (in_arr(data.direction, [1, 2, 3])) { + player.s.blink.d = data.direction; + } + if (player.role != "gm") { + consume_mp(player, G.skills[data.name].mp); + } + // xy_emit(player,"ui",{type:"blinking",name:player.name}); + resend(player, "u+cid"); + // #TODO: Appear animation for non-self's [21/05/18] + } + if (data.name == "warp") { + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + var ins = data.in || "main"; + var instance = instances[ins]; + if (!instance) { + return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); + } + if (instance.mount != instances[player.in].mount) { + return player.socket.emit("game_response", { response: "cant_in_bank", place: data.name, failed: true }); + } + var spot = safe_xy_nearby(instance.name, x, y); + if (!spot) { + return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); + } + player.s.blink = { ms: 200 }; + player.s.blink.in = ins; + player.s.blink.map = instance.name; + player.s.blink.x = spot.x; + player.s.blink.y = spot.y; + player.s.blink.d = 0; + if (in_arr(data.direction, [1, 2, 3])) { + player.s.blink.d = data.direction; + } + if (player.role != "gm") { + consume_mp(player, G.skills[data.name].mp); + } + // xy_emit(player,"ui",{type:"blinking",name:player.name}); + player.to_resend = "u+cid"; + // #TODO: Appear animation for non-self's [21/05/18] + } + if (data.name == "mluck") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp); + if ( + !target.s[G.skills[data.name].condition] || + !target.s[G.skills[data.name].condition].strong || + target.s[G.skills[data.name].condition].f == player.name + ) { + target.s[G.skills[data.name].condition] = { ms: G.conditions.mluck.duration, f: player.name }; + } + if (target.owner == player.owner) { + target.s[G.skills[data.name].condition].strong = true; + } + xy_emit(player, "ui", { type: "mluck", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + } + if (data.name == "rspeed") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp); + target.s[G.skills[data.name].condition] = { ms: G.conditions.rspeed.duration, f: player.name }; + xy_emit(player, "ui", { type: "rspeed", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + if (player.party == target.party && player != target) { + add_pdps(player, target, 2000); + } + } + if (data.name == "reflection") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp); + target.s[G.skills[data.name].condition] = { ms: G.conditions.reflection.duration, f: player.name }; + xy_emit(player, "ui", { type: "reflection", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + if (player.party == target.party && player != target) { + add_pdps(player, target, 4000); + } + } + if (data.name == "energize") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (!player.mp) { + return socket.emit("game_response", { response: "no_mp", place: data.name, failed: true }); + } + var mp = parseInt(data.mp) || 10000; + mp = max(1, min(player.mp, mp)); + if (mp > target.max_mp - target.mp) { + mp = target.max_mp - target.mp; + } + target.mp += mp; + player.mp -= mp; + if (player.party == target.party && player != target) { + add_pdps(player, target, mp * 2); + } + disappearing_text(player.socket, player, "-" + mp, { color: "mana", xy: 1 }); + disappearing_text(target.socket, target, "+" + mp, { color: "mana", xy: 1 }); + target.s[G.skills[data.name].condition] = { ms: G.conditions[G.skills[data.name].condition].duration }; + xy_emit(player, "ui", { type: "energize", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + } + if (data.name == "alchemy") { + var gold = 0; + var rate = 0.8; + if (player.level >= 100) { + rate = 1.12; + } else if (player.level >= 90) { + rate = 1.1; + } else if (player.level >= 80) { + rate = 1.06; + } else if (player.level >= 70) { + rate = 1; + } else if (player.level >= 60) { + rate = 0.92; + } else if (player.level >= 50) { + rate = 0.86; + } + consume_mp(player, G.skills[data.name].mp); + xy_emit(player, "ui", { type: "alchemy", name: player.name }); + for (var i = 0; i < player.isize; i++) { + if (!player.items[i] || player.items[i].l) { + continue; + } + gold = calculate_item_value(player.items[i]); + consume_one(player, i); + break; + } + player.gold += gold * rate; + resend(player, "reopen"); + socket.emit("game_response", { response: "gold_received", gold: gold * rate }); + } + if (cool) { + consume_skill(player, data.name); + } + if (player.to_resend) { + resend(player, player.to_resend); + } + if (reject) { + if (!reject.response) { + reject.response = "data"; + } + socket.emit("game_response", reject); + } else if (resolve) { + socket.emit("game_response", resolve); + } + }); + socket.on("click", function (data) { + // You'll be missed 'click' method, the 'click' method was the first method on this server, it was used as an attack method up until [17/06/18] - at this date, there were 3 simple conditions left which checked for data.button=="right" - the game matured so that all interactions were handled client-side rather than processed server-side + socket.emit("game_log", "'click' method is deprecated."); + }); + socket.on("attack", function (data) { + return socket.fs.skill({ name: "attack", id: data.id }); + }); + socket.on("heal", function (data) { + return socket.fs.skill({ name: "heal", id: data.id }); + }); + socket.on("interaction", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.type == "newyear_tree") { + var x = ""; + if ( + !G.maps[player.map].ref || + !G.maps[player.map].ref[data.type] || + distance(G.maps[player.map].ref[data.type], player, true) > 300 + ) { + return socket.emit("game_response", "distance"); + } + if (!player.s.holidayspirit && player.esize) { + add_item(player, "funtoken"); + x = "+reopen"; + } + add_condition(player, "holidayspirit"); + resend(player, "u+cid" + x); + } else if (["redorb", "blueorb", "greenorb", "yelloworb"].includes(data.type)) { + if ( + !G.maps[player.map].ref || + !G.maps[player.map].ref[data.type] || + distance(G.maps[player.map].ref[data.type], player, true) > 40 + ) { + return socket.emit("game_response", "distance"); + } + ["redorb", "blueorb", "greenorb", "yelloworb"].forEach(function (s) { + delete player.s[s]; + }); + add_condition(player, data.type); + resend(player, "u+cid"); + } else if (data == "the_lever") { + if (player.map != "resort_e") { + return socket.emit("game_response", "distance"); + } + player.s.magiport = { ms: 300 }; + player.s.magiport.x = player.x; + player.s.magiport.y = player.y; + player.s.magiport.f = player.name; + player.s.magiport.in = "resort"; + player.s.magiport.map = "resort"; + resend(player, "u+cid"); + } else if (data.type == "dailytask") { + if (!G.maps.main.ref.dailytask || distance(G.maps.main.ref.dailytask, player, true) > 150) { + return socket.emit("game_response", "distance"); + } + player.p.monsterhunt = { ms: 60 * 60 * 1000, m: "goo" }; + socket.emit("game_response", { response: "monsterhunt", monster: "goo" }); + } else if (data.key && player.konami) { + if (data.key == "B" && player.konami.length < 20) { + player.konami.push(data.key[0]); + } else if (data.key == "A") { + if (player.konami.join("") == "uuddlrlrB") { + player.tskin = "konami"; + resend(player, "u+cid"); + if (!player.p.target_lock || !G.monsters[player.p.target_lock] || hsince(player.p.dt.last_tl) > 15 * 24) { + var monsters = []; + for (var name in G.monsters) { + if (!G.monsters[name].special && !G.monsters[name].stationary && G.monsters[name].c) { + monsters.push(name); + } + } + player.p.target_lock = random_one(monsters); + player.p.dt.last_tl = new Date(); + } + socket.emit("game_response", { response: "target_lock", monster: player.p.target_lock }); + } else { + player.konami = []; + } + } + } + }); + socket.on("mreport", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var a = 0; + var b = 0; + a = parseFloat(data.x) || 0; + b = parseFloat(data.y) || 0; + // console.log(JSON.stringify(data)); + socket.emit("game_log", "" + simple_distance({ x: player.x, y: player.y }, { x: a, y: b })); + }); + socket.on("move", function (data) { + // if(observers[socket.id]) observers[socket.id].x=data.x,observers[socket.id].y=data.y; Old observer code [17/02/17] + var player = players[socket.id]; + var current = -1; + var going = -1; + var actual = true; + var x = parseFloat(data.going_x) || 0; + var y = parseFloat(data.going_y) || 0; + if (data.pet) { + player = player.monster; + actual = false; + } + if (data.key && (!player.konami || player.konami.length < 20)) { + // console.log(data.key); + if (!player.konami) { + player.konami = []; + } + player.konami.push(data.key[0]); + } + if (player && player.m == data.m && can_walk(player) && (x != player.x || y != player.y)) { + // if(is_sdk) server_log("Player moving to: "+data.going_x+","+data.going_y); + + // player.x=data.x; player.y=data.y; [03/08/16] Seems like a really bad idea to update x/y based on what players provide + data.x = parseFloat(data.x) || 0; + data.y = parseFloat(data.y) || 0; + player.going_x = x; + player.going_y = y; + if (smap_data[player.map] != -1 && mode.enforce_smap) { + current = smap_data[player.map][rphash(data.x, data.y)]; + going = smap_data[player.map][rphash(player.going_x, player.going_y)]; + // server_log("current:"+current+" going:"+going+" real:"+smap_data[player.map][phash(player.x,player.y)]); + if (current === undefined || current >= 2 || going === undefined || going >= 2) { + server_log( + "#C cheater: " + + player.name + + " current: " + + current + + "[" + + data.x + + "," + + data.x + + "] going: " + + going + + "[" + + player.going_x + + "," + + player.going_y + + "] at " + + player.map, + 1, + ); + appengine_log("violation", "move_line: " + player.name + " afk: " + player.afk + " code: " + player.code); + player.socket.emit("game_log", "Line violation detected"); + player.socket.emit("game_log", "Make sure you only move with the built-in move function"); + defeat_player(player); + transport_player_to(player, "jail"); + return; + } + } + // player.check_x=player.going_x; player.check_y=player.going_y; player.checked_xy=false; + start_moving_element(player); + if ( + !player.pet && + simple_distance(player, { x: data.x, y: data.y }) > 132 && + current == 0 && + mode.lcorrection + ) { + // server_log("Correction sent"); + socket.emit("correction", { x: player.x, y: player.y }); + } + if (actual) { + var change = false; + for (var id in player.c) { + if (G.conditions[id] && !G.conditions[id].can_move) { + change = true; + delete player.c[id]; + } + } + if (change) { + resend(player, "u+cid"); + } + } + //if(smap_data[player.map][rphash(player.x,player.y)]===0) push_xyh(player,player.x,player.y); + //else push_xyh(player,data.x,data.y); + } + }); + socket.on("open_chest", function (data) { + var chest = chests[data.id]; + var player = players[socket.id]; + var reopen = false; + if (!player) { + return; + } + var r = { id: data.id, goldm: player.goldm, opener: player.name, items: [] }; + if (chest && simple_distance(chest, player) > 400) { + r.goldm = 1; + r.dry = true; + } + if (chest && msince(chest.date) > 8) { + r.goldm = 1; + r.stale = true; + } + if (player.map == "woffice" || G.maps[player.map].mount || is_invis(player)) { + return fail_response("loot_failed"); + } + try { + if (chest && !chest.pvp && chest.gold > 100000000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " looted " + to_pretty_num(server_tax(round(chest.gold * r.goldm), true)) + " gold", + color: "gold", + type: "server_gold", + name: player.name, + }); + } + if (chest && player.owner && chest.owners && !in_arr(player.owner, chest.owners) && !W.chest[player.owner]) { + W.chest[player.owner] = new Date(); + server_log("SEVERE - Cross Loot from " + player.name + " not from " + chest.owners.toString()); + } + if (chest && !player.party) { + var all_items = chest.items.slice(0); + all_items.concat(chest.pvp_items); + if (!can_add_items(player, all_items)) { + return fail_response("loot_no_space"); + } + delete chests[data.id]; // The add_shells routine was at the top, so when inventory was full, attempting to open the chest gave shells infinitely, repeated lesson, always remove before adding, or exceptions [11/07/18] + if (chest && chest.cash) { + add_shells(player, chest.cash, "chest", true, "override"); + } + chest.items.forEach(function (item) { + item.src = "pvp"; + add_item(player, item, { found: 1, m: 1, v: B.v }); + reopen = true; + var ritem = cache_item(item); + ritem.looter = player.name; + r.items.push(ritem); + socket.emit("game_log", { message: "Found " + item_to_phrase(item), color: "#4BAEAA" }); + if (player.t) { + player.t.dgold += round(G.items[item.name].g * 0.6); + } + }); + (chest.pvp_items || []).forEach(function (item) { + item.v = new Date(); + if (can_add_item(player, item)) { + add_item(player, item, { found: 1, v: B.v }); + reopen = true; + var ritem = cache_item(item); + ritem.looter = player.name; + ritem.pvp_loot = true; + r.items.push(ritem); + socket.emit("game_log", { message: "Looted " + item_to_phrase(item), color: "#4BAEAA" }); + } else { + lostandfound_logic(item); + var ritem = cache_item(item); + ritem.looter = null; + ritem.lostandfound = true; + r.items.push(ritem); + socket.emit("game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); + } + }); + r.gold = round(chest.gold * r.goldm) + round(chest.egold || 0); + r.gold = server_tax(r.gold); + player.gold += r.gold; + if (player.t) { + player.t.cgold += r.gold; + } + if (r.gold) { + socket.emit("game_log", { message: to_pretty_num(r.gold) + " gold", color: "gold" }); + } + socket.emit("disappearing_text", { + message: "+" + r.gold, + x: chest.x, + y: chest.y - 10, + args: { color: "+gold", size: "large" }, + }); + if (!r.items.length) { + delete r.items; + } + resend(player, (reopen && "reopen+nc+inv") || ""); + socket.emit("chest_opened", r); + } else if (chest) { + // var gold=round(chest.gold/parties[player.party].length); + r.party = true; + var chest = chests[data.id]; + var reopen = {}; + delete chests[data.id]; + if (chest && chest.cash) { + add_shells(player, chest.cash, "chest", true, "override"); + } + chest.items.forEach(function (item) { + // console.log(item); + var pool = 0; + var can = {}; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && can_add_item(current, item)) { + pool += current.share; + can[name] = true; + } + }); + var pool_winner = Math.random() * pool; + var pool_current = 0; + var awarded = false; + parties[player.party].forEach(function (name) { + if (awarded) { + return; + } + var current = players[name_to_id[name]]; + if (current && can[name]) { + if (pool_winner <= pool_current + current.share) { + awarded = true; + add_item(current, item, { found: 1, m: 1, v: B.v }); + reopen[current.id] = true; + var ritem = cache_item(item); + ritem.looter = current.name; + r.items.push(ritem); + party_emit(player.party, "game_log", { + message: current.name + " found " + item_to_phrase(item), + color: "#4BAEAA", + }); + if (current.t) { + current.t.dgold += round(G.items[item.name].g * 0.6); + } + } else { + pool_current += current.share; + } + } + }); + if (!awarded) { + lostandfound_logic(item); + var ritem = cache_item(item); + ritem.looter = null; + ritem.lostandfound = true; + r.items.push(ritem); + party_emit(player.party, "game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); + } + }); + (chest.pvp_items || []).forEach(function (item) { + item.v = new Date(); + var pool = 0; + var can = {}; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && current.share && can_add_item(current, item)) { + pool += current.share; + can[name] = true; + } + }); + var pool_winner = Math.random() * pool; + var pool_current = 0; + var awarded = false; + parties[player.party].forEach(function (name) { + if (awarded) { + return; + } + var current = players[name_to_id[name]]; + if (current && can[name]) { + if (pool_winner <= pool_current + current.share) { + awarded = true; + add_item(current, item, { found: 1, v: B.v }); + reopen[current.id] = true; + var ritem = cache_item(item); + ritem.looter = current.name; + ritem.pvp_loot = true; + r.items.push(ritem); + party_emit(player.party, "game_log", { + message: current.name + " looted " + item_to_phrase(item), + color: "#4BAEAA", + }); + } else { + pool_current += current.share; + } + } + }); + if (!awarded) { + lostandfound_logic(item); + var ritem = cache_item(item); + ritem.looter = null; + ritem.pvp_loot = true; + ritem.lostandfound = true; + r.items.push(ritem); + party_emit(player.party, "game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); + } + }); + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + var cgold = + round(chest.gold * (current.share || 0) * r.goldm) + round((chest.egold || 0) * (current.share || 0)); + r.gold = cgold = server_tax(cgold); + current.gold += cgold; + if (current.t) { + current.t.cgold += cgold; + } + if (cgold) { + current.socket.emit("game_log", { message: to_pretty_num(cgold) + " gold", color: "gold" }); + } + if (current.in == player.in) { + current.socket.emit("disappearing_text", { + message: "+" + cgold, + x: chest.x, + y: chest.y - 10, + args: { color: "gold", size: "large" }, + }); + } + resend(current, (reopen[current.id] && "reopen+nc+inv") || ""); + current.socket.emit("chest_opened", r); + }); + } else { + socket.emit("chest_opened", { id: data.id, gone: true }); + } + } catch (e) { + delete chests[data.id]; // If this didn't exist, any exception would end up being a source for infinite gold and items + log_trace("#X chest_error", e); + return fail_response("error"); + } + }); + socket.on("auth", function (data) { + if (gameplay == "test" && data.passphrase != "potato salad") { + return socket.emit("game_log", "Wrong passphrase!"); + } + if (observers[socket.id] && observers[socket.id].auth_engaged) { + return socket.emit("game_log", "Authorization in progress."); + } + if (dc_players[data.character]) { + return socket.emit("game_log", "Authorization in progress."); + } + if (!server.live || !observers[socket.id] || players[socket.id]) { + return; + } + if (Object.keys(players).length >= max_players) { + socket.emit("game_error", "Can't accept more than " + max_players + " players at this time"); + return; + } + socket.observer_secret = randomStr(24); + observers[socket.id].auth_engaged = true; + appengine_call( + "start_character", + { + auth: data.user + "-" + data.auth, + secret: socket.observer_secret, + code_slot: data.code_slot, + character: data.character, + mode: gameplay, + ip: get_ip(socket), + suffix: "/" + data.character, + }, + function (result) { + if (observers[socket.id]) { + observers[socket.id].auth_engaged = false; + } + if (result.failed) { + socket.emit("game_error", "Failed: " + result.reason); + return; + } + // console.log(JSON.stringify(result)); + server_log("start_character: " + JSON.stringify(result.character.name), 1); + var player = { u: true, is_player: true, humanoid: true, secret: socket.observer_secret }; + for (prop in result.character) { + player[prop] = result.character[prop]; + } + if (!instances[player.map] || !instances[player.map].allow || instances[player.map].mount) { + var place = G.maps[player.map].on_exit || G.maps[B.start_map].on_exit || ["main", 0]; + player.map = player.in = place[0]; + player.x = G.maps[player.map].spawns[place[1]][0]; + player.y = G.maps[player.map].spawns[place[1]][1]; + } else { + player.in = player.map; + } + player.owner = data.user; + player.auth = data.user + "-" + data.auth; + player.last_sync = new Date(); + player.socket = socket; + player.max_stats = result.stats; + + if (data.bot == variables.bot_key) { + player.bot = true; + player.afk = "bot"; + } + if (data.no_html) { + player.afk = "code"; + try { + player.controller = (name_to_id[data.no_html] && data.no_html) || ""; + } catch (e) { + player.controller = ""; + } + } + if (!player.afk) { + player.afk = true; + } + if (gameplay == "test") { + player.name += parseInt(Math.random() * 10000); + } + player.real_id = player.id; + player.id = player.name; + + player.total_ips = 1; + player.width = 26; + player.height = 36; + player.damage_type = G.classes[player.type].damage_type; + player.xrange = 25; + player.red_zone = 0; + player.targets = player.targets_p = player.targets_m = player.targets_u = 0; + player.cid = 1; + player.hits = 0; + player.kills = 0; + player.m = 0; // map number + /* party variables*/ + player.pdps = 0; + player.party_length = 1; + player.party_luck = 0; + player.party_xp = 0; + player.party_gold = 0; + player.share = 0.1; + player.cx = player.cx || {}; + if (!player.s) { + player.s = {}; + } + player.t = { mdamage: 0, cgold: 0, dgold: 0, xp: 0, start: new Date() }; + player.hitchhikers = []; // socket events to be registered after a resend + player.last = { attack: future_ms(-1200), attacked: really_old }; + player.bets = {}; + player.base = dbase; + player.age = parseInt(ceil(hsince(new Date(player.created)) / 24.0)); + // player.vision=[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision]; + // player.vision[0]=min(1000,player.vision[0]); + // player.vision[1]=min(700,player.vision[1]); + player.vision = B.vision; + + if (!player.verified) { + player.s.notverified = { ms: 30 * 60 * 1000 }; + } else if (player.s.notverified) { + player.s.notverified = { ms: 100 }; + } + + if (player.guild) { + console.log(player.guild); + player.guild = player.guild.short; + } + + if (gameplay == "hardcore") { + reset_player(player); + } // || gameplay=="test" + + init_player(player); + if (!observers[socket.id]) { + // observer hang up before "auth" + server_log("Abrupt stop for " + result.character.name, 1); + if (gameplay != "hardcore" && gameplay != "test") { + dc_players[player.real_id] = player; + } + sync_loop(); + return; + } + try { + delete_observer(socket); + } catch (e) {} + + players[socket.id] = player; + resume_instance(instances[player.in]); + instances[player.in].players[player.id] = player; + pmap_add(player); + + name_to_id[player.name] = socket.id; + id_to_id[player.id] = socket.id; + + cache_player_items(player); + invincible_logic(player); + serverhop_logic(player); + calculate_player_stats(player); + + if (data.epl == "mas" && data.receipt) { + player.platform = "mas"; + verify_mas_receipt(player, data.receipt); + } else if (data.epl == "steam" && data.ticket) { + player.platform = "steam"; + verify_steam_ticket(player, data.ticket); + } else { + player.platform = "web"; + if (result.character.pid) { + player.auth_id = result.character.pid; + } // part of the new restriction system [02/05/19] + } + + if (mode.drm_check) { + if (result.character.drm && !player.auth_id) { + player.s.authfail = { ms: 900000 * 1000 }; + } else if (player.s.authfail) { + player.s.authfail = { ms: 100 }; + } + } + + if (!is_player_allowed(player)) { + socket.emit("disconnect_reason", "limits"); + socket.disconnect(); + } else { + var cdata = player_to_client(player); + player.ipass = cdata.ipass = randomStr(12); + player.last_ipass = new Date(); + player.last.attack = future_ms(-10000); + player.last.transport = future_ms(-10000); + cdata.home = player.p.home; + cdata.friends = player.friends; + cdata.acx = player.p.acx; + cdata.xcx = player.p.xcx; + cdata.emx = player.p.emx; + cdata.info = instances[player.in].info; + cdata.base_gold = D.base_gold; + broadcast_e(true); + cdata.s_info = E; + if (result.code) { + cdata.code = result.code; + cdata.code_slot = result.code_slot; + cdata.code_version = result.code_version; + } + cdata.entities = send_all_xy(player, { raw: true }); + socket.emit("start", cdata); + total_players++; + } + }, + function (err) { + server_log("start_character_failed: " + data.character + " reason: " + err, 1); + if (observers[socket.id]) { + observers[socket.id].auth_engaged = false; + } + }, + ); + }); + socket.on("use", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.item == "hp" || data.item == "mp") { + if (player.last.potion && mssince(player.last.potion) < 0) { + return fail_response("not_ready"); + } + player.last.potion = future_ms(4000); + if (data.item == "hp") { + player.hp += 50; + disappearing_text(socket, player, "+50", { color: "green", xy: 1, s: "hp", nohp: 1 }); + } + if (data.item == "mp") { + player.mp += 100; + disappearing_text(socket, player, "+100", { color: "#006AA9", xy: 1, s: "mp", nomp: 1 }); + } + player.hp = min(player.hp, player.max_hp); + player.mp = min(player.mp, player.max_mp); + // calculate_player_stats(player); [22/11/16] + player.cid++; + player.u = true; + socket.emit("player", player_to_client(player)); + socket.emit("eval", { code: "pot_timeout(4000)" }); + } + success_response({}); + }); + socket.on("friend", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + server_log("friend: " + JSON.stringify(data)); + if (data.event == "request") { + var friend = players[name_to_id[data.name || ""]]; + if (!friend) { + return fail_response("friend_rleft"); + } + if (in_arr(friend.owner, player.friends) || friend.owner == player.owner) { + return success_response("friend_already"); + } + requests[player.name + "-" + friend.name] = { a: player.owner, b: friend.owner }; + friend.socket.emit("friend", { event: "request", name: player.name }); + return success_response("friend_rsent"); + } + if (data.event == "accept") { + if (!requests[data.name + "-" + player.name]) { + return fail_response("friend_expired"); + } + appengine_call( + "set_friends", + { user1: requests[data.name + "-" + player.name].a, user2: requests[data.name + "-" + player.name].b }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + if (player) { + socket.emit("game_response", { response: "friend_failed", reason: result.reason }); + } + return; + } + //var friend=players[name_to_id[data.name||""]]; + //if(player) + //if(friend) friend.emit("friend",{event:"accepted",name:player.name}); + }, + function () { + var player = players[socket.id]; + if (player) { + socket.emit("game_response", { response: "friend_failed", reason: "coms failure" }); + } + }, + ); + requests[data.name + "-" + player.name] = false; + } + if (data.event == "unfriend") { + appengine_call( + "not_friends", + { user1: player.owner, user2: data.name }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + if (player) { + socket.emit("game_response", { response: "unfriend_failed", reason: result.reason }); + } + return; + } + }, + function () { + var player = players[socket.id]; + if (player) { + socket.emit("game_response", { response: "unfriend_failed", reason: "coms failure" }); + } + }, + ); + } + success_response({ success: false, in_progress: true }); + }); + socket.on("duel", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.event == "challenge") { + var invited = players[name_to_id[data.id || data.name]]; + if (!invited || invited.id == player.id) { + return socket.emit("game_log", "Invalid"); + } + if (invited.duel || player.duel) { + return socket.emit("game_log", "Already dueling"); + } + invited.socket.emit("duel", { event: "chellenge", name: player.name }); + socket.emit("game_response", { response: "challenge_sent", name: invited.name }); + invited.socket.emit("game_response", { response: "challenge_received", name: player.name }); + challenges[player.name] = invited.name; + } else if (data.event == "accept") { + var challenger = players[name_to_id[data.id || data.name]]; + if (!challenger || challenges[challenger.name] != player.name) { + return socket.emit("game_log", "Challenge expired"); + } + if (challenger.duel || player.duel) { + return socket.emit("game_log", "Already dueling"); + } + if (is_in_pvp(challenger) || is_in_pvp(player)) { + return socket.emit("game_log", "Can't start a duel if any of the parties are already in a pvp zone"); + } + delete challenges[challenger.name]; + challenger.socket.emit("game_response", { response: "challenge_accepted", name: player.name }); + var name = randomStr(20); + var a = []; + var b = []; + instance = create_instance(name, "duelland"); + instance.info = { + seconds: (is_sdk && 20) || 60, + active: false, + A: [player_to_summary(challenger)], + B: [player_to_summary(player)], + id: name, + }; + instance.info.A[0].active = true; + instance.info.B[0].active = true; + clean_slate(challenger); + transport_player_to(challenger, name, 1); + if (challenger.party) { + parties[challenger.party].forEach(function (p) { + a.push(p); + }); + } else { + a = [challenger.name]; + } + clean_slate(player); + transport_player_to(player, name, 2); + if (player.party) { + parties[player.party].forEach(function (p) { + b.push(p); + }); + } else { + b = [player.name]; + } + if (!E.duels) { + E.duels = {}; + } + var duel = { + challenger: challenger.name, + a: a, + vs: player.name, + b: b, + instance: name, + seconds: (is_sdk && 20) || 60, + active: false, + id: name, + }; + challenger.team = "A"; + challenger.duel = duel; + challenger.s.stunned = { ms: 120000 }; + resend(challenger, "u+cid"); + player.team = "B"; + player.duel = duel; + player.s.stunned = { ms: 120000 }; + resend(player, "u+cid"); + E.duels[name] = duel; + broadcast_e(); + a.forEach(function (p) { + if (p != challenger.name && get_player(p)) { + get_player(p).socket.emit("game_response", { + response: "duel_started", + challenger: challenger.name, + vs: player.name, + id: name, + }); + } + }); + b.forEach(function (p) { + if (p != player.name && get_player(p)) { + get_player(p).socket.emit("game_response", { + response: "duel_started", + challenger: challenger.name, + vs: player.name, + id: name, + }); + } + }); + } else if (data.event == "enter") { + if (is_in_pvp(player)) { + return socket.emit("game_log", "Can't join the duel from a pvp zone!"); + } + if (player.duel) { + return socket.emit("game_log", "Already in a duel!"); + } + if (!E.duels[data.id]) { + return socket.emit("game_log", "Duel expired"); + } + if (E.duels[data.id].active) { + return socket.emit("game_log", "Duel already started"); + } + if (!(E.duels[data.id].a.includes(player.name) || E.duels[data.id].b.includes(player.name))) { + return socket.emit("game_log", "Not your duel"); + } + clean_slate(player); + + if (E.duels[data.id].a.includes(player.name)) { + transport_player_to(player, data.id, 1); + player.team = "A"; + } else { + transport_player_to(player, data.id, 2); + player.team = "B"; + } + + player.duel = E.duels[data.id]; + if (player.duel.a.includes(player.name)) { + instances[data.id].info.A.push(player_to_summary(player)); + instances[data.id].info.A[instances[data.id].info.A.length - 1].active = true; + } else { + instances[data.id].info.B.push(player_to_summary(player)); + instances[data.id].info.B[instances[data.id].info.B.length - 1].active = true; + } + player.s.stunned = { ms: 120000 }; + + resend(player, "u+cid"); + instance_emit(data.id, "game_chat", { message: player.name + " joined the duel!" }); + } + }); + socket.on("party", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.event == "invite") { + // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can send invites"); return; } + if (player.party && (parties[player.party].length >= limits.party_max || player.party_length >= limits.party)) { + return fail_response("party_full"); + } + var invited = players[name_to_id[data.id || data.name]]; + if (!invited || invited.id == player.id) { + return fail_response("invalid"); + } + if (player.party && player.party == invited.party) { + return success_response("already_in_party"); + } + // if(invited.party) { socket.emit("game_log",invited.name+" is already partying"); return; } + invited.socket.emit("invite", { name: player.name }); + socket.emit("game_log", "Invited " + invited.name + " to party"); + if (!invitations[player.name]) { + invitations[player.name] = {}; + } + invitations[player.name][invited.id] = 1; + } + if (data.event == "request") { + // if(player.party) { return; } + var invited = players[name_to_id[data.id || data.name]]; + if (!invited || invited.id == player.id) { + return fail_response("invalid"); + } + if (player.party && player.party == invited.party) { + return success_response("already_in_party"); + } + // if(!invited.party) { socket.emit("game_log",invited.name+" isn't partying"); return; } + invited.socket.emit("request", { name: player.name }); + socket.emit("game_log", "Requested to join " + invited.name + "'s party"); + if (!requests[player.name]) { + requests[player.name] = {}; + } + requests[player.name][invited.id] = 1; + } + if (data.event == "accept") { + var inviter = players[name_to_id[data.name]]; + // if(player.party) { socket.emit("party_update",{list:parties[player.party],party:party_to_client(player.party)}); socket.emit("game_log","Already partying"); return; } + // if(!inviter || (inviter.party && inviter.party!=inviter.name)) { socket.emit("game_log","Party was disbanded"); return; } + if (!inviter) { + return fail_response("player_gone", { name: data.name }); + } + if ( + inviter.party && + (parties[inviter.party].length >= limits.party_max || inviter.party_length >= limits.party) + ) { + return fail_response("party_full"); + } + if (!invitations[inviter.name] || !invitations[inviter.name][player.id]) { + return fail_response("invitation_expired"); + } + if (player.party && player.party == inviter.party) { + return success_response("already_in_party"); + } + if (player.party) { + leave_party(player.party, player); + socket.emit("party_update", {}); + socket.emit("game_log", "Left your current party"); + } + invitations[inviter.name][player.id] = 0; + if (!inviter.party) { + inviter.party = inviter.name; + parties[inviter.name] = [inviter.name]; + resend(inviter, "nc+u+cid"); + delete requests[inviter.name]; + if (!players[name_to_id[data.name]]) { + return fail_response("player_gone", { name: data.name }); + } // these repetitions are all because socket.emit's can cause in-line disconnects and disband parties right after creation [07/08/20] + } + player.party = inviter.party; + parties[inviter.party].push(player.name); + party_emit(player.party, "party_update", { + list: parties[inviter.party], + party: party_to_client(inviter.party), + message: + player.name + + " joined the party" + + ((inviter.party != inviter.name && " with " + inviter.name + "'s invite") || ""), + }); + resend(player, "nc+u+cid"); + delete requests[player.name]; + delete requests[inviter.name]; // optional + } + if (data.event == "raccept") { + var requester = players[name_to_id[data.name]]; + if (!requester) { + return fail_response("player_gone", { name: data.name }); + } + // if(requester.party) { socket.emit("game_log","Already partying"); return; } + if (player.party && (parties[player.party].length >= limits.party_max || player.party_length >= limits.party)) { + return fail_response("party_full"); + } + if (!requests[requester.name] || !requests[requester.name][player.id]) { + return fail_response("request_expired"); + } + if (player.party && player.party == requester.party) { + return success_response("already_in_party"); + } + if (requester.party) { + leave_party(requester.party, requester); + requester.socket.emit("party_update", {}); + requester.socket.emit("game_log", "Left the party"); + if (!players[name_to_id[data.name]]) { + return fail_response("player_gone", { name: data.name }); + } + } + requests[requester.name][player.id] = 0; + if (!player.party) { + player.party = player.name; + parties[player.name] = [player.name]; + resend(player, "nc+u+cid"); + delete requests[player.name]; + } + requester.party = player.party; + parties[player.party].push(requester.name); + party_emit(requester.party, "party_update", { + list: parties[player.party], + party: party_to_client(player.party), + message: requester.name + " joined the party", + }); + resend(requester, "nc+u+cid"); + delete requests[requester.name]; + delete requests[player.name]; // optional + } + if (data.event == "leave") { + if (!player.party) { + socket.emit("party_update", {}); + return success_response({}); + } + leave_party(player.party, player); + socket.emit("party_update", {}); + socket.emit("game_log", "Left the party"); + resend(player, "nc+u+cid"); + } + if (data.event == "kick") { + if (!player.party) { + return success_response({}); + } + // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can kick someone"); return; } + if (!in_arr(data.name, parties[player.party])) { + socket.emit("party_update", { list: parties[player.party], party: party_to_client(player.party) }); + return success_response({}); + } + if (parties[player.party].indexOf(player.name) > parties[player.party].indexOf(data.name)) { + return fail_response("cant_kick"); + } + var kicked = players[name_to_id[data.name]]; + if (!kicked) { + return fail_response("player_gone"); + } + leave_party(player.party, kicked); + if (player.party) { + party_emit(player.party, "game_log", player.name + " kicked " + kicked.name); + } + kicked.socket.emit("party_update", {}); + kicked.socket.emit("game_log", "You've been removed from the party"); + resend(kicked, "nc+u+cid"); + } + success_response({}); + }); + socket.on("magiport", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (magiportations[data.name] && magiportations[data.name][player.name] && get_player(data.name)) { + delete magiportations[data.name][player.name]; + if (instances[get_player(data.name).in].mount != instances[player.in].mount) { + return fail_response("cant_in_bank"); + } + if (!magiport_someone(player, get_player(data.name))) { + return fail_response("invalid"); + } + return success_response({}); + } else { + player.socket.emit("game_response", { response: "magiport_gone", name: data.name }); + return fail_response("inviter_gone"); + } + }); + socket.on("trade", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.event == "show" && player.p.trades != 1) { + player.p.trades = 1; + reslot_player(player); + resend(player, "nc+u+cid"); + } else if (data.event == "hide" && player.p.trades != null) { + player.p.trades = null; + reslot_player(player); + resend(player, "nc+u+cid"); + } + }); + socket.on("signup", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!signups[player.name]) { + disappearing_text(false_socket, npcs.bean, "+1", { color: "#4D9A59", xy: 1 }); + } + signups[player.name] = true; + socket.emit("game_response", { response: "signed_up" }); + }); + socket.on("join", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (player.type == "merchant") { + return fail_response("no_merchants"); + } + if (player.s.hopsickness) { + return fail_response("cant_when_sick"); + } + if (player.user) { + return fail_response("cant_in_bank"); + } + if (data.name == "goobrawl" && events.goobrawl) { + if (player.map != "goobrawl") { + transport_player_to(player, "goobrawl"); + } + } else if (data.name == "crabxx" && events.crabxx) { + if (simple_distance(player, { map: "main", x: -1000, y: 1700 }) > 200) { + transport_player_to(player, "main", [-1000, 1700, 0, 40]); + } + } else if (data.name == "franky" && events.franky) { + if (simple_distance(player, { map: "level2w", x: -300, y: 150 }) > 200) { + transport_player_to(player, "level2w", [-300, 150, 0, 40]); + } + } else if (data.name == "icegolem" && events.icegolem) { + if (simple_distance(player, { map: "winterland", x: 820, y: 425 }) > 100) { + transport_player_to(player, "winterland", [820, 425, 0, 10]); + } + } else if (data.name == "abtesting" && E.abtesting) { + if (player.map != "abtesting" || !player.team) { + if ( + ssince(timers.abtesting_start) > 120 && + (!player.p.abtesting || player.p.abtesting[0] != E.abtesting.id) + ) { + return fail_response("join_too_late"); + } + + if (player.p.abtesting && player.p.abtesting[0] == E.abtesting.id) { + player.team = player.p.abtesting[1]; + } else { + player.team = random_one(["A", "B"]); + } + + player.p.abtesting = [E.abtesting.id, player.team]; + + player.team = player.p.abtesting[1]; // transport does a restore ... + save_state(player); + + if (player.team == "A") { + transport_player_to(player, "abtesting", 2); + } else if (player.team == "B") { + transport_player_to(player, "abtesting", 3); + } + + resend(player, "cid"); + } + } else { + return fail_response("cant_join"); + } + success_response(); + }); + socket.on("stop", function (data) { + var player = players[socket.id]; + var change = false; + if (!player) { + return; + } + if (!data || !data.action) { + if (player.s.invis) { + step_out_of_invis(player); + change = true; + } + if (Object.keys(player.c).length) { + player.c = {}; + change = true; + } + } else if (data.action == "invis") { + if (player.s.invis) { + step_out_of_invis(player); + change = true; + } + } else if (data.action == "channeling") { + if (Object.keys(player.c).length) { + player.c = {}; + change = true; + } + } else if (data.action == "teleport" || data.action == "town") { + if (player.c.town) { + delete player.c.town; + change = true; + } + } else if (data.action == "revival") { + if (player.c.revival) { + delete player.c.revival; + change = true; + } + } + if (change) { + resend(player, "u+cid"); + } + success_response(); + }); + socket.on("tarot", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var npc = G.maps[player.map].ref.twitch; + if (!npc || simple_distance(npc, player) > 500) { + return socket.emit("game_response", "distance"); + } + for (var name in player.s) { + if (name.startsWith("tarot")) { + return socket.emit("game_response", "tarot_exists"); + } + } + }); + socket.on("bet", function (data) { + if (!instances.tavern) { + return; + } + var player = players[socket.id]; + if (!player || player.user || (player.map != "tavern" && !is_sdk)) { + return; + } + if (player.s.xshotted) { + return socket.emit("game_response", "bet_xshot"); + } + if (data.type == "roulette") { + if (!is_sdk) { + return; + } // Old routine [23/08/18] + if (!data.odds) { + data.odds = "black"; + } + data.odds = "" + data.odds; + data.gold = max(1, parseInt(data.gold) || 1); + if (!tavern.roulette.odds[data.odds]) { + return; + } + if (tavern.roulette.state != "bets") { + socket.emit("game_log", "Not taking bets yet!"); + return; + } + if (Object.keys(player.bets).length >= 5) { + socket.emit("game_log", "You already have 5 active bets"); + return; + } + if (player.gold < data.gold) { + socket.emit("game_log", "Not enough gold"); + return; + } + + socket.emit("game_log", { message: "Bet accepted on " + data.odds, color: "white" }); + socket.emit("game_log", { message: to_pretty_num(data.gold) + " gold", color: "gray" }); + + var rid = randomStr(10); + player.gold -= data.gold; + player.bets[rid] = { + id: rid, + type: "roulette", + odds: data.odds, + gold: data.gold, + pid: socket.id, + state: "bet", + }; + tavern.roulette.players[player.socket.id] = true; + io.to("roulette").emit("bet", { name: player.name, type: "roulette", odds: data.odds, gold: data.gold }); + socket.emit("tavern", player.bets[rid]); + resend(player, "reopen+nc"); + } + if (data.type == "dice") { + // server_log(JSON.stringify(data)); + var num = min(99.99, max(parseFloat(data.num) || 0, 0.01)); + var gold = max(10000, min(parseInt(data.gold) || 0, 100000000000)); + var odds = 100.0 / num; + var dir = "down"; + if (data.dir == "up") { + odds = 100.0 / (100 - num); + dir = "up"; + } + odds = min(odds, 10000); + odds = parseFloat(floor_f2(odds)); + var win = gold * odds; + var raw = win; + var edge = ceil(((gold * odds - gold) * house_edge()) / 100.0); + win = parseInt(win); + edge = parseInt(edge); + if (tavern.dice.state == "roll") { + return socket.emit("game_response", "tavern_too_late"); + } + if (tavern.dice.state != "bets") { + return socket.emit("game_response", "tavern_not_yet"); + } + if (gold > player.gold) { + return socket.emit("game_response", "gold_not_enough"); + } + if (win - edge - gold > (S.gold - house_debt()) * 0.4) { + return socket.emit("game_response", "tavern_gold_not_enough"); + } + if (Object.keys(player.bets).length) { + return socket.emit("game_response", "tavern_dice_exist"); + } + // if(Object.keys(player.bets).length>=5) return socket.emit("game_response","tavern_too_many_bets"); + // socket.emit("game_log",{"message":"Bet accepted on "+num.toFixed(2)+" "+dir.toUpperCase(),"color":"white"}); + socket.emit("game_log", { message: "Num: " + num.toFixed(2) + " " + dir.toUpperCase(), color: "white" }); + socket.emit("game_log", { message: "Bet: " + to_pretty_num(gold) + " gold", color: "gray" }); + + var rid = randomStr(10); + player.gold -= gold; + player.bets[rid] = { + id: rid, + type: "dice", + num: num, + gold: gold, + dir: dir, + pid: socket.id, + state: "bet", + win: win, + edge: edge, + odds: odds, + }; + tavern.dice.players[player.socket.id] = true; + instance_emit(tavern, "tavern", { + event: "bet", + name: player.name, + type: "dice", + num: num, + gold: gold, + dir: dir, + }); + resend(player, "reopen+nc"); + } + if (data.type == "slots") { + var gold = 1000000; + if (gold > player.gold) { + return socket.emit("game_response", "gold_not_enough"); + } + player.gold -= gold; + S.gold += gold; + player.q.slots = { ms: 3000 }; + xy_emit(player, "ui", { type: "slots", player: player.name }); + socket.emit("game_response", { response: "gold_use", gold: gold, game: data.type }); + resend(player, "u+cid+reopen+nc"); + } + }); + socket.on("tavern", function (data) { + if (data.event == "info") { + socket.emit("tavern", { event: "info", edge: house_edge(), max: parseInt((S.gold - house_debt()) * 0.4) }); + } + }); + socket.on("play", function (data) {}); + socket.on("pet", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!player.monster) { + player.monster = new_monster(player.in, { + type: player.pet, + stype: "pet", + x: player.x, + y: player.y, + owner: player.name, + name: "Skimpy", + }); + } + }); + socket.on("whistle", function (data) { + if (!player.monster) { + return; + } + xy_emit(player.monster, "disappear", { id: player.monster.id }); + player.monster.x = player.x; + player.monster.y = player.y; + player.monster.map = player.map; + player.monster.in = player.in; + player.monster.u = true; + player.monster.cid++; + }); + socket.on("list_pvp", function (data) { + var plist = []; + for (var j = 1; j < 201; j++) { + if (pend - j < 0) { + break; + } + plist.push(pwns[(pend - j) % 200]); + } + socket.emit("pvp_list", { code: data && data.code, list: plist }); + }); + socket.on("players", function (data) { + var player = players[socket.id]; + var sdata = []; + if (!player) { + return; + } // || is_pvp + for (var id in players) { + var current = players[id]; + var mapn = current.map; + current.age = parseInt(ceil(hsince(new Date(current.created)) / 24.0)); + if (is_pvp) { + mapn = "Unknown"; + sdata.push({ + name: "Hidden", + map: mapn, + age: current.age, + level: current.level, + type: current.type, + afk: (current.afk && 1) || 0, + party: (current.party && "Hidden") || "", + kills: current.kills, + }); + } else if (!is_in_pvp(current)) { + sdata.push({ + name: current.name, + map: mapn, + age: current.age, + level: current.level, + type: current.type, + afk: (current.afk && 1) || 0, + party: current.party || "", + }); + } + } + socket.emit("players", sdata); + }); + socket.on("pets", function (data) { + var player = players[socket.id]; + var sdata = []; + if (!player) { + return; + } // || is_pvp + for (var id in player.p.pets || {}) { + sdata.push(player.p.pets[id]); + } + socket.emit("players", sdata); + }); + socket.on("harakiri", function (data) { + var player = players[socket.id]; + if (!player || player.rip) { + return; + } + defeat_player(player); + if (!player.rip) { + rip(player); + } + disappearing_text(player.socket, player, "SEPPUKU", { xy: 1, size: "huge", color: "#6F76A6" }); + resend(player, "u+cid"); + }); + socket.on("deepsea", function (data) { + return; + var player = players[socket.id]; + if (!player || player.rip || player.tskin == "deepsea") { + return; + } + player.tskin = "deepsea"; + disappearing_text(player.socket, player, "ROARRRRRRR", { xy: 1, size: "huge", color: "#60A975" }); + resend(player, "u+cid"); + }); + socket.on("blend", function (data) { + var player = players[socket.id]; + var min = 99999; + var x = null; + if (!player || player.rip) { + return; + } + for (var id in instances[player.in].monsters || {}) { + var m = instances[player.in].monsters[id]; + var c = distance(m, player, true); + if (c < min) { + min = c; + x = m; + } + } + if (x) { + var skin = G.monsters[x.type].skin || x.type; + if (player.tskin != skin) { + player.tskin = skin; + resend(player, "u+cid"); + } + } + }); + socket.on("legacify", function (data) { + var player = players[socket.id]; + if (!player || player.rip) { + return; + } + for (var i = 0; i < 42; i++) { + if (0 && player.items[i] && !player.items[i].p && ["fury", "starkillers"].includes(player.items[i].name)) { + player.items[i].p = "legacy"; + } + } + resend(player, "reopen"); + }); + socket.on("requested_ack", function (data) { + // send "requesting_ack", to verify someone is actually connected [03/08/16] + server_log("requested_ack" + JSON.stringify(data), 1); + }); + socket.on("disconnect", function () { + //#IMPORTANT: disconnect exceptions are fatal [07/08/16] + // console.log("disconnect!"); + var player = players[socket.id]; + var observer = observers[socket.id]; + try { + delete sockets[socket.id]; + } catch (e) {} + if (player) { + player.dc = true; + try { + defeat_player(player); + } catch (e) { + log_trace("#X DCERRORPVP", e); + } + // if(!server.live) { server_log('Ignoring the "disconnect" of '+player.name,1); return; } + + try { + if (player.moving && distance(player, { x: player.going_x, y: player.going_y }) < 800) { + player.x = player.going_x; + player.y = player.going_y; + } + } catch (e) { + log_trace("#X DCERRORM", e); + } + // to not get stuck on out-of-bounds corners + + try { + if (player.party) { + leave_party(player.party, player); + } + } catch (e) { + log_trace("#X DCERROR1", e); + } + + try { + xy_emit(player, "disappear", { id: player.id, reason: "disconnect" }); + } catch (e) { + log_trace("#X DCERROR2", e); + } + + server_log("Disconnected Player: " + socket.id + " " + player.name + " Calls: " + socket.total_calls, 1); + + try { + for (var bid in player.bets) { + player.gold += player.bets[bid].gold; + } + player.bets = {}; + } catch (e) { + log_trace("#X DCERRORBETS", e); + } + + try { + if (player.monster) { + remove_monster(player.monster); + } + } catch (e) { + log_trace("#X DCERRORPETS", e); + } + + try { + delete players[socket.id]; + delete instances[player.in].players[player.id]; + if (instances[player.in].solo == player.id) { + destroy_instance(player.in); + } + pmap_remove(player); + } catch (e) { + log_trace("#X DCERROR3 ", e); + } + + try { + restore_state(player, true); + } catch (e) { + log_trace("#X DCERRORrestore", e); + } + + try { + if (gameplay != "hardcore" && gameplay != "test") { + dc_players[player.real_id] = player; + sync_loop(); + } else { + save_player(player); + } + } catch (e) { + log_trace("#X DCERROR", e); + } + } + if (observer) { + server_log("Disconnected Observer: " + socket.id + " Calls: " + socket.total_calls); + try { + delete_observer(socket); + } catch (e) { + log_trace("#X DCERRORO ", e); + } + } + }); + socket.on("shutdown", function (data) { + if (data.pass != variables.access_master) { + return; + } + if (data.reason) { + broadcast("disconnect_reason", data.reason); + } + setTimeout(shutdown_routine, 10000); + }); + socket.on("notice", function (data) { + if (data.pass != variables.access_master) { + return; + } + broadcast("notice", { message: data.message }); + }); + socket.on("render", function (data) { + if (data.pass != variables.access_master) { + return; + } + var player = players[socket.id]; + var output = ""; + var json_output = undefined; + var window = null; + var after = ""; + try { + eval(data.code); + } catch (e) { + output = "Exception: " + e; + } + if (window) { + socket.emit("simple_eval", { + window: window, + after: after, + code: "for(var id in data.window) window[id]=data.window[id]; eval(data.after);", + }); + } else if (json_output !== undefined) { + socket.emit("simple_eval", { output: output, json_output: json_output, code: "show_json(data.json_output)" }); + } else { + socket.emit("simple_eval", { + output: output, + json_output: json_output, + code: 'show_modal("
"+data.output+"
")', + }); + } + resend(player, "reopen+u+cid"); + }); + socket.on("error", function (data) { + // socket.emit("error") brings server down [16/02/22] + }); + socket.on("eval", function (data) { + if (data.command) { + var player = players[socket.id]; + if (!player) { + return; + } + if (player.map != "cyberland" || player.rip) { + return player.socket.emit("game_log", "Not connected to the mainframe"); + } + if (data.command == "hello") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "hi", + id: "mainframe", + }); + } else if (data.command == "give") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "what?", + id: "mainframe", + }); + } else if (data.command.startsWith("swap")) { + var numbers = data.command.split(" "); + if (numbers.length != 3) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "...", + id: "mainframe", + }); + } else { + var a = parseInt(numbers[1]); + var b = parseInt(numbers[2]); + if (0 <= a && a < 42 && 0 <= b && b < 42) { + if (a == player.p.item_num) { + player.p.item_num = b; + } else if (b == player.p.item_num) { + player.p.item_num = a; + } + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "done", + id: "mainframe", + }); + } else { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "ugh, ok", + id: "mainframe", + }); + } + } + } else if (data.command == "stop") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "mechagnomes assemble", + id: "mainframe", + }); + get_monsters("mechagnome").forEach(function (m) { + if (m.target) { + stop_pursuit(m, { stop: 1, cause: "stop()" }); + } + //else m.irregular=3; + }); + } else if (data.command.startsWith("give") && data.command != "give spares") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "no", + id: "mainframe", + }); + } else if (data.command == "secret web mode" && (player.p.steam_id || player.p.mas_auth_id)) { + player.p.secret_web_mode = true; + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "secret web mode unlocked", + id: "mainframe", + }); + } else if (data.command == "give spares") { + if (S.misc && S.misc.spares && S.misc.spares.length) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "here you go", + id: "mainframe", + }); + drop_one_thing(player, S.misc.spares, { x: 1, y: -88 }); + S.misc.spares = []; + } else { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "come later", + id: "mainframe", + }); + } + } else { + if (!player.supercomputer) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "UNAUTHORIZED COMMAND", + id: "mainframe", + }); + for (var id in instances.cyberland.monsters) { + var monster = instances.cyberland.monsters[id]; + if (!monster.target) { + target_player(monster, player); + } + } + } else { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "UNAUTHORIZED, COMRADE", + id: "mainframe", + }); + } + } + } + if (data.pass == variables.access_master) { + eval(data.code); + } + }); + }); } function add_pdps(player, target, points) { - var pdps_mult = (target && target.pdps_mult) || (target && target.difficulty) || 1; - player.pdps += pdps_mult * points; - player.pdps_mult = pdps_mult; - // console.log([player.pdps,pdps_mult]); + var pdps_mult = (target && target.pdps_mult) || (target && target.difficulty) || 1; + player.pdps += pdps_mult * points; + player.pdps_mult = pdps_mult; + // console.log([player.pdps,pdps_mult]); } function add_coop_points(m, attacker, mnet) { - if (!m) { - return; - } - if (m["1hp"]) { - mnet = 1; - } - if (attacker.s.hopsickness) { - mnet /= 4; - } - m.points[attacker.name] = (m.points[attacker.name] || 0) + mnet; - attacker.s.coop = { ms: 12 * 60 * 1000, id: m.id, p: m.points[attacker.name] }; + if (!m) { + return; + } + if (m["1hp"]) { + mnet = 1; + } + if (attacker.s.hopsickness) { + mnet /= 4; + } + m.points[attacker.name] = (m.points[attacker.name] || 0) + mnet; + attacker.s.coop = { ms: 12 * 60 * 1000, id: m.id, p: m.points[attacker.name] }; } function level_monster(monster, args) { - if (!args) { - args = {}; - } - var mult = 1; - if (args.delevel) { - mult = -1; - } - monster.xp += G.monsters[monster.type].xp * mult; - monster.max_hp += parseInt(G.monsters[monster.type].hp / 2) * mult; - if (mult > 0) { - monster.hp += parseInt(G.monsters[monster.type].hp / 2) * mult; - } else { - monster.hp = min(monster.hp, monster.max_hp); - } - monster.level += 1 * mult; - monster.u = true; - monster.abs = true; - monster.moving = false; - monster.cid++; - monster.last_level = future_s(Math.random() * 100 - 50); - if (mode.fast_mlevels) { - monster.last_level = new Date(); - } - if (!args.silent) { - calculate_monster_stats(monster); - xy_emit(monster, "ui", { id: monster.id, type: "mlevel", mult: mult }); - } - monster.luckx += 0.25 * mult; + if (!args) { + args = {}; + } + var mult = 1; + if (args.delevel) { + mult = -1; + } + monster.xp += G.monsters[monster.type].xp * mult; + monster.max_hp += parseInt(G.monsters[monster.type].hp / 2) * mult; + if (mult > 0) { + monster.hp += parseInt(G.monsters[monster.type].hp / 2) * mult; + } else { + monster.hp = min(monster.hp, monster.max_hp); + } + monster.level += 1 * mult; + monster.u = true; + monster.abs = true; + monster.moving = false; + monster.cid++; + monster.last_level = future_s(Math.random() * 100 - 50); + if (mode.fast_mlevels) { + monster.last_level = new Date(); + } + if (!args.silent) { + calculate_monster_stats(monster); + xy_emit(monster, "ui", { id: monster.id, type: "mlevel", mult: mult }); + } + monster.luckx += 0.25 * mult; } function remove_monster(target, args) { - if (!args) { - args = {}; - } - if (!args.method) { - args.method = "death"; - } - target.dead = true; - target.dc = true; - if ( - G.monsters[target.type].respawn != -1 && - !G.monsters[target.type].special && - !target.special && - !target.map_def.special && - !target.pet && - !target.spawn && - !args.nospawn && - !target.temp - ) { - if (target.map_def.grow && (target.map_def.live || 0) <= (target.map_def.count * 2) / 3) { - setTimeout(new_monster_f(target.oin, target.map_def, { before_respawn: target }), 25); - } else if (G.monsters[target.type].respawn > 200) { - setTimeout( - new_monster_f(target.oin, target.map_def, { before_respawn: target }), - round(G.monsters[target.type].respawn * (720 + Math.random() * 480)), - ); - } else { - setTimeout( - new_monster_f(target.oin, target.map_def, { before_respawn: target }), - round(G.monsters[target.type].respawn * 1000 + Math.random() * 900), - ); - } // previously Math.random()*2000 - } - var luckm = undefined; - if (target.target) { - var player = players[name_to_id[target.target]]; - if (player) { - luckm = player.luckm; - if (!args.no_decrease) { - reduce_targets(player, target); - } - } - } - if (!args.silent) { - xy_emit(target, args.method, { - id: target.id, - luckm: luckm || 1, - points: (target.cooperative && target.points) || undefined, - }); - } - delete instances[target.in].monsters[target.id]; - if (!args.nospawn) { - target.map_def.live--; - monster_c[target.type]--; - } + if (!args) { + args = {}; + } + if (!args.method) { + args.method = "death"; + } + target.dead = true; + target.dc = true; + if ( + G.monsters[target.type].respawn != -1 && + !G.monsters[target.type].special && + !target.special && + !target.map_def.special && + !target.pet && + !target.spawn && + !args.nospawn && + !target.temp + ) { + if (target.map_def.grow && (target.map_def.live || 0) <= (target.map_def.count * 2) / 3) { + setTimeout(new_monster_f(target.oin, target.map_def, { before_respawn: target }), 25); + } else if (G.monsters[target.type].respawn > 200) { + setTimeout( + new_monster_f(target.oin, target.map_def, { before_respawn: target }), + round(G.monsters[target.type].respawn * (720 + Math.random() * 480)), + ); + } else { + setTimeout( + new_monster_f(target.oin, target.map_def, { before_respawn: target }), + round(G.monsters[target.type].respawn * 1000 + Math.random() * 900), + ); + } // previously Math.random()*2000 + } + var luckm = undefined; + if (target.target) { + var player = players[name_to_id[target.target]]; + if (player) { + luckm = player.luckm; + if (!args.no_decrease) { + reduce_targets(player, target); + } + } + } + if (!args.silent) { + xy_emit(target, args.method, { + id: target.id, + luckm: luckm || 1, + points: (target.cooperative && target.points) || undefined, + }); + } + delete instances[target.in].monsters[target.id]; + if (!args.nospawn) { + target.map_def.live--; + monster_c[target.type]--; + } } function new_monster(instance, map_def, args) { - if (!instances[instance]) { - return; - } //otherwise late dungeon spawns bring down the server - if (!args) { - args = {}; - } - var monster = {}; - var id = ++total_monsters + ""; - var name = map_def.type; - monster.id = id; - monster.cid = 1; - monster.mult = 1; // gets reduced during a server restart - monster.m = 0; - monster.outgoing = 0; - monster.u = true; - monster.type = name; - if (args.before_respawn && G.monsters[args.before_respawn.type] && G.monsters[args.before_respawn.type].respawn_as) { - name = monster.type = G.monsters[args.before_respawn.type].respawn_as; - (G.maps[instances[instance].map].monsters || []).forEach(function (pack) { - if (pack.type == name) { - map_def.gold = pack.gold; - } - }); - } - var monster_def = G.monsters[monster.type]; - monster.s = {}; - monster.a = {}; - monster.last = { attacked: new Date() }; - monster.level = 1; - monster.luckx = 1; - monster.xrange = 0; - if (map_def.stype == "randomrespawn") { - var boundary = map_def.boundaries[parseInt(floor(Math.random() * map_def.boundaries.length))]; - if (!server.live) { - boundary = map_def.boundaries[0]; - } // to prevent referencing un-created instances - instance = boundary[0]; - map_def.boundary = [boundary[1], boundary[2], boundary[3], boundary[4]]; - } + if (!instances[instance]) { + return; + } //otherwise late dungeon spawns bring down the server + if (!args) { + args = {}; + } + var monster = {}; + var id = ++total_monsters + ""; + var name = map_def.type; + monster.id = id; + monster.cid = 1; + monster.mult = 1; // gets reduced during a server restart + monster.m = 0; + monster.outgoing = 0; + monster.u = true; + monster.type = name; + if (args.before_respawn && G.monsters[args.before_respawn.type] && G.monsters[args.before_respawn.type].respawn_as) { + name = monster.type = G.monsters[args.before_respawn.type].respawn_as; + (G.maps[instances[instance].map].monsters || []).forEach(function (pack) { + if (pack.type == name) { + map_def.gold = pack.gold; + } + }); + } + var monster_def = G.monsters[monster.type]; + monster.s = {}; + monster.a = {}; + monster.last = { attacked: new Date() }; + monster.level = 1; + monster.luckx = 1; + monster.xrange = 0; + if (map_def.stype == "randomrespawn") { + var boundary = map_def.boundaries[parseInt(floor(Math.random() * map_def.boundaries.length))]; + if (!server.live) { + boundary = map_def.boundaries[0]; + } // to prevent referencing un-created instances + instance = boundary[0]; + map_def.boundary = [boundary[1], boundary[2], boundary[3], boundary[4]]; + } - monster.gold = map_def.gold; + monster.gold = map_def.gold; - if (G.dimensions[name]) { - monster.width = G.dimensions[name][0]; - monster.height = G.dimensions[name][1]; - } - set_base(monster); + if (G.dimensions[name]) { + monster.width = G.dimensions[name][0]; + monster.height = G.dimensions[name][1]; + } + set_base(monster); - if (map_def.random) { - var spot = random_place(instances[instance].map); - monster.x = spot.x; - monster.y = spot.y; - } else if (map_def.stype == "pet") { - monster.pet = true; - monster.x = map_def.x; - monster.y = map_def.y; - monster.owner = map_def.owner; - monster.name = map_def.name; - } else if (map_def.stype == "trap") { - monster.trap = true; - monster.x = map_def.x; - monster.y = map_def.y; - monster.owner = map_def.owner; - } else if (map_def.stype == "spawn") { - monster.spawn = true; - monster.x = map_def.x; - monster.y = map_def.y; - monster.master = map_def.master; - } else if (map_def.polygon) { - var p = random_point(map_def.polygon, monster.base); - var times = 0; - while (!is_xy_safe(instances[instance].map, p[0], p[1])) { - p = random_point(map_def.polygon, monster.base); - times++; - if (times > 40) { - break; - } - } - monster.x = p[0]; - monster.y = p[1]; - } else if (map_def.position) { - monster.x = map_def.position[0] + Math.random() * (2 * map_def.radius) - map_def.radius; - monster.y = map_def.position[1] + Math.random() * (2 * map_def.radius) - map_def.radius; - } else if (map_def.boundary) { - monster.x = map_def.boundary[0] + Math.random() * (map_def.boundary[2] - map_def.boundary[0]); - monster.y = map_def.boundary[1] + Math.random() * (map_def.boundary[3] - map_def.boundary[1]); - } - [ - "speed", - "xp", - "hp", - "attack", - "range", - "frequency", - "damage_type", - "aggro", - "evasion", - "avoidance", - "reflection", - "armor", - "resistance", - "dreturn", - "rage", - "apiercing", - "rpiercing", - "immune", - "cooperative", - "peaceful", - "drop_on_hit", - "global", - "1hp", - "heal", - "spawns", - "lifesteal", - "manasteal", - "rbuff", - "cbuff", - "projectile", - "slots", - "crit", - "humanoid", - "explosion", - "for", - "difficulty", - "phresistance", - ].forEach(function (prop) { - if (prop in monster_def) { - monster[prop] = monster_def[prop]; - } - }); - monster.mp = ceil((monster.hp * 2) / 100); - if (monster_def.s) { - monster.s = clone(monster_def.s); - } - if (monster_def.abilities) { - monster.a = monster_def.abilities; - } - if (monster_def.poisonous) { - monster.s.poisonous = { ms: 99999999999999 }; - } - if (mode.range_test) { - monster.range = 2000; - } - monster.width = monster.height = 24; - if (args.temp) { - monster.temp = 1; - } - if (map_def.rage) { - monster.rid = map_def.id; - } - monster.points = {}; - for (var a in monster.a) { - if (monster.a[a].cooldown) { - monster.s[a] = { ms: parseInt(monster.a[a].cooldown * Math.random()), ability: true }; - } - } - monster.s.young = { ms: 500 }; - monster.is_monster = true; - monster.max_hp = monster_def.hp; - monster.oin = instance; - monster.in = instance; - monster.map = instances[instance].map; - monster.map_def = map_def; - monster.last_level = future_s(Math.random() * 100 - 50); - if (mode.fast_mlevels) { - monster.last_level = new Date(); - } - monster.last.attack = new Date(); - monster.last.attacked = really_old; - monster.hits = 0; - if (args.last_state) { - while (monster.level < args.last_state.level) { - level_monster(monster, { silent: true }); - } - ["hp", "level", "s", "temp", "points", "m", "extra_gold", "outgoing", "id"].forEach(function (p) { - if (args.last_state[p]) { - monster[p] = args.last_state[p]; - } - }); - calculate_monster_stats(monster); - } - if (args.before_respawn) { - while (monster.level < args.before_respawn.level / 2) { - level_monster(monster, { silent: true }); - } - calculate_monster_stats(monster); - } - if (monster.aggro) { - monster.last_aggro = new Date(); - } - instances[instance].monsters[monster.id] = monster; - if (!args.last_state) { - map_def.live = (map_def.live || 0) + 1; - monster_def.c = (monster_def.c || 0) + 1; - monster_c[monster.type] = (monster_c[monster.type] || 0) + 1; - } - if (map_def.grow && server.live && map_def.live <= map_def.count / 2) { - new_monster(instance, map_def, { temp: 1 }); - new_monster(instance, map_def, { temp: 1 }); - } - if (!args.last_state && monster_def.announce && server.live) { - broadcast("server_message", { - color: monster_def.announce, - message: monster_def.name + " spawned in " + G.maps[monster.map].name + "!", - discord: "orange", - }); - if (monster.cooperative) { - broadcast("server_message", { - color: monster_def.announce, - message: "Join the fight against " + monster_def.name + "!", - event: true, - }); - } - } - if (Object.keys(monster.s).length) { - calculate_monster_stats(monster); - } - if (map_def.stype == "spawn") { - target_player(monster, get_player(map_def.target)); - } - return monster; + if (map_def.random) { + var spot = random_place(instances[instance].map); + monster.x = spot.x; + monster.y = spot.y; + } else if (map_def.stype == "pet") { + monster.pet = true; + monster.x = map_def.x; + monster.y = map_def.y; + monster.owner = map_def.owner; + monster.name = map_def.name; + } else if (map_def.stype == "trap") { + monster.trap = true; + monster.x = map_def.x; + monster.y = map_def.y; + monster.owner = map_def.owner; + } else if (map_def.stype == "spawn") { + monster.spawn = true; + monster.x = map_def.x; + monster.y = map_def.y; + monster.master = map_def.master; + } else if (map_def.polygon) { + var p = random_point(map_def.polygon, monster.base); + var times = 0; + while (!is_xy_safe(instances[instance].map, p[0], p[1])) { + p = random_point(map_def.polygon, monster.base); + times++; + if (times > 40) { + break; + } + } + monster.x = p[0]; + monster.y = p[1]; + } else if (map_def.position) { + monster.x = map_def.position[0] + Math.random() * (2 * map_def.radius) - map_def.radius; + monster.y = map_def.position[1] + Math.random() * (2 * map_def.radius) - map_def.radius; + } else if (map_def.boundary) { + monster.x = map_def.boundary[0] + Math.random() * (map_def.boundary[2] - map_def.boundary[0]); + monster.y = map_def.boundary[1] + Math.random() * (map_def.boundary[3] - map_def.boundary[1]); + } + [ + "speed", + "xp", + "hp", + "attack", + "range", + "frequency", + "damage_type", + "aggro", + "evasion", + "avoidance", + "reflection", + "armor", + "resistance", + "dreturn", + "rage", + "apiercing", + "rpiercing", + "immune", + "cooperative", + "peaceful", + "drop_on_hit", + "global", + "1hp", + "heal", + "spawns", + "lifesteal", + "manasteal", + "rbuff", + "cbuff", + "projectile", + "slots", + "crit", + "humanoid", + "explosion", + "for", + "difficulty", + "phresistance", + ].forEach(function (prop) { + if (prop in monster_def) { + monster[prop] = monster_def[prop]; + } + }); + monster.mp = ceil((monster.hp * 2) / 100); + if (monster_def.s) { + monster.s = clone(monster_def.s); + } + if (monster_def.abilities) { + monster.a = monster_def.abilities; + } + if (monster_def.poisonous) { + monster.s.poisonous = { ms: 99999999999999 }; + } + if (mode.range_test) { + monster.range = 2000; + } + monster.width = monster.height = 24; + if (args.temp) { + monster.temp = 1; + } + if (map_def.rage) { + monster.rid = map_def.id; + } + monster.points = {}; + for (var a in monster.a) { + if (monster.a[a].cooldown) { + monster.s[a] = { ms: parseInt(monster.a[a].cooldown * Math.random()), ability: true }; + } + } + monster.s.young = { ms: 500 }; + monster.is_monster = true; + monster.max_hp = monster_def.hp; + monster.oin = instance; + monster.in = instance; + monster.map = instances[instance].map; + monster.map_def = map_def; + monster.last_level = future_s(Math.random() * 100 - 50); + if (mode.fast_mlevels) { + monster.last_level = new Date(); + } + monster.last.attack = new Date(); + monster.last.attacked = really_old; + monster.hits = 0; + if (args.last_state) { + while (monster.level < args.last_state.level) { + level_monster(monster, { silent: true }); + } + ["hp", "level", "s", "temp", "points", "m", "extra_gold", "outgoing", "id"].forEach(function (p) { + if (args.last_state[p]) { + monster[p] = args.last_state[p]; + } + }); + calculate_monster_stats(monster); + } + if (args.before_respawn) { + while (monster.level < args.before_respawn.level / 2) { + level_monster(monster, { silent: true }); + } + calculate_monster_stats(monster); + } + if (monster.aggro) { + monster.last_aggro = new Date(); + } + instances[instance].monsters[monster.id] = monster; + if (!args.last_state) { + map_def.live = (map_def.live || 0) + 1; + monster_def.c = (monster_def.c || 0) + 1; + monster_c[monster.type] = (monster_c[monster.type] || 0) + 1; + } + if (map_def.grow && server.live && map_def.live <= map_def.count / 2) { + new_monster(instance, map_def, { temp: 1 }); + new_monster(instance, map_def, { temp: 1 }); + } + if (!args.last_state && monster_def.announce && server.live) { + broadcast("server_message", { + color: monster_def.announce, + message: monster_def.name + " spawned in " + G.maps[monster.map].name + "!", + discord: "orange", + }); + if (monster.cooperative) { + broadcast("server_message", { + color: monster_def.announce, + message: "Join the fight against " + monster_def.name + "!", + event: true, + }); + } + } + if (Object.keys(monster.s).length) { + calculate_monster_stats(monster); + } + if (map_def.stype == "spawn") { + target_player(monster, get_player(map_def.target)); + } + return monster; } function new_monster_f(instance, map_def, args) { - return function () { - try { - new_monster(instance, map_def, args); - } catch (e) { - log_trace("#X Critical-new_monster", e); - } - }; + return function () { + try { + new_monster(instance, map_def, args); + } catch (e) { + log_trace("#X Critical-new_monster", e); + } + }; } function start_moving_element(monster) { - // var last.move=monster.moving&&monster.last.move; - if (!monster.moving) { - monster.last.move = new Date(); - } // VERY VERY VERY VERY IMPORTANT - This was the cause of the server/client position discrepancies [08/01/16] - monster.moving = true; - monster.abs = false; - monster.u = true; - monster.from_x = monster.x; - monster.from_y = monster.y; - monster.move_num = total_moves++; - calculate_vxy(monster); + // var last.move=monster.moving&&monster.last.move; + if (!monster.moving) { + monster.last.move = new Date(); + } // VERY VERY VERY VERY IMPORTANT - This was the cause of the server/client position discrepancies [08/01/16] + monster.moving = true; + monster.abs = false; + monster.u = true; + monster.from_x = monster.x; + monster.from_y = monster.y; + monster.move_num = total_moves++; + calculate_vxy(monster); } function send_xy_updates(player, list) { - // third version, very refined - deleted older versions after the "instances" commit - // #NOTE: this routine is the bottlenck, unclear whether anything can be done about it, test with mode.nopush [31/07/18] - if (mode.noxy) { - return; - } - // list includes to_push - everything in that instance that are updated - var data = { players: [], monsters: [], type: "xy", in: player.in, map: player.map }; - var m_mark = {}; - var p_count = 0; - list.forEach(function (def) { - if (!is_invis(def.entity) && within_xy_range(player, def.entity) && player.id != def.entity.id) { - if (def.entity.is_monster) { - if (player.push) { - m_mark[def.entity.id] = 1; - } - data.monsters.push(def.data); - } else { - if (player.push) { - m_mark[def.entity.id] = 1; - } - data.players.push(def.data); - } - } - }); - perfc.sxyu += list.length; - if (!mode.nopush && player.push) { - // player is moving and receiving/seeing new entities - //#GTODO: Maybe also calculate and factor in the monster/player dx/dy [27/08/16] - var log_push = mode.upush_test; - var dx = player.x - player.push[0]; - var dy = player.y - player.push[1]; - var avoid = { x: player.push[0] - dx * 0.4, y: player.push[1] - dy * 0.4, vision: player.vision, in: player.in }; - // on main1, when you put a merchant on the town's bottom limit, and move upwards from the first island to the town, the merchant is lost with the avoid logic - var grab = { x: player.push[0] + dx * 1.4, y: player.push[1] + dy * 1.4, vision: player.vision, in: player.in }; - if (log_push) { - server_log(JSON.stringify(grab) + " dx/dy " + dx + " " + dy); - } - for (var id in instances[player.in].monsters) { - if (m_mark[instances[player.in].monsters[id].id]) { - continue; - } - var monster = instances[player.in].monsters[id]; - if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { - data.monsters.push(monster_to_client(monster)); - p_count++; - } - } - for (var id in instances[player.in].players) { - if ( - m_mark[instances[player.in].players[id].id] || - instances[player.in].players[id].id == player.id || - is_invis(instances[player.in].players[id]) - ) { - continue; - } - var monster = instances[player.in].players[id]; - if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { - data.players.push(player_to_client(monster, 1)); - p_count++; - } - } - perfc.sxyu += Object.keys(instances[player.in].players).length + Object.keys(instances[player.in].monsters).length; - if (log_push) { - server_log("push done: " + p_count); - } - player.push = false; - } - if (data.players.length || data.monsters.length || (mode.xyinf && player.moving)) { - if (mode.xyinf && player.moving) { - data.xy = { x: player.x, y: player.y }; - } // to synchronise the client speed to match the server displacement [07/01/16] - player.socket.emit("entities", data); - } + // third version, very refined - deleted older versions after the "instances" commit + // #NOTE: this routine is the bottlenck, unclear whether anything can be done about it, test with mode.nopush [31/07/18] + if (mode.noxy) { + return; + } + // list includes to_push - everything in that instance that are updated + var data = { players: [], monsters: [], type: "xy", in: player.in, map: player.map }; + var m_mark = {}; + var p_count = 0; + list.forEach(function (def) { + if (!is_invis(def.entity) && within_xy_range(player, def.entity) && player.id != def.entity.id) { + if (def.entity.is_monster) { + if (player.push) { + m_mark[def.entity.id] = 1; + } + data.monsters.push(def.data); + } else { + if (player.push) { + m_mark[def.entity.id] = 1; + } + data.players.push(def.data); + } + } + }); + perfc.sxyu += list.length; + if (!mode.nopush && player.push) { + // player is moving and receiving/seeing new entities + //#GTODO: Maybe also calculate and factor in the monster/player dx/dy [27/08/16] + var log_push = mode.upush_test; + var dx = player.x - player.push[0]; + var dy = player.y - player.push[1]; + var avoid = { x: player.push[0] - dx * 0.4, y: player.push[1] - dy * 0.4, vision: player.vision, in: player.in }; + // on main1, when you put a merchant on the town's bottom limit, and move upwards from the first island to the town, the merchant is lost with the avoid logic + var grab = { x: player.push[0] + dx * 1.4, y: player.push[1] + dy * 1.4, vision: player.vision, in: player.in }; + if (log_push) { + server_log(JSON.stringify(grab) + " dx/dy " + dx + " " + dy); + } + for (var id in instances[player.in].monsters) { + if (m_mark[instances[player.in].monsters[id].id]) { + continue; + } + var monster = instances[player.in].monsters[id]; + if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { + data.monsters.push(monster_to_client(monster)); + p_count++; + } + } + for (var id in instances[player.in].players) { + if ( + m_mark[instances[player.in].players[id].id] || + instances[player.in].players[id].id == player.id || + is_invis(instances[player.in].players[id]) + ) { + continue; + } + var monster = instances[player.in].players[id]; + if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { + data.players.push(player_to_client(monster, 1)); + p_count++; + } + } + perfc.sxyu += Object.keys(instances[player.in].players).length + Object.keys(instances[player.in].monsters).length; + if (log_push) { + server_log("push done: " + p_count); + } + player.push = false; + } + if (data.players.length || data.monsters.length || (mode.xyinf && player.moving)) { + if (mode.xyinf && player.moving) { + data.xy = { x: player.x, y: player.y }; + } // to synchronise the client speed to match the server displacement [07/01/16] + player.socket.emit("entities", data); + } } function step_out_of_invis(player) { - if (player.s.invincible) { - player.s.invincible = { ms: 0 }; - } - if (!player.s.invis) { - return; - } - delete player.s.invis; - player.u = true; - consume_skill(player, "invis", true); - resend(player); + if (player.s.invincible) { + player.s.invincible = { ms: 0 }; + } + if (!player.s.invis) { + return; + } + delete player.s.invis; + player.u = true; + consume_skill(player, "invis", true); + resend(player); } function reduce_targets(player, monster) { - if (is_string(player)) { - player = players[name_to_id[player]]; - } - if (!player) { - return; - } - player.targets--; - if (player.targets < 0) { - player.targets = 0; - } + if (is_string(player)) { + player = players[name_to_id[player]]; + } + if (!player) { + return; + } + player.targets--; + if (player.targets < 0) { + player.targets = 0; + } - var p = "targets_p"; - if (monster.damage_type == "magical") { - p = "targets_m"; - } else if (monster.damage_type == "pure") { - p = "targets_u"; - } - player[p]--; - if (player[p] < 0) { - player[p] = 0; - } + var p = "targets_p"; + if (monster.damage_type == "magical") { + p = "targets_m"; + } else if (monster.damage_type == "pure") { + p = "targets_u"; + } + player[p]--; + if (player[p] < 0) { + player[p] = 0; + } - resend(player); + resend(player); } function increase_targets(player, monster) { - if (!player) { - return; - } - player.targets++; - player.to_resend = " "; + if (!player) { + return; + } + player.targets++; + player.to_resend = " "; - var p = "targets_p"; - if (monster.damage_type == "magical") { - p = "targets_m"; - } else if (monster.damage_type == "pure") { - p = "targets_u"; - } - player[p]++; + var p = "targets_p"; + if (monster.damage_type == "magical") { + p = "targets_m"; + } else if (monster.damage_type == "pure") { + p = "targets_u"; + } + player[p]++; - resend(player); // this needs to be added, or all usages need to be re-visited + resend(player); // this needs to be added, or all usages need to be re-visited } function port_monster(monster, target, extras) { - if (is_disabled(monster)) { - return false; - } - var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); - if (!spot || instances[target.in].solo) { - return false; - } - pulled = monster; - pulled.s.magiport = { ms: (monster == target && 80) || 400 }; - pulled.s.magiport.x = spot.x; - pulled.s.magiport.y = spot.y; - pulled.s.magiport.f = target.name; - pulled.s.magiport.in = target.in; - pulled.s.magiport.map = target.map; - for (var id in extras) { - pulled.s.magiport[id] = extras[id]; - } - monster.abs = true; - monster.moving = false; - monster.m++; - monster.cid++; - monster.u = true; - return true; + if (is_disabled(monster)) { + return false; + } + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (!spot || instances[target.in].solo) { + return false; + } + pulled = monster; + pulled.s.magiport = { ms: (monster == target && 80) || 400 }; + pulled.s.magiport.x = spot.x; + pulled.s.magiport.y = spot.y; + pulled.s.magiport.f = target.name; + pulled.s.magiport.in = target.in; + pulled.s.magiport.map = target.map; + for (var id in extras) { + pulled.s.magiport[id] = extras[id]; + } + monster.abs = true; + monster.moving = false; + monster.m++; + monster.cid++; + monster.u = true; + return true; } function stop_pursuit(monster, args) { - // if(is_sdk && monster.target && !(args && args.cause)) console.log("stop_pursuit: "+monster.target); - if (!args) { - args = {}; - } - if (monster.target) { - var target = players[name_to_id[monster.target]]; - if ( - !args.redirect && - !args.force && - !args.stop && - target && - !is_invis(target) && - !target.rip && - monster.a.portal && - port_monster(monster, target) - ) { - return; - } - monster.target = null; - if (monster.master && target && !args.redirect) { - if ( - instances[monster.in].monsters[monster.master] && - instances[monster.in].monsters[monster.master].cooperative - ) { - add_coop_points(instances[monster.in].monsters[monster.master], target, max(monster.hp, 300)); - } - } - reduce_targets(target, monster); - } - if (monster.spawn && !args.redirect) { - return remove_monster(monster, { method: "disappear" }); - } - if (is_sdk && args && args.cause) { - console.log("stop_pursuit: " + args.cause); - } - monster.last_level = future_s(Math.random() * 100 - 50); - monster.cid++; - monster.u = true; - monster.irregular = 2; - calculate_monster_stats(monster); - xy_emit(monster, "ui", { id: monster.id, type: "disengage", event: true }); + // if(is_sdk && monster.target && !(args && args.cause)) console.log("stop_pursuit: "+monster.target); + if (!args) { + args = {}; + } + if (monster.target) { + var target = players[name_to_id[monster.target]]; + if ( + !args.redirect && + !args.force && + !args.stop && + target && + !is_invis(target) && + !target.rip && + monster.a.portal && + port_monster(monster, target) + ) { + return; + } + monster.target = null; + if (monster.master && target && !args.redirect) { + if ( + instances[monster.in].monsters[monster.master] && + instances[monster.in].monsters[monster.master].cooperative + ) { + add_coop_points(instances[monster.in].monsters[monster.master], target, max(monster.hp, 300)); + } + } + reduce_targets(target, monster); + } + if (monster.spawn && !args.redirect) { + return remove_monster(monster, { method: "disappear" }); + } + if (is_sdk && args && args.cause) { + console.log("stop_pursuit: " + args.cause); + } + monster.last_level = future_s(Math.random() * 100 - 50); + monster.cid++; + monster.u = true; + monster.irregular = 2; + calculate_monster_stats(monster); + xy_emit(monster, "ui", { id: monster.id, type: "disengage", event: true }); } function defeated_by_a_monster(attacker, player) { - var divider = 1; - if (is_in_pvp(player) && !(!is_pvp && G.maps[player.map].safe_pvp)) { - divider = 10; - } - var lost_xp = floor(min(max((player.max_xp * 0.01) / divider, (player.xp * 0.02) / divider), player.xp)); - // Originally all of them are divided by player.xpm [05/06/19] - for (var i = 0; i < player.isize; i++) { - if (player.items[i] && player.items[i].name == "xptome") { - lost_xp = floor(lost_xp / 50); - consume_one(player, i); - player.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); - break; - } - } - player.xp -= lost_xp; - if (gameplay == "hardcore") { - player.socket.emit("game_log", "Lost 1 level"); - } - player.socket.emit("game_response", { response: "defeated_by_a_monster", xp: lost_xp, monster: attacker.type }); - if (attacker.target) { - stop_pursuit(attacker, { force: true, cause: "defeat" }); - } - rip(player); - if (gameplay == "hardcore") { - player.level = max(1, player.level - 1); - player.xp = 0; - } - lost_xp /= 12; - if (player.type == "merchant") { - lost_xp = 0; - } - var mult = 1; - if (attacker["1hp"]) { - mult = 500; - } else if (G.monsters[attacker.type] && G.monsters[attacker.type].special) { - mult = 20; - } - while (lost_xp > attacker.max_hp * 2.4 * mult) { - lost_xp -= attacker.max_hp * 2.4 * mult; - lost_xp *= 0.9; - if (!attacker.dead) { - level_monster(attacker); - } - } + var divider = 1; + if (is_in_pvp(player) && !(!is_pvp && G.maps[player.map].safe_pvp)) { + divider = 10; + } + var lost_xp = floor(min(max((player.max_xp * 0.01) / divider, (player.xp * 0.02) / divider), player.xp)); + // Originally all of them are divided by player.xpm [05/06/19] + for (var i = 0; i < player.isize; i++) { + if (player.items[i] && player.items[i].name == "xptome") { + lost_xp = floor(lost_xp / 50); + consume_one(player, i); + player.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); + break; + } + } + player.xp -= lost_xp; + if (gameplay == "hardcore") { + player.socket.emit("game_log", "Lost 1 level"); + } + player.socket.emit("game_response", { response: "defeated_by_a_monster", xp: lost_xp, monster: attacker.type }); + if (attacker.target) { + stop_pursuit(attacker, { force: true, cause: "defeat" }); + } + rip(player); + if (gameplay == "hardcore") { + player.level = max(1, player.level - 1); + player.xp = 0; + } + lost_xp /= 12; + if (player.type == "merchant") { + lost_xp = 0; + } + var mult = 1; + if (attacker["1hp"]) { + mult = 500; + } else if (G.monsters[attacker.type] && G.monsters[attacker.type].special) { + mult = 20; + } + while (lost_xp > attacker.max_hp * 2.4 * mult) { + lost_xp -= attacker.max_hp * 2.4 * mult; + lost_xp *= 0.9; + if (!attacker.dead) { + level_monster(attacker); + } + } } function can_attack(monster, player) { - if (is_disabled(monster)) { - return false; - } - if (player == "aggro") { - return ( - mssince(monster.last_aggro) > max(1200, 1200 / monster.frequency) && - mssince(monster.last.attack) > 1000 / monster.frequency - ); - } //aggro check is arbitrary - if (!player) { - return mssince(monster.last.attack) > 1000 / monster.frequency; - } - return distance(player, monster, true) < monster.range && mssince(monster.last.attack) > 1000 / monster.frequency; + if (is_disabled(monster)) { + return false; + } + if (player == "aggro") { + return ( + mssince(monster.last_aggro) > max(1200, 1200 / monster.frequency) && + mssince(monster.last.attack) > 1000 / monster.frequency + ); + } //aggro check is arbitrary + if (!player) { + return mssince(monster.last.attack) > 1000 / monster.frequency; + } + return distance(player, monster, true) < monster.range && mssince(monster.last.attack) > 1000 / monster.frequency; } function rage_logic(instance) { - if (!instance.rage_list.length || (instance.last_rage && mssince(instance.last_rage) < 4200)) { - return; - } - instance.last_rage = new Date(); - instance.rage_list.forEach(function (map_def) { - for (var id in instance.players) { - var player = instance.players[id]; - if ( - player && - !player.npc && - !player.rip && - !is_invinc(player) && - map_def.rage[0] <= player.x && - player.x <= map_def.rage[2] && - map_def.rage[1] <= player.y && - player.y <= map_def.rage[3] - ) { - if (Math.random() < player.aggro_diff) { - return; - } - for (var id in instance.monsters) { - var monster = instance.monsters[id]; - if (monster.rid == map_def.id) { - if ( - (!monster.s.sleeping && !monster.target) || - (monster.target && get_player(monster.target) && !is_same(player, get_player(monster.target), 1)) - ) { - if (monster.target && get_player(monster.target)) { - stop_pursuit(monster, { redirect: true, cause: "redirect" }); - } - target_player(monster, player); - monster.walk_once = true; - } - } - } - return; - } - } - }); + if (!instance.rage_list.length || (instance.last_rage && mssince(instance.last_rage) < 4200)) { + return; + } + instance.last_rage = new Date(); + instance.rage_list.forEach(function (map_def) { + for (var id in instance.players) { + var player = instance.players[id]; + if ( + player && + !player.npc && + !player.rip && + !is_invinc(player) && + map_def.rage[0] <= player.x && + player.x <= map_def.rage[2] && + map_def.rage[1] <= player.y && + player.y <= map_def.rage[3] + ) { + if (Math.random() < player.aggro_diff) { + return; + } + for (var id in instance.monsters) { + var monster = instance.monsters[id]; + if (monster.rid == map_def.id) { + if ( + (!monster.s.sleeping && !monster.target) || + (monster.target && get_player(monster.target) && !is_same(player, get_player(monster.target), 1)) + ) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster, { redirect: true, cause: "redirect" }); + } + target_player(monster, player); + monster.walk_once = true; + } + } + } + return; + } + } + }); } function update_instance(instance) { - if (instance.paused) { - return; - } - instance.operators = 0; - var ms = mssince(instance.last_update); - instance.last_update = new Date(); - rage_logic(instance); - var to_push = []; - var monster_map = {}; - var now_date = instance.last_update; - var aggressives = {}; - var targets = {}; - for (var id in instance.monsters) { - var monster = instance.monsters[id]; - var events = []; - var change = false; - var def = monster.type; - if ((monster.target && monster.a.portal) || G.monsters[monster.type].operator) { - instance.operators += 1; - } - var focus = instance.monsters[monster.focus]; - if ((monster.focus && !instance.monsters[monster.focus]) || (focus && distance(monster, focus) > 380)) { - focus = monster.focus = null; - monster.cid++; - monster.u = true; - change = true; - } - if (monster.focus) { - change = true; - } // better to re-calculate for now, for charge speed changes - for (var name in monster.s) { - var def = G.conditions[name]; - var ref = monster.s[name]; - var value = monster.s[name].ms; - monster.s[name].ms -= ms; - if (def && def.interval) { - if (!monster.s[name].last || mssince(monster.s[name].last) >= def.interval) { - monster.s[name].last = new Date(); - if (name == "eburn") { - var damage = G.conditions.eburn.damage; - if (monster.immune) { - damage = 0; - } - disappearing_text({}, monster, "-" + damage, { color: "red", xy: 1 }); - monster.hp = max(1, monster.hp - damage); - } - if (name == "eheal") { - var heal = G.conditions.eheal.heal; - if (monster.immune) { - heal = 0; - } - disappearing_text({}, monster, "+" + heal, { color: "heal", xy: 1 }); - monster.hp = min(monster.max_hp, monster.hp + heal); - } - if (name == "burned") { - var damage = ceil(ref.intensity / 5); - //disappearing_text({},monster,"-"+damage,{color:"burn",xy:1}); - monster.hp = max(0, monster.hp - damage); - xy_emit(monster, "hit", { - source: "burn", - hid: ref.f, - id: monster.id, - damage: damage, - kill: monster.hp <= 0, - }); - var attacker = (monster.target && get_player(monster.target)) || get_player(ref.f); - var burner = get_player(ref.f); - if (burner) { - add_pdps(burner, monster, damage); - if (monster.cooperative) { - add_coop_points(monster, burner, damage); - } - } - if (monster.hp <= 0) { - if (burner) { - achievement_logic_burn_last_hit(burner); - } - kill_monster(attacker, monster); - } - } - monster.u = true; - monster.cid++; - } - } - if (monster.s[name].ms <= 0) { - if (monster.a[name] && monster.a[name].cooldown) { - monster.s[name].ms = monster.a[name].cooldown; - } else { - delete monster.s[name]; - } - if (is_disabled(monster) && G.skills[name] && !G.skills[name].passive) { - continue; - } - if (name != "young") { - monster.u = true; - monster.cid++; - change = true; - } - if (name == "self_healing") { - var hp = monster.hp; - var heal = monster.a.self_healing.heal; - if (monster.s.poisoned) { - heal /= 2; - } - monster.hp = min(monster.max_hp, monster.hp + heal); - if (hp != monster.hp) { - events.push(["ui", { type: "mheal", id: id, heal: monster.hp - hp }]); - } - } - if (name == "healing") { - var target = monster; - if (focus && distance(focus, monster) < 120) { - target = focus; - } - var hp = target.hp; - var heal = monster.a.healing.heal; - if (target.s.poisoned) { - heal /= 2; - } - target.hp = min(target.max_hp, target.hp + heal); - if (hp != target.hp) { - events.push(["ui", { type: "mheal", id: target.id, heal: target.hp - hp }]); - } - } - if (name == "mtangle") { - if (monster.target && get_player(monster.target)) { - var player = get_player(monster.target); - add_condition(player, "tangled"); - resend(player, "u+cid"); - } - } - if (name == "multi_burn") { - if (monster.cooperative) { - for (var name in monster.points || {}) { - var player = get_player(name); - if (player && simple_distance(monster, player) < 480) { - commence_attack(monster, player, "fireball"); - } - } - } else { - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (distance(player, monster) < 480) { - commence_attack(monster, player, "fireball"); - } - } - } - monster.cid++; - monster.u = true; - change = true; - } - if (name == "multi_freeze") { - for (var name in monster.points || {}) { - var player = get_player(name); - if (player && simple_distance(monster, player) < 480) { - commence_attack(monster, player, "frostball"); - } - } - monster.cid++; - monster.u = true; - change = true; - } - if (name == "degen") { - monster.hp -= 60; - monster.cid++; - monster.u = true; - change = true; - if (monster.hp <= 0) { - monster.hp = 0; - remove_monster(monster); - } - } - if (name == "zap") { - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (distance(player, monster) < monster.a[name].radius) { - commence_attack(monster, player, "zap"); - } - } - } - if (monster.a && monster.a[name] && monster.a[name].aura) { - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (distance(player, monster) < monster.a[name].radius) { - player.s[monster.a[name].condition] = { ms: G.conditions[monster.a[name].condition].duration }; - resend(player, "u+cid"); - } - } - } - if (name == "deepfreeze") { - var c = []; - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius && !player.npc) { - c.push(player); - } - } - var theone = random_one(c); - if (theone) { - commence_attack(monster, theone, "deepfreeze"); - } - } - if (name == "anger") { - var c = []; - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { - c.push(player); - } - } - var theone = random_one(c); - if (theone) { - if (monster.target && get_player(monster.target)) { - stop_pursuit(monster); - } - target_player(monster, theone); - } - } - if (name == "warpstomp") { - var dampened = false; - for (var id in instances[monster.in].monsters) { - var m = instances[monster.in].monsters[id]; - if (m.type == "fieldgen0" && point_distance(monster.x, monster.y, m.x, m.y) < 300) { - monster.s.dampened = { ms: 2000 }; - dampened = true; - } - } - if (!dampened) { - var c = []; - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { - c.push(player); - } - } - var theone = random_one(c); - if (theone) { - if (monster.target && get_player(monster.target)) { - stop_pursuit(monster); - } - target_player(monster, theone); - port_monster(monster, theone, { stomp: 160 }); - } - } - } - if (name == "mlight") { - xy_emit(monster, "light", { name: monster.id }); - } - if (name == "stone") { - if (monster.target && get_player(monster.target)) { - var player = get_player(monster.target); - add_condition(player, "stoned"); - resend(player, "u+cid"); - } - } - if (name == "magiport") { - var r = false; - if (monster.map != ref.map) { - r = true; - } - transport_monster_to(monster, ref.in, ref.map, ref.x, ref.y); - if (ref.stomp) { - for (var id in instances[monster.in].players) { - var target = instances[monster.in].players[id]; - var dist = simple_distance(monster, target); - if ( - dist < 160 && - !target.npc && - !target.s.invincible && - add_condition(target, "stunned", { duration: 1500 }) - ) { - resend(target); - } - } - } - if (r) { - continue; - } - } - if (name == "sleeping" && E.schedule.night && Math.random() < 0.9) { - monster.s.sleeping = { ms: 3000 + 5000 * Math.random() }; - monster.u = true; - monster.cid++; - } - } - } - if (monster.dead) { - continue; - } - if (G.monsters[monster.type].supporter && !monster.focus) { - for (var mid in instance.monsters) { - var m = instance.monsters[mid]; - if ( - !m.focus && - m != monster && - G.monsters[monster.type].humanoid == G.monsters[m.type].humanoid && - distance(m, monster) < 300 - ) { - monster.focus = m.id; - change = true; - break; - } - } - } - if (change) { - calculate_monster_stats(monster); - } - if ( - !monster.pet && - !monster.trap && - mode.aggro && - !monster.target && - monster.aggro && - can_attack(monster, "aggro") - ) { - monster.last_aggro = new Date(); - if (monster.aggro > 0.99 || Math.random() < monster.aggro) { - set_ghash(aggressives, monster, 32); - } - } - if (monster.target && monster.spawns && get_player(monster.target) && !is_disabled(monster)) { - monster.spawns.forEach(function (spi) { - var interval = spi[0]; - var name = spi[1]; - if (!monster.last[name] || mssince(monster.last[name]) > interval) { - var pname = random_one(Object.keys(monster.points)); - var player = get_player(pname); - if (!player || player.npc || distance(monster, player) > 400) { - return; - } - if (!is_same(player, get_player(monster.target), true)) { - return; - } - monster.last[name] = new Date(); - var spot = safe_xy_nearby(player.map, player.x + Math.random() * 20 - 10, player.y + Math.random() * 20 - 10); - if (!spot) { - return; - } - new_monster(instance.name, { - type: name, - stype: "spawn", - x: spot.x, - y: spot.y, - target: player.name, - master: monster.id, - }); - } - }); - } - function attack_target_or_move() { - var player = players[name_to_id[monster.target]]; - if (player && ssince(monster.last.attacked) > 20 && Math.random() > monster.rage * 0.99) { - stop_pursuit(monster, { force: true, cause: "bored" }); - return; - } - if (focus && distance(focus, monster) > 40 && !monster.moving) { - if (mode.all_smart) { - if (!monster.worker) { - monster.working = true; - workers[wlast++ % workers.length].postMessage({ - type: "fast_astar", - in: monster.in, - id: monster.id, - map: monster.map, - sx: monster.x, - sy: monster.y, - tx: focus.x, - ty: focus.y, - }); - } - } else { - monster.ogoing_x = monster.going_x; - monster.ogoing_y = monster.going_y; - monster.going_x = monster.x + (focus.x - monster.x) / 2; - monster.going_y = monster.y + (focus.y - monster.y) / 2; - if (mode.path_checks && !can_move(monster)) { - monster.going_x = monster.ogoing_x; - monster.going_y = monster.ogoing_y; - } else { - start_moving_element(monster); - } - } - } - if (player && player.in == monster.in && !player.rip && !is_invis(player)) { - if ( - distance(player, monster, true) > - min(monster.range / 1.6, 240) + min(100, monster.attack / 5.0) + 160 + ((mode.all_smart && 320) || 1) && - !monster.walk_once - ) { - stop_pursuit(monster, { cause: "exceeds_range" }); - } else if (can_attack(monster, player)) { - var attack = commence_attack(monster, player, "attack"); - if (attack && attack.events && attack.events.length) { - events.push(...attack.events); - } - } else if ( - distance(monster, player, true) > 12 && - !mode.range_test && - !(mode.all_smart && monster.moving) && - !focus - ) { - // console.log(monster.height); - if (mode.all_smart) { - if (!monster.worker) { - monster.working = true; - workers[wlast++ % workers.length].postMessage({ - type: "fast_astar", - in: monster.in, - id: monster.id, - map: monster.map, - sx: monster.x, - sy: monster.y, - tx: player.x, - ty: player.y, - }); - } - } else { - monster.ogoing_x = monster.going_x; - monster.ogoing_y = monster.going_y; - monster.going_x = monster.x + (player.x - monster.x) / 2; - monster.going_y = monster.y + (player.y - monster.y) / 2; - if (mode.path_checks && !can_move(monster)) { - monster.going_x = monster.ogoing_x; - monster.going_y = monster.ogoing_y; - if (monster.attack < 120 || distance(monster, player, true) > monster.range) { - stop_pursuit(monster, { cause: "cant_move" }); - } - } else { - // console.log("Moving to "+monster.going_x+" "+monster.going_y); - start_moving_element(monster); - } - } - } - } else if (monster.target) { - stop_pursuit(monster, { cause: "player_gone" }); - } - if (monster.walk_once) { - monster.walk_once = false; - } - } - if (monster.moving) { - var ms = mssince(monster.last.move); - ms = min(ms, 2000); // to prevent monsters from jumping off the map when the machine sleeps - monster.x += (monster.vx * ms) / 1000.0; - monster.y += (monster.vy * ms) / 1000.0; - stop_logic(monster); - monster.last.move = new Date(); - xy_u_logic(monster); + if (instance.paused) { + return; + } + instance.operators = 0; + var ms = mssince(instance.last_update); + instance.last_update = new Date(); + rage_logic(instance); + var to_push = []; + var monster_map = {}; + var now_date = instance.last_update; + var aggressives = {}; + var targets = {}; + for (var id in instance.monsters) { + var monster = instance.monsters[id]; + var events = []; + var change = false; + var def = monster.type; + if ((monster.target && monster.a.portal) || G.monsters[monster.type].operator) { + instance.operators += 1; + } + var focus = instance.monsters[monster.focus]; + if ((monster.focus && !instance.monsters[monster.focus]) || (focus && distance(monster, focus) > 380)) { + focus = monster.focus = null; + monster.cid++; + monster.u = true; + change = true; + } + if (monster.focus) { + change = true; + } // better to re-calculate for now, for charge speed changes + for (var name in monster.s) { + var def = G.conditions[name]; + var ref = monster.s[name]; + var value = monster.s[name].ms; + monster.s[name].ms -= ms; + if (def && def.interval) { + if (!monster.s[name].last || mssince(monster.s[name].last) >= def.interval) { + monster.s[name].last = new Date(); + if (name == "eburn") { + var damage = G.conditions.eburn.damage; + if (monster.immune) { + damage = 0; + } + disappearing_text({}, monster, "-" + damage, { color: "red", xy: 1 }); + monster.hp = max(1, monster.hp - damage); + } + if (name == "eheal") { + var heal = G.conditions.eheal.heal; + if (monster.immune) { + heal = 0; + } + disappearing_text({}, monster, "+" + heal, { color: "heal", xy: 1 }); + monster.hp = min(monster.max_hp, monster.hp + heal); + } + if (name == "burned") { + var damage = ceil(ref.intensity / 5); + //disappearing_text({},monster,"-"+damage,{color:"burn",xy:1}); + monster.hp = max(0, monster.hp - damage); + xy_emit(monster, "hit", { + source: "burn", + hid: ref.f, + id: monster.id, + damage: damage, + kill: monster.hp <= 0, + }); + var attacker = (monster.target && get_player(monster.target)) || get_player(ref.f); + var burner = get_player(ref.f); + if (burner) { + add_pdps(burner, monster, damage); + if (monster.cooperative) { + add_coop_points(monster, burner, damage); + } + } + if (monster.hp <= 0) { + if (burner) { + achievement_logic_burn_last_hit(burner); + } + kill_monster(attacker, monster); + } + } + monster.u = true; + monster.cid++; + } + } + if (monster.s[name].ms <= 0) { + if (monster.a[name] && monster.a[name].cooldown) { + monster.s[name].ms = monster.a[name].cooldown; + } else { + delete monster.s[name]; + } + if (is_disabled(monster) && G.skills[name] && !G.skills[name].passive) { + continue; + } + if (name != "young") { + monster.u = true; + monster.cid++; + change = true; + } + if (name == "self_healing") { + var hp = monster.hp; + var heal = monster.a.self_healing.heal; + if (monster.s.poisoned) { + heal /= 2; + } + monster.hp = min(monster.max_hp, monster.hp + heal); + if (hp != monster.hp) { + events.push(["ui", { type: "mheal", id: id, heal: monster.hp - hp }]); + } + } + if (name == "healing") { + var target = monster; + if (focus && distance(focus, monster) < 120) { + target = focus; + } + var hp = target.hp; + var heal = monster.a.healing.heal; + if (target.s.poisoned) { + heal /= 2; + } + target.hp = min(target.max_hp, target.hp + heal); + if (hp != target.hp) { + events.push(["ui", { type: "mheal", id: target.id, heal: target.hp - hp }]); + } + } + if (name == "mtangle") { + if (monster.target && get_player(monster.target)) { + var player = get_player(monster.target); + add_condition(player, "tangled"); + resend(player, "u+cid"); + } + } + if (name == "multi_burn") { + if (monster.cooperative) { + for (var name in monster.points || {}) { + var player = get_player(name); + if (player && simple_distance(monster, player) < 480) { + commence_attack(monster, player, "fireball"); + } + } + } else { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < 480) { + commence_attack(monster, player, "fireball"); + } + } + } + monster.cid++; + monster.u = true; + change = true; + } + if (name == "multi_freeze") { + for (var name in monster.points || {}) { + var player = get_player(name); + if (player && simple_distance(monster, player) < 480) { + commence_attack(monster, player, "frostball"); + } + } + monster.cid++; + monster.u = true; + change = true; + } + if (name == "degen") { + monster.hp -= 60; + monster.cid++; + monster.u = true; + change = true; + if (monster.hp <= 0) { + monster.hp = 0; + remove_monster(monster); + } + } + if (name == "zap") { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < monster.a[name].radius) { + commence_attack(monster, player, "zap"); + } + } + } + if (monster.a && monster.a[name] && monster.a[name].aura) { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < monster.a[name].radius) { + player.s[monster.a[name].condition] = { ms: G.conditions[monster.a[name].condition].duration }; + resend(player, "u+cid"); + } + } + } + if (name == "deepfreeze") { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius && !player.npc) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + commence_attack(monster, theone, "deepfreeze"); + } + } + if (name == "anger") { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster); + } + target_player(monster, theone); + } + } + if (name == "warpstomp") { + var dampened = false; + for (var id in instances[monster.in].monsters) { + var m = instances[monster.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(monster.x, monster.y, m.x, m.y) < 300) { + monster.s.dampened = { ms: 2000 }; + dampened = true; + } + } + if (!dampened) { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster); + } + target_player(monster, theone); + port_monster(monster, theone, { stomp: 160 }); + } + } + } + if (name == "mlight") { + xy_emit(monster, "light", { name: monster.id }); + } + if (name == "stone") { + if (monster.target && get_player(monster.target)) { + var player = get_player(monster.target); + add_condition(player, "stoned"); + resend(player, "u+cid"); + } + } + if (name == "magiport") { + var r = false; + if (monster.map != ref.map) { + r = true; + } + transport_monster_to(monster, ref.in, ref.map, ref.x, ref.y); + if (ref.stomp) { + for (var id in instances[monster.in].players) { + var target = instances[monster.in].players[id]; + var dist = simple_distance(monster, target); + if ( + dist < 160 && + !target.npc && + !target.s.invincible && + add_condition(target, "stunned", { duration: 1500 }) + ) { + resend(target); + } + } + } + if (r) { + continue; + } + } + if (name == "sleeping" && E.schedule.night && Math.random() < 0.9) { + monster.s.sleeping = { ms: 3000 + 5000 * Math.random() }; + monster.u = true; + monster.cid++; + } + } + } + if (monster.dead) { + continue; + } + if (G.monsters[monster.type].supporter && !monster.focus) { + for (var mid in instance.monsters) { + var m = instance.monsters[mid]; + if ( + !m.focus && + m != monster && + G.monsters[monster.type].humanoid == G.monsters[m.type].humanoid && + distance(m, monster) < 300 + ) { + monster.focus = m.id; + change = true; + break; + } + } + } + if (change) { + calculate_monster_stats(monster); + } + if ( + !monster.pet && + !monster.trap && + mode.aggro && + !monster.target && + monster.aggro && + can_attack(monster, "aggro") + ) { + monster.last_aggro = new Date(); + if (monster.aggro > 0.99 || Math.random() < monster.aggro) { + set_ghash(aggressives, monster, 32); + } + } + if (monster.target && monster.spawns && get_player(monster.target) && !is_disabled(monster)) { + monster.spawns.forEach(function (spi) { + var interval = spi[0]; + var name = spi[1]; + if (!monster.last[name] || mssince(monster.last[name]) > interval) { + var pname = random_one(Object.keys(monster.points)); + var player = get_player(pname); + if (!player || player.npc || distance(monster, player) > 400) { + return; + } + if (!is_same(player, get_player(monster.target), true)) { + return; + } + monster.last[name] = new Date(); + var spot = safe_xy_nearby(player.map, player.x + Math.random() * 20 - 10, player.y + Math.random() * 20 - 10); + if (!spot) { + return; + } + new_monster(instance.name, { + type: name, + stype: "spawn", + x: spot.x, + y: spot.y, + target: player.name, + master: monster.id, + }); + } + }); + } + function attack_target_or_move() { + var player = players[name_to_id[monster.target]]; + if (player && ssince(monster.last.attacked) > 20 && Math.random() > monster.rage * 0.99) { + stop_pursuit(monster, { force: true, cause: "bored" }); + return; + } + if (focus && distance(focus, monster) > 40 && !monster.moving) { + if (mode.all_smart) { + if (!monster.worker) { + monster.working = true; + workers[wlast++ % workers.length].postMessage({ + type: "fast_astar", + in: monster.in, + id: monster.id, + map: monster.map, + sx: monster.x, + sy: monster.y, + tx: focus.x, + ty: focus.y, + }); + } + } else { + monster.ogoing_x = monster.going_x; + monster.ogoing_y = monster.going_y; + monster.going_x = monster.x + (focus.x - monster.x) / 2; + monster.going_y = monster.y + (focus.y - monster.y) / 2; + if (mode.path_checks && !can_move(monster)) { + monster.going_x = monster.ogoing_x; + monster.going_y = monster.ogoing_y; + } else { + start_moving_element(monster); + } + } + } + if (player && player.in == monster.in && !player.rip && !is_invis(player)) { + if ( + distance(player, monster, true) > + min(monster.range / 1.6, 240) + min(100, monster.attack / 5.0) + 160 + ((mode.all_smart && 320) || 1) && + !monster.walk_once + ) { + stop_pursuit(monster, { cause: "exceeds_range" }); + } else if (can_attack(monster, player)) { + var attack = commence_attack(monster, player, "attack"); + if (attack && attack.events && attack.events.length) { + events.push(...attack.events); + } + } else if ( + distance(monster, player, true) > 12 && + !mode.range_test && + !(mode.all_smart && monster.moving) && + !focus + ) { + // console.log(monster.height); + if (mode.all_smart) { + if (!monster.worker) { + monster.working = true; + workers[wlast++ % workers.length].postMessage({ + type: "fast_astar", + in: monster.in, + id: monster.id, + map: monster.map, + sx: monster.x, + sy: monster.y, + tx: player.x, + ty: player.y, + }); + } + } else { + monster.ogoing_x = monster.going_x; + monster.ogoing_y = monster.going_y; + monster.going_x = monster.x + (player.x - monster.x) / 2; + monster.going_y = monster.y + (player.y - monster.y) / 2; + if (mode.path_checks && !can_move(monster)) { + monster.going_x = monster.ogoing_x; + monster.going_y = monster.ogoing_y; + if (monster.attack < 120 || distance(monster, player, true) > monster.range) { + stop_pursuit(monster, { cause: "cant_move" }); + } + } else { + // console.log("Moving to "+monster.going_x+" "+monster.going_y); + start_moving_element(monster); + } + } + } + } else if (monster.target) { + stop_pursuit(monster, { cause: "player_gone" }); + } + if (monster.walk_once) { + monster.walk_once = false; + } + } + if (monster.moving) { + var ms = mssince(monster.last.move); + ms = min(ms, 2000); // to prevent monsters from jumping off the map when the machine sleeps + monster.x += (monster.vx * ms) / 1000.0; + monster.y += (monster.vy * ms) / 1000.0; + stop_logic(monster); + monster.last.move = new Date(); + xy_u_logic(monster); - if (monster.moving && monster.attack > 100 && monster.target) { - attack_target_or_move(); - } - } else if (monster.s.sleeping || monster.working) { - } else if (can_walk(monster)) { - // for the .s.stunned check - if (monster.s.magiport) { - } else if (monster.target || focus) { - attack_target_or_move(); - } else if (!mode.upush_test) { - if (monster.map_def.position && !mode.all_roam) { - var position = monster.map_def.position; - var radius = monster.map_def.radius; - var dx = Math.random() * radius - radius / 2; - var dy = Math.random() * radius - radius / 2; - if (abs(dx) + abs(dy) < 80) { - // optimization to prevent short walks - if (dx < 0 && dx > -60) { - dx = -60; - } - if (dx > 0 && dx < 60) { - dx = 60; - } - if (dy < 0 && dy > -60) { - dy = -60; - } - if (dy > 0 && dy < 60) { - dy = 60; - } - } - monster.going_x = monster.x + dx; - monster.going_y = monster.y + dy; - if (monster.going_x > position[0] + radius) { - monster.going_x = position[0] + radius; - } - if (monster.going_y > position[1] + radius) { - monster.going_y = position[1] + radius; - } - if (monster.going_x < position[0] - radius) { - monster.going_x = position[0] - radius; - } - if (monster.going_y < position[1] - radius) { - monster.going_y = position[1] - radius; - } - start_moving_element(monster); - } else if ( - monster.map_def.roam || - G.monsters[monster.type].roam || - mode.all_roam || - (monster.map_def.polygon && !monster.irregular) - ) { - perfc.roams += 1; - var map_def = monster.map_def; - var tries = 1; - if (!monster.map_def.roam && !mode.all_roam && monster.map_def.polygon) { - tries = 12; - } - if (!monster.rmove) { - monster.rmove = parseInt(Math.random() * 100); - } - monster.dmove = monster.dmove || 0; - for (var t = 0; t < tries; t++) { - var moves = [ - [1, 0], - [0.8, 0.8], - [0, 1], - [-0.8, 0.8], - [-1, 0], - [-0.8, -0.8], - [0, -1], - [0.8, -0.8], - ]; - var multipliers = [500, 200, 100, 50, 10]; - if (Math.random() < 0.1) { - monster.rmove = parseInt(Math.random() * moves.length); - } - var move = moves[monster.rmove % moves.length]; - var d = multipliers[monster.dmove % multipliers.length]; - monster.going_x = monster.x + move[0] * d; - monster.going_y = monster.y + move[1] * d; - if (tries > 1 && monster.map_def.polygon) { - if (is_point_inside([monster.going_x, monster.going_y], monster.map_def.polygon)) { - break; - } else { - monster.rmove++; - monster.dmove++; - } - } - } - // console.log(monster.rmove+","+monster.dmove+" "+monster.going_x+","+monster.going_y); - if (can_move(monster)) { - start_moving_element(monster); - monster.dmove = 0; - } else { - monster.rmove++; - monster.dmove++; - } - } else if ( - monster.map_def.stype != "pet" && - monster.map_def.stype != "spawn" && - monster.map_def.stype != "trap" - ) { - var map_def = monster.map_def; - var to_move = true; - if (map_def.polygon) { - var p = random_point(map_def.polygon, monster.base); - monster.going_x = p[0]; - monster.going_y = p[1]; - } else { - monster.going_x = map_def.boundary[0] + Math.random() * (map_def.boundary[2] - map_def.boundary[0]); - monster.going_y = map_def.boundary[1] + Math.random() * (map_def.boundary[3] - map_def.boundary[1]); - } - if (monster.irregular == 3) { - // new [01/03/19] - monster.m++; - setTimeout(new_monster_f(monster.oin, monster.map_def, { last_state: monster }), 500); - remove_monster(monster, { nospawn: true, method: "disappear" }); - } else if (monster.irregular == 2) { - server_log("Irregular2 move for " + monster.id); - if (monster.in != monster.oin) { - port_monster(monster, { map: monster.oin, x: monster.going_x, y: monster.going_y, in: monster.oin }); - } else { - recalculate_move(monster); - } - monster.irregular = 1; - } else if (monster.irregular == 1) { - if (!can_move(monster)) { - monster.m++; - server_log("Irregular1 respawn: " + monster.id); - setTimeout(new_monster_f(monster.oin, monster.map_def, { last_state: monster }), 500); - remove_monster(monster, { nospawn: true, method: "disappear" }); - } else { - server_log("No longer irregular: " + monster.id); - delete monster["irregular"]; - } - } - if (monster.going_x != monster.x || monster.going_y != monster.y) { - // so Automatron's don't move - start_moving_element(monster); - } - } - } - } - if (!monster.dead && (monster.u || events.length)) { - monster.u = false; - to_push.push({ id: id, entity: monster, data: monster_to_client(monster, events) }); - monster_map[id] = to_push[to_push.length - 1]; - } - } + if (monster.moving && monster.attack > 100 && monster.target) { + attack_target_or_move(); + } + } else if (monster.s.sleeping || monster.working) { + } else if (can_walk(monster)) { + // for the .s.stunned check + if (monster.s.magiport) { + } else if (monster.target || focus) { + attack_target_or_move(); + } else if (!mode.upush_test) { + if (monster.map_def.position && !mode.all_roam) { + var position = monster.map_def.position; + var radius = monster.map_def.radius; + var dx = Math.random() * radius - radius / 2; + var dy = Math.random() * radius - radius / 2; + if (abs(dx) + abs(dy) < 80) { + // optimization to prevent short walks + if (dx < 0 && dx > -60) { + dx = -60; + } + if (dx > 0 && dx < 60) { + dx = 60; + } + if (dy < 0 && dy > -60) { + dy = -60; + } + if (dy > 0 && dy < 60) { + dy = 60; + } + } + monster.going_x = monster.x + dx; + monster.going_y = monster.y + dy; + if (monster.going_x > position[0] + radius) { + monster.going_x = position[0] + radius; + } + if (monster.going_y > position[1] + radius) { + monster.going_y = position[1] + radius; + } + if (monster.going_x < position[0] - radius) { + monster.going_x = position[0] - radius; + } + if (monster.going_y < position[1] - radius) { + monster.going_y = position[1] - radius; + } + start_moving_element(monster); + } else if ( + monster.map_def.roam || + G.monsters[monster.type].roam || + mode.all_roam || + (monster.map_def.polygon && !monster.irregular) + ) { + perfc.roams += 1; + var map_def = monster.map_def; + var tries = 1; + if (!monster.map_def.roam && !mode.all_roam && monster.map_def.polygon) { + tries = 12; + } + if (!monster.rmove) { + monster.rmove = parseInt(Math.random() * 100); + } + monster.dmove = monster.dmove || 0; + for (var t = 0; t < tries; t++) { + var moves = [ + [1, 0], + [0.8, 0.8], + [0, 1], + [-0.8, 0.8], + [-1, 0], + [-0.8, -0.8], + [0, -1], + [0.8, -0.8], + ]; + var multipliers = [500, 200, 100, 50, 10]; + if (Math.random() < 0.1) { + monster.rmove = parseInt(Math.random() * moves.length); + } + var move = moves[monster.rmove % moves.length]; + var d = multipliers[monster.dmove % multipliers.length]; + monster.going_x = monster.x + move[0] * d; + monster.going_y = monster.y + move[1] * d; + if (tries > 1 && monster.map_def.polygon) { + if (is_point_inside([monster.going_x, monster.going_y], monster.map_def.polygon)) { + break; + } else { + monster.rmove++; + monster.dmove++; + } + } + } + // console.log(monster.rmove+","+monster.dmove+" "+monster.going_x+","+monster.going_y); + if (can_move(monster)) { + start_moving_element(monster); + monster.dmove = 0; + } else { + monster.rmove++; + monster.dmove++; + } + } else if ( + monster.map_def.stype != "pet" && + monster.map_def.stype != "spawn" && + monster.map_def.stype != "trap" + ) { + var map_def = monster.map_def; + var to_move = true; + if (map_def.polygon) { + var p = random_point(map_def.polygon, monster.base); + monster.going_x = p[0]; + monster.going_y = p[1]; + } else { + monster.going_x = map_def.boundary[0] + Math.random() * (map_def.boundary[2] - map_def.boundary[0]); + monster.going_y = map_def.boundary[1] + Math.random() * (map_def.boundary[3] - map_def.boundary[1]); + } + if (monster.irregular == 3) { + // new [01/03/19] + monster.m++; + setTimeout(new_monster_f(monster.oin, monster.map_def, { last_state: monster }), 500); + remove_monster(monster, { nospawn: true, method: "disappear" }); + } else if (monster.irregular == 2) { + server_log("Irregular2 move for " + monster.id); + if (monster.in != monster.oin) { + port_monster(monster, { map: monster.oin, x: monster.going_x, y: monster.going_y, in: monster.oin }); + } else { + recalculate_move(monster); + } + monster.irregular = 1; + } else if (monster.irregular == 1) { + if (!can_move(monster)) { + monster.m++; + server_log("Irregular1 respawn: " + monster.id); + setTimeout(new_monster_f(monster.oin, monster.map_def, { last_state: monster }), 500); + remove_monster(monster, { nospawn: true, method: "disappear" }); + } else { + server_log("No longer irregular: " + monster.id); + delete monster["irregular"]; + } + } + if (monster.going_x != monster.x || monster.going_y != monster.y) { + // so Automatron's don't move + start_moving_element(monster); + } + } + } + } + if (!monster.dead && (monster.u || events.length)) { + monster.u = false; + to_push.push({ id: id, entity: monster, data: monster_to_client(monster, events) }); + monster_map[id] = to_push[to_push.length - 1]; + } + } - for (var id in instance.players) { - var player = instance.players[id]; - if (!player) { - continue; - } - for (var name in player.s) { - var def = G.conditions[name]; - var ref = player.s[name]; - var value = player.s[name].ms; - player.s[name].ms -= ms; - if (def && def.interval) { - if (!player.s[name].last || mssince(player.s[name].last) >= def.interval) { - player.s[name].last = new Date(); - if (name == "eburn") { - disappearing_text(player.socket, player, "-50", { color: "red", xy: 1 }); - player.hp = max(1, player.hp - 50); - } - if (name == "eheal") { - disappearing_text(player.socket, player, "+50", { color: "heal", xy: 1 }); - player.hp = min(player.max_hp, player.hp + 50); - } - if (name == "burned") { - var damage = ceil(ref.intensity / 5); - disappearing_text(player.socket, player, "-" + damage, { color: "red", xy: 1 }); - player.hp = max(0, player.hp - damage); - xy_emit(player, "hit", { - source: "burn", - hid: ref.fid, - id: player.name, - damage: damage, - kill: player.hp <= 0, - }); - player_rip_logic(player); - } - resend(player, "u+cid+nc"); - } - } - if (in_arr(name, ["damage_received"])) { - player.s[name].amount = max(0, (player.s[name].amount * (4000 - ms)) / 4000.0); - } - if (player.s[name].ms <= 0) { - delete player.s[name]; - if (name == "blink") { - if (player.s.dampened) { - xy_emit(player, "ui", { type: "dampened", name: player.name }); - } else { - decay_s(player, 30000); - transport_player_to(player, ref.in, [ref.x, ref.y, ref.d], "blink"); - } - } - if (name == "magiport") { - var dampened = false; - for (var id in instances[ref.in].monsters) { - var m = instances[ref.in].monsters[id]; - if (m.type == "fieldgen0" && point_distance(ref.x, ref.y, m.x, m.y) < 300) { - xy_emit(player, "ui", { type: "dampened", name: player.name }); - dampened = true; - break; - } - } - if (!dampened) { - var skip = false; - if (ref.map != player.map) { - skip = true; - } - decay_s(player, 30000); - transport_player_to(player, ref.in, [ref.x, ref.y], "magiport"); - if (skip) { - resend(player, "u+cid"); - return; // player isn't in this instance any more - } - } - } - if (def) { - player.socket.emit("game_response", { response: "ex_condition", name: name }); - } - if (def && def.ui) { - resend(player, "u+cid"); - } // +cid to update the UI's [23/10/18] - else { - resend(player, "u"); - } // #TODO: don't need to resend actually, maybe reconsider [27/06/18] - } - } - for (var name in player.q) { - var value = player.q[name].ms; - var ref = player.q[name]; - player.q[name].ms -= ms; - if (name == "upgrade") { - if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - var def = player.items[ref.num].p; - var change = false; - if (value < ref.len * 0.8 && def.nums[0] === undefined) { - def.nums[0] = parseInt(player.p.u_roll * 10000) % 10; - change = true; - } - if (value < ref.len * 0.64 && def.nums[1] === undefined) { - def.nums[1] = parseInt(player.p.u_roll * 1000) % 10; - change = true; - } - if (value < ref.len * 0.4 && def.nums[2] === undefined) { - def.nums[2] = parseInt(player.p.u_roll * 100) % 10; - change = true; - } - if (value < min(3000, ref.len * 0.3) && def.nums[3] === undefined) { - def.nums[3] = parseInt(player.p.u_roll * 10); - change = true; - } - if (value < min(2200, ref.len * 0.22) && player.p.u_item && !player.p.u_fail && !def.success) { - def.success = true; - change = true; - } - if (value < min(2200, ref.len * 0.22) && (player.p.u_itemx || player.p.u_fail) && !def.failure) { - def.failure = true; - change = true; - } - if (change) { - player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); - } - } - } - if (name == "compound") { - if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - var def = player.items[ref.num].p; - var change = false; - if (value < 8000 && def.nums[0] === undefined) { - def.nums[0] = parseInt(player.p.c_roll * 10000) % 10; - change = true; - } - if (value < 6400 && def.nums[1] === undefined) { - def.nums[1] = parseInt(player.p.c_roll * 1000) % 10; - change = true; - } - if (value < 5000 && def.nums[2] === undefined) { - def.nums[2] = parseInt(player.p.c_roll * 100) % 10; - change = true; - } - if (value < 3000 && def.nums[3] === undefined) { - def.nums[3] = parseInt(player.p.c_roll * 10); - change = true; - } - if (value < 2200 && player.p.c_item && !def.success) { - def.success = true; - change = true; - } - if (value < 2200 && player.p.c_itemx && !def.failure) { - def.failure = true; - change = true; - } - if (change) { - player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); - } - } - } - if (player.q[name].ms <= 0) { - delete player.q[name]; - if (name == "exchange") { - if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - player.citems[ref.num] = player.items[ref.num] = null; - } - exchange(player, ref.id, { v: ref.v }); - if (ref.qs) { - player.socket.emit("game_response", { response: ref.qs + "_success", suffix: ref.s || "" }); - } else { - xy_emit(G.maps.main.exchange, "upgrade", { type: "exchange", success: 1 }); - } - resend(player, "reopen+u+cid"); - } - if (name == "compound") { - if (player.p.c_item) { - var item = player.p.c_item; - var def = G.items[item.name]; - player.hitchhikers.push([ - "game_response", - { - response: "compound_success", - stale: ref.stale, - level: item.level, - num: data.num, - up: item.extra || undefined, - }, - ]); - if (calculate_item_value(item) + (def.edge || 0) * 2000000 > 1800000 && !player.stealth) { - broadcast("server_message", { - message: player.name + " received " + item_to_phrase(item), - color: colors.server_success, - item: cache_item(item), - type: "server_csuccess", - name: player.name, - }); - } - xy_emit(G.maps.main.compound, "upgrade", { type: "compound", success: 1 }); - if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - player.items[ref.num] = item; - player.citems[ref.num] = cache_item(player.items[ref.num]); - } - achievement_logic_compound_success(player, item); - } else { - var item = player.p.c_itemx; - var def = G.items[item.name]; - player.hitchhikers.push([ - "game_response", - { response: "compound_fail", level: item.level, num: data.num, stale: ref.stale }, - ]); - if (calculate_item_value(item) + (def.edge || 0) * 2000000 > 920000 && !player.stealth) { - broadcast("server_message", { - message: player.name + " lost " + item_to_phrase(item) + "'s", - color: colors.server_failure, - item: cache_item(item), - type: "server_cfail", - name: player.name, - }); - } - xy_emit(G.maps.main.compound, "upgrade", { type: "compound", success: 0 }); - if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - player.citems[ref.num] = player.items[ref.num] = null; - player.esize++; - } - } - delete player.p.c_item; - delete player.p.c_itemx; - delete player.p.c_roll; - resend(player, "reopen+u+cid+nc+inv"); - } - if (name == "upgrade") { - var success = false; - var announce = false; - var new_level = player.p.u_level + 1; - var item = player.p.u_item || player.p.u_itemx; - var p = player.items[ref.num] && player.items[ref.num].p; - if (player.p.u_item && player.items[ref.num] && player.items[ref.num].name == "placeholder") { - success = true; - player.items[ref.num] = player.p.u_item; - player.citems[ref.num] = cache_item(player.items[ref.num]); - } else if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - player.citems[ref.num] = player.items[ref.num] = null; - player.esize++; - } - if (player.p.u_type == "offering") { - if (success) { - player.hitchhikers.push(["game_response", { response: "upgrade_offering_success", stale: ref.stale }]); - } - } else if (player.p.u_type == "stat") { - if (success) { - player.hitchhikers.push([ - "game_response", - { - response: "upgrade_success_stat", - stale: ref.stale, - stat_type: p && G.items[p.scroll].stat, - num: ref.num, - }, - ]); - } - } else if (!ref.silent) { - announce = true; - } + for (var id in instance.players) { + var player = instance.players[id]; + if (!player) { + continue; + } + for (var name in player.s) { + var def = G.conditions[name]; + var ref = player.s[name]; + var value = player.s[name].ms; + player.s[name].ms -= ms; + if (def && def.interval) { + if (!player.s[name].last || mssince(player.s[name].last) >= def.interval) { + player.s[name].last = new Date(); + if (name == "eburn") { + disappearing_text(player.socket, player, "-50", { color: "red", xy: 1 }); + player.hp = max(1, player.hp - 50); + } + if (name == "eheal") { + disappearing_text(player.socket, player, "+50", { color: "heal", xy: 1 }); + player.hp = min(player.max_hp, player.hp + 50); + } + if (name == "burned") { + var damage = ceil(ref.intensity / 5); + disappearing_text(player.socket, player, "-" + damage, { color: "red", xy: 1 }); + player.hp = max(0, player.hp - damage); + xy_emit(player, "hit", { + source: "burn", + hid: ref.fid, + id: player.name, + damage: damage, + kill: player.hp <= 0, + }); + player_rip_logic(player); + } + resend(player, "u+cid+nc"); + } + } + if (in_arr(name, ["damage_received"])) { + player.s[name].amount = max(0, (player.s[name].amount * (4000 - ms)) / 4000.0); + } + if (player.s[name].ms <= 0) { + delete player.s[name]; + if (name == "blink") { + if (player.s.dampened) { + xy_emit(player, "ui", { type: "dampened", name: player.name }); + } else { + decay_s(player, 30000); + transport_player_to(player, ref.in, [ref.x, ref.y, ref.d], "blink"); + } + } + if (name == "magiport") { + var dampened = false; + for (var id in instances[ref.in].monsters) { + var m = instances[ref.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(ref.x, ref.y, m.x, m.y) < 300) { + xy_emit(player, "ui", { type: "dampened", name: player.name }); + dampened = true; + break; + } + } + if (!dampened) { + var skip = false; + if (ref.map != player.map) { + skip = true; + } + decay_s(player, 30000); + transport_player_to(player, ref.in, [ref.x, ref.y], "magiport"); + if (skip) { + resend(player, "u+cid"); + return; // player isn't in this instance any more + } + } + } + if (def) { + player.socket.emit("game_response", { response: "ex_condition", name: name }); + } + if (def && def.ui) { + resend(player, "u+cid"); + } // +cid to update the UI's [23/10/18] + else { + resend(player, "u"); + } // #TODO: don't need to resend actually, maybe reconsider [27/06/18] + } + } + for (var name in player.q) { + var value = player.q[name].ms; + var ref = player.q[name]; + player.q[name].ms -= ms; + if (name == "upgrade") { + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + var def = player.items[ref.num].p; + var change = false; + if (value < ref.len * 0.8 && def.nums[0] === undefined) { + def.nums[0] = parseInt(player.p.u_roll * 10000) % 10; + change = true; + } + if (value < ref.len * 0.64 && def.nums[1] === undefined) { + def.nums[1] = parseInt(player.p.u_roll * 1000) % 10; + change = true; + } + if (value < ref.len * 0.4 && def.nums[2] === undefined) { + def.nums[2] = parseInt(player.p.u_roll * 100) % 10; + change = true; + } + if (value < min(3000, ref.len * 0.3) && def.nums[3] === undefined) { + def.nums[3] = parseInt(player.p.u_roll * 10); + change = true; + } + if (value < min(2200, ref.len * 0.22) && player.p.u_item && !player.p.u_fail && !def.success) { + def.success = true; + change = true; + } + if (value < min(2200, ref.len * 0.22) && (player.p.u_itemx || player.p.u_fail) && !def.failure) { + def.failure = true; + change = true; + } + if (change) { + player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); + } + } + } + if (name == "compound") { + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + var def = player.items[ref.num].p; + var change = false; + if (value < 8000 && def.nums[0] === undefined) { + def.nums[0] = parseInt(player.p.c_roll * 10000) % 10; + change = true; + } + if (value < 6400 && def.nums[1] === undefined) { + def.nums[1] = parseInt(player.p.c_roll * 1000) % 10; + change = true; + } + if (value < 5000 && def.nums[2] === undefined) { + def.nums[2] = parseInt(player.p.c_roll * 100) % 10; + change = true; + } + if (value < 3000 && def.nums[3] === undefined) { + def.nums[3] = parseInt(player.p.c_roll * 10); + change = true; + } + if (value < 2200 && player.p.c_item && !def.success) { + def.success = true; + change = true; + } + if (value < 2200 && player.p.c_itemx && !def.failure) { + def.failure = true; + change = true; + } + if (change) { + player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); + } + } + } + if (player.q[name].ms <= 0) { + delete player.q[name]; + if (name == "exchange") { + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.citems[ref.num] = player.items[ref.num] = null; + } + exchange(player, ref.id, { v: ref.v }); + if (ref.qs) { + player.socket.emit("game_response", { response: ref.qs + "_success", suffix: ref.s || "" }); + } else { + xy_emit(G.maps.main.exchange, "upgrade", { type: "exchange", success: 1 }); + } + resend(player, "reopen+u+cid"); + } + if (name == "compound") { + if (player.p.c_item) { + var item = player.p.c_item; + var def = G.items[item.name]; + player.hitchhikers.push([ + "game_response", + { + response: "compound_success", + stale: ref.stale, + level: item.level, + num: data.num, + up: item.extra || undefined, + }, + ]); + if (calculate_item_value(item) + (def.edge || 0) * 2000000 > 1800000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " received " + item_to_phrase(item), + color: colors.server_success, + item: cache_item(item), + type: "server_csuccess", + name: player.name, + }); + } + xy_emit(G.maps.main.compound, "upgrade", { type: "compound", success: 1 }); + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.items[ref.num] = item; + player.citems[ref.num] = cache_item(player.items[ref.num]); + } + achievement_logic_compound_success(player, item); + } else { + var item = player.p.c_itemx; + var def = G.items[item.name]; + player.hitchhikers.push([ + "game_response", + { response: "compound_fail", level: item.level, num: data.num, stale: ref.stale }, + ]); + if (calculate_item_value(item) + (def.edge || 0) * 2000000 > 920000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " lost " + item_to_phrase(item) + "'s", + color: colors.server_failure, + item: cache_item(item), + type: "server_cfail", + name: player.name, + }); + } + xy_emit(G.maps.main.compound, "upgrade", { type: "compound", success: 0 }); + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.citems[ref.num] = player.items[ref.num] = null; + player.esize++; + } + } + delete player.p.c_item; + delete player.p.c_itemx; + delete player.p.c_roll; + resend(player, "reopen+u+cid+nc+inv"); + } + if (name == "upgrade") { + var success = false; + var announce = false; + var new_level = player.p.u_level + 1; + var item = player.p.u_item || player.p.u_itemx; + var p = player.items[ref.num] && player.items[ref.num].p; + if (player.p.u_item && player.items[ref.num] && player.items[ref.num].name == "placeholder") { + success = true; + player.items[ref.num] = player.p.u_item; + player.citems[ref.num] = cache_item(player.items[ref.num]); + } else if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.citems[ref.num] = player.items[ref.num] = null; + player.esize++; + } + if (player.p.u_type == "offering") { + if (success) { + player.hitchhikers.push(["game_response", { response: "upgrade_offering_success", stale: ref.stale }]); + } + } else if (player.p.u_type == "stat") { + if (success) { + player.hitchhikers.push([ + "game_response", + { + response: "upgrade_success_stat", + stale: ref.stale, + stat_type: p && G.items[p.scroll].stat, + num: ref.num, + }, + ]); + } + } else if (!ref.silent) { + announce = true; + } - if (success && !player.p.u_fail) { - if (announce) { - player.hitchhikers.push([ - "game_response", - { response: "upgrade_success", level: new_level, num: ref.num, stale: ref.stale }, - ]); - } - if (announce && calculate_item_value(item) > 4800000 && !player.stealth) { - broadcast("server_message", { - message: player.name + " received " + item_to_phrase(item), - color: colors.server_success, - item: cache_item(item), - type: "server_usuccess", - name: player.name, - }); - } - xy_emit((instances.main && G.maps.main.upgrade) || player, "upgrade", { type: "upgrade", success: 1 }); - achievement_logic_upgrade_success(player, item); - } else { - player.hitchhikers.push([ - "game_response", - { response: "upgrade_fail", level: new_level, num: ref.num, stale: ref.stale }, - ]); - if (announce && calculate_item_value(item) > 4800000 && !player.stealth) { - broadcast("server_message", { - message: player.name + " lost " + item_to_phrase(item), - color: colors.server_failure, - item: cache_item(item), - type: "server_ufail", - name: player.name, - }); - } - xy_emit((instances.main && G.maps.main.upgrade) || player, "upgrade", { type: "upgrade", success: 0 }); - } - delete player.p.u_item; - delete player.p.u_type; - delete player.p.u_itemx; - delete player.p.u_roll; - delete player.p.u_fail; - delete player.p.u_level; - resend(player, "reopen+u+cid+nc+inv"); - } - if (name == "slots") { - if (Math.random() < ((S.gold > 500000000 && D.odds.slots_good) || D.odds.slots)) { - var gold = 500000000; - player.gold += gold; - S.gold -= gold; - broadcast("server_message", { - message: player.name + " received " + to_pretty_num(gold) + " gold", - color: "gold", - }); - player.socket.emit("game_response", "slots_success"); - player.socket.emit("game_log", { message: "Received " + to_pretty_num(gold) + " gold", color: "gold" }); - // resend(player,"u+cid"); - } else { - player.socket.emit("game_response", "slots_fail"); - } - } - } - } - for (var name in player.c) { - player.c[name].ms -= ms; - if (player.c[name].ms <= 0) { - var ref = player.c[name]; - delete player.c[name]; - if (name == "town") { - decay_s(player, 4000); - player.last.town = new Date(); - transport_player_to(player, player.in, undefined, 1); - resend(player); - } - if (name == "pickpocket") { - var target = get_player(ref.target); - if (!target) { - player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "player_gone" }); - } else if (distance(player, target) > 20) { - player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "distance" }); - } else { - var num = floor(Math.random() * 42); - if (target.items[num] && target.items[num].v) { - var item = target.items[num]; - if (item.q && item.q > 1) { - target.items[num].q -= 1; - target.citems[num].q -= 1; - item = create_new_item(item.name); - } else { - target.citems[num] = target.items[num] = null; - } - item.v = new Date(); - add_item(player, item); - player.socket.emit("game_response", { response: "picked", slot: "mainhand", cevent: true }); - consume_skill(player, "pickpocket", true); - resend(player, "reopen"); - resend(target, "reopen"); - target.socket.emit("game_response", { response: "got_picked", cevent: true }); - } else { - player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "misfortune" }); - } - } - } - if (name == "fishing") { - if (Math.random() < 0.1) { - if (player.esize) { - exchange(player, ref.drop, { phrase: "Fished" }); - } - consume_skill(player, "fishing", true); - if (player.slots.mainhand) { - var prop = calculate_item_properties(player.slots.mainhand); - if (prop.breaks && Math.random() < max(0, prop.breaks / 100.0)) { - player.cid++; - player.u = true; - player.cslots.mainhand = player.slots.mainhand = null; - player.socket.emit("game_log", "Your rod broke down ..."); - player.socket.emit("game_response", { response: "data", cevent: "item_break", slot: "mainhand" }); - } - } - player.socket.emit("game_response", { response: "data", cevent: "fishing_success", slot: "mainhand" }); - resend(player, "reopen"); - } else { - player.socket.emit("ui", { name: player.name, type: "fishing_none", cevent: true }); - } - } - if (name == "mining") { - if (Math.random() < 0.2) { - if (player.esize) { - exchange(player, ref.drop, { phrase: "Mined" }); - } - consume_skill(player, "mining", true); - if (player.slots.mainhand) { - var prop = calculate_item_properties(player.slots.mainhand); - if (prop.breaks && Math.random() < max(0, prop.breaks / 100.0)) { - player.cid++; - player.u = true; - player.cslots.mainhand = player.slots.mainhand = null; - player.socket.emit("game_log", "Your pickaxe broke down ..."); - } - } - resend(player, "reopen"); - } else { - player.socket.emit("ui", { type: "mining_none" }); - } - } - if (name == "revival") { - player.rip = false; - invincible_logic(player); - if (player.party) { - send_party_update(player.party); - } - resend(player, "u+cid"); - } - } - } - if (player.slots.elixir && player.slots.elixir.expires && now_date > player.slots.elixir.expires) { - try { - var def = G.items[player.slots.elixir.name]; - player.socket.emit("game_log", def.name + " wore off ..."); - if (def.withdrawal) { - add_condition(player, def.withdrawal); - } - } catch (e) {} - player.slots.elixir = null; - player.cslots.elixir = null; - resend(player, "reopen+u+cid"); - } - if (player.moving) { - var ms = mssince(player.last.move); - player.x += (player.vx * ms) / 1000.0; - player.y += (player.vy * ms) / 1000.0; - player.red_zone *= 0.99; - if (smap_data[player.map] != -1 && !player.npc && mode.red_zone && !player.s.dash) { - var current = smap_data[player.map][phash(player)]; - if (current === undefined) { - current = 8; - } - current = max(0, current - 1); // 1 is the new 0 [01/08/18] - player.red_zone += current; - // console.log(player.red_zone); - if (player.red_zone > smap_edge) { - player.red_zone = 0; - player.hp -= parseInt(player.hp / 2 + 1000); - player.socket.emit("game_log", "Received a movement penalty"); - player.socket.emit("game_log", "This might have happened if your network is too slow"); - appengine_log("violation", "red_zone: " + player.name + " afk: " + player.afk + " code: " + player.code); - transport_player_to(player, player.map); - defeat_player(player); - if (player.hp <= 0) { - rip(player); - } - resend(player, "u+cid"); - } - } - stop_logic(player); - // if(!player.moving) player.check_x=player.x,player.check_y=player.y,player.checked_xy=false; - player.last.move = new Date(); - xy_u_logic(player); - xy_upush_logic(player); - if (!player.npc) { - pmap_move(player); - } - } else { - player.red_zone *= 0.97; - } - if (mode.aggro && !player.rip && !is_invinc(player) && !player.npc) { - //#GTODO: calculate an angle + compare against monster's .angle before attacking - var l = get_nearby_ghash(aggressives, player, 32); - l.forEach(function (monster) { - if (is_in_front(monster, player) && can_attack(monster, player)) { - if (Math.random() < player.aggro_diff) { - return; - } - if (monster.rage && Math.random() < monster.rage - player.aggro_diff) { - target_player(monster, player); - } - var attack = commence_attack(monster, player, "attack"); - if (attack && attack.events && attack.events.length) { - if (monster_map[monster.id]) { - if (!monster_map[monster.id].data.events) { - monster_map[monster.id].data.events = attack.events; - } - monster_map[monster.id].data.events.push(...attack.events); - } else { - to_push.push({ id: monster.id, entity: monster, data: monster_to_client(monster, attack.events) }); - monster_map[monster.id] = to_push[to_push.length - 1]; - } - } - // disappearing_text(player.socket,monster,"ATTACK!",{color:"#E082B1",xy:1}); - } - }); - } - if (player.u) { - player.u = false; - to_push.push({ id: id, entity: player, data: player_to_client(player, 1) }); - } - if (player.violations >= 3) { - player.socket.disconnect(); - } - } + if (success && !player.p.u_fail) { + if (announce) { + player.hitchhikers.push([ + "game_response", + { response: "upgrade_success", level: new_level, num: ref.num, stale: ref.stale }, + ]); + } + if (announce && calculate_item_value(item) > 4800000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " received " + item_to_phrase(item), + color: colors.server_success, + item: cache_item(item), + type: "server_usuccess", + name: player.name, + }); + } + xy_emit((instances.main && G.maps.main.upgrade) || player, "upgrade", { type: "upgrade", success: 1 }); + achievement_logic_upgrade_success(player, item); + } else { + player.hitchhikers.push([ + "game_response", + { response: "upgrade_fail", level: new_level, num: ref.num, stale: ref.stale }, + ]); + if (announce && calculate_item_value(item) > 4800000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " lost " + item_to_phrase(item), + color: colors.server_failure, + item: cache_item(item), + type: "server_ufail", + name: player.name, + }); + } + xy_emit((instances.main && G.maps.main.upgrade) || player, "upgrade", { type: "upgrade", success: 0 }); + } + delete player.p.u_item; + delete player.p.u_type; + delete player.p.u_itemx; + delete player.p.u_roll; + delete player.p.u_fail; + delete player.p.u_level; + resend(player, "reopen+u+cid+nc+inv"); + } + if (name == "slots") { + if (Math.random() < ((S.gold > 500000000 && D.odds.slots_good) || D.odds.slots)) { + var gold = 500000000; + player.gold += gold; + S.gold -= gold; + broadcast("server_message", { + message: player.name + " received " + to_pretty_num(gold) + " gold", + color: "gold", + }); + player.socket.emit("game_response", "slots_success"); + player.socket.emit("game_log", { message: "Received " + to_pretty_num(gold) + " gold", color: "gold" }); + // resend(player,"u+cid"); + } else { + player.socket.emit("game_response", "slots_fail"); + } + } + } + } + for (var name in player.c) { + player.c[name].ms -= ms; + if (player.c[name].ms <= 0) { + var ref = player.c[name]; + delete player.c[name]; + if (name == "town") { + decay_s(player, 4000); + player.last.town = new Date(); + transport_player_to(player, player.in, undefined, 1); + resend(player); + } + if (name == "pickpocket") { + var target = get_player(ref.target); + if (!target) { + player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "player_gone" }); + } else if (distance(player, target) > 20) { + player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "distance" }); + } else { + var num = floor(Math.random() * 42); + if (target.items[num] && target.items[num].v) { + var item = target.items[num]; + if (item.q && item.q > 1) { + target.items[num].q -= 1; + target.citems[num].q -= 1; + item = create_new_item(item.name); + } else { + target.citems[num] = target.items[num] = null; + } + item.v = new Date(); + add_item(player, item); + player.socket.emit("game_response", { response: "picked", slot: "mainhand", cevent: true }); + consume_skill(player, "pickpocket", true); + resend(player, "reopen"); + resend(target, "reopen"); + target.socket.emit("game_response", { response: "got_picked", cevent: true }); + } else { + player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "misfortune" }); + } + } + } + if (name == "fishing") { + if (Math.random() < 0.1) { + if (player.esize) { + exchange(player, ref.drop, { phrase: "Fished" }); + } + consume_skill(player, "fishing", true); + if (player.slots.mainhand) { + var prop = calculate_item_properties(player.slots.mainhand); + if (prop.breaks && Math.random() < max(0, prop.breaks / 100.0)) { + player.cid++; + player.u = true; + player.cslots.mainhand = player.slots.mainhand = null; + player.socket.emit("game_log", "Your rod broke down ..."); + player.socket.emit("game_response", { response: "data", cevent: "item_break", slot: "mainhand" }); + } + } + player.socket.emit("game_response", { response: "data", cevent: "fishing_success", slot: "mainhand" }); + resend(player, "reopen"); + } else { + player.socket.emit("ui", { name: player.name, type: "fishing_none", cevent: true }); + } + } + if (name == "mining") { + if (Math.random() < 0.2) { + if (player.esize) { + exchange(player, ref.drop, { phrase: "Mined" }); + } + consume_skill(player, "mining", true); + if (player.slots.mainhand) { + var prop = calculate_item_properties(player.slots.mainhand); + if (prop.breaks && Math.random() < max(0, prop.breaks / 100.0)) { + player.cid++; + player.u = true; + player.cslots.mainhand = player.slots.mainhand = null; + player.socket.emit("game_log", "Your pickaxe broke down ..."); + } + } + resend(player, "reopen"); + } else { + player.socket.emit("ui", { type: "mining_none" }); + } + } + if (name == "revival") { + player.rip = false; + invincible_logic(player); + if (player.party) { + send_party_update(player.party); + } + resend(player, "u+cid"); + } + } + } + if (player.slots.elixir && player.slots.elixir.expires && now_date > player.slots.elixir.expires) { + try { + var def = G.items[player.slots.elixir.name]; + player.socket.emit("game_log", def.name + " wore off ..."); + if (def.withdrawal) { + add_condition(player, def.withdrawal); + } + } catch (e) {} + player.slots.elixir = null; + player.cslots.elixir = null; + resend(player, "reopen+u+cid"); + } + if (player.moving) { + var ms = mssince(player.last.move); + player.x += (player.vx * ms) / 1000.0; + player.y += (player.vy * ms) / 1000.0; + player.red_zone *= 0.99; + if (smap_data[player.map] != -1 && !player.npc && mode.red_zone && !player.s.dash) { + var current = smap_data[player.map][phash(player)]; + if (current === undefined) { + current = 8; + } + current = max(0, current - 1); // 1 is the new 0 [01/08/18] + player.red_zone += current; + // console.log(player.red_zone); + if (player.red_zone > smap_edge) { + player.red_zone = 0; + player.hp -= parseInt(player.hp / 2 + 1000); + player.socket.emit("game_log", "Received a movement penalty"); + player.socket.emit("game_log", "This might have happened if your network is too slow"); + appengine_log("violation", "red_zone: " + player.name + " afk: " + player.afk + " code: " + player.code); + transport_player_to(player, player.map); + defeat_player(player); + if (player.hp <= 0) { + rip(player); + } + resend(player, "u+cid"); + } + } + stop_logic(player); + // if(!player.moving) player.check_x=player.x,player.check_y=player.y,player.checked_xy=false; + player.last.move = new Date(); + xy_u_logic(player); + xy_upush_logic(player); + if (!player.npc) { + pmap_move(player); + } + } else { + player.red_zone *= 0.97; + } + if (mode.aggro && !player.rip && !is_invinc(player) && !player.npc) { + //#GTODO: calculate an angle + compare against monster's .angle before attacking + var l = get_nearby_ghash(aggressives, player, 32); + l.forEach(function (monster) { + if (is_in_front(monster, player) && can_attack(monster, player)) { + if (Math.random() < player.aggro_diff) { + return; + } + if (monster.rage && Math.random() < monster.rage - player.aggro_diff) { + target_player(monster, player); + } + var attack = commence_attack(monster, player, "attack"); + if (attack && attack.events && attack.events.length) { + if (monster_map[monster.id]) { + if (!monster_map[monster.id].data.events) { + monster_map[monster.id].data.events = attack.events; + } + monster_map[monster.id].data.events.push(...attack.events); + } else { + to_push.push({ id: monster.id, entity: monster, data: monster_to_client(monster, attack.events) }); + monster_map[monster.id] = to_push[to_push.length - 1]; + } + } + // disappearing_text(player.socket,monster,"ATTACK!",{color:"#E082B1",xy:1}); + } + }); + } + if (player.u) { + player.u = false; + to_push.push({ id: id, entity: player, data: player_to_client(player, 1) }); + } + if (player.violations >= 3) { + player.socket.disconnect(); + } + } - (G.maps[instance.map].traps || []).forEach(function (trap) { - if (trap.type == "spikes") { - for (var id in pmap_get({ x: trap.position[0], y: trap.position[1], in: instance.name })) { - var player = players[id_to_id[id]]; - if (player && !player.npc) { - disappearing_text(player.socket, player, "-50", { color: "red", xy: 1 }); - player.hp = max(0, player.hp - 50); - player_rip_logic(player); - resend(player, "u+cid"); - } - } - } else if (trap.type == "debuff") { - for (var id in instance.players) { - var player = instance.players[id]; - if (is_point_inside([player.x, player.y], trap.polygon)) { - // player.s["debuffaura"]={"ms":200,"name":"Debuff","skin":"citizens","citizens":true,"speed":-40}; - add_condition(player, "slowness", { duration: 200 }); - resend(player, "u+cid"); - } - } - } - }); + (G.maps[instance.map].traps || []).forEach(function (trap) { + if (trap.type == "spikes") { + for (var id in pmap_get({ x: trap.position[0], y: trap.position[1], in: instance.name })) { + var player = players[id_to_id[id]]; + if (player && !player.npc) { + disappearing_text(player.socket, player, "-50", { color: "red", xy: 1 }); + player.hp = max(0, player.hp - 50); + player_rip_logic(player); + resend(player, "u+cid"); + } + } + } else if (trap.type == "debuff") { + for (var id in instance.players) { + var player = instance.players[id]; + if (is_point_inside([player.x, player.y], trap.polygon)) { + // player.s["debuffaura"]={"ms":200,"name":"Debuff","skin":"citizens","citizens":true,"speed":-40}; + add_condition(player, "slowness", { duration: 200 }); + resend(player, "u+cid"); + } + } + } + }); - for (var id in instance.players) { - send_xy_updates(instance.players[id], to_push); - } - for (var id in instance.observers) { - send_xy_updates(instance.observers[id], to_push); - } + for (var id in instance.players) { + send_xy_updates(instance.players[id], to_push); + } + for (var id in instance.observers) { + send_xy_updates(instance.observers[id], to_push); + } } function count_unique_users() { - unique_players = 0; - var marked = {}; - for (var id in players) { - var ip = get_ip(players[id]); - if (!marked[ip]) { - marked[ip] = 1; - unique_players++; - } - } - if (is_sdk) { - unique_players = 9; - } - for (name in instances) { - var instance = instances[name]; - if (!instance.pvp) { - continue; - } - var initial = instance.allow; - var npc = npcs.pvp; - if ( - (is_sdk && Object.keys(players).length >= 2) || - unique_players >= B.arena_limit || - Object.keys(instance.players).length - ) { - // direction logic at game.js/update_sprite - if (instance.allow) { - continue; - } - instance.allow = true; - if (!npc) { - continue; - } - npc.going_x = npc.positions[1][0]; - npc.going_y = npc.positions[1][1]; - npc.allow = true; - npc.u = true; - start_moving_element(npc); - } else { - if (!instance.allow) { - continue; - } - instance.allow = false; - if (!npc) { - continue; - } - npc.going_x = npc.positions[0][0]; - npc.going_y = npc.positions[0][1]; - npc.allow = false; - npc.u = true; - start_moving_element(npc); - } - } + unique_players = 0; + var marked = {}; + for (var id in players) { + var ip = get_ip(players[id]); + if (!marked[ip]) { + marked[ip] = 1; + unique_players++; + } + } + if (is_sdk) { + unique_players = 9; + } + for (name in instances) { + var instance = instances[name]; + if (!instance.pvp) { + continue; + } + var initial = instance.allow; + var npc = npcs.pvp; + if ( + (is_sdk && Object.keys(players).length >= 2) || + unique_players >= B.arena_limit || + Object.keys(instance.players).length + ) { + // direction logic at game.js/update_sprite + if (instance.allow) { + continue; + } + instance.allow = true; + if (!npc) { + continue; + } + npc.going_x = npc.positions[1][0]; + npc.going_y = npc.positions[1][1]; + npc.allow = true; + npc.u = true; + start_moving_element(npc); + } else { + if (!instance.allow) { + continue; + } + instance.allow = false; + if (!npc) { + continue; + } + npc.going_x = npc.positions[0][0]; + npc.going_y = npc.positions[0][1]; + npc.allow = false; + npc.u = true; + start_moving_element(npc); + } + } } var last_iloop = new Date(); function instance_loop() { - var ms_since = 32; - try { - var now_date = new Date(); + var ms_since = 32; + try { + var now_date = new Date(); - for (name in instances) { - var instance = instances[name]; - if (mssince(instance.last_update) > 75) { - update_instance(instance); - } - } + for (name in instances) { + var instance = instances[name]; + if (mssince(instance.last_update) > 75) { + update_instance(instance); + } + } - ms_since = mssince(now_date); - if (ms_since > B.instance_loop_log_edge) { - server_log("Instance loop took " + mssince(now_date) + "ms", 1); - } - perfc.instance_loop[ms_since] = (perfc.instance_loop[ms_since] || 0) + 1; - perfc.instance_loops += 1; - var mss_since = mssince(last_iloop); - perfc.instance_delay[mss_since] = (perfc.instance_delay[mss_since] || 0) + 1; - if (mss_since / 1000 > 15) { - console.log("Sleep detected: " + mss_since / 1000); - } - last_iloop = new Date(); - } catch (e) { - log_trace("#X Instance loop error", e); - } - setTimeout(instance_loop, max(75, min(1000, ms_since * 2 + 2))); + ms_since = mssince(now_date); + if (ms_since > B.instance_loop_log_edge) { + server_log("Instance loop took " + mssince(now_date) + "ms", 1); + } + perfc.instance_loop[ms_since] = (perfc.instance_loop[ms_since] || 0) + 1; + perfc.instance_loops += 1; + var mss_since = mssince(last_iloop); + perfc.instance_delay[mss_since] = (perfc.instance_delay[mss_since] || 0) + 1; + if (mss_since / 1000 > 15) { + console.log("Sleep detected: " + mss_since / 1000); + } + last_iloop = new Date(); + } catch (e) { + log_trace("#X Instance loop error", e); + } + setTimeout(instance_loop, max(75, min(1000, ms_since * 2 + 2))); } function npc_loop() { - // now mainly just NPC's, back in the day pretty much everything [11/08/22] - if (!server.live) { - return setTimeout(npc_loop, 10); - } - var ms_since = 32; - try { - var now_date = new Date(); + // now mainly just NPC's, back in the day pretty much everything [11/08/22] + if (!server.live) { + return setTimeout(npc_loop, 10); + } + var ms_since = 32; + try { + var now_date = new Date(); - for (var id in npcs) { - var npc = npcs[id]; - var delay = -npc.delay * npc.d_multiplier; - if (npc.rip) { - continue; - } - var def = G.npcs[npc.ntype] || {}; - if ( - def && - instances[npc.in] && - instances[npc.in].players && - def.aura && - (!npc.last_aura || mssince(npc.last_aura) > 2000) - ) { - npc.last_aura = new Date(); - for (var pid in instances[npc.in].players) { - var player = instances[npc.in].players[pid]; - // console.log(player); - if (!player.npc && distance(player, npc) < 320) { - player.s[npc.ntype + "aura"] = { ms: 6000, name: "Citizen's Aura", skin: "citizens", citizens: true }; - for (var p in def.aura) { - player.s[npc.ntype + "aura"][p] = def.aura[p]; - } - resend(player, "u+cid"); - } - } - } - if (def.attack && ssince(npc.last.attack) > 1.5) { - for (var id in instances[npc.in].monsters) { - var monster = instances[npc.in].monsters[id]; - if (distance(npc, monster) < npc.range) { - // && monster.hp>5000 - npc.last.attack = new Date(); - commence_attack(npc, monster, "attack"); - break; - } - } - } + for (var id in npcs) { + var npc = npcs[id]; + var delay = -npc.delay * npc.d_multiplier; + if (npc.rip) { + continue; + } + var def = G.npcs[npc.ntype] || {}; + if ( + def && + instances[npc.in] && + instances[npc.in].players && + def.aura && + (!npc.last_aura || mssince(npc.last_aura) > 2000) + ) { + npc.last_aura = new Date(); + for (var pid in instances[npc.in].players) { + var player = instances[npc.in].players[pid]; + // console.log(player); + if (!player.npc && distance(player, npc) < 320) { + player.s[npc.ntype + "aura"] = { ms: 6000, name: "Citizen's Aura", skin: "citizens", citizens: true }; + for (var p in def.aura) { + player.s[npc.ntype + "aura"][p] = def.aura[p]; + } + resend(player, "u+cid"); + } + } + } + if (def.attack && ssince(npc.last.attack) > 1.5) { + for (var id in instances[npc.in].monsters) { + var monster = instances[npc.in].monsters[id]; + if (distance(npc, monster) < npc.range) { + // && monster.hp>5000 + npc.last.attack = new Date(); + commence_attack(npc, monster, "attack"); + break; + } + } + } - if (def.seek) { - var target = npc.focus && get_player(npc.focus); - if (target && def.transport && target.map != npc.map && target.in == target.map) { - var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); - if (spot) { - transport_player_to(npc, get_player(npc.focus).map, [spot.x, spot.y]); - } - } - var target = null; - var old_focus = npc.focus; - var min_val = 99999; - var max_val = -99999; + if (def.seek) { + var target = npc.focus && get_player(npc.focus); + if (target && def.transport && target.map != npc.map && target.in == target.map) { + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (spot) { + transport_player_to(npc, get_player(npc.focus).map, [spot.x, spot.y]); + } + } + var target = null; + var old_focus = npc.focus; + var min_val = 99999; + var max_val = -99999; - for (var pid in instances[npc.in].players) { - var player = instances[npc.in].players[pid]; - if (def.seek == "low_hp" && player.hp < player.max_hp * 0.275 && player.hp < min_val) { - min_val = player.hp; - target = player; - } - if (def.seek == "cuteness" && player.cuteness && player.cuteness > max_val) { - max_val = player.cuteness; - target = player; - } - if ( - def.seek == "thrill" && - player.thrilling && - player.thrilling > now_date && - (!target || player.thrilling > target.thrilling) - ) { - target = player; - } - if (def.seek == "gold" && player.gold > 1000000000 * ((!is_pvp && 10) || 1) && player.gold > max_val) { - max_val = player.gold; - target = player; - } - if (player.type == "merchant") { - continue; - } - if ( - def.seek == "dragondagger" && - player.slots.mainhand && - player.slots.mainhand.name == "dragondagger" && - player.level < min_val - ) { - min_val = player.level; - target = player; - } - } + for (var pid in instances[npc.in].players) { + var player = instances[npc.in].players[pid]; + if (def.seek == "low_hp" && player.hp < player.max_hp * 0.275 && player.hp < min_val) { + min_val = player.hp; + target = player; + } + if (def.seek == "cuteness" && player.cuteness && player.cuteness > max_val) { + max_val = player.cuteness; + target = player; + } + if ( + def.seek == "thrill" && + player.thrilling && + player.thrilling > now_date && + (!target || player.thrilling > target.thrilling) + ) { + target = player; + } + if (def.seek == "gold" && player.gold > 1000000000 * ((!is_pvp && 10) || 1) && player.gold > max_val) { + max_val = player.gold; + target = player; + } + if (player.type == "merchant") { + continue; + } + if ( + def.seek == "dragondagger" && + player.slots.mainhand && + player.slots.mainhand.name == "dragondagger" && + player.level < min_val + ) { + min_val = player.level; + target = player; + } + } - npc.focus = target && target.name; - if (old_focus != npc.focus) { - npc.u = true; - npc.cid++; - } - } + npc.focus = target && target.name; + if (old_focus != npc.focus) { + npc.u = true; + npc.cid++; + } + } - if (!npc.movable) { - continue; - } - var moves = [ - [1, 0], - [0, 1], - [-1, 0], - [0, -1], - [0.8, 0.8], - [-0.8, -0.8], - [0.8, -0.8], - [-0.8, 0.8], - ]; - var multiplier = npc.steps; - shuffle(moves); - if (def.aura && G.maps[npc.map].ref.transporter && distance(npc, G.maps[npc.map].ref.transporter) < 2000) { - delay = -2400; - multiplier *= 4; - } else if (def.aura) { - delay *= 3; - } - if (!npc.citizen || npc.moving || npc.last.move > future_ms(delay) || instances[npc.in].paused) { - continue; - } - if (Math.random() < 0.3) { - multiplier *= 2; - } else if (Math.random() < 0.3) { - multiplier *= 4; - } + if (!npc.movable) { + continue; + } + var moves = [ + [1, 0], + [0, 1], + [-1, 0], + [0, -1], + [0.8, 0.8], + [-0.8, -0.8], + [0.8, -0.8], + [-0.8, 0.8], + ]; + var multiplier = npc.steps; + shuffle(moves); + if (def.aura && G.maps[npc.map].ref.transporter && distance(npc, G.maps[npc.map].ref.transporter) < 2000) { + delay = -2400; + multiplier *= 4; + } else if (def.aura) { + delay *= 3; + } + if (!npc.citizen || npc.moving || npc.last.move > future_ms(delay) || instances[npc.in].paused) { + continue; + } + if (Math.random() < 0.3) { + multiplier *= 2; + } else if (Math.random() < 0.3) { + multiplier *= 4; + } - if (def.heal) { - for (var id in instances[npc.in].players) { - var player = instances[npc.in].players[id]; - if (distance(player, npc) < 320 && player.hp < player.max_hp) { - commence_attack(npc, player, "partyheal"); - } - } - } + if (def.heal) { + for (var id in instances[npc.in].players) { + var player = instances[npc.in].players[id]; + if (distance(player, npc) < 320 && player.hp < player.max_hp) { + commence_attack(npc, player, "partyheal"); + } + } + } - if (npc.focus) { - var target = get_player(npc.focus); - if (!target || distance(target, npc) < 30) { - continue; - } - npc.going_x = (npc.x + target.x) / 2; - npc.going_y = (npc.y + target.y) / 2; - if (!can_move(npc)) { - npc.going_x = npc.x + moves[0][0] * multiplier; - npc.going_y = npc.y + moves[0][1] * multiplier; - } - } else { - npc.going_x = npc.x + moves[0][0] * multiplier; - npc.going_y = npc.y + moves[0][1] * multiplier; - } - npc.d_multiplier = 1 + 2 * Math.random(); + if (npc.focus) { + var target = get_player(npc.focus); + if (!target || distance(target, npc) < 30) { + continue; + } + npc.going_x = (npc.x + target.x) / 2; + npc.going_y = (npc.y + target.y) / 2; + if (!can_move(npc)) { + npc.going_x = npc.x + moves[0][0] * multiplier; + npc.going_y = npc.y + moves[0][1] * multiplier; + } + } else { + npc.going_x = npc.x + moves[0][0] * multiplier; + npc.going_y = npc.y + moves[0][1] * multiplier; + } + npc.d_multiplier = 1 + 2 * Math.random(); - if ( - npc.boundary && - (npc.going_x < npc.boundary[0] || - npc.going_x > npc.boundary[2] || - npc.going_y < npc.boundary[1] || - npc.going_y > npc.boundary[3]) - ) { - } else if (can_move(npc)) { - npc.u = true; - start_moving_element(npc); - // server_log("Moving to "+npc.going_x+","+npc.going_y); - } else { - npc.going_x = npc.x; - npc.going_y = npc.y; - npc.last.move = new Date(); - } - } - ms_since = mssince(now_date); - } catch (e) { - log_trace("#X NPC loop error", e); - } - setTimeout(npc_loop, max(28, min(1000, ms_since * 2 + 2))); // originally 24 + if ( + npc.boundary && + (npc.going_x < npc.boundary[0] || + npc.going_x > npc.boundary[2] || + npc.going_y < npc.boundary[1] || + npc.going_y > npc.boundary[3]) + ) { + } else if (can_move(npc)) { + npc.u = true; + start_moving_element(npc); + // server_log("Moving to "+npc.going_x+","+npc.going_y); + } else { + npc.going_x = npc.x; + npc.going_y = npc.y; + npc.last.move = new Date(); + } + } + ms_since = mssince(now_date); + } catch (e) { + log_trace("#X NPC loop error", e); + } + setTimeout(npc_loop, max(28, min(1000, ms_since * 2 + 2))); // originally 24 } function game_loop() { - // back in the day pretty much everything was in here [11/08/22] - if (!server.live) { - return setTimeout(game_loop, 10); - } - var ms_since = 32; - try { - var now_date = new Date(); - // for(name in instances) - // { - // var instance=instances[name]; - // if(mssince(instance.last_update)>75) update_instance(instance); - // } - for (var id in server.s) { - server.s[id].ms -= 10; - if (server.s[id].ms < 0) { - delete server.s[id]; - } - } + // back in the day pretty much everything was in here [11/08/22] + if (!server.live) { + return setTimeout(game_loop, 10); + } + var ms_since = 32; + try { + var now_date = new Date(); + // for(name in instances) + // { + // var instance=instances[name]; + // if(mssince(instance.last_update)>75) update_instance(instance); + // } + for (var id in server.s) { + server.s[id].ms -= 10; + if (server.s[id].ms < 0) { + delete server.s[id]; + } + } - ms_since = mssince(now_date); - if (ms_since > B.game_loop_log_edge) { - server_log("Main loop took " + mssince(now_date) + "ms", 1); - } - perfc.game_loop[ms_since] = (perfc.game_loop[ms_since] || 0) + 1; - perfc.game_loops += 1; - } catch (e) { - log_trace("#X Main loop error", e); - } - setTimeout(game_loop, max(28, min(1000, ms_since * 2 + 2))); // originally 24 + ms_since = mssince(now_date); + if (ms_since > B.game_loop_log_edge) { + server_log("Main loop took " + mssince(now_date) + "ms", 1); + } + perfc.game_loop[ms_since] = (perfc.game_loop[ms_since] || 0) + 1; + perfc.game_loops += 1; + } catch (e) { + log_trace("#X Main loop error", e); + } + setTimeout(game_loop, max(28, min(1000, ms_since * 2 + 2))); // originally 24 } var lrid = 0; function aura_loop() { - try { - lrid++; - for (var id in players) { - var player = players[id]; - if (!player || !player.aura || player.rid % 5 != lrid % 5) { - continue; - } - for (var aid in player.aura) { - for (var pid in instances[player.in].players) { - if (!is_same(instances[player.in].players[pid], player, 3)) { - continue; - } - if (distance(instances[player.in].players[pid], player) > 200) { - continue; - } - instances[player.in].players[pid].s[aid] = { ms: G.conditions[aid].duration || 30000, f: player.name }; - if (G.conditions[aid].attr0) { - instances[player.in].players[pid].s[aid][G.conditions[aid].attr0] = player.aura[aid].attr0; - } - resend(instances[player.in].players[pid], "u+cid"); - } - } - } - } catch (e) { - log_trace("#X aura loop error", e); - } - setTimeout(aura_loop, 1000); + try { + lrid++; + for (var id in players) { + var player = players[id]; + if (!player || !player.aura || player.rid % 5 != lrid % 5) { + continue; + } + for (var aid in player.aura) { + for (var pid in instances[player.in].players) { + if (!is_same(instances[player.in].players[pid], player, 3)) { + continue; + } + if (distance(instances[player.in].players[pid], player) > 200) { + continue; + } + instances[player.in].players[pid].s[aid] = { ms: G.conditions[aid].duration || 30000, f: player.name }; + if (G.conditions[aid].attr0) { + instances[player.in].players[pid].s[aid][G.conditions[aid].attr0] = player.aura[aid].attr0; + } + resend(instances[player.in].players[pid], "u+cid"); + } + } + } + } catch (e) { + log_trace("#X aura loop error", e); + } + setTimeout(aura_loop, 1000); } setTimeout(game_loop, 10); @@ -13005,773 +13005,773 @@ setTimeout(aura_loop, 10); setInterval(count_unique_users, 6000); setInterval(function () { - try { - for (var id in observers) { - var observer = observers[id]; - if (observer.player && get_player(observer.player.name)) { - var player = get_player(observer.player.name); - if (simple_distance(observer, player) > 200) { - transport_observer_to( - observer, - player.in, - player.map, - player.x, - player.y + ((observer.socket.desktop && 120) || 0), - ); - } - } else { - if (simple_distance(observer, { map: observer_map, in: observer_map, x: observer_x, y: observer_y }) > 200) { - transport_observer_to( - observer, - observer_map, - observer_map, - observer_x, - observer_y + ((observer.socket.desktop && 120) || 0), - ); - } - } - } - } catch (e) { - log_trace("#X observer loop2 error", e); - } + try { + for (var id in observers) { + var observer = observers[id]; + if (observer.player && get_player(observer.player.name)) { + var player = get_player(observer.player.name); + if (simple_distance(observer, player) > 200) { + transport_observer_to( + observer, + player.in, + player.map, + player.x, + player.y + ((observer.socket.desktop && 120) || 0), + ); + } + } else { + if (simple_distance(observer, { map: observer_map, in: observer_map, x: observer_x, y: observer_y }) > 200) { + transport_observer_to( + observer, + observer_map, + observer_map, + observer_x, + observer_y + ((observer.socket.desktop && 120) || 0), + ); + } + } + } + } catch (e) { + log_trace("#X observer loop2 error", e); + } }, 13200); setInterval(function () { - try { - if (is_pvp) { - return; - } - var names = []; - var player; - total_merchants = 0; - for (var id in players) { - player = players[id]; - if ( - !player.npc && - player.last.attack && - player.map == player.in && - ssince(player.last.attack) < 3 && - !G.maps[player.map].pvp - ) { - names.push(player.name); - } - if (player.p && player.p.stand) { - total_merchants += 1; - } - } - if (names.length) { - player = get_player(names[floor(Math.random() * names.length)]); - observer_map = player.map; - observer_x = parseInt(player.x); - observer_y = parseInt(player.y); - server_log("Set the observer location: " + [observer_map, observer_x, observer_y]); - } else { - observer_map = merchant_map; - observer_x = merchant_x; - observer_y = merchant_y; - } - } catch (e) { - log_trace("#X observer loop error", e); - } + try { + if (is_pvp) { + return; + } + var names = []; + var player; + total_merchants = 0; + for (var id in players) { + player = players[id]; + if ( + !player.npc && + player.last.attack && + player.map == player.in && + ssince(player.last.attack) < 3 && + !G.maps[player.map].pvp + ) { + names.push(player.name); + } + if (player.p && player.p.stand) { + total_merchants += 1; + } + } + if (names.length) { + player = get_player(names[floor(Math.random() * names.length)]); + observer_map = player.map; + observer_x = parseInt(player.x); + observer_y = parseInt(player.y); + server_log("Set the observer location: " + [observer_map, observer_x, observer_y]); + } else { + observer_map = merchant_map; + observer_x = merchant_x; + observer_y = merchant_y; + } + } catch (e) { + log_trace("#X observer loop error", e); + } }, 4000); setTimeout( - function () { - setInterval( - function () { - for (var id in players) { - var player = players[id]; - if (player.type == "merchant" && player.p.stand) { - var xp = 1000; - if (player.level <= 40) { - xp = G.levels[player.level] / 100; - } else if (player.level <= 60) { - xp = G.levels[player.level] / 200; - } else if (player.level <= 70) { - xp = G.levels[player.level] / 400; - } else { - xp = G.levels[70] / 400; - } - xp = parseInt(Math.round(xp)); - player.xp += xp; - player.socket.emit("game_log", "Gained " + to_pretty_num(xp) + " marketing XP"); - player.socket.emit("disappearing_text", { - message: "+" + xp, - x: player.x, - y: player.y - 32, - args: { color: "gray", size: "large" }, - }); - resend(player, "reopen+u+cid"); - } - } - }, - 3 * 60 * 60 * 1000, - ); //3 - }, - 1 * 60 * 60 * 1000, + function () { + setInterval( + function () { + for (var id in players) { + var player = players[id]; + if (player.type == "merchant" && player.p.stand) { + var xp = 1000; + if (player.level <= 40) { + xp = G.levels[player.level] / 100; + } else if (player.level <= 60) { + xp = G.levels[player.level] / 200; + } else if (player.level <= 70) { + xp = G.levels[player.level] / 400; + } else { + xp = G.levels[70] / 400; + } + xp = parseInt(Math.round(xp)); + player.xp += xp; + player.socket.emit("game_log", "Gained " + to_pretty_num(xp) + " marketing XP"); + player.socket.emit("disappearing_text", { + message: "+" + xp, + x: player.x, + y: player.y - 32, + args: { color: "gray", size: "large" }, + }); + resend(player, "reopen+u+cid"); + } + } + }, + 3 * 60 * 60 * 1000, + ); //3 + }, + 1 * 60 * 60 * 1000, ); //1 setInterval(function () { - try { - var edge = future_s(-120); - for (var id in players) { - if (0 && players[id].last_ipass < edge) { - players[id].ban = "ipass"; - players[id].socket.emit("disconnect_reason", "Failed to check in. Your network might be too slow."); - players[id].socket.disconnect(); - } - } - } catch (e) { - log_trace("#X ipass loop error", e); - } + try { + var edge = future_s(-120); + for (var id in players) { + if (0 && players[id].last_ipass < edge) { + players[id].ban = "ipass"; + players[id].socket.emit("disconnect_reason", "Failed to check in. Your network might be too slow."); + players[id].socket.disconnect(); + } + } + } catch (e) { + log_trace("#X ipass loop error", e); + } }, 132000); setTimeout(function () { - setInterval(function () { - try { - for (var id in instances) { - var instance = instances[id]; - if ( - B.pause_instances && - !instance.paused && - !instance.operators && - Object.keys(instance.players).length <= instance.npcs && - !Object.keys(instance.observers).length && - ssince(instance.last_player) > 50 - ) { - pause_instance(instance); - } - if (Object.keys(instance.players).length > instance.npcs || Object.keys(instance.observers).length) { - instance.last_player = new Date(); - } - } - } catch (e) { - log_trace("#X pause loop error", e); - } - }, 2000); + setInterval(function () { + try { + for (var id in instances) { + var instance = instances[id]; + if ( + B.pause_instances && + !instance.paused && + !instance.operators && + Object.keys(instance.players).length <= instance.npcs && + !Object.keys(instance.observers).length && + ssince(instance.last_player) > 50 + ) { + pause_instance(instance); + } + if (Object.keys(instance.players).length > instance.npcs || Object.keys(instance.observers).length) { + instance.last_player = new Date(); + } + } + } catch (e) { + log_trace("#X pause loop error", e); + } + }, 2000); }, 60000); // delay for a minute, so citizens move around setInterval(function () { - try { - for (var oname in parties) { - send_party_update(oname); - } - } catch (e) { - log_trace("#X party loop error", e); - } + try { + for (var oname in parties) { + send_party_update(oname); + } + } catch (e) { + log_trace("#X party loop error", e); + } }, 60000); setInterval(function () { - try { - for (var id in players) { - var player = players[id]; - players[id].pdps *= 0.8; - player.p.minutes++; - trade_slots.forEach(function (slot) { - if (player.slots[slot] && player.slots[slot].giveaway) { - player.slots[slot].giveaway--; - if (!player.slots[slot].giveaway) { - var list = []; - player.slots[slot].list.forEach(function (p) { - var p = get_player(p); - if (p && p.esize) { - list.push(p); - } else if (!mode.prevent_external) { - list.push({ name: p }); - } - }); - if (!list.length) { - player.slots[slot].giveaway = 5; - } else { - if (!mode.prevent_external) { - var winner = random_one(list); - var item = player.slots[slot]; - player.slots[slot] = null; - delete item.list; - delete item.giveaway; - delete item.registry; - item.gf = player.name; - item.src = "gva"; - var mitem = JSON.stringify(item); - add_to_trade_history(player, "giveaway", winner.name, cache_item(item, true)); - xy_emit(player, "game_log", { - message: winner.name + " won " + player.name + "'s giveaway of " + G.items[item.name].name + "!", - color: "#42A0DC", - confetti: winner.name, - }); - appengine_call( - "send_mail", - { - fro: player.name, - to: winner.name, - subject: "You've won a giveaway!", - message: - "Congratulations, you won " + player.name + "'s giveaway. Participants were: " + list.join(", "), - rid: randomStr(50), - retries: 5, - item: mitem, - }, - function (result) {}, - function () { - console.log("#M unsent giveaway, lost item: " + mitem); - }, - ); - } else { - var winner = random_one(list); - var item = player.slots[slot]; - player.slots[slot] = null; - delete item.list; - delete item.giveaway; - delete item.registry; - item.gf = player.name; - item.src = "gva"; - add_item(winner, item); - add_to_trade_history(player, "giveaway", winner.name, cache_item(item, true)); - xy_emit(player, "game_log", { - message: winner.name + " won " + player.name + "'s giveaway of " + G.items[item.name].name + "!", - color: "#42A0DC", - confetti: winner.name, - }); - resend(winner, "reopen"); - } - } - } - player.cslots[slot] = cache_item(player.slots[slot], true); - resend(player, "u+cid" + ((!player.slots[slot] && "+reopen") || "")); - } - }); - } - } catch (e) { - log_trace("#X decay loop error", e); - } + try { + for (var id in players) { + var player = players[id]; + players[id].pdps *= 0.8; + player.p.minutes++; + trade_slots.forEach(function (slot) { + if (player.slots[slot] && player.slots[slot].giveaway) { + player.slots[slot].giveaway--; + if (!player.slots[slot].giveaway) { + var list = []; + player.slots[slot].list.forEach(function (p) { + var p = get_player(p); + if (p && p.esize) { + list.push(p); + } else if (!mode.prevent_external) { + list.push({ name: p }); + } + }); + if (!list.length) { + player.slots[slot].giveaway = 5; + } else { + if (!mode.prevent_external) { + var winner = random_one(list); + var item = player.slots[slot]; + player.slots[slot] = null; + delete item.list; + delete item.giveaway; + delete item.registry; + item.gf = player.name; + item.src = "gva"; + var mitem = JSON.stringify(item); + add_to_trade_history(player, "giveaway", winner.name, cache_item(item, true)); + xy_emit(player, "game_log", { + message: winner.name + " won " + player.name + "'s giveaway of " + G.items[item.name].name + "!", + color: "#42A0DC", + confetti: winner.name, + }); + appengine_call( + "send_mail", + { + fro: player.name, + to: winner.name, + subject: "You've won a giveaway!", + message: + "Congratulations, you won " + player.name + "'s giveaway. Participants were: " + list.join(", "), + rid: randomStr(50), + retries: 5, + item: mitem, + }, + function (result) {}, + function () { + console.log("#M unsent giveaway, lost item: " + mitem); + }, + ); + } else { + var winner = random_one(list); + var item = player.slots[slot]; + player.slots[slot] = null; + delete item.list; + delete item.giveaway; + delete item.registry; + item.gf = player.name; + item.src = "gva"; + add_item(winner, item); + add_to_trade_history(player, "giveaway", winner.name, cache_item(item, true)); + xy_emit(player, "game_log", { + message: winner.name + " won " + player.name + "'s giveaway of " + G.items[item.name].name + "!", + color: "#42A0DC", + confetti: winner.name, + }); + resend(winner, "reopen"); + } + } + } + player.cslots[slot] = cache_item(player.slots[slot], true); + resend(player, "u+cid" + ((!player.slots[slot] && "+reopen") || "")); + } + }); + } + } catch (e) { + log_trace("#X decay loop error", e); + } }, 60000); var a_score = {}; // announcement score setInterval(function () { - try { - for (var id in a_score) { - a_score[id] *= 0.99; - } - } catch (e) { - log_trace("#X ascore loop error", e); - } + try { + for (var id in a_score) { + a_score[id] *= 0.99; + } + } catch (e) { + log_trace("#X ascore loop error", e); + } }, 10000); setInterval(function () { - try { - for (var id in players) { - var player = players[id]; - player.xrange = min(25, player.xrange + 5); - } - } catch (e) { - log_trace("#X xrange loop error", e); - } + try { + for (var id in players) { + var player = players[id]; + player.xrange = min(25, player.xrange + 5); + } + } catch (e) { + log_trace("#X xrange loop error", e); + } }, 1000); setInterval(function () { - try { - var c = new Date(); - for (var id in players) { - var player = players[id]; - if (player.auth_id && player.p.first === undefined && !player.first_u_call) { - function first_call(player) { - player.first_u_call = appengine_call( - "is_first", - { auth: player.auth, auth_id: player.auth_id, character: player.real_id, suffix: "/" + player.id }, - function (result) { - if (result.first) { - player.p.first = true; - } else { - player.p.first = false; - } - }, - function () { - delete player.first_u_call; - }, - ); - } - first_call(player); - } - if (player.p.first && !player.p.dt.first) { - realm_broadcast("server_message", { message: player.name + " joined Adventure Land!", color: "#24A2FA" }); - player.p.dt.first = future_h(24 * 7); - } else if (player.p.dt.first && player.p.dt.first > c) { - player.paura = player.paura || {}; - player.paura["newcomersblessing"] = { attr0: 0, attr1: 0 }; - } - } - } catch (e) { - log_trace("#X first_character loop error", e); - } + try { + var c = new Date(); + for (var id in players) { + var player = players[id]; + if (player.auth_id && player.p.first === undefined && !player.first_u_call) { + function first_call(player) { + player.first_u_call = appengine_call( + "is_first", + { auth: player.auth, auth_id: player.auth_id, character: player.real_id, suffix: "/" + player.id }, + function (result) { + if (result.first) { + player.p.first = true; + } else { + player.p.first = false; + } + }, + function () { + delete player.first_u_call; + }, + ); + } + first_call(player); + } + if (player.p.first && !player.p.dt.first) { + realm_broadcast("server_message", { message: player.name + " joined Adventure Land!", color: "#24A2FA" }); + player.p.dt.first = future_h(24 * 7); + } else if (player.p.dt.first && player.p.dt.first > c) { + player.paura = player.paura || {}; + player.paura["newcomersblessing"] = { attr0: 0, attr1: 0 }; + } + } + } catch (e) { + log_trace("#X first_character loop error", e); + } }, 4000); function projectiles_loop() { - var now = new Date(); - for (var id in projectiles) { - try { - if (projectiles[id].eta <= now) { - var projectile = projectiles[id]; - delete projectiles[id]; - complete_attack(projectile.attacker, projectile.target, projectile); - } - } catch (e) { - log_trace("#X projectile loop error", e); - } - } + var now = new Date(); + for (var id in projectiles) { + try { + if (projectiles[id].eta <= now) { + var projectile = projectiles[id]; + delete projectiles[id]; + complete_attack(projectile.attacker, projectile.target, projectile); + } + } catch (e) { + log_trace("#X projectile loop error", e); + } + } } setInterval(projectiles_loop, 7); var accel = 1; if (mode.fast_mlevels) { - accel = 100; + accel = 100; } setInterval(function () { - try { - for (var id in instances) { - for (var mid in instances[id].monsters || []) { - var monster = instances[id].monsters[mid]; - if ( - G.monsters[monster.type].cute || - G.monsters[monster.type].peaceful || - G.monsters[monster.type].stationary || - monster.target - ) { - continue; - } - var exp = Math.pow(2, (monster.level - 1) * 0.3); - var mult = 1; - if (monster["1hp"]) { - mult = 200; - } else if (G.monsters[monster.type].special) { - mult = 20; - } - if ( - mssince(monster.last_level) > max((180000 * exp * mult) / accel, (monster.max_hp * mult * 30 * exp) / accel) - ) { - if (monster.temp) { - remove_monster(monster); - } else { - level_monster(monster); - } - } - } - } - } catch (e) { - log_trace("#X mlevel loop error", e); - } + try { + for (var id in instances) { + for (var mid in instances[id].monsters || []) { + var monster = instances[id].monsters[mid]; + if ( + G.monsters[monster.type].cute || + G.monsters[monster.type].peaceful || + G.monsters[monster.type].stationary || + monster.target + ) { + continue; + } + var exp = Math.pow(2, (monster.level - 1) * 0.3); + var mult = 1; + if (monster["1hp"]) { + mult = 200; + } else if (G.monsters[monster.type].special) { + mult = 20; + } + if ( + mssince(monster.last_level) > max((180000 * exp * mult) / accel, (monster.max_hp * mult * 30 * exp) / accel) + ) { + if (monster.temp) { + remove_monster(monster); + } else { + level_monster(monster); + } + } + } + } + } catch (e) { + log_trace("#X mlevel loop error", e); + } }, 16000 / accel); setInterval(function () { - try { - for (var id in instances) { - for (var mid in instances[id].monsters || []) { - var monster = instances[id].monsters[mid]; - if (G.monsters[monster.type].cute || G.monsters[monster.type].stationary || !monster.target) { - continue; - } - var target = get_player(monster.target); - if (!target || (target.targets > 1 && monster.hp < monster.max_hp * 0.23)) { - continue; - } - monster.extra_gold = - (monster.extra_gold || 0) + - ((min(min(2400, target.attack) / 220.0 + monster.attack / 1.2, 55) + 19) * - ((gameplay == "hardcore" && 100) || 1)) / - (max((target.targets - 1) * (target.targets - 1), 1) || 1); - } - } - } catch (e) { - log_trace("#X mgold loop error", e); - } + try { + for (var id in instances) { + for (var mid in instances[id].monsters || []) { + var monster = instances[id].monsters[mid]; + if (G.monsters[monster.type].cute || G.monsters[monster.type].stationary || !monster.target) { + continue; + } + var target = get_player(monster.target); + if (!target || (target.targets > 1 && monster.hp < monster.max_hp * 0.23)) { + continue; + } + monster.extra_gold = + (monster.extra_gold || 0) + + ((min(min(2400, target.attack) / 220.0 + monster.attack / 1.2, 55) + 19) * + ((gameplay == "hardcore" && 100) || 1)) / + (max((target.targets - 1) * (target.targets - 1), 1) || 1); + } + } + } catch (e) { + log_trace("#X mgold loop error", e); + } }, 2000); setInterval(function () { - try { - for (var id in players) { - var player = players[id]; - if (Math.random() < 1.0 / (6 * 15)) { - // once every 15 hours - var slots = []; - character_slots.forEach(function (slot) { - if ( - player.slots[slot] && - (G.items[player.slots[slot].name].upgrade || G.items[player.slots[slot].name].compound) - ) { - slots.push(slot); - } - }); - if (!slots.length) { - continue; - } - var slot = random_one(slots); - player.slots[slot].grace = (player.slots[slot].grace || 0) + 0.4; - console.log("random grace " + player.name + " " + slot + " " + player.slots[slot].grace); - } - if (player.computer && Math.random() < 1.0 / (6 * 1)) { - // once every hour - for (var s in player.slots) { - if (player.slots[s] && G.items[player.slots[s].name].charge) { - if (player.slots[s].charges > G.items[player.slots[s].name].charge && Math.random() < 0.96) { - continue; - } - player.slots[s].charges = (player.slots[s].charges || 0) + 1; - player.cslots[s] = cache_item(player.slots[s]); - } - } - for (var i = 0; i < player.items.length; i++) { - if (player.items[i] && G.items[player.items[i].name].charge) { - if (player.items[i].charges > G.items[player.items[i].name].charge && Math.random() < 0.96) { - continue; - } - player.items[i].charges = (player.items[i].charges || 0) + 1; - player.citems[i] = cache_item(player.items[i]); - } - } - } - } - } catch (e) { - log_trace("#X random grace loop error", e); - } + try { + for (var id in players) { + var player = players[id]; + if (Math.random() < 1.0 / (6 * 15)) { + // once every 15 hours + var slots = []; + character_slots.forEach(function (slot) { + if ( + player.slots[slot] && + (G.items[player.slots[slot].name].upgrade || G.items[player.slots[slot].name].compound) + ) { + slots.push(slot); + } + }); + if (!slots.length) { + continue; + } + var slot = random_one(slots); + player.slots[slot].grace = (player.slots[slot].grace || 0) + 0.4; + console.log("random grace " + player.name + " " + slot + " " + player.slots[slot].grace); + } + if (player.computer && Math.random() < 1.0 / (6 * 1)) { + // once every hour + for (var s in player.slots) { + if (player.slots[s] && G.items[player.slots[s].name].charge) { + if (player.slots[s].charges > G.items[player.slots[s].name].charge && Math.random() < 0.96) { + continue; + } + player.slots[s].charges = (player.slots[s].charges || 0) + 1; + player.cslots[s] = cache_item(player.slots[s]); + } + } + for (var i = 0; i < player.items.length; i++) { + if (player.items[i] && G.items[player.items[i].name].charge) { + if (player.items[i].charges > G.items[player.items[i].name].charge && Math.random() < 0.96) { + continue; + } + player.items[i].charges = (player.items[i].charges || 0) + 1; + player.citems[i] = cache_item(player.items[i]); + } + } + } + } + } catch (e) { + log_trace("#X random grace loop error", e); + } }, 10 * 60000); // every 10 minutes setInterval( - function () { - try { - server_loot(); - } catch (e) { - log_trace("#X server loot error", e); - } - }, - 4 * 60 * 60 * 1000, + function () { + try { + server_loot(); + } catch (e) { + log_trace("#X server loot error", e); + } + }, + 4 * 60 * 60 * 1000, ); setInterval(function () { - try { - if (server.live) { - broadcast_e(); - } - } catch (e) { - log_trace("#X broadcast error", e); - } + try { + if (server.live) { + broadcast_e(); + } + } catch (e) { + log_trace("#X broadcast error", e); + } }, 24 * 1000); function sync_loop() { - function check_for_delays(player) { - var limit = 6; - if (player.mounting && msince(player.mounting) > limit && !player.mount_issue) { - server_log("#X SEVERE: " + limit + " minutes and still mounting: " + player.name, 1); - player.mount_issue = new Date(); - } - if (player.unmounting && msince(player.unmounting) > limit && !player.unmount_issue) { - server_log("#X SEVERE: " + limit + " minutes and still unmounting: " + player.name, 1); - player.unmount_issue = new Date(); - } - if (player.sync_call && msince(player.last_sync) > limit && !player.sync_issue) { - server_log("#X SEVERE: " + limit + " minutes and still syncing: " + player.name, 1); - // player.sync_issue=new Date(); - delete player.sync_call; - } - if (player.stopping && msince(player.stopping) > limit && !player.stop_issue) { - server_log("#X SEVERE: " + limit + " minutes and still stopping: " + player.name, 1); - player.stop_issue = new Date(); - } - } - function mount_call(player) { - // player.last_sync=new Date(); - sync doesn't happen at mount - maybe it should [16/08/17] - player.mount_call = appengine_call( - "mount_user", - { auth: player.auth, character: player.real_id, suffix: "/" + player.id, to: player.mount_to }, - function (result) { - server_log("mount_user: " + player.name + " owner: " + player.owner, 1); - delete player.mounting; - delete player.mount_call; - if (result.failed) { - server_log( - "mount_user[failed]: " + player.name + " in-bank: " + result.name + " result: " + JSON.stringify(result), - 1, - ); - player.socket.emit("game_response", { response: "bank_opx", name: result.name, reason: result.reason }); - return; - } - player.user = result.user; - init_bank(player); - if (players[player.socket.id]) { - transport_player_to(player, player.mount_to, player.mount_s); - resend(player); - } - }, - function () { - delete player.mounting; - delete player.mount_call; - server_log("#X SEVERE-ish: Mount failed for " + player.name, 1); - }, - ); - } - function unmount_call(player) { - player.last_sync = new Date(); - init_bank_exit(player); - player.unmount_call = appengine_call( - "sync_character", - { - auth: player.auth, - character: player.real_id, - data: player_to_server(player, "sync"), - user_data: player.user || "", - retries: 100, - unmount: 1, - suffix: "/" + player.name + "/unmount", - }, - function (result) { - server_log( - "unmount_user[sync]: " + player.name + " owner: " + player.owner + " result: " + JSON.stringify(result), - 1, - ); - delete player.unmount_call; - if (result.failed) { - server_log("unmount_user[failed]: " + player.name + " owner: " + player.owner, 1); - return; - } - delete player.unmounting; - player.user = null; - player.cuser = null; - if (players[player.socket.id] && player.unmount_to) { - transport_player_to(player, player.unmount_to, player.unmount_s); - } - if (players[player.socket.id]) { - resend(player); - } - }, - function () { - server_log("#X SEVERE: Unmount failed for " + player.name, 1); - delete player.unmounting; - delete player.unmount_call; - if (players[player.socket.id]) { - players[socket.id].socket.disconnect(); - } - }, - ); - } - function sync_call(player) { - player.last_sync = new Date(); - player.sync_call = appengine_call( - "sync_character", - { - auth: player.auth, - character: player.real_id, - data: player_to_server(player, "sync"), - user_data: player.user || "", - retries: 0, - suffix: "/" + player.name, - }, - function (result) { - if (result && result.reason == "notingame") { - server_log("#X SEVERE: sync notingame disconnect for " + player.name, 1); - try { - player.socket.disconnect(); - } catch (e) {} - } - delete player.sync_call; - }, - function () { - delete player.sync_call; - }, - ); - } - function stop_call(player) { - var bank = false; - if (player.user) { - bank = true; - } - player.stopping = new Date(); - init_player_exit(player); - if (player.unmount_call) { - player.unmount_call.retries = 0; - } - player.stop_call = appengine_call( - "stop_character", - { - auth: player.auth, - character: player.real_id, - data: player_to_server(player), - user_data: player.user || "", - retries: CINF, - suffix: "/" + player.name, - }, - function (result) { - server_log( - "stop_character: " + - player.name + - " owner: " + - player.owner + - " bank: " + - bank + - " result: " + - JSON.stringify(result), - 1, - ); - if (result.done) { - delete dc_players[player.real_id]; - } - delete player.stop_call; - }, - function () { - server_log("#X SEVERE: stop_character failed for " + player.name, 1); - delete player.stop_call; - }, - ); - } + function check_for_delays(player) { + var limit = 6; + if (player.mounting && msince(player.mounting) > limit && !player.mount_issue) { + server_log("#X SEVERE: " + limit + " minutes and still mounting: " + player.name, 1); + player.mount_issue = new Date(); + } + if (player.unmounting && msince(player.unmounting) > limit && !player.unmount_issue) { + server_log("#X SEVERE: " + limit + " minutes and still unmounting: " + player.name, 1); + player.unmount_issue = new Date(); + } + if (player.sync_call && msince(player.last_sync) > limit && !player.sync_issue) { + server_log("#X SEVERE: " + limit + " minutes and still syncing: " + player.name, 1); + // player.sync_issue=new Date(); + delete player.sync_call; + } + if (player.stopping && msince(player.stopping) > limit && !player.stop_issue) { + server_log("#X SEVERE: " + limit + " minutes and still stopping: " + player.name, 1); + player.stop_issue = new Date(); + } + } + function mount_call(player) { + // player.last_sync=new Date(); - sync doesn't happen at mount - maybe it should [16/08/17] + player.mount_call = appengine_call( + "mount_user", + { auth: player.auth, character: player.real_id, suffix: "/" + player.id, to: player.mount_to }, + function (result) { + server_log("mount_user: " + player.name + " owner: " + player.owner, 1); + delete player.mounting; + delete player.mount_call; + if (result.failed) { + server_log( + "mount_user[failed]: " + player.name + " in-bank: " + result.name + " result: " + JSON.stringify(result), + 1, + ); + player.socket.emit("game_response", { response: "bank_opx", name: result.name, reason: result.reason }); + return; + } + player.user = result.user; + init_bank(player); + if (players[player.socket.id]) { + transport_player_to(player, player.mount_to, player.mount_s); + resend(player); + } + }, + function () { + delete player.mounting; + delete player.mount_call; + server_log("#X SEVERE-ish: Mount failed for " + player.name, 1); + }, + ); + } + function unmount_call(player) { + player.last_sync = new Date(); + init_bank_exit(player); + player.unmount_call = appengine_call( + "sync_character", + { + auth: player.auth, + character: player.real_id, + data: player_to_server(player, "sync"), + user_data: player.user || "", + retries: 100, + unmount: 1, + suffix: "/" + player.name + "/unmount", + }, + function (result) { + server_log( + "unmount_user[sync]: " + player.name + " owner: " + player.owner + " result: " + JSON.stringify(result), + 1, + ); + delete player.unmount_call; + if (result.failed) { + server_log("unmount_user[failed]: " + player.name + " owner: " + player.owner, 1); + return; + } + delete player.unmounting; + player.user = null; + player.cuser = null; + if (players[player.socket.id] && player.unmount_to) { + transport_player_to(player, player.unmount_to, player.unmount_s); + } + if (players[player.socket.id]) { + resend(player); + } + }, + function () { + server_log("#X SEVERE: Unmount failed for " + player.name, 1); + delete player.unmounting; + delete player.unmount_call; + if (players[player.socket.id]) { + players[socket.id].socket.disconnect(); + } + }, + ); + } + function sync_call(player) { + player.last_sync = new Date(); + player.sync_call = appengine_call( + "sync_character", + { + auth: player.auth, + character: player.real_id, + data: player_to_server(player, "sync"), + user_data: player.user || "", + retries: 0, + suffix: "/" + player.name, + }, + function (result) { + if (result && result.reason == "notingame") { + server_log("#X SEVERE: sync notingame disconnect for " + player.name, 1); + try { + player.socket.disconnect(); + } catch (e) {} + } + delete player.sync_call; + }, + function () { + delete player.sync_call; + }, + ); + } + function stop_call(player) { + var bank = false; + if (player.user) { + bank = true; + } + player.stopping = new Date(); + init_player_exit(player); + if (player.unmount_call) { + player.unmount_call.retries = 0; + } + player.stop_call = appengine_call( + "stop_character", + { + auth: player.auth, + character: player.real_id, + data: player_to_server(player), + user_data: player.user || "", + retries: CINF, + suffix: "/" + player.name, + }, + function (result) { + server_log( + "stop_character: " + + player.name + + " owner: " + + player.owner + + " bank: " + + bank + + " result: " + + JSON.stringify(result), + 1, + ); + if (result.done) { + delete dc_players[player.real_id]; + } + delete player.stop_call; + }, + function () { + server_log("#X SEVERE: stop_character failed for " + player.name, 1); + delete player.stop_call; + }, + ); + } - for (var id in players) { - if (gameplay == "hardcore" || gameplay == "test") { - return; - } - var player = players[id]; - try { - check_for_delays(player); + for (var id in players) { + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + var player = players[id]; + try { + check_for_delays(player); - if (player.unmount_call || player.mount_call || player.sync_call || player.stop_call) { - } else if (!server.live) { - player.socket.disconnect(); - } else if (player.unmounting) { - unmount_call(player); - } else if (player.mounting) { - mount_call(player); - } else if (msince(player.last_sync) > 5 && !player.unmounting && !player.mounting) { - sync_call(player); - } - } catch (e) { - log_trace("#X dc loop error1", e); - } - } - for (var id in dc_players) { - if (gameplay == "hardcore" || gameplay == "test") { - return; - } - var player = dc_players[id]; - try { - check_for_delays(player); + if (player.unmount_call || player.mount_call || player.sync_call || player.stop_call) { + } else if (!server.live) { + player.socket.disconnect(); + } else if (player.unmounting) { + unmount_call(player); + } else if (player.mounting) { + mount_call(player); + } else if (msince(player.last_sync) > 5 && !player.unmounting && !player.mounting) { + sync_call(player); + } + } catch (e) { + log_trace("#X dc loop error1", e); + } + } + for (var id in dc_players) { + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + var player = dc_players[id]; + try { + check_for_delays(player); - if (player.unmount_call || player.mount_call || player.sync_call || player.stop_call) { - } else if (!player.stop_call) { - stop_call(player); - } - } catch (e) { - log_trace("#X dc loop error2", e); - } - } + if (player.unmount_call || player.mount_call || player.sync_call || player.stop_call) { + } else if (!player.stop_call) { + stop_call(player); + } + } catch (e) { + log_trace("#X dc loop error2", e); + } + } } function server_loop() { - // #IMPORTANT: sometimes none of the success/error callbacks trigger [20/08/19] - if (server.update_call && msince(server.update_call.init) > 4) { - delete server.update_call; - } - if (server.update_call || server.stop_call) { - } else if (server.live && ssince(server.last_update) > 75) { - server.update_call = appengine_call( - "update_server", - { - keyword: variables.keyword, - id: server_id, - players: Object.keys(players).length, - observers: Object.keys(observers).length, - merchants: total_merchants, - total_players: total_players, - data: S, - retries: 3, - }, - function (result) { - server.last_update = new Date(); - server_log("Server update sent"); - delete server.update_call; - }, - function () { - server_log("Server update failed", 1); - delete server.update_call; - }, - ); - } else if (server.started && !server.live && !server.stopped) { - server.stop_call = appengine_call( - "stop_server", - { keyword: variables.keyword, id: server_id, retries: CINF, data: S }, - function (result) { - server_log("stop_server success", 1); - // these are here as shutting the server first disrupts connections in actual servers - // io.close(); app.close(); - decided to comment these out, they don't do anything now [23/09/16] - server.stopped = true; - delete server.stop_call; - }, - ); - } else if ( - server.stopped && - ((!Object.keys(dc_players).length && !Object.keys(players).length) || gameplay == "hardcore" || gameplay == "test") - ) { - process.exit(); - } else if (server.stopped) { - sync_loop(); - } + // #IMPORTANT: sometimes none of the success/error callbacks trigger [20/08/19] + if (server.update_call && msince(server.update_call.init) > 4) { + delete server.update_call; + } + if (server.update_call || server.stop_call) { + } else if (server.live && ssince(server.last_update) > 75) { + server.update_call = appengine_call( + "update_server", + { + keyword: variables.keyword, + id: server_id, + players: Object.keys(players).length, + observers: Object.keys(observers).length, + merchants: total_merchants, + total_players: total_players, + data: S, + retries: 3, + }, + function (result) { + server.last_update = new Date(); + server_log("Server update sent"); + delete server.update_call; + }, + function () { + server_log("Server update failed", 1); + delete server.update_call; + }, + ); + } else if (server.started && !server.live && !server.stopped) { + server.stop_call = appengine_call( + "stop_server", + { keyword: variables.keyword, id: server_id, retries: CINF, data: S }, + function (result) { + server_log("stop_server success", 1); + // these are here as shutting the server first disrupts connections in actual servers + // io.close(); app.close(); - decided to comment these out, they don't do anything now [23/09/16] + server.stopped = true; + delete server.stop_call; + }, + ); + } else if ( + server.stopped && + ((!Object.keys(dc_players).length && !Object.keys(players).length) || gameplay == "hardcore" || gameplay == "test") + ) { + process.exit(); + } else if (server.stopped) { + sync_loop(); + } } setInterval(sync_loop, 24000); setInterval(server_loop, 1000); function shutdown() { - server_log("shutdown", 1); - server.live = false; - if (!server.exists) { - server_loot("all"); - } - sync_loop(); + server_log("shutdown", 1); + server.live = false; + if (!server.exists) { + server_loot("all"); + } + sync_loop(); } function shutdown_routine() { - server_log("shutdown_routine", 1); - server.shutdown = true; - for (var name in instances) { - for (var id in instances[name].monsters) { - var monster = instances[name].monsters[id]; - if (monster.target) { - monster.u = true; - monster.cid++; - monster.mult = (monster.max_hp - monster.hp) / monster.max_hp; - monster.hp = 1; - } - } - } - var seconds = 20; - if (is_sdk) { - seconds = 1; - } else if (gameplay == "hardcore") { - seconds = 240; - } - workers.forEach(function (worker) { - try { - worker.postMessage({ type: "exit" }); - } catch (e) { - console.log(e); - } - }); - for (var i = 0; i < seconds; i++) { - function m(x) { - return function () { - broadcast("server_message", { message: "Server shutdown in: " + x, color: "#DD1B50", nod: true, log: true }); - }; - } - setTimeout(m(seconds - i), i * 1000); - } - broadcast("eval", { code: "call_code_function('trigger_event','shutdown',{seconds:" + seconds + "})" }); - setTimeout(shutdown, seconds * 1000); - if (!is_sdk && region == "EU" && server_name == "I") { - discord_call("Game update sequence initiated. Servers are shutting down in 20 seconds!"); - } + server_log("shutdown_routine", 1); + server.shutdown = true; + for (var name in instances) { + for (var id in instances[name].monsters) { + var monster = instances[name].monsters[id]; + if (monster.target) { + monster.u = true; + monster.cid++; + monster.mult = (monster.max_hp - monster.hp) / monster.max_hp; + monster.hp = 1; + } + } + } + var seconds = 20; + if (is_sdk) { + seconds = 1; + } else if (gameplay == "hardcore") { + seconds = 240; + } + workers.forEach(function (worker) { + try { + worker.postMessage({ type: "exit" }); + } catch (e) { + console.log(e); + } + }); + for (var i = 0; i < seconds; i++) { + function m(x) { + return function () { + broadcast("server_message", { message: "Server shutdown in: " + x, color: "#DD1B50", nod: true, log: true }); + }; + } + setTimeout(m(seconds - i), i * 1000); + } + broadcast("eval", { code: "call_code_function('trigger_event','shutdown',{seconds:" + seconds + "})" }); + setTimeout(shutdown, seconds * 1000); + if (!is_sdk && region == "EU" && server_name == "I") { + discord_call("Game update sequence initiated. Servers are shutting down in 20 seconds!"); + } } function exit_handler(options, err) { - if (options.exit) { - server_log("exit_handler", 1); - shutdown_routine(); - } + if (options.exit) { + server_log("exit_handler", 1); + shutdown_routine(); + } } process.on("exit", exit_handler.bind(null, { cleanup: true })); process.on("SIGHUP", exit_handler.bind(null, { exit: true })); diff --git a/node/server_functions.js b/node/server_functions.js index d158207..9ef2f8c 100644 --- a/node/server_functions.js +++ b/node/server_functions.js @@ -4,13 +4,13 @@ var range_check = require("range_check"); var protobuf = require("protobufjs"); var ByteBuffer = require("bytebuffer"); // Steam decryption var false_socket = { - emit: function (a, b) { - if (is_sdk && !server.shutdown) { - console.log([a, b]); - } - }, - total_calls: 0, - calls: [], + emit: function (a, b) { + if (is_sdk && !server.shutdown) { + console.log([a, b]); + } + }, + total_calls: 0, + calls: [], }; var current_socket = false_socket; var call_modifier = 1; @@ -18,3514 +18,3514 @@ var ls_method = ""; var server_start = new Date(); var timers = { - pinkgoo: false, - snowman: false, - wabbit: false, - hardcore: new Date(), - hide_and_seek: false, - cyberland: new Date(), + pinkgoo: false, + snowman: false, + wabbit: false, + hardcore: new Date(), + hide_and_seek: false, + cyberland: new Date(), }; var npcs = {}; var signups = {}; var hide_and_seek = {}; var stats = { - kills: {}, + kills: {}, }; var edges = { - next_goldenbat: 80000, - next_cutebee: 480000, + next_goldenbat: 80000, + next_cutebee: 480000, }; var NPC_prefix = "NPC0000000000000000NPC"; function sprocess_game_data() { - for (var name in G.items) { - var def = G.items[name]; - def.igrade = calculate_item_grade(def); - if (!def.igrade) { - def.igrace = 1; - } else if (def.igrade == 1) { - def.igrace = -1; - } else if (def.igrade == 2) { - def.igrace = -2; - } + for (var name in G.items) { + var def = G.items[name]; + def.igrade = calculate_item_grade(def); + if (!def.igrade) { + def.igrace = 1; + } else if (def.igrade == 1) { + def.igrace = -1; + } else if (def.igrade == 2) { + def.igrace = -2; + } - def.a = 0; + def.a = 0; - if (def.compound) { - var u_v = calculate_item_value({ name: name, level: 3 + (def.edge || 0) }); - def.a = parseInt(round(u_v / 5000000)); - } else if (def.upgrade) { - var u_v = calculate_item_value({ name: name, level: 7 + (def.edge || 0) }); - def.a = parseInt(round(u_v / 5000000)); - } else { - var u_v = calculate_item_value({ name: name }); - def.a = parseInt(round(u_v / 5000000)); - } - if (def.event) { - def.a = 2; - } - if (def.rare) { - def.a = 12; - } - } - G.items.lostearring.igrade = 2; - if (gameplay == "test") { - test_logic(); - } - if (gameplay == "hardcore") { - hardcore_logic(); - for (var m in D.drops.monsters) { - for (var i = 0; i < D.drops.monsters[m].length; i++) { - D.drops.monsters[m][i][0] *= 200; - if (D.drops.monsters[m][i][0] < 0.0001) { - D.drops.monsters[m][i][0] *= 12.0; - } - D.drops.monsters[m][i][0] = min(1, D.drops.monsters[m][i][0]); - } + if (def.compound) { + var u_v = calculate_item_value({ name: name, level: 3 + (def.edge || 0) }); + def.a = parseInt(round(u_v / 5000000)); + } else if (def.upgrade) { + var u_v = calculate_item_value({ name: name, level: 7 + (def.edge || 0) }); + def.a = parseInt(round(u_v / 5000000)); + } else { + var u_v = calculate_item_value({ name: name }); + def.a = parseInt(round(u_v / 5000000)); + } + if (def.event) { + def.a = 2; + } + if (def.rare) { + def.a = 12; + } + } + G.items.lostearring.igrade = 2; + if (gameplay == "test") { + test_logic(); + } + if (gameplay == "hardcore") { + hardcore_logic(); + for (var m in D.drops.monsters) { + for (var i = 0; i < D.drops.monsters[m].length; i++) { + D.drops.monsters[m][i][0] *= 200; + if (D.drops.monsters[m][i][0] < 0.0001) { + D.drops.monsters[m][i][0] *= 12.0; + } + D.drops.monsters[m][i][0] = min(1, D.drops.monsters[m][i][0]); + } - D.drops.monsters[m].push([1.0 / 100, "glitch"]); - D.drops.monsters[m].push([1.0 / 100, "glitch"]); - D.drops.monsters[m].push([1.0 / 1000, "glitch"]); - } - for (var n in D.drops) { - if (!is_array(D.drops[n])) { - continue; - } - var total = 0; - for (var i = 0; i < D.drops[n].length; i++) { - total += D.drops[n][i][0]; - } - for (var i = 0; i < D.drops[n].length; i++) { - if (D.drops[n][i][0] < 0.001) { - D.drops[n][i][0] *= 200; - } + D.drops.monsters[m].push([1.0 / 100, "glitch"]); + D.drops.monsters[m].push([1.0 / 100, "glitch"]); + D.drops.monsters[m].push([1.0 / 1000, "glitch"]); + } + for (var n in D.drops) { + if (!is_array(D.drops[n])) { + continue; + } + var total = 0; + for (var i = 0; i < D.drops[n].length; i++) { + total += D.drops[n][i][0]; + } + for (var i = 0; i < D.drops[n].length; i++) { + if (D.drops[n][i][0] < 0.001) { + D.drops[n][i][0] *= 200; + } - if (D.drops[n][i][0] * 3 < total / D.drops[n].length) { - D.drops[n][i][0] *= 12.0; - } else if (D.drops[n][i][0] * 1.5 < total / D.drops[n].length) { - D.drops[n][i][0] *= 3.0; - } else if (D.drops[n][i][0] / 3 > total / D.drops[n].length) { - D.drops[n][i][0] /= 12.0; - } else if (D.drops[n][i][0] / 1.5 > total / D.drops[n].length) { - D.drops[n][i][0] /= 3.0; - } - } - } - for (var mname in G.maps) { - if (!D.drops.maps[mname]) { - D.drops.maps[mname] = []; - } + if (D.drops[n][i][0] * 3 < total / D.drops[n].length) { + D.drops[n][i][0] *= 12.0; + } else if (D.drops[n][i][0] * 1.5 < total / D.drops[n].length) { + D.drops[n][i][0] *= 3.0; + } else if (D.drops[n][i][0] / 3 > total / D.drops[n].length) { + D.drops[n][i][0] /= 12.0; + } else if (D.drops[n][i][0] / 1.5 > total / D.drops[n].length) { + D.drops[n][i][0] /= 3.0; + } + } + } + for (var mname in G.maps) { + if (!D.drops.maps[mname]) { + D.drops.maps[mname] = []; + } - for (var i = 0; i < D.drops.maps[mname].length; i++) { - D.drops.maps[mname][i][0] *= 200; - D.drops.maps[mname][i][0] = min(1, D.drops.maps[mname][i][0]); - } + for (var i = 0; i < D.drops.maps[mname].length; i++) { + D.drops.maps[mname][i][0] *= 200; + D.drops.maps[mname][i][0] = min(1, D.drops.maps[mname][i][0]); + } - D.drops.maps[mname].push([1.0 / 1200, "gem0"]); - } - D.drops.gem0.push([0.5, "candycane"]); - D.drops.gem0.push([0.05, "basketofeggs"]); - D.drops.monsters.iceroamer.push([0.1, "open", "statbelt"]); - D.drops.monsters.arcticbee.push([0.00001, "fclaw"]); - D.drops.monsters.minimush.push([0.00001, "throwingstars"]); - D.drops.monsters.squig.push([0.000008, "glitch"]); - D.drops.monsters.bbpompom.push([0.00008, "glitch"]); - D.drops.monsters.croc.push([0.000008, "glitch"]); - D.drops.monsters.mole.push([0.002, "gemfragment"]); - D.drops.monsters.mole.push([0.002, "gemfragment"]); - D.drops.maps.mansion.push([0.001, "lostearring"]); - D.drops.maps.mansion.push([0.001, "lostearring"]); - D.drops.monsters.croc.push([1, "seashell"]); - D.drops.monsters.croc.push([1, "seashell"]); - D.drops.maps.global = []; - D.monster_gold.bat *= 50; - D.monster_gold.bbpompom *= 75; - D.monster_gold.ghost *= 50; - events.egghunt = 0.1; - } - D.craftmap = {}; - for (var name in G.craft) { - var items = []; - G.craft[name].items.forEach(function (x) { - var name = x[1]; - if (x[2]) { - name += "+" + x[2]; - } - items.push(name); - }); - items.sort(); - var key = items.join(","); - D.craftmap[key] = name; - } - process_game_data(); - for (var name in G.monsters) { - stats.kills[name] = stats.kills[name] || 0; - } - D.base_gold = {}; - for (var mname in G.maps) { - var map = G.maps[mname]; - (map.monsters || []).forEach(function (pack) { - var def = G.monsters[pack.type]; - var drop_value = 0; - var map_value = 0; - var global_value = 0; - var hp_mult = def.hp / 1000.0; - // #BETTER IDEA# - // track damage done + dps utilisation / pack fullness - calculate gold based on that - // /1400.0 /40.000 /14000.0 originally - // 0.0624 is the original modifier - pack.gold = - def.hp * - (0.0782 + - max(def.charge - 40, 0) / 1900.0 + - ((1 - dps_multiplier(def.attack + (def.apiercing || 0) / 2 + (def.rpiercing || 0) / 2)) * 1000) / 120000.0 + - min( - (1 - damage_multiplier(def.armor || 0)) * 1000 + (def.evasion || 0) * 10 + (def.avoidance || 0) * 10, - (1 - damage_multiplier(def.resistance || 0)) * 1000 + - (def.reflection || 0) * 25 + - (def.avoidance || 0) * 10, - ) / - 12000.0) - - def.xp * 0.0172; - if ((map.pvp || map.safe_pvp) && !is_pvp) { - pack.gold *= 2; - } - drop_value = calculate_xvalue(D.drops.monsters[pack.type] || []); - map_value = calculate_xvalue(D.drops.maps[mname] || [], undefined, undefined, hp_mult); - global_value = calculate_xvalue(D.drops.maps.global || [], undefined, undefined, hp_mult); - pack.gold = pack.gold - map_value / 1.002 - drop_value * 1.002; // Originally 1.2 + D.drops.maps[mname].push([1.0 / 1200, "gem0"]); + } + D.drops.gem0.push([0.5, "candycane"]); + D.drops.gem0.push([0.05, "basketofeggs"]); + D.drops.monsters.iceroamer.push([0.1, "open", "statbelt"]); + D.drops.monsters.arcticbee.push([0.00001, "fclaw"]); + D.drops.monsters.minimush.push([0.00001, "throwingstars"]); + D.drops.monsters.squig.push([0.000008, "glitch"]); + D.drops.monsters.bbpompom.push([0.00008, "glitch"]); + D.drops.monsters.croc.push([0.000008, "glitch"]); + D.drops.monsters.mole.push([0.002, "gemfragment"]); + D.drops.monsters.mole.push([0.002, "gemfragment"]); + D.drops.maps.mansion.push([0.001, "lostearring"]); + D.drops.maps.mansion.push([0.001, "lostearring"]); + D.drops.monsters.croc.push([1, "seashell"]); + D.drops.monsters.croc.push([1, "seashell"]); + D.drops.maps.global = []; + D.monster_gold.bat *= 50; + D.monster_gold.bbpompom *= 75; + D.monster_gold.ghost *= 50; + events.egghunt = 0.1; + } + D.craftmap = {}; + for (var name in G.craft) { + var items = []; + G.craft[name].items.forEach(function (x) { + var name = x[1]; + if (x[2]) { + name += "+" + x[2]; + } + items.push(name); + }); + items.sort(); + var key = items.join(","); + D.craftmap[key] = name; + } + process_game_data(); + for (var name in G.monsters) { + stats.kills[name] = stats.kills[name] || 0; + } + D.base_gold = {}; + for (var mname in G.maps) { + var map = G.maps[mname]; + (map.monsters || []).forEach(function (pack) { + var def = G.monsters[pack.type]; + var drop_value = 0; + var map_value = 0; + var global_value = 0; + var hp_mult = def.hp / 1000.0; + // #BETTER IDEA# + // track damage done + dps utilisation / pack fullness - calculate gold based on that + // /1400.0 /40.000 /14000.0 originally + // 0.0624 is the original modifier + pack.gold = + def.hp * + (0.0782 + + max(def.charge - 40, 0) / 1900.0 + + ((1 - dps_multiplier(def.attack + (def.apiercing || 0) / 2 + (def.rpiercing || 0) / 2)) * 1000) / 120000.0 + + min( + (1 - damage_multiplier(def.armor || 0)) * 1000 + (def.evasion || 0) * 10 + (def.avoidance || 0) * 10, + (1 - damage_multiplier(def.resistance || 0)) * 1000 + + (def.reflection || 0) * 25 + + (def.avoidance || 0) * 10, + ) / + 12000.0) - + def.xp * 0.0172; + if ((map.pvp || map.safe_pvp) && !is_pvp) { + pack.gold *= 2; + } + drop_value = calculate_xvalue(D.drops.monsters[pack.type] || []); + map_value = calculate_xvalue(D.drops.maps[mname] || [], undefined, undefined, hp_mult); + global_value = calculate_xvalue(D.drops.maps.global || [], undefined, undefined, hp_mult); + pack.gold = pack.gold - map_value / 1.002 - drop_value * 1.002; // Originally 1.2 - if (def.hp <= 2000 && pack.gold < def.hp * 0.0482) { - pack.gold = def.hp * 0.0482; - } else if (pack.gold < def.hp * 0.02808) { - pack.gold = def.hp * 0.02808; - } + if (def.hp <= 2000 && pack.gold < def.hp * 0.0482) { + pack.gold = def.hp * 0.0482; + } else if (pack.gold < def.hp * 0.02808) { + pack.gold = def.hp * 0.02808; + } - //if(def.hp<2000 && pack.gold0.08 && def.hp>900) pack.gold*=0.7; + // if((pack.gold/def.hp)>0.08 && def.hp>900) pack.gold*=0.7; - // if(def.hp>=50000) pack.gold*=0.90; // better dps utilisation - don't punish this + // if(def.hp>=50000) pack.gold*=0.90; // better dps utilisation - don't punish this - pack.gold *= def.difficulty || 1; - pack.gold = parseInt(ceil(pack.gold)) || 0; - if (def.stationary || def.xp < 0) { - pack.gold = D.monster_gold[pack.type] || 0; - } - D.base_gold[pack.type] = D.base_gold[pack.type] || {}; - D.base_gold[pack.type][mname] = pack.gold; - console.log( - mname + - " " + - pack.type + - " gold: " + - pack.gold + - "[" + - parseInt(drop_value) + - "," + - parseInt(map_value) + - "] gold/hp: " + - pack.gold / def.hp + - " total%: " + - (pack.gold + drop_value + map_value) / def.hp, - ); - }); - } - for (var sname in G.sets) { - var set = G.sets[sname]; - for (var i = 2; i <= set.items.length; i++) { - set[i] = set[i] || {}; - for (var prop in set[i - 1]) { - if (set[i][prop]) { - set[i][prop] += set[i - 1][prop]; - } else { - set[i][prop] = set[i - 1][prop]; - } - } - } - } - if (is_pvp) { - D.drops.maps.global_static.push([1.0 / ((gameplay == "hardcore" && 1000) || 100000), "pvptoken"]); - } + pack.gold *= def.difficulty || 1; + pack.gold = parseInt(ceil(pack.gold)) || 0; + if (def.stationary || def.xp < 0) { + pack.gold = D.monster_gold[pack.type] || 0; + } + D.base_gold[pack.type] = D.base_gold[pack.type] || {}; + D.base_gold[pack.type][mname] = pack.gold; + console.log( + mname + + " " + + pack.type + + " gold: " + + pack.gold + + "[" + + parseInt(drop_value) + + "," + + parseInt(map_value) + + "] gold/hp: " + + pack.gold / def.hp + + " total%: " + + (pack.gold + drop_value + map_value) / def.hp, + ); + }); + } + for (var sname in G.sets) { + var set = G.sets[sname]; + for (var i = 2; i <= set.items.length; i++) { + set[i] = set[i] || {}; + for (var prop in set[i - 1]) { + if (set[i][prop]) { + set[i][prop] += set[i - 1][prop]; + } else { + set[i][prop] = set[i - 1][prop]; + } + } + } + } + if (is_pvp) { + D.drops.maps.global_static.push([1.0 / ((gameplay == "hardcore" && 1000) || 100000), "pvptoken"]); + } - if (events.halloween) { - G.monsters.jr.respawn = 480; - G.monsters.greenjr.respawn = 480; - D.drops.maps.global.push([0.00005, "candy0"]); - D.drops.maps.global.push([0.00125, "candy1"]); - } + if (events.halloween) { + G.monsters.jr.respawn = 480; + G.monsters.greenjr.respawn = 480; + D.drops.maps.global.push([0.00005, "candy0"]); + D.drops.maps.global.push([0.00125, "candy1"]); + } - if (events.holidayseason) { - events.snowman = 60; - } + if (events.holidayseason) { + events.snowman = 60; + } } function calculate_xvalue(arr, rec, divide, mult) { - var value = 0; - var total = 0; - // console.log(arr); - arr.forEach(function (drop) { - total += drop[0]; - }); - if (!mult) { - mult = 1; - } - if (!divide) { - total = 1; - } else { - mult = 1; - } - arr.forEach(function (drop) { - if (drop[1] == "open") { - value += min(1, (drop[0] * mult) / total) * calculate_xvalue(D.drops[drop[2]], 1, 1, mult); - } else if (drop[1] == "shells") { - value += min(1, (drop[0] * mult) / total) * drop[2] * G.multipliers.shells_to_gold; - } else if (drop[1] == "empty") { - } else if (drop[1] == "gold") { - value += drop[2] * min(1, (drop[0] * mult) / total); - } else { - var def = G.items[drop[1]]; - var q = drop[2] || 1; - var g = def.g; - var avalue = def.g; - if (def.e && !rec) { - var suffix = ""; - if (def.upgrade || def.compound) { - suffix = "0"; - } - avalue = calculate_xvalue(D.drops[drop[1] + suffix], 1, 1, mult) / def.e; - // console.log(drop[1]+" value: "+g+" actual: "+avalue); - } - value += (q * min(1, (drop[0] * mult) / total) * (g + avalue)) / 2; - } - if (!value) { - console.log(drop); - } - }); - return value; + var value = 0; + var total = 0; + // console.log(arr); + arr.forEach(function (drop) { + total += drop[0]; + }); + if (!mult) { + mult = 1; + } + if (!divide) { + total = 1; + } else { + mult = 1; + } + arr.forEach(function (drop) { + if (drop[1] == "open") { + value += min(1, (drop[0] * mult) / total) * calculate_xvalue(D.drops[drop[2]], 1, 1, mult); + } else if (drop[1] == "shells") { + value += min(1, (drop[0] * mult) / total) * drop[2] * G.multipliers.shells_to_gold; + } else if (drop[1] == "empty") { + } else if (drop[1] == "gold") { + value += drop[2] * min(1, (drop[0] * mult) / total); + } else { + var def = G.items[drop[1]]; + var q = drop[2] || 1; + var g = def.g; + var avalue = def.g; + if (def.e && !rec) { + var suffix = ""; + if (def.upgrade || def.compound) { + suffix = "0"; + } + avalue = calculate_xvalue(D.drops[drop[1] + suffix], 1, 1, mult) / def.e; + // console.log(drop[1]+" value: "+g+" actual: "+avalue); + } + value += (q * min(1, (drop[0] * mult) / total) * (g + avalue)) / 2; + } + if (!value) { + console.log(drop); + } + }); + return value; } function add_to_trade_history(player, event, name, item, price) { - if (!player.p.trade_history) { - player.p.trade_history = []; - } - var last = player.p.trade_history[player.p.trade_history.length - 1]; - if (last && last[0] == event && last[1] == name && last[2].name == item.name && last[2].level == item.level) { - last[2].q = (last[2].q || 1) + (item.q || 1); - last[3] += price; - return; - } - if (player.p.trade_history.length >= 40) { - player.p.trade_history.shift(); - } - player.p.trade_history.push([event, name, item, price]); + if (!player.p.trade_history) { + player.p.trade_history = []; + } + var last = player.p.trade_history[player.p.trade_history.length - 1]; + if (last && last[0] == event && last[1] == name && last[2].name == item.name && last[2].level == item.level) { + last[2].q = (last[2].q || 1) + (item.q || 1); + last[3] += price; + return; + } + if (player.p.trade_history.length >= 40) { + player.p.trade_history.shift(); + } + player.p.trade_history.push([event, name, item, price]); } function add_to_history(player, event) { - if (!player.p.history) { - player.p.history = []; - } - if (player.p.history.length >= 400) { - player.p.history.shift(); - } - player.p.history.push(event); + if (!player.p.history) { + player.p.history = []; + } + if (player.p.history.length >= 400) { + player.p.history.shift(); + } + player.p.history.push(event); } function is_free(player) { - if (player.bot || player.p.free || player.s.licenced || player.role == "gm") { - return true; - } - return false; + if (player.bot || player.p.free || player.s.licenced || player.role == "gm") { + return true; + } + return false; } function is_player_allowed(player) { - if (gameplay == "hardcore") { - //if(player.type=="mage" || player.type=="priest") return false; - //if(player.type=="rogue") return false; - } - var socket = player.socket; - var characters = 0; - var ips = 0; - var ipx = player.ipx || 1; - var auths = 0; - if (player.temp_auth || player.auth_id) { - ipx = 12; - } - if (is_free(player) || (player.stones && 0)) { - return true; - } - if (player.type == "merchant") { - for (var id in players) { - if ((players[id].stones && 0) || id == player.socket.id || is_free(player)) { - continue; - } - if (players[id].owner == player.owner && players[id].type == "merchant") { - return false; - } - } - return true; - } - for (var id in players) { - if ((players[id].stones && 0) || is_free(players[id]) || players[id].type == "merchant") { - continue; - } // was requested [28/10/16] - if (players[id].owner == player.owner) { - characters++; - } - if (players[id].name == player.name && player != players[id]) { - return false; - } // "hardcore" - if (get_ip(players[id]) == get_ip(player)) { - ips++; - } - if (player.auth_id && players[id].auth_id == player.auth_id) { - auths++; - } - if (auths > variables.character_limit) { - return false; - } - if (characters > variables.character_limit) { - return false; - } - if (ips > variables.ip_limit * ipx) { - return false; - } - } - return true; + if (gameplay == "hardcore") { + //if(player.type=="mage" || player.type=="priest") return false; + //if(player.type=="rogue") return false; + } + var socket = player.socket; + var characters = 0; + var ips = 0; + var ipx = player.ipx || 1; + var auths = 0; + if (player.temp_auth || player.auth_id) { + ipx = 12; + } + if (is_free(player) || (player.stones && 0)) { + return true; + } + if (player.type == "merchant") { + for (var id in players) { + if ((players[id].stones && 0) || id == player.socket.id || is_free(player)) { + continue; + } + if (players[id].owner == player.owner && players[id].type == "merchant") { + return false; + } + } + return true; + } + for (var id in players) { + if ((players[id].stones && 0) || is_free(players[id]) || players[id].type == "merchant") { + continue; + } // was requested [28/10/16] + if (players[id].owner == player.owner) { + characters++; + } + if (players[id].name == player.name && player != players[id]) { + return false; + } // "hardcore" + if (get_ip(players[id]) == get_ip(player)) { + ips++; + } + if (player.auth_id && players[id].auth_id == player.auth_id) { + auths++; + } + if (auths > variables.character_limit) { + return false; + } + if (characters > variables.character_limit) { + return false; + } + if (ips > variables.ip_limit * ipx) { + return false; + } + } + return true; } function rip(player) { - player.hp = 0; - player.rip = true; - player.rip_time = new Date(); - player.moving = false; - player.abs = true; - if (player.party) { - send_party_update(player.party); - } + player.hp = 0; + player.rip = true; + player.rip_time = new Date(); + player.moving = false; + player.abs = true; + if (player.party) { + send_party_update(player.party); + } } function notify_friends(data) { - data.list.forEach(function (name) { - var player = players[name_to_id[name]]; - if (!player) { - return; - } - player.socket.emit("online", { name: data.name, server: data.server }); - }); + data.list.forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + return; + } + player.socket.emit("online", { name: data.name, server: data.server }); + }); } function check_player(player) { - // to check players in setTimeout's - if (!player || !player.socket || player.dc || !players[player.socket.id]) { - return false; - } - return true; + // to check players in setTimeout's + if (!player || !player.socket || player.dc || !players[player.socket.id]) { + return false; + } + return true; } function is_invis(player) { - if (player.s && (player.s.invis || player.s.ethereal)) { - return true; - } - return false; + if (player.s && (player.s.invis || player.s.ethereal)) { + return true; + } + return false; } function is_invinc(player) { - if (player.s && (player.s.invis || player.s.ethereal || player.s.invincible)) { - return true; - } - return false; + if (player.s && (player.s.invis || player.s.ethereal || player.s.invincible)) { + return true; + } + return false; } function is_in_pvp(player, allow_safe) { - if (allow_safe && G.maps[player.map].safe) { - return false; - } - if (is_pvp || G.maps[player.map].pvp) { - return true; - } - return false; + if (allow_safe && G.maps[player.map].safe) { + return false; + } + if (is_pvp || G.maps[player.map].pvp) { + return true; + } + return false; } function is_map_pvp(map, allow_safe) { - if (allow_safe && G.maps[map].safe) { - return false; - } - if (is_pvp || G.maps[map].pvp) { - return true; - } - return false; + if (allow_safe && G.maps[map].safe) { + return false; + } + if (is_pvp || G.maps[map].pvp) { + return true; + } + return false; } function is_same(player1, player2, party) { - if (is_sdk && player1.name != player2.name) { - return false; - } - if (player1.name == player2.name) { - return true; - } - if ((player1.owner && player1.owner == player2.owner) || (!is_sdk && get_ip(player1) == get_ip(player2))) { - return true; - } - if (party == 3 && !is_in_pvp(player1)) { - return true; - } - if (party && player1.s && player1.s.coop && player2.s && player2.s.coop && player1.s.coop.id == player2.s.coop.id) { - return true; - } // previously party==2 [08/05/21] - if (party && player1.party && player1.party == player2.party) { - return true; - } - if (party && player1.team && player1.team == player2.team) { - return true; - } - return false; + if (is_sdk && player1.name != player2.name) { + return false; + } + if (player1.name == player2.name) { + return true; + } + if ((player1.owner && player1.owner == player2.owner) || (!is_sdk && get_ip(player1) == get_ip(player2))) { + return true; + } + if (party == 3 && !is_in_pvp(player1)) { + return true; + } + if (party && player1.s && player1.s.coop && player2.s && player2.s.coop && player1.s.coop.id == player2.s.coop.id) { + return true; + } // previously party==2 [08/05/21] + if (party && player1.party && player1.party == player2.party) { + return true; + } + if (party && player1.team && player1.team == player2.team) { + return true; + } + return false; } function decay_s(player, ms) { - for (var name in player.s || {}) { - if (G.conditions[name] && G.conditions[name].debuff) { - continue; - } - if (player.s[name].ms < 60000 || player.s[name].citizens) { - if (player.s[name].citizens) { - for (var p in player.s[name]) { - if (p != "ms" && is_number(player.s[name][p])) { - player.s[name][p] = round(player.s[name][p] / 4.0); - } - } - } - player.s[name].ms -= ms; - } - } + for (var name in player.s || {}) { + if (G.conditions[name] && G.conditions[name].debuff) { + continue; + } + if (player.s[name].ms < 60000 || player.s[name].citizens) { + if (player.s[name].citizens) { + for (var p in player.s[name]) { + if (p != "ms" && is_number(player.s[name][p])) { + player.s[name][p] = round(player.s[name][p] / 4.0); + } + } + } + player.s[name].ms -= ms; + } + } } function reset_player(player, soft) { - if (!player.p.hardcore) { - player.p = { hardcore: true, dt: {} }; - player.max_stats = { monsters: {} }; - } - player.xp = 0; - if (soft) { - // player.level=max(1,player.level-20); - } else { - player.level = 1; - player.xp = 0; - player.s = {}; - player.slots = {}; - player.items = [{ name: "computer" }, { name: "tracker" }]; - player.gold = 0; - if (P[player.real_id] && gameplay != "test") { - for (var p in P[player.real_id]) { - player[p] = P[player.real_id][p]; - } - P[player.real_id] = null; - } - } + if (!player.p.hardcore) { + player.p = { hardcore: true, dt: {} }; + player.max_stats = { monsters: {} }; + } + player.xp = 0; + if (soft) { + // player.level=max(1,player.level-20); + } else { + player.level = 1; + player.xp = 0; + player.s = {}; + player.slots = {}; + player.items = [{ name: "computer" }, { name: "tracker" }]; + player.gold = 0; + if (P[player.real_id] && gameplay != "test") { + for (var p in P[player.real_id]) { + player[p] = P[player.real_id][p]; + } + P[player.real_id] = null; + } + } } function save_player(player) { - P[player.real_id] = { - level: player.level, - xp: player.xp, - slots: player.slots, - items: player.items, - gold: player.gold, - x: player.x, - y: player.y, - map: player.map, - in: player.in, - rip: player.rip, - kills: player.kills, - p: player.p, - name: player.name, - auth_id: player.auth_id, - }; + P[player.real_id] = { + level: player.level, + xp: player.xp, + slots: player.slots, + items: player.items, + gold: player.gold, + x: player.x, + y: player.y, + map: player.map, + in: player.in, + rip: player.rip, + kills: player.kills, + p: player.p, + name: player.name, + auth_id: player.auth_id, + }; } function secondhands_logic(item, quantity) { - var new_item = cache_item(item); - var done = false; - var count = 0; - var replace = null; - if ( - !( - !G.items[item.name].buy || - (G.items[item.name].upgrade && item.level >= 7) || - (G.items[item.name].compound && item.level >= 2) - ) || - G.items[item.name].cash || - item.expires || - item.acl - ) { - return; - } - if (item.grace !== undefined) { - new_item.grace = item.grace; - } - if (new_item.q !== undefined) { - new_item.q = quantity; - } - delete new_item.v; - new_item.rid = randomStr(5); - for (var i = 0; i < S.sold.length; i++) { - if (done) { - return; - } - if (can_stack(S.sold[i], new_item)) { - done = true; - S.sold[i].q = S.sold[i].q + new_item.q; - csold[i] = cache_item(S.sold[i], true); - } - if (S.sold[i].name == new_item.name && S.sold[i].level == new_item.level) { - if (!S.sold[i].p) { - replace = i; - } - count += 1; - } - } - if (!done && (count < 5 || (new_item.p && replace !== null))) { - var current = S.sold.length % 400; - if (count >= 5) { - current = replace; - } - S.sold[current] = new_item; - csold[current] = cache_item(S.sold[current], true); - } + var new_item = cache_item(item); + var done = false; + var count = 0; + var replace = null; + if ( + !( + !G.items[item.name].buy || + (G.items[item.name].upgrade && item.level >= 7) || + (G.items[item.name].compound && item.level >= 2) + ) || + G.items[item.name].cash || + item.expires || + item.acl + ) { + return; + } + if (item.grace !== undefined) { + new_item.grace = item.grace; + } + if (new_item.q !== undefined) { + new_item.q = quantity; + } + delete new_item.v; + new_item.rid = randomStr(5); + for (var i = 0; i < S.sold.length; i++) { + if (done) { + return; + } + if (can_stack(S.sold[i], new_item)) { + done = true; + S.sold[i].q = S.sold[i].q + new_item.q; + csold[i] = cache_item(S.sold[i], true); + } + if (S.sold[i].name == new_item.name && S.sold[i].level == new_item.level) { + if (!S.sold[i].p) { + replace = i; + } + count += 1; + } + } + if (!done && (count < 5 || (new_item.p && replace !== null))) { + var current = S.sold.length % 400; + if (count >= 5) { + current = replace; + } + S.sold[current] = new_item; + csold[current] = cache_item(S.sold[current], true); + } } function lostandfound_logic(item) { - var done = false; - var count = 0; - var replace = null; - item.rid = randomStr(5); - for (var i = 0; i < S.found.length; i++) { - if (done) { - return; - } - if (can_stack(S.found[i], item)) { - done = true; - S.found[i].q = S.found[i].q + item.q; - cfound[i] = cache_item(S.found[i], true); - } - if (S.found[i].name == item.name && S.found[i].level == item.level) { - if (!S.found[i].p) { - replace = i; - } - count += 1; - } - } - if (!done && (count < 5 || (item.p && replace !== null))) { - var current = S.found.length % 400; - if (count >= 5) { - current = replace; - } - S.found[current] = item; - cfound[current] = cache_item(S.found[current], true); - } + var done = false; + var count = 0; + var replace = null; + item.rid = randomStr(5); + for (var i = 0; i < S.found.length; i++) { + if (done) { + return; + } + if (can_stack(S.found[i], item)) { + done = true; + S.found[i].q = S.found[i].q + item.q; + cfound[i] = cache_item(S.found[i], true); + } + if (S.found[i].name == item.name && S.found[i].level == item.level) { + if (!S.found[i].p) { + replace = i; + } + count += 1; + } + } + if (!done && (count < 5 || (item.p && replace !== null))) { + var current = S.found.length % 400; + if (count >= 5) { + current = replace; + } + S.found[current] = item; + cfound[current] = cache_item(S.found[current], true); + } } function server_loot(type) { - if (type == "all") { - for (var mtype in D.drops.monsters) { - for (var j = 0; j < D.drops.monsters[mtype].length; j++) { - if (D.drops.monsters[mtype][j][0] > 0.1) { - D.drops.monsters[mtype][j][0] = 0.05; - } - } - } - for (var id in instances) { - if (instances[id].map != instances[id].name) { - continue; - } - for (var mid in instances[id].monsters) { - if (instances[id].monsters[mid].frequency < 4 && instances.main) { - drop_something(instances.main.players[NPC_prefix + "Kane"], instances[id].monsters[mid]); - } - } - } - } - for (var id in chests) { - var chest = chests[id]; - if (type != "all" && hsince(chests[id].date) < 48) { - return; - } - delete chests[id]; - S.gold += chest.gold; - if (chest.cash) { - S.cash += chest.cash; - } - if (chest.items) { - chest.items.forEach(function (item) { - lostandfound_logic(item); - }); - } - if (chest.pvp_items) { - chest.pvp_items.forEach(function (item) { - lostandfound_logic(item); - }); - } - } + if (type == "all") { + for (var mtype in D.drops.monsters) { + for (var j = 0; j < D.drops.monsters[mtype].length; j++) { + if (D.drops.monsters[mtype][j][0] > 0.1) { + D.drops.monsters[mtype][j][0] = 0.05; + } + } + } + for (var id in instances) { + if (instances[id].map != instances[id].name) { + continue; + } + for (var mid in instances[id].monsters) { + if (instances[id].monsters[mid].frequency < 4 && instances.main) { + drop_something(instances.main.players[NPC_prefix + "Kane"], instances[id].monsters[mid]); + } + } + } + } + for (var id in chests) { + var chest = chests[id]; + if (type != "all" && hsince(chests[id].date) < 48) { + return; + } + delete chests[id]; + S.gold += chest.gold; + if (chest.cash) { + S.cash += chest.cash; + } + if (chest.items) { + chest.items.forEach(function (item) { + lostandfound_logic(item); + }); + } + if (chest.pvp_items) { + chest.pvp_items.forEach(function (item) { + lostandfound_logic(item); + }); + } + } } function server_tax(gold, preview) { - var tax = 0; - if (1) { - tax = parseInt(gold * 0.1); - } else if (S.gold < 500000000) { - tax = parseInt(gold * 0.25); - } else if (S.gold < 1000000000) { - tax = parseInt(gold * 0.5); - } - if (!preview) { - S.gold += tax; - } - return gold - tax; + var tax = 0; + if (1) { + tax = parseInt(gold * 0.1); + } else if (S.gold < 500000000) { + tax = parseInt(gold * 0.25); + } else if (S.gold < 1000000000) { + tax = parseInt(gold * 0.5); + } + if (!preview) { + S.gold += tax; + } + return gold - tax; } function merchant_xp_logic(player, seller, price, tax) { - if (is_same(player, seller)) { - return; - } - if (!player.p.xpcache || Object.keys(player.p.xpcache).length > 120 || hsince(player.p.dt.last_xpcache) > 5 * 24) { - player.p.xpcache = {}; - player.p.dt.last_xpcache = new Date(); - } - if (!player.p.xpcache[seller.name]) { - player.p.xpcache[seller.name] = 0; - } - var initial = player.p.xpcache[seller.name]; - player.p.xpcache[seller.name] = min(120000000, player.p.xpcache[seller.name] + round(price / 4)); - player.xp += player.p.xpcache[seller.name] - initial; + if (is_same(player, seller)) { + return; + } + if (!player.p.xpcache || Object.keys(player.p.xpcache).length > 120 || hsince(player.p.dt.last_xpcache) > 5 * 24) { + player.p.xpcache = {}; + player.p.dt.last_xpcache = new Date(); + } + if (!player.p.xpcache[seller.name]) { + player.p.xpcache[seller.name] = 0; + } + var initial = player.p.xpcache[seller.name]; + player.p.xpcache[seller.name] = min(120000000, player.p.xpcache[seller.name] + round(price / 4)); + player.xp += player.p.xpcache[seller.name] - initial; } function merchant_xp_logic(player, seller, price, tax) { - if (is_same(player, seller)) { - return; - } - player.xp += tax * 3.2; + if (is_same(player, seller)) { + return; + } + player.xp += tax * 3.2; } function normalise(data) { - // normalise(data,["gold","gold"],["num","inv"]) - for (var i = 1; i < arguments.length; i++) { - var def = arguments[i]; - var name = def[0]; - var type = def[1]; - var value = data[name]; - if (type == "gold") { - value = max(0, min(parseInt(value) || 0, 99999999999)); - } - data[name] = value; - } + // normalise(data,["gold","gold"],["num","inv"]) + for (var i = 1; i < arguments.length; i++) { + var def = arguments[i]; + var name = def[0]; + var type = def[1]; + var value = data[name]; + if (type == "gold") { + value = max(0, min(parseInt(value) || 0, 99999999999)); + } + data[name] = value; + } } var cloudflare_ips = [ - // https://www.cloudflare.com/ips/ - "103.21.244.0/22", - "103.22.200.0/22", - "103.31.4.0/22", - "104.16.0.0/12", - "108.162.192.0/18", - "131.0.72.0/22", - "141.101.64.0/18", - "162.158.0.0/15", - "172.64.0.0/13", - "173.245.48.0/20", - "188.114.96.0/20", - "190.93.240.0/20", - "197.234.240.0/22", - "198.41.128.0/17", - "2400:cb00::/32", - "2405:b500::/32", - "2606:4700::/32", - "2803:f800::/32", - "2c0f:f248::/32", - "2a06:98c0::/29", + // https://www.cloudflare.com/ips/ + "103.21.244.0/22", + "103.22.200.0/22", + "103.31.4.0/22", + "104.16.0.0/12", + "108.162.192.0/18", + "131.0.72.0/22", + "141.101.64.0/18", + "162.158.0.0/15", + "172.64.0.0/13", + "173.245.48.0/20", + "188.114.96.0/20", + "190.93.240.0/20", + "197.234.240.0/22", + "198.41.128.0/17", + "2400:cb00::/32", + "2405:b500::/32", + "2606:4700::/32", + "2803:f800::/32", + "2c0f:f248::/32", + "2a06:98c0::/29", ]; function get_ip_raw(player) { - if (!player.socket) { - player = { socket: player }; - } // so get_ip(socket) works too [06/09/18] - // return player.socket.handshake.address; - // BEWARE: player.socket.request.connection.remoteAddress - try { - if (player.last_ip) { - return player.last_ip; - } - if (player.first_ip) { - return player.first_ip; - } - if (player.socket.request["headers"]["cf-connecting-ip"]) { - if (range_check.inRange(range_check.displayIP(player.socket.handshake.address), cloudflare_ips)) { - player.first_ip = player.socket.request["headers"]["cf-connecting-ip"]; - return player.first_ip; - } else { - player.ipx = -1; - } - } - } catch (e) {} - try { - return player.socket.request.connection.remoteAddress || player.socket.handshake.address; - } catch (e) {} - try { - return player.socket.handshake.address; - } catch (e) {} + if (!player.socket) { + player = { socket: player }; + } // so get_ip(socket) works too [06/09/18] + // return player.socket.handshake.address; + // BEWARE: player.socket.request.connection.remoteAddress + try { + if (player.last_ip) { + return player.last_ip; + } + if (player.first_ip) { + return player.first_ip; + } + if (player.socket.request["headers"]["cf-connecting-ip"]) { + if (range_check.inRange(range_check.displayIP(player.socket.handshake.address), cloudflare_ips)) { + player.first_ip = player.socket.request["headers"]["cf-connecting-ip"]; + return player.first_ip; + } else { + player.ipx = -1; + } + } + } catch (e) {} + try { + return player.socket.request.connection.remoteAddress || player.socket.handshake.address; + } catch (e) {} + try { + return player.socket.handshake.address; + } catch (e) {} } function get_ip(player) { - var ip = get_ip_raw(player) || ""; - return ip.replace("::ffff:", ""); + var ip = get_ip_raw(player) || ""; + return ip.replace("::ffff:", ""); } function quick_hash(str) { - var hash = 0; - if (str.length == 0) { - return hash; - } - for (var i = 0; i < str.length; i++) { - var char = str.charCodeAt(i); - hash = (hash << 5) - hash + char; - hash = hash & hash; - } - return hash; + var hash = 0; + if (str.length == 0) { + return hash; + } + for (var i = 0; i < str.length; i++) { + var char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash = hash & hash; + } + return hash; } function verify_steam_ticket(player, ticket) { - // Thanks: https://github.com/DoctorMcKay/node-steam-user - try { - var outer = EncryptedAppTicket.decode(new Buffer(ticket, "hex")); - var decrypted = symmetricDecrypt(outer.encryptedTicket, new Buffer(variables.steam_key, "hex")); - let userData = decrypted.slice(0, outer.cbEncrypteduserdata); - let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); - let ownershipTicket = parseAppTicket( - decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), - ); - if (ownershipTicket) { - ownershipTicket.userData = userData.toString(); - } - if (ownershipTicket.appID == 777150 && ownershipTicket.steamID) { - player.auth_type = "steam"; - player.auth_id = ownershipTicket.steamID; - player.p.steam_id = ownershipTicket.steamID; - delete player.s.authfail; - } - } catch (e) { - console.log("#A verify_steam_ticket: " + e); - } + // Thanks: https://github.com/DoctorMcKay/node-steam-user + try { + var outer = EncryptedAppTicket.decode(new Buffer(ticket, "hex")); + var decrypted = symmetricDecrypt(outer.encryptedTicket, new Buffer(variables.steam_key, "hex")); + let userData = decrypted.slice(0, outer.cbEncrypteduserdata); + let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); + let ownershipTicket = parseAppTicket( + decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), + ); + if (ownershipTicket) { + ownershipTicket.userData = userData.toString(); + } + if (ownershipTicket.appID == 777150 && ownershipTicket.steamID) { + player.auth_type = "steam"; + player.auth_id = ownershipTicket.steamID; + player.p.steam_id = ownershipTicket.steamID; + delete player.s.authfail; + } + } catch (e) { + console.log("#A verify_steam_ticket: " + e); + } } function verify_mas_receipt(player, receipt) { - if (player.p.mas_hash == quick_hash(receipt) && player.p.dt.mas_expire > new Date()) { - player.auth_type = "mas"; - player.auth_id = player.p.mas_auth_id; - delete player.s.authfail; - } else { - console.log("verify_mas_receipt for " + player.name); - player.temp_auth = "mas"; - //player.mas_receipt=receipt; - var content = { "receipt-data": receipt, password: variables.apple_token }; - var url = "https://buy.itunes.apple.com/verifyReceipt"; - request( - { - url: url, - method: "POST", - json: content, - }, - function (err, response, body) { - if (err) { - console.log("#M: mas error: " + player.name + " - " + err); - } - if (body && body.status == 0 && body.receipt.download_id && body.receipt.original_purchase_date_ms) { - delete player.temp_auth; - player.auth_type = "mas"; - player.auth_id = ("" + body.receipt.download_id).substr(0, 4) + body.receipt.original_purchase_date_ms; - player.p.mas_auth_id = player.auth_id; - player.p.mas_hash = quick_hash(receipt); - player.p.dt.mas_expire = future_s(3 * 24 * 60 * 60); - delete player.s.authfail; - } else { - delete player.temp_auth; - console.log("#M: mas declined: " + player.name + " " + (body && body.status)); - } - }, - ); - } + if (player.p.mas_hash == quick_hash(receipt) && player.p.dt.mas_expire > new Date()) { + player.auth_type = "mas"; + player.auth_id = player.p.mas_auth_id; + delete player.s.authfail; + } else { + console.log("verify_mas_receipt for " + player.name); + player.temp_auth = "mas"; + //player.mas_receipt=receipt; + var content = { "receipt-data": receipt, password: variables.apple_token }; + var url = "https://buy.itunes.apple.com/verifyReceipt"; + request( + { + url: url, + method: "POST", + json: content, + }, + function (err, response, body) { + if (err) { + console.log("#M: mas error: " + player.name + " - " + err); + } + if (body && body.status == 0 && body.receipt.download_id && body.receipt.original_purchase_date_ms) { + delete player.temp_auth; + player.auth_type = "mas"; + player.auth_id = ("" + body.receipt.download_id).substr(0, 4) + body.receipt.original_purchase_date_ms; + player.p.mas_auth_id = player.auth_id; + player.p.mas_hash = quick_hash(receipt); + player.p.dt.mas_expire = future_s(3 * 24 * 60 * 60); + delete player.s.authfail; + } else { + delete player.temp_auth; + console.log("#M: mas declined: " + player.name + " " + (body && body.status)); + } + }, + ); + } } function verify_steam_ownership(player) { - var url = "https://partner.steam-api.com/ISteamUser/CheckAppOwnership/v2/"; - data = { - key: variables.steam_partner_key, - steamid: player.p.steam_id, - appid: "777150", - }; - request.get({ url: url, qs: data }, function (err, response, body) { - console.log(body); - }); + var url = "https://partner.steam-api.com/ISteamUser/CheckAppOwnership/v2/"; + data = { + key: variables.steam_partner_key, + steamid: player.p.steam_id, + appid: "777150", + }; + request.get({ url: url, qs: data }, function (err, response, body) { + console.log(body); + }); } function initiate_steam_microtxn(player) { - var url = "https://partner.steam-api.com/ISteamMicroTxn/InitTxn/v3/"; - var orderid = parseInt(Math.random() * 1000000000 + 1); - console.log(orderid); - data = { - key: variables.steam_partner_key, - steamid: player.p.steam_id, - appid: "777150", - usersession: "web", - ipaddress: "85.98.170.74", - orderid: orderid, - itemcount: 1, - language: "en", - currency: "USD", - "itemid[0]": "123", - "qty[0]": 1, - "amount[0]": 999, // $9.99 - "description[0]": "1000 Shells", - }; - request.post({ url: url, form: data }, function (err, response, body) { - console.log(body); - }); + var url = "https://partner.steam-api.com/ISteamMicroTxn/InitTxn/v3/"; + var orderid = parseInt(Math.random() * 1000000000 + 1); + console.log(orderid); + data = { + key: variables.steam_partner_key, + steamid: player.p.steam_id, + appid: "777150", + usersession: "web", + ipaddress: "85.98.170.74", + orderid: orderid, + itemcount: 1, + language: "en", + currency: "USD", + "itemid[0]": "123", + "qty[0]": 1, + "amount[0]": 999, // $9.99 + "description[0]": "1000 Shells", + }; + request.post({ url: url, form: data }, function (err, response, body) { + console.log(body); + }); } function invincible_logic(player, place) { - if (is_pvp || G.maps[player.map].pvp) { - player.s.invincible = { ms: max(6000, (player.s.invincible && player.s.invincible.ms) || 0) }; - } + if (is_pvp || G.maps[player.map].pvp) { + player.s.invincible = { ms: max(6000, (player.s.invincible && player.s.invincible.ms) || 0) }; + } } function serverhop_logic(player) { - delete player.s.hopsickness; - player.p.entries = player.p.entries || []; - var servers = {}; - var main = null; - var mx = 0; - var hops = 0; - for (var i = player.p.entries.length - 2; i >= 0; i--) { - var hours = Math.abs(new Date(player.p.entries[i + 1][1]) - new Date(player.p.entries[i][1])) / 36e5; - servers[player.p.entries[i][0]] = (servers[player.p.entries[i][0]] || 0) + hours; - } - for (var id in servers) { - if (servers[id] > mx) { - main = id; - mx = servers[id]; - } - } - for (var i = player.p.entries.length - 1; i >= 0; i--) { - if (hsince(new Date(player.p.entries[i][1])) < 4 && player.p.entries[i][0] != main) { - hops++; - } - } - if (main && hops && main != region + server_name && player.level >= 60) { - player.s.hopsickness = { ms: min(10000 * hops * hops, 3 * 60 * 60 * 1000) }; - if (hops > 20) { - player.s.hopsickness.luck = -80; - player.s.hopsickness.gold = -80; - player.s.hopsickness.output = -50; - } else if (hops > 15) { - player.s.hopsickness.luck = -60; - player.s.hopsickness.gold = -60; - player.s.hopsickness.output = -40; - } else if (hops > 10) { - player.s.hopsickness.luck = -40; - player.s.hopsickness.gold = -40; - player.s.hopsickness.output = -30; - } else if (hops > 5) { - player.s.hopsickness.luck = -30; - player.s.hopsickness.gold = -30; - player.s.hopsickness.output = -20; - } else if (hops > 2) { - player.s.hopsickness.luck = -20; - player.s.hopsickness.gold = -20; - player.s.hopsickness.output = -10; - } - calculate_player_stats(player); - } - var c = [region + server_name, "" + new Date()]; - player.p.entries.push(c); - if (player.p.entries.length > 60) { - player.p.entries.shift(); - } + delete player.s.hopsickness; + player.p.entries = player.p.entries || []; + var servers = {}; + var main = null; + var mx = 0; + var hops = 0; + for (var i = player.p.entries.length - 2; i >= 0; i--) { + var hours = Math.abs(new Date(player.p.entries[i + 1][1]) - new Date(player.p.entries[i][1])) / 36e5; + servers[player.p.entries[i][0]] = (servers[player.p.entries[i][0]] || 0) + hours; + } + for (var id in servers) { + if (servers[id] > mx) { + main = id; + mx = servers[id]; + } + } + for (var i = player.p.entries.length - 1; i >= 0; i--) { + if (hsince(new Date(player.p.entries[i][1])) < 4 && player.p.entries[i][0] != main) { + hops++; + } + } + if (main && hops && main != region + server_name && player.level >= 60) { + player.s.hopsickness = { ms: min(10000 * hops * hops, 3 * 60 * 60 * 1000) }; + if (hops > 20) { + player.s.hopsickness.luck = -80; + player.s.hopsickness.gold = -80; + player.s.hopsickness.output = -50; + } else if (hops > 15) { + player.s.hopsickness.luck = -60; + player.s.hopsickness.gold = -60; + player.s.hopsickness.output = -40; + } else if (hops > 10) { + player.s.hopsickness.luck = -40; + player.s.hopsickness.gold = -40; + player.s.hopsickness.output = -30; + } else if (hops > 5) { + player.s.hopsickness.luck = -30; + player.s.hopsickness.gold = -30; + player.s.hopsickness.output = -20; + } else if (hops > 2) { + player.s.hopsickness.luck = -20; + player.s.hopsickness.gold = -20; + player.s.hopsickness.output = -10; + } + calculate_player_stats(player); + } + var c = [region + server_name, "" + new Date()]; + player.p.entries.push(c); + if (player.p.entries.length > 60) { + player.p.entries.shift(); + } } function serverhop_logic(player) { - if (player.p.entries && player.p.entries[0] && player.p.entries[0][0] == region + server_name) { - return; - } - delete player.s.hopsickness; - if (!player.p.home) { - player.p.home = region + server_name; - } - player.p.entries = [[region + server_name, "" + new Date()]]; - if (player.p.home != region + server_name && player.level >= 60 && server_name != "PVP") { - add_condition(player, "hopsickness"); - } + if (player.p.entries && player.p.entries[0] && player.p.entries[0][0] == region + server_name) { + return; + } + delete player.s.hopsickness; + if (!player.p.home) { + player.p.home = region + server_name; + } + player.p.entries = [[region + server_name, "" + new Date()]]; + if (player.p.home != region + server_name && player.level >= 60 && server_name != "PVP") { + add_condition(player, "hopsickness"); + } } function ghash(entity, zone, a_d, b_d) { - //zone is the square's dimension, a_d, and b_d are displacements - var a = floor((1.0 * entity.x) / zone) + (a_d || 0); - var b = floor((1.0 * entity.y) / zone) + (b_d || 0); - return a + "|" + b; + //zone is the square's dimension, a_d, and b_d are displacements + var a = floor((1.0 * entity.x) / zone) + (a_d || 0); + var b = floor((1.0 * entity.y) / zone) + (b_d || 0); + return a + "|" + b; } function set_ghash(hash, entity, zone) { - var h = ghash(entity, zone); - if (!hash[h]) { - hash[h] = {}; - } - hash[h][entity.id] = entity; + var h = ghash(entity, zone); + if (!hash[h]) { + hash[h] = {}; + } + hash[h][entity.id] = entity; } function get_nearby_ghash(hash, entity, zone) { - var d = [-1, 0, 1]; - var l = []; - var h; - for (var i = 0; i < 3; i++) { - for (var j = 0; j < 3; j++) { - h = ghash(entity, zone, d[i], d[j]); - for (var id in hash[h]) { - l.push(hash[h][id]); - } - } - } - return l; + var d = [-1, 0, 1]; + var l = []; + var h; + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + h = ghash(entity, zone, d[i], d[j]); + for (var id in hash[h]) { + l.push(hash[h][id]); + } + } + } + return l; } function screenshot_npc(player, args) { - if (!args) { - args = {}; - } - if (!args.name) { - args.name = randomStr(10); - } - instance = instances[player.in]; - var npc = create_npc( - { - name: args.name, - level: args.level || 40, - speed: 1200, - hp: 100000000, - skin: args.skin || "mranger", - cx: args.cx || [], - }, - { position: [player.x, player.y], id: args.name }, - instance, - ); - if (args.stand) { - npc.p.stand = args.stand; - } - npc.screenshot = true; - npc.direction = player.direction; - npc.id = args.name; - instance.players[NPC_prefix + npc.id] = npc; + if (!args) { + args = {}; + } + if (!args.name) { + args.name = randomStr(10); + } + instance = instances[player.in]; + var npc = create_npc( + { + name: args.name, + level: args.level || 40, + speed: 1200, + hp: 100000000, + skin: args.skin || "mranger", + cx: args.cx || [], + }, + { position: [player.x, player.y], id: args.name }, + instance, + ); + if (args.stand) { + npc.p.stand = args.stand; + } + npc.screenshot = true; + npc.direction = player.direction; + npc.id = args.name; + instance.players[NPC_prefix + npc.id] = npc; } function get_npc_coords(id) { - for (var iid in instances) { - if (G.maps[iid] && G.maps[iid].ref && G.maps[iid].ref[id]) { - return G.maps[iid].ref[id]; - } - } - return { x: 0, y: 0, map: "main", in: "main" }; + for (var iid in instances) { + if (G.maps[iid] && G.maps[iid].ref && G.maps[iid].ref[id]) { + return G.maps[iid].ref[id]; + } + } + return { x: 0, y: 0, map: "main", in: "main" }; } function pmap_remove(player) { - try { - delete instances[player.in].pmap[player.last_hash][player.id]; - if (!Object.keys(instances[player.in].pmap[player.last_hash]).length) { - delete instances[player.in].pmap[player.last_hash]; - } - } catch (e) {} + try { + delete instances[player.in].pmap[player.last_hash][player.id]; + if (!Object.keys(instances[player.in].pmap[player.last_hash]).length) { + delete instances[player.in].pmap[player.last_hash]; + } + } catch (e) {} } function pmap_get(player) { - var hash = ghash(player, 6); - if (!instances[player.in].pmap[hash]) { - return {}; - } - return instances[player.in].pmap[hash]; + var hash = ghash(player, 6); + if (!instances[player.in].pmap[hash]) { + return {}; + } + return instances[player.in].pmap[hash]; } function pmap_add(player) { - var hash = ghash(player, 6); - if (!instances[player.in].pmap[hash]) { - instances[player.in].pmap[hash] = {}; - } - instances[player.in].pmap[hash][player.id] = player; - player.last_hash = hash; + var hash = ghash(player, 6); + if (!instances[player.in].pmap[hash]) { + instances[player.in].pmap[hash] = {}; + } + instances[player.in].pmap[hash][player.id] = player; + player.last_hash = hash; } function pmap_move(player) { - var hash = ghash(player, 6); - if (player.last_hash == hash) { - return; - } - pmap_remove(player); - pmap_add(player); + var hash = ghash(player, 6); + if (player.last_hash == hash) { + return; + } + pmap_remove(player); + pmap_add(player); } function init_tavern() { - if (!instances.tavern) { - return; - } - tavern = instances.tavern; - tavern.roulette = { - state: "bets", - next: future_s(25), - odds: { - red: 2, - black: 2, - odd: 2, - even: 2, - "1/3": 3, - "2/3": 3, - "3/3": 3, - "1/2": 2, - "2/2": 2, - "3n": 3, - "3n+1": 3, - "3n+2": 3, - }, - gain: 0, - players: {}, - }; - for (var i = 0; i <= 36; i++) { - tavern.roulette.odds["" + i] = 36; - } - tavern.poker = { - rooms: [], - gain: 0, - }; - tavern.dice = { - rolls: [], - state: "lock", - seconds: 0, - next: future_s(1), - players: {}, - bets: [], - }; - tavern.dice.num = - "" + - parseInt(Math.random() * 10) + - "" + - parseInt(Math.random() * 10) + - "." + - parseInt(Math.random() * 10) + - "" + - parseInt(Math.random() * 10); - tavern.info.dice = "lock"; - tavern.info.num = tavern.dice.num; - tavern.info.seconds = tavern.dice.seconds; + if (!instances.tavern) { + return; + } + tavern = instances.tavern; + tavern.roulette = { + state: "bets", + next: future_s(25), + odds: { + red: 2, + black: 2, + odd: 2, + even: 2, + "1/3": 3, + "2/3": 3, + "3/3": 3, + "1/2": 2, + "2/2": 2, + "3n": 3, + "3n+1": 3, + "3n+2": 3, + }, + gain: 0, + players: {}, + }; + for (var i = 0; i <= 36; i++) { + tavern.roulette.odds["" + i] = 36; + } + tavern.poker = { + rooms: [], + gain: 0, + }; + tavern.dice = { + rolls: [], + state: "lock", + seconds: 0, + next: future_s(1), + players: {}, + bets: [], + }; + tavern.dice.num = + "" + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10) + + "." + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10); + tavern.info.dice = "lock"; + tavern.info.num = tavern.dice.num; + tavern.info.seconds = tavern.dice.seconds; } function house_debt() { - var gold = 0; - for (var id in tavern.dice.players) { - var player = players[id]; - if (!player) { - continue; - } - for (var b_id in player.bets || {}) { - var bet = player.bets[b_id]; - if (bet.type != "dice") { - continue; - } - gold += bet.win - bet.edge - bet.gold; - } - } - return gold; + var gold = 0; + for (var id in tavern.dice.players) { + var player = players[id]; + if (!player) { + continue; + } + for (var b_id in player.bets || {}) { + var bet = player.bets[b_id]; + if (bet.type != "dice") { + continue; + } + gold += bet.win - bet.edge - bet.gold; + } + } + return gold; } function house_edge() { - var gold = S.gold - house_debt(); - if (gold > 5000000000) { - return 0.5; - } - if (gold > 2000000000) { - return 1; - } - if (gold > 1000000000) { - return 1.5; - } - return 2; + var gold = S.gold - house_debt(); + if (gold > 5000000000) { + return 0.5; + } + if (gold > 2000000000) { + return 1; + } + if (gold > 1000000000) { + return 1.5; + } + return 2; } var dice_last_roll = 0; function tavern_loop() { - try { - if (!server.live || !instances.tavern) { - return; - } - var c = new Date(); - if (c > tavern.dice.next) { - if (tavern.dice.state == "bets") { - tavern.dice.seconds += 1; - tavern.info.seconds = tavern.dice.seconds; - if (tavern.dice.seconds >= 30) { - tavern.info.dice = tavern.dice.state = "roll"; - dice_last_roll = tavern.info.num; - delete tavern.info.num; - tavern.dice.next = future_s(10); - instance_emit(tavern, "dice", { state: "roll" }); - //server_log("Dice: Bets Over"); - } - } else if (tavern.dice.state == "roll") { - tavern.info.dice = tavern.dice.state = "lock"; - tavern.info.num = tavern.dice.num; - tavern.dice.next = future_s(1.6); - for (var id in tavern.dice.players) { - var player = players[id]; - if (!player) { - continue; - } - for (var b_id in player.bets || {}) { - var bet = player.bets[b_id]; - if (bet.type != "dice") { - continue; - } - delete players[bet.pid].bets[bet.id]; - tavern.dice.bets.push(bet); - if ( - (bet.dir == "up" && parseFloat(tavern.dice.num) >= bet.num) || - (bet.dir == "down" && parseFloat(tavern.dice.num) <= bet.num) - ) { - if (player.type == "merchant") { - player.xp += parseInt(bet.edge * 7.2); - } - player.gold += bet.win - bet.edge; - S.gold -= bet.win - bet.edge - bet.gold; - if (bet.win - bet.edge - bet.gold >= 12000000) { - lstack(S.logs.dice, { name: player.name, gold: bet.win - bet.edge - bet.gold, odds: bet.odds }); - } - } else { - if (bet.gold >= 10000000) { - lstack(S.logs.dice, { name: player.name, gold: -bet.gold, odds: bet.odds }); - } - S.gold += bet.gold; - } - } - } - instance_emit(tavern, "dice", { - state: "lock", - num: tavern.dice.num, - text: tavern.dice.text, - key: tavern.dice.key, - }); - //server_log("Dice: Roll Over - Locking"); - } else if (tavern.dice.state == "lock") { - tavern.dice.state = "suspense"; - tavern.dice.bets.forEach(function (bet) { - var player = players[bet.pid]; - if (!player) { - return; - } - if ( - (bet.dir == "up" && parseFloat(tavern.dice.num) >= bet.num) || - (bet.dir == "down" && parseFloat(tavern.dice.num) <= bet.num) - ) { - player.socket.emit("game_log", { - message: "Won: " + to_pretty_num(bet.win) + " gold [" + tavern.dice.num + "]", - color: "gold", - }); - player.socket.emit("game_log", { - message: "House edge: " + to_pretty_num(bet.edge) + " gold", - color: "gray", - }); - instance_emit(tavern, "tavern", { - event: "won", - name: player.name, - type: "dice", - num: bet.num, - gold: bet.win, - dir: bet.dir, - net: bet.win - bet.gold - bet.edge, - }); - } else { - player.socket.emit("game_log", { - message: "Lost: " + to_pretty_num(bet.gold) + " gold [" + tavern.dice.num + "]", - color: "gray", - }); - instance_emit(tavern, "tavern", { - event: "lost", - name: player.name, - type: "dice", - num: bet.num, - gold: bet.gold, - dir: bet.dir, - }); - } - if (player.xp >= player.max_xp) { - resend(player, "reopen"); - } else { - resend(player, "reopen+nc"); - } - }); - tavern.dice.next = future_s(2); - //server_log("Dice: Locked - Suspensing"); - } else if (tavern.dice.state == "suspense") { - var timer = new Date(); - tavern.info.dice = tavern.dice.state = "bets"; - tavern.info.seconds = tavern.dice.seconds = 0; - tavern.dice.players = {}; - tavern.dice.bets = []; - tavern.dice.num = - "" + - parseInt(Math.random() * 10) + - "" + - parseInt(Math.random() * 10) + - "." + - parseInt(Math.random() * 10) + - "" + - parseInt(Math.random() * 10); - tavern.dice.key = randomStr(20 + parseInt(Math.random() * 20)); - var initials = ""; - for (var id in tavern.players) { - if (!tavern.players[id].npc) { - initials += tavern.players[id].name[0]; - } - } - tavern.dice.text = - "Num: " + - tavern.dice.num + - " Initials: " + - initials + - " Random: " + - randomStr(16 + parseInt(Math.random() * 16)); - var hmac = crypto.createHmac("sha256", tavern.dice.key); - hmac.update(tavern.dice.text); - tavern.dice.hex = hmac.digest("hex"); - hmac.end(); - instance_emit(tavern, "dice", { state: "bets", hex: tavern.dice.hex, algorithm: "hmac-sha256" }); - if (gameplay == "hardcore" && Math.random() < 0.07 && dice_last_roll) { - tavern.dice.num = dice_last_roll; - } - //server_log("Dice: Bets Starting For: "+tavern.dice.num+" in "+mssince(timer)+"ms"); - } - } - if (c > tavern.roulette.next && 0) { - if (tavern.roulette.state == "bets") { - server_log("Roulette: Bets Over"); - tavern.roulette.state = "roll"; - tavern.roulette.next = future_s(10); - } else if (tavern.roulette.state == "roll") { - tavern.roulette.roll = floor(Math.random() * 37); - tavern.roulette.state = "award"; - tavern.roulette.next = future_s(5); - server_log("Roulette: Rolled " + tavern.roulette.roll); - } else if (tavern.roulette.state == "award") { - var winners = {}; - var roll = tavern.roulette.roll; - if (roll == 0) { - winners["0"] = 1; - } else { - if (roll % 2) { - winners["odd"] = 1; - } else { - winners["even"] = 1; - } + try { + if (!server.live || !instances.tavern) { + return; + } + var c = new Date(); + if (c > tavern.dice.next) { + if (tavern.dice.state == "bets") { + tavern.dice.seconds += 1; + tavern.info.seconds = tavern.dice.seconds; + if (tavern.dice.seconds >= 30) { + tavern.info.dice = tavern.dice.state = "roll"; + dice_last_roll = tavern.info.num; + delete tavern.info.num; + tavern.dice.next = future_s(10); + instance_emit(tavern, "dice", { state: "roll" }); + //server_log("Dice: Bets Over"); + } + } else if (tavern.dice.state == "roll") { + tavern.info.dice = tavern.dice.state = "lock"; + tavern.info.num = tavern.dice.num; + tavern.dice.next = future_s(1.6); + for (var id in tavern.dice.players) { + var player = players[id]; + if (!player) { + continue; + } + for (var b_id in player.bets || {}) { + var bet = player.bets[b_id]; + if (bet.type != "dice") { + continue; + } + delete players[bet.pid].bets[bet.id]; + tavern.dice.bets.push(bet); + if ( + (bet.dir == "up" && parseFloat(tavern.dice.num) >= bet.num) || + (bet.dir == "down" && parseFloat(tavern.dice.num) <= bet.num) + ) { + if (player.type == "merchant") { + player.xp += parseInt(bet.edge * 7.2); + } + player.gold += bet.win - bet.edge; + S.gold -= bet.win - bet.edge - bet.gold; + if (bet.win - bet.edge - bet.gold >= 12000000) { + lstack(S.logs.dice, { name: player.name, gold: bet.win - bet.edge - bet.gold, odds: bet.odds }); + } + } else { + if (bet.gold >= 10000000) { + lstack(S.logs.dice, { name: player.name, gold: -bet.gold, odds: bet.odds }); + } + S.gold += bet.gold; + } + } + } + instance_emit(tavern, "dice", { + state: "lock", + num: tavern.dice.num, + text: tavern.dice.text, + key: tavern.dice.key, + }); + //server_log("Dice: Roll Over - Locking"); + } else if (tavern.dice.state == "lock") { + tavern.dice.state = "suspense"; + tavern.dice.bets.forEach(function (bet) { + var player = players[bet.pid]; + if (!player) { + return; + } + if ( + (bet.dir == "up" && parseFloat(tavern.dice.num) >= bet.num) || + (bet.dir == "down" && parseFloat(tavern.dice.num) <= bet.num) + ) { + player.socket.emit("game_log", { + message: "Won: " + to_pretty_num(bet.win) + " gold [" + tavern.dice.num + "]", + color: "gold", + }); + player.socket.emit("game_log", { + message: "House edge: " + to_pretty_num(bet.edge) + " gold", + color: "gray", + }); + instance_emit(tavern, "tavern", { + event: "won", + name: player.name, + type: "dice", + num: bet.num, + gold: bet.win, + dir: bet.dir, + net: bet.win - bet.gold - bet.edge, + }); + } else { + player.socket.emit("game_log", { + message: "Lost: " + to_pretty_num(bet.gold) + " gold [" + tavern.dice.num + "]", + color: "gray", + }); + instance_emit(tavern, "tavern", { + event: "lost", + name: player.name, + type: "dice", + num: bet.num, + gold: bet.gold, + dir: bet.dir, + }); + } + if (player.xp >= player.max_xp) { + resend(player, "reopen"); + } else { + resend(player, "reopen+nc"); + } + }); + tavern.dice.next = future_s(2); + //server_log("Dice: Locked - Suspensing"); + } else if (tavern.dice.state == "suspense") { + var timer = new Date(); + tavern.info.dice = tavern.dice.state = "bets"; + tavern.info.seconds = tavern.dice.seconds = 0; + tavern.dice.players = {}; + tavern.dice.bets = []; + tavern.dice.num = + "" + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10) + + "." + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10); + tavern.dice.key = randomStr(20 + parseInt(Math.random() * 20)); + var initials = ""; + for (var id in tavern.players) { + if (!tavern.players[id].npc) { + initials += tavern.players[id].name[0]; + } + } + tavern.dice.text = + "Num: " + + tavern.dice.num + + " Initials: " + + initials + + " Random: " + + randomStr(16 + parseInt(Math.random() * 16)); + var hmac = crypto.createHmac("sha256", tavern.dice.key); + hmac.update(tavern.dice.text); + tavern.dice.hex = hmac.digest("hex"); + hmac.end(); + instance_emit(tavern, "dice", { state: "bets", hex: tavern.dice.hex, algorithm: "hmac-sha256" }); + if (gameplay == "hardcore" && Math.random() < 0.07 && dice_last_roll) { + tavern.dice.num = dice_last_roll; + } + //server_log("Dice: Bets Starting For: "+tavern.dice.num+" in "+mssince(timer)+"ms"); + } + } + if (c > tavern.roulette.next && 0) { + if (tavern.roulette.state == "bets") { + server_log("Roulette: Bets Over"); + tavern.roulette.state = "roll"; + tavern.roulette.next = future_s(10); + } else if (tavern.roulette.state == "roll") { + tavern.roulette.roll = floor(Math.random() * 37); + tavern.roulette.state = "award"; + tavern.roulette.next = future_s(5); + server_log("Roulette: Rolled " + tavern.roulette.roll); + } else if (tavern.roulette.state == "award") { + var winners = {}; + var roll = tavern.roulette.roll; + if (roll == 0) { + winners["0"] = 1; + } else { + if (roll % 2) { + winners["odd"] = 1; + } else { + winners["even"] = 1; + } - if ([2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35].indexOf(roll) != -1) { - winners["black"] = 1; - } else { - winners["red"] = 1; - } + if ([2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35].indexOf(roll) != -1) { + winners["black"] = 1; + } else { + winners["red"] = 1; + } - if (roll <= 12) { - winners["1/3"] = 1; - } else if (roll <= 24) { - winners["2/3"] = 1; - } else { - winners["3/3"] = 1; - } + if (roll <= 12) { + winners["1/3"] = 1; + } else if (roll <= 24) { + winners["2/3"] = 1; + } else { + winners["3/3"] = 1; + } - if (roll <= 18) { - winners["1/2"] = 1; - } else { - winners["2/2"] = 1; - } + if (roll <= 18) { + winners["1/2"] = 1; + } else { + winners["2/2"] = 1; + } - if (roll % 3 == 1) { - winners["3n+1"] = 1; - } else if (roll % 3 == 2) { - winners["3n+2"] = 1; - } else { - winners["3n"] = 1; - } - } - var totals = {}; - try { - for (var id in tavern.roulette.players) { - var player = players[id]; - if (!player) { - continue; - } - for (var b_id in player.bets || {}) { - var bet = player.bets[b_id]; - if (bet.type != "roulette") { - continue; - } - if (!totals[bet.pid]) { - totals[bet.pid] = 0; - } - if (winners[bet.odd]) { - totals[bet.pid] += bet.gold * tavern.roulette.odds[bet.odd]; - tavern.roulette.gain -= bet.gold * tavern.roulette.odds[bet.odd]; - } else { - tavern.roulette.gain += bet.gold; - } - // else totals[bet.pid]-=bet.gold; - if (players[bet.pid]) { - delete players[bet.pid].bets[bet.id]; - if (winners[bet.odd]) { - players[bet.pid].gold += bet.gold * tavern.roulette.odds[bet.odd]; - } - } - } - } - } catch (e) { - log_trace("Critical-roulette_win", e); - } - server_log("Roulette: Awards | Gain: " + to_pretty_num(tavern.roulette.gain)); - for (var id in totals) { - var player = players[id]; - if (!player) { - continue; - } - if (winners["0"]) { - player.socket.emit("game_log", { message: "The lucky number is " + roll + " Green", color: "#2E7C2A" }); - } else if (winners["red"]) { - player.socket.emit("game_log", { message: "The lucky number is " + roll + " Red", color: "#911609" }); - } else { - player.socket.emit("game_log", { message: "The lucky number is " + roll + " Black", color: "#5C5D5D" }); - } - if (!totals[id]) { - player.socket.emit("game_log", "Better luck next time"); - } else { - player.socket.emit("game_log", { - message: "You've won " + to_pretty_num(totals[id]) + " gold", - color: "gold", - }); - } - resend(player, "reopen+nc"); - } - tavern.roulette.state = "bets"; - tavern.roulette.next = future_s(25); - server_log("Roulette: Bets Open"); - } - } - tavern.poker.rooms.forEach(function (room) { - if (room.state == "shuffle") { - room.cards = []; - for (var i = 1; i <= 52; i++) { - room.cards.push(i); - } - shuffle(room.cards); - } - }); - } catch (e) { - log_trace("Critical-tavern_loop", e); - } + if (roll % 3 == 1) { + winners["3n+1"] = 1; + } else if (roll % 3 == 2) { + winners["3n+2"] = 1; + } else { + winners["3n"] = 1; + } + } + var totals = {}; + try { + for (var id in tavern.roulette.players) { + var player = players[id]; + if (!player) { + continue; + } + for (var b_id in player.bets || {}) { + var bet = player.bets[b_id]; + if (bet.type != "roulette") { + continue; + } + if (!totals[bet.pid]) { + totals[bet.pid] = 0; + } + if (winners[bet.odd]) { + totals[bet.pid] += bet.gold * tavern.roulette.odds[bet.odd]; + tavern.roulette.gain -= bet.gold * tavern.roulette.odds[bet.odd]; + } else { + tavern.roulette.gain += bet.gold; + } + // else totals[bet.pid]-=bet.gold; + if (players[bet.pid]) { + delete players[bet.pid].bets[bet.id]; + if (winners[bet.odd]) { + players[bet.pid].gold += bet.gold * tavern.roulette.odds[bet.odd]; + } + } + } + } + } catch (e) { + log_trace("Critical-roulette_win", e); + } + server_log("Roulette: Awards | Gain: " + to_pretty_num(tavern.roulette.gain)); + for (var id in totals) { + var player = players[id]; + if (!player) { + continue; + } + if (winners["0"]) { + player.socket.emit("game_log", { message: "The lucky number is " + roll + " Green", color: "#2E7C2A" }); + } else if (winners["red"]) { + player.socket.emit("game_log", { message: "The lucky number is " + roll + " Red", color: "#911609" }); + } else { + player.socket.emit("game_log", { message: "The lucky number is " + roll + " Black", color: "#5C5D5D" }); + } + if (!totals[id]) { + player.socket.emit("game_log", "Better luck next time"); + } else { + player.socket.emit("game_log", { + message: "You've won " + to_pretty_num(totals[id]) + " gold", + color: "gold", + }); + } + resend(player, "reopen+nc"); + } + tavern.roulette.state = "bets"; + tavern.roulette.next = future_s(25); + server_log("Roulette: Bets Open"); + } + } + tavern.poker.rooms.forEach(function (room) { + if (room.state == "shuffle") { + room.cards = []; + for (var i = 1; i <= 52; i++) { + room.cards.push(i); + } + shuffle(room.cards); + } + }); + } catch (e) { + log_trace("Critical-tavern_loop", e); + } } function create_npc(npc, map_def, instance) { - var entity = { - speed: npc.speed || 20, - attack: npc.attack || 100, - range: npc.range || 40, - level: npc.level || 100, - hp: npc.hp || 1200, - max_hp: npc.hp || 1200, - armor: 500, - mp: 2000, - a: {}, - xp: 0, - pdps: 0, // once got killed by a blaster ... [07/04/21] - skin: npc.skin, - name: npc.name, - red_zone: 0, - in: instance.name, - map: instance.map, - def: npc, - npc: map_def.id, - ntype: map_def.id, - is_player: true, - is_npc: true, - type: npc["class"] || "merchant", - id: "$" + npc.name, - gold: 0, - items: [], - citems: [], - cid: 0, - u: true, - citizen: true, - luckm: 1, // for loot_all_monsters - s: {}, - c: {}, - q: {}, - p: {}, - m: 0, - slots: {}, - bets: {}, - vision: [0, 0], - socket: false_socket, - cx: npc.cx || {}, - base: { h: 8, v: 7, vn: 2 }, - last: { move: new Date(), attack: really_old, attacked: really_old }, - delay: npc.delay || 600, - d_multiplier: 1, - steps: npc.steps || 40, - }; + var entity = { + speed: npc.speed || 20, + attack: npc.attack || 100, + range: npc.range || 40, + level: npc.level || 100, + hp: npc.hp || 1200, + max_hp: npc.hp || 1200, + armor: 500, + mp: 2000, + a: {}, + xp: 0, + pdps: 0, // once got killed by a blaster ... [07/04/21] + skin: npc.skin, + name: npc.name, + red_zone: 0, + in: instance.name, + map: instance.map, + def: npc, + npc: map_def.id, + ntype: map_def.id, + is_player: true, + is_npc: true, + type: npc["class"] || "merchant", + id: "$" + npc.name, + gold: 0, + items: [], + citems: [], + cid: 0, + u: true, + citizen: true, + luckm: 1, // for loot_all_monsters + s: {}, + c: {}, + q: {}, + p: {}, + m: 0, + slots: {}, + bets: {}, + vision: [0, 0], + socket: false_socket, + cx: npc.cx || {}, + base: { h: 8, v: 7, vn: 2 }, + last: { move: new Date(), attack: really_old, attacked: really_old }, + delay: npc.delay || 600, + d_multiplier: 1, + steps: npc.steps || 40, + }; - if (npc.slots) { - entity.slots = npc.slots; - } - if (npc.projectile) { - entity.projectile = npc.projectile; - } + if (npc.slots) { + entity.slots = npc.slots; + } + if (npc.projectile) { + entity.projectile = npc.projectile; + } - if (map_def.positions) { - entity.x = map_def.positions[0][0]; - entity.y = map_def.positions[0][1]; - entity.positions = map_def.positions; - } else { - entity.x = map_def.position[0]; - entity.y = map_def.position[1]; - if (npc.type == "fullstatic" && map_def.position.length == 3) { - entity.direction = map_def.position[2]; - } - } + if (map_def.positions) { + entity.x = map_def.positions[0][0]; + entity.y = map_def.positions[0][1]; + entity.positions = map_def.positions; + } else { + entity.x = map_def.position[0]; + entity.y = map_def.position[1]; + if (npc.type == "fullstatic" && map_def.position.length == 3) { + entity.direction = map_def.position[2]; + } + } - if (map_def.boundary) { - entity.boundary = map_def.boundary; - } - if (map_def.loop) { - entity.going_x = entity.x; - entity.going_y = entity.y; - start_moving_element(entity); - entity.loop = true; - entity.last_m = 0; - } + if (map_def.boundary) { + entity.boundary = map_def.boundary; + } + if (map_def.loop) { + entity.going_x = entity.x; + entity.going_y = entity.y; + start_moving_element(entity); + entity.loop = true; + entity.last_m = 0; + } - if (npc.role == "citizen" || npc.moving) { - entity.movable = true; - } - return entity; + if (npc.role == "citizen" || npc.moving) { + entity.movable = true; + } + return entity; } function create_instance(name, map_name, args) { - if (!map_name) { - map_name = name; - } - if (!smap_data[map_name] && smap_data[map_name] != -1) { - server_bfs(map_name); - } - if (!args) { - args = {}; - } - var instance = { - players: {}, - monsters: {}, - observers: {}, - map: map_name, - allow: true, - name: name, - pmap: {}, - rage_list: [], - last_update: future_ms(parseInt(Math.random() * 30)), - last_player: future_s(240), - npcs: 0, - paused: false, - info: {}, - operators: 1, - }; - instances[name] = instance; - if (args.solo) { - instance.solo = args.solo; - } - if (args.pvp) { - instance.pvp = true; - instance.allow = false; - } - if (args.event) { - instance.allow = false; - } - var map = G.maps[map_name]; - if (map.mount && gameplay == "normal") { - instance.mount = true; - } - for (var i = 0; i < (map.monsters || []).length; i++) { - var map_def = map.monsters[i]; - if (map_def.special) { - continue; - } - if (map_def.rage) { - map_def.id = randomStr(5); - instance.rage_list.push(map_def); - } - for (var j = 0; j < map_def.count; j++) { - if (G.monsters[map_def.type].announce) { - setTimeout(new_monster_f(name, map_def), 120 * 1000); - } else { - new_monster(name, map_def); - } - } - } - // for(var i=0;i<(map.specials||[]).length;i++) - // { - // var map_def=map.specials[i]; - // new_monster(name,map_def,1); - // } - // console.log(JSON.stringify(instance.players)); - for (var i = 0; i < (map.npcs || []).length; i++) { - var map_def = map.npcs[i]; - var def = G.npcs[map_def.id]; - if (instance.players[NPC_prefix + def.name]) { - console.log("NPC NAME CLASH: " + def.name); - shutdown_routine(); - } - var npc = (instance.players[NPC_prefix + def.name] = create_npc(def, map_def, instance)); - npcs[map_def.id] = npc; - instance.npcs += 1; - } - map.spawns.forEach(function (spawn) { - var cdist = closest_line(map_name, spawn[0], spawn[1]); - if (cdist < 12) { - console.log( - "Spawn [" + map_name + "," + spawn[0] + "," + spawn[1] + "] is " + cdist + " close to a line! >=12 is a must.", - ); - } - }); - server_log("Created an instance of " + instances[name].map, 1); - return instance; + if (!map_name) { + map_name = name; + } + if (!smap_data[map_name] && smap_data[map_name] != -1) { + server_bfs(map_name); + } + if (!args) { + args = {}; + } + var instance = { + players: {}, + monsters: {}, + observers: {}, + map: map_name, + allow: true, + name: name, + pmap: {}, + rage_list: [], + last_update: future_ms(parseInt(Math.random() * 30)), + last_player: future_s(240), + npcs: 0, + paused: false, + info: {}, + operators: 1, + }; + instances[name] = instance; + if (args.solo) { + instance.solo = args.solo; + } + if (args.pvp) { + instance.pvp = true; + instance.allow = false; + } + if (args.event) { + instance.allow = false; + } + var map = G.maps[map_name]; + if (map.mount && gameplay == "normal") { + instance.mount = true; + } + for (var i = 0; i < (map.monsters || []).length; i++) { + var map_def = map.monsters[i]; + if (map_def.special) { + continue; + } + if (map_def.rage) { + map_def.id = randomStr(5); + instance.rage_list.push(map_def); + } + for (var j = 0; j < map_def.count; j++) { + if (G.monsters[map_def.type].announce) { + setTimeout(new_monster_f(name, map_def), 120 * 1000); + } else { + new_monster(name, map_def); + } + } + } + // for(var i=0;i<(map.specials||[]).length;i++) + // { + // var map_def=map.specials[i]; + // new_monster(name,map_def,1); + // } + // console.log(JSON.stringify(instance.players)); + for (var i = 0; i < (map.npcs || []).length; i++) { + var map_def = map.npcs[i]; + var def = G.npcs[map_def.id]; + if (instance.players[NPC_prefix + def.name]) { + console.log("NPC NAME CLASH: " + def.name); + shutdown_routine(); + } + var npc = (instance.players[NPC_prefix + def.name] = create_npc(def, map_def, instance)); + npcs[map_def.id] = npc; + instance.npcs += 1; + } + map.spawns.forEach(function (spawn) { + var cdist = closest_line(map_name, spawn[0], spawn[1]); + if (cdist < 12) { + console.log( + "Spawn [" + map_name + "," + spawn[0] + "," + spawn[1] + "] is " + cdist + " close to a line! >=12 is a must.", + ); + } + }); + server_log("Created an instance of " + instances[name].map, 1); + return instance; } function pause_instance(instance) { - server_log("Paused: " + instance.name); - instance.paused = true; + server_log("Paused: " + instance.name); + instance.paused = true; } function resume_instance(instance) { - if (!instance.paused) { - return; - } - // server_log("Resumed: "+instance.name); - instance.paused = false; - update_instance(instance); + if (!instance.paused) { + return; + } + // server_log("Resumed: "+instance.name); + instance.paused = false; + update_instance(instance); } function destroy_instance(name) { - for (var id in instances[name].players) { - var player = instances[name].players[id]; - if (player.npc) { - continue; - } - if ( - player.state && - !player.state.restored && - instances[player.state.map] && - !instances[player.state.map].mount && - player.in != name - ) { - transport_player_to(player, player.state.map, [player.state.x, player.state.y]); - } else { - transport_player_to(player, "main"); - } - restore_state(player); - resend(player, "cid+reopen"); // reopen for abtesting - } - for (var id in instances[name].monsters) { - remove_monster(instances[name].monsters[id], { silent: true }); // to make sure targets are always updated - } - server_log("Deleted an instance of " + instances[name].map, 1); - delete instances[name]; + for (var id in instances[name].players) { + var player = instances[name].players[id]; + if (player.npc) { + continue; + } + if ( + player.state && + !player.state.restored && + instances[player.state.map] && + !instances[player.state.map].mount && + player.in != name + ) { + transport_player_to(player, player.state.map, [player.state.x, player.state.y]); + } else { + transport_player_to(player, "main"); + } + restore_state(player); + resend(player, "cid+reopen"); // reopen for abtesting + } + for (var id in instances[name].monsters) { + remove_monster(instances[name].monsters[id], { silent: true }); // to make sure targets are always updated + } + server_log("Deleted an instance of " + instances[name].map, 1); + delete instances[name]; } function spawn_special_monster(type) { - if (type == "pinkgoo") { - var packs = []; - ["main", "cave", "halloween", "winterland"].forEach(function (m) { - G.maps[m].monsters.forEach(function (p) { - if (!p.boundaries) { - packs.push({ type: "pinkgoo", boundary: p.boundary, count: 1, i: m }); - } // comment boundary out next year [09/02/19] - }); - }); - var pack = packs[floor(Math.random() * packs.length)]; - pack.gold = D.monster_gold.pinkgoo; - server_log("Love Goo: " + JSON.stringify(pack)); - new_monster(pack.i, pack); - broadcast("game_event", { name: "pinkgoo", map: pack.i }); - } - if (type == "crabxx") { - var pack = null; - ["main"].forEach(function (m) { - G.maps[m].monsters.forEach(function (p) { - if (p.type == "crabx") { - pack = { type: "crabxx", boundary: p.boundary, count: 1, i: m }; - } - }); - }); - pack.gold = D.monster_gold.crabxx; - server_log("Crab XX: " + JSON.stringify(pack)); - new_monster(pack.i, pack); - broadcast("game_event", { name: "crabxx", map: pack.i }); - } - if (type == "snowman") { - var packs = []; - ["winterland"].forEach(function (m) { - G.maps[m].monsters.forEach(function (p) { - if (!p.boundaries) { - packs.push({ type: "snowman", boundary: p.boundary, count: 1, i: m }); - } - }); - }); - var pack = packs[floor(Math.random() * packs.length)]; - pack.gold = D.monster_gold.snowman; - pack = { type: "snowman", boundary: [682, -967, 1482, -779], count: 1, i: "winterland" }; - server_log("Snowman: " + JSON.stringify(pack)); - new_monster(pack.i, pack); - broadcast("game_event", { name: "snowman", map: pack.i, x: 900, y: -800 }); - } - if (type == "dragold") { - var packs = []; - var pack = packs[floor(Math.random() * packs.length)]; - pack = { type: "dragold", boundary: [1018, -940, 1385, -624], count: 1, i: "cave" }; - pack.gold = 100000; - server_log("Dragold: " + JSON.stringify(pack)); - new_monster(pack.i, pack); - broadcast("game_event", { name: "dragold", map: pack.i, x: 900, y: -800 }); - } - if (type == "franky") { - var map = "level2w"; - var pack = { type: "franky", boundary: G.maps[map].monsters[0].boundary, count: 1, i: map }; - pack.gold = D.monster_gold.franky; - server_log("Franky: " + JSON.stringify(pack)); - new_monster(pack.i, pack); - broadcast("game_event", { - name: "franky", - map: pack.i, - x: G.maps[map].monsters[0].boundary[0], - y: G.maps[map].monsters[0].boundary[1], - }); - } - if (type == "icegolem") { - var map = "winterland"; - var pack = { type: "icegolem", boundary: [782.25, 395.96, 888.71, 450.28], count: 1, i: map, roam: true }; - pack.gold = D.monster_gold.icegolem; - server_log("Ice Golem: " + JSON.stringify(pack)); - new_monster(pack.i, pack); - broadcast("game_event", { name: "icegolem", map: pack.i, x: pack.boundary[0], y: pack.boundary[1] }); - } - if (type == "mrpumpkin" || type == "mrgreen" || type == "grinch") { - var pack = {}; - var map = "none"; - for (var mname in G.maps) { - var def = G.maps[mname]; - if (def.ignore) { - continue; - } - (def.monsters || []).forEach(function (p) { - if (p.type == type) { - pack = p; - map = mname; - } - }); - } - server_log(G.monsters[type].name + ": " + JSON.stringify(pack)); - var monster = new_monster(map, pack); - broadcast("game_event", { name: type, map: map, x: pack.boundary[0], y: pack.boundary[1] }); - if (type == "grinch") { - monster.extra_gold = 12000000; - } - } - if (type == "slenderman") { - var packs = []; - var m = random_one(["cave", "halloween", "spookytown"]); - var p = random_place(m); - var pack = { type: "slenderman", boundary: [p.x, p.y, p.x, p.y], count: 1, i: m }; - pack.gold = D.monster_gold.slenderman; - //server_log("Love Goo: "+JSON.stringify(pack)); - var monster = new_monster(pack.i, pack); - broadcast("game_event", { name: "slenderman", map: pack.i }); - } - if (type == "tiger") { - var packs = []; - var m = random_one(["cave", "main"]); - var p = random_place(m); - var pack = { type: "tiger", boundary: [p.x, p.y, p.x, p.y], count: 1, i: m }; - pack.gold = D.monster_gold.tiger; - //server_log("Love Goo: "+JSON.stringify(pack)); - var monster = new_monster(pack.i, pack); - broadcast("game_event", { name: "tiger", map: pack.i }); - } - if (type == "wabbit") { - var packs = []; - ["main", "cave", "halloween", "winterland", "tunnel", "mansion", "winter_cave"].forEach(function (m) { - G.maps[m].monsters.forEach(function (p) { - if (!p.boundaries) { - packs.push({ type: "wabbit", boundary: p.boundary, count: 1, i: m, roam: true }); - } - }); - }); - var pack = packs[floor(Math.random() * packs.length)]; - pack.gold = D.monster_gold.wabbit; - //server_log("Love Goo: "+JSON.stringify(pack)); - new_monster(pack.i, pack); - broadcast("game_event", { name: "wabbit", map: pack.i }); - } - if (type == "goldenbat") { - var packs = []; - ["cave"].forEach(function (m) { - G.maps[m].monsters.forEach(function (p) { - if (!p.boundaries) { - packs.push({ type: "goldenbat", boundary: p.boundary, count: 1, i: m, roam: true }); - } - }); - }); - var pack = packs[floor(Math.random() * packs.length)]; - pack.gold = D.monster_gold.goldenbat; - //server_log("Love Goo: "+JSON.stringify(pack)); - new_monster(pack.i, pack); - // broadcast("game_event",{name:"goldenbat",map:pack.i}); - } - if (type == "cutebee") { - var packs = []; - ["main"].forEach(function (m) { - G.maps[m].monsters.forEach(function (p) { - if (!p.boundaries) { - packs.push({ type: "cutebee", boundary: p.boundary, count: 1, i: m, roam: true }); - } - }); - }); - var pack = packs[floor(Math.random() * packs.length)]; - pack.gold = D.monster_gold.cutebee; - //server_log("Love Goo: "+JSON.stringify(pack)); - new_monster(pack.i, pack); - } + if (type == "pinkgoo") { + var packs = []; + ["main", "cave", "halloween", "winterland"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "pinkgoo", boundary: p.boundary, count: 1, i: m }); + } // comment boundary out next year [09/02/19] + }); + }); + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.pinkgoo; + server_log("Love Goo: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "pinkgoo", map: pack.i }); + } + if (type == "crabxx") { + var pack = null; + ["main"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (p.type == "crabx") { + pack = { type: "crabxx", boundary: p.boundary, count: 1, i: m }; + } + }); + }); + pack.gold = D.monster_gold.crabxx; + server_log("Crab XX: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "crabxx", map: pack.i }); + } + if (type == "snowman") { + var packs = []; + ["winterland"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "snowman", boundary: p.boundary, count: 1, i: m }); + } + }); + }); + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.snowman; + pack = { type: "snowman", boundary: [682, -967, 1482, -779], count: 1, i: "winterland" }; + server_log("Snowman: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "snowman", map: pack.i, x: 900, y: -800 }); + } + if (type == "dragold") { + var packs = []; + var pack = packs[floor(Math.random() * packs.length)]; + pack = { type: "dragold", boundary: [1018, -940, 1385, -624], count: 1, i: "cave" }; + pack.gold = 100000; + server_log("Dragold: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "dragold", map: pack.i, x: 900, y: -800 }); + } + if (type == "franky") { + var map = "level2w"; + var pack = { type: "franky", boundary: G.maps[map].monsters[0].boundary, count: 1, i: map }; + pack.gold = D.monster_gold.franky; + server_log("Franky: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { + name: "franky", + map: pack.i, + x: G.maps[map].monsters[0].boundary[0], + y: G.maps[map].monsters[0].boundary[1], + }); + } + if (type == "icegolem") { + var map = "winterland"; + var pack = { type: "icegolem", boundary: [782.25, 395.96, 888.71, 450.28], count: 1, i: map, roam: true }; + pack.gold = D.monster_gold.icegolem; + server_log("Ice Golem: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "icegolem", map: pack.i, x: pack.boundary[0], y: pack.boundary[1] }); + } + if (type == "mrpumpkin" || type == "mrgreen" || type == "grinch") { + var pack = {}; + var map = "none"; + for (var mname in G.maps) { + var def = G.maps[mname]; + if (def.ignore) { + continue; + } + (def.monsters || []).forEach(function (p) { + if (p.type == type) { + pack = p; + map = mname; + } + }); + } + server_log(G.monsters[type].name + ": " + JSON.stringify(pack)); + var monster = new_monster(map, pack); + broadcast("game_event", { name: type, map: map, x: pack.boundary[0], y: pack.boundary[1] }); + if (type == "grinch") { + monster.extra_gold = 12000000; + } + } + if (type == "slenderman") { + var packs = []; + var m = random_one(["cave", "halloween", "spookytown"]); + var p = random_place(m); + var pack = { type: "slenderman", boundary: [p.x, p.y, p.x, p.y], count: 1, i: m }; + pack.gold = D.monster_gold.slenderman; + //server_log("Love Goo: "+JSON.stringify(pack)); + var monster = new_monster(pack.i, pack); + broadcast("game_event", { name: "slenderman", map: pack.i }); + } + if (type == "tiger") { + var packs = []; + var m = random_one(["cave", "main"]); + var p = random_place(m); + var pack = { type: "tiger", boundary: [p.x, p.y, p.x, p.y], count: 1, i: m }; + pack.gold = D.monster_gold.tiger; + //server_log("Love Goo: "+JSON.stringify(pack)); + var monster = new_monster(pack.i, pack); + broadcast("game_event", { name: "tiger", map: pack.i }); + } + if (type == "wabbit") { + var packs = []; + ["main", "cave", "halloween", "winterland", "tunnel", "mansion", "winter_cave"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "wabbit", boundary: p.boundary, count: 1, i: m, roam: true }); + } + }); + }); + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.wabbit; + //server_log("Love Goo: "+JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "wabbit", map: pack.i }); + } + if (type == "goldenbat") { + var packs = []; + ["cave"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "goldenbat", boundary: p.boundary, count: 1, i: m, roam: true }); + } + }); + }); + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.goldenbat; + //server_log("Love Goo: "+JSON.stringify(pack)); + new_monster(pack.i, pack); + // broadcast("game_event",{name:"goldenbat",map:pack.i}); + } + if (type == "cutebee") { + var packs = []; + ["main"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "cutebee", boundary: p.boundary, count: 1, i: m, roam: true }); + } + }); + }); + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.cutebee; + //server_log("Love Goo: "+JSON.stringify(pack)); + new_monster(pack.i, pack); + } } function collect_signups(event) { - var names = []; - var toggle = 0; - for (var name in signups) { - names.push(name); - } - shuffle(names); - names.forEach(function (name) { - var player = players[name_to_id[name]]; - if (!player) { - return; - } + var names = []; + var toggle = 0; + for (var name in signups) { + names.push(name); + } + shuffle(names); + names.forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + return; + } - if (event == "abtesting") { - player.team = (toggle == 1 && "A") || "B"; - toggle = 1 - toggle; - } + if (event == "abtesting") { + player.team = (toggle == 1 && "A") || "B"; + toggle = 1 - toggle; + } - if (event == "abtesting" && player.team == "A") { - transport_player_to(player, event, 2); - resend(player, "cid"); - } else if (event == "abtesting" && player.team == "B") { - transport_player_to(player, event, 3); - resend(player, "cid"); - } else { - transport_player_to(player, event); - } - }); + if (event == "abtesting" && player.team == "A") { + transport_player_to(player, event, 2); + resend(player, "cid"); + } else if (event == "abtesting" && player.team == "B") { + transport_player_to(player, event, 3); + resend(player, "cid"); + } else { + transport_player_to(player, event); + } + }); } var last_daily = null; function event_loop() { - try { - var c = new Date(); - var ch = (c.getUTCHours() + 24 + E.schedule.time_offset) % 24; - var change = false; - if (ch >= 0 && ch <= 5) { - E.schedule.night = true; - } else { - E.schedule.night = false; - } + try { + var c = new Date(); + var ch = (c.getUTCHours() + 24 + E.schedule.time_offset) % 24; + var change = false; + if (ch >= 0 && ch <= 5) { + E.schedule.night = true; + } else { + E.schedule.night = false; + } - E.schedule.dailies.forEach(function (h) { - if (ch == h && last_daily != h && msince(server_start) > 2) { - last_daily = h; - var event = dailies.shift(); - dailies.push(event); - events[event] = true; - } - }); + E.schedule.dailies.forEach(function (h) { + if (ch == h && last_daily != h && msince(server_start) > 2) { + last_daily = h; + var event = dailies.shift(); + dailies.push(event); + events[event] = true; + } + }); - E.schedule.nightlies.forEach(function (h) { - if (ch == h && last_daily != h && msince(server_start) > 2) { - last_daily = h; - var event = nightlies.shift(); - nightlies.push(event); - events[event] = true; - } - }); + E.schedule.nightlies.forEach(function (h) { + if (ch == h && last_daily != h && msince(server_start) > 2) { + last_daily = h; + var event = nightlies.shift(); + nightlies.push(event); + events[event] = true; + } + }); - if (events.halloween) { - var s = get_monster("slenderman"); - if (s) { - var p = false; - for (var id in projectiles) { - if (projectiles[id].target == s) { - p = true; - } - } - if (!s.last_jump || msince(s.last_jump) > 2) { - p = true; - } - for (var name in instances[s.in].players) { - var player = instances[s.in].players[name]; - if (p || (!player.s.invis && distance(player, s) < 600)) { - xy_emit(s, "disappear", { id: s.id }); - delete instances[s.in].monsters[s.id]; - s.oin = s.in = s.map = random_one(["spookytown", "halloween", "cave"]); - instances[s.in].monsters[s.id] = s; - var p = random_place(s.map); - s.moving = false; - s.abs = true; - s.map_def.boundary = [p.x, p.y, p.x, p.y]; - s.map_def.i = s.map; - s.x = p.x; - s.y = p.y; - s.u = true; - s.cid++; - s.last_jump = new Date(); - break; - } - } - } - } + if (events.halloween) { + var s = get_monster("slenderman"); + if (s) { + var p = false; + for (var id in projectiles) { + if (projectiles[id].target == s) { + p = true; + } + } + if (!s.last_jump || msince(s.last_jump) > 2) { + p = true; + } + for (var name in instances[s.in].players) { + var player = instances[s.in].players[name]; + if (p || (!player.s.invis && distance(player, s) < 600)) { + xy_emit(s, "disappear", { id: s.id }); + delete instances[s.in].monsters[s.id]; + s.oin = s.in = s.map = random_one(["spookytown", "halloween", "cave"]); + instances[s.in].monsters[s.id] = s; + var p = random_place(s.map); + s.moving = false; + s.abs = true; + s.map_def.boundary = [p.x, p.y, p.x, p.y]; + s.map_def.i = s.map; + s.x = p.x; + s.y = p.y; + s.u = true; + s.cid++; + s.last_jump = new Date(); + break; + } + } + } + } - if (monster_c.tiger) { - var m = get_monster("tiger"); - var ps = []; - for (var id in players) { - ps.push(players[id]); - } - var player = random_one(ps); - var spot = null; - var p = false; - for (var id in projectiles) { - if (projectiles[id].target == m && projectiles[id].attack > 999) { - p = true; - } - } - if (p || Math.random() < 0.01) { - if (player) { - spot = safe_xy_nearby(player.map, player.x, player.y); - } - if ( - m && - player && - player.in == player.map && - spot && - !G.maps[player.map].mount && - !player.s.hopsickness && - player.p.home == region + server_name - ) { - xy_emit(m, "disappear", { id: m.id }); - delete instances[m.in].monsters[m.id]; - m.oin = m.in = m.map = player.map; - instances[m.in].monsters[m.id] = m; - m.moving = false; - m.abs = true; - m.x = spot.x; - m.y = spot.y; - m.map_def.boundary = [m.x, m.y, m.x, m.y]; - m.map_def.i = m.map; - m.u = true; - m.cid++; - m.last_jump = new Date(); - } - } - } - ["crabxx", "franky", "icegolem"].forEach(function (name) { - if (events[name]) { - var monster = get_monster(name); - if (!monster && monster_c[name]) { - return; - } // irregular move - if (!timers[name]) { - timers[name] = future_s(G.events[name].duration); - spawn_special_monster(name); - } else if (c > timers[name] && (!monster || !monster.last.attacked || ssince(monster.last.attacked) > 20)) { - if (monster) { - remove_monster(monster, { method: "disappear" }); - } - broadcast("notice", { message: G.monsters[name].name + " Event is over ..." }); - events[name] = false; - change = true; - delete E[name]; - delete timers[name]; - } else { - var monster = get_monster(name); - if (!monster) { - broadcast("notice", { message: G.monsters[name].name + " has been defeated!" }); - events[name] = false; - change = true; - delete E[name]; - delete timers[name]; - } else { - if (!E[name]) { - change = true; - } - E[name] = { - live: true, - map: monster.map, - hp: monster.hp, - max_hp: monster.max_hp, - target: monster.target, - x: monster.x, - y: monster.y, - end: timers[name], - }; - if (name == "crabxx") { - var big = get_monster("crabx"); - if (monster && big && !monster["1hp"]) { - monster["1hp"] = true; - monster.s = {}; - monster.cid++; - monster.u = true; - } else if (monster && !big && monster["1hp"]) { - monster["1hp"] = false; - monster.cid++; - monster.u = true; - } - } - } - } - } - }); + if (monster_c.tiger) { + var m = get_monster("tiger"); + var ps = []; + for (var id in players) { + ps.push(players[id]); + } + var player = random_one(ps); + var spot = null; + var p = false; + for (var id in projectiles) { + if (projectiles[id].target == m && projectiles[id].attack > 999) { + p = true; + } + } + if (p || Math.random() < 0.01) { + if (player) { + spot = safe_xy_nearby(player.map, player.x, player.y); + } + if ( + m && + player && + player.in == player.map && + spot && + !G.maps[player.map].mount && + !player.s.hopsickness && + player.p.home == region + server_name + ) { + xy_emit(m, "disappear", { id: m.id }); + delete instances[m.in].monsters[m.id]; + m.oin = m.in = m.map = player.map; + instances[m.in].monsters[m.id] = m; + m.moving = false; + m.abs = true; + m.x = spot.x; + m.y = spot.y; + m.map_def.boundary = [m.x, m.y, m.x, m.y]; + m.map_def.i = m.map; + m.u = true; + m.cid++; + m.last_jump = new Date(); + } + } + } + ["crabxx", "franky", "icegolem"].forEach(function (name) { + if (events[name]) { + var monster = get_monster(name); + if (!monster && monster_c[name]) { + return; + } // irregular move + if (!timers[name]) { + timers[name] = future_s(G.events[name].duration); + spawn_special_monster(name); + } else if (c > timers[name] && (!monster || !monster.last.attacked || ssince(monster.last.attacked) > 20)) { + if (monster) { + remove_monster(monster, { method: "disappear" }); + } + broadcast("notice", { message: G.monsters[name].name + " Event is over ..." }); + events[name] = false; + change = true; + delete E[name]; + delete timers[name]; + } else { + var monster = get_monster(name); + if (!monster) { + broadcast("notice", { message: G.monsters[name].name + " has been defeated!" }); + events[name] = false; + change = true; + delete E[name]; + delete timers[name]; + } else { + if (!E[name]) { + change = true; + } + E[name] = { + live: true, + map: monster.map, + hp: monster.hp, + max_hp: monster.max_hp, + target: monster.target, + x: monster.x, + y: monster.y, + end: timers[name], + }; + if (name == "crabxx") { + var big = get_monster("crabx"); + if (monster && big && !monster["1hp"]) { + monster["1hp"] = true; + monster.s = {}; + monster.cid++; + monster.u = true; + } else if (monster && !big && monster["1hp"]) { + monster["1hp"] = false; + monster.cid++; + monster.u = true; + } + } + } + } + } + }); - if (events.goobrawl) { - if (!timers.goobrawl) { - timers.goobrawl = future_s(G.events.goobrawl.duration); - E.goobrawl = { end: timers.goobrawl }; - change = true; - //create_instance("goobrawl","goobrawl",{event:true}); - // collect_signups("goobrawl"); - broadcast("notice", { message: "Goo Brawl has begun!" }); - } else if (c > timers.goobrawl) { - events.goobrawl = false; - delete E.goobrawl; - delete timers.goobrawl; - change = true; - //destroy_instance("goobrawl"); - broadcast("notice", { message: "Goo Brawl is over, hope you had fun!" }); - } else if (instances.goobrawl && Object.keys(instances.goobrawl.monsters).length < 6 && Math.random() < 0.3) { - if (Math.random() < 0.01) { - var data = clone(G.maps.goobrawl.monsters[0]); - data.type = "rgoo"; - var m = new_monster("goobrawl", data); - //m.s.filter={ms:9999999,name:"scale",scale:2}; - } else { - var data = clone(G.maps.goobrawl.monsters[0]); - data.type = "bgoo"; - var m = new_monster("goobrawl", data); - m.skin = random_one(["goo0", "goo1", "goo2", "goo3", "goo4", "goo5", "goo6"]); - // m.drops=[[0.5,"funtoken"]]; - m.u = true; - m.cid++; - } - } - } + if (events.goobrawl) { + if (!timers.goobrawl) { + timers.goobrawl = future_s(G.events.goobrawl.duration); + E.goobrawl = { end: timers.goobrawl }; + change = true; + //create_instance("goobrawl","goobrawl",{event:true}); + // collect_signups("goobrawl"); + broadcast("notice", { message: "Goo Brawl has begun!" }); + } else if (c > timers.goobrawl) { + events.goobrawl = false; + delete E.goobrawl; + delete timers.goobrawl; + change = true; + //destroy_instance("goobrawl"); + broadcast("notice", { message: "Goo Brawl is over, hope you had fun!" }); + } else if (instances.goobrawl && Object.keys(instances.goobrawl.monsters).length < 6 && Math.random() < 0.3) { + if (Math.random() < 0.01) { + var data = clone(G.maps.goobrawl.monsters[0]); + data.type = "rgoo"; + var m = new_monster("goobrawl", data); + //m.s.filter={ms:9999999,name:"scale",scale:2}; + } else { + var data = clone(G.maps.goobrawl.monsters[0]); + data.type = "bgoo"; + var m = new_monster("goobrawl", data); + m.skin = random_one(["goo0", "goo1", "goo2", "goo3", "goo4", "goo5", "goo6"]); + // m.drops=[[0.5,"funtoken"]]; + m.u = true; + m.cid++; + } + } + } - if (gameplay == "hardcore" && ssince(timers.hardcore) > 64) { - timers.hardcore = new Date(); - for (var id in instances.woffice.players) { - var player = instances.woffice.players[id]; - var gold = 480000; - player.gold += gold; - player.socket.emit("game_log", "Received " + to_pretty_num(gold) + " gold"); - player.socket.emit("disappearing_text", { - message: "+" + gold, - x: player.x, - y: player.y - 32, - args: { color: "+gold", size: "large" }, - }); - resend(player, "reopen"); - } - } + if (gameplay == "hardcore" && ssince(timers.hardcore) > 64) { + timers.hardcore = new Date(); + for (var id in instances.woffice.players) { + var player = instances.woffice.players[id]; + var gold = 480000; + player.gold += gold; + player.socket.emit("game_log", "Received " + to_pretty_num(gold) + " gold"); + player.socket.emit("disappearing_text", { + message: "+" + gold, + x: player.x, + y: player.y - 32, + args: { color: "+gold", size: "large" }, + }); + resend(player, "reopen"); + } + } - if (events.goldenbat && stats.kills.bat > edges.next_goldenbat) { - edges.next_goldenbat += parseInt(events.goldenbat * Math.random()); - spawn_special_monster("goldenbat"); - } + if (events.goldenbat && stats.kills.bat > edges.next_goldenbat) { + edges.next_goldenbat += parseInt(events.goldenbat * Math.random()); + spawn_special_monster("goldenbat"); + } - if (events.cutebee && stats.kills.bee > edges.next_cutebee) { - edges.next_cutebee += parseInt(events.cutebee * Math.random()); - spawn_special_monster("cutebee"); - } + if (events.cutebee && stats.kills.bee > edges.next_cutebee) { + edges.next_cutebee += parseInt(events.cutebee * Math.random()); + spawn_special_monster("cutebee"); + } - if (!events.holidayseason && events.snowman && !monster_c.snowman) { - if (!timers.snowman) { - timers.snowman = future_s(events.snowman * 60); - } else if (timers.snowman && c > timers.snowman) { - timers.snowman = 0; - spawn_special_monster("snowman"); - } - } + if (!events.holidayseason && events.snowman && !monster_c.snowman) { + if (!timers.snowman) { + timers.snowman = future_s(events.snowman * 60); + } else if (timers.snowman && c > timers.snowman) { + timers.snowman = 0; + spawn_special_monster("snowman"); + } + } - var eventmap = [ - ["halloween", "mrpumpkin"], - ["halloween", "mrgreen"], - ["halloween", "slenderman", true], - ["holidayseason", "grinch"], - ["holidayseason", "snowman"], - ["lunarnewyear", "dragold"], - ["lunarnewyear", "tiger", true], - ["valentines", "pinkgoo", true], - ["egghunt", "wabbit", true], - ]; - eventmap.forEach(function (s) { - if (events[s[0]] && !monster_c[s[1]]) { - if (!timers[s[1]]) { - if (timers[s[1]] !== 0) { - timers[s[1]] = future_s(120); - } else { - timers[s[1]] = future_s(G.monsters[s[1]].respawn); - } - E[s[1]] = { live: false, spawn: timers[s[1]] }; - broadcast_e(); - } else if (timers[s[1]] && c > timers[s[1]]) { - timers[s[1]] = 0; - spawn_special_monster(s[1]); - var m = get_monsters(s[1])[0]; - var data = { live: true, map: m.map, hp: m.hp, max_hp: m.max_hp, target: m.target }; - if (!s[2]) { - data.x = m.x; - data.y = m.y; - } - E[s[1]] = data; - broadcast_e(); - } - } - }); - ["holidayseason", "halloween", "lunarnewyear", "valentines", "egghunt"].forEach(function (event) { - if (events[event]) { - E[event] = true; - } else if (E[event]) { - delete E[event]; - } - }); - ["snowman", "grinch", "dragold", "mrpumpkin", "mrgreen", "wabbit", "pinkgoo", "tiger"].forEach(function (type) { - if (E[type] && !monster_c[type]) { - delete E[type]; // [20/01/23] - change = true; - } - }); - for (var name in instances) { - for (var id in instances[name].monsters) { - var monster = instances[name].monsters[id]; - ["tiger"].forEach(function (type) { - if (monster.type == type) { - E[type] = { live: true, map: monster.map, hp: monster.hp, max_hp: monster.max_hp, target: monster.target }; - } - }); - ["snowman", "grinch", "dragold", "mrpumpkin", "mrgreen", "wabbit", "pinkgoo"].forEach(function (type) { - if (monster.type == type) { - if (!E[type]) { - change = true; - } - E[type] = { - live: true, - map: monster.map, - hp: monster.hp, - max_hp: monster.max_hp, - target: monster.target, - x: monster.x, - y: monster.y, - }; - } - }); - if (monster.type == "grinch") { - if (instances[monster.in].paused) { - resume_instance(instances[monster.in]); - } + var eventmap = [ + ["halloween", "mrpumpkin"], + ["halloween", "mrgreen"], + ["halloween", "slenderman", true], + ["holidayseason", "grinch"], + ["holidayseason", "snowman"], + ["lunarnewyear", "dragold"], + ["lunarnewyear", "tiger", true], + ["valentines", "pinkgoo", true], + ["egghunt", "wabbit", true], + ]; + eventmap.forEach(function (s) { + if (events[s[0]] && !monster_c[s[1]]) { + if (!timers[s[1]]) { + if (timers[s[1]] !== 0) { + timers[s[1]] = future_s(120); + } else { + timers[s[1]] = future_s(G.monsters[s[1]].respawn); + } + E[s[1]] = { live: false, spawn: timers[s[1]] }; + broadcast_e(); + } else if (timers[s[1]] && c > timers[s[1]]) { + timers[s[1]] = 0; + spawn_special_monster(s[1]); + var m = get_monsters(s[1])[0]; + var data = { live: true, map: m.map, hp: m.hp, max_hp: m.max_hp, target: m.target }; + if (!s[2]) { + data.x = m.x; + data.y = m.y; + } + E[s[1]] = data; + broadcast_e(); + } + } + }); + ["holidayseason", "halloween", "lunarnewyear", "valentines", "egghunt"].forEach(function (event) { + if (events[event]) { + E[event] = true; + } else if (E[event]) { + delete E[event]; + } + }); + ["snowman", "grinch", "dragold", "mrpumpkin", "mrgreen", "wabbit", "pinkgoo", "tiger"].forEach(function (type) { + if (E[type] && !monster_c[type]) { + delete E[type]; // [20/01/23] + change = true; + } + }); + for (var name in instances) { + for (var id in instances[name].monsters) { + var monster = instances[name].monsters[id]; + ["tiger"].forEach(function (type) { + if (monster.type == type) { + E[type] = { live: true, map: monster.map, hp: monster.hp, max_hp: monster.max_hp, target: monster.target }; + } + }); + ["snowman", "grinch", "dragold", "mrpumpkin", "mrgreen", "wabbit", "pinkgoo"].forEach(function (type) { + if (monster.type == type) { + if (!E[type]) { + change = true; + } + E[type] = { + live: true, + map: monster.map, + hp: monster.hp, + max_hp: monster.max_hp, + target: monster.target, + x: monster.x, + y: monster.y, + }; + } + }); + if (monster.type == "grinch") { + if (instances[monster.in].paused) { + resume_instance(instances[monster.in]); + } - var phrase = null; - var disengage = false; + var phrase = null; + var disengage = false; - if (Math.random() < 0.1) { - if (monster.target && !G.monsters.grinch.good) { - phrase = random_one([ - "Come to papa", - "This is not a chew toy!", - "Give me that! Don't you know you're not supposed to take things that don't belong to you? What's the matter with you? You some kind of wild animal?", - "HELP ME…I'm FEELING.", - "It came without ribbons, it came without tags. It came without packages, boxes, or bags.", - "Poor, poor, " + monster.target, - "Innie, minnie, tiny " + monster.target + "innie", - ]); - } else if (!monster.target) { - phrase = random_one([ - "That is not a chew toy!", - "Stupid. Ugly. Out of date. This is ridiculous. If I can't find something nice to wear I'm not going.", - "Kids today. So desensitized by movies and television.", - "Holiday who-be what-ee?", - "I could use a little social interaction.", - "It's because I'm green isn't it?", - "Social distancing what?", - ]); - } - } + if (Math.random() < 0.1) { + if (monster.target && !G.monsters.grinch.good) { + phrase = random_one([ + "Come to papa", + "This is not a chew toy!", + "Give me that! Don't you know you're not supposed to take things that don't belong to you? What's the matter with you? You some kind of wild animal?", + "HELP ME…I'm FEELING.", + "It came without ribbons, it came without tags. It came without packages, boxes, or bags.", + "Poor, poor, " + monster.target, + "Innie, minnie, tiny " + monster.target + "innie", + ]); + } else if (!monster.target) { + phrase = random_one([ + "That is not a chew toy!", + "Stupid. Ugly. Out of date. This is ridiculous. If I can't find something nice to wear I'm not going.", + "Kids today. So desensitized by movies and television.", + "Holiday who-be what-ee?", + "I could use a little social interaction.", + "It's because I'm green isn't it?", + "Social distancing what?", + ]); + } + } - if ( - monster.target && - get_player(monster.target) && - get_player(monster.target).slots.chest && - get_player(monster.target).slots.chest.name == "xmassweater" && - !G.monsters.grinch.good - ) { - phrase = "Ugh. What's that ugly thing you are wearing?! I can't look at it. Stop."; - disengage = true; - } + if ( + monster.target && + get_player(monster.target) && + get_player(monster.target).slots.chest && + get_player(monster.target).slots.chest.name == "xmassweater" && + !G.monsters.grinch.good + ) { + phrase = "Ugh. What's that ugly thing you are wearing?! I can't look at it. Stop."; + disengage = true; + } - if (phrase) { - xy_emit(monster, "chat_log", { owner: "Grinch", message: phrase, id: monster.id, color: "#418343" }); - } + if (phrase) { + xy_emit(monster, "chat_log", { owner: "Grinch", message: phrase, id: monster.id, color: "#418343" }); + } - if (!monster.target && Math.random() < 0.1 * Object.keys(players).length) { - monster.last_attack = future_ms(1000); - var player = random_one(players); - if ( - (player.level < 50 && Math.random() > 0.08) || - (G.maps[player.map] && - (G.maps[player.map].safe || G.maps[player.map].instance || G.maps[player.map].irregular)) - ) { - player = null; - } - if (player && 0) { - // attack everyone - for (var pid in instances[player.in].players) { - if (!player) { - break; - } - var ally = instances[player.in].players[pid]; - if ( - ally.name != player.name && - !ally.npc && - (ally.type != "merchant" || player.type == "merchant") && - simple_distance(ally, player) < 500 - ) { - player = null; - break; - } - } - } - if (player) { - target_player(monster, player); - } - } - if ((monster.target && !is_disabled(monster) && Math.random() < 0.05) || disengage) { - stop_pursuit(monster, { force: true, cause: "disengage" }); - } - } - } - } + if (!monster.target && Math.random() < 0.1 * Object.keys(players).length) { + monster.last_attack = future_ms(1000); + var player = random_one(players); + if ( + (player.level < 50 && Math.random() > 0.08) || + (G.maps[player.map] && + (G.maps[player.map].safe || G.maps[player.map].instance || G.maps[player.map].irregular)) + ) { + player = null; + } + if (player && 0) { + // attack everyone + for (var pid in instances[player.in].players) { + if (!player) { + break; + } + var ally = instances[player.in].players[pid]; + if ( + ally.name != player.name && + !ally.npc && + (ally.type != "merchant" || player.type == "merchant") && + simple_distance(ally, player) < 500 + ) { + player = null; + break; + } + } + } + if (player) { + target_player(monster, player); + } + } + if ((monster.target && !is_disabled(monster) && Math.random() < 0.05) || disengage) { + stop_pursuit(monster, { force: true, cause: "disengage" }); + } + } + } + } - eventmap.forEach(function (s) { - if (events[s[0]] && !E[s[1]]) { - E[s[1]] = { live: false, spawn: timers[s[1]] }; - change = true; - } - }); + eventmap.forEach(function (s) { + if (events[s[0]] && !E[s[1]]) { + E[s[1]] = { live: false, spawn: timers[s[1]] }; + change = true; + } + }); - if (events.goblin) { - } + if (events.goblin) { + } - if (events.hide_and_seek) { - } + if (events.hide_and_seek) { + } - if (events.abtesting) { - if (timers.abtesting && c > timers.abtesting) { - var winner = "A"; - if (E.abtesting.B > E.abtesting.A) { - winner = "B"; - } - events.abtesting = false; - timers.abtesting = false; - delete E.abtesting; - change = true; - broadcast("server_message", { message: "Team " + winner + " wins! Hope you all had fun!", color: "#4BB6E1" }); - for (var id in instances.abtesting.players) { - var player = instances.abtesting.players[id]; - var table = "abtesting"; - if (!player.team) { - continue; - } - if (player.team != winner) { - table = "abtesting_loser"; - } - if (!player.esize) { - socket.emit("game_log", "Full inventory. Unable to receive a prize."); - continue; - } - exchange(player, table); - } - destroy_instance("abtesting"); - } else if (!timers.abtesting) { - timers.abtesting = future_s(G.events.abtesting.duration); - change = true; - timers.abtesting_start = c; - E.abtesting = { end: timers.abtesting, signup_end: future_s(60), A: 0, B: 0, id: randomStr(5) }; - create_instance("abtesting", "abtesting", { event: true }); - collect_signups("abtesting"); - broadcast("server_message", { message: "A/B Testing has begun!", color: "#4BB6E1" }); - // npcs.bean.s.invis={ms:999999999999}; xy_emit(npcs.bean,"disappear",{id:"Bean"}); - } - } + if (events.abtesting) { + if (timers.abtesting && c > timers.abtesting) { + var winner = "A"; + if (E.abtesting.B > E.abtesting.A) { + winner = "B"; + } + events.abtesting = false; + timers.abtesting = false; + delete E.abtesting; + change = true; + broadcast("server_message", { message: "Team " + winner + " wins! Hope you all had fun!", color: "#4BB6E1" }); + for (var id in instances.abtesting.players) { + var player = instances.abtesting.players[id]; + var table = "abtesting"; + if (!player.team) { + continue; + } + if (player.team != winner) { + table = "abtesting_loser"; + } + if (!player.esize) { + socket.emit("game_log", "Full inventory. Unable to receive a prize."); + continue; + } + exchange(player, table); + } + destroy_instance("abtesting"); + } else if (!timers.abtesting) { + timers.abtesting = future_s(G.events.abtesting.duration); + change = true; + timers.abtesting_start = c; + E.abtesting = { end: timers.abtesting, signup_end: future_s(60), A: 0, B: 0, id: randomStr(5) }; + create_instance("abtesting", "abtesting", { event: true }); + collect_signups("abtesting"); + broadcast("server_message", { message: "A/B Testing has begun!", color: "#4BB6E1" }); + // npcs.bean.s.invis={ms:999999999999}; xy_emit(npcs.bean,"disappear",{id:"Bean"}); + } + } - if (instances.cyberland && ssince(timers.cyberland) > 10) { - var detected = false; - timers.cyberland = new Date(); - for (var id in instances.cyberland.monsters) { - var monster = instances.cyberland.monsters[id]; - if (monster.target) { - detected = get_player(monster.target); - } - } - if (detected) { - xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { - owner: "mainframe", - message: "ALERT", - id: "mainframe", - }); - for (var id in instances.cyberland.monsters) { - var monster = instances.cyberland.monsters[id]; - if (!monster.target) { - target_player(monster, detected); - } - } - } - if (Math.random() < 1.0 / 1600) { - S.misc.spares.push("electronics"); - if (Math.random() < 1.0 / 1020) { - S.misc.spares.push("networkcard"); - } - if (S.misc.spares.length > 2000) { - S.misc.spares.length = 2000; - } - } - } + if (instances.cyberland && ssince(timers.cyberland) > 10) { + var detected = false; + timers.cyberland = new Date(); + for (var id in instances.cyberland.monsters) { + var monster = instances.cyberland.monsters[id]; + if (monster.target) { + detected = get_player(monster.target); + } + } + if (detected) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "ALERT", + id: "mainframe", + }); + for (var id in instances.cyberland.monsters) { + var monster = instances.cyberland.monsters[id]; + if (!monster.target) { + target_player(monster, detected); + } + } + } + if (Math.random() < 1.0 / 1600) { + S.misc.spares.push("electronics"); + if (Math.random() < 1.0 / 1020) { + S.misc.spares.push("networkcard"); + } + if (S.misc.spares.length > 2000) { + S.misc.spares.length = 2000; + } + } + } - for (var id in E.duels || {}) { - var duel = E.duels[id]; - var instance = instances[id]; - var info = instance.info; - var a = 0; - var b = 0; - if (info.seconds) { - info.seconds--; - } - // console.log(info.A); console.log(info.B); - for (var i = 0; i < info.A.length; i++) { - var p = info.A[i]; - var player = get_player(p.name); - //console.log(player.in+" "+player.duel.id+" "+player.rip); - if (!player || player.in != id || !player.duel || player.rip || !player.team) { - p.active = false; - } - if (p.active) { - info.A[i] = player_to_summary(player); - a++; - info.A[i].active = true; - } - } - for (var i = 0; i < info.B.length; i++) { - var p = info.B[i]; - var player = get_player(p.name); - if (!player || player.in != id || !player.duel || player.rip || !player.team) { - p.active = false; - } - if (p.active) { - info.B[i] = player_to_summary(player); - b++; - info.B[i].active = true; - } - } - if (!info.seconds && !info.active) { - duel.a = []; - duel.b = []; - info.active = true; - info.A.forEach(function (p) { - var player = get_player(p.name); - if (player && p.active) { - player.s = {}; - duel.a.push(p.name); - resend(player, "u+cid"); - } - }); - info.B.forEach(function (p) { - var player = get_player(p.name); - if (player && p.active) { - player.s = {}; - duel.b.push(p.name); - resend(player, "u+cid"); - } - }); - } - if (!a || !b) { - var message = duel.challenger + " wins the duel!"; - var chat = duel.challenger + " defeated " + duel.vs + "!"; - var sent = {}; - if (!a) { - message = duel.vs + " wins the duel!"; - chat = duel.vs + " defeated " + duel.challenger + "!"; - } - for (var pid in instance.players) { - sent[instance.players[pid].name] = true; - instance.players[pid].socket.emit("game_chat", { message: message, color: "#47C1AE" }); - } - info.A.forEach(function (p) { - var player = get_player(p.name); - if (player && !sent[p.name]) { - player.socket.emit("game_chat", { message: message, color: "#47C1AE" }); - } - }); - info.B.forEach(function (p) { - var player = get_player(p.name); - if (player && !sent[p.name]) { - player.socket.emit("game_chat", { message: message, color: "#47C1AE" }); - } - }); + for (var id in E.duels || {}) { + var duel = E.duels[id]; + var instance = instances[id]; + var info = instance.info; + var a = 0; + var b = 0; + if (info.seconds) { + info.seconds--; + } + // console.log(info.A); console.log(info.B); + for (var i = 0; i < info.A.length; i++) { + var p = info.A[i]; + var player = get_player(p.name); + //console.log(player.in+" "+player.duel.id+" "+player.rip); + if (!player || player.in != id || !player.duel || player.rip || !player.team) { + p.active = false; + } + if (p.active) { + info.A[i] = player_to_summary(player); + a++; + info.A[i].active = true; + } + } + for (var i = 0; i < info.B.length; i++) { + var p = info.B[i]; + var player = get_player(p.name); + if (!player || player.in != id || !player.duel || player.rip || !player.team) { + p.active = false; + } + if (p.active) { + info.B[i] = player_to_summary(player); + b++; + info.B[i].active = true; + } + } + if (!info.seconds && !info.active) { + duel.a = []; + duel.b = []; + info.active = true; + info.A.forEach(function (p) { + var player = get_player(p.name); + if (player && p.active) { + player.s = {}; + duel.a.push(p.name); + resend(player, "u+cid"); + } + }); + info.B.forEach(function (p) { + var player = get_player(p.name); + if (player && p.active) { + player.s = {}; + duel.b.push(p.name); + resend(player, "u+cid"); + } + }); + } + if (!a || !b) { + var message = duel.challenger + " wins the duel!"; + var chat = duel.challenger + " defeated " + duel.vs + "!"; + var sent = {}; + if (!a) { + message = duel.vs + " wins the duel!"; + chat = duel.vs + " defeated " + duel.challenger + "!"; + } + for (var pid in instance.players) { + sent[instance.players[pid].name] = true; + instance.players[pid].socket.emit("game_chat", { message: message, color: "#47C1AE" }); + } + info.A.forEach(function (p) { + var player = get_player(p.name); + if (player && !sent[p.name]) { + player.socket.emit("game_chat", { message: message, color: "#47C1AE" }); + } + }); + info.B.forEach(function (p) { + var player = get_player(p.name); + if (player && !sent[p.name]) { + player.socket.emit("game_chat", { message: message, color: "#47C1AE" }); + } + }); - destroy_instance(id); + destroy_instance(id); - if (!a) { - info.B.forEach(function (p) { - var player = get_player(p.name); - if (player) { - xy_emit(player, "eval", "confetti_shower(get_player('" + player.name + "'),2)"); - } - }); - } else { - info.A.forEach(function (p) { - var player = get_player(p.name); - if (player) { - xy_emit(player, "eval", "confetti_shower(get_player('" + player.name + "'),2)"); - } - }); - } + if (!a) { + info.B.forEach(function (p) { + var player = get_player(p.name); + if (player) { + xy_emit(player, "eval", "confetti_shower(get_player('" + player.name + "'),2)"); + } + }); + } else { + info.A.forEach(function (p) { + var player = get_player(p.name); + if (player) { + xy_emit(player, "eval", "confetti_shower(get_player('" + player.name + "'),2)"); + } + }); + } - if (!a && get_player(duel.vs)) { - xy_emit(get_player(duel.vs), "game_chat", { message: chat, color: "#47C1AE" }); - } - if (!b && get_player(duel.challenger)) { - xy_emit(get_player(duel.challenger), "game_chat", { message: chat, color: "#47C1AE" }); - } + if (!a && get_player(duel.vs)) { + xy_emit(get_player(duel.vs), "game_chat", { message: chat, color: "#47C1AE" }); + } + if (!b && get_player(duel.challenger)) { + xy_emit(get_player(duel.challenger), "game_chat", { message: chat, color: "#47C1AE" }); + } - delete E.duels[id]; - broadcast_e(); - continue; - } - duel.active = instance.active; - duel.seconds = instance.seconds; - instance_emit(id, "map_info", instance.info); - } + delete E.duels[id]; + broadcast_e(); + continue; + } + duel.active = instance.active; + duel.seconds = instance.seconds; + instance_emit(id, "map_info", instance.info); + } - for (var e in E) { - if ((E[e] && E[e].target) || Object.keys(E.duels || {}).length) { - change = true; - break; - } - } - if (change) { - broadcast_e(); - } - } catch (e) { - log_trace("Critical-event_loop: ", e); - } + for (var e in E) { + if ((E[e] && E[e].target) || Object.keys(E.duels || {}).length) { + change = true; + break; + } + } + if (change) { + broadcast_e(); + } + } catch (e) { + log_trace("Critical-event_loop: ", e); + } } function broadcast_e(dont_send) { - if (!dont_send) { - broadcast("server_info", E); - } + if (!dont_send) { + broadcast("server_info", E); + } } function start_event(name) { - if (name == "goblin") { - events.goblin = true; - } else if (name == "goobrawl") { - signups = {}; - events.goobrawl = 1; - timers.goobrawl = future_s(45); - broadcast("notice", { message: "Goo Brawl is about to start!" }); - } else if (name == "abtesting") { - signups = {}; - events.abtesting = 1; - timers.abtesting = future_s(45); - // instances.main.players[NPC_prefix+"Bean"]=npcs.bean; - // npcs.bean.party="abtesting"; npcs.bean.last.move=new Date(); - broadcast("server_message", { message: "A/B Testing is about to start!", color: "#4BB6E1" }); - } + if (name == "goblin") { + events.goblin = true; + } else if (name == "goobrawl") { + signups = {}; + events.goobrawl = 1; + timers.goobrawl = future_s(45); + broadcast("notice", { message: "Goo Brawl is about to start!" }); + } else if (name == "abtesting") { + signups = {}; + events.abtesting = 1; + timers.abtesting = future_s(45); + // instances.main.players[NPC_prefix+"Bean"]=npcs.bean; + // npcs.bean.party="abtesting"; npcs.bean.last.move=new Date(); + broadcast("server_message", { message: "A/B Testing is about to start!", color: "#4BB6E1" }); + } } function new_worker(num) { - var worker = new Worker(variables.worker_path, { - workerData: { G: G, amap_data: amap_data, smap_data: smap_data }, - env: SHARE_ENV, - execArgv: [], - }); - worker.wnum = num; - worker.on("message", function (data) { - if (data.type == "monster_move") { - var monster = instances[data.in].monsters[data.id]; - if (!monster) { - return; - } - monster.working = false; - if (data.move && (data.move[2] != "bad" || (monster.bmoves || 0) < 6)) { - if (data.move[2] == "bad") { - monster.bmoves = (monster.bmoves || 0) + 1; - } else { - monster.bmoves = 0; - } - monster.going_x = data.move[0]; - monster.going_y = data.move[1]; - start_moving_element(monster); - } else { - var player = monster.target && get_player(monster.target); - if (player) { - if (monster.attack < 120 || distance(monster, player, true) > monster.range) { - stop_pursuit(monster, { cause: "cant_move_smart" }); - } - } - } - } - }); - worker.on("error", function (data) { - console.log("#W Worker error: " + JSON.stringify(data)); - }); - worker.on("exit", function (code) { - workers[this.wnum] = new_worker(this.wnum); - }); - return worker; + var worker = new Worker(variables.worker_path, { + workerData: { G: G, amap_data: amap_data, smap_data: smap_data }, + env: SHARE_ENV, + execArgv: [], + }); + worker.wnum = num; + worker.on("message", function (data) { + if (data.type == "monster_move") { + var monster = instances[data.in].monsters[data.id]; + if (!monster) { + return; + } + monster.working = false; + if (data.move && (data.move[2] != "bad" || (monster.bmoves || 0) < 6)) { + if (data.move[2] == "bad") { + monster.bmoves = (monster.bmoves || 0) + 1; + } else { + monster.bmoves = 0; + } + monster.going_x = data.move[0]; + monster.going_y = data.move[1]; + start_moving_element(monster); + } else { + var player = monster.target && get_player(monster.target); + if (player) { + if (monster.attack < 120 || distance(monster, player, true) > monster.range) { + stop_pursuit(monster, { cause: "cant_move_smart" }); + } + } + } + } + }); + worker.on("error", function (data) { + console.log("#W Worker error: " + JSON.stringify(data)); + }); + worker.on("exit", function (code) { + workers[this.wnum] = new_worker(this.wnum); + }); + return worker; } function init_server() { - if (gameplay == "hardcore") { - E = { - rewards: { - item8: null, - item9: null, - item10: null, - item11: null, - item12: null, - leader: null, - first_fvampire: null, - first_mvampire: null, - first_skeletor: null, - first_stompy: null, - first_franky: null, - first_ent: null, - first_wabbit: null, - accessory5: null, - accessory6: null, - first_warrior_70: null, - first_paladin_70: null, - first_priest_70: null, - first_mage_70: null, - first_ranger_70: null, - first_rogue_70: null, - "!participation": "Every authorized account with a level 60+ character receives a participation award!", - }, - }; - E.minutes = 340; - if (is_sdk) { - E.minutes = 12; - } - setInterval(hardcore_loop, 60 * 1000); - } - setInterval(event_loop, 1000); - setInterval(tavern_loop, 1000); - workers = [ - new_worker(0), - new_worker(1), - new_worker(2), - new_worker(3), - new_worker(4), - new_worker(5), - new_worker(6), - new_worker(7), - ]; + if (gameplay == "hardcore") { + E = { + rewards: { + item8: null, + item9: null, + item10: null, + item11: null, + item12: null, + leader: null, + first_fvampire: null, + first_mvampire: null, + first_skeletor: null, + first_stompy: null, + first_franky: null, + first_ent: null, + first_wabbit: null, + accessory5: null, + accessory6: null, + first_warrior_70: null, + first_paladin_70: null, + first_priest_70: null, + first_mage_70: null, + first_ranger_70: null, + first_rogue_70: null, + "!participation": "Every authorized account with a level 60+ character receives a participation award!", + }, + }; + E.minutes = 340; + if (is_sdk) { + E.minutes = 12; + } + setInterval(hardcore_loop, 60 * 1000); + } + setInterval(event_loop, 1000); + setInterval(tavern_loop, 1000); + workers = [ + new_worker(0), + new_worker(1), + new_worker(2), + new_worker(3), + new_worker(4), + new_worker(5), + new_worker(6), + new_worker(7), + ]; } function init_server_data() { - if (!S.dt) { - S.dt = {}; - } - if (!S.sold) { - S.sold = []; - } - if (!S.found) { - S.found = []; - } - if (!S.gold) { - S.gold = 0; - } - if (!S.cash) { - S.cash = 0; - } - if (!S.logs) { - S.logs = { donate: [], dice: [] }; - } - if (!S.misc) { - S.misc = { spares: ["electronics"] }; - } - ["sold", "found"].forEach(function (store) { - for (var i = S[store].length - 1; i >= 0; i--) { - if ( - !S[store][i].name || - !G.items[S[store][i].name] || - in_arr(S[store][i].name, ["cxjar", "emotionjar"]) || - G.items[S[store][i].name].cash - ) { - S[store].splice(i, 1); - } - } - }); - for (var i = 0; i < S.sold.length; i++) { - csold[i] = cache_item(S.sold[i], true); - } - for (var i = 0; i < S.found.length; i++) { - cfound[i] = cache_item(S.found[i], true); - } - if (!S.ugrace) { - S.ugrace = []; - S.cgrace = []; - for (var i = 0; i < 25; i++) { - S.ugrace[i] = 24; - S.cgrace[i] = 24; - } - } + if (!S.dt) { + S.dt = {}; + } + if (!S.sold) { + S.sold = []; + } + if (!S.found) { + S.found = []; + } + if (!S.gold) { + S.gold = 0; + } + if (!S.cash) { + S.cash = 0; + } + if (!S.logs) { + S.logs = { donate: [], dice: [] }; + } + if (!S.misc) { + S.misc = { spares: ["electronics"] }; + } + ["sold", "found"].forEach(function (store) { + for (var i = S[store].length - 1; i >= 0; i--) { + if ( + !S[store][i].name || + !G.items[S[store][i].name] || + in_arr(S[store][i].name, ["cxjar", "emotionjar"]) || + G.items[S[store][i].name].cash + ) { + S[store].splice(i, 1); + } + } + }); + for (var i = 0; i < S.sold.length; i++) { + csold[i] = cache_item(S.sold[i], true); + } + for (var i = 0; i < S.found.length; i++) { + cfound[i] = cache_item(S.found[i], true); + } + if (!S.ugrace) { + S.ugrace = []; + S.cgrace = []; + for (var i = 0; i < 25; i++) { + S.ugrace[i] = 24; + S.cgrace[i] = 24; + } + } } function get_monsters(type) { - var l = []; - for (var name in instances) { - for (var id in instances[name].monsters) { - var monster = instances[name].monsters[id]; - if (monster.type == type) { - l.push(monster); - } - } - } - return l; + var l = []; + for (var name in instances) { + for (var id in instances[name].monsters) { + var monster = instances[name].monsters[id]; + if (monster.type == type) { + l.push(monster); + } + } + } + return l; } function get_monster(type) { - return get_monsters(type)[0]; + return get_monsters(type)[0]; } function add_item_property(item, prop) { - if (prop == "legacy") { - return; - } - if (!item.ps) { - item.ps = []; - } - if (item.p && !in_arr(item.p, item.ps)) { - item.ps.push(item.p); - } // backwards compatibility - if (!in_arr(prop, item.ps)) { - item.ps.push(prop); - } - item.p = prop; + if (prop == "legacy") { + return; + } + if (!item.ps) { + item.ps = []; + } + if (item.p && !in_arr(item.p, item.ps)) { + item.ps.push(item.p); + } // backwards compatibility + if (!in_arr(prop, item.ps)) { + item.ps.push(prop); + } + item.p = prop; } function add_condition(target, condition, args) { - var def = G.conditions[condition]; - if (!def) { - return; - } - if (!args) { - args = {}; - } - var response = { response: "condition", name: condition, cevent: true }; - var duration = args.duration || args.ms || def.duration; - if (duration == undefined) { - duration = 1000; - } - var C = { ms: duration }; - if (condition == "poisoned" && target.pnresistance) { - if (Math.random() < (target.pnresistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "poison_resist", id: target.id }); - } + var def = G.conditions[condition]; + if (!def) { + return; + } + if (!args) { + args = {}; + } + var response = { response: "condition", name: condition, cevent: true }; + var duration = args.duration || args.ms || def.duration; + if (duration == undefined) { + duration = 1000; + } + var C = { ms: duration }; + if (condition == "poisoned" && target.pnresistance) { + if (Math.random() < (target.pnresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "poison_resist", id: target.id }); + } - duration *= (100 - target.pnresistance) / 100.0; - } - if (condition == "stunned") { - if (Math.random() < (target.phresistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "stun_resist", id: target.id }); - } + duration *= (100 - target.pnresistance) / 100.0; + } + if (condition == "stunned") { + if (Math.random() < (target.phresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "stun_resist", id: target.id }); + } - target.abs = true; - target.moving = false; - } - if (condition == "burned") { - if (Math.random() < (target.firesistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "fire_resist", id: target.id }); - } + target.abs = true; + target.moving = false; + } + if (condition == "burned") { + if (Math.random() < (target.firesistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "fire_resist", id: target.id }); + } - duration = 5000; - if (!args.attack) { - args.attack = 1000; - } - if (args.divider == 3 && target.s.burned && target.s.burned.ms) { - duration = min( - 12000, - max(duration + 400, min(8000, parseInt(duration / 4 + (50 * args.attack) / (target.s.burned.intensity + 200)))), - ); - } - C.intensity = max( - (target.s.burned && target.s.burned.intensity) || 1, - parseInt(((target.s.burned && target.s.burned.intensity) || 0) / (args.divider || 3) + args.attack), - ); - C.fid = args.fid; - disappearing_text({}, target, "BURN!", { xy: 1, size: "huge", color: "burn", nv: 1 }); //target.is_player&&"huge"||undefined - } - if (condition == "deepfreezed") { - if (Math.random() < (target.fzresistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); - } - } - if (condition == "woven") { - C.s = min((target.is_monster && 20) || 5, (target.s.woven && target.s.woven.s + 1) || 1); - C.speed = -3 * condition.s; - } - duration = max((target.s[condition] && target.s[condition].ms) || 0, duration); - if (target.stresistance && def && def.debuff) { - duration *= (100 - target.stresistance) / 100.0; - } - target.s[condition] = C; - C.ms = response.duration = duration; - server_log(C); - if (args.from) { - C.f = args.from.name; - } - if (args.f) { - C.f = args.f; - } - if (C.f) { - response.from = C.f; - } - target.cid++; - target.u = true; - if (target.socket) { - target.hitchhikers.push(["game_response", response]); - } - return true; + duration = 5000; + if (!args.attack) { + args.attack = 1000; + } + if (args.divider == 3 && target.s.burned && target.s.burned.ms) { + duration = min( + 12000, + max(duration + 400, min(8000, parseInt(duration / 4 + (50 * args.attack) / (target.s.burned.intensity + 200)))), + ); + } + C.intensity = max( + (target.s.burned && target.s.burned.intensity) || 1, + parseInt(((target.s.burned && target.s.burned.intensity) || 0) / (args.divider || 3) + args.attack), + ); + C.fid = args.fid; + disappearing_text({}, target, "BURN!", { xy: 1, size: "huge", color: "burn", nv: 1 }); //target.is_player&&"huge"||undefined + } + if (condition == "deepfreezed") { + if (Math.random() < (target.fzresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); + } + } + if (condition == "woven") { + C.s = min((target.is_monster && 20) || 5, (target.s.woven && target.s.woven.s + 1) || 1); + C.speed = -3 * condition.s; + } + duration = max((target.s[condition] && target.s[condition].ms) || 0, duration); + if (target.stresistance && def && def.debuff) { + duration *= (100 - target.stresistance) / 100.0; + } + target.s[condition] = C; + C.ms = response.duration = duration; + server_log(C); + if (args.from) { + C.f = args.from.name; + } + if (args.f) { + C.f = args.f; + } + if (C.f) { + response.from = C.f; + } + target.cid++; + target.u = true; + if (target.socket) { + target.hitchhikers.push(["game_response", response]); + } + return true; } function consume_mp(player, mp, target) { - var mult = 1; - if (target && target.humanoid) { - mult = 5; - } - if (player.a.restore_mp && Math.random() < (player.a.restore_mp.attr0 * mult) / 100.0) { - player.mp += mp * 2; - xy_emit(player, "ui", { id: player.id, type: "restore_mp", amount: mp * 2 }); - } else { - player.mp -= (mp * (100 - (player.mp_reduction || 0))) / 100.0; - } - player.mp = parseInt(max(0, player.mp)); - player.mp = min(player.mp, player.max_mp); + var mult = 1; + if (target && target.humanoid) { + mult = 5; + } + if (player.a.restore_mp && Math.random() < (player.a.restore_mp.attr0 * mult) / 100.0) { + player.mp += mp * 2; + xy_emit(player, "ui", { id: player.id, type: "restore_mp", amount: mp * 2 }); + } else { + player.mp -= (mp * (100 - (player.mp_reduction || 0))) / 100.0; + } + player.mp = parseInt(max(0, player.mp)); + player.mp = min(player.mp, player.max_mp); } function game_response(response, data) { - if (!data) { - data = {}; - } - data.response = response; - current_socket.emit("game_response", data); + if (!data) { + data = {}; + } + data.response = response; + current_socket.emit("game_response", data); } function fail_response(response, place, data) { - if (data && is_string(data)) { - data = { reason: data }; - } - if (!response) { - response = "data"; - } - if (is_object(response)) { - data = response; - response = "data"; - place = ls_method; - } - if (is_object(place)) { - data = place; - place = ls_method; - } - if (!data) { - data = {}; - } - if (!place) { - place = ls_method; - } - data.response = response; - data.place = place; - data.failed = true; - current_socket.emit("game_response", data); + if (data && is_string(data)) { + data = { reason: data }; + } + if (!response) { + response = "data"; + } + if (is_object(response)) { + data = response; + response = "data"; + place = ls_method; + } + if (is_object(place)) { + data = place; + place = ls_method; + } + if (!data) { + data = {}; + } + if (!place) { + place = ls_method; + } + data.response = response; + data.place = place; + data.failed = true; + current_socket.emit("game_response", data); } function success_response(response, place, data) { - if (!response) { - response = "data"; - } - if (is_object(response)) { - data = response; - response = "data"; - place = ls_method; - } - if (place && is_object(place)) { - data = place; - place = ls_method; - } - if (!data) { - data = {}; - } - if (!place) { - place = ls_method; - } - if (data.success !== false) { - data.success = true; - } - data.response = response; - data.place = place; - current_socket.emit("game_response", data); + if (!response) { + response = "data"; + } + if (is_object(response)) { + data = response; + response = "data"; + place = ls_method; + } + if (place && is_object(place)) { + data = place; + place = ls_method; + } + if (!data) { + data = {}; + } + if (!place) { + place = ls_method; + } + if (data.success !== false) { + data.success = true; + } + data.response = response; + data.place = place; + current_socket.emit("game_response", data); } function consume_skill(player, name, reuse) { - var penalty_cd = (player.s.penalty_cd && player.s.penalty_cd.ms) || 0; - var multiplier = 1; - //if(name=="attack" || G.skills[name].share=="attack") - if (G.skills[name].share) { - multiplier = G.skills[name].cooldown_multiplier || 1; - name = G.skills[name].share; - } - var cooldown = G.skills[name].cooldown; - if (reuse) { - cooldown = G.skills[name].reuse_cooldown; - } - if (!cooldown) { - return; - } - player.last[name] = future_ms(min(penalty_cd, 10000) + cooldown * (multiplier - 1)); - player.socket.emit("skill_timeout", { - name: name, - ms: min(penalty_cd, 10000) + cooldown * multiplier, - penalty: min(penalty_cd, 10000), - }); - // player.socket.emit("eval",{code:"skill_timeout('"+name+"',"+(min(penalty_cd,10000)+cooldown*multiplier)+")"}); + var penalty_cd = (player.s.penalty_cd && player.s.penalty_cd.ms) || 0; + var multiplier = 1; + //if(name=="attack" || G.skills[name].share=="attack") + if (G.skills[name].share) { + multiplier = G.skills[name].cooldown_multiplier || 1; + name = G.skills[name].share; + } + var cooldown = G.skills[name].cooldown; + if (reuse) { + cooldown = G.skills[name].reuse_cooldown; + } + if (!cooldown) { + return; + } + player.last[name] = future_ms(min(penalty_cd, 10000) + cooldown * (multiplier - 1)); + player.socket.emit("skill_timeout", { + name: name, + ms: min(penalty_cd, 10000) + cooldown * multiplier, + penalty: min(penalty_cd, 10000), + }); + // player.socket.emit("eval",{code:"skill_timeout('"+name+"',"+(min(penalty_cd,10000)+cooldown*multiplier)+")"}); } function get_entity(name) { - if (players[name_to_id[name]]) { - return players[name_to_id[name]]; - } - var l = []; - for (var iname in instances) { - if (instances[iname].monsters[name]) { - return instances[iname].monsters[name]; - } - } - return null; + if (players[name_to_id[name]]) { + return players[name_to_id[name]]; + } + var l = []; + for (var iname in instances) { + if (instances[iname].monsters[name]) { + return instances[iname].monsters[name]; + } + } + return null; } function get_player(name) { - return players[name_to_id[name]]; + return players[name_to_id[name]]; } function realm_broadcast(event, data) { - data.sname = region + " " + server_name; - appengine_call("broadcast", { event: event, data: JSON.stringify(data) }); - // broadcast(event,data); + data.sname = region + " " + server_name; + appengine_call("broadcast", { event: event, data: JSON.stringify(data) }); + // broadcast(event,data); } function broadcast(event, data) { - io.emit(event, data); - if (event == "notice" || (event == "server_message" && gameplay == "normal")) { - var to_log = false; - if (data.sname && data.sname != region + " " + server_name) { - data.message += " [" + data.sname + "]"; - } - if (event == "notice") { - data.color = "orange"; - } - if ( - event == "server_message" && - !data.event && - !data.nod && - (!data.sname || data.sname == region + " " + server_name) - ) { - var message = data.message; - if (data.sname) { - message += " [" + region + " " + server_name + "]"; - } - if (data.discord == "orange") { - message = "```css\n[" + message + "```"; - } // no ] - if (data.discord == "red") { - message = "```diff\n-" + message + "```"; - } - discord_call(message); - to_log = true; - } - if (to_log) { - appengine_call( - "server_event", - { - event: event, - keyword: variables.keyword, - id: server_id, - message: data.message, - color: data.color || "", - }, - function (result) { - server_log("Server notice: " + data.message); - }, - ); - } - } + io.emit(event, data); + if (event == "notice" || (event == "server_message" && gameplay == "normal")) { + var to_log = false; + if (data.sname && data.sname != region + " " + server_name) { + data.message += " [" + data.sname + "]"; + } + if (event == "notice") { + data.color = "orange"; + } + if ( + event == "server_message" && + !data.event && + !data.nod && + (!data.sname || data.sname == region + " " + server_name) + ) { + var message = data.message; + if (data.sname) { + message += " [" + region + " " + server_name + "]"; + } + if (data.discord == "orange") { + message = "```css\n[" + message + "```"; + } // no ] + if (data.discord == "red") { + message = "```diff\n-" + message + "```"; + } + discord_call(message); + to_log = true; + } + if (to_log) { + appengine_call( + "server_event", + { + event: event, + keyword: variables.keyword, + id: server_id, + message: data.message, + color: data.color || "", + }, + function (result) { + server_log("Server notice: " + data.message); + }, + ); + } + } } function instance_emit(name, event, data) { - var instance = instances[name] || name; - if (gameplay == "hardcore" && event != "tavern" && event != "dice") { - return broadcast(event, data); - } - for (var id in instance.players) { - var player = instance.players[id]; - if (!player.npc) { - player.socket.emit(event, data); - } - } + var instance = instances[name] || name; + if (gameplay == "hardcore" && event != "tavern" && event != "dice") { + return broadcast(event, data); + } + for (var id in instance.players) { + var player = instance.players[id]; + if (!player.npc) { + player.socket.emit(event, data); + } + } } function party_emit(name, event, data, args) { - if (!parties[name]) { - return; - } - // console.log(parties[name]); - var owners = []; - var owner = get_player(data.owner); - parties[name].forEach(function (player_name) { - var player = players[name_to_id[player_name]]; - if (args && args.instance && player.in != args.instance) { - return; - } - if (0 && in_arr(event, ["disappearing_text"])) { - player.socket.emit(event, data); - } //volatile. - else { - player.socket.emit(event, data); - } - if (event == "partym") { - owners[player.owner] = owners[player.owner] || []; - owners[player.owner].push(player.name); - } - }); - if (event == "partym" && owner) { - appengine_call("log_chat", { - to: Object.entries(owners), - type: "party", - message: data.message, - fro: owner.name, - author: owner.owner, - }); - } + if (!parties[name]) { + return; + } + // console.log(parties[name]); + var owners = []; + var owner = get_player(data.owner); + parties[name].forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (args && args.instance && player.in != args.instance) { + return; + } + if (0 && in_arr(event, ["disappearing_text"])) { + player.socket.emit(event, data); + } //volatile. + else { + player.socket.emit(event, data); + } + if (event == "partym") { + owners[player.owner] = owners[player.owner] || []; + owners[player.owner].push(player.name); + } + }); + if (event == "partym" && owner) { + appengine_call("log_chat", { + to: Object.entries(owners), + type: "party", + message: data.message, + fro: owner.name, + author: owner.owner, + }); + } } function leave_party(name, leaver) { - // [10/07/18]: For a long time chased oddities around party routines that didn't make sense, yesterday I realised that .emit causes a "disconnect" to be handled right inside this function, rather than afterwards, causing the oddities and irregularities - so - after a .emit, there's no guarantee that the player will still be there - var newparty = []; - var oldparty = parties[name]; - if (!oldparty) { - leaver.party = null; - console.log("#X NO PARTY " + name); - return; - } // Don't know the cause, maybe a disconnect triggering on the accept routines? [12/07/18] - parties[name].forEach(function (player_name) { - var player = players[name_to_id[player_name]]; - if (!player) { - console.log("#X SHOULD'VE FOUND " + player_name); - return; - } - player.party = null; - if (leaver.name != player.name) { - newparty.push(player_name); - } - }); - delete parties[name]; - if (newparty.length >= 2) { - parties[newparty[0]] = newparty; - } - if (newparty.length < 2) { - if (newparty.length) { - var player = players[name_to_id[newparty[0]]]; - if (!player) { - console.log("#X SHOULD'VE FOUND2 " + newparty[0]); - } else { - player.party = null; - } - } - } else { - newparty.forEach(function (player_name) { - var player = players[name_to_id[player_name]]; - if (!player) { - console.log("#X SHOULD'VE FOUND3 " + newparty[0]); - return; - } - player.party = newparty[0]; - }); - } - // Moved the socket routine to the end, after all party changes are made [10/07/18] - oldparty.forEach(function (player_name) { - var player = players[name_to_id[player_name]]; - if (!player || player.name == leaver.name) { - return; - } - newparty = (newparty && parties[newparty[0]]) || []; // During these .socket.emit's, "disconnect"'s happen, the parties can become empty, and party_to_client fails [21/08/18] - player.socket.emit("party_update", { - message: leaver.name + " left the party", - leave: 1, - list: newparty.length >= 2 && newparty, - party: (newparty.length >= 2 && party_to_client(newparty[0])) || {}, - }); - resend(player, "nc+u+cid"); - }); + // [10/07/18]: For a long time chased oddities around party routines that didn't make sense, yesterday I realised that .emit causes a "disconnect" to be handled right inside this function, rather than afterwards, causing the oddities and irregularities - so - after a .emit, there's no guarantee that the player will still be there + var newparty = []; + var oldparty = parties[name]; + if (!oldparty) { + leaver.party = null; + console.log("#X NO PARTY " + name); + return; + } // Don't know the cause, maybe a disconnect triggering on the accept routines? [12/07/18] + parties[name].forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (!player) { + console.log("#X SHOULD'VE FOUND " + player_name); + return; + } + player.party = null; + if (leaver.name != player.name) { + newparty.push(player_name); + } + }); + delete parties[name]; + if (newparty.length >= 2) { + parties[newparty[0]] = newparty; + } + if (newparty.length < 2) { + if (newparty.length) { + var player = players[name_to_id[newparty[0]]]; + if (!player) { + console.log("#X SHOULD'VE FOUND2 " + newparty[0]); + } else { + player.party = null; + } + } + } else { + newparty.forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (!player) { + console.log("#X SHOULD'VE FOUND3 " + newparty[0]); + return; + } + player.party = newparty[0]; + }); + } + // Moved the socket routine to the end, after all party changes are made [10/07/18] + oldparty.forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (!player || player.name == leaver.name) { + return; + } + newparty = (newparty && parties[newparty[0]]) || []; // During these .socket.emit's, "disconnect"'s happen, the parties can become empty, and party_to_client fails [21/08/18] + player.socket.emit("party_update", { + message: leaver.name + " left the party", + leave: 1, + list: newparty.length >= 2 && newparty, + party: (newparty.length >= 2 && party_to_client(newparty[0])) || {}, + }); + resend(player, "nc+u+cid"); + }); } function delete_observer(socket) { - var observer = observers[socket.id]; - delete observers[socket.id]; - delete instances[observer.in].observers[observer.id]; + var observer = observers[socket.id]; + delete observers[socket.id]; + delete instances[observer.in].observers[observer.id]; } function send_all_xy(observer, args) { - var data = { players: [], monsters: [], type: "all", in: observer.in, map: observer.map }; - var instance = instances[observer.in]; - for (var id in instance.players) { - if (!instance.players[id].s.invis && within_xy_range(observer, instance.players[id])) { - data.players.push(player_to_client(instance.players[id], 1)); - } - } - for (var id in instance.monsters) { - if (within_xy_range(observer, instance.monsters[id])) { - data.monsters.push(monster_to_client(instance.monsters[id])); - } - } - observer.last_ux = observer.x; - observer.last_uy = observer.y; - if (observer.moving && mode.xyinf) { - data.xy = { x: observer.x, y: observer.y }; - } - if (args && args.raw) { - return data; - } - observer.socket.emit("entities", data); + var data = { players: [], monsters: [], type: "all", in: observer.in, map: observer.map }; + var instance = instances[observer.in]; + for (var id in instance.players) { + if (!instance.players[id].s.invis && within_xy_range(observer, instance.players[id])) { + data.players.push(player_to_client(instance.players[id], 1)); + } + } + for (var id in instance.monsters) { + if (within_xy_range(observer, instance.monsters[id])) { + data.monsters.push(monster_to_client(instance.monsters[id])); + } + } + observer.last_ux = observer.x; + observer.last_uy = observer.y; + if (observer.moving && mode.xyinf) { + data.xy = { x: observer.x, y: observer.y }; + } + if (args && args.raw) { + return data; + } + observer.socket.emit("entities", data); } function xy_emit(entity, event, data, must) { - var x = entity.x; - var y = entity.y; - var owners = {}; - for (var id in instances[entity.in].players) { - var player = instances[entity.in].players[id]; - if (player.npc) { - continue; - } - if ( - (player.x - player.vision[0] < x && - x < player.x + player.vision[0] && - player.y - player.vision[1] < y && - y < player.y + player.vision[1]) || - player.id == must - ) { - if (event == "light" && (player.type == "rogue" || player.s.invis) && distance(player, entity) < 300) { - player.last.invis = new Date(); - delete player.s.invis; - player.socket.emit("light", { name: data.name, affected: 1 }); - resend(player, "u"); - } else if ((event == "upgrade" || event == "ui") && entity.name != player.name) { - player.socket.emit(event, data); - } //volatile. [02/02/18] - else { - player.socket.emit(event, data); - if (event == "chat_log" && data.p) { - owners[player.owner] = owners[player.owner] || []; - owners[player.owner].push(player.name); - } - } - // else if(!data.nv && in_arr(event,["disappearing_text","upgrade"])) player.socket.emit(event,data); //volatile. - } - } - for (var id in instances[entity.in].observers) { - var player = instances[entity.in].observers[id]; - if ( - player.x - player.vision[0] < x && - x < player.x + player.vision[0] && - player.y - player.vision[1] < y && - y < player.y + player.vision[1] - ) { - if (event == "upgrade") { - player.socket.emit(event, data); - } //volatile. [02/02/18] - else if (!data.nv && in_arr(event, ["disappearing_text", "upgrade"])) { - player.socket.emit(event, data); - } //volatile. - else { - player.socket.emit(event, data); - } - } - } - if (event == "chat_log" && data.p) { - appengine_call("log_chat", { - to: Object.entries(owners), - type: "ambient", - message: data.message, - fro: entity.name, - author: entity.owner, - }); - } + var x = entity.x; + var y = entity.y; + var owners = {}; + for (var id in instances[entity.in].players) { + var player = instances[entity.in].players[id]; + if (player.npc) { + continue; + } + if ( + (player.x - player.vision[0] < x && + x < player.x + player.vision[0] && + player.y - player.vision[1] < y && + y < player.y + player.vision[1]) || + player.id == must + ) { + if (event == "light" && (player.type == "rogue" || player.s.invis) && distance(player, entity) < 300) { + player.last.invis = new Date(); + delete player.s.invis; + player.socket.emit("light", { name: data.name, affected: 1 }); + resend(player, "u"); + } else if ((event == "upgrade" || event == "ui") && entity.name != player.name) { + player.socket.emit(event, data); + } //volatile. [02/02/18] + else { + player.socket.emit(event, data); + if (event == "chat_log" && data.p) { + owners[player.owner] = owners[player.owner] || []; + owners[player.owner].push(player.name); + } + } + // else if(!data.nv && in_arr(event,["disappearing_text","upgrade"])) player.socket.emit(event,data); //volatile. + } + } + for (var id in instances[entity.in].observers) { + var player = instances[entity.in].observers[id]; + if ( + player.x - player.vision[0] < x && + x < player.x + player.vision[0] && + player.y - player.vision[1] < y && + y < player.y + player.vision[1] + ) { + if (event == "upgrade") { + player.socket.emit(event, data); + } //volatile. [02/02/18] + else if (!data.nv && in_arr(event, ["disappearing_text", "upgrade"])) { + player.socket.emit(event, data); + } //volatile. + else { + player.socket.emit(event, data); + } + } + } + if (event == "chat_log" && data.p) { + appengine_call("log_chat", { + to: Object.entries(owners), + type: "ambient", + message: data.message, + fro: entity.name, + author: entity.owner, + }); + } } function xy_u_logic(element) { - // sets .u so updates are sent - this element might enter someone's vision - var u = false; + // sets .u so updates are sent - this element might enter someone's vision + var u = false; - if (!element.last_u) { - u = true; - } else if (abs(element.last_u[0] - element.x) > B.u_boundary) { - u = true; - } else if (abs(element.last_u[1] - element.y) > B.u_boundary) { - u = true; - } + if (!element.last_u) { + u = true; + } else if (abs(element.last_u[0] - element.x) > B.u_boundary) { + u = true; + } else if (abs(element.last_u[1] - element.y) > B.u_boundary) { + u = true; + } - if (u) { - element.last_u = [element.x, element.y]; - element.u = true; - } + if (u) { + element.last_u = [element.x, element.y]; + element.u = true; + } } function xy_upush_logic(element) { - // sets .push so new entities in that area are pushed to the user/element - var u = false; + // sets .push so new entities in that area are pushed to the user/element + var u = false; - if (!element.last_upush) { - element.last_upush = [element.x, element.y]; - return; - } else if (abs(element.last_upush[0] - element.x) > B.u_vision) { - u = true; - } else if (abs(element.last_upush[1] - element.y) > B.u_vision) { - u = true; - } + if (!element.last_upush) { + element.last_upush = [element.x, element.y]; + return; + } else if (abs(element.last_upush[0] - element.x) > B.u_vision) { + u = true; + } else if (abs(element.last_upush[1] - element.y) > B.u_vision) { + u = true; + } - if (u) { - element.push = element.last_upush; - element.last_upush = [element.x, element.y]; - } + if (u) { + element.push = element.last_upush; + element.last_upush = [element.x, element.y]; + } } function appengine_call(method, args, on_success, on_error) { - var api_path = "/api/" + method; - if ( - mode.prevent_external && - !in_arr(method, ["create_server", "update_server", "stop_server", "start_character", "send_mail"]) - ) { - return; - } - function retry_call() { - var t = 1600; - args.retried = (args.retried || 0) + 1; - args.retries--; - if (args.retried > 20) { - t = 240000; - } else if (args.retried > 10) { - t = 60000; - } else if (args.retried > 5) { - t = 20000; - } - setTimeout(function () { - appengine_call(method, args, on_success, on_error); - }, t); - // network retries, for "Error: socket hang up" etc. [09/09/16] - } - if (!args) { - args = {}; - } - if (!on_success) { - on_success = function () {}; - } - if (args.suffix) { - api_path += args.suffix; - delete args.suffix; - } - data = { - method: method, - arguments: JSON.stringify(args), - server_auth: server_id + "-" + server_auth, - auth: args.auth, - }; - try { - request.post({ url: base_url + api_path, form: data }, function (err, response, body) { - try { - if (err || !response || response.statusCode != 200) { - // node.request sends an undefined response when there is an issue ... - console.log( - "appengine_call - unknown error " + - err + - " or code: " + - (response && response.statusCode) + - " retries: " + - args.retries + - new Date() + - " on " + - api_path, - ); - if (method != "log_error") { - setTimeout( - (function (err, response, api_path, body) { - return function () { - try { - appengine_call("log_error", { - type: "appengine_call_error", - err: (response && response.statusCode) + " " + api_path, - info: "" + body + "" + err + "\n" + JSON.stringify((response && response.headers) || {}), - }); - } catch (e) { - log_trace("appengine_call's log_error " + api_path, e); - } - }; - })(err, response, api_path, body), - 120000, - ); - } - if (args.retries) { - retry_call(); - } else if (on_error) { - on_error("" + err + " " + (response && response.statusCode)); - } - } else { - ct = JSON.parse(body); - if (on_success) { - on_success.apply(this, [ct]); - } - } - } catch (e) { - log_trace("appengine_call exception on " + api_path, e); - if (args.retries) { - retry_call(); - } else if (on_error) { - on_error("" + err + " " + e + " " + (response && response.statusCode)); - } else if (on_success) { - on_success.apply(this, [{ failed: 1, reason: "callbackfailed" }]); - } - } - }); - } catch (e) { - log_trace("appengine_call on " + api_path, e); - if (args.retries) { - retry_call(); - } else if (on_error) { - on_error("" + e + " " + (response && response.statusCode)); - } else if (on_success) { - on_success.apply(this, [{ failed: 1, reason: "callfailed" }]); - } - } - if (!args.init) { - args.init = new Date(); - } - return args; + var api_path = "/api/" + method; + if ( + mode.prevent_external && + !in_arr(method, ["create_server", "update_server", "stop_server", "start_character", "send_mail"]) + ) { + return; + } + function retry_call() { + var t = 1600; + args.retried = (args.retried || 0) + 1; + args.retries--; + if (args.retried > 20) { + t = 240000; + } else if (args.retried > 10) { + t = 60000; + } else if (args.retried > 5) { + t = 20000; + } + setTimeout(function () { + appengine_call(method, args, on_success, on_error); + }, t); + // network retries, for "Error: socket hang up" etc. [09/09/16] + } + if (!args) { + args = {}; + } + if (!on_success) { + on_success = function () {}; + } + if (args.suffix) { + api_path += args.suffix; + delete args.suffix; + } + data = { + method: method, + arguments: JSON.stringify(args), + server_auth: server_id + "-" + server_auth, + auth: args.auth, + }; + try { + request.post({ url: base_url + api_path, form: data }, function (err, response, body) { + try { + if (err || !response || response.statusCode != 200) { + // node.request sends an undefined response when there is an issue ... + console.log( + "appengine_call - unknown error " + + err + + " or code: " + + (response && response.statusCode) + + " retries: " + + args.retries + + new Date() + + " on " + + api_path, + ); + if (method != "log_error") { + setTimeout( + (function (err, response, api_path, body) { + return function () { + try { + appengine_call("log_error", { + type: "appengine_call_error", + err: (response && response.statusCode) + " " + api_path, + info: "" + body + "" + err + "\n" + JSON.stringify((response && response.headers) || {}), + }); + } catch (e) { + log_trace("appengine_call's log_error " + api_path, e); + } + }; + })(err, response, api_path, body), + 120000, + ); + } + if (args.retries) { + retry_call(); + } else if (on_error) { + on_error("" + err + " " + (response && response.statusCode)); + } + } else { + ct = JSON.parse(body); + if (on_success) { + on_success.apply(this, [ct]); + } + } + } catch (e) { + log_trace("appengine_call exception on " + api_path, e); + if (args.retries) { + retry_call(); + } else if (on_error) { + on_error("" + err + " " + e + " " + (response && response.statusCode)); + } else if (on_success) { + on_success.apply(this, [{ failed: 1, reason: "callbackfailed" }]); + } + } + }); + } catch (e) { + log_trace("appengine_call on " + api_path, e); + if (args.retries) { + retry_call(); + } else if (on_error) { + on_error("" + e + " " + (response && response.statusCode)); + } else if (on_success) { + on_success.apply(this, [{ failed: 1, reason: "callfailed" }]); + } + } + if (!args.init) { + args.init = new Date(); + } + return args; } function discord_call(message) { - if (gameplay == "hardcore" || gameplay == "test") { - return; - } - if (is_sdk) { - return server_log("Discord: " + message); - } - var url = "https://discordapp.com/api/channels/404333059018719233/messages"; - if (message.search(" joined Adventure Land") != -1) { - url = "https://discordapp.com/api/channels/839163123499794481/messages"; - } - request( - { - url: url, - headers: { Authorization: "Bot " + variables.discord_token }, - method: "POST", - json: { - content: message, - }, - }, - function (err, response, body) { - //console.log(response); - }, - ); + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + if (is_sdk) { + return server_log("Discord: " + message); + } + var url = "https://discordapp.com/api/channels/404333059018719233/messages"; + if (message.search(" joined Adventure Land") != -1) { + url = "https://discordapp.com/api/channels/839163123499794481/messages"; + } + request( + { + url: url, + headers: { Authorization: "Bot " + variables.discord_token }, + method: "POST", + json: { + content: message, + }, + }, + function (err, response, body) { + //console.log(response); + }, + ); } function server_log(message, important) { - if (is_sdk || important) { - if (is_sdk) { - console.log(message); - } else { - console.log(message + " (" + new Date() + ")"); - } - if (message && (message + "").indexOf("SEVERE") != -1) { - appengine_call( - "server_event", - { - event: "notice", - keyword: variables.keyword, - id: server_id, - message: message, - color: "red", - }, - function (result) {}, - ); - } - } + if (is_sdk || important) { + if (is_sdk) { + console.log(message); + } else { + console.log(message + " (" + new Date() + ")"); + } + if (message && (message + "").indexOf("SEVERE") != -1) { + appengine_call( + "server_event", + { + event: "notice", + keyword: variables.keyword, + id: server_id, + message: message, + color: "red", + }, + function (result) {}, + ); + } + } } function appengine_log(event, message, color) { - if (!color) { - color = "gray"; - } - appengine_call( - "server_event", - { - event: event, - keyword: variables.keyword, - id: server_id, - message: message, - color: color, - }, - function (result) {}, - ); + if (!color) { + color = "gray"; + } + appengine_call( + "server_event", + { + event: event, + keyword: variables.keyword, + id: server_id, + message: message, + color: color, + }, + function (result) {}, + ); } function disappearing_text(socket, entity, text, args) { - var x = entity.x; - var y = entity.y - 30; - var d_args = {}; - if (!args) { - args = {}; - } - // if(!args.size) args.size=16; - if (!args.color) { - args.color = ""; - } + var x = entity.x; + var y = entity.y - 30; + var d_args = {}; + if (!args) { + args = {}; + } + // if(!args.size) args.size=16; + if (!args.color) { + args.color = ""; + } - if (args.color) { - d_args.c = args.color; - } // color - if (args.s) { - d_args.s = args.s; - } // sound - if (args.size) { - d_args.sz = args.size; - } - if (args.from) { - d_args.from = args.from; - } // for d_text + .evade + d_line + if (args.color) { + d_args.c = args.color; + } // color + if (args.s) { + d_args.s = args.s; + } // sound + if (args.size) { + d_args.sz = args.size; + } + if (args.from) { + d_args.from = args.from; + } // for d_text + .evade + d_line - if (args.xy && args.nv) { - xy_emit(entity, "disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args, nv: 1 }); - } else if (args.xy) { - xy_emit(entity, "disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args }); - } else if (args.party) { - party_emit( - args.party, - "disappearing_text", - { message: text, x: x, y: y, id: entity.id, args: d_args }, - { map: args.map }, - ); - } else { - socket.emit("disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args }); - } // volatile. + if (args.xy && args.nv) { + xy_emit(entity, "disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args, nv: 1 }); + } else if (args.xy) { + xy_emit(entity, "disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args }); + } else if (args.party) { + party_emit( + args.party, + "disappearing_text", + { message: text, x: x, y: y, id: entity.id, args: d_args }, + { map: args.map }, + ); + } else { + socket.emit("disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args }); + } // volatile. } function magiport_someone(pulled, player) { - var spot = random_one([ - [-10, 16], - [10, 16], - [10, -16], - [-10, -16], - [0, -24], - [-20, -32], - [20, 32], - [-20, 32], - [20, -32], - ]); - var spot = safe_xy_nearby(player.map, player.x + spot[0], player.y + spot[1]); - if (!spot) { - return false; - } - pulled.s.magiport = { ms: 400 }; - pulled.s.magiport.x = spot.x; - pulled.s.magiport.y = spot.y; - pulled.s.magiport.f = player.name; - pulled.s.magiport.in = player.in; - pulled.s.magiport.map = player.map; - if (player.party == pulled.party) { - player.pdps += 2000; - } - resend(pulled, "u+cid"); - return true; + var spot = random_one([ + [-10, 16], + [10, 16], + [10, -16], + [-10, -16], + [0, -24], + [-20, -32], + [20, 32], + [-20, 32], + [20, -32], + ]); + var spot = safe_xy_nearby(player.map, player.x + spot[0], player.y + spot[1]); + if (!spot) { + return false; + } + pulled.s.magiport = { ms: 400 }; + pulled.s.magiport.x = spot.x; + pulled.s.magiport.y = spot.y; + pulled.s.magiport.f = player.name; + pulled.s.magiport.in = player.in; + pulled.s.magiport.map = player.map; + if (player.party == pulled.party) { + player.pdps += 2000; + } + resend(pulled, "u+cid"); + return true; } function exchange(player, name, args) { - if (!args) { - args = {}; - } - var socket = player.socket; - var done = false; - var total = 0; - var current = 0; - var table = D.drops[name]; - if (is_array(name)) { - table = name; - name = args.name; - } - if (name.startsWith("cosmo")) { - table = clone(table); - table.forEach(function (drop) { - if (player.p.acx[drop[2]]) { - drop[0] /= 10 ** player.p.acx[drop[2]]; - } - }); - console.log(table); - } - table.forEach(function (drop) { - total += drop[0]; - }); - result = Math.random() * total; - table.forEach(function (drop) { - if (done) { - return; - } - current += drop[0]; - if (result <= current) { - done = true; - if (drop[1] == "gold") { - player.gold += drop[2]; - socket.emit("game_log", { message: "Received " + to_pretty_num(drop[2]) + " gold", color: "gold" }); - if (drop[2] > 3000000 && !player.stealth) { - broadcast("server_message", { - message: player.name + " received " + to_pretty_num(drop[2]) + " gold", - color: "gold", - }); - } - } else if (drop[1] == "shells") { - add_shells(player, drop[2], name, true, "override"); - } else if (drop[1] == "empty") { - socket.emit("game_log", "Didn't receive anything"); - } else if (drop[1] == "cx" || drop[1] == "cxbundle") { - player.p.acx[drop[2]] = (player.p.acx[drop[2]] || 0) + 1; - socket.emit("game_response", { - response: "cx_new", - acx: player.p.acx, - name: drop[2], - from: name, - bundle: drop[1] == "cxbundle", - }); - } else if (drop[1] == "open") { - exchange(player, drop[2]); - } else { - var item = create_new_item(drop[1], drop[2]); - var prop = undefined; - if ( - name == "glitch" && - (G.items[item.name].upgrade || - G.items[item.name].compound || - character_slots.includes(G.items[item.name].type)) - ) { - item.p = "glitched"; - } - if (name == "glitch") { - args.phrase = "Glitched"; - } - if (args.v) { - item.v = args.v; - } - if (drop[1] == "cxjar" || drop[1] == "emotionjar") { - item.data = drop[3]; - } - add_item(player, item, { r: 1, phrase: args.phrase }); - socket.emit("game_log", { - message: (args.phrase || "Received") + " " + item_to_phrase(item), - color: colors.server_success, - }); - } - } - }); - if (!done) { - socket.emit("game_log", "Didn't receive anything"); - } + if (!args) { + args = {}; + } + var socket = player.socket; + var done = false; + var total = 0; + var current = 0; + var table = D.drops[name]; + if (is_array(name)) { + table = name; + name = args.name; + } + if (name.startsWith("cosmo")) { + table = clone(table); + table.forEach(function (drop) { + if (player.p.acx[drop[2]]) { + drop[0] /= 10 ** player.p.acx[drop[2]]; + } + }); + console.log(table); + } + table.forEach(function (drop) { + total += drop[0]; + }); + result = Math.random() * total; + table.forEach(function (drop) { + if (done) { + return; + } + current += drop[0]; + if (result <= current) { + done = true; + if (drop[1] == "gold") { + player.gold += drop[2]; + socket.emit("game_log", { message: "Received " + to_pretty_num(drop[2]) + " gold", color: "gold" }); + if (drop[2] > 3000000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " received " + to_pretty_num(drop[2]) + " gold", + color: "gold", + }); + } + } else if (drop[1] == "shells") { + add_shells(player, drop[2], name, true, "override"); + } else if (drop[1] == "empty") { + socket.emit("game_log", "Didn't receive anything"); + } else if (drop[1] == "cx" || drop[1] == "cxbundle") { + player.p.acx[drop[2]] = (player.p.acx[drop[2]] || 0) + 1; + socket.emit("game_response", { + response: "cx_new", + acx: player.p.acx, + name: drop[2], + from: name, + bundle: drop[1] == "cxbundle", + }); + } else if (drop[1] == "open") { + exchange(player, drop[2]); + } else { + var item = create_new_item(drop[1], drop[2]); + var prop = undefined; + if ( + name == "glitch" && + (G.items[item.name].upgrade || + G.items[item.name].compound || + character_slots.includes(G.items[item.name].type)) + ) { + item.p = "glitched"; + } + if (name == "glitch") { + args.phrase = "Glitched"; + } + if (args.v) { + item.v = args.v; + } + if (drop[1] == "cxjar" || drop[1] == "emotionjar") { + item.data = drop[3]; + } + add_item(player, item, { r: 1, phrase: args.phrase }); + socket.emit("game_log", { + message: (args.phrase || "Received") + " " + item_to_phrase(item), + color: colors.server_success, + }); + } + } + }); + if (!done) { + socket.emit("game_log", "Didn't receive anything"); + } } function chest_exchange(chest, name) { - var done = false; - var total = 0; - var current = 0; - D.drops[name].forEach(function (drop) { - total += drop[0]; - }); - result = Math.random() * total; - D.drops[name].forEach(function (drop) { - if (done) { - return; - } - current += drop[0]; - if (result <= current) { - done = true; - if (drop[1] == "gold") { - chest.gold += drop[2]; - } else if (drop[1] == "shells") { - chest.cash += drop[2]; - } else if (drop[1] == "empty") { - } else if (drop[1] == "open") { - chest_exchange(chest, drop[2]); - } else { - chest.items.push(create_new_item(drop[1])); - } - } - }); + var done = false; + var total = 0; + var current = 0; + D.drops[name].forEach(function (drop) { + total += drop[0]; + }); + result = Math.random() * total; + D.drops[name].forEach(function (drop) { + if (done) { + return; + } + current += drop[0]; + if (result <= current) { + done = true; + if (drop[1] == "gold") { + chest.gold += drop[2]; + } else if (drop[1] == "shells") { + chest.cash += drop[2]; + } else if (drop[1] == "empty") { + } else if (drop[1] == "open") { + chest_exchange(chest, drop[2]); + } else { + chest.items.push(create_new_item(drop[1])); + } + } + }); } var item_p_ignore = { - grace: true, - giveaway: true, - gf: true, - price: true, - b: true, - rid: true, - list: true, - o: true, - oo: true, - src: true, + grace: true, + giveaway: true, + gf: true, + price: true, + b: true, + rid: true, + list: true, + o: true, + oo: true, + src: true, }; var item_trade_p_ignore = { grace: true, o: true, oo: true, src: true }; @@ -3558,459 +3558,459 @@ var item_trade_p_ignore = { grace: true, o: true, oo: true, src: true }; // .list -> the list for .giveaway function cache_item(current, trade, override) { - if (!current) { - return null; - } - var item = {}; - if (trade) { - for (var p in current) { - if (!item_trade_p_ignore[p]) { - item[p] = current[p]; - } - } - if (!item.giveaway) { - delete item.list; - } - } else { - for (var p in current) { - if (!item_p_ignore[p]) { - item[p] = current[p]; - } - } - } - if (override) { - for (var p in override) { - item[p] = override[p]; - } - } - return item; + if (!current) { + return null; + } + var item = {}; + if (trade) { + for (var p in current) { + if (!item_trade_p_ignore[p]) { + item[p] = current[p]; + } + } + if (!item.giveaway) { + delete item.list; + } + } else { + for (var p in current) { + if (!item_p_ignore[p]) { + item[p] = current[p]; + } + } + } + if (override) { + for (var p in override) { + item[p] = override[p]; + } + } + return item; } function get_trade_slots(player) { - if (player.p.stand) { - var num = 16; - var slots = []; - if (player.type == "merchant" && player.level >= 80) { - num = 30; - } else if (player.type == "merchant" && (player.level >= 70 || player.p.stand == "cstand")) { - num = 24; - } - for (var i = 1; i <= num; i++) { - slots.push("trade" + i); - } - return slots; - } else if (player.p.trades) { - return ["trade1", "trade2", "trade3", "trade4"]; - } - return []; + if (player.p.stand) { + var num = 16; + var slots = []; + if (player.type == "merchant" && player.level >= 80) { + num = 30; + } else if (player.type == "merchant" && (player.level >= 70 || player.p.stand == "cstand")) { + num = 24; + } + for (var i = 1; i <= num; i++) { + slots.push("trade" + i); + } + return slots; + } else if (player.p.trades) { + return ["trade1", "trade2", "trade3", "trade4"]; + } + return []; } function reslot_player(player) { - trade_slots.forEach(function (slot) { - try { - delete player.cslots[slot]; - } catch (e) {} - }); - get_trade_slots(player).forEach(function (slot) { - player.cslots[slot] = cache_item(player.slots[slot], 1); - }); + trade_slots.forEach(function (slot) { + try { + delete player.cslots[slot]; + } catch (e) {} + }); + get_trade_slots(player).forEach(function (slot) { + player.cslots[slot] = cache_item(player.slots[slot], 1); + }); } function cache_player_items(player) { - // console.log("Cached "+player.name+"'s items"); - if (player.slots && player.slots.ring1 && player.slots.ring1.name == "tristone" && player.slots.ring1.level >= 4) { - player.slots.ring1.name = "darktristone"; - } - if (player.slots && player.slots.ring2 && player.slots.ring2.name == "tristone" && player.slots.ring2.level >= 4) { - player.slots.ring2.name = "darktristone"; - } - player.cslots = {}; - player.citems = []; - character_slots.forEach(function (slot) { - player.cslots[slot] = cache_item(player.slots[slot]); - }); - for (var i = 0; i < player.items.length; i++) { - player.citems[i] = cache_item(player.items[i]); - } - reslot_player(player); + // console.log("Cached "+player.name+"'s items"); + if (player.slots && player.slots.ring1 && player.slots.ring1.name == "tristone" && player.slots.ring1.level >= 4) { + player.slots.ring1.name = "darktristone"; + } + if (player.slots && player.slots.ring2 && player.slots.ring2.name == "tristone" && player.slots.ring2.level >= 4) { + player.slots.ring2.name = "darktristone"; + } + player.cslots = {}; + player.citems = []; + character_slots.forEach(function (slot) { + player.cslots[slot] = cache_item(player.slots[slot]); + }); + for (var i = 0; i < player.items.length; i++) { + player.citems[i] = cache_item(player.items[i]); + } + reslot_player(player); } function init_bank(player) { - player.cuser = {}; - for (var pack in bank_packs) { - if (!player.user[pack]) { - continue; - } - player.cuser[pack] = []; - for (var i = 0; i < player.user[pack].length; i++) { - if (!player.user[pack][i]) { - player.cuser[pack][i] = null; - continue; - } - if (player.user[pack][i].expires) { - player.user[pack][i].expires = new Date(player.user[pack][i].expires); - } - player.cuser[pack][i] = cache_item(player.user[pack][i]); - } - } + player.cuser = {}; + for (var pack in bank_packs) { + if (!player.user[pack]) { + continue; + } + player.cuser[pack] = []; + for (var i = 0; i < player.user[pack].length; i++) { + if (!player.user[pack][i]) { + player.cuser[pack][i] = null; + continue; + } + if (player.user[pack][i].expires) { + player.user[pack][i].expires = new Date(player.user[pack][i].expires); + } + player.cuser[pack][i] = cache_item(player.user[pack][i]); + } + } } function init_bank_exit(player) { - for (var pack in bank_packs) { - if (!player.user[pack]) { - continue; - } - if (player.user[pack].length > 42) { - player.user[pack].length = 42; - } - } + for (var pack in bank_packs) { + if (!player.user[pack]) { + continue; + } + if (player.user[pack].length > 42) { + player.user[pack].length = 42; + } + } } function init_player(player) { - var class_def = G.classes[player.type]; - player.citems = []; - if ( - (player.slots.mainhand && - player.slots.offhand && - G.classes[player.type].doublehand[ - G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type - ]) || - (player.slots.mainhand && - !G.classes[player.type].mainhand[ - G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type - ] && - !G.classes[player.type].doublehand[ - G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type - ]) - ) { - add_item(player, player.slots.mainhand, { announce: false }); - player.slots.mainhand = null; - } - if ( - player.slots.offhand && - !G.classes[player.type].offhand[G.items[player.slots.offhand.name].wtype || G.items[player.slots.offhand.name].type] - ) { - add_item(player, player.slots.offhand, { announce: false }); - player.slots.offhand = null; - } - for (var i = 0; i < player.items.length; i++) { - if (!player.items[i]) { - continue; - } - delete player.items[i].m; - if (player.items[i].v && msince(new Date(player.items[i].v)) > 60) { - delete player.items[i].v; - } - if (player.items[i].expires) { - player.items[i].expires = new Date(player.items[i].expires); - } - if (!Object.keys(player.q || {}).length && player.items[i].name == "placeholder") { - player.items[i] = null; - } - } - check_slots.forEach(function (slot) { - if (player.slots[slot] && player.slots[slot].expires) { - player.slots[slot].expires = new Date(player.slots[slot].expires); - } - }); - if (player.p.item_num === undefined) { - player.p.item_num = parseInt(Math.random() * 42); - } - for (var s in player.s || {}) { - if (player.s[s] && player.s[s].last) { - player.s[s].last = new Date(player.s[s].last); - } - } - for (var dt in player.p.dt) { - if (!is_string(player.p.dt[dt])) { - continue; - } - player.p.dt[dt] = new Date(player.p.dt[dt]); - } - for (var id in G.skills) { - if (G.skills[id]["class"] && G.skills[id]["class"].includes(player.type) && G.skills[id].persistent) { - player.last[id] = player.p.dt[id] || new Date(); - if ((G.skills[id].cooldown || G.skills[id].reuse_cooldown) > mssince(player.last[id])) { - player.hitchhikers.push([ - "eval", - { - code: - "skill_timeout('" + - id + - "'," + - ((G.skills[id].cooldown || G.skills[id].reuse_cooldown) - mssince(player.last[id])) + - ")", - }, - ]); - } - } - } - if (!player.skin || !T[player.skin]) { - player.skin = class_def.looks[0][0]; - player.cx = clone(class_def.looks[0][1]); - } - prune_cx(player.cx || {}); - if (!player.p.acx) { - player.p.acx = {}; - player.p.xcx = []; - } - if (!player.p.emx) { - player.p.emx = {}; - } - if (!player.p.ap) { - player.p.ap = {}; - } // achievement progress - if (!player.p.achievements) { - player.p.achievements = {}; - } - if (player.p.mute) { - player.mute = true; - } - if (player.p.role == "gm") { - player.gm = true; - } - if (player.p.role) { - player.role = player.p.role; - } - if (events.holidayseason && !player.p.firstbuff) { - add_condition(player, "holidayspirit"); - player.p.firstbuff = true; - } - if (!player.p.ugrace || player.p.ugrace.length != 15) { - player.p.ugrace = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } // first digit and some of the last aren't used [01/10/17] - if (!player.p.cgrace || player.p.cgrace.length != 15) { - player.p.cgrace = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } - if (!player.p.ograce) { - player.p.ograce = 0; - } - if (!player.p.minutes) { - player.p.minutes = 0; - } - if (!player.p.stats) { - player.p.stats = { monsters: {} }; - } - if (!player.p.stats.exchanges) { - player.p.stats.exchanges = {}; - } - if (!player.p.stats.monsters_diff) { - player.p.stats.monsters_diff = {}; - } - if (is_array(player.cx)) { - player.cx = {}; - } - if (player.items.length < 42) { - player.items.length = 42; - } + var class_def = G.classes[player.type]; + player.citems = []; + if ( + (player.slots.mainhand && + player.slots.offhand && + G.classes[player.type].doublehand[ + G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type + ]) || + (player.slots.mainhand && + !G.classes[player.type].mainhand[ + G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type + ] && + !G.classes[player.type].doublehand[ + G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type + ]) + ) { + add_item(player, player.slots.mainhand, { announce: false }); + player.slots.mainhand = null; + } + if ( + player.slots.offhand && + !G.classes[player.type].offhand[G.items[player.slots.offhand.name].wtype || G.items[player.slots.offhand.name].type] + ) { + add_item(player, player.slots.offhand, { announce: false }); + player.slots.offhand = null; + } + for (var i = 0; i < player.items.length; i++) { + if (!player.items[i]) { + continue; + } + delete player.items[i].m; + if (player.items[i].v && msince(new Date(player.items[i].v)) > 60) { + delete player.items[i].v; + } + if (player.items[i].expires) { + player.items[i].expires = new Date(player.items[i].expires); + } + if (!Object.keys(player.q || {}).length && player.items[i].name == "placeholder") { + player.items[i] = null; + } + } + check_slots.forEach(function (slot) { + if (player.slots[slot] && player.slots[slot].expires) { + player.slots[slot].expires = new Date(player.slots[slot].expires); + } + }); + if (player.p.item_num === undefined) { + player.p.item_num = parseInt(Math.random() * 42); + } + for (var s in player.s || {}) { + if (player.s[s] && player.s[s].last) { + player.s[s].last = new Date(player.s[s].last); + } + } + for (var dt in player.p.dt) { + if (!is_string(player.p.dt[dt])) { + continue; + } + player.p.dt[dt] = new Date(player.p.dt[dt]); + } + for (var id in G.skills) { + if (G.skills[id]["class"] && G.skills[id]["class"].includes(player.type) && G.skills[id].persistent) { + player.last[id] = player.p.dt[id] || new Date(); + if ((G.skills[id].cooldown || G.skills[id].reuse_cooldown) > mssince(player.last[id])) { + player.hitchhikers.push([ + "eval", + { + code: + "skill_timeout('" + + id + + "'," + + ((G.skills[id].cooldown || G.skills[id].reuse_cooldown) - mssince(player.last[id])) + + ")", + }, + ]); + } + } + } + if (!player.skin || !T[player.skin]) { + player.skin = class_def.looks[0][0]; + player.cx = clone(class_def.looks[0][1]); + } + prune_cx(player.cx || {}); + if (!player.p.acx) { + player.p.acx = {}; + player.p.xcx = []; + } + if (!player.p.emx) { + player.p.emx = {}; + } + if (!player.p.ap) { + player.p.ap = {}; + } // achievement progress + if (!player.p.achievements) { + player.p.achievements = {}; + } + if (player.p.mute) { + player.mute = true; + } + if (player.p.role == "gm") { + player.gm = true; + } + if (player.p.role) { + player.role = player.p.role; + } + if (events.holidayseason && !player.p.firstbuff) { + add_condition(player, "holidayspirit"); + player.p.firstbuff = true; + } + if (!player.p.ugrace || player.p.ugrace.length != 15) { + player.p.ugrace = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } // first digit and some of the last aren't used [01/10/17] + if (!player.p.cgrace || player.p.cgrace.length != 15) { + player.p.cgrace = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + if (!player.p.ograce) { + player.p.ograce = 0; + } + if (!player.p.minutes) { + player.p.minutes = 0; + } + if (!player.p.stats) { + player.p.stats = { monsters: {} }; + } + if (!player.p.stats.exchanges) { + player.p.stats.exchanges = {}; + } + if (!player.p.stats.monsters_diff) { + player.p.stats.monsters_diff = {}; + } + if (is_array(player.cx)) { + player.cx = {}; + } + if (player.items.length < 42) { + player.items.length = 42; + } - //if(player.s.monsterhunt && player.s.monsterhunt.sn!=region+" "+server_name) delete player.s.monsterhunt; + //if(player.s.monsterhunt && player.s.monsterhunt.sn!=region+" "+server_name) delete player.s.monsterhunt; - for (var n in player.s) { - if (to_number(player.s[n])) { - player.s[n] = { ms: to_number(player.s[n]) }; - } - } + for (var n in player.s) { + if (to_number(player.s[n])) { + player.s[n] = { ms: to_number(player.s[n]) }; + } + } - player.xyh = [{ x: player.x, y: player.y, map: player.map, t: new Date() }]; // xy history - player.rid = round(Math.random() * 1000); // random number to randomise events + player.xyh = [{ x: player.x, y: player.y, map: player.map, t: new Date() }]; // xy history + player.rid = round(Math.random() * 1000); // random number to randomise events } function init_player_exit(player) { - // these are safety routines, if a bug allows the inventory to grow infinitely, this prevents item duplication possibilities [18/10/18] - for (var id in player.q) { - player.q[id].stale = true; - } - if (player.items.length > 70) { - player.items.length = 70; - } - if (Object.keys(player.slots).length > 500) { - player.slots = {}; - } - for (var id in G.skills) { - if ( - G.skills[id]["class"] && - G.skills[id]["class"].includes(player.type) && - G.skills[id].persistent && - player.last[id] - ) { - player.p.dt[id] = player.last[id]; - } - } + // these are safety routines, if a bug allows the inventory to grow infinitely, this prevents item duplication possibilities [18/10/18] + for (var id in player.q) { + player.q[id].stale = true; + } + if (player.items.length > 70) { + player.items.length = 70; + } + if (Object.keys(player.slots).length > 500) { + player.slots = {}; + } + for (var id in G.skills) { + if ( + G.skills[id]["class"] && + G.skills[id]["class"].includes(player.type) && + G.skills[id].persistent && + player.last[id] + ) { + player.p.dt[id] = player.last[id]; + } + } } function push_xyh(player, x, y) { - if (!player.xyh) { - return; - } - var last = player.xyh[player.xyh.length - 1]; - var current = { x: x, y: y, map: player.map, t: new Date() }; - if (mssince(last.t) > 50 || simple_distance(last, current) > 1) { - player.xyh.push(current); - } - if (player.xyh.length > 100) { - player.xyh.shift(); - } + if (!player.xyh) { + return; + } + var last = player.xyh[player.xyh.length - 1]; + var current = { x: x, y: y, map: player.map, t: new Date() }; + if (mssince(last.t) > 50 || simple_distance(last, current) > 1) { + player.xyh.push(current); + } + if (player.xyh.length > 100) { + player.xyh.shift(); + } } function item_name(item) { - var def = G.items[item.name]; - var name = def.name; - if (item.level) { - name += " +" + item.level; - } - return name; + var def = G.items[item.name]; + var name = def.name; + if (item.level) { + name += " +" + item.level; + } + return name; } function startswith_an(name) { - if (in_arr(name.toLowerCase()[0], ["a", "e", "o", "u", "i"])) { - return true; - } - return false; + if (in_arr(name.toLowerCase()[0], ["a", "e", "o", "u", "i"])) { + return true; + } + return false; } function item_to_phrase(item) { - if (item.q && item.q > 1) { - return G.items[item.name].name + " [x" + item.q + "]"; - } - var prefix = ""; - if (startswith_an(G.items[item.name].name)) { - prefix = "an "; - } else { - prefix = "a "; - } - if (item.p) { - prefix += item.p.toTitleCase() + " "; - } - if (item.level) { - return prefix + G.items[item.name].name + " +" + item.level; - } - return prefix + G.items[item.name].name; + if (item.q && item.q > 1) { + return G.items[item.name].name + " [x" + item.q + "]"; + } + var prefix = ""; + if (startswith_an(G.items[item.name].name)) { + prefix = "an "; + } else { + prefix = "a "; + } + if (item.p) { + prefix += item.p.toTitleCase() + " "; + } + if (item.level) { + return prefix + G.items[item.name].name + " +" + item.level; + } + return prefix + G.items[item.name].name; } function killed_message(type) { - if (G.monsters[type].prefix === "the") { - return "killed the " + G.monsters[type].name; - } else if (G.monsters[type].prefix === "") { - return "killed " + G.monsters[type].name; - } else if (startswith_an(G.monsters[type].name)) { - return "killed an " + G.monsters[type].name; - } else { - return "killed a " + G.monsters[type].name; - } + if (G.monsters[type].prefix === "the") { + return "killed the " + G.monsters[type].name; + } else if (G.monsters[type].prefix === "") { + return "killed " + G.monsters[type].name; + } else if (startswith_an(G.monsters[type].name)) { + return "killed an " + G.monsters[type].name; + } else { + return "killed a " + G.monsters[type].name; + } } function is_xy_safe_old(map, x, y) { - var safe = true; - if (smap_data[map] == -1) { - return true; - } - [ - [0, 0], - [0, smap_step + 1], - [0, -smap_step - 1], - [smap_step + 1, 0], - [-smap_step - 1, 0], - [0, smap_step / 2], - [smap_step / 2, 0], - [0, -smap_step / 2], - [-smap_step / 2, 0], - ].forEach(function (m) { - var data = smap_data[map][phash(x + m[0], y + m[1])]; - if (data || data === undefined) { - safe = false; - } - }); - return safe; + var safe = true; + if (smap_data[map] == -1) { + return true; + } + [ + [0, 0], + [0, smap_step + 1], + [0, -smap_step - 1], + [smap_step + 1, 0], + [-smap_step - 1, 0], + [0, smap_step / 2], + [smap_step / 2, 0], + [0, -smap_step / 2], + [-smap_step / 2, 0], + ].forEach(function (m) { + var data = smap_data[map][phash(x + m[0], y + m[1])]; + if (data || data === undefined) { + safe = false; + } + }); + return safe; } function is_xy_safe(map, x, y) { - var safe = true; - if (smap_data[map] == -1) { - return true; - } - // x=parseInt(x)-(parseInt(x)%smap_step); - // y=parseInt(y)-(parseInt(y)%smap_step); - [ - [0, 0], - [0, 1], - [0, -1], - [1, 0], - [-1, 0], - [1, 1], - [-1, 1], - [1, -1], - [-1, -1], - ].forEach(function (m) { - // ,[2,2],[-2,-2],[2,-2],[-2,2] - var data = smap_data[map][phash(x + m[0] * smap_step, y + m[1] * smap_step)]; - if (data || data === undefined) { - safe = false; - } - }); - return safe; + var safe = true; + if (smap_data[map] == -1) { + return true; + } + // x=parseInt(x)-(parseInt(x)%smap_step); + // y=parseInt(y)-(parseInt(y)%smap_step); + [ + [0, 0], + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + [1, 1], + [-1, 1], + [1, -1], + [-1, -1], + ].forEach(function (m) { + // ,[2,2],[-2,-2],[2,-2],[-2,2] + var data = smap_data[map][phash(x + m[0] * smap_step, y + m[1] * smap_step)]; + if (data || data === undefined) { + safe = false; + } + }); + return safe; } function safe_xy_nearby(map, x, y) { - var point = false; - // if(is_xy_safe(map,x,y)) return {x:x,y:y}; - x = smap_round(x); - y = smap_round(y); - // [1,0],[0,1],[1,1],[-1,1],[1,-1],[-1,0],[0,-1],[-1,-1],[2,0],[0,2],[2,2],[-2,-2],[-2,0],[0,-2] - [ - [0, 0], - [1, 0], - [0, 1], - [-1, 0], - [0, -1], - [2, 0], - [0, 2], - [-2, 0], - [0, -2], - [3, 0], - [0, 3], - [-3, 0], - [0, -3], - ].forEach(function (m) { - if (!point && is_xy_safe(map, x + m[0] * smap_step, y + m[1] * smap_step)) { - point = { x: x + m[0] * smap_step, y: y + m[1] * smap_step }; - } - }); - return point; + var point = false; + // if(is_xy_safe(map,x,y)) return {x:x,y:y}; + x = smap_round(x); + y = smap_round(y); + // [1,0],[0,1],[1,1],[-1,1],[1,-1],[-1,0],[0,-1],[-1,-1],[2,0],[0,2],[2,2],[-2,-2],[-2,0],[0,-2] + [ + [0, 0], + [1, 0], + [0, 1], + [-1, 0], + [0, -1], + [2, 0], + [0, 2], + [-2, 0], + [0, -2], + [3, 0], + [0, 3], + [-3, 0], + [0, -3], + ].forEach(function (m) { + if (!point && is_xy_safe(map, x + m[0] * smap_step, y + m[1] * smap_step)) { + point = { x: x + m[0] * smap_step, y: y + m[1] * smap_step }; + } + }); + return point; } function smap_round(x) { - // for blink, port, magiport - var x1 = parseInt(x) - (parseInt(x) % smap_step); - var x2 = x1 + smap_step; - if (x < 0) { - x2 = x1 - smap_step; - } - if (abs(x - x1) < abs(x - x2)) { - return x1; - } - return x2; + // for blink, port, magiport + var x1 = parseInt(x) - (parseInt(x) % smap_step); + var x2 = x1 + smap_step; + if (x < 0) { + x2 = x1 - smap_step; + } + if (abs(x - x1) < abs(x - x2)) { + return x1; + } + return x2; } function phash(x, y) { - if (x.x !== undefined) { - y = x.y; - x = x.x; - } - x = parseInt(x) - (parseInt(x) % smap_step); - y = parseInt(y) - (parseInt(y) % smap_step); - return x + "|" + y; + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + return x + "|" + y; } // Without rphash, halloween -448.0000001,-158.0000001 rounds to a corner which is a 2 function rphash(x, y) { - if (x.x !== undefined) { - y = x.y; - x = x.x; - } - x = smap_round(x); - y = smap_round(y); - return x + "|" + y; + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = smap_round(x); + y = smap_round(y); + return x + "|" + y; } var smap_data = {}; @@ -4019,1054 +4019,1054 @@ var smap_edge = 60; // for smap_step 24, the edge was 40 - also check out access // if(is_sdk) smap_step=24; // 10 takes toooo long [22/06/18] var hiding_places = []; function server_bfs(map) { - if ((precomputed && precomputed.version == G.version) || (precomputed && precomputed.smap_data)) { - smap_data[map] = precomputed.smap_data[map]; - amap_data[map] = precomputed.amap_data[map]; - return; - } - if (G.maps[map].no_bounds) { - smap_data[map] = -1; - amap_data[map] = {}; - return; - } - if (is_sdk && variables.fast_sdk && !(map == "level1" || map == "arena")) { - smap_data[map] = -1; - amap_data[map] = {}; - return; - } - server_bfs2(map); - smap_data[map] = {}; - var queue = []; - var visited = {}; - var start = 0; - var level = 0; - var chiding_places = []; - function push(x, y, c) { - x = parseInt(x) - (parseInt(x) % smap_step); - y = parseInt(y) - (parseInt(y) % smap_step); - var hash = phash(x, y); - if (visited[hash] && visited[hash].l <= c) { - return; - } - if (!c && !G.maps[map].pvp && !G.maps[map].instance && Math.random() < 0.01) { - chiding_places.push([map, x, y]); - } - if (c == level) { - queue.push([x, y]); - } - visited[hash] = { l: c, x: x, y: y }; - smap_data[map][hash] = c; - } - for (level = 0; level < 7; level++) { - if (level == 0) { - G.maps[map].spawns.forEach(function (s) { - var x = s[0]; - var y = s[1]; - var done = false; - x = parseInt(x) - (parseInt(x) % smap_step); - y = parseInt(y) - (parseInt(y) % smap_step); - var current = [x, y]; - [ - [0, 0], - [0, smap_step], - [0, -smap_step], - [smap_step, 0], - [-smap_step, 0], - ].forEach(function (m) { - if (!done && can_move({ map: map, x: x, y: y, going_x: current[0] + m[0], going_y: current[1] + m[1] })) { - push(current[0] + m[0], current[1] + m[1], level); - done = true; - } - }); - }); - } else { - for (var h in visited) { - var e = visited[h]; - if (e.l == level) { - queue.push([e.x, e.y]); - } - } - } - while (start < queue.length) { - if (queue.length > 50000) { - smap_data[map] = -1; - server_log(map + " is either un-bounded or the spawn point is too close to an edge", 1); - return; - } - var current = queue[start++]; - [ - [0, smap_step], - [0, -smap_step], - [smap_step, 0], - [-smap_step, 0], - ].forEach(function (m) { - if (level == 0) { - if ( - can_move({ map: map, x: current[0], y: current[1], going_x: current[0] + m[0], going_y: current[1] + m[1] }) - ) { - push(current[0] + m[0], current[1] + m[1], level); - } else { - push(current[0] + m[0], current[1] + m[1], level + 1); - } - } else { - push(current[0] + m[0], current[1] + m[1], level + 1); - } - }); - } - if (level == 0 && 0) { - // In the new system, 1 is safe too [01/08/18] - for (var i = 0; i < (G.maps[map].data.x_lines || []).length; i++) { - var current = G.maps[map].data.x_lines[i]; - push(current[0], current[1], 0); - push(current[0], current[2], 0); - for (var j = current[1]; j < current[2]; j += smap_step) { - push(current[0], j, 0); - [ - [0, smap_step], - [0, -smap_step], - [smap_step, 0], - [-smap_step, 0], - ].forEach(function (m) { - push(current[0] + m[0], j + m[1], 1); - }); - } - } - for (var i = 0; i < (G.maps[map].data.y_lines || []).length; i++) { - var current = G.maps[map].data.y_lines[i]; - push(current[1], current[0], 0); - push(current[2], current[0], 0); - for (var j = current[1]; j < current[2]; j += smap_step) { - push(j, current[0], 0); - [ - [0, smap_step], - [0, -smap_step], - [smap_step, 0], - [-smap_step, 0], - ].forEach(function (m) { - push(j + m[0], current[0] + m[1], 1); - }); - } - } - } - start = 0; - queue = []; - } - if (chiding_places.length) { - Array.prototype.push.apply(hiding_places, chiding_places); - } + if ((precomputed && precomputed.version == G.version) || (precomputed && precomputed.smap_data)) { + smap_data[map] = precomputed.smap_data[map]; + amap_data[map] = precomputed.amap_data[map]; + return; + } + if (G.maps[map].no_bounds) { + smap_data[map] = -1; + amap_data[map] = {}; + return; + } + if (is_sdk && variables.fast_sdk && !(map == "level1" || map == "arena")) { + smap_data[map] = -1; + amap_data[map] = {}; + return; + } + server_bfs2(map); + smap_data[map] = {}; + var queue = []; + var visited = {}; + var start = 0; + var level = 0; + var chiding_places = []; + function push(x, y, c) { + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + var hash = phash(x, y); + if (visited[hash] && visited[hash].l <= c) { + return; + } + if (!c && !G.maps[map].pvp && !G.maps[map].instance && Math.random() < 0.01) { + chiding_places.push([map, x, y]); + } + if (c == level) { + queue.push([x, y]); + } + visited[hash] = { l: c, x: x, y: y }; + smap_data[map][hash] = c; + } + for (level = 0; level < 7; level++) { + if (level == 0) { + G.maps[map].spawns.forEach(function (s) { + var x = s[0]; + var y = s[1]; + var done = false; + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + var current = [x, y]; + [ + [0, 0], + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + if (!done && can_move({ map: map, x: x, y: y, going_x: current[0] + m[0], going_y: current[1] + m[1] })) { + push(current[0] + m[0], current[1] + m[1], level); + done = true; + } + }); + }); + } else { + for (var h in visited) { + var e = visited[h]; + if (e.l == level) { + queue.push([e.x, e.y]); + } + } + } + while (start < queue.length) { + if (queue.length > 50000) { + smap_data[map] = -1; + server_log(map + " is either un-bounded or the spawn point is too close to an edge", 1); + return; + } + var current = queue[start++]; + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + if (level == 0) { + if ( + can_move({ map: map, x: current[0], y: current[1], going_x: current[0] + m[0], going_y: current[1] + m[1] }) + ) { + push(current[0] + m[0], current[1] + m[1], level); + } else { + push(current[0] + m[0], current[1] + m[1], level + 1); + } + } else { + push(current[0] + m[0], current[1] + m[1], level + 1); + } + }); + } + if (level == 0 && 0) { + // In the new system, 1 is safe too [01/08/18] + for (var i = 0; i < (G.maps[map].data.x_lines || []).length; i++) { + var current = G.maps[map].data.x_lines[i]; + push(current[0], current[1], 0); + push(current[0], current[2], 0); + for (var j = current[1]; j < current[2]; j += smap_step) { + push(current[0], j, 0); + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + push(current[0] + m[0], j + m[1], 1); + }); + } + } + for (var i = 0; i < (G.maps[map].data.y_lines || []).length; i++) { + var current = G.maps[map].data.y_lines[i]; + push(current[1], current[0], 0); + push(current[2], current[0], 0); + for (var j = current[1]; j < current[2]; j += smap_step) { + push(j, current[0], 0); + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + push(j + m[0], current[0] + m[1], 1); + }); + } + } + } + start = 0; + queue = []; + } + if (chiding_places.length) { + Array.prototype.push.apply(hiding_places, chiding_places); + } } function amap_round(x) { - // for blink, port, magiport - var x1 = parseInt(x) - (parseInt(x) % amap_step); - var x2 = x1 + amap_step; - if (x < 0) { - x2 = x1 - amap_step; - } - if (abs(x - x1) < abs(x - x2)) { - return x1; - } - return x2; + // for blink, port, magiport + var x1 = parseInt(x) - (parseInt(x) % amap_step); + var x2 = x1 + amap_step; + if (x < 0) { + x2 = x1 - amap_step; + } + if (abs(x - x1) < abs(x - x2)) { + return x1; + } + return x2; } function phash2(x, y) { - if (x.x !== undefined) { - y = x.y; - x = x.x; - } - x = parseInt(x) - (parseInt(x) % amap_step); - y = parseInt(y) - (parseInt(y) % amap_step); - return x + "|" + y; + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = parseInt(x) - (parseInt(x) % amap_step); + y = parseInt(y) - (parseInt(y) % amap_step); + return x + "|" + y; } function rphash2(x, y) { - if (x.x !== undefined) { - y = x.y; - x = x.x; - } - x = amap_round(x); - y = amap_round(y); - return x + "|" + y; + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = amap_round(x); + y = amap_round(y); + return x + "|" + y; } var amap_data = {}; var amap_step = 8; function server_bfs2(map) { - var base = { h: 9, v: 9, vn: 2 }; - var xmult = 1; - var vhmult = 1; - amap_data[map] = {}; //new Map(); - Map can't be sent to the Worker! :) [19/08/20] - var queue = []; - var visited = {}; - var start = 0; - var level = 0; - var chiding_places = []; - function push(x, y, c) { - x = parseInt(x) - (parseInt(x) % amap_step); - y = parseInt(y) - (parseInt(y) % amap_step); - var hash = phash2(x, y); - if (visited[hash]) { - return; - } - queue.push([x, y]); - visited[hash] = 1; - } - G.maps[map].spawns.forEach(function (s) { - var x = s[0]; - var y = s[1]; - var done = false; - x = parseInt(x) - (parseInt(x) % amap_step); - y = parseInt(y) - (parseInt(y) % amap_step); - var current = [x, y]; - [ - [0, 0], - [amap_step, amap_step], - [amap_step, -amap_step], - [-amap_step, -amap_step], - [-amap_step, amap_step], - [0, amap_step], - [0, -amap_step], - [amap_step, 0], - [-amap_step, 0], - ].forEach(function (m) { - if ( - !done && - can_move({ - map: map, - x: x, - y: y, - going_x: current[0] + m[0] * xmult, - going_y: current[1] + m[1] * xmult, - base: base, - }) - ) { - push(current[0] + m[0], current[1] + m[1], level); - done = true; - } - }); - }); - while (start < queue.length) { - if (queue.length > 120000) { - amap_data[map] = {}; - server_log(map + " is either un-bounded or the spawn point is too close to an edge [server_bfs2]", 1); - return; - } - var current = queue[start++]; - [ - [amap_step, amap_step], - [amap_step, -amap_step], - [-amap_step, -amap_step], - [-amap_step, amap_step], - [0, amap_step], - [0, -amap_step], - [amap_step, 0], - [-amap_step, 0], - ].forEach(function (m) { - if ( - can_move({ - map: map, - x: current[0], - y: current[1], - going_x: current[0] + m[0] * xmult, - going_y: current[1] + m[1] * xmult, - base: base, - }) - ) { - push(current[0] + m[0], current[1] + m[1]); - } - }); - } - for (var hash in visited) { - var x = parseInt(hash.split("|")[0]); - var y = parseInt(hash.split("|")[1]); - var count = 0; - [ - [amap_step, amap_step], - [amap_step, -amap_step], - [-amap_step, -amap_step], - [-amap_step, amap_step], - [0, amap_step], - [0, -amap_step], - [amap_step, 0], - [-amap_step, 0], - ].forEach(function (m) { - if (visited[x + m[0] + "|" + (y + m[1])]) { - count += 1; - } - }); - if (count >= 2) { - amap_data[map][hash] = 1; - } - } - for (var hash in amap_data[map]) { - var x = parseInt(hash.split("|")[0]); - var y = parseInt(hash.split("|")[1]); - var count = 0; - [ - [amap_step, amap_step], - [amap_step, -amap_step], - [-amap_step, -amap_step], - [-amap_step, amap_step], - [0, amap_step], - [0, -amap_step], - [amap_step, 0], - [-amap_step, 0], - ].forEach(function (m) { - if (amap_data[map][x + m[0] + "|" + (y + m[1])]) { - count += 1; - } - }); - amap_data[map][hash] = count; - } + var base = { h: 9, v: 9, vn: 2 }; + var xmult = 1; + var vhmult = 1; + amap_data[map] = {}; //new Map(); - Map can't be sent to the Worker! :) [19/08/20] + var queue = []; + var visited = {}; + var start = 0; + var level = 0; + var chiding_places = []; + function push(x, y, c) { + x = parseInt(x) - (parseInt(x) % amap_step); + y = parseInt(y) - (parseInt(y) % amap_step); + var hash = phash2(x, y); + if (visited[hash]) { + return; + } + queue.push([x, y]); + visited[hash] = 1; + } + G.maps[map].spawns.forEach(function (s) { + var x = s[0]; + var y = s[1]; + var done = false; + x = parseInt(x) - (parseInt(x) % amap_step); + y = parseInt(y) - (parseInt(y) % amap_step); + var current = [x, y]; + [ + [0, 0], + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if ( + !done && + can_move({ + map: map, + x: x, + y: y, + going_x: current[0] + m[0] * xmult, + going_y: current[1] + m[1] * xmult, + base: base, + }) + ) { + push(current[0] + m[0], current[1] + m[1], level); + done = true; + } + }); + }); + while (start < queue.length) { + if (queue.length > 120000) { + amap_data[map] = {}; + server_log(map + " is either un-bounded or the spawn point is too close to an edge [server_bfs2]", 1); + return; + } + var current = queue[start++]; + [ + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if ( + can_move({ + map: map, + x: current[0], + y: current[1], + going_x: current[0] + m[0] * xmult, + going_y: current[1] + m[1] * xmult, + base: base, + }) + ) { + push(current[0] + m[0], current[1] + m[1]); + } + }); + } + for (var hash in visited) { + var x = parseInt(hash.split("|")[0]); + var y = parseInt(hash.split("|")[1]); + var count = 0; + [ + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if (visited[x + m[0] + "|" + (y + m[1])]) { + count += 1; + } + }); + if (count >= 2) { + amap_data[map][hash] = 1; + } + } + for (var hash in amap_data[map]) { + var x = parseInt(hash.split("|")[0]); + var y = parseInt(hash.split("|")[1]); + var count = 0; + [ + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if (amap_data[map][x + m[0] + "|" + (y + m[1])]) { + count += 1; + } + }); + amap_data[map][hash] = count; + } } function can_amove(map, sx, sy, tx, ty) { - if (!amap_data[map][amap_round(tx) + "|" + amap_round(ty)]) { - return false; - } + if (!amap_data[map][amap_round(tx) + "|" + amap_round(ty)]) { + return false; + } - if (sy == ty) { - var p = (tx < sx && -1) || 1; - var step = 8; - for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { - if (!amap_data[map][amap_round(sx + p * s * step) + "|" + amap_round(ty)]) { - return false; - } - } - } else if (sx == tx) { - var p = (ty < sy && -1) || 1; - var step = 8; - for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { - if (!amap_data[map][amap_round(tx) + "|" + amap_round(sy + p * s * step)]) { - return false; - } - } - } else { - var len = point_distance(sx, sy, tx, ty); - var p = (tx < sx && -1) || 1; - var step = 5; - for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { - var x = sx + p * s * step; - var y = sy + ((ty - sy) * abs(s * step)) / abs(tx - sx); - //console.log("x-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); - if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { - return false; - } - } - var p = (ty < sy && -1) || 1; - var step = 5; - for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { - var x = sx + ((tx - sx) * abs(s * step)) / abs(ty - sy); - var y = sy + p * s * step; - //console.log("y-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); - if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { - return false; - } - } - } - return true; + if (sy == ty) { + var p = (tx < sx && -1) || 1; + var step = 8; + for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { + if (!amap_data[map][amap_round(sx + p * s * step) + "|" + amap_round(ty)]) { + return false; + } + } + } else if (sx == tx) { + var p = (ty < sy && -1) || 1; + var step = 8; + for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { + if (!amap_data[map][amap_round(tx) + "|" + amap_round(sy + p * s * step)]) { + return false; + } + } + } else { + var len = point_distance(sx, sy, tx, ty); + var p = (tx < sx && -1) || 1; + var step = 5; + for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { + var x = sx + p * s * step; + var y = sy + ((ty - sy) * abs(s * step)) / abs(tx - sx); + //console.log("x-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); + if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { + return false; + } + } + var p = (ty < sy && -1) || 1; + var step = 5; + for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { + var x = sx + ((tx - sx) * abs(s * step)) / abs(ty - sy); + var y = sy + p * s * step; + //console.log("y-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); + if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { + return false; + } + } + } + return true; } function random_place(map) { - var place = random_one(Object.keys(amap_data[map] || {})); - if (!place) { - return null; - } - var xy = place.split("|"); - return { x: parseInt(xy[0]), y: parseInt(xy[1]), map: map, in: map }; + var place = random_one(Object.keys(amap_data[map] || {})); + if (!place) { + return null; + } + var xy = place.split("|"); + return { x: parseInt(xy[0]), y: parseInt(xy[1]), map: map, in: map }; } function fast_astar(args) { - var map = args.map; - var sx = args.sx; - var sy = args.sy; - var tx = args.tx; - var ty = args.ty; - var heap = vHeap(); - var visited = {}; - var total = 0; - var start = new Date(); - var best = 999999999999; - var theone = null; - var good = false; - function hpush(cx, cy, fr, dir, bad) { - // if(visited[cx+"|"+cy]) return; - var value = point_distance(cx, cy, tx, ty); - var hash = cx + "|" + cy; - if (amap_data[map][hash] < 4) { - bad += 2; - } else if (amap_data[map][hash] < 6) { - bad += 1; - } else if (amap_data[map][hash] < 8) { - bad += 0.5; - } - total += 1; - // console.log(["hpush",total,cx,cy,bad,value]); - heap.insert({ value: value + bad * 0.25, x: cx, y: cy, bad: bad, dir: dir }); - visited[hash] = fr; - } - function finalise(current) { - var dx = current.x; - var dy = current.y; - path = [[dx, dy]]; - while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { - dx = visited[dx + "|" + dy].split("|"); - dy = parseInt(dx[1]); - dx = parseInt(dx[0]); - path.push([dx, dy]); - } - path.reverse(); - // path.push([tx,ty]); // Looks bad - dx = path[0][0]; - dy = path[0][1]; - // console.log(path); - for ( - var i = 1; - i < path.length; - i++ // 1ms at max [17/08/20] - ) { - if (can_amove(map, sx, sy, path[i][0], path[i][1])) { - dx = path[i][0]; - dy = path[i][1]; - } else { - break; - } - } - // if(!good && point_distance(sx,sy,dx,dy)<30) return null; - server_log([total, dx, dy, mssince(start)]); - return [dx, dy]; - } - for (var step = 1; step <= 2; step++) { - if (heap.array.length) { - break; - } - [ - [0, 0, 0, 0], - [amap_step, amap_step, 0, amap_step * 1.41], - [amap_step, -amap_step, 1, amap_step * 1.41], - [-amap_step, -amap_step, 2, amap_step * 1.41], - [-amap_step, amap_step, 3, amap_step * 1.41], - [0, amap_step, 4, amap_step], - [0, -amap_step, 5, amap_step], - [amap_step, 0, 6, amap_step], - [-amap_step, 0, 7, amap_step], - ].forEach(function (m) { - var cx = amap_round(sx + m[0] * step); - var cy = amap_round(sy + m[1] * step); - if (amap_data[map][cx + "|" + cy]) { - hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); - } - }); - } - while (heap.array.length) { - if (heap.array.length > 1200 || total > 4000) { - server_log(["heap", "fast_astar"]); - if (theone) { - var result = finalise(theone); - result.push("bad"); - return result; - } - return null; - } - var current = heap.removeTop(); - var dist = point_distance(current.x, current.y, tx, ty); - var rnd = Math.random() * 100; - // console.log([current.x,current.y,visited[current.x+"|"+current.y]]); - if (dist < 28) { - good = true; - return finalise(current); - } - if (dist + current.bad - rnd < best) { - best = dist + current.bad - rnd; - theone = current; - } - [ - [amap_step, amap_step, 0, amap_step * 1.41], - [amap_step, -amap_step, 1, amap_step * 1.41], - [-amap_step, -amap_step, 2, amap_step * 1.41], - [-amap_step, amap_step, 3, amap_step * 1.41], - [0, amap_step, 4, amap_step], - [0, -amap_step, 5, amap_step], - [amap_step, 0, 6, amap_step], - [-amap_step, 0, 7, amap_step], - ].forEach(function (m) { - var cx = current.x + m[0]; - var cy = current.y + m[1]; - if (amap_data[map][cx + "|" + cy] && !visited[cx + "|" + cy]) { - hpush(cx, cy, current.x + "|" + current.y, m[2], current.bad + m[3] + ((current.dir != m[2] && 0.5) || 0)); - } - }); - } - server_log(["no start", "fast_astar"]); + var map = args.map; + var sx = args.sx; + var sy = args.sy; + var tx = args.tx; + var ty = args.ty; + var heap = vHeap(); + var visited = {}; + var total = 0; + var start = new Date(); + var best = 999999999999; + var theone = null; + var good = false; + function hpush(cx, cy, fr, dir, bad) { + // if(visited[cx+"|"+cy]) return; + var value = point_distance(cx, cy, tx, ty); + var hash = cx + "|" + cy; + if (amap_data[map][hash] < 4) { + bad += 2; + } else if (amap_data[map][hash] < 6) { + bad += 1; + } else if (amap_data[map][hash] < 8) { + bad += 0.5; + } + total += 1; + // console.log(["hpush",total,cx,cy,bad,value]); + heap.insert({ value: value + bad * 0.25, x: cx, y: cy, bad: bad, dir: dir }); + visited[hash] = fr; + } + function finalise(current) { + var dx = current.x; + var dy = current.y; + path = [[dx, dy]]; + while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { + dx = visited[dx + "|" + dy].split("|"); + dy = parseInt(dx[1]); + dx = parseInt(dx[0]); + path.push([dx, dy]); + } + path.reverse(); + // path.push([tx,ty]); // Looks bad + dx = path[0][0]; + dy = path[0][1]; + // console.log(path); + for ( + var i = 1; + i < path.length; + i++ // 1ms at max [17/08/20] + ) { + if (can_amove(map, sx, sy, path[i][0], path[i][1])) { + dx = path[i][0]; + dy = path[i][1]; + } else { + break; + } + } + // if(!good && point_distance(sx,sy,dx,dy)<30) return null; + server_log([total, dx, dy, mssince(start)]); + return [dx, dy]; + } + for (var step = 1; step <= 2; step++) { + if (heap.array.length) { + break; + } + [ + [0, 0, 0, 0], + [amap_step, amap_step, 0, amap_step * 1.41], + [amap_step, -amap_step, 1, amap_step * 1.41], + [-amap_step, -amap_step, 2, amap_step * 1.41], + [-amap_step, amap_step, 3, amap_step * 1.41], + [0, amap_step, 4, amap_step], + [0, -amap_step, 5, amap_step], + [amap_step, 0, 6, amap_step], + [-amap_step, 0, 7, amap_step], + ].forEach(function (m) { + var cx = amap_round(sx + m[0] * step); + var cy = amap_round(sy + m[1] * step); + if (amap_data[map][cx + "|" + cy]) { + hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); + } + }); + } + while (heap.array.length) { + if (heap.array.length > 1200 || total > 4000) { + server_log(["heap", "fast_astar"]); + if (theone) { + var result = finalise(theone); + result.push("bad"); + return result; + } + return null; + } + var current = heap.removeTop(); + var dist = point_distance(current.x, current.y, tx, ty); + var rnd = Math.random() * 100; + // console.log([current.x,current.y,visited[current.x+"|"+current.y]]); + if (dist < 28) { + good = true; + return finalise(current); + } + if (dist + current.bad - rnd < best) { + best = dist + current.bad - rnd; + theone = current; + } + [ + [amap_step, amap_step, 0, amap_step * 1.41], + [amap_step, -amap_step, 1, amap_step * 1.41], + [-amap_step, -amap_step, 2, amap_step * 1.41], + [-amap_step, amap_step, 3, amap_step * 1.41], + [0, amap_step, 4, amap_step], + [0, -amap_step, 5, amap_step], + [amap_step, 0, 6, amap_step], + [-amap_step, 0, 7, amap_step], + ].forEach(function (m) { + var cx = current.x + m[0]; + var cy = current.y + m[1]; + if (amap_data[map][cx + "|" + cy] && !visited[cx + "|" + cy]) { + hpush(cx, cy, current.x + "|" + current.y, m[2], current.bad + m[3] + ((current.dir != m[2] && 0.5) || 0)); + } + }); + } + server_log(["no start", "fast_astar"]); } function fast_abfs(monster, tx, ty) { - var map = monster.map; - var sx = monster.x; - var sy = monster.y; - var start = 0; - var last = 0; - var queue = []; - var visited = {}; - var start_t = new Date(); - var best = 999999999999; - var theone = null; - var good = false; - function hpush(cx, cy, fr, dir, bad) { - var value = point_distance(cx, cy, tx, ty); - var hash = cx + "|" + cy; - queue[last++] = { x: cx, y: cy, bad: bad, dir: dir }; - visited[hash] = fr; - } - function finalise(current) { - var dx = current.x; - var dy = current.y; - path = [[dx, dy]]; - while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { - dx = visited[dx + "|" + dy].split("|"); - dy = parseInt(dx[1]); - dx = parseInt(dx[0]); - path.push([dx, dy]); - } - path.reverse(); - // path.push([tx,ty]); // Looks bad - dx = path[0][0]; - dy = path[0][1]; - for ( - var i = 1; - i < path.length; - i++ // 1ms at max [17/08/20] - ) { - if (can_amove(map, sx, sy, path[i][0], path[i][1])) { - dx = path[i][0]; - dy = path[i][1]; - } else { - break; - } - } - if (!good && point_distance(sx, sy, dx, dy) < 30) { - return null; - } - server_log([last, dx, dy, mssince(start_t)]); - return [dx, dy]; - } - for (var step = 1; step <= 2; step++) { - if (last) { - break; - } - [ - [sx, sy, 0, 0], - [amap_step, amap_step, 0, amap_step * 1.41], - [amap_step, -amap_step, 1, amap_step * 1.41], - [-amap_step, -amap_step, 2, amap_step * 1.41], - [-amap_step, amap_step, 3, amap_step * 1.41], - [0, amap_step, 4, amap_step], - [0, -amap_step, 5, amap_step], - [amap_step, 0, 6, amap_step], - [-amap_step, 0, 7, amap_step], - ].forEach(function (m) { - var cx = amap_round(sx + m[0] * step); - var cy = amap_round(sy + m[1] * step); - if (amap_data[map][cx + "|" + cy]) { - hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); - } - }); - } - while (start < last) { - if (last - start > 1200 || last > 4000) { - server_log(["queue", "fast_abfs"]); - monster.bpath = (monster.bpath || 0) + 1; - if (theone && monster.bpath < 20) { - return finalise(theone); - } - return null; - } - var current = queue[start++]; - var dist = point_distance(current.x, current.y, tx, ty); - var rnd = Math.random() * 100; - if (dist < 28) { - monster.bpath = 0; - good = true; - return finalise(current); - } - if (dist + current.bad - rnd < best) { - best = dist + current.bad - rnd; - theone = current; - } - [ - [amap_step, amap_step, 0, amap_step * 1.41], - [amap_step, -amap_step, 1, amap_step * 1.41], - [-amap_step, -amap_step, 2, amap_step * 1.41], - [-amap_step, amap_step, 3, amap_step * 1.41], - [0, amap_step, 4, amap_step], - [0, -amap_step, 5, amap_step], - [amap_step, 0, 6, amap_step], - [-amap_step, 0, 7, amap_step], - ].forEach(function (m) { - var cx = current.x + m[0]; - var cy = current.y + m[1]; - if (amap_data[map][cx + "|" + cy] && !visited[cx + "|" + cy]) { - hpush(cx, cy, current.x + "|" + current.y, m[2], current.bad + m[3] + ((current.dir != m[2] && 0.5) || 0)); - } - }); - } - server_log(["no start", "fast_abfs"]); + var map = monster.map; + var sx = monster.x; + var sy = monster.y; + var start = 0; + var last = 0; + var queue = []; + var visited = {}; + var start_t = new Date(); + var best = 999999999999; + var theone = null; + var good = false; + function hpush(cx, cy, fr, dir, bad) { + var value = point_distance(cx, cy, tx, ty); + var hash = cx + "|" + cy; + queue[last++] = { x: cx, y: cy, bad: bad, dir: dir }; + visited[hash] = fr; + } + function finalise(current) { + var dx = current.x; + var dy = current.y; + path = [[dx, dy]]; + while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { + dx = visited[dx + "|" + dy].split("|"); + dy = parseInt(dx[1]); + dx = parseInt(dx[0]); + path.push([dx, dy]); + } + path.reverse(); + // path.push([tx,ty]); // Looks bad + dx = path[0][0]; + dy = path[0][1]; + for ( + var i = 1; + i < path.length; + i++ // 1ms at max [17/08/20] + ) { + if (can_amove(map, sx, sy, path[i][0], path[i][1])) { + dx = path[i][0]; + dy = path[i][1]; + } else { + break; + } + } + if (!good && point_distance(sx, sy, dx, dy) < 30) { + return null; + } + server_log([last, dx, dy, mssince(start_t)]); + return [dx, dy]; + } + for (var step = 1; step <= 2; step++) { + if (last) { + break; + } + [ + [sx, sy, 0, 0], + [amap_step, amap_step, 0, amap_step * 1.41], + [amap_step, -amap_step, 1, amap_step * 1.41], + [-amap_step, -amap_step, 2, amap_step * 1.41], + [-amap_step, amap_step, 3, amap_step * 1.41], + [0, amap_step, 4, amap_step], + [0, -amap_step, 5, amap_step], + [amap_step, 0, 6, amap_step], + [-amap_step, 0, 7, amap_step], + ].forEach(function (m) { + var cx = amap_round(sx + m[0] * step); + var cy = amap_round(sy + m[1] * step); + if (amap_data[map][cx + "|" + cy]) { + hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); + } + }); + } + while (start < last) { + if (last - start > 1200 || last > 4000) { + server_log(["queue", "fast_abfs"]); + monster.bpath = (monster.bpath || 0) + 1; + if (theone && monster.bpath < 20) { + return finalise(theone); + } + return null; + } + var current = queue[start++]; + var dist = point_distance(current.x, current.y, tx, ty); + var rnd = Math.random() * 100; + if (dist < 28) { + monster.bpath = 0; + good = true; + return finalise(current); + } + if (dist + current.bad - rnd < best) { + best = dist + current.bad - rnd; + theone = current; + } + [ + [amap_step, amap_step, 0, amap_step * 1.41], + [amap_step, -amap_step, 1, amap_step * 1.41], + [-amap_step, -amap_step, 2, amap_step * 1.41], + [-amap_step, amap_step, 3, amap_step * 1.41], + [0, amap_step, 4, amap_step], + [0, -amap_step, 5, amap_step], + [amap_step, 0, 6, amap_step], + [-amap_step, 0, 7, amap_step], + ].forEach(function (m) { + var cx = current.x + m[0]; + var cy = current.y + m[1]; + if (amap_data[map][cx + "|" + cy] && !visited[cx + "|" + cy]) { + hpush(cx, cy, current.x + "|" + current.y, m[2], current.bad + m[3] + ((current.dir != m[2] && 0.5) || 0)); + } + }); + } + server_log(["no start", "fast_abfs"]); } function add_call_cost(socket, num, method) { - if (!socket) { - socket = 1; - } - if (is_number(socket)) { - num = socket; - socket = current_socket; - } - if (socket.socket) { - socket = socket.socket; - } // player - if (!num) { - num = 1; - } - if (!method) { - method = ls_method; - } + if (!socket) { + socket = 1; + } + if (is_number(socket)) { + num = socket; + socket = current_socket; + } + if (socket.socket) { + socket = socket.socket; + } // player + if (!num) { + num = 1; + } + if (!method) { + method = ls_method; + } - while (socket.calls.length && mssince(socket.calls[0][0]) > 4000) { - socket.calls.shift(); - } // #TODO: make one operation [27/02/23] + while (socket.calls.length && mssince(socket.calls[0][0]) > 4000) { + socket.calls.shift(); + } // #TODO: make one operation [27/02/23] - if (socket.calls.length && socket.calls[socket.calls.length - 1][1] == method && num != -1) { - socket.calls[socket.calls.length - 1][2] += num; - } else { - socket.calls.push([new Date(), method, num == -1 ? 1 : num]); - } + if (socket.calls.length && socket.calls[socket.calls.length - 1][1] == method && num != -1) { + socket.calls[socket.calls.length - 1][2] += num; + } else { + socket.calls.push([new Date(), method, num == -1 ? 1 : num]); + } } function reduce_call_cost(socket, num) { - if (!socket) { - socket = 1; - } - if (is_number(socket)) { - num = socket; - socket = current_socket; - } - if (socket.socket) { - socket = socket.socket; - } // player - if (!num) { - num = 1; - } + if (!socket) { + socket = 1; + } + if (is_number(socket)) { + num = socket; + socket = current_socket; + } + if (socket.socket) { + socket = socket.socket; + } // player + if (!num) { + num = 1; + } - if (socket.calls.length && socket.calls[socket.calls.length - 1][1] == ls_method) { - socket.calls[socket.calls.length - 1][2] -= num; - if (socket.calls[socket.calls.length - 1][2] <= 0) { - socket.calls.pop(); - } - } + if (socket.calls.length && socket.calls[socket.calls.length - 1][1] == ls_method) { + socket.calls[socket.calls.length - 1][2] -= num; + if (socket.calls[socket.calls.length - 1][2] <= 0) { + socket.calls.pop(); + } + } } function get_call_cost(socket) { - if (!socket) { - socket = current_socket; - } - if (socket.socket) { - socket = socket.socket; - } // player + if (!socket) { + socket = current_socket; + } + if (socket.socket) { + socket = socket.socket; + } // player - while (socket.calls.length && mssince(socket.calls[0][0]) > 4000) { - socket.calls.shift(); - } // #TODO: make one operation [27/02/23] + while (socket.calls.length && mssince(socket.calls[0][0]) > 4000) { + socket.calls.shift(); + } // #TODO: make one operation [27/02/23] - var cost = 0; - socket.calls.forEach(function (c) { - cost += c[2]; - }); - return cost; + var cost = 0; + socket.calls.forEach(function (c) { + cost += c[2]; + }); + return cost; } function set_direction() {} // compatibility function symmetricDecrypt(input, key, checkHmac) { - var aesIv = crypto.createDecipheriv("aes-256-ecb", key, ""); - aesIv.setAutoPadding(false); - aesIv.end(input.slice(0, 16)); - var iv = aesIv.read(); + var aesIv = crypto.createDecipheriv("aes-256-ecb", key, ""); + aesIv.setAutoPadding(false); + aesIv.end(input.slice(0, 16)); + var iv = aesIv.read(); - var aesData = crypto.createDecipheriv("aes-256-cbc", key, iv); - aesData.end(input.slice(16)); - var plaintext = aesData.read(); + var aesData = crypto.createDecipheriv("aes-256-cbc", key, iv); + aesData.end(input.slice(16)); + var plaintext = aesData.read(); - if (checkHmac) { - // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC - var remotePartialHmac = iv.slice(0, iv.length - 3); - var random = iv.slice(iv.length - 3, iv.length); - var hmac = crypto.createHmac("sha1", key.slice(0, 16)); - hmac.update(random); - hmac.update(plaintext); - if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { - throw new Error("Received invalid HMAC from remote host."); - } - } + if (checkHmac) { + // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC + var remotePartialHmac = iv.slice(0, iv.length - 3); + var random = iv.slice(iv.length - 3, iv.length); + var hmac = crypto.createHmac("sha1", key.slice(0, 16)); + hmac.update(random); + hmac.update(plaintext); + if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { + throw new Error("Received invalid HMAC from remote host."); + } + } - return plaintext; + return plaintext; } function parseAppTicket(ticket) { - // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl + // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl - // console.log(ticket); - if (!ByteBuffer.isByteBuffer(ticket)) { - ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); - } + // console.log(ticket); + if (!ByteBuffer.isByteBuffer(ticket)) { + ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); + } - let details = {}; + let details = {}; - try { - let initialLength = ticket.readUint32(); - // console.log(initialLength); - if (initialLength == 20) { - // This is a full appticket, with a GC token and session header (in addition to ownership ticket) - details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation + try { + let initialLength = ticket.readUint32(); + // console.log(initialLength); + if (initialLength == 20) { + // This is a full appticket, with a GC token and session header (in addition to ownership ticket) + details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation - details.gcToken = ticket.readUint64().toString(); - //details.steamID = new SteamID(ticket.readUint64().toString()); - ticket.skip(8); // the SteamID gets read later on - details.tokenGenerated = new Date(ticket.readUint32() * 1000); + details.gcToken = ticket.readUint64().toString(); + //details.steamID = new SteamID(ticket.readUint64().toString()); + ticket.skip(8); // the SteamID gets read later on + details.tokenGenerated = new Date(ticket.readUint32() * 1000); - if (ticket.readUint32() != 24) { - // SESSIONHEADER should be 24 bytes. - return null; - } + if (ticket.readUint32() != 24) { + // SESSIONHEADER should be 24 bytes. + return null; + } - ticket.skip(8); // unknown 1 and unknown 2 - details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); - ticket.skip(4); // filler - details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms - details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to + ticket.skip(8); // unknown 1 and unknown 2 + details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); + ticket.skip(4); // filler + details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms + details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to - if (ticket.readUint32() + ticket.offset != ticket.limit) { - // OWNERSHIPSECTIONWITHSIGNATURE sectlength - return null; - } - } else { - ticket.skip(-4); - } + if (ticket.readUint32() + ticket.offset != ticket.limit) { + // OWNERSHIPSECTIONWITHSIGNATURE sectlength + return null; + } + } else { + ticket.skip(-4); + } - // Start reading the ownership ticket - let ownershipTicketOffset = ticket.offset; - let ownershipTicketLength = ticket.readUint32(); // including itself, for some reason - if ( - ownershipTicketOffset + ownershipTicketLength != ticket.limit && - ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit - ) { - return null; - } + // Start reading the ownership ticket + let ownershipTicketOffset = ticket.offset; + let ownershipTicketLength = ticket.readUint32(); // including itself, for some reason + if ( + ownershipTicketOffset + ownershipTicketLength != ticket.limit && + ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit + ) { + return null; + } - let i; - let j; - let dlc; + let i; + let j; + let dlc; - details.version = ticket.readUint32(); - details.steamID = ticket.readUint64().toString(); - details.appID = ticket.readUint32(); - details.ownershipTicketExternalIP = ticket.readUint32(); - details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( - details.ownershipFlags = ticket.readUint32(); - details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); - details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); - details.licenses = []; - // return details; + details.version = ticket.readUint32(); + details.steamID = ticket.readUint64().toString(); + details.appID = ticket.readUint32(); + details.ownershipTicketExternalIP = ticket.readUint32(); + details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( + details.ownershipFlags = ticket.readUint32(); + details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); + details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); + details.licenses = []; + // return details; - let licenseCount = ticket.readUint16(); - for (i = 0; i < licenseCount; i++) { - details.licenses.push(ticket.readUint32()); - } + let licenseCount = ticket.readUint16(); + for (i = 0; i < licenseCount; i++) { + details.licenses.push(ticket.readUint32()); + } - details.dlc = []; + details.dlc = []; - let dlcCount = ticket.readUint16(); - for (i = 0; i < dlcCount; i++) { - dlc = {}; - dlc.appID = ticket.readUint32(); - dlc.licenses = []; + let dlcCount = ticket.readUint16(); + for (i = 0; i < dlcCount; i++) { + dlc = {}; + dlc.appID = ticket.readUint32(); + dlc.licenses = []; - licenseCount = ticket.readUint16(); + licenseCount = ticket.readUint16(); - for (j = 0; j < licenseCount; j++) { - dlc.licenses.push(ticket.readUint32()); - } + for (j = 0; j < licenseCount; j++) { + dlc.licenses.push(ticket.readUint32()); + } - details.dlc.push(dlc); - } + details.dlc.push(dlc); + } - ticket.readUint16(); // reserved - if (ticket.offset + 128 == ticket.limit) { - // Has signature - details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); - } + ticket.readUint16(); // reserved + if (ticket.offset + 128 == ticket.limit) { + // Has signature + details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); + } - let date = new Date(); - details.isExpired = details.ownershipTicketExpires < date; - details.hasValidSignature = - !!details.signature && - SteamCrypto.verifySignature( - ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), - details.signature, - ); - details.isValid = !details.isExpired && (!details.signature || details.hasValidSignature); - } catch (ex) { - console.log("parseAppTicket: " + ex); - return details; - return null; // not a valid ticket - } + let date = new Date(); + details.isExpired = details.ownershipTicketExpires < date; + details.hasValidSignature = + !!details.signature && + SteamCrypto.verifySignature( + ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), + details.signature, + ); + details.isValid = !details.isExpired && (!details.signature || details.hasValidSignature); + } catch (ex) { + console.log("parseAppTicket: " + ex); + return details; + return null; // not a valid ticket + } - return details; + return details; } var proto = { - nested: { - EncryptedAppTicket: { - fields: { - ticketVersionNo: { type: "uint32", id: 1 }, - crcEncryptedticket: { type: "uint32", id: 2 }, - cbEncrypteduserdata: { type: "uint32", id: 3 }, - cbEncryptedAppownershipticket: { type: "uint32", id: 4 }, - encryptedTicket: { type: "bytes", id: 5 }, - }, - }, - }, + nested: { + EncryptedAppTicket: { + fields: { + ticketVersionNo: { type: "uint32", id: 1 }, + crcEncryptedticket: { type: "uint32", id: 2 }, + cbEncrypteduserdata: { type: "uint32", id: 3 }, + cbEncryptedAppownershipticket: { type: "uint32", id: 4 }, + encryptedTicket: { type: "bytes", id: 5 }, + }, + }, + }, }; var proto_root = protobuf.Root.fromJSON(proto); var EncryptedAppTicket = proto_root.lookupType("EncryptedAppTicket"); function getClientIp(req) { - //https://github.com/pbojinov/request-ip/blob/master/index.js - // NOTE: Back in the day, we had a system called ipass - for some weird and unexplainable reason, no Node server, mainly Socket.io, can track the current IP of a client - // So the clients needed to ping the game every 40 seconds, at a secondary, http-only handler just to get their IP - and, again, for some extremely weird reason, Node servers, don't let you just easily disconnect a client - // Rather than auto Keep-Alive, so it had a hacky disconnect routine - all in all, decided to move this system to App Engine, while the SSL refactoring was happening [17/11/18] + //https://github.com/pbojinov/request-ip/blob/master/index.js + // NOTE: Back in the day, we had a system called ipass - for some weird and unexplainable reason, no Node server, mainly Socket.io, can track the current IP of a client + // So the clients needed to ping the game every 40 seconds, at a secondary, http-only handler just to get their IP - and, again, for some extremely weird reason, Node servers, don't let you just easily disconnect a client + // Rather than auto Keep-Alive, so it had a hacky disconnect routine - all in all, decided to move this system to App Engine, while the SSL refactoring was happening [17/11/18] - // the ipAddress we return - var ipAddress; + // the ipAddress we return + var ipAddress; - // workaround to get real client IP - // most likely because our app will be behind a [reverse] proxy or load balancer - var clientIp = req.headers["x-client-ip"]; - var forwardedForAlt = req.headers["x-forwarded-for"]; - var realIp = req.headers["x-real-ip"]; + // workaround to get real client IP + // most likely because our app will be behind a [reverse] proxy or load balancer + var clientIp = req.headers["x-client-ip"]; + var forwardedForAlt = req.headers["x-forwarded-for"]; + var realIp = req.headers["x-real-ip"]; - // more obsure ones below - var clusterClientIp = req.headers["x-cluster-client-ip"]; - var forwardedAlt = req.headers["x-forwarded"]; - var forwardedFor = req.headers["forwarded-for"]; - var forwarded = req.headers["forwarded"]; + // more obsure ones below + var clusterClientIp = req.headers["x-cluster-client-ip"]; + var forwardedAlt = req.headers["x-forwarded"]; + var forwardedFor = req.headers["forwarded-for"]; + var forwarded = req.headers["forwarded"]; - // remote address check - var reqConnectionRemoteAddress = req.connection ? req.connection.remoteAddress : null; - var reqSocketRemoteAddress = req.socket ? req.socket.remoteAddress : null; - var reqConnectionSocketRemoteAddress = - req.connection && req.connection.socket ? req.connection.socket.remoteAddress : null; - var reqInfoRemoteAddress = req.info ? req.info.remoteAddress : null; + // remote address check + var reqConnectionRemoteAddress = req.connection ? req.connection.remoteAddress : null; + var reqSocketRemoteAddress = req.socket ? req.socket.remoteAddress : null; + var reqConnectionSocketRemoteAddress = + req.connection && req.connection.socket ? req.connection.socket.remoteAddress : null; + var reqInfoRemoteAddress = req.info ? req.info.remoteAddress : null; - // x-client-ip - if (clientIp) { - ipAddress = clientIp; - } + // x-client-ip + if (clientIp) { + ipAddress = clientIp; + } - // x-forwarded-for - // (typically when your node app is behind a load-balancer (eg. AWS ELB) or proxy) - else if (forwardedForAlt) { - // x-forwarded-for may return multiple IP addresses in the format: - // "client IP, proxy 1 IP, proxy 2 IP" - // Therefore, the right-most IP address is the IP address of the most recent proxy - // and the left-most IP address is the IP address of the originating client. - // source: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html - var forwardedIps = forwardedForAlt.split(","); - ipAddress = forwardedIps[0]; - } + // x-forwarded-for + // (typically when your node app is behind a load-balancer (eg. AWS ELB) or proxy) + else if (forwardedForAlt) { + // x-forwarded-for may return multiple IP addresses in the format: + // "client IP, proxy 1 IP, proxy 2 IP" + // Therefore, the right-most IP address is the IP address of the most recent proxy + // and the left-most IP address is the IP address of the originating client. + // source: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html + var forwardedIps = forwardedForAlt.split(","); + ipAddress = forwardedIps[0]; + } - // x-real-ip - // (default nginx proxy/fcgi) - else if (realIp) { - // alternative to x-forwarded-for, used by some proxies - ipAddress = realIp; - } + // x-real-ip + // (default nginx proxy/fcgi) + else if (realIp) { + // alternative to x-forwarded-for, used by some proxies + ipAddress = realIp; + } - // x-cluster-client-ip - // (Rackspace LB and Riverbed's Stingray) - // http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address - // https://splash.riverbed.com/docs/DOC-1926 - else if (clusterClientIp) { - ipAddress = clusterClientIp; - } + // x-cluster-client-ip + // (Rackspace LB and Riverbed's Stingray) + // http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address + // https://splash.riverbed.com/docs/DOC-1926 + else if (clusterClientIp) { + ipAddress = clusterClientIp; + } - // x-forwarded - else if (forwardedAlt) { - ipAddress = forwardedAlt; - } + // x-forwarded + else if (forwardedAlt) { + ipAddress = forwardedAlt; + } - // forwarded-for - else if (forwardedFor) { - ipAddress = forwardedFor; - } + // forwarded-for + else if (forwardedFor) { + ipAddress = forwardedFor; + } - // forwarded - else if (forwarded) { - ipAddress = forwarded; - } + // forwarded + else if (forwarded) { + ipAddress = forwarded; + } - // remote address checks - else if (reqConnectionRemoteAddress) { - ipAddress = reqConnectionRemoteAddress; - } else if (reqSocketRemoteAddress) { - ipAddress = reqSocketRemoteAddress; - } else if (reqConnectionSocketRemoteAddress) { - ipAddress = reqConnectionSocketRemoteAddress; - } else if (reqInfoRemoteAddress) { - ipAddress = reqInfoRemoteAddress; - } + // remote address checks + else if (reqConnectionRemoteAddress) { + ipAddress = reqConnectionRemoteAddress; + } else if (reqSocketRemoteAddress) { + ipAddress = reqSocketRemoteAddress; + } else if (reqConnectionSocketRemoteAddress) { + ipAddress = reqConnectionSocketRemoteAddress; + } else if (reqInfoRemoteAddress) { + ipAddress = reqInfoRemoteAddress; + } - // return null if we cannot find an address - else { - ipAddress = null; - } + // return null if we cannot find an address + else { + ipAddress = null; + } - return ipAddress; + return ipAddress; } function deduct_gender(player) { - if (player.cx && player.cx.head && player.cx.head[0] == "f") { - return "female"; - } - return "male"; + if (player.cx && player.cx.head && player.cx.head[0] == "f") { + return "female"; + } + return "male"; } function safe_search(obj, phrase) { - obj = JSON.parse(safe_stringify(obj)); - for (l1 in obj) { - var c1 = obj[l1]; - if ((c1 && is_string(c1) && c1.indexOf(phrase) != -1) || l1.indexOf(phrase) != -1) { - console.log(l1 + " "); - } - if (!is_object(c1)) { - continue; - } - for (l2 in c1) { - var c2 = c1[l2]; - if ((c2 && is_string(c2) && c2.indexOf(phrase) != -1) || l2.indexOf(phrase) != -1) { - console.log(l1 + " " + l2); - } - if (!is_object(c2)) { - continue; - } - for (l3 in c2) { - var c3 = c2[l3]; - if ((c3 && is_string(c3) && c3.indexOf(phrase) != -1) || l3.indexOf(phrase) != -1) { - console.log(l1 + " " + l2 + " " + l3); - } - if (!is_object(c3)) { - continue; - } - for (l4 in c3) { - var c4 = c3[l4]; - if ((c4 && is_string(c4) && c4.indexOf(phrase) != -1) || l4.indexOf(phrase) != -1) { - console.log(l1 + " " + l2 + " " + l3 + " " + l4); - } - if (!is_object(c4)) { - continue; - } - for (l5 in c4) { - var c5 = c4[l5]; - if ((c5 && is_string(c5) && c5.indexOf(phrase) != -1) || l5.indexOf(phrase) != -1) { - console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5); - } - if (!is_object(c5)) { - continue; - } - for (l6 in c5) { - var c6 = c5[l6]; - if ((c6 && is_string(c6) && c6.indexOf(phrase) != -1) || l6.indexOf(phrase) != -1) { - console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6); - } - if (!is_object(c6)) { - continue; - } - for (l7 in c6) { - var c7 = c6[l7]; - if ((c7 && is_string(c7) && c7.indexOf(phrase) != -1) || l7.indexOf(phrase) != -1) { - console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7); - } - if (!is_object(c7)) { - continue; - } - for (l8 in c7) { - var c8 = c7[l8]; - if ((c8 && is_string(c8) && c8.indexOf(phrase) != -1) || l8.indexOf(phrase) != -1) { - console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7 + " " + l8); - } - if (!is_object(c8)) { - continue; - } - for (l9 in c8) { - var c9 = c8[l9]; - if ((c9 && is_string(c9) && c9.indexOf(phrase) != -1) || l9.indexOf(phrase) != -1) { - console.log( - l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7 + " " + l8 + " " + l9, - ); - } - if (!is_object(c9)) { - continue; - } - for (l10 in c9) { - var c10 = c9[l10]; - if ((c10 && is_string(c10) && c10.indexOf(phrase) != -1) || l10.indexOf(phrase) != -1) { - console.log( - l1 + - " " + - l2 + - " " + - l3 + - " " + - l4 + - " " + - l5 + - " " + - l6 + - " " + - l7 + - " " + - l8 + - " " + - l9 + - " " + - l10, - ); - } - if (!is_object(c10)) { - continue; - } - } - } - } - } - } - } - } - } - } - } + obj = JSON.parse(safe_stringify(obj)); + for (l1 in obj) { + var c1 = obj[l1]; + if ((c1 && is_string(c1) && c1.indexOf(phrase) != -1) || l1.indexOf(phrase) != -1) { + console.log(l1 + " "); + } + if (!is_object(c1)) { + continue; + } + for (l2 in c1) { + var c2 = c1[l2]; + if ((c2 && is_string(c2) && c2.indexOf(phrase) != -1) || l2.indexOf(phrase) != -1) { + console.log(l1 + " " + l2); + } + if (!is_object(c2)) { + continue; + } + for (l3 in c2) { + var c3 = c2[l3]; + if ((c3 && is_string(c3) && c3.indexOf(phrase) != -1) || l3.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3); + } + if (!is_object(c3)) { + continue; + } + for (l4 in c3) { + var c4 = c3[l4]; + if ((c4 && is_string(c4) && c4.indexOf(phrase) != -1) || l4.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4); + } + if (!is_object(c4)) { + continue; + } + for (l5 in c4) { + var c5 = c4[l5]; + if ((c5 && is_string(c5) && c5.indexOf(phrase) != -1) || l5.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5); + } + if (!is_object(c5)) { + continue; + } + for (l6 in c5) { + var c6 = c5[l6]; + if ((c6 && is_string(c6) && c6.indexOf(phrase) != -1) || l6.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6); + } + if (!is_object(c6)) { + continue; + } + for (l7 in c6) { + var c7 = c6[l7]; + if ((c7 && is_string(c7) && c7.indexOf(phrase) != -1) || l7.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7); + } + if (!is_object(c7)) { + continue; + } + for (l8 in c7) { + var c8 = c7[l8]; + if ((c8 && is_string(c8) && c8.indexOf(phrase) != -1) || l8.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7 + " " + l8); + } + if (!is_object(c8)) { + continue; + } + for (l9 in c8) { + var c9 = c8[l9]; + if ((c9 && is_string(c9) && c9.indexOf(phrase) != -1) || l9.indexOf(phrase) != -1) { + console.log( + l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7 + " " + l8 + " " + l9, + ); + } + if (!is_object(c9)) { + continue; + } + for (l10 in c9) { + var c10 = c9[l10]; + if ((c10 && is_string(c10) && c10.indexOf(phrase) != -1) || l10.indexOf(phrase) != -1) { + console.log( + l1 + + " " + + l2 + + " " + + l3 + + " " + + l4 + + " " + + l5 + + " " + + l6 + + " " + + l7 + + " " + + l8 + + " " + + l9 + + " " + + l10, + ); + } + if (!is_object(c10)) { + continue; + } + } + } + } + } + } + } + } + } + } + } } /* eslint-disable no-misleading-character-class,no-control-regex */ function strip_string(str) { - var regexSymbolWithCombiningMarks = - /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g; - var regexLineBreakCombiningMarks = - /[\0-\x08\x0E-\x1F\x7F-\x84\x86-\x9F\u0300-\u034E\u0350-\u035B\u0363-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u200C\u200E\u200F\u202A-\u202E\u2066-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3035\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFFF9-\uFFFB]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E\uDCA0-\uDCA3]|\uD834[\uDD65-\uDD69\uDD6D-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A]|\uDB40[\uDC01\uDC20-\uDC7F\uDD00-\uDDEF]/g; - // https://github.com/mathiasbynens/strip-combining-marks/blob/master/strip-combining-marks.js - return str - .replace(regexLineBreakCombiningMarks, "") - .replace(regexSymbolWithCombiningMarks, "$1") - .replace(/[^\p{L}\p{N}\p{S}\p{P}\p{Z}]/gu, ""); // https://stackoverflow.com/a/63464318/914546 + var regexSymbolWithCombiningMarks = + /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g; + var regexLineBreakCombiningMarks = + /[\0-\x08\x0E-\x1F\x7F-\x84\x86-\x9F\u0300-\u034E\u0350-\u035B\u0363-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u200C\u200E\u200F\u202A-\u202E\u2066-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3035\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFFF9-\uFFFB]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E\uDCA0-\uDCA3]|\uD834[\uDD65-\uDD69\uDD6D-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A]|\uDB40[\uDC01\uDC20-\uDC7F\uDD00-\uDDEF]/g; + // https://github.com/mathiasbynens/strip-combining-marks/blob/master/strip-combining-marks.js + return str + .replace(regexLineBreakCombiningMarks, "") + .replace(regexSymbolWithCombiningMarks, "$1") + .replace(/[^\p{L}\p{N}\p{S}\p{P}\p{Z}]/gu, ""); // https://stackoverflow.com/a/63464318/914546 } /* eslint-enable no-misleading-character-class,no-control-regex */ @@ -5074,259 +5074,259 @@ function strip_string(str) { var hardcore_done = false; function hardcore_loop() { - if (hardcore_done) { - return; - } - var m = 0; - for (var id in players) { - if (players[id].level > m) { - E.rewards.leader = players[id].name; - m = players[id].level; - } - } - if (E.minutes) { - E.minutes -= 1; - } else { - hardcore_done = true; - send_hardcore_rewards(); - shutdown_routine(); - } - broadcast("hardcore_info", { E: E }); + if (hardcore_done) { + return; + } + var m = 0; + for (var id in players) { + if (players[id].level > m) { + E.rewards.leader = players[id].name; + m = players[id].level; + } + } + if (E.minutes) { + E.minutes -= 1; + } else { + hardcore_done = true; + send_hardcore_rewards(); + shutdown_routine(); + } + broadcast("hardcore_info", { E: E }); } // #ACHIEVEMENTS function send_hardcore_rewards() { - rd = [ - ["leader", "Leadership", { name: "xbox", q: 1 }], - ["item8", "First +8 Item", { name: "armorbox", q: 10 }], - ["item9", "First +9 Item", { name: "weaponbox", q: 10 }], - ["item10", "First +X Item", { name: "armorbox", q: 100 }], - ["item11", "First +Y Item", { name: "armorbox", q: 100 }], - ["item12", "First +Z Item", { name: "scroll3", q: 1 }], - ["accessory5", "First +V Accessory", { name: "armorbox", q: 50 }], - ["accessory6", "First +S Accessory", { name: "cscroll3", q: 1 }], - ["first_warrior_70", "First Warrior to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_paladin_70", "First Paladin to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_priest_70", "First Priest to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_mage_70", "First Mage to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_rogue_70", "First Rogue to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_ranger_70", "First Ranger to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_franky", "First Franky Kill", { name: "brownegg", q: 1 }], - ["first_stompy", "First Stompy Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_ent", "First Ent Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_wabbit", "First Wabbit Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_fvampire", "First Ms.Vampire Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_mvampire", "First Mr.Vampire Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_skeletor", "First Skeletor Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ["first_goo", "First Goo Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], - ]; - rd.forEach(function (r) { - if (E.rewards[r[0]]) { - appengine_call("send_mail", { - subject: "HARDCORE: Congratulations!", - message: "Reward for " + r[1], - type: "system", - rid: randomStr(50), - to: E.rewards[r[0]], - item: JSON.stringify(r[2]), - retries: 8, - }); - } - }); - for (var id in players) { - save_player(players[id]); - } - var sent = {}; - for (var id in P) { - var p = P[id]; - if (p.auth_id && p.level >= 60 && !sent[p.auth_id]) { - sent[p.auth_id] = true; - appengine_call("send_mail", { - subject: "HARDCORE: Congratulations!", - message: "Reward for Participation", - type: "system", - rid: randomStr(50), - to: p.name, - item: JSON.stringify({ name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }), - }); - } - } + rd = [ + ["leader", "Leadership", { name: "xbox", q: 1 }], + ["item8", "First +8 Item", { name: "armorbox", q: 10 }], + ["item9", "First +9 Item", { name: "weaponbox", q: 10 }], + ["item10", "First +X Item", { name: "armorbox", q: 100 }], + ["item11", "First +Y Item", { name: "armorbox", q: 100 }], + ["item12", "First +Z Item", { name: "scroll3", q: 1 }], + ["accessory5", "First +V Accessory", { name: "armorbox", q: 50 }], + ["accessory6", "First +S Accessory", { name: "cscroll3", q: 1 }], + ["first_warrior_70", "First Warrior to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_paladin_70", "First Paladin to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_priest_70", "First Priest to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_mage_70", "First Mage to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_rogue_70", "First Rogue to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_ranger_70", "First Ranger to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_franky", "First Franky Kill", { name: "brownegg", q: 1 }], + ["first_stompy", "First Stompy Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_ent", "First Ent Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_wabbit", "First Wabbit Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_fvampire", "First Ms.Vampire Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_mvampire", "First Mr.Vampire Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_skeletor", "First Skeletor Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_goo", "First Goo Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ]; + rd.forEach(function (r) { + if (E.rewards[r[0]]) { + appengine_call("send_mail", { + subject: "HARDCORE: Congratulations!", + message: "Reward for " + r[1], + type: "system", + rid: randomStr(50), + to: E.rewards[r[0]], + item: JSON.stringify(r[2]), + retries: 8, + }); + } + }); + for (var id in players) { + save_player(players[id]); + } + var sent = {}; + for (var id in P) { + var p = P[id]; + if (p.auth_id && p.level >= 60 && !sent[p.auth_id]) { + sent[p.auth_id] = true; + appengine_call("send_mail", { + subject: "HARDCORE: Congratulations!", + message: "Reward for Participation", + type: "system", + rid: randomStr(50), + to: p.name, + item: JSON.stringify({ name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }), + }); + } + } } function add_achievement(player, name) { - player.p.achievements[name] = (player.p.achievements[name] || 0) + 1; - player.socket.emit("achievement_success", { name: name }); + player.p.achievements[name] = (player.p.achievements[name] || 0) + 1; + player.socket.emit("achievement_success", { name: name }); } function item_achievement_increment(player, item, ach, amount) { - if (!G.achievements[ach] || !item) { - return; - } - if (item.ach != ach || !item.acc) { - item.acc = 0; - } - var needed = G.achievements[ach].count; - var old = item.acc; - var rr = G.achievements[ach].rr || 1; - item.ach = ach; - item.acc += amount || 1; - if (item.acc < needed) { - if (parseInt(old / rr) != parseInt(item.acc / rr)) { - player.socket.emit("achievement_progress", { name: ach, count: item.acc, needed: needed }); - } - } else { - add_item_property(item, G.achievements[ach].title); - delete item.ach; - delete item.acc; - add_achievement(player, ach); - cache_player_items(player); - resend(player, "u+cid+reopen"); - } + if (!G.achievements[ach] || !item) { + return; + } + if (item.ach != ach || !item.acc) { + item.acc = 0; + } + var needed = G.achievements[ach].count; + var old = item.acc; + var rr = G.achievements[ach].rr || 1; + item.ach = ach; + item.acc += amount || 1; + if (item.acc < needed) { + if (parseInt(old / rr) != parseInt(item.acc / rr)) { + player.socket.emit("achievement_progress", { name: ach, count: item.acc, needed: needed }); + } + } else { + add_item_property(item, G.achievements[ach].title); + delete item.ach; + delete item.acc; + add_achievement(player, ach); + cache_player_items(player); + resend(player, "u+cid+reopen"); + } } function achievement_logic_monster_damage(player, monster, damage) { - try { - if (monster.type == "grinch") { - item_achievement_increment(player, player.slots.cape, "festive", damage); - } - } catch (e) { - console.log("#A: " + e); - } + try { + if (monster.type == "grinch") { + item_achievement_increment(player, player.slots.cape, "festive", damage); + } + } catch (e) { + console.log("#A: " + e); + } } function achievement_logic_monster_kill(player, monster) { - try { - if (gameplay == "hardcore") { - var announce = false; - ["ent", "stompy", "franky", "fvampire", "mvampire", "skeletor", "goo"].forEach(function (m) { - if (monster.type == m && !E.rewards["first_" + m] && E.minutes) { - E.rewards["first_" + m] = player.name; - announce = true; - } - }); - if (announce) { - broadcast("hardcore_info", { E: E, achiever: player.name }); - } - } else { - } - } catch (e) { - console.log("#A: " + e); - } + try { + if (gameplay == "hardcore") { + var announce = false; + ["ent", "stompy", "franky", "fvampire", "mvampire", "skeletor", "goo"].forEach(function (m) { + if (monster.type == m && !E.rewards["first_" + m] && E.minutes) { + E.rewards["first_" + m] = player.name; + announce = true; + } + }); + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); + } } function achievement_logic_level(player) { - try { - if (gameplay == "hardcore") { - var announce = false; - ["warrior", "paladin", "priest", "mage", "ranger", "rogue", "wabbit"].forEach(function (c) { - if (player.type == c && player.level >= 70 && !E.rewards["first_" + c + "_70"] && E.minutes) { - E.rewards["first_" + c + "_70"] = player.name; - announce = true; - } - }); - if (announce) { - broadcast("hardcore_info", { E: E, achiever: player.name }); - } - } else { - } - } catch (e) { - console.log("#A: " + e); - } + try { + if (gameplay == "hardcore") { + var announce = false; + ["warrior", "paladin", "priest", "mage", "ranger", "rogue", "wabbit"].forEach(function (c) { + if (player.type == c && player.level >= 70 && !E.rewards["first_" + c + "_70"] && E.minutes) { + E.rewards["first_" + c + "_70"] = player.name; + announce = true; + } + }); + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); + } } function achievement_logic_compound_success(player, item) { - try { - if (gameplay == "hardcore") { - var announce = false; - if (item.level == 5 && !E.rewards.accessory5 && E.minutes) { - E.rewards.accessory5 = player.name; - announce = true; - } - if (item.level == 6 && !E.rewards.accessory6 && E.minutes) { - E.rewards.accessory6 = player.name; - announce = true; - } - if (announce) { - broadcast("hardcore_info", { E: E, achiever: player.name }); - } - } else { - } - } catch (e) { - console.log("#A: " + e); - } + try { + if (gameplay == "hardcore") { + var announce = false; + if (item.level == 5 && !E.rewards.accessory5 && E.minutes) { + E.rewards.accessory5 = player.name; + announce = true; + } + if (item.level == 6 && !E.rewards.accessory6 && E.minutes) { + E.rewards.accessory6 = player.name; + announce = true; + } + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); + } } function achievement_logic_upgrade_success(player, item) { - try { - if (gameplay == "hardcore") { - var announce = false; - if (item.level == 8 && !E.rewards.item8 && E.minutes) { - E.rewards.item8 = player.name; - announce = true; - } - if (item.level == 9 && !E.rewards.item9 && E.minutes) { - E.rewards.item9 = player.name; - announce = true; - } - if (item.level == 10 && !E.rewards.item10 && E.minutes) { - E.rewards.item10 = player.name; - announce = true; - } - if (item.level == 11 && !E.rewards.item11 && E.minutes) { - E.rewards.item11 = player.name; - announce = true; - } - if (item.level == 12 && !E.rewards.item12 && E.minutes) { - E.rewards.item12 = player.name; - announce = true; - } - if (announce) { - broadcast("hardcore_info", { E: E, achiever: player.name }); - } - } else { - } - } catch (e) { - console.log("#A: " + e); - } + try { + if (gameplay == "hardcore") { + var announce = false; + if (item.level == 8 && !E.rewards.item8 && E.minutes) { + E.rewards.item8 = player.name; + announce = true; + } + if (item.level == 9 && !E.rewards.item9 && E.minutes) { + E.rewards.item9 = player.name; + announce = true; + } + if (item.level == 10 && !E.rewards.item10 && E.minutes) { + E.rewards.item10 = player.name; + announce = true; + } + if (item.level == 11 && !E.rewards.item11 && E.minutes) { + E.rewards.item11 = player.name; + announce = true; + } + if (item.level == 12 && !E.rewards.item12 && E.minutes) { + E.rewards.item12 = player.name; + announce = true; + } + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); + } } function achievement_logic_monster_last_hit(player, monster) { - try { - if (server.shutdown) { - return; - } - if (player.slots.mainhand) { - delete player.slots.mainhand.ach; - } - } catch (e) { - console.log("#A: " + e); - } + try { + if (server.shutdown) { + return; + } + if (player.slots.mainhand) { + delete player.slots.mainhand.ach; + } + } catch (e) { + console.log("#A: " + e); + } } function achievement_logic_monster_hit(monster, player, attack) { - try { - if (monster.type == "stompy" && !monster.target) { - item_achievement_increment(player, player.slots.helmet, "stomped"); - } else if (monster.type != "stompy" && player.slots.helmet) { - delete player.slots.helmet.ach; - } + try { + if (monster.type == "stompy" && !monster.target) { + item_achievement_increment(player, player.slots.helmet, "stomped"); + } else if (monster.type != "stompy" && player.slots.helmet) { + delete player.slots.helmet.ach; + } - if (monster.type == "goo" || monster.type == "cgoo") { - item_achievement_increment(player, player.slots.pants, "gooped", attack); - } - } catch (e) { - console.log("#A: " + e); - } + if (monster.type == "goo" || monster.type == "cgoo") { + item_achievement_increment(player, player.slots.pants, "gooped", attack); + } + } catch (e) { + console.log("#A: " + e); + } } function achievement_logic_burn_last_hit(player) { - try { - if (server.shutdown) { - return; - } - item_achievement_increment(player, player.slots.mainhand, "firehazard"); - } catch (e) { - console.log("#A: " + e); - } + try { + if (server.shutdown) { + return; + } + item_achievement_increment(player, player.slots.mainhand, "firehazard"); + } catch (e) { + console.log("#A: " + e); + } } diff --git a/node/server_worker.js b/node/server_worker.js index 302dc8b..69d837f 100644 --- a/node/server_worker.js +++ b/node/server_worker.js @@ -12,17 +12,17 @@ var smap_data = workerData.smap_data; var amap_data = workerData.amap_data; parentPort.on("message", function (data) { - try { - //console.log(data); - if (data.type == "fast_astar") { - parentPort.postMessage({ type: "monster_move", move: fast_astar(data), id: data.id, in: data.in }); - } - if (data.type == "exit") { - process.exit(); - } - } catch (e) { - console.log(e); - } + try { + //console.log(data); + if (data.type == "fast_astar") { + parentPort.postMessage({ type: "monster_move", move: fast_astar(data), id: data.id, in: data.in }); + } + if (data.type == "exit") { + process.exit(); + } + } catch (e) { + console.log(e); + } }); setInterval(function () {}, 10); diff --git a/node/test.js b/node/test.js index 4bdad91..363e692 100644 --- a/node/test.js +++ b/node/test.js @@ -3,174 +3,174 @@ var protobuf = require("protobufjs"); var ByteBuffer = require("bytebuffer"); function symmetricDecrypt(input, key, checkHmac) { - var aesIv = crypto.createDecipheriv("aes-256-ecb", key, ""); - aesIv.setAutoPadding(false); - aesIv.end(input.slice(0, 16)); - var iv = aesIv.read(); + var aesIv = crypto.createDecipheriv("aes-256-ecb", key, ""); + aesIv.setAutoPadding(false); + aesIv.end(input.slice(0, 16)); + var iv = aesIv.read(); - var aesData = crypto.createDecipheriv("aes-256-cbc", key, iv); - aesData.end(input.slice(16)); - var plaintext = aesData.read(); + var aesData = crypto.createDecipheriv("aes-256-cbc", key, iv); + aesData.end(input.slice(16)); + var plaintext = aesData.read(); - if (checkHmac) { - // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC - var remotePartialHmac = iv.slice(0, iv.length - 3); - var random = iv.slice(iv.length - 3, iv.length); - var hmac = crypto.createHmac("sha1", key.slice(0, 16)); - hmac.update(random); - hmac.update(plaintext); - if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { - throw new Error("Received invalid HMAC from remote host."); - } - } + if (checkHmac) { + // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC + var remotePartialHmac = iv.slice(0, iv.length - 3); + var random = iv.slice(iv.length - 3, iv.length); + var hmac = crypto.createHmac("sha1", key.slice(0, 16)); + hmac.update(random); + hmac.update(plaintext); + if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { + throw new Error("Received invalid HMAC from remote host."); + } + } - return plaintext; + return plaintext; } function parseAppTicket(ticket) { - // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl + // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl - console.log(ticket); - if (!ByteBuffer.isByteBuffer(ticket)) { - ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); - } + console.log(ticket); + if (!ByteBuffer.isByteBuffer(ticket)) { + ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); + } - let details = {}; + let details = {}; - try { - let initialLength = ticket.readUint32(); - console.log(initialLength); - if (initialLength == 20) { - // This is a full appticket, with a GC token and session header (in addition to ownership ticket) - details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation + try { + let initialLength = ticket.readUint32(); + console.log(initialLength); + if (initialLength == 20) { + // This is a full appticket, with a GC token and session header (in addition to ownership ticket) + details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation - details.gcToken = ticket.readUint64().toString(); - //details.steamID = new SteamID(ticket.readUint64().toString()); - ticket.skip(8); // the SteamID gets read later on - details.tokenGenerated = new Date(ticket.readUint32() * 1000); + details.gcToken = ticket.readUint64().toString(); + //details.steamID = new SteamID(ticket.readUint64().toString()); + ticket.skip(8); // the SteamID gets read later on + details.tokenGenerated = new Date(ticket.readUint32() * 1000); - if (ticket.readUint32() != 24) { - // SESSIONHEADER should be 24 bytes. - return null; - } + if (ticket.readUint32() != 24) { + // SESSIONHEADER should be 24 bytes. + return null; + } - ticket.skip(8); // unknown 1 and unknown 2 - details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); - ticket.skip(4); // filler - details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms - details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to + ticket.skip(8); // unknown 1 and unknown 2 + details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); + ticket.skip(4); // filler + details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms + details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to - if (ticket.readUint32() + ticket.offset != ticket.limit) { - // OWNERSHIPSECTIONWITHSIGNATURE sectlength - return null; - } - } else { - ticket.skip(-4); - } + if (ticket.readUint32() + ticket.offset != ticket.limit) { + // OWNERSHIPSECTIONWITHSIGNATURE sectlength + return null; + } + } else { + ticket.skip(-4); + } - // Start reading the ownership ticket - let ownershipTicketOffset = ticket.offset; - let ownershipTicketLength = ticket.readUint32(); // including itself, for some reason - if ( - ownershipTicketOffset + ownershipTicketLength != ticket.limit && - ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit - ) { - return null; - } + // Start reading the ownership ticket + let ownershipTicketOffset = ticket.offset; + let ownershipTicketLength = ticket.readUint32(); // including itself, for some reason + if ( + ownershipTicketOffset + ownershipTicketLength != ticket.limit && + ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit + ) { + return null; + } - let i; - let j; - let dlc; + let i; + let j; + let dlc; - details.version = ticket.readUint32(); - details.steamID = ticket.readUint64().toString(); - details.appID = ticket.readUint32(); - details.ownershipTicketExternalIP = ticket.readUint32(); - details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( - details.ownershipFlags = ticket.readUint32(); - details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); - details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); - details.licenses = []; - // return details; + details.version = ticket.readUint32(); + details.steamID = ticket.readUint64().toString(); + details.appID = ticket.readUint32(); + details.ownershipTicketExternalIP = ticket.readUint32(); + details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( + details.ownershipFlags = ticket.readUint32(); + details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); + details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); + details.licenses = []; + // return details; - let licenseCount = ticket.readUint16(); - for (i = 0; i < licenseCount; i++) { - details.licenses.push(ticket.readUint32()); - } + let licenseCount = ticket.readUint16(); + for (i = 0; i < licenseCount; i++) { + details.licenses.push(ticket.readUint32()); + } - details.dlc = []; + details.dlc = []; - let dlcCount = ticket.readUint16(); - for (i = 0; i < dlcCount; i++) { - dlc = {}; - dlc.appID = ticket.readUint32(); - dlc.licenses = []; + let dlcCount = ticket.readUint16(); + for (i = 0; i < dlcCount; i++) { + dlc = {}; + dlc.appID = ticket.readUint32(); + dlc.licenses = []; - licenseCount = ticket.readUint16(); + licenseCount = ticket.readUint16(); - for (j = 0; j < licenseCount; j++) { - dlc.licenses.push(ticket.readUint32()); - } + for (j = 0; j < licenseCount; j++) { + dlc.licenses.push(ticket.readUint32()); + } - details.dlc.push(dlc); - } + details.dlc.push(dlc); + } - ticket.readUint16(); // reserved - if (ticket.offset + 128 == ticket.limit) { - // Has signature - details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); - } + ticket.readUint16(); // reserved + if (ticket.offset + 128 == ticket.limit) { + // Has signature + details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); + } - let date = new Date(); - details.isExpired = details.ownershipTicketExpires < date; - details.hasValidSignature = - !!details.signature && - SteamCrypto.verifySignature( - ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), - details.signature, - ); - details.isValid = !details.isExpired && (!details.signature || details.hasValidSignature); - } catch (ex) { - console.log("parseAppTicket: " + ex); - return details; - return null; // not a valid ticket - } + let date = new Date(); + details.isExpired = details.ownershipTicketExpires < date; + details.hasValidSignature = + !!details.signature && + SteamCrypto.verifySignature( + ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), + details.signature, + ); + details.isValid = !details.isExpired && (!details.signature || details.hasValidSignature); + } catch (ex) { + console.log("parseAppTicket: " + ex); + return details; + return null; // not a valid ticket + } - return details; + return details; } var proto = { - nested: { - EncryptedAppTicket: { - fields: { - ticketVersionNo: { type: "uint32", id: 1 }, - crcEncryptedticket: { type: "uint32", id: 2 }, - cbEncrypteduserdata: { type: "uint32", id: 3 }, - cbEncryptedAppownershipticket: { type: "uint32", id: 4 }, - encryptedTicket: { type: "bytes", id: 5 }, - }, - }, - }, + nested: { + EncryptedAppTicket: { + fields: { + ticketVersionNo: { type: "uint32", id: 1 }, + crcEncryptedticket: { type: "uint32", id: 2 }, + cbEncrypteduserdata: { type: "uint32", id: 3 }, + cbEncryptedAppownershipticket: { type: "uint32", id: 4 }, + encryptedTicket: { type: "bytes", id: 5 }, + }, + }, + }, }; var proto_root = protobuf.Root.fromJSON(proto); var EncryptedAppTicket = proto_root.lookupType("EncryptedAppTicket"); var outer = EncryptedAppTicket.decode( - new Buffer( - "080110a7e59890031815204f2a80014527ad8cccd8cd9b7d3260529556004ddf0454a1c2e8543495b1eb72b3dff44d9d67555798ef44b7e3f97cf86d3bf1bc3f84a91de415c382b0d96033c8e5398f2b775bcb004f5487c651d81ef410d7d2917cbb407713397d2c29a455b6852eac7db1a0e52248585bd5440ef07de54b7519f67eefbad5420d97b77ef7e35450af", - "hex", - ), + new Buffer( + "080110a7e59890031815204f2a80014527ad8cccd8cd9b7d3260529556004ddf0454a1c2e8543495b1eb72b3dff44d9d67555798ef44b7e3f97cf86d3bf1bc3f84a91de415c382b0d96033c8e5398f2b775bcb004f5487c651d81ef410d7d2917cbb407713397d2c29a455b6852eac7db1a0e52248585bd5440ef07de54b7519f67eefbad5420d97b77ef7e35450af", + "hex", + ), ); var decrypted = symmetricDecrypt( - outer.encryptedTicket, - new Buffer("8af5c487af6c7709ef1a4e6e2275fcc0a90f57ae4319de793fca3d7ed8655520", "hex"), + outer.encryptedTicket, + new Buffer("8af5c487af6c7709ef1a4e6e2275fcc0a90f57ae4319de793fca3d7ed8655520", "hex"), ); let userData = decrypted.slice(0, outer.cbEncrypteduserdata); let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); let ownershipTicket = parseAppTicket( - decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), + decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), ); if (ownershipTicket) { - ownershipTicket.userData = userData.toString(); + ownershipTicket.userData = userData.toString(); } console.log(ownershipTicket);