From 4e532ee76077751ba97d7d13fc52163f85d4a18b Mon Sep 17 00:00:00 2001 From: Fluffy <65877598+FluffyGhoster@users.noreply.github.com> Date: Thu, 2 May 2024 00:35:32 +0200 Subject: [PATCH] Hivebot scaling and minor refactor (#18984) Hivebot beacons scale with the currently playing player count, if they spawn on the main map (the Horizon, most likely). Refactored the hivebot code slightly, mostly in terms of DMDoc and reordering. --- .../simple_animal/hostile/hivebots/hivebot.dm | 144 +++++++++-------- .../hostile/hivebots/hivebot_beacon.dm | 145 ++++++++++++++---- .../changelogs/fluffyghost-hivebotscaling.yml | 59 +++++++ 3 files changed, 252 insertions(+), 96 deletions(-) create mode 100644 html/changelogs/fluffyghost-hivebotscaling.yml diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm index 62d1ddd31a7..e56389e08e7 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm @@ -40,9 +40,30 @@ ) speak_chance = 5 attack_emote = "focuses on" - var/mob/living/simple_animal/hostile/hivebotbeacon/linked_parent = null psi_pingable = FALSE + /** + * The hivebot beacon that we are liked to (and likely generated us) + */ + var/mob/living/simple_animal/hostile/hivebotbeacon/linked_parent = null + +/mob/living/simple_animal/hostile/hivebot/Initialize(mapload,mob/living/simple_animal/hostile/hivebot/hivebotbeacon) + . = ..() + + if(hivebotbeacon) + linked_parent = hivebotbeacon + + if(!mapload) + new /obj/effect/effect/smoke(src.loc,30) + playsound(src.loc, 'sound/effects/EMPulse.ogg', 25, 1) + +/mob/living/simple_animal/hostile/hivebot/Destroy() + if(linked_parent) + linked_parent.linked_bots -= src + linked_parent = null + + . = ..() + /mob/living/simple_animal/hostile/hivebot/get_bullet_impact_effect_type(var/def_zone) return BULLET_IMPACT_METAL @@ -60,6 +81,55 @@ else return "blood_overlay_armed" +/mob/living/simple_animal/hostile/hivebot/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj, /obj/item/projectile/bullet/pistol/hivebotspike) || istype(Proj, /obj/item/projectile/beam/hivebot)) + Proj.no_attack_log = 1 + return PROJECTILE_CONTINUE + else + return ..(Proj) + +/mob/living/simple_animal/hostile/hivebot/death() + ..(null,"blows apart!") + var/T = get_turf(src) + new /obj/effect/gibspawner/robot(T) + spark(T, 1, GLOB.alldirs) + qdel(src) + +/mob/living/simple_animal/hostile/hivebot/think() + . =..() + if(stance == HOSTILE_STANCE_IDLE) + icon_state = "[initial(icon_state)]" + else + icon_state = "[initial(icon_state)]_armed" + +/mob/living/simple_animal/hostile/hivebot/Allow_Spacemove(var/check_drift = 0) + return TRUE + +/mob/living/simple_animal/hostile/hivebot/AirflowCanMove(n) + return FALSE + +/mob/living/simple_animal/hostile/hivebot/emp_act(severity) + . = ..() + + LoseTarget() + change_stance(HOSTILE_STANCE_TIRED) + addtimer(CALLBACK(src, PROC_REF(wakeup)), 50) + visible_message(SPAN_DANGER("[src] suffers a teleportation malfunction!")) + playsound(src.loc, 'sound/effects/teleport.ogg', 25, 1) + var/turf/random_turf = get_turf(pick(orange(src,7))) + do_teleport(src, random_turf) + +/mob/living/simple_animal/hostile/hivebot/proc/wakeup() + change_stance(HOSTILE_STANCE_IDLE) + + +/*############ + SUBTYPES +############*/ + +/** + * # Hivebot Guardian + */ /mob/living/simple_animal/hostile/hivebot/guardian health = 80 maxHealth = 45 @@ -72,22 +142,24 @@ mob_swap_flags = ~HEAVY mob_push_flags = 0 - /mob/living/simple_animal/hostile/hivebot/guardian/Initialize(mapload,mob/living/simple_animal/hostile/hivebot/hivebotbeacon) .=..() if(hivebotbeacon && linked_parent) linked_parent.guard_amt++ -/mob/living/simple_animal/hostile/hivebot/guardian/think() - . =..() - if(stance != HOSTILE_STANCE_IDLE) - wander = 1 - /mob/living/simple_animal/hostile/hivebot/guardian/Destroy() .=..() if(linked_parent) linked_parent.guard_amt-- +/mob/living/simple_animal/hostile/hivebot/guardian/think() + . =..() + if(stance != HOSTILE_STANCE_IDLE) + wander = 1 + +/** + * # Hivebot Bomber + */ /mob/living/simple_animal/hostile/hivebot/bomber desc = "A primitive in design, hovering robot, with some menacing looking blades jutting out from it. It bears no manufacturer markings of any kind. This one appears round in design and moves slower than its brethren." health = 100 @@ -121,6 +193,9 @@ fragem(src,10,30,2,3,5,1,FALSE) src.gib() +/** + * # Hivebot Ranged + */ /mob/living/simple_animal/hostile/hivebot/range name = "Hivebot" desc = "A primitive in design, hovering robot, with a simple looking launcher sticking out of it. It bears no manufacturer markings of any kind." @@ -130,58 +205,3 @@ /mob/living/simple_animal/hostile/hivebot/range/rapid projectiletype = /obj/item/projectile/bullet/pistol/hivebotspike/needle rapid = 1 - -//Creates a reference to its parent beacon on init. -/mob/living/simple_animal/hostile/hivebot/Initialize(mapload,mob/living/simple_animal/hostile/hivebot/hivebotbeacon) - if(hivebotbeacon) - linked_parent = hivebotbeacon - .=..() - if(!mapload) - new /obj/effect/effect/smoke(src.loc,30) - playsound(src.loc, 'sound/effects/EMPulse.ogg', 25, 1) - -/mob/living/simple_animal/hostile/hivebot/bullet_act(var/obj/item/projectile/Proj) - if(istype(Proj, /obj/item/projectile/bullet/pistol/hivebotspike) || istype(Proj, /obj/item/projectile/beam/hivebot)) - Proj.no_attack_log = 1 - return PROJECTILE_CONTINUE - else - return ..(Proj) - -/mob/living/simple_animal/hostile/hivebot/death() - ..(null,"blows apart!") - var/T = get_turf(src) - new /obj/effect/gibspawner/robot(T) - spark(T, 1, GLOB.alldirs) - qdel(src) - -/mob/living/simple_animal/hostile/hivebot/Destroy() - . = ..() - if(linked_parent) - linked_parent.linked_bots -= src - -/mob/living/simple_animal/hostile/hivebot/think() - . =..() - if(stance == HOSTILE_STANCE_IDLE) - icon_state = "[initial(icon_state)]" - else - icon_state = "[initial(icon_state)]_armed" - -/mob/living/simple_animal/hostile/hivebot/Allow_Spacemove(var/check_drift = 0) - return 1 - -/mob/living/simple_animal/hostile/hivebot/AirflowCanMove(n) - return 0 - -/mob/living/simple_animal/hostile/hivebot/emp_act(severity) - . = ..() - - LoseTarget() - change_stance(HOSTILE_STANCE_TIRED) - addtimer(CALLBACK(src, PROC_REF(wakeup)), 50) - visible_message(SPAN_DANGER("[src] suffers a teleportation malfunction!")) - playsound(src.loc, 'sound/effects/teleport.ogg', 25, 1) - var/turf/random_turf = get_turf(pick(orange(src,7))) - do_teleport(src, random_turf) - -/mob/living/simple_animal/hostile/hivebot/proc/wakeup() - change_stance(HOSTILE_STANCE_IDLE) diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm index 22e2b121a2b..4e2f9b97f5d 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm @@ -34,28 +34,84 @@ minbodytemp = 0 speed = -10 destroy_surroundings = 0 - var/bot_type - var/bot_amt = 160 //Number of total bots that are spawned before the beacon disappears completely. - var/max_bots = 40 //Number of bots linked to this beacon specifically that can exist, before spawning more is halted. - var/list/linked_bots = list() - var/guard_amt = 0 - var/harvester_amt = 0 - var/spawn_delay - var/activated = 0 - var/max_bots_reached - var/list/destinations = list() - var/list/close_destinations = list() - var/area/latest_area attack_emote = "focuses on" psi_pingable = FALSE -/mob/living/simple_animal/hostile/hivebotbeacon/incendiary - projectiletype = /obj/item/projectile/beam/hivebot/incendiary - projectilesound = 'sound/weapons/plasma_cutter.ogg' - rapid = 0 + /** + * Number of total bots that are spawned before the beacon disappears completely + * + * This gets updated in /Initialize() with a formula based on the + * `total_hivebots_to_spawn_to_playing_players_scaling_factor` variable + */ + var/total_hivebots_to_spawn = 160 + + /** + * The scaling factor for the number of total hivebots to spawn respective to playing players + * + * Eg: scaling factor of 1 would mean that the beacon would spawn 1 additional hivebot per player + */ + var/total_hivebots_to_spawn_to_playing_players_scaling_factor = 1 + + /** + * Number of bots linked to this beacon specifically that can exist, before spawning more is halted + * + * This gets updated in /Initialize() with a formula based on the + * `maximum_linked_and_alive_hivebots_to_playing_players_scaling_factor` variable + */ + var/maximum_linked_and_alive_hivebots = 40 + + /** + * The scaling factor for the maximum number of linked and alive hivebots respective to playing players + * + * Eg: scaling factor of 1 would mean that the beacon would have one additional linked and alive hivebot per player + */ + var/maximum_linked_and_alive_hivebots_to_playing_players_scaling_factor = 1 + + ///A list of `/mob/living/simple_animal/hostile` hivebots that are linked to this beacon + var/list/mob/living/simple_animal/hostile/hivebot/linked_bots = list() + + ///Amount of hivebots spawned and alive linked to this beacon, guardian type + var/guard_amt = 0 + + ///Amount of hivebots spawned and alive linked to this beacon, harvester type + var/harvester_amt = 0 + + ///Calculated delay between spawning new bots, internal use only + VAR_PRIVATE/spawn_delay + + /** + * The scaling factor for the subtraction from the calculated `spawn_delay` based on player count + * + * Eg: scaling factor of 1 would mean that each player reduces the `spawn_delay` by 1 decisecond + * + * This only works up to a minumum of 80 deciseconds + */ + var/spawn_delay_to_playing_players_scaling_factor = 1.8 + + /** + * The activation state of the beacon + * + * FALSE (0) -> beacon is off + * TRUE (1) -> beacon is on + * -1 -> beacon is disabled and should not be activated (usually for mapping purposes) + */ + var/activated = 0 + + ///Boolean, if the maximum number of bots has been reached, internal use only + VAR_PRIVATE/maximum_linked_and_alive_hivebots_reached + + var/list/destinations = list() + var/list/close_destinations = list() + var/area/latest_area /mob/living/simple_animal/hostile/hivebotbeacon/Initialize(mapload) - .=..() + . = ..() + + //Calculate the actual values based on player population, but only if it's on the main map (most likely the Horizon) + if(isStationLevel(src.z)) + total_hivebots_to_spawn = total_hivebots_to_spawn + (length(GLOB.player_list) * total_hivebots_to_spawn_to_playing_players_scaling_factor) + maximum_linked_and_alive_hivebots = maximum_linked_and_alive_hivebots + (length(GLOB.player_list) * maximum_linked_and_alive_hivebots_to_playing_players_scaling_factor) + if(!mapload) var/datum/effect/effect/system/smoke_spread/S = new /datum/effect/effect/system/smoke_spread() S.set_up(5, 0, src.loc) @@ -63,11 +119,25 @@ visible_message(SPAN_DANGER("[src] warps in!")) playsound(src.loc, 'sound/effects/EMPulse.ogg', 25, 1) addtimer(CALLBACK(src, PROC_REF(activate_beacon)), 450) + latest_area = get_area(src) icon_state = "hivebotbeacon_off" addtimer(CALLBACK(src, PROC_REF(generate_warp_destinations)), 10) //So we don't sleep during init set_light(6,0.5,LIGHT_COLOR_GREEN) +/mob/living/simple_animal/hostile/hivebotbeacon/Destroy() + //Remove the reference from all linked bots to us + for(var/mob/living/simple_animal/hostile/hivebot/latest_child in linked_bots) + latest_child.linked_parent = null + linked_bots.Cut() + + //Smoke effect, we disappear in a smoke + var/datum/effect/effect/system/smoke_spread/S = new /datum/effect/effect/system/smoke_spread() + S.set_up(5, 0, src.loc) + S.start() + + . = ..() + /mob/living/simple_animal/hostile/hivebotbeacon/proc/generate_warp_destinations() destinations.Cut() @@ -102,15 +172,6 @@ qdel(src) return -/mob/living/simple_animal/hostile/hivebotbeacon/Destroy() - for (var/mob/living/simple_animal/hostile/hivebot/latest_child in linked_bots) - latest_child.linked_parent = null - linked_bots.Cut() - var/datum/effect/effect/system/smoke_spread/S = new /datum/effect/effect/system/smoke_spread() - S.set_up(5, 0, src.loc) - S.start() - .=..() - /mob/living/simple_animal/hostile/hivebotbeacon/think() . =..() if(stance != HOSTILE_STANCE_IDLE && activated == 0) @@ -180,7 +241,7 @@ activate_beacon() /mob/living/simple_animal/hostile/hivebotbeacon/proc/warpbots() - if(!bot_amt) + if(!total_hivebots_to_spawn) visible_message(SPAN_DANGER("[src] disappears in a cloud of smoke!")) playsound(src.loc, 'sound/effects/teleport.ogg', 25, 1) new /obj/effect/decal/cleanable/greenglow(src.loc) @@ -190,9 +251,11 @@ if(activated == -1) return - if(linked_bots.len < max_bots) + if(linked_bots.len < maximum_linked_and_alive_hivebots) visible_message(SPAN_WARNING("[src] radiates with energy!")) + var/bot_type + if(guard_amt < 4 && prob(50)) bot_type = GUARDIAN else @@ -245,16 +308,21 @@ linked_bots += latest_child //Adds the spawned hivebot to the list of the beacon's children. latest_child.faction = faction - bot_amt-- + total_hivebots_to_spawn-- - if(bot_amt>0 && linked_bots.len < max_bots) + if(total_hivebots_to_spawn>0 && linked_bots.len < maximum_linked_and_alive_hivebots) calc_spawn_delay() addtimer(CALLBACK(src, PROC_REF(warpbots)), spawn_delay) else - max_bots_reached = 1 + maximum_linked_and_alive_hivebots_reached = 1 /mob/living/simple_animal/hostile/hivebotbeacon/proc/calc_spawn_delay() - spawn_delay = 80*1.085**(linked_bots.len + 1) + spawn_delay = 80 * (1.085 ** (linked_bots.len + 1)) + + //Adapt the value based on player population, but only if it's on the main map (most likely the Horizon) + if(isStationLevel(src.z)) + spawn_delay = min(80, spawn_delay - (length(GLOB.player_list) * spawn_delay_to_playing_players_scaling_factor)) + return /mob/living/simple_animal/hostile/hivebotbeacon/Life() @@ -262,11 +330,20 @@ if(wander) wander = 0 stop_automated_movement = 1 - if(max_bots_reached && activated == 1 && linked_bots.len < max_bots) - max_bots_reached = 0 + if(maximum_linked_and_alive_hivebots_reached && activated == 1 && linked_bots.len < maximum_linked_and_alive_hivebots) + maximum_linked_and_alive_hivebots_reached = 0 calc_spawn_delay() addtimer(CALLBACK(src, PROC_REF(warpbots)), spawn_delay) +/*################ + SUBTYPES +################*/ + +/mob/living/simple_animal/hostile/hivebotbeacon/incendiary + projectiletype = /obj/item/projectile/beam/hivebot/incendiary + projectilesound = 'sound/weapons/plasma_cutter.ogg' + rapid = 0 + #undef NORMAL #undef RANGED #undef RAPID diff --git a/html/changelogs/fluffyghost-hivebotscaling.yml b/html/changelogs/fluffyghost-hivebotscaling.yml new file mode 100644 index 00000000000..aca25b7c1fa --- /dev/null +++ b/html/changelogs/fluffyghost-hivebotscaling.yml @@ -0,0 +1,59 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# - (fixes bugs) +# wip +# - (work in progress) +# qol +# - (quality of life) +# soundadd +# - (adds a sound) +# sounddel +# - (removes a sound) +# rscadd +# - (adds a feature) +# rscdel +# - (removes a feature) +# imageadd +# - (adds an image or sprite) +# imagedel +# - (removes an image or sprite) +# spellcheck +# - (fixes spelling or grammar) +# experiment +# - (experimental change) +# balance +# - (balance changes) +# code_imp +# - (misc internal code change) +# refactor +# - (refactors code) +# config +# - (makes a change to the config files) +# admin +# - (makes changes to administrator tools) +# server +# - (miscellaneous changes to server) +################################# + +# Your name. +author: FluffyGhost + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - balance: "Hivebot beacons scale with the currently playing player count, if they spawn on the main map (the Horizon, most likely)." + - refactor: "Refactored the hivebot code slightly, mostly in terms of DMDoc and reordering."