var is_game=0,is_server=1,is_code=0,is_pvp=false; var server={ 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, }}); // default is 25000 to 60000 var fs=require('fs'),url=require('url'); var {Worker,SHARE_ENV}=require('worker_threads'),workers=[],wlast=0; eval(""+fs.readFileSync(variables.cfunctions_path)); eval(""+fs.readFileSync(variables.functions_path)); eval(""+fs.readFileSync(variables.data_path)); var base_url=variables.base_url,server_id="1",server_auth="123456",server_name=""; var players={}; var dc_players={}; var sockets={}; var observers={}; var total_monsters=0; var max_players=96; var chests={}; var projectiles={}; var name_to_id={},id_to_id={}; var invitations={},challenges={},magiportations={},requests={},parties={},frequests={},trequests={}; var total_moves=1,total_players=0,unique_players=0; var observer_map="winterland",observer_x=0,observer_y=0; var merchant_map="main",merchant_y=-50,merchant_x=0,total_merchants=0; var csold=[],cfound=[]; // cache of S.sold + S.found var monster_c={}; // monster counts var pwns=[],pend=0; var tavern={}; var dbase={h:8,v:7,vn:2}; // default-base var G={},D={},P={}; // the game var S={}; // server data var T={}; // cosmetics var E={ schedule:{ time_offset:0, dailies:[13,20], nightlies:[23], night:false, }, }; // server event data var TX=[],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, }; 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, }; var limits={ "calls":200, // 4 seconds "party":9, "party_max":10, }; var W={ // 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 }; 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, }; var dailies=["crabxx","goobrawl","abtesting"]; shuffle(dailies); var nightlies=["icegolem","franky"]; shuffle(nightlies); if(events.holidayseason) events.snowman=60; if(events.valentines) events.pinkgoo=60; TIMEO={ "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, } var instances={}; // at first there were no instances, monsters were global // instances improve performance and allow things like dynamic events, dungeons var luckm=1,xpm=1,goldm=1,buym=1; region=process.argv[process.argv.length-3]; server_name=process.argv[process.argv.length-2]; 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; } 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; } 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; } else 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.20; 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"); }); } 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"}); }); } 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; } function parse_http_json(data) { var result=decode_http_data(data),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),args=url_parts.query,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),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); } }); } 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; } 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||""; 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; } function monster_to_client(monster,events) { var data={},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; } 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; } 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}; } function clean_slate(player) { 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; } function party_to_client(oname) { var list=parties[oname]; var party={},output=0,length=0,newbies=0,odps={},c={},add=36000; calculate_party(oname); list.forEach(function(name){ var player=players[name_to_id[name]],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]],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}); }); } 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 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", } 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; } } 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],item_attack=0,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;icurrent.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(mx80)&&0.010||(player.level>80)&&0.012||(player.level>70)&&0.020||(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; 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; } 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); } 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 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,freq_mult=0.034,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); } 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; } 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]); } function consume_one(player,num) { consume(player,num,1); } function consume_one_by_id(player,id) { for(var i=0;i0.00001) && Math.random()/share/player.luckm/monster.level/monster_mult0.00001) && Math.random()/share/player.luckm/monster.level/monster_mult1) // && !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; } 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=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; 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); } 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(!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]; target.socket.emit("game_log",{message:"Slain by "+attacker.name,color:"#F12F02"}); var lost_shells=0,psize=1; if(lost_xp) for(var i=0;i10) { 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),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; // 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; // 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} } // 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; // 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(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,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=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,mp=attacker.next_mp; if(atype=="cburst") mp=mp_cutoff=attacker.next_mp; if(attacker.mp3*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(!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); 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(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()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=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); } if(attacker==target && !target.dead); // reflect after dead fix [04/02/23] else if(target.evasion && defense=="armor" && Math.random()*10072*(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(combo>10) combo_m=combo/4.0; else if(combo>1) combo_m=[1,1.6,1.62,1.64,1.70,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,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)0 && target.s.invincible) attack=0; if(attacker.tskin=="konami" && target.type!=attacker.p.target_lock) attack=0; o_attack=attack=ceil(attack); if(info.conditions.includes("frozen") && !target.immune && target.hp>attack) { if(Math.random()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("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; 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,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_hp0 && 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(attacker.is_player) { if(net>0) // "attack" { if(attacker.t) attacker.t.mdamage+=net; add_pdps(attacker,target,net); } else // "heal" add_pdps(attacker,target,-net*B.dps_heal_mult); var m=target,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(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()*1006000) { var ms=max(0,-mssince(player.last.attack))+3200,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}); } 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; } 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 } } } 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,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,len=data.message.length,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],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],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,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]],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,name="goo",count=100,times=0,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,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],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],s=data.s||0,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)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",ref=G.maps.cave.spawns[2],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=[],heads=[],hairs=[],wings=[],hats=[]; for(var s in G.sprites) { var current=G.sprites[s],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;i5) 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.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.golde[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],check=true,items=[],quantity={},place={},space=false,p={},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],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.goldB.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.qB.sell_dist) return fail_response("distance"); if(item.qB.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(dateB.sell_dist) return socket.emit("game_response",{response:"distance",place:"compound",failed:true}); var def=G.items[item0.name],scroll_def=G.items[scroll.name],offering_def=offering&&G.items[offering.name],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,probability=1,oprobability=1,result=0,proc=0,grace=0,igrade=def.igrade,high=false,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()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],scroll_def=scroll&&G.items[scroll.name],offering_def=offering&&G.items[offering.name],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,probability=1,oprobability=1,grace=0,high=false; var ograde=calculate_item_grade(item_def,{name:item.name,level:0}),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,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].offeringgrade && 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=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],to_update="reopen+u+cid",resolve={num:data.num},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,price=round(min(99999999999,max(parseInt(data.price)||1,1))),minutes=1; data.q=max(1,parseInt(data.q)||1); if((item.q||1)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,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],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],num=data.num,item=player.items[data.num],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;iplayer.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],name=data.name,def=G.items[data.name],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),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],done=false,npc=G.maps.main.ref.secondhands,c=S.sold,cc=csold,e="+$p",ev="secondhands",mult=2,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;iplayer.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],can_reach=false; if(!player || player.user) return fail_response("cant_in_bank"); var name=data.name,quantity=min(max(parseInt(data.quantity)||0,1),G.items[name]&&G.items[name].s||9999),cost=0,added=false,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.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),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],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],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],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],seller=players[id_to_id[data.id]],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],buyer=players[id_to_id[data.id]],num,actual=null,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)buyer.gold) return fail_response("buyer_gold"); for(var i=0;i=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],seller=players[id_to_id[data.id]],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)=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],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.goldplayer.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],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=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],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],item=player.items[data.num],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,cool=true,resolve={response:"data",place:data.name,success:true},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.mpB.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].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,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,y=parseFloat(data.y)||0,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],r="u+cid",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),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={},times=0,attack=null,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],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],attack=null,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)=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],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.distG.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.mp0.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=[],reftarget=null,c_resolve=null; for(var id in instances[player.in].monsters) { var target=instances[player.in].monsters[id]; var dist=distance(player,target); if(distG.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,rate=0.8; if(player.level>=100) rate=1.12; else if(player.level>=90) rate=1.10; 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;i300) 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,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],current=-1,going=-1,actual=true; var x=parseFloat(data.going_x)||0,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],player=players[socket.id],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],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,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,pool_current=0,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,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,pool_current=0,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]],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),a=[],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],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.goldplayer.gold) return socket.emit("game_response","gold_not_enough"); if(win-edge-gold>(S.gold-house_debt())*0.40) 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.40)}) } }); 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],sdata=[]; if(!player) return; // || is_pvp for(var id in players) { var current=players[id],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],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],min=99999,x=null; if(!player || player.rip) return; for(var id in instances[player.in].monsters||{}) { var m=instances[player.in].monsters[id],c=distance(m,player,true); if(c"+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]),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]); } 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]}; } 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; } 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]--; } } function new_monster(instance,map_def,args) { if(!instances[instance]) return; //otherwise late dungeon spawns bring down the server if(!args) args={}; var monster={},id=++total_monsters+"",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; 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),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.levelattacker.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)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()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],ref=monster.s[name],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,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,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)0.99 || Math.random()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,radius=monster.map_def.radius,dx=Math.random()*(radius)-radius/2,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_x1 && 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,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],ref=player.s[name],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,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,change=false; if(value1800000 && !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,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,announce=false,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()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()=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"); } } }); 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,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(); 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))); } 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(); for(var id in npcs) { var npc=npcs[id],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)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,old_focus=npc.focus,min_val=99999,max_val=-99999; for(var pid in instances[npc.in].players) { var player=instances[npc.in].players[pid]; if(def.seek=="low_hp" && player.hpmax_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.levelfuture_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.hpnpc.boundary[2] || npc.going_ynpc.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]; } 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); } setTimeout(game_loop,10); setTimeout(npc_loop,10); setTimeout(instance_loop,10); 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);}; },13200); setInterval(function(){ try{ if(is_pvp) return; var names=[],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); //1 setInterval(function(){ try{ var edge=future_s(-120); for(var id in players) { if(0 && players[id].last_ipass50) 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);}; },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);}; },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);}; },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);}; },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);}; },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);}; } } setInterval(projectiles_loop,7); var accel=1; if(mode.fast_mlevels) 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),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.hpG.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;iG.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); setInterval(function(){ 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; }); } 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(!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(); } 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(); } 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