[MIRROR] Basic Constructs: parent type + Harvester [MDB IGNORE] (#24258)

* Basic Constructs: parent type + Harvester (#78807)

## About The Pull Request

I kind of hate cult as a whole, but I like these little guys. Let's
basic-ize them.

This PR begins the process with the harbinger of the Red Harvest, the
Harvester! Their actual capabilities have been changed very little,
except that most of their unique properties have been moved to
components and elements. The basic parent type of constructs has also
been set up to make the next bunch of conversions easier.

- Constructs capable of repair now receive the healing hands component.
Healing hands has been extended, to allow the healing particles to come
in custom colors, and to allow it to print the target's health if the
target is not a carbon.
- Repairing constructs also receive a new element: Structure repair is a
lighter-weight variant on healing hands that allows repairing clicked-on
atoms of specified types.
- Constructs capable of damaging walls, meanwhile, receive the wall
smasher element.

Harvesters in specific have two special elements:
- The existing "amputating limbs" element, making them instantly rip a
limb off of any carbon they attack. As before, if they attempt this on a
carbon with no arms or legs, the harvester will hear Nar'Sie's call to
bring the victim to her.
- A new "wall walker" element, allowing them to walk through walls of
specified type (cult walls for harvesters) and allowing them to drag any
atom through as well.

Other than laying the groundwork, there's not much else here. I started
with Harvesters specifically because they are only ever
player-controlled, which makes things easy.

I'm not completely happy with the use of healing hands here - it gets
the job done, but currently loses a bit of the previous flavor (a
healing beam as a visual; printing the target's health in cult span). I
may extend it further to allow this behavior.

I've included an UpdatePaths script, even if these things shouldn't be
mapped, just in case something fucky is going on on a downstream. You
never know.
## Why It's Good For The Game

Constructs, currently, occupy _19_ spots on the simple animal list. This
is something close to 10% of all the remaining ones. Also, like
everything to do with cult, construct code is janky, old, and
desperately in need of updating. This is the first step.
## Changelog
🆑
refactor: Harvester constructs have been updated to the basic mob
framework. This should have very little impact on their behavior, but
please report any issues.
/🆑

---------

Co-authored-by: san7890 <the@ san7890.com>

* Basic Constructs: parent type + Harvester

---------

Co-authored-by: lizardqueenlexi <105025397+lizardqueenlexi@users.noreply.github.com>
Co-authored-by: san7890 <the@ san7890.com>
This commit is contained in:
SkyratBot
2023-10-11 00:37:00 +02:00
committed by GitHub
parent ad77a0b7ab
commit ae2c7f8fbb
18 changed files with 316 additions and 72 deletions

View File

@@ -137,6 +137,10 @@
#define COMSIG_LIVING_UNARMED_ATTACK "living_unarmed_attack" #define COMSIG_LIVING_UNARMED_ATTACK "living_unarmed_attack"
///From base of mob/living/MobBump() (mob/living) ///From base of mob/living/MobBump() (mob/living)
#define COMSIG_LIVING_MOB_BUMP "living_mob_bump" #define COMSIG_LIVING_MOB_BUMP "living_mob_bump"
///From base of mob/living/Bump() (turf/closed)
#define COMSIG_LIVING_WALL_BUMP "living_wall_bump"
///From base of turf/closed/Exited() (turf/closed)
#define COMSIG_LIVING_WALL_EXITED "living_wall_exited"
///From base of mob/living/ZImpactDamage() (mob/living, levels, turf/t) ///From base of mob/living/ZImpactDamage() (mob/living, levels, turf/t)
#define COMSIG_LIVING_Z_IMPACT "living_z_impact" #define COMSIG_LIVING_Z_IMPACT "living_z_impact"
#define NO_Z_IMPACT_DAMAGE (1<<0) #define NO_Z_IMPACT_DAMAGE (1<<0)

View File

@@ -148,6 +148,9 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
#define ismining(A) (istype(A, /mob/living/simple_animal/hostile/asteroid) || istype(A, /mob/living/basic/mining)) #define ismining(A) (istype(A, /mob/living/simple_animal/hostile/asteroid) || istype(A, /mob/living/basic/mining))
/// constructs, which are both simple and basic for now
#define isconstruct(A) (istype(A, /mob/living/simple_animal/hostile/construct) || istype(A, /mob/living/basic/construct))
//Simple animals //Simple animals
#define isanimal(A) (istype(A, /mob/living/simple_animal)) #define isanimal(A) (istype(A, /mob/living/simple_animal))
@@ -175,8 +178,6 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
#define isguardian(A) (istype(A, /mob/living/simple_animal/hostile/guardian)) #define isguardian(A) (istype(A, /mob/living/simple_animal/hostile/guardian))
#define isconstruct(A) (istype(A, /mob/living/simple_animal/hostile/construct))
#define ismegafauna(A) (istype(A, /mob/living/simple_animal/hostile/megafauna)) #define ismegafauna(A) (istype(A, /mob/living/simple_animal/hostile/megafauna))
#define isclown(A) (istype(A, /mob/living/basic/clown)) #define isclown(A) (istype(A, /mob/living/basic/clown))

View File

@@ -91,6 +91,7 @@ GLOBAL_LIST_INIT(phobia_mobs, list(
"the supernatural" = typecacheof(list( "the supernatural" = typecacheof(list(
/mob/dead/observer, /mob/dead/observer,
/mob/living/basic/bat, /mob/living/basic/bat,
/mob/living/basic/construct,
/mob/living/basic/demon, /mob/living/basic/demon,
/mob/living/basic/faithless, /mob/living/basic/faithless,
/mob/living/basic/ghost, /mob/living/basic/ghost,

View File

@@ -32,6 +32,10 @@
var/action_text var/action_text
/// Text to print when action completes, replaces %SOURCE% with healer and %TARGET% with healed mob /// Text to print when action completes, replaces %SOURCE% with healer and %TARGET% with healed mob
var/complete_text var/complete_text
/// Whether to print the target's remaining health after healing (for non-carbon targets only)
var/show_health
/// Color for the healing effect
var/heal_color
/datum/component/healing_touch/Initialize( /datum/component/healing_touch/Initialize(
heal_brute = 20, heal_brute = 20,
@@ -46,6 +50,8 @@
self_targetting = HEALING_TOUCH_NOT_SELF, self_targetting = HEALING_TOUCH_NOT_SELF,
action_text = "%SOURCE% begins healing %TARGET%", action_text = "%SOURCE% begins healing %TARGET%",
complete_text = "%SOURCE% finishes healing %TARGET%", complete_text = "%SOURCE% finishes healing %TARGET%",
show_health = FALSE,
heal_color = COLOR_HEALING_CYAN,
) )
if (!isliving(parent)) if (!isliving(parent))
return COMPONENT_INCOMPATIBLE return COMPONENT_INCOMPATIBLE
@@ -62,6 +68,8 @@
src.self_targetting = self_targetting src.self_targetting = self_targetting
src.action_text = action_text src.action_text = action_text
src.complete_text = complete_text src.complete_text = complete_text
src.show_health = show_health
src.heal_color = heal_color
RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(try_healing)) // Players RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(try_healing)) // Players
RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(try_healing)) // NPCs RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(try_healing)) // NPCs
@@ -147,7 +155,11 @@
healer.visible_message(span_notice("[format_string(complete_text, healer, target)]")) healer.visible_message(span_notice("[format_string(complete_text, healer, target)]"))
target.heal_overall_damage(brute = heal_brute, burn = heal_burn, stamina = heal_stamina, required_bodytype = required_bodytype) target.heal_overall_damage(brute = heal_brute, burn = heal_burn, stamina = heal_stamina, required_bodytype = required_bodytype)
new /obj/effect/temp_visual/heal(get_turf(target), COLOR_HEALING_CYAN) new /obj/effect/temp_visual/heal(get_turf(target), heal_color)
if(show_health && !iscarbon(target))
var/formatted_string = format_string("%TARGET% now has <b>[target.health]/[target.maxHealth] health.</b>", healer, target)
healer.visible_message(span_danger(formatted_string))
/// Reformats the passed string with the replacetext keys /// Reformats the passed string with the replacetext keys
/datum/component/healing_touch/proc/format_string(string, atom/source, atom/target) /datum/component/healing_touch/proc/format_string(string, atom/source, atom/target)

View File

@@ -1,4 +1,4 @@
/// This component will intercept bare-handed attacks by the owner on critically injured carbons and amputate random limbs instead /// This component will intercept bare-handed attacks by the owner on sufficiently injured carbons and amputate random limbs instead
/datum/element/amputating_limbs /datum/element/amputating_limbs
element_flags = ELEMENT_BESPOKE element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2 argument_hash_start_idx = 2

View File

@@ -0,0 +1,45 @@
/// Intercepts attacks from mobs with this component to instead repair specified structures.
/datum/element/structure_repair
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// How much to heal structures by
var/heal_amount
/// Typecache of types of structures to repair
var/list/structure_types_typecache
/datum/element/structure_repair/Attach(
datum/target,
heal_amount = 5,
structure_types_typecache = typecacheof(list(/obj/structure)),
)
. = ..()
if (!isliving(target))
return ELEMENT_INCOMPATIBLE
src.heal_amount = heal_amount
src.structure_types_typecache = structure_types_typecache
RegisterSignals(target, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET), PROC_REF(try_repair))
/datum/element/structure_repair/Detach(datum/source)
UnregisterSignal(source, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET))
return ..()
/// If the target is of a valid type, interrupt the attack chain to repair it instead
/datum/element/structure_repair/proc/try_repair(mob/living/fixer, atom/target)
SIGNAL_HANDLER
if (!is_type_in_typecache(target, structure_types_typecache))
return
if (target.get_integrity() >= target.max_integrity)
target.balloon_alert(fixer, "not damaged!")
return COMPONENT_CANCEL_ATTACK_CHAIN
target.repair_damage(heal_amount)
fixer.Beam(target, icon_state = "sendbeam", time = 0.4 SECONDS)
fixer.visible_message(
span_danger("[fixer] repairs [target]."),
span_danger("You repair [target], leaving it at <b>[round(target.get_integrity() * 100 / target.max_integrity)]%</b> stability."),
)
return COMPONENT_CANCEL_ATTACK_CHAIN

View File

@@ -0,0 +1,49 @@
/// This element will allow the mob it's attached to to pass through a specified type of wall, and drag anything through it.
/datum/element/wall_walker
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// What kind of walls can we pass through?
var/wall_type
/datum/element/wall_walker/Attach(
datum/target,
wall_type = /turf/closed/wall,
)
. = ..()
if (!isliving(target))
return ELEMENT_INCOMPATIBLE
src.wall_type = wall_type
RegisterSignal(target, COMSIG_LIVING_WALL_BUMP, PROC_REF(try_pass_wall))
RegisterSignal(target, COMSIG_LIVING_WALL_EXITED, PROC_REF(exit_wall))
/datum/element/wall_walker/Detach(datum/source)
UnregisterSignal(source, list(COMSIG_LIVING_WALL_BUMP, COMSIG_LIVING_WALL_EXITED))
return ..()
/// If the wall is of the proper type, pass into it and keep hold on whatever you're pulling
/datum/element/wall_walker/proc/try_pass_wall(mob/living/passing_mob, turf/closed/bumped_wall)
if(!istype(bumped_wall, wall_type))
return
var/atom/movable/stored_pulling = passing_mob.pulling
if(stored_pulling) //force whatever you're pulling to come with you
stored_pulling.setDir(get_dir(stored_pulling.loc, passing_mob.loc))
stored_pulling.forceMove(passing_mob.loc)
passing_mob.forceMove(bumped_wall)
if(stored_pulling) //don't drop them because we went into a wall
passing_mob.start_pulling(stored_pulling, supress_message = TRUE)
/// If the wall is of the proper type, pull whatever you're pulling into it
/datum/element/wall_walker/proc/exit_wall(mob/living/passing_mob, turf/closed/exited_wall)
if(!istype(exited_wall, wall_type))
return
var/atom/movable/stored_pulling = passing_mob.pulling
if(isnull(stored_pulling))
return
stored_pulling.setDir(get_dir(stored_pulling.loc, passing_mob.loc))
stored_pulling.forceMove(exited_wall)
passing_mob.start_pulling(stored_pulling, supress_message = TRUE)

View File

@@ -18,16 +18,6 @@
/turf/closed/wall/mineral/cult/devastate_wall() /turf/closed/wall/mineral/cult/devastate_wall()
new sheet_type(get_turf(src), sheet_amount) new sheet_type(get_turf(src), sheet_amount)
/turf/closed/wall/mineral/cult/Exited(atom/movable/gone, direction)
. = ..()
if(istype(gone, /mob/living/simple_animal/hostile/construct/harvester)) //harvesters can go through cult walls, dragging something with
var/mob/living/simple_animal/hostile/construct/harvester/H = gone
var/atom/movable/stored_pulling = H.pulling
if(stored_pulling)
stored_pulling.setDir(direction)
stored_pulling.forceMove(src)
H.start_pulling(stored_pulling, supress_message = TRUE)
/turf/closed/wall/mineral/cult/artificer /turf/closed/wall/mineral/cult/artificer
name = "runed stone wall" name = "runed stone wall"
desc = "A cold stone wall engraved with indecipherable symbols. Studying them causes your head to pound." desc = "A cold stone wall engraved with indecipherable symbols. Studying them causes your head to pound."

View File

@@ -363,5 +363,13 @@
/turf/closed/wall/metal_foam_base /turf/closed/wall/metal_foam_base
girder_type = /obj/structure/foamedmetal girder_type = /obj/structure/foamedmetal
/turf/closed/wall/Bumped(atom/movable/bumped_atom)
. = ..()
SEND_SIGNAL(bumped_atom, COMSIG_LIVING_WALL_BUMP, src)
/turf/closed/wall/Exited(atom/movable/gone, direction)
. = ..()
SEND_SIGNAL(gone, COMSIG_LIVING_WALL_EXITED, src)
#undef MAX_DENT_DECALS #undef MAX_DENT_DECALS
#undef LEANING_OFFSET #undef LEANING_OFFSET

View File

@@ -0,0 +1,156 @@
/mob/living/basic/construct
icon = 'icons/mob/nonhuman-player/cult.dmi'
gender = NEUTER
basic_mob_flags = DEL_ON_DEATH
combat_mode = TRUE
mob_biotypes = MOB_MINERAL | MOB_SPECIAL
faction = list(FACTION_CULT)
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
maximum_survivable_temperature = INFINITY
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
pressure_resistance = 100
speed = 0
unique_name = TRUE
initial_language_holder = /datum/language_holder/construct
death_message = "collapses in a shattered heap."
speak_emote = list("hisses")
response_help_continuous = "thinks better of touching"
response_help_simple = "think better of touching"
response_disarm_continuous = "flails at"
response_disarm_simple = "flail at"
response_harm_continuous = "punches"
response_harm_simple = "punch"
// Vivid red, cause cult theme
lighting_cutoff_red = 30
lighting_cutoff_green = 5
lighting_cutoff_blue = 20
/// List of spells that this construct can cast
var/list/construct_spells = list()
/// Flavor text shown to players when they spawn as this construct
var/playstyle_string = "You are a generic construct. Your job is to not exist, and you should probably adminhelp this."
/// The construct's master
var/master = null
/// Whether this construct is currently seeking nar nar
var/seeking = FALSE
/// Whether this construct can repair other constructs or cult buildings. Gets the healing_touch component if so.
var/can_repair = FALSE
/// Whether this construct can repair itself. Works independently of can_repair.
var/can_repair_self = FALSE
/// Theme controls color. THEME_CULT is red THEME_WIZARD is purple and THEME_HOLY is blue
var/theme = THEME_CULT
/// What flavor of gunk does this construct drop on death?
var/static/list/remains = list(/obj/item/ectoplasm/construct)
/// Can this construct smash walls? Gets the wall_smasher element if so.
var/smashes_walls = FALSE
/mob/living/basic/construct/Initialize(mapload)
. = ..()
AddElement(/datum/element/simple_flying)
if(length(remains))
AddElement(/datum/element/death_drops, remains)
if(smashes_walls)
AddElement(/datum/element/wall_smasher, strength_flag = ENVIRONMENT_SMASH_WALLS)
if(can_repair)
AddComponent(\
/datum/component/healing_touch,\
heal_brute = 5,\
heal_burn = 0,\
heal_time = 0,\
valid_targets_typecache = typecacheof(list(/mob/living/basic/construct, /mob/living/simple_animal/hostile/construct, /mob/living/simple_animal/shade)),\
self_targetting = can_repair_self ? HEALING_TOUCH_ANYONE : HEALING_TOUCH_NOT_SELF,\
action_text = "%SOURCE% begins repairing %TARGET%'s dents.",\
complete_text = "%TARGET%'s dents are repaired.",\
show_health = TRUE,\
heal_color = COLOR_CULT_RED,\
)
var/static/list/structure_types = typecacheof(list(/obj/structure/destructible/cult))
AddElement(\
/datum/element/structure_repair,\
structure_types_typecache = structure_types,\
)
add_traits(list(TRAIT_HEALS_FROM_CULT_PYLONS, TRAIT_SPACEWALK), INNATE_TRAIT)
for(var/spell in construct_spells)
var/datum/action/new_spell = new spell(src)
new_spell.Grant(src)
var/spell_count = 1
for(var/datum/action/spell as anything in actions)
if(!(spell.type in construct_spells))
continue
var/pos = 2 + spell_count * 31
if(construct_spells.len >= 4)
pos -= 31 * (construct_spells.len - 4)
spell.default_button_position = "6:[pos],4:-2" // Set the default position to this random position
spell_count++
update_action_buttons()
if(icon_state)
add_overlay("glow_[icon_state]_[theme]")
/mob/living/basic/construct/Login()
. = ..()
if(!. || !client)
return FALSE
to_chat(src, span_bold(playstyle_string))
/mob/living/basic/construct/examine(mob/user)
var/text_span
switch(theme)
if(THEME_CULT)
text_span = "cult"
if(THEME_WIZARD)
text_span = "purple"
if(THEME_HOLY)
text_span = "blue"
. = list("<span class='[text_span]'>This is [icon2html(src, user)] \a <b>[src]</b>!\n[desc]")
if(health < maxHealth)
if(health >= maxHealth/2)
. += span_warning("[p_They()] look[p_s()] slightly dented.")
else
. += span_warning(span_bold("[p_They()] look[p_s()] severely dented!"))
. += "</span>"
return .
/mob/living/basic/construct/narsie_act()
return
/mob/living/basic/construct/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
return FALSE
// Allows simple constructs to repair basic constructs.
/mob/living/basic/construct/attack_animal(mob/living/simple_animal/user, list/modifiers)
if(!isconstruct(user))
if(src != user)
return ..()
return
if(src == user) //basic constructs use the healing hands component instead
return
var/mob/living/simple_animal/hostile/construct/doll = user
if(!doll.can_repair || (doll == src && !doll.can_repair_self))
return ..()
if(theme != doll.theme)
return ..()
if(health >= maxHealth)
to_chat(user, span_cult("You cannot repair <b>[src]'s</b> dents, as [p_they()] [p_have()] none!"))
return
heal_overall_damage(brute = 5)
Beam(user, icon_state = "sendbeam", time = 4)
user.visible_message(
span_danger("[user] repairs some of \the <b>[src]'s</b> dents."),
span_cult("You repair some of <b>[src]'s</b> dents, leaving <b>[src]</b> at <b>[health]/[maxHealth]</b> health."),
)
/// Construct ectoplasm. Largely a placeholder, since the death drop element needs a unique list.
/obj/item/ectoplasm/construct
name = "blood-red ectoplasm"
desc = "Has a pungent metallic smell."

View File

@@ -1,4 +1,4 @@
/mob/living/simple_animal/hostile/construct/harvester /mob/living/basic/construct/harvester
name = "Harvester" name = "Harvester"
real_name = "Harvester" real_name = "Harvester"
desc = "A long, thin construct built to herald Nar'Sie's rise. It'll be all over soon." desc = "A long, thin construct built to herald Nar'Sie's rise. It'll be all over soon."
@@ -24,59 +24,34 @@
can_repair = TRUE can_repair = TRUE
slowed_by_drag = FALSE slowed_by_drag = FALSE
/mob/living/basic/construct/harvester/Initialize(mapload)
/mob/living/simple_animal/hostile/construct/harvester/Bump(atom/thing)
. = ..() . = ..()
if(!istype(thing, /turf/closed/wall/mineral/cult) || thing == loc) AddElement(\
return // we can go through cult walls /datum/element/amputating_limbs,\
var/atom/movable/stored_pulling = pulling surgery_time = 0,\
surgery_verb = "slicing",\
if(stored_pulling) minimum_stat = CONSCIOUS,\
stored_pulling.setDir(get_dir(stored_pulling.loc, loc)) )
stored_pulling.forceMove(loc) AddElement(/datum/element/wall_walker, /turf/closed/wall/mineral/cult)
forceMove(thing) var/datum/action/innate/seek_prey/seek = new(src)
if(stored_pulling)
start_pulling(stored_pulling, supress_message = TRUE) //drag anything we're pulling through the wall with us by magic
/mob/living/simple_animal/hostile/construct/harvester/AttackingTarget()
if(!iscarbon(target))
return ..()
var/mob/living/carbon/victim = target
if(HAS_TRAIT(victim, TRAIT_NODISMEMBER))
return ..() //ATTACK!
var/list/parts = list()
var/strong_limbs = 0
for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
if(limb.body_part == HEAD || limb.body_part == CHEST)
continue
if(!(limb.bodypart_flags & BODYPART_UNREMOVABLE))
parts += limb
else
strong_limbs++
if(!LAZYLEN(parts))
if(strong_limbs) // they have limbs we can't remove, and no parts we can, attack!
return ..()
victim.Paralyze(60)
visible_message(span_danger("[src] knocks [victim] down!"))
to_chat(src, span_cultlarge("\"Bring [victim.p_them()] to me.\""))
return FALSE
do_attack_animation(victim)
var/obj/item/bodypart/limb = pick(parts)
limb.dismember()
return FALSE
/mob/living/simple_animal/hostile/construct/harvester/Initialize(mapload)
. = ..()
var/datum/action/innate/seek_prey/seek = new()
seek.Grant(src) seek.Grant(src)
seek.Activate() seek.Activate()
/// If the attack is a limbless carbon, abort the attack, paralyze them, and get a special message from Nar'Sie.
/mob/living/basic/construct/harvester/resolve_unarmed_attack(atom/attack_target, list/modifiers)
if(!iscarbon(attack_target))
return ..()
var/mob/living/carbon/carbon_target = attack_target
for(var/obj/item/bodypart/limb as anything in carbon_target.bodyparts)
if(limb.body_part == HEAD || limb.body_part == CHEST)
continue
return ..() //if any arms or legs exist, attack
carbon_target.Paralyze(6 SECONDS)
visible_message(span_danger("[src] knocks [carbon_target] down!"))
to_chat(src, span_cultlarge("\"Bring [carbon_target.p_them()] to me.\""))
/datum/action/innate/seek_master /datum/action/innate/seek_master
name = "Seek your Master" name = "Seek your Master"
desc = "You and your master share a soul-link that informs you of their location" desc = "You and your master share a soul-link that informs you of their location"
@@ -89,7 +64,7 @@
/// Where is nar nar? Are we even looking? /// Where is nar nar? Are we even looking?
var/tracking = FALSE var/tracking = FALSE
/// The construct we're attached to /// The construct we're attached to
var/mob/living/simple_animal/hostile/construct/the_construct var/mob/living/basic/construct/the_construct
/datum/action/innate/seek_master/Grant(mob/living/player) /datum/action/innate/seek_master/Grant(mob/living/player)
the_construct = player the_construct = player
@@ -132,7 +107,7 @@
/datum/action/innate/seek_prey/Activate() /datum/action/innate/seek_prey/Activate()
if(GLOB.cult_narsie == null) if(GLOB.cult_narsie == null)
return return
var/mob/living/simple_animal/hostile/construct/harvester/the_construct = owner var/mob/living/basic/construct/harvester/the_construct = owner
if(the_construct.seeking) if(the_construct.seeking)
desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!" desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"

View File

@@ -464,7 +464,7 @@
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cult_ending_helper), CULT_VICTORY_MASS_CONVERSION), 120) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cult_ending_helper), CULT_VICTORY_MASS_CONVERSION), 120)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(ending_helper)), 270) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(ending_helper)), 270)
if(client) if(client)
makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, src, cultoverride = TRUE) makeNewConstruct(/mob/living/basic/construct/harvester, src, cultoverride = TRUE)
else else
switch(rand(1, 4)) switch(rand(1, 4))
if(1) if(1)

