diff --git a/code/modules/arcade/mob_hunt/game_server.dm b/code/modules/arcade/mob_hunt/game_server.dm index cbcaa400925..7058eb5f575 100644 --- a/code/modules/arcade/mob_hunt/game_server.dm +++ b/code/modules/arcade/mob_hunt/game_server.dm @@ -3,9 +3,9 @@ var/global/datum/controller/process/mob_hunt/mob_hunt_server /datum/controller/process/mob_hunt var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this var/list/normal_spawns = list() + var/max_trap_spawns = 15 //change this to adjust the number of trap spawns that can exist at one time. traps spawned beyond this point clear the oldest traps var/list/trap_spawns = list() var/list/connected_clients = list() - var/server_instability = 0 //tracks the instability of the game server. trapped mobs and users increase instability, which causes random kicks and eventually crashing var/server_status = 1 //1 is online, 0 is offline /datum/controller/process/mob_hunt/setup() @@ -23,24 +23,12 @@ var/global/datum/controller/process/mob_hunt/mob_hunt_server /datum/controller/process/mob_hunt/doWork() if(server_status == 0) return + client_mob_update() if(normal_spawns.len < max_normal_spawns) spawn_mob() - check_stability() - if(server_instability >= 75) - if(server_instability >= 100) - server_crash() - else if(prob(server_instability - 50)) - server_crash() - if(server_instability >= 25 && connected_clients.len >= 5) - if(prob(server_instability)) - var/datum/data/pda/app/mob_hunter_game/client = pick(connected_clients) - client.disconnect("Server Communication Error") - -/datum/controller/process/mob_hunt/proc/check_stability() - server_instability = connected_clients.len + (trap_spawns.len * 5) - -/datum/controller/process/mob_hunt/proc/server_crash() +//leaving this here in case admins want to use it for a random mini-event or something +/datum/controller/process/mob_hunt/proc/server_crash(recover_time = 3000) server_status = 0 for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients) client.disconnect("Server Crash") @@ -52,13 +40,27 @@ var/global/datum/controller/process/mob_hunt/mob_hunt_server normal_spawns.Cut() trap_spawns.Cut() connected_clients.Cut() - //set a timer to automatically recover in 5 minutes (can be manually restarted if you get impatient too) - addtimer(src, "auto_recover", 3000, TRUE) + if(!isnum(recover_time)) + recover_time = 3000 + if(recover_time > 0) //when provided with a negative or zero valued recover_time argument, the server won't auto-restart but can be manually rebooted still + //set a timer to automatically recover after recover_time has passed (can be manually restarted if you get impatient too) + addtimer(src, "auto_recover", recover_time, TRUE) + +/datum/controller/process/mob_hunt/proc/client_mob_update() + var/list/ex_players = list() + for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients) + var/mob/living/carbon/human/H = client.get_player() + if(connected_clients[client]) + if(!H || H != connected_clients[client]) + ex_players |= connected_clients[client] + connected_clients[client] = H + if(ex_players.len) //to make sure we don't do this if we didn't lose any player since the last update + for(var/obj/effect/nanomob/N in (normal_spawns + trap_spawns)) + N.conceal(ex_players) /datum/controller/process/mob_hunt/proc/auto_recover() if(server_status != 0) return - server_instability = 0 server_status = 1 while(normal_spawns.len < max_normal_spawns) //repopulate the server's spawns completely if we auto-recover from crash spawn_mob() @@ -68,7 +70,6 @@ var/global/datum/controller/process/mob_hunt/mob_hunt_server N.despawn() for(var/obj/effect/nanomob/N in normal_spawns) N.despawn() - check_stability() server_status = 1 /datum/controller/process/mob_hunt/proc/spawn_mob() @@ -81,6 +82,7 @@ var/global/datum/controller/process/mob_hunt/mob_hunt_server return 0 var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info) normal_spawns += new_mob + new_mob.reveal() return 1 /datum/controller/process/mob_hunt/proc/register_trap(datum/mob_hunt/mob_info) @@ -90,5 +92,8 @@ var/global/datum/controller/process/mob_hunt/mob_hunt_server return register_spawn(mob_info) var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info) trap_spawns += new_mob - check_stability() + new_mob.reveal() + if(trap_spawns.len > max_trap_spawns) + var/obj/effect/nanomob/old_trap = trap_spawns[1] + old_trap.despawn() return 1 \ No newline at end of file diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm index 085d2a7f6bc..cadc7d35e2f 100644 --- a/code/modules/arcade/mob_hunt/mob_avatar.dm +++ b/code/modules/arcade/mob_hunt/mob_avatar.dm @@ -2,14 +2,15 @@ /obj/effect/nanomob name = "Nano-Mob Avatar" //will be overridden by the mob datum name value when created desc = "A wild Nano-Mob appeared! Hit it with your PDA with the game open to attempt to capture it!" - invisibility = 101 //normally invisible, this gets adjusted through the game's scanning + invisibility = 101 alpha = 128 anchored = 1 //just in case density = 0 icon = 'icons/effects/mob_hunt.dmi' - //icon_state gets set in New() because it is determined by the mob_hunt datum being used + var/state_name var/datum/mob_hunt/mob_info = null var/list/clients_encountered = list() //tracks who has already interacted with us, so they can't attempt a second capture + var/image/avatar /obj/effect/nanomob/New(loc, datum/mob_hunt/new_info) ..() @@ -28,21 +29,20 @@ name = mob_info.mob_name desc = "A wild [name] (level [mob_info.level]) appeared! Hit it with your PDA with the game open to attempt to capture it!" if(mob_info.is_shiny) - icon_state = mob_info.icon_state_shiny + state_name = mob_info.icon_state_shiny else - icon_state = mob_info.icon_state_normal + state_name = mob_info.icon_state_normal + avatar = image(icon, src, state_name) + avatar.override = 1 + add_alt_appearance("nanomob_avatar", avatar) /obj/effect/nanomob/attackby(obj/item/O, mob/user) - if(invisibility) //no catching mobs you can't normally see! - return if(istype(O, /obj/item/device/pda)) var/obj/item/device/pda/P = O attempt_capture(P, -20) //attempting a melee capture reduces the mob's effective run_chance by 20% to balance the risk of triggering a trap mob return 1 /obj/effect/nanomob/hitby(obj/item/O) - if(invisibility) //no catching mobs you can't normally see! - return if(istype(O, /obj/item/device/pda)) var/obj/item/device/pda/P = O attempt_capture(P) //attempting a ranged capture does not affect the mob's effective run_chance but does prevent you from being shocked by a trap mob @@ -63,7 +63,7 @@ return if(istype(P.loc, /mob/living/carbon)) var/mob/living/carbon/C = P.loc - //Strike them down with a lightning bolt to complete the illusion (copied from the surge reagent overdose, probably could + //Strike them down with a lightning bolt to complete the illusion (copied from the surge reagent overdose, probably could make this a general-use proc in the future) playsound(get_turf(C), 'sound/effects/eleczap.ogg', 75, 1) var/icon/I=new('icons/obj/zap.dmi',"lightningend") I.Turn(-135) @@ -75,36 +75,50 @@ C.electrocute_act(20, P, 1) //same damage as a revenant's overload ability, except subject to gloves/species shock resistance (for human mobs at least) return - var/obj/item/weapon/cartridge/cartridge = P.cartridge - var/cart_ref = "\ref[cartridge]" - if(cart_ref in clients_encountered) //we've already dealt with you, go away! + if(client in clients_encountered) //we've already dealt with you, go away! return else //deal with the new hunter by either running away or getting caught - clients_encountered += cart_ref + clients_encountered += client var/message = "[bicon(P)] " var/effective_run_chance = mob_info.run_chance + total_catch_mod if((effective_run_chance > 0) && prob(effective_run_chance)) message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!" + conceal(client) else if(client.register_capture(mob_info, 1)) message += "Capture success! [P.owner ? P.owner : "This hunter"] captured [name]!" + conceal(client) else message += "Capture error! Try again." - clients_encountered -= cart_ref //if the capture registration failed somehow, let them have another chance with this mob + clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob P.audible_message(message, null, 4) /obj/effect/nanomob/proc/despawn() if(mob_hunt_server) if(mob_info.is_trap) mob_hunt_server.trap_spawns -= src - mob_hunt_server.check_stability() else mob_hunt_server.normal_spawns -= src qdel(src) /obj/effect/nanomob/proc/reveal() - if(invisibility == 101) - invisibility = 0 - spawn(30) - if(src) - invisibility = 101 + if(!mob_hunt_server) + return + var/list/show_to = list() + for(var/A in mob_hunt_server.connected_clients) + if((A in clients_encountered) || !mob_hunt_server.connected_clients[A]) + continue + show_to |= mob_hunt_server.connected_clients[A] + display_alt_appearance("nanomob_avatar", show_to) + +/obj/effect/nanomob/proc/conceal(list/hide_from) + if(!mob_hunt_server) + return + var/list/hiding_from = list() + if(hide_from) + hiding_from = hide_from + else + for(var/A in mob_hunt_server.connected_clients) + if((A in clients_encountered) && mob_hunt_server.connected_clients[A]) + hiding_from |= mob_hunt_server.connected_clients[A] + hide_alt_appearance("nanomob_avatar", hiding_from) diff --git a/code/modules/arcade/mob_hunt/mob_datums.dm b/code/modules/arcade/mob_hunt/mob_datums.dm index 20dd869bf28..e0930a0ee67 100644 --- a/code/modules/arcade/mob_hunt/mob_datums.dm +++ b/code/modules/arcade/mob_hunt/mob_datums.dm @@ -34,7 +34,7 @@ var/base_health = 5 //base health of the mob for battling (effectively max health at level 0) var/attack_multiplier = 1 //how much additional damage per level the mob deals (level * attack_multiplier) var/health_multiplier = 1 //how much additional health per level the mob gets (level * health_multiplier) for calculating max health - var/health = list(0, 0) //health[1] is the mob's current health, health[2] is the mob's max health (calculated in New()) + var/health = list("current" = 0, "max" = 0) //SPAWN PREFERENCES AND VARIABLES //A note on mob spawn preferences: The mob types also have preferences, which are handled prior to per-mob preferences, so ultimately you use a combined set of preferences @@ -61,7 +61,7 @@ level = rand(min_level, max_level) if(prob(1) && prob(1)) is_shiny = 1 - health[2] = base_health + (level * health_multiplier) + health["max"] = base_health + (level * health_multiplier) if(primary_type) primary_type = new primary_type() if(secondary_type) @@ -152,28 +152,27 @@ var/list/possible_turfs = list() //setup, sets all turfs in spawn_area to weight 1 for(var/turf/T in spawn_area) - if(T.z > 1) //mobs will only consider station-level turfs for spawning. Largely here so we won't have to worry about mapping errors or mobs on the derelict solars + if(!is_station_level(T.z)) //mobs will only consider station-level turfs for spawning. Largely here so we won't have to worry about mapping errors or mobs on the derelict solars continue possible_turfs[T] = 1 - //primary type preferences - if(primary_type) - for(var/turf/T in primary_type.turf_whitelist) + //primary type preferences + if(primary_type) + if(is_type_in_list(T, primary_type.turf_whitelist)) + possible_turfs[T] += 4 + if(is_type_in_list(T, primary_type.turf_blacklist)) + possible_turfs[T] -= 2 + //secondary type preferences + if(secondary_type) + if(is_type_in_list(T, secondary_type.turf_whitelist)) + possible_turfs[T] += 4 + if(is_type_in_list(T, secondary_type.turf_blacklist)) + possible_turfs[T] -= 2 + //mob preferences + if(is_type_in_list(T, turf_whitelist)) possible_turfs[T] += 4 - for(var/turf/T in primary_type.turf_blacklist) + if(is_type_in_list(T, turf_blacklist)) possible_turfs[T] -= 2 - //secondary type preferences - if(secondary_type) - for(var/turf/T in secondary_type.turf_whitelist) - possible_turfs[T] += 4 - for(var/turf/T in secondary_type.turf_blacklist) - possible_turfs[T] -= 2 - //mob preferences - for(var/turf/T in turf_whitelist) - possible_turfs[T] += 4 - for(var/turf/T in turf_blacklist) - possible_turfs[T] -= 2 - //weight check, remove negative or zero weight turfs from the list, then return the list - for(var/T in possible_turfs) + //weight check, remove negative or zero weight turfs from the list, then return the list if(possible_turfs[T] < 1) possible_turfs -= T return possible_turfs @@ -210,9 +209,9 @@ var/message = "" var/multiplier = calc_dam_multiplier(attack_type) var/total_damage = raw_damage * multiplier - if(!health[1]) //it's already downed, quit hitting it + if(!health["current"]) //it's already downed, quit hitting it return null - health[1] = max(health[1] - total_damage, 0) + health["current"] = max(health["current"] - total_damage, 0) switch(multiplier) if(0) message += "The attack is completely ineffective! " @@ -224,7 +223,7 @@ message += "It's super effective! " if(4) message += "It's ultra effective! " - if(!health[1]) + if(!health["current"]) message += "[nickname ? nickname : mob_name] is downed!" return message @@ -236,7 +235,7 @@ message += "[nickname ? nickname : mob_name] can't get any stronger right now!" else - health[2] = base_health + (level * health_multiplier) + health["max"] = base_health + (level * health_multiplier) message += "[nickname ? nickname : mob_name] has reached level [level]!" /datum/mob_hunt/proc/get_type1() diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm index 512f320a32b..f41d046ae5c 100644 --- a/code/modules/pda/mob_hunt_game_app.dm +++ b/code/modules/pda/mob_hunt_game_app.dm @@ -17,6 +17,7 @@ var/hacked = 0 //if set, this cartridge is able to spawn trap mobs from its collection (set via emag_act on the cartridge) var/catch_mod = 0 //used to adjust the likelihood of a mob running from this client, a negative value means it is less likely to run (support for temporary bonuses) var/wild_captures = 0 //used to track the total number of mobs captured from the wild (does not count card mobs) by this client + var/scan_range = 3 //maximum distance (in tiles) from which the client can reveal nearby mobs /datum/data/pda/app/mob_hunter_game/start() ..() @@ -24,18 +25,20 @@ /datum/data/pda/app/mob_hunter_game/stop() ..() + disconnect("Program Terminated") processing_objects.Remove(pda) /datum/data/pda/app/mob_hunter_game/proc/scan_nearby() if(!mob_hunt_server || !connected) return - for(var/turf/T in range(3, get_turf(pda))) - for(var/obj/O in T.contents) - if(!istype(O, /obj/effect/nanomob)) - continue - var/obj/effect/nanomob/N = O - //reveal the mob - N.reveal() + for(var/turf/T in range(scan_range, get_turf(pda))) + for(var/obj/effect/nanomob/N in T.contents) + if(src in N.clients_encountered) + //hide the mob + N.conceal() + else + //reveal the mob + N.reveal() /datum/data/pda/app/mob_hunter_game/proc/reconnect() if(!mob_hunt_server || !mob_hunt_server.server_status || connected) @@ -47,10 +50,20 @@ pda.audible_message("[bicon(pda)] Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!", null, 2) return 1 +/datum/data/pda/app/mob_hunter_game/proc/get_player() + if(!pda) + return null + if(ishuman(pda.loc)) + var/mob/living/carbon/human/H = pda.loc + return H + return null + /datum/data/pda/app/mob_hunter_game/proc/disconnect(reason = null) if(!mob_hunt_server || !connected) return mob_hunt_server.connected_clients -= src + for(var/obj/effect/nanomob/N in (mob_hunt_server.normal_spawns + mob_hunt_server.trap_spawns)) + N.conceal(list(get_player())) connected = 0 //show a disconnect message if we were disconnected involuntarily (reason argument provided) if(pda && reason) @@ -105,8 +118,6 @@ if(mob_info.is_shiny) entry["sprite"] = "[mob_info.icon_state_shiny].png" data["entry"] = entry - for(var/a in entry) - log_debug("[a] = [entry[a]]") /datum/data/pda/app/mob_hunter_game/proc/assign_nickname() if(!my_collection.len)