diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm
index 7747a6fb93..12fb1bae48 100644
--- a/code/_globalvars/game_modes.dm
+++ b/code/_globalvars/game_modes.dm
@@ -7,6 +7,7 @@ GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report
// Cult, needs to be global so admin cultists are functional
GLOBAL_VAR_INIT(blood_target, null) // Cult Master's target or Construct's Master
GLOBAL_DATUM(blood_target_image, /image)
+GLOBAL_VAR_INIT(blood_target_reset_timer, null)
GLOBAL_DATUM(sac_mind, /datum/mind)
GLOBAL_VAR_INIT(sac_image, null)
GLOBAL_VAR_INIT(cult_vote_called, FALSE)
diff --git a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm
index a8dfb2dd4a..b82e9a9c85 100644
--- a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm
+++ b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm
@@ -117,12 +117,10 @@
if(!isnewplayer(M))
flash_color(M, flash_color="#966400", flash_time=1)
shake_camera(M, 4, 3)
- var/ratvar_chance = min(SSticker.mode.servants_of_ratvar.len, 50)
- var/narsie_chance = SSticker.mode.cult.len
- for(var/mob/living/simple_animal/hostile/construct/harvester/C in GLOB.player_list)
- narsie_chance++
+ var/ratvar_chance = min(LAZYLEN(SSticker.mode.servants_of_ratvar), 50)
+ var/narsie_chance = min(LAZYLEN(SSticker.mode.cult), 50)
ratvar_chance = rand(base_victory_chance, ratvar_chance)
- narsie_chance = rand(base_victory_chance, min(narsie_chance, 50))
+ narsie_chance = rand(base_victory_chance, narsie_chance)
if(ratvar_chance > narsie_chance)
winner = "Ratvar"
break
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 28c22d695a..32b88724bf 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -279,12 +279,13 @@
var/explanation
switch(cult_objectives[obj_count])
if("sacrifice")
- if(GLOB.sac_complete)
- explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!"
- SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS")
- else
- explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail."
- SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL")
+ if(GLOB.sac_mind)
+ if(GLOB.sac_complete)
+ explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!"
+ SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS")
+ else
+ explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail."
+ SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL")
if("eldergod")
if(!eldergod)
explanation = "Summon Nar-Sie. Success!"
diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm
index 781714c716..6258ea284d 100644
--- a/code/game/gamemodes/cult/cult_comms.dm
+++ b/code/game/gamemodes/cult/cult_comms.dm
@@ -141,7 +141,7 @@
to_chat(B.current, "[Nominee] could not win the cult's support and shall continue to serve as an acolyte.")
return FALSE
GLOB.cult_mastered = TRUE
- SSticker.mode.remove_cultist(Nominee.mind, FALSE)
+ SSticker.mode.remove_cultist(Nominee.mind, TRUE)
Nominee.mind.add_antag_datum(ANTAG_DATUM_CULT_MASTER)
for(var/datum/mind/B in SSticker.mode.cult)
if(B.current)
@@ -288,7 +288,7 @@
B.current.client.images += GLOB.blood_target_image
attached_action.owner.update_action_buttons_icon()
remove_ranged_ability("The marking rite is complete! It will last for 90 seconds.")
- addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target), 900, TIMER_OVERRIDE)
+ GLOB.blood_target_reset_timer = addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target), 900, TIMER_STOPPABLE)
return TRUE
return FALSE
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 8c01d361c0..40cad5c0dd 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -481,7 +481,12 @@ structure_check() searches for nearby cultist structures required for the invoca
if(src)
color = "#FF0000"
SSticker.mode.eldergod = FALSE
- new /obj/singularity/narsie/large(T) //Causes Nar-Sie to spawn even if the rune has been removed
+ deltimer(GLOB.blood_target_reset_timer)
+ GLOB.blood_target = new /obj/singularity/narsie/large(T) //Causes Nar-Sie to spawn even if the rune has been removed
+ for(var/datum/mind/cult_mind in SSticker.mode.cult)
+ if(isliving(cult_mind.current))
+ var/mob/living/L = cult_mind.current
+ L.narsie_act()
/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
if((istype(I, /obj/item/weapon/tome) && iscultist(user)))
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index d3aeef178c..503fc0f396 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -216,9 +216,8 @@
to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow their orders and help them complete their goals at all costs.")
else if(stoner)
to_chat(newstruct, "You are still bound to serve your creator, [stoner], follow their orders and help them complete their goals at all costs.")
- newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
- var/obj/screen/alert/bloodsense/BS = newstruct.alerts["bloodsense"]
- BS.Cviewer = newstruct
+ var/obj/screen/alert/bloodsense/BS = newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
+ BS.Cviewer = newstruct
newstruct.cancel_camera()
@@ -271,4 +270,4 @@
T.dropItemToGround(W)
init_shade(T, U)
qdel(T)
- return 1
+ return 1
\ No newline at end of file
diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm
index 74fcf77ce5..9819d4374a 100644
--- a/code/game/machinery/computer/computer.dm
+++ b/code/game/machinery/computer/computer.dm
@@ -52,7 +52,7 @@
update_icon()
/obj/machinery/computer/narsie_act()
- if(clockwork && clockwork != initial(clockwork) && prob(20)) //if it's clockwork but isn't normally clockwork
+ if(clockwork && clockwork != initial(clockwork)) //if it's clockwork but isn't normally clockwork
clockwork = FALSE
icon_screen = initial(icon_screen)
icon_keyboard = initial(icon_keyboard)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 7ff38f53c9..1750aa7aa8 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -179,18 +179,17 @@
/obj/machinery/door/airlock/narsie_act()
var/turf/T = get_turf(src)
var/runed = prob(20)
- if(prob(20))
- if(glass)
- if(runed)
- new/obj/machinery/door/airlock/cult/glass(T)
- else
- new/obj/machinery/door/airlock/cult/unruned/glass(T)
+ if(glass)
+ if(runed)
+ new/obj/machinery/door/airlock/cult/glass(T)
else
- if(runed)
- new/obj/machinery/door/airlock/cult(T)
- else
- new/obj/machinery/door/airlock/cult/unruned(T)
- qdel(src)
+ new/obj/machinery/door/airlock/cult/unruned/glass(T)
+ else
+ if(runed)
+ new/obj/machinery/door/airlock/cult(T)
+ else
+ new/obj/machinery/door/airlock/cult/unruned(T)
+ qdel(src)
/obj/machinery/door/airlock/ratvar_act() //Airlocks become pinion airlocks that only allow servants
if(glass)
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 9657c7d888..2336aa71ed 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -71,9 +71,8 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
qdel(src)
/obj/item/stack/sheet/metal/narsie_act()
- if(prob(20))
- new /obj/item/stack/sheet/runed_metal(loc, amount)
- qdel(src)
+ new /obj/item/stack/sheet/runed_metal(loc, amount)
+ qdel(src)
/obj/item/stack/sheet/metal/fifty
amount = 50
@@ -325,9 +324,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
turf_type = /turf/open/floor/clockwork
/obj/item/stack/tile/brass/narsie_act()
- if(prob(20))
- new /obj/item/stack/sheet/runed_metal(loc, amount)
- qdel(src)
+ new /obj/item/stack/sheet/runed_metal(loc, amount)
+ qdel(src)
/obj/item/stack/tile/brass/Initialize(mapload, new_amount, merge = TRUE)
recipes = GLOB.brass_recipes
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 87560f426f..7202aeae1c 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -37,10 +37,9 @@
return attack_hand(user)
/obj/structure/chair/narsie_act()
- if(prob(20))
- var/obj/structure/chair/wood/W = new/obj/structure/chair/wood(get_turf(src))
- W.setDir(dir)
- qdel(src)
+ var/obj/structure/chair/wood/W = new/obj/structure/chair/wood(get_turf(src))
+ W.setDir(dir)
+ qdel(src)
/obj/structure/chair/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/weapon/wrench) && !(flags&NODECONSTRUCT))
@@ -239,10 +238,9 @@
var/obj/structure/chair/origin_type = /obj/structure/chair
/obj/item/chair/narsie_act()
- if(prob(20))
- var/obj/item/chair/wood/W = new/obj/item/chair/wood(get_turf(src))
- W.setDir(dir)
- qdel(src)
+ var/obj/item/chair/wood/W = new/obj/item/chair/wood(get_turf(src))
+ W.setDir(dir)
+ qdel(src)
/obj/item/chair/attack_self(mob/user)
plant(user)
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 9f37710d78..bdf2a5eb40 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -297,9 +297,8 @@
qdel(src)
/obj/structure/girder/narsie_act()
- if(prob(25))
- new /obj/structure/girder/cult(loc)
- qdel(src)
+ new /obj/structure/girder/cult(loc)
+ qdel(src)
/obj/structure/girder/displaced
name = "displaced girder"
diff --git a/code/game/objects/structures/table_frames.dm b/code/game/objects/structures/table_frames.dm
index b6837015f1..50aa23434e 100644
--- a/code/game/objects/structures/table_frames.dm
+++ b/code/game/objects/structures/table_frames.dm
@@ -84,9 +84,8 @@
qdel(src)
/obj/structure/table_frame/narsie_act()
- if(prob(20))
- new /obj/structure/table_frame/wood(src.loc)
- qdel(src)
+ new /obj/structure/table_frame/wood(src.loc)
+ qdel(src)
/obj/structure/table_frame/ratvar_act()
new /obj/structure/table_frame/brass(src.loc)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index c42b61a622..cbb0fcc5f7 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -47,8 +47,7 @@
queue_smooth_neighbors(src)
/obj/structure/table/narsie_act()
- if(prob(20))
- new /obj/structure/table/wood(src.loc)
+ new /obj/structure/table/wood(src.loc)
/obj/structure/table/ratvar_act()
new /obj/structure/table/reinforced/brass(src.loc)
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 5f581fba63..c1993677f3 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -188,8 +188,9 @@
else if(prob(50))
ReplaceWithLattice()
-/turf/open/floor/narsie_act()
- if(prob(20))
+/turf/open/floor/narsie_act(force, ignore_mobs, probability = 20)
+ . = ..()
+ if(.)
ChangeTurf(/turf/open/floor/engine/cult)
/turf/open/floor/ratvar_act(force, ignore_mobs)
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index cce7756892..4f6da47bde 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -158,8 +158,14 @@
if(smooth)
queue_smooth_neighbors(src)
-/turf/open/floor/carpet/narsie_act()
- return
+/turf/open/floor/carpet/narsie_act(force, ignore_mobs, probability = 20)
+ . = (prob(probability) || force)
+ for(var/I in src)
+ var/atom/A = I
+ if(ignore_mobs && ismob(A))
+ continue
+ if(ismob(A) || .)
+ A.narsie_act()
/turf/open/floor/carpet/break_tile()
broken = 1
diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm
index 79ad84e752..9144cbc871 100644
--- a/code/game/turfs/simulated/floor/misc_floor.dm
+++ b/code/game/turfs/simulated/floor/misc_floor.dm
@@ -250,9 +250,10 @@
if(severity < 3 || target == src)
ChangeTurf(src.baseturf)
-/turf/open/floor/vines/narsie_act()
- if(prob(20))
- ChangeTurf(src.baseturf) //nar sie eats this shit
+/turf/open/floor/vines/narsie_act(force, ignore_mobs, probability = 20)
+ if(prob(probability) || force)
+ ChangeTurf(baseturf) //nar sie eats this shit
+ narsie_act(force, ignore_mobs, probability)
/turf/open/floor/vines/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm
index bbc61d54d3..9c40b8276f 100644
--- a/code/game/turfs/simulated/floor/reinf_floor.dm
+++ b/code/game/turfs/simulated/floor/reinf_floor.dm
@@ -125,9 +125,6 @@
qdel(realappearence)
realappearence = null
-/turf/open/floor/engine/cult/narsie_act()
- return
-
/turf/open/floor/engine/cult/ratvar_act()
. = ..()
if(istype(src, /turf/open/floor/engine/cult)) //if we haven't changed type
diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm
index a0ed982144..9fa69fc6a7 100644
--- a/code/game/turfs/simulated/wall/misc_walls.dm
+++ b/code/game/turfs/simulated/wall/misc_walls.dm
@@ -4,19 +4,27 @@
icon = 'icons/turf/walls/cult_wall.dmi'
icon_state = "cult"
canSmoothWith = null
+ smooth = SMOOTH_MORE
sheet_type = /obj/item/stack/sheet/runed_metal
sheet_amount = 1
girder_type = /obj/structure/girder/cult
/turf/closed/wall/mineral/cult/Initialize()
new /obj/effect/overlay/temp/cult/turf(src)
- ..()
+ . = ..()
/turf/closed/wall/mineral/cult/devastate_wall()
new sheet_type(get_turf(src), sheet_amount)
-/turf/closed/wall/mineral/cult/narsie_act()
- return
+/turf/closed/wall/mineral/cult/Exited(atom/movable/AM, atom/newloc)
+ . = ..()
+ if(istype(AM, /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 = AM
+ var/atom/movable/stored_pulling = H.pulling
+ if(stored_pulling)
+ stored_pulling.setDir(get_dir(stored_pulling.loc, newloc))
+ stored_pulling.forceMove(src)
+ H.start_pulling(stored_pulling, TRUE)
/turf/closed/wall/mineral/cult/ratvar_act()
. = ..()
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index 1aea3d7dbc..820a54da42 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -238,8 +238,9 @@
if(prob(30))
dismantle_wall()
-/turf/closed/wall/narsie_act()
- if(prob(20))
+/turf/closed/wall/narsie_act(force, ignore_mobs, probability = 20)
+ . = ..()
+ if(.)
ChangeTurf(/turf/closed/wall/mineral/cult)
/turf/closed/wall/ratvar_act(force, ignore_mobs)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 618894f207..96e3b9c590 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -375,6 +375,15 @@
A.ex_act(severity, target)
CHECK_TICK
+/turf/narsie_act(force, ignore_mobs, probability = 20)
+ . = (prob(probability) || force)
+ for(var/I in src)
+ var/atom/A = I
+ if(ignore_mobs && ismob(A))
+ continue
+ if(ismob(A) || .)
+ A.narsie_act()
+
/turf/ratvar_act(force, ignore_mobs, probability = 40)
. = (prob(probability) || force)
for(var/I in src)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 316a7a3b83..0599aaaa8a 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -127,22 +127,21 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
/mob/dead/observer/narsie_act()
var/old_color = color
color = "#960000"
- animate(src, color = old_color, time = 10)
+ animate(src, color = old_color, time = 10, flags = ANIMATION_PARALLEL)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 10)
/mob/dead/observer/ratvar_act()
var/old_color = color
color = "#FAE48C"
- animate(src, color = old_color, time = 10)
+ animate(src, color = old_color, time = 10, flags = ANIMATION_PARALLEL)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 10)
+/mob/dead/observer/Destroy()
GLOB.ghost_images_default -= ghostimage_default
- qdel(ghostimage_default)
- ghostimage_default = null
+ QDEL_NULL(ghostimage_default)
GLOB.ghost_images_simple -= ghostimage_simple
- qdel(ghostimage_simple)
- ghostimage_simple = null
+ QDEL_NULL(ghostimage_simple)
updateallghostimages()
return ..()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index e4129e642d..d15d71c23c 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -306,17 +306,15 @@
reagents.add_reagent("heparin", 5)
return FALSE
if(client)
- makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, src, null, 0)
+ makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, src, cultoverride = TRUE)
else
- switch(rand(1, 10))
+ switch(rand(1, 6))
if(1)
new /mob/living/simple_animal/hostile/construct/armored/hostile(get_turf(src))
if(2)
new /mob/living/simple_animal/hostile/construct/wraith/hostile(get_turf(src))
if(3 to 6)
new /mob/living/simple_animal/hostile/construct/builder/hostile(get_turf(src))
- if(6 to 10)
- new /mob/living/simple_animal/hostile/construct/harvester/hostile(get_turf(src))
spawn_dust()
gib()
return TRUE
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 304eb31e11..c0f88e3641 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -27,25 +27,22 @@
unique_name = 1
AIStatus = AI_OFF //normal constructs don't have AI
loot = list(/obj/item/weapon/ectoplasm)
- del_on_death = TRUE
- initial_language_holder = /datum/language_holder/construct
+ del_on_death = TRUE
+ initial_language_holder = /datum/language_holder/construct
deathmessage = "collapses in a shattered heap."
var/list/construct_spells = list()
var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this."
var/master = null
var/seeking = FALSE
+ var/can_repair_constructs = FALSE
+ var/can_repair_self = FALSE
/mob/living/simple_animal/hostile/construct/Initialize()
. = ..()
+ update_health_hud()
for(var/spell in construct_spells)
AddSpell(new spell(null))
-/mob/living/simple_animal/hostile/construct/Destroy()
- for(var/X in actions)
- var/datum/action/A = X
- qdel(A)
- ..()
-
/mob/living/simple_animal/hostile/construct/Login()
..()
to_chat(src, playstyle_string)
@@ -67,7 +64,10 @@
to_chat(user, msg)
/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M)
- if(istype(M, /mob/living/simple_animal/hostile/construct/builder))
+ if(isconstruct(M)) //is it a construct?
+ var/mob/living/simple_animal/hostile/construct/C = M
+ if(!C.can_repair_constructs || (C == src && !C.can_repair_self))
+ return
if(health < maxHealth)
adjustHealth(-5)
if(src != M)
@@ -128,38 +128,6 @@
AIStatus = AI_ON
environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP
-
-///////////////////////Master-Tracker///////////////////////
-
-/datum/action/innate/seek_master
- name = "Seek your Master"
- desc = "You and your master share a soul-link that informs you of their location"
- background_icon_state = "bg_demon"
- buttontooltipstyle = "cult"
- button_icon_state = "cult_mark"
- var/tracking = FALSE
- var/mob/living/simple_animal/hostile/construct/the_construct
-
-/datum/action/innate/seek_master/Grant(var/mob/living/C)
- the_construct = C
- ..()
-
-/datum/action/innate/seek_master/Activate()
- if(!the_construct.master)
- to_chat(the_construct, "You have no master to seek!")
- the_construct.seeking = FALSE
- return
- if(tracking)
- tracking = FALSE
- the_construct.seeking = FALSE
- to_chat(the_construct, "You are no longer tracking your master.")
- return
- else
- tracking = TRUE
- the_construct.seeking = TRUE
- to_chat(the_construct, "You are now tracking your master.")
-
-
/mob/living/simple_animal/hostile/construct/armored/bullet_act(obj/item/projectile/P)
if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))
var/reflectchance = 80 - round(P.damage/3)
@@ -203,30 +171,7 @@
attacktext = "slashes"
attack_sound = 'sound/weapons/bladeslice.ogg'
construct_spells = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift)
- playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, can phase through walls, and your attacks will lower the cooldown on phasing."
- var/attack_refund = 10 //1 second per attack
- var/crit_refund = 50 //5 seconds when putting a target into critical
- var/kill_refund = 250 //full refund on kills
-
-/mob/living/simple_animal/hostile/construct/wraith/AttackingTarget() //refund jaunt cooldown when attacking living targets
- var/prev_stat
- if(isliving(target) && !iscultist(target))
- var/mob/living/L = target
- prev_stat = L.stat
-
- . = ..()
-
- if(. && isnum(prev_stat))
- var/mob/living/L = target
- var/refund = 0
- if(QDELETED(L) || (L.stat == DEAD && prev_stat != DEAD)) //they're dead, you killed them
- refund += kill_refund
- else if(L.InCritical() && prev_stat == CONSCIOUS) //you knocked them into critical
- refund += crit_refund
- if(L.stat != DEAD && prev_stat != DEAD)
- refund += attack_refund
- for(var/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/S in mob_spell_list)
- S.charge_counter = min(S.charge_counter + refund, S.charge_max)
+ playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls."
/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things
AIStatus = AI_ON
@@ -261,6 +206,8 @@
use magic missile, repair allied constructs, shades, and yourself (by clicking on them), \
and, most important of all, create new constructs by producing soulstones to capture souls, \
and shells to place those soulstones into."
+ can_repair_constructs = TRUE
+ can_repair_self = TRUE
/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here?
if(isconstruct(A)) //is it a construct?
@@ -322,22 +269,81 @@
icon_living = "harvester"
maxHealth = 60
health = 60
- melee_damage_lower = 1
- melee_damage_upper = 5
- retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier
- attacktext = "prods"
- environment_smash = 3
- attack_sound = 'sound/weapons/tap.ogg'
- construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/wall,
- /obj/effect/proc_holder/spell/aoe_turf/conjure/floor,
- /obj/effect/proc_holder/spell/targeted/smoke/disable)
- playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \
+ sight = SEE_MOBS
+ melee_damage_lower = 15
+ melee_damage_upper = 20
+ attacktext = "butchers"
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/area_conversion,
+ /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall)
+ playstyle_string = "You are a Harvester. You are incapable of directly killing humans, but your attacks will remove their limbs: \
Bring those who still cling to this world of illusion back to the Geometer so they may know Truth."
+ can_repair_constructs = TRUE
-/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things
- AIStatus = AI_ON
- environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP
+/mob/living/simple_animal/hostile/construct/harvester/Bump(atom/AM)
+ . = ..()
+ if(istype(AM, /turf/closed/wall/mineral/cult) && AM != loc) //we can go through cult walls
+ var/atom/movable/stored_pulling = pulling
+ if(stored_pulling)
+ stored_pulling.setDir(get_dir(stored_pulling.loc, loc))
+ stored_pulling.forceMove(loc)
+ forceMove(AM)
+ if(stored_pulling)
+ start_pulling(stored_pulling, TRUE) //drag anything we're pulling through the wall with us by magic
+/mob/living/simple_animal/hostile/construct/harvester/AttackingTarget()
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ var/list/parts = list()
+ var/undismembermerable_limbs = 0
+ for(var/X in C.bodyparts)
+ var/obj/item/bodypart/BP = X
+ if(BP.body_part != HEAD && BP.body_part != CHEST)
+ if(BP.dismemberable)
+ parts += BP
+ else
+ undismembermerable_limbs++
+ if(!LAZYLEN(parts))
+ if(undismembermerable_limbs) //they have limbs we can't remove, and no parts we can, attack!
+ return ..()
+ to_chat(src, "\"Bring [C.p_them()] to me.\"")
+ return FALSE
+ do_attack_animation(C)
+ var/obj/item/bodypart/BP = pick(parts)
+ BP.dismember()
+ return FALSE
+ . = ..()
+
+
+///////////////////////Master-Tracker///////////////////////
+
+/datum/action/innate/seek_master
+ name = "Seek your Master"
+ desc = "You and your master share a soul-link that informs you of their location"
+ background_icon_state = "bg_demon"
+ buttontooltipstyle = "cult"
+ button_icon_state = "cult_mark"
+ var/tracking = FALSE
+ var/mob/living/simple_animal/hostile/construct/the_construct
+
+/datum/action/innate/seek_master/Grant(var/mob/living/C)
+ the_construct = C
+ ..()
+
+/datum/action/innate/seek_master/Activate()
+ if(!the_construct.master)
+ to_chat(the_construct, "You have no master to seek!")
+ the_construct.seeking = FALSE
+ return
+ if(tracking)
+ tracking = FALSE
+ the_construct.seeking = FALSE
+ to_chat(the_construct, "You are no longer tracking your master.")
+ return
+ else
+ tracking = TRUE
+ the_construct.seeking = TRUE
+ to_chat(the_construct, "You are now tracking your master.")
/////////////////////////////ui stuff/////////////////////////////
@@ -355,4 +361,4 @@
else if(health > maxHealth*0.2)
hud_used.healths.icon_state = "[icon_state]_health5"
else
- hud_used.healths.icon_state = "[icon_state]_health6"
\ No newline at end of file
+ hud_used.healths.icon_state = "[icon_state]_health6"
diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm
index 6ebac2394c..7dfb76f579 100644
--- a/code/modules/mob/living/simple_animal/shade.dm
+++ b/code/modules/mob/living/simple_animal/shade.dm
@@ -44,7 +44,10 @@
return TRUE //this doesn't make much sense; you'd thing TRUE would mean it'd process spacemove but it means it doesn't
/mob/living/simple_animal/shade/attack_animal(mob/living/simple_animal/M)
- if(istype(M, /mob/living/simple_animal/hostile/construct/builder))
+ if(isconstruct(M))
+ var/mob/living/simple_animal/hostile/construct/C = M
+ if(!C.can_repair_constructs)
+ return
if(health < maxHealth)
adjustHealth(-25)
Beam(M,icon_state="sendbeam",time=4)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index a5fdc3ed1c..d4333872e1 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -331,12 +331,14 @@
changeNext_move(CLICK_CD_GRABBING)
if(AM.pulledby)
- visible_message("[src] has pulled [AM] from [AM.pulledby]'s grip.")
+ if(!supress_message)
+ visible_message("[src] has pulled [AM] from [AM.pulledby]'s grip.")
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
AM.pulledby = src
- playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+ if(!supress_message)
+ playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
update_pull_hud_icon()
if(ismob(AM))
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index fb2df4deeb..1997b6bcbd 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -39,13 +39,12 @@
narsie_spawn_animation()
- sleep(70)
- SSshuttle.emergency.request(null, set_coefficient = 0.1) // Cannot recall
+ sleep(19)
+ SSshuttle.emergency.request(null, set_coefficient = 0) //instantly arrives
/obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user as mob)
- makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, user, null, 0, loc_override = src.loc)
- new /obj/effect/particle_effect/smoke/sleeping(src.loc)
+ makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, user, cultoverride = TRUE, loc_override = src.loc)
/obj/singularity/narsie/process()
@@ -82,7 +81,8 @@
/obj/singularity/narsie/consume(atom/A)
- A.narsie_act()
+ if(isturf(A))
+ A.narsie_act()
/obj/singularity/narsie/ex_act() //No throwing bombs at her either.
diff --git a/code/modules/spells/spell_types/construct_spells.dm b/code/modules/spells/spell_types/construct_spells.dm
index c59af27ddd..b6898afdef 100644
--- a/code/modules/spells/spell_types/construct_spells.dm
+++ b/code/modules/spells/spell_types/construct_spells.dm
@@ -9,6 +9,26 @@
cult_req = 1
charge_max = 2500
+
+/obj/effect/proc_holder/spell/aoe_turf/area_conversion
+ name = "Area Conversion"
+ desc = "This spell instantly converts a small area around you."
+
+ school = "transmutation"
+ charge_max = 50
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+ range = 2
+ action_icon_state = "areaconvert"
+ action_background_icon_state = "bg_cult"
+
+/obj/effect/proc_holder/spell/aoe_turf/area_conversion/cast(list/targets, mob/user = usr)
+ playsound(get_turf(user), 'sound/items/welder.ogg', 75, 1)
+ for(var/turf/T in targets)
+ T.narsie_act(FALSE, TRUE, 100 - (get_dist(user, T) * 25))
+
+
/obj/effect/proc_holder/spell/aoe_turf/conjure/floor
name = "Summon Cult Floor"
desc = "This spell constructs a cult floor"