View File

@@ -3,7 +3,7 @@
real_name = "Construct" real_name = "Construct"
desc = "" desc = ""
gender = NEUTER gender = NEUTER
mob_biotypes = NONE mob_biotypes = MOB_MINERAL | MOB_SPECIAL
speak_emote = list("hisses") speak_emote = list("hisses")
response_help_continuous = "thinks better of touching" response_help_continuous = "thinks better of touching"
response_help_simple = "think better of touching" response_help_simple = "think better of touching"

View File

@@ -372,7 +372,7 @@
if(!MP) if(!MP)
return FALSE //Sanity, this should never happen. return FALSE //Sanity, this should never happen.
if(ispath(MP, /mob/living/simple_animal/hostile/construct)) if(ispath(MP, /mob/living/simple_animal/hostile/construct) || ispath(MP, /mob/living/basic/construct))
return FALSE //Verbs do not appear for players. return FALSE //Verbs do not appear for players.
//Good mobs! //Good mobs!

View File

@@ -112,7 +112,7 @@
return ..() return ..()
/obj/narsie/attack_ghost(mob/user) /obj/narsie/attack_ghost(mob/user)
makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, user, cultoverride = TRUE, loc_override = loc) makeNewConstruct(/mob/living/basic/construct/harvester, user, cultoverride = TRUE, loc_override = loc)
/obj/narsie/process() /obj/narsie/process()
var/datum/component/singularity/singularity_component = singularity.resolve() var/datum/component/singularity/singularity_component = singularity.resolve()

