Mob Hunt Adjustments

Removes server instability, random disconnects, and server crash due to
trap mobs / high player count

Trap mobs are now capped just like normal spawns
- If a trap mob is created that would push the total number of trap mobs
over this limit, the server will remove the oldest trap mob (effectively
replacing it)

Changes health from a list using magic numbers to an associated list for
readability (doesn't make sense to have two vars for what little uses
this)

Converts mob displaying to use the alternate appearance system, to allow
for a per-mob control over visibility
- Only will show for mobs that are holding a PDA with a game client that
is connected to the game server.
- Nanomobs properly are concealed from player mobs after being interacted
with (capture/escape), PDA dropped, or game client disconnection
- Game client will disconnect from game server when you change the active
PDA app (including going to PDA main menu) and/or eject the game cartridge

Also finished my thought in that one comment. It's about time I-
This commit is contained in:
FalseIncarnate
2016-09-15 04:23:40 -04:00
parent 6c4bce1eac
commit 6edafc1a97
4 changed files with 103 additions and 74 deletions
+26 -21
View File
@@ -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
+34 -20
View File
@@ -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)
+23 -24
View File
@@ -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()
+20 -9
View File
@@ -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)