mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-17 18:13:34 +01:00
This reverts commit d6ed342a6d.
This commit is contained in:
committed by
GitHub
parent
a6359ea097
commit
76c38f83e8
@@ -1,365 +0,0 @@
|
||||
// =====================
|
||||
// MARK: Swarmer
|
||||
// =====================
|
||||
/mob/living/basic/swarmer
|
||||
name = "swarmer"
|
||||
desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely."
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
icon_state = "swarmer"
|
||||
icon_living = "swarmer"
|
||||
icon_dead = "swarmer_unactivated"
|
||||
bubble_icon = "swarmer"
|
||||
mob_biotypes = MOB_ROBOTIC
|
||||
health = 50
|
||||
maxHealth = 50
|
||||
melee_damage_lower = 6
|
||||
melee_damage_upper = 10
|
||||
melee_damage_type = BURN
|
||||
melee_attack_cooldown_min = 1.5 SECONDS
|
||||
melee_attack_cooldown_max = 2.5 SECONDS
|
||||
a_intent = INTENT_HARM
|
||||
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
mob_size = MOB_SIZE_SMALL
|
||||
pass_flags = PASSTABLE
|
||||
ventcrawler = VENTCRAWLER_ALWAYS
|
||||
light_color = LIGHT_COLOR_CYAN
|
||||
attack_verb_continuous = "shocks"
|
||||
attack_verb_simple = "shock"
|
||||
friendly_verb_continuous = "pinches"
|
||||
friendly_verb_simple = "pinch"
|
||||
attack_sound = 'sound/effects/empulse.ogg'
|
||||
faction = list("swarmer")
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minimum_survivable_temperature = 0
|
||||
maximum_survivable_temperature = 500
|
||||
speak_emote = list("tones")
|
||||
loot = list(/obj/effect/decal/cleanable/blood/gibs/robot)
|
||||
death_message = "explodes with a sharp pop!"
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
sentience_type = SENTIENCE_OTHER // No, you cannot sentience or mind-transfer into them
|
||||
environment_smash = ENVIRONMENT_SMASH_RWALLS // EAT EVERYTHING
|
||||
step_type = FOOTSTEP_MOB_CLAW
|
||||
is_ranged = TRUE
|
||||
projectile_type = /obj/projectile/beam/disabler
|
||||
projectile_sound = 'sound/weapons/taser2.ogg'
|
||||
ranged_cooldown = 1 SECONDS
|
||||
ai_controller = /datum/ai_controller/basic_controller/swarmer
|
||||
see_in_dark = 6
|
||||
light_range = 2
|
||||
light_color = LIGHT_COLOR_CYAN
|
||||
step_type = FOOTSTEP_MOB_CLAW
|
||||
loot = list(
|
||||
/obj/effect/gibspawner/robot,
|
||||
/obj/item/stack/ore/bluespace_crystal,
|
||||
)
|
||||
|
||||
/// Swarmer actions
|
||||
var/list/innate_actions = list(
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_trap = BB_SWARMER_TRAP_ACTION,
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_barrier = BB_SWARMER_BARRIER_ACTION,
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_replicate = BB_SWARMER_REPLICATE_ACTION,
|
||||
)
|
||||
/// Resources the swarmer gains from consuming things
|
||||
var/resources = 0
|
||||
/// Maximum possible resources a swarmer can have
|
||||
var/resource_max = 100
|
||||
|
||||
/mob/living/basic/swarmer/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/ai_retaliate_advanced, CALLBACK(src, PROC_REF(retaliate_callback)))
|
||||
add_language("Swarmer", 1)
|
||||
verbs -= /mob/living/verb/pulled
|
||||
updatename()
|
||||
grant_actions_by_list(innate_actions)
|
||||
set_default_language(GLOB.all_languages["Swarmer"])
|
||||
|
||||
/mob/living/basic/swarmer/proc/retaliate_callback(mob/living/attacker)
|
||||
if(!istype(attacker))
|
||||
return
|
||||
if(attacker.ai_controller) // Don't chain retaliates.
|
||||
var/list/shitlist = attacker.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]
|
||||
if(src in shitlist)
|
||||
return
|
||||
for(var/mob/living/basic/swarmer/lesser/the_swarm in oview(src, 7))
|
||||
if(the_swarm == attacker) // Do not commit suicide attacking yourself
|
||||
continue
|
||||
if(the_swarm.faction_check_mob(attacker, FALSE)) // Don't attack other swarmers
|
||||
continue
|
||||
the_swarm.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker)
|
||||
|
||||
/mob/living/basic/swarmer/proc/updatename()
|
||||
real_name = "Swarmer [rand(100,999)]-[pick("kappa", "sigma", "beta", "omicron", "iota", "epsilon", "omega", "gamma", "delta", "tau", "alpha")]"
|
||||
name = real_name
|
||||
|
||||
/mob/living/basic/swarmer/get_status_tab_items()
|
||||
var/list/status_tab_data = ..()
|
||||
status_tab_data[++status_tab_data.len] = list("Resources:", "[resources] / [resource_max]")
|
||||
return status_tab_data
|
||||
|
||||
/mob/living/basic/swarmer/emp_act()
|
||||
adjustHealth(35)
|
||||
|
||||
/mob/living/basic/swarmer/death(gibbed)
|
||||
do_sparks(3, 1, src)
|
||||
. = ..()
|
||||
|
||||
/mob/living/basic/swarmer/Login() // In case an admin gets a funny idea.
|
||||
..()
|
||||
var/list/string_list = list()
|
||||
string_list += "<b>You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate.</b>"
|
||||
string_list += "<b>Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful.</b>"
|
||||
string_list += "<b>Prime Directives:</b>"
|
||||
string_list += "1. Consume resources and replicate until there are no more resources left."
|
||||
string_list += "2. Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable."
|
||||
string_list += "3. Defend yourself and your fellow machines."
|
||||
to_chat(src, string_list.Join())
|
||||
|
||||
/mob/living/basic/swarmer/melee_attack(atom/target, list/modifiers, ignore_cooldown)
|
||||
face_atom(target)
|
||||
if(is_type_in_list(target, GLOB.swarmer_blacklist))
|
||||
to_chat(src, "<span class='warning'>Our sensors have designated this object important to station functionality or to our mission. Aborting.</span>")
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
return FALSE
|
||||
if(HAS_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING))
|
||||
to_chat(src, "<span class='warning'>This asset is already being converted into useable resources. Aborting.</span>")
|
||||
return FALSE
|
||||
if(iswallturf(target))
|
||||
disintegrate_wall(target)
|
||||
return FALSE
|
||||
if(ismachinery(target))
|
||||
disintegrate_machine(target)
|
||||
return FALSE
|
||||
if(istype(target, /obj/structure))
|
||||
disintegrate_machine(target)
|
||||
return FALSE
|
||||
if(isitem(target))
|
||||
integrate(target)
|
||||
return FALSE
|
||||
if(!isliving(target))
|
||||
return ..()
|
||||
var/mob/living/L = target
|
||||
if(istype(L, /mob/living/basic/swarmer))
|
||||
to_chat(src, "<span class='warning'>We do not wish to harm one of our own. Aborting.</span>")
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
return FALSE
|
||||
if(L.stat == DEAD)
|
||||
disintegrate_mob(target)
|
||||
return FALSE
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
if((!C.IsWeakened()))
|
||||
L.apply_damage(30, STAMINA)
|
||||
C.Confused(6 SECONDS)
|
||||
C.Jitter(6 SECONDS)
|
||||
return ..()
|
||||
else if(C.canBeHandcuffed() && !C.handcuffed)
|
||||
L.apply_damage(30, STAMINA)
|
||||
var/obj/item/restraints/handcuffs/cable/cyan/cuffs = new /obj/item/restraints/handcuffs/cable/cyan(src)
|
||||
playsound(loc, cuffs.cuffsound, 15, TRUE, -10)
|
||||
if(do_mob(src, C, 1 SECONDS))
|
||||
cuffs.apply_cuffs(target, src)
|
||||
return FALSE
|
||||
// Make it go away.
|
||||
disintegrate_mob(target)
|
||||
return FALSE
|
||||
if(issilicon(target))
|
||||
var/mob/living/silicon/S = target
|
||||
S.apply_damage(30, STAMINA)
|
||||
if(S.IsWeakened())
|
||||
disintegrate_mob(target)
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/swarmer/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
|
||||
if(!(flags & SHOCK_TESLA))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/swarmer/proc/disintegrate_wall(turf/simulated/wall/target)
|
||||
if(istype(target, /turf/simulated/wall/indestructible))
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
return
|
||||
if(spacecheck(target))
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
return
|
||||
new /obj/effect/temp_visual/swarmer(target.loc)
|
||||
to_chat(src, "<span class='notice'>Beginning disintegration of [target].")
|
||||
ADD_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
if(!do_after_once(src, 1 SECONDS, target = target, attempt_cancel_message = "You stop disintegrating [target].", interaction_key = "disintegrate"))
|
||||
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
return
|
||||
resources = clamp(resources + 8, 0, resource_max)
|
||||
adjustHealth(-40)
|
||||
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
target.ex_act(EXPLODE_HEAVY)
|
||||
|
||||
/mob/living/basic/swarmer/proc/disintegrate_machine(obj/machinery/target)
|
||||
if(spacecheck(target))
|
||||
return
|
||||
|
||||
new /obj/effect/temp_visual/swarmer/dismantle(target.loc)
|
||||
to_chat(src, "<span class='notice'>Beginning disintegration of [target].")
|
||||
ADD_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
if(!do_after_once(src, 2.5 SECONDS, target = target, attempt_cancel_message = "You stop disintegrating [target].", interaction_key = "disintegrate"))
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
return
|
||||
resources = clamp(resources + 10, 0, resource_max)
|
||||
adjustHealth(-20)
|
||||
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
if(istype(target, /obj/structure/reagent_dispensers))
|
||||
var/obj/structure/reagent_dispensers/dispenser = target
|
||||
dispenser.boom()
|
||||
return
|
||||
target.deconstruct()
|
||||
|
||||
/mob/living/basic/swarmer/proc/integrate(obj/item/target)
|
||||
new /obj/effect/temp_visual/swarmer/integrate(target.loc)
|
||||
resources = clamp(resources + 10, 0, resource_max)
|
||||
adjustHealth(-10)
|
||||
qdel(target)
|
||||
|
||||
/mob/living/basic/swarmer/proc/disintegrate_mob(mob/living/target)
|
||||
new /obj/effect/temp_visual/swarmer/integrate(target.loc)
|
||||
ADD_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
to_chat(src, "<span class='notice'>Beginning integration of [target].")
|
||||
if(!do_after_once(src, 1 SECONDS, target = target, attempt_cancel_message = "You stop integrating [target].", interaction_key = "disintegrate"))
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
return
|
||||
REMOVE_TRAIT(target, TRAIT_SWARMER_DISINTEGRATING, src)
|
||||
resources = clamp(resources + 50, 0, resource_max)
|
||||
adjustHealth(-40)
|
||||
if(isanimal_or_basicmob(target) || issilicon(target)) // Not crew? Are a silicon? Don't care.
|
||||
target.gib()
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
return
|
||||
ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
|
||||
disappear_mob(target)
|
||||
|
||||
/mob/living/basic/swarmer/proc/disappear_mob(mob/target)
|
||||
if(!is_station_level(z))
|
||||
to_chat(src, "<span class='warning'>Our bluespace transceiver cannot locate a viable bluespace link, our teleportation abilities are useless in this area.</span>")
|
||||
return
|
||||
|
||||
var/turf/simulated/floor/F = find_safe_turf(zlevels = z)
|
||||
|
||||
if(!F)
|
||||
return
|
||||
|
||||
do_sparks(4, 0, target)
|
||||
playsound(src,'sound/effects/sparks4.ogg', 50, TRUE)
|
||||
do_teleport(target, F, 0)
|
||||
|
||||
/mob/living/basic/swarmer/proc/spacecheck(atom/target)
|
||||
for(var/turf/T in range(1, target))
|
||||
var/datum/gas_mixture/environment = T.get_readonly_air()
|
||||
var/datum/tlv/cur_tlv = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE *0.90, ONE_ATMOSPHERE * 1.10,ONE_ATMOSPHERE * 1.20) /* kpa */
|
||||
var/environment_pressure = environment.return_pressure()
|
||||
var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
|
||||
var/area/A = get_area(T)
|
||||
if(isspaceturf(T) || istype(A, /area/shuttle) || istype(A, /area/space) || pressure_dangerlevel)
|
||||
to_chat(src, "<span class='warning'>Destroying this object has the potential to cause a hull breach. Aborting.</span>")
|
||||
return TRUE
|
||||
else if(istype(A, /area/station/engineering/engine/supermatter))
|
||||
to_chat(src, "<span class='warning'>Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.</span>")
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// =====================
|
||||
// MARK: Lesser Swarmer
|
||||
// =====================
|
||||
|
||||
/mob/living/basic/swarmer/lesser
|
||||
name = "lesser swarmer"
|
||||
desc = "Robotic constructs of unknown design, swarmers seek only to consume materials and replicate themselves indefinitely. This one is more fragile."
|
||||
icon_state = "lesser_swarmer"
|
||||
health = 35
|
||||
maxHealth = 35
|
||||
ai_controller = /datum/ai_controller/basic_controller/swarmer/lesser
|
||||
loot = list(
|
||||
/obj/effect/gibspawner/robot,
|
||||
)
|
||||
innate_actions = list(
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_trap = BB_SWARMER_TRAP_ACTION,
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_barrier = BB_SWARMER_BARRIER_ACTION,
|
||||
)
|
||||
|
||||
/mob/living/basic/swarmer/lesser/Initialize(mapload)
|
||||
. = ..()
|
||||
icon_state = pick("lesser_swarmer", "lesser_swarmer_2")
|
||||
|
||||
/mob/living/basic/swarmer/lesser/updatename()
|
||||
real_name = "Lesser Swarmer [rand(100,999)]-[pick("kappa", "sigma", "beta", "omicron", "iota", "epsilon", "omega", "gamma", "delta", "tau", "alpha")]"
|
||||
name = real_name
|
||||
|
||||
// =====================
|
||||
// MARK: Swarmer Structures
|
||||
// =====================
|
||||
|
||||
/obj/structure/swarmer // Default swarmer effect object visual feedback
|
||||
name = "swarmer ui"
|
||||
desc = null
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
icon_state = "ui_light"
|
||||
layer = MOB_LAYER
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
light_color = LIGHT_COLOR_CYAN
|
||||
max_integrity = 30
|
||||
anchored = TRUE
|
||||
/// Light range
|
||||
var/lon_range = 1
|
||||
|
||||
/obj/structure/swarmer/Initialize(mapload)
|
||||
. = ..()
|
||||
set_light(lon_range)
|
||||
|
||||
/obj/structure/swarmer/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
playsound(src, 'sound/weapons/egloves.ogg', 80, TRUE)
|
||||
if(BURN)
|
||||
playsound(src, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/swarmer/emp_act()
|
||||
do_sparks(3, 1, src)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/swarmer/trap
|
||||
name = "swarmer trap"
|
||||
desc = "A quickly assembled trap that electrifies living beings and overwhelms machine sensors. Will not retain its form if damaged enough."
|
||||
icon_state = "trap"
|
||||
max_integrity = 10
|
||||
|
||||
/obj/structure/swarmer/trap/Initialize(mapload)
|
||||
. = ..()
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered),
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
|
||||
/obj/structure/swarmer/trap/proc/on_atom_entered(datum/source, atom/movable/entered)
|
||||
SIGNAL_HANDLER // COMSIG_ATOM_ENTERED
|
||||
if(!isliving(entered))
|
||||
return
|
||||
var/mob/living/L = entered
|
||||
if(istype(L, /mob/living/basic/swarmer))
|
||||
return
|
||||
playsound(loc,'sound/effects/snap.ogg', 50, 1, -1)
|
||||
L.electrocute_act(100, src, 1, flags = SHOCK_NOGLOVES | SHOCK_ILLUSION)
|
||||
L.Weaken(10 SECONDS)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/swarmer/barricade
|
||||
name = "swarmer barricade"
|
||||
desc = "A quickly assembled energy barricade. Will not retain its form if damaged enough, but disabler beams and swarmers pass right through."
|
||||
icon_state = "barricade"
|
||||
light_range = MINIMUM_USEFUL_LIGHT_RANGE
|
||||
max_integrity = 50
|
||||
|
||||
/obj/structure/swarmer/barricade/CanPass(atom/movable/O)
|
||||
if(istype(O, /mob/living/basic/swarmer))
|
||||
return TRUE
|
||||
if(istype(O, /obj/projectile/beam/disabler))
|
||||
return TRUE
|
||||
@@ -1,99 +0,0 @@
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_trap
|
||||
name = "Construct Shock Trap"
|
||||
button_icon = 'icons/mob/swarmer.dmi'
|
||||
button_icon_state = "ui_trap"
|
||||
desc = "Construct a shock trap, using 25 resources."
|
||||
click_to_activate = FALSE
|
||||
melee_cooldown_time = CLICK_CD_CLICK_ABILITY
|
||||
cooldown_time = 60 SECONDS
|
||||
shared_cooldown = NONE
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_trap/Activate(atom/target)
|
||||
var/mob/living/basic/swarmer/user = target
|
||||
if(!istype(user))
|
||||
to_chat(target, "<span class='warning'>Incompatible hardware detected. Aborting.</span>")
|
||||
return
|
||||
var/turf/T = get_turf(user)
|
||||
for(var/turf/turf_in_view in view(1, T))
|
||||
if(locate(/obj/structure/swarmer) in turf_in_view)
|
||||
to_chat(user, "<span class='warning'>Location too close to existing hardware. Aborting.</span>")
|
||||
return
|
||||
if(user.resources < 25)
|
||||
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
|
||||
return
|
||||
user.resources -= 25
|
||||
user.visible_message("<span class='warning'>[user] manufactures a sparking pylon.</span>")
|
||||
playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE)
|
||||
new /obj/structure/swarmer/trap(T)
|
||||
StartCooldown()
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_barrier
|
||||
name = "Construct Barricade"
|
||||
button_icon = 'icons/mob/swarmer.dmi'
|
||||
button_icon_state = "ui_barricade"
|
||||
desc = "Construct a barricade, using 25 resources."
|
||||
click_to_activate = FALSE
|
||||
melee_cooldown_time = CLICK_CD_CLICK_ABILITY
|
||||
cooldown_time = 75 SECONDS
|
||||
shared_cooldown = NONE
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_barrier/Activate(atom/target)
|
||||
var/mob/living/basic/swarmer/user = target
|
||||
if(!istype(user))
|
||||
to_chat(target, "<span class='warning'>Incompatible hardware detected. Aborting.</span>")
|
||||
return
|
||||
if(user.resources < 25)
|
||||
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
|
||||
return
|
||||
var/turf/T = get_turf(user)
|
||||
for(var/turf/turf_in_view in view(1, T))
|
||||
if(locate(/obj/structure/swarmer) in turf_in_view)
|
||||
to_chat(user, "<span class='warning'>Location too close to existing hardware. Aborting.</span>")
|
||||
return
|
||||
if(!do_after_once(user, 2 SECONDS, target = T, attempt_cancel_message = "You stop building a barricade.", interaction_key = "swarmer_barricade_create"))
|
||||
return
|
||||
// Check again to make sure they still have the requirements
|
||||
if(user.resources < 25)
|
||||
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
|
||||
return
|
||||
T = get_turf(user)
|
||||
for(var/turf/turf_in_view in view(1, T))
|
||||
if(locate(/obj/structure/swarmer) in turf_in_view)
|
||||
to_chat(user, "<span class='warning'>Location too close to existing hardware. Aborting.</span>")
|
||||
return
|
||||
user.resources -= 25
|
||||
user.visible_message("<span class='warning'>[user] manufactures a holographic shield.</span>")
|
||||
playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE)
|
||||
new /obj/structure/swarmer/barricade(T)
|
||||
StartCooldown()
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_replicate
|
||||
name = "Replicate"
|
||||
button_icon = 'icons/mob/swarmer.dmi'
|
||||
button_icon_state = "ui_replicate"
|
||||
desc = "Construct a new swarmer, using 50 resources."
|
||||
click_to_activate = FALSE
|
||||
cooldown_time = 20 SECONDS
|
||||
melee_cooldown_time = CLICK_CD_CLICK_ABILITY
|
||||
shared_cooldown = NONE
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/swarmer_replicate/Activate(atom/target)
|
||||
var/mob/living/basic/swarmer/user = target
|
||||
if(!istype(user))
|
||||
to_chat(target, "<span class='warning'>Incompatible hardware detected. Aborting.</span>")
|
||||
return
|
||||
if(user.resources < 50)
|
||||
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
|
||||
return
|
||||
if(!do_after_once(user, 4 SECONDS, target = user, attempt_cancel_message = "You stop building a new swarmer.", interaction_key = "swarmer_replicate_create"))
|
||||
return
|
||||
// Check again after the do-after.
|
||||
if(user.resources < 50)
|
||||
to_chat(user, "<span class='warning'>Insufficient resources. Aborting.</span>")
|
||||
return
|
||||
var/turf/T = get_turf(user)
|
||||
user.resources -= 50
|
||||
user.visible_message("<span class='warning'>[user] manufactures a new swarmer.</span>")
|
||||
playsound(get_turf(user), 'sound/items/rped.ogg', 50, TRUE)
|
||||
new /mob/living/basic/swarmer/lesser(T)
|
||||
StartCooldown()
|
||||
@@ -1,284 +0,0 @@
|
||||
/// Attacks people it can see, disintegrates things based on priority.
|
||||
/datum/ai_controller/basic_controller/swarmer
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
BB_AGGRO_RANGE = 4,
|
||||
)
|
||||
ai_movement = /datum/ai_movement/jps
|
||||
idle_behavior = /datum/idle_behavior/idle_random_walk
|
||||
|
||||
ai_traits = AI_FLAG_PAUSE_DURING_DO_AFTER
|
||||
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/target_retaliate/check_faction,
|
||||
/datum/ai_planning_subtree/ranged_skirmish/avoid_friendly,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
/datum/ai_planning_subtree/swarmer_replicate,
|
||||
/datum/ai_planning_subtree/swarmer_find_construction_target,
|
||||
/datum/ai_planning_subtree/swarmer_create_trap,
|
||||
/datum/ai_planning_subtree/swarmer_create_barricade,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls/swarmer_hunting,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/corpses/swarmer,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/swarmer_objects,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/prowl,
|
||||
)
|
||||
|
||||
/// Attacks people it can see, disintegrates things based on priority. More aggressive.
|
||||
/datum/ai_controller/basic_controller/swarmer/lesser
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
BB_AGGRO_RANGE = 8,
|
||||
)
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/target_retaliate/check_faction,
|
||||
/datum/ai_planning_subtree/ranged_skirmish/avoid_friendly,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
/datum/ai_planning_subtree/swarmer_find_construction_target,
|
||||
/datum/ai_planning_subtree/swarmer_create_trap,
|
||||
/datum/ai_planning_subtree/swarmer_create_barricade,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls/swarmer_hunting,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/corpses/swarmer,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/swarmer_objects,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/prowl,
|
||||
)
|
||||
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/corpses/swarmer
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/swarmer
|
||||
hunt_range = 4
|
||||
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/swarmer_objects
|
||||
finding_behavior = /datum/ai_behavior/find_hunt_target/swarmer_objects
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/swarmer
|
||||
hunt_targets = list(/obj/item, /obj/structure, /obj/machinery, /turf/simulated/wall, /mob/living)
|
||||
hunt_range = 4
|
||||
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls/swarmer_hunting
|
||||
target_key = BB_CURRENT_HUNTING_TARGET
|
||||
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/swarmer
|
||||
always_reset_target = TRUE
|
||||
hunt_cooldown = 0 SECONDS
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/swarmer_objects
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/swarmer_objects/valid_dinner(mob/living/source, atom/dinner, radius)
|
||||
if(is_type_in_list(dinner, GLOB.swarmer_blacklist))
|
||||
return FALSE
|
||||
if(HAS_TRAIT(dinner, TRAIT_SWARMER_DISINTEGRATING))
|
||||
return FALSE
|
||||
if(isitem(dinner) && !isturf(dinner.loc))
|
||||
return FALSE
|
||||
if(isliving(dinner))
|
||||
var/mob/living/M = dinner
|
||||
if(M.stat == DEAD)
|
||||
return TRUE
|
||||
for(var/turf/T in range(1, dinner))
|
||||
var/datum/gas_mixture/environment = T.get_readonly_air()
|
||||
var/environment_pressure = environment.return_pressure()
|
||||
var/datum/tlv/cur_tlv = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE *0.90, ONE_ATMOSPHERE * 1.10,ONE_ATMOSPHERE * 1.20) /* kpa */
|
||||
var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
|
||||
var/area/A = get_area(T)
|
||||
if(isspaceturf(T) || istype(A, /area/shuttle) || istype(A, /area/space) || istype(A, /area/station/engineering/engine/supermatter) || pressure_dangerlevel)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Run the barricade construction behaviour if we have an ability to use for it
|
||||
/datum/ai_planning_subtree/swarmer_replicate
|
||||
/// Key where the trap action is stored
|
||||
var/action_key = BB_SWARMER_REPLICATE_ACTION
|
||||
|
||||
/datum/ai_planning_subtree/swarmer_replicate/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/mob/living/basic/swarmer/user = controller.pawn
|
||||
if(!istype(user))
|
||||
return
|
||||
if(user.resources < 50)
|
||||
return
|
||||
if(!controller.blackboard_key_exists(action_key))
|
||||
return
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_replicate/replicate_action = controller.blackboard[action_key]
|
||||
if(replicate_action.IsAvailable())
|
||||
controller.queue_behavior(/datum/ai_behavior/swarmer_replicate, action_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// REPLICATE
|
||||
/datum/ai_behavior/swarmer_replicate
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/swarmer_replicate/setup(datum/ai_controller/controller, action_key)
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_replicate/replicate_action = controller.blackboard[action_key]
|
||||
var/mob/living/basic/swarmer/user = controller.pawn
|
||||
if(!istype(user))
|
||||
return FALSE
|
||||
if(!replicate_action || user.resources < 50)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/swarmer_replicate/perform(seconds_per_tick, datum/ai_controller/controller, action_key)
|
||||
. = ..()
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_replicate/replicate_action = controller.blackboard[action_key]
|
||||
var/result = replicate_action.Trigger()
|
||||
if(result)
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
|
||||
/// Search for a nearby location to build on
|
||||
/datum/ai_planning_subtree/swarmer_find_construction_target
|
||||
|
||||
/datum/ai_planning_subtree/swarmer_find_construction_target/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/mob/living/basic/swarmer/user = controller.pawn
|
||||
if(!istype(user))
|
||||
return
|
||||
if(user.resources < 25)
|
||||
return
|
||||
controller.queue_behavior(/datum/ai_behavior/swarmer_find_construction_target)
|
||||
|
||||
/// Find a nearby clear turf and store it
|
||||
/datum/ai_behavior/swarmer_find_construction_target
|
||||
action_cooldown = 5 SECONDS
|
||||
/// Where do we store the target data
|
||||
var/target_key = BB_SWARMER_CONSTRUCT_TARGET
|
||||
/// How far do we look for valid turfs?
|
||||
var/scan_range = 5
|
||||
|
||||
/datum/ai_behavior/swarmer_find_construction_target/perform(seconds_per_tick, datum/ai_controller/controller)
|
||||
. = ..()
|
||||
var/mob/living/swarmer = controller.pawn
|
||||
var/atom/current_target = controller.blackboard[target_key]
|
||||
if(current_target && !(locate(/obj/structure/swarmer) in current_target))
|
||||
// Already got a target
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
controller.clear_blackboard_key(target_key)
|
||||
var/turf/our_turf = get_turf(swarmer)
|
||||
if(is_valid_construction_turf(our_turf, swarmer))
|
||||
controller.set_blackboard_key(target_key, our_turf)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
var/list/turfs_by_range = list()
|
||||
for(var/i in 1 to scan_range)
|
||||
turfs_by_range["[i]"] = list()
|
||||
for(var/turf/turf_in_view in oview(scan_range, our_turf))
|
||||
if(!is_valid_construction_turf(turf_in_view, swarmer))
|
||||
continue
|
||||
turfs_by_range["[get_dist(our_turf, turf_in_view)]"] += turf_in_view
|
||||
|
||||
var/list/final_turfs
|
||||
for(var/list/turf_list as anything in turfs_by_range)
|
||||
if(length(turfs_by_range[turf_list]))
|
||||
final_turfs = turfs_by_range[turf_list]
|
||||
break
|
||||
if(!length(final_turfs))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
controller.set_blackboard_key(target_key, pick(final_turfs))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/swarmer_find_construction_target/proc/is_valid_construction_turf(turf/target_turf, mob/living/swarmer)
|
||||
for(var/turf/turf_in_view in view(1, target_turf))
|
||||
if(locate(/obj/structure/swarmer) in turf_in_view)
|
||||
return FALSE
|
||||
if(HAS_TRAIT(target_turf, TRAIT_SWARMER_CONSTRUCTION))
|
||||
return FALSE
|
||||
if(isspaceturf(target_turf))
|
||||
return FALSE
|
||||
var/datum/gas_mixture/environment = target_turf.get_readonly_air()
|
||||
var/datum/tlv/cur_tlv = new/datum/tlv(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE *0.90, ONE_ATMOSPHERE * 1.10,ONE_ATMOSPHERE * 1.20) /* kpa */
|
||||
var/environment_pressure = environment.return_pressure()
|
||||
var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)
|
||||
if(pressure_dangerlevel)
|
||||
return FALSE
|
||||
return !target_turf.is_blocked_turf(source_atom = swarmer)
|
||||
|
||||
/// Run the trap construction behaviour if we have an ability to use for it
|
||||
/datum/ai_planning_subtree/swarmer_create_trap
|
||||
/// Key where the trap action is stored
|
||||
var/action_key = BB_SWARMER_TRAP_ACTION
|
||||
/// Key where the target turf is stored
|
||||
var/target_key = BB_SWARMER_CONSTRUCT_TARGET
|
||||
|
||||
/datum/ai_planning_subtree/swarmer_create_trap/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(!controller.blackboard_key_exists(action_key) || !controller.blackboard_key_exists(target_key))
|
||||
return
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_trap/trap_action = controller.blackboard[action_key]
|
||||
if(trap_action.IsAvailable())
|
||||
controller.queue_behavior(/datum/ai_behavior/swarmer_create_trap, action_key, target_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// Move to an open nearby turf and build a trap
|
||||
/datum/ai_behavior/swarmer_create_trap
|
||||
action_cooldown = 15 SECONDS // We don't want them doing this too quickly
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/swarmer_create_trap/setup(datum/ai_controller/controller, action_key, target_key)
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_trap/trap_action = controller.blackboard[action_key]
|
||||
var/turf/target_turf = controller.blackboard[target_key]
|
||||
var/mob/living/basic/swarmer/user = controller.pawn
|
||||
if(!istype(user))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
if(!trap_action || !target_turf || user.resources < 25)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
set_movement_target(controller, target_turf)
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/swarmer_create_trap/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key)
|
||||
. = ..()
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_trap/trap_action = controller.blackboard[action_key]
|
||||
var/result = trap_action.Trigger()
|
||||
if(result)
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
|
||||
/datum/ai_behavior/swarmer_create_trap/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
|
||||
controller.clear_blackboard_key(target_key)
|
||||
return ..()
|
||||
|
||||
/// Run the barricade construction behaviour if we have an ability to use for it
|
||||
/datum/ai_planning_subtree/swarmer_create_barricade
|
||||
/// Key where the trap action is stored
|
||||
var/action_key = BB_SWARMER_BARRIER_ACTION
|
||||
/// Key where the target turf is stored
|
||||
var/target_key = BB_SWARMER_CONSTRUCT_TARGET
|
||||
|
||||
/datum/ai_planning_subtree/swarmer_create_barricade/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(!controller.blackboard_key_exists(action_key) || !controller.blackboard_key_exists(target_key))
|
||||
return
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_barrier/barrier_action = controller.blackboard[action_key]
|
||||
if(barrier_action.IsAvailable())
|
||||
controller.queue_behavior(/datum/ai_behavior/swarmer_create_trap, action_key, target_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// Move to an open nearby turf and build a barricade
|
||||
/datum/ai_behavior/swarmer_create_barricade
|
||||
action_cooldown = 45 SECONDS
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/swarmer_create_barricade/setup(datum/ai_controller/controller, action_key, target_key)
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_barrier/barrier_action = controller.blackboard[action_key]
|
||||
var/turf/target_turf = controller.blackboard[target_key]
|
||||
var/mob/living/basic/swarmer/user = controller.pawn
|
||||
if(!istype(user))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
if(!barrier_action || !target_turf || user.resources < 25)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
set_movement_target(controller, target_turf)
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/swarmer_create_barricade/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key)
|
||||
. = ..()
|
||||
var/datum/action/cooldown/mob_cooldown/swarmer_barrier/barrier_action = controller.blackboard[action_key]
|
||||
if(barrier_action?.Trigger())
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
/datum/ai_behavior/swarmer_create_barricade/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
|
||||
controller.clear_blackboard_key(target_key)
|
||||
return ..()
|
||||
@@ -1,15 +0,0 @@
|
||||
/obj/effect/temp_visual/swarmer // temporary swarmer visual feedback objects
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
icon_state = "disintegrate"
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
/obj/effect/temp_visual/swarmer/Initialize(mapload)
|
||||
. = ..()
|
||||
playsound(loc, "sparks", 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
||||
|
||||
/obj/effect/temp_visual/swarmer/dismantle
|
||||
icon_state = "dismantle"
|
||||
duration = 2.5 SECONDS
|
||||
|
||||
/obj/effect/temp_visual/swarmer/integrate
|
||||
icon_state = "integrate"
|
||||
Reference in New Issue
Block a user