View File

@@ -73,7 +73,6 @@
/mob/living/simple_animal/hostile/construct/artificer/hostile, /mob/living/simple_animal/hostile/construct/artificer/hostile,
/mob/living/simple_animal/hostile/construct/artificer/mystic, /mob/living/simple_animal/hostile/construct/artificer/mystic,
/mob/living/simple_animal/hostile/construct/artificer/noncult, /mob/living/simple_animal/hostile/construct/artificer/noncult,
/mob/living/simple_animal/hostile/construct/harvester,
/mob/living/simple_animal/hostile/construct/juggernaut, /mob/living/simple_animal/hostile/construct/juggernaut,
/mob/living/simple_animal/hostile/construct/juggernaut/angelic, /mob/living/simple_animal/hostile/construct/juggernaut/angelic,
/mob/living/simple_animal/hostile/construct/juggernaut/hostile, /mob/living/simple_animal/hostile/construct/juggernaut/hostile,

View File

@@ -1449,6 +1449,7 @@
#include "code\datums\elements\squish.dm" #include "code\datums\elements\squish.dm"
#include "code\datums\elements\sticker.dm" #include "code\datums\elements\sticker.dm"
#include "code\datums\elements\strippable.dm" #include "code\datums\elements\strippable.dm"
#include "code\datums\elements\structure_repair.dm"
#include "code\datums\elements\swabbable.dm" #include "code\datums\elements\swabbable.dm"
#include "code\datums\elements\tear_wall.dm" #include "code\datums\elements\tear_wall.dm"
#include "code\datums\elements\temporary_atom.dm" #include "code\datums\elements\temporary_atom.dm"
@@ -1466,6 +1467,7 @@
#include "code\datums\elements\waddling.dm" #include "code\datums\elements\waddling.dm"
#include "code\datums\elements\wall_engraver.dm" #include "code\datums\elements\wall_engraver.dm"
#include "code\datums\elements\wall_smasher.dm" #include "code\datums\elements\wall_smasher.dm"
#include "code\datums\elements\wall_walker.dm"
#include "code\datums\elements\weapon_description.dm" #include "code\datums\elements\weapon_description.dm"
#include "code\datums\elements\weather_listener.dm" #include "code\datums\elements\weather_listener.dm"
#include "code\datums\elements\web_walker.dm" #include "code\datums\elements\web_walker.dm"
@@ -4446,6 +4448,8 @@
#include "code\modules\mob\living\basic\blob_minions\blobbernaut.dm" #include "code\modules\mob\living\basic\blob_minions\blobbernaut.dm"
#include "code\modules\mob\living\basic\clown\clown.dm" #include "code\modules\mob\living\basic\clown\clown.dm"
#include "code\modules\mob\living\basic\clown\clown_ai.dm" #include "code\modules\mob\living\basic\clown\clown_ai.dm"
#include "code\modules\mob\living\basic\constructs\_construct.dm"
#include "code\modules\mob\living\basic\constructs\harvester.dm"
#include "code\modules\mob\living\basic\farm_animals\deer.dm" #include "code\modules\mob\living\basic\farm_animals\deer.dm"
#include "code\modules\mob\living\basic\farm_animals\pig.dm" #include "code\modules\mob\living\basic\farm_animals\pig.dm"
#include "code\modules\mob\living\basic\farm_animals\pony.dm" #include "code\modules\mob\living\basic\farm_animals\pony.dm"
@@ -4822,7 +4826,6 @@
#include "code\modules\mob\living\simple_animal\hostile\zombie.dm" #include "code\modules\mob\living\simple_animal\hostile\zombie.dm"
#include "code\modules\mob\living\simple_animal\hostile\constructs\artificer.dm" #include "code\modules\mob\living\simple_animal\hostile\constructs\artificer.dm"
#include "code\modules\mob\living\simple_animal\hostile\constructs\constructs.dm" #include "code\modules\mob\living\simple_animal\hostile\constructs\constructs.dm"
#include "code\modules\mob\living\simple_animal\hostile\constructs\harvester.dm"
#include "code\modules\mob\living\simple_animal\hostile\constructs\juggernaut.dm" #include "code\modules\mob\living\simple_animal\hostile\constructs\juggernaut.dm"
#include "code\modules\mob\living\simple_animal\hostile\constructs\wraith.dm" #include "code\modules\mob\living\simple_animal\hostile\constructs\wraith.dm"
#include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm" #include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm"

View File

@@ -0,0 +1 @@
/mob/living/simple_animal/hostile/construct/harvester : /mob/living/basic/construct/harvester{@OLD}