diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm
index 325a4a4b7ec..f1b9165a264 100644
--- a/code/_onclick/hud/other_mobs.dm
+++ b/code/_onclick/hud/other_mobs.dm
@@ -93,9 +93,12 @@
return
-/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD)
- HUD.construct_hud()
+/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD)
+ ..(HUD)
+
+// HUD.construct_hud() //Archaic.
+/*
/datum/hud/proc/construct_hud()
var/constructtype
@@ -109,30 +112,28 @@
constructtype = "harvester"
if(constructtype)
- mymob.fire = new /obj/screen()
+
mymob.fire.icon = 'icons/mob/screen1_construct.dmi'
mymob.fire.icon_state = "fire0"
mymob.fire.name = "fire"
mymob.fire.screen_loc = ui_construct_fire
- mymob.healths = new /obj/screen()
mymob.healths.icon = 'icons/mob/screen1_construct.dmi'
mymob.healths.icon_state = "[constructtype]_health0"
mymob.healths.name = "health"
mymob.healths.screen_loc = ui_construct_health
- mymob.pullin = new /obj/screen()
mymob.pullin.icon = 'icons/mob/screen1_construct.dmi'
mymob.pullin.icon_state = "pull0"
mymob.pullin.name = "pull"
mymob.pullin.screen_loc = ui_construct_pull
- mymob.zone_sel = new /obj/screen/zone_sel()
+
mymob.zone_sel.icon = 'icons/mob/screen1_construct.dmi'
mymob.zone_sel.overlays.len = 0
mymob.zone_sel.overlays += image('icons/mob/zone_sel.dmi', "[mymob.zone_sel.selecting]")
- mymob.purged = new /obj/screen()
+
mymob.purged.icon = 'icons/mob/screen1_construct.dmi'
mymob.purged.icon_state = "purge0"
mymob.purged.name = "purged"
@@ -142,3 +143,4 @@
mymob.client.screen += list(mymob.fire, mymob.healths, mymob.pullin, mymob.zone_sel, mymob.purged)
mymob.client.screen += mymob.client.void
+*/
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index e2a4c28c6b9..729330ddaa3 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -92,9 +92,11 @@
if(spattack_min_range <= 1)
SpecialAtkTarget()
+
else if(melee_damage_upper == 0 && istype(A,/mob/living))
custom_emote(1,"[pick(friendly)] [A]!")
+
else
DoPunch(A)
diff --git a/code/game/gamemodes/cult/construct_spells.dm b/code/game/gamemodes/cult/construct_spells.dm
new file mode 100644
index 00000000000..f8ff57fc357
--- /dev/null
+++ b/code/game/gamemodes/cult/construct_spells.dm
@@ -0,0 +1,698 @@
+//cast_method flags, needs to be up to date with Technomancer's. They were, for some reason, not working outside it.
+#define CAST_USE 1 // Clicking the spell in your hand.
+#define CAST_MELEE 2 // Clicking an atom in melee range.
+#define CAST_RANGED 4 // Clicking an atom beyond melee range.
+#define CAST_THROW 8 // Throwing the spell and hitting an atom.
+#define CAST_COMBINE 16 // Clicking another spell with this spell.
+#define CAST_INNATE 32 // Activates upon verb usage, used for mobs without hands.
+
+//Aspects
+#define ASPECT_FIRE "fire" //Damage over time and raising body-temp. Firesuits protect from this.
+#define ASPECT_FROST "frost" //Slows down the affected, also involves imbedding with icicles. Winter coats protect from this.
+#define ASPECT_SHOCK "shock" //Energy-expensive, usually stuns. Insulated armor protects from this.
+#define ASPECT_AIR "air" //Mostly involves manipulation of atmos, useless in a vacuum. Magboots protect from this.
+#define ASPECT_FORCE "force" //Manipulates gravity to push things away or towards a location.
+#define ASPECT_TELE "tele" //Teleportation of self, other objects, or other people.
+#define ASPECT_DARK "dark" //Makes all those photons vanish using magic-- WITH SCIENCE. Used for sneaky stuff.
+#define ASPECT_LIGHT "light" //The opposite of dark, usually blinds, makes holo-illusions, or makes laser lightshows.
+#define ASPECT_BIOMED "biomed" //Mainly concerned with healing and restoration.
+#define ASPECT_EMP "emp" //Unused now.
+#define ASPECT_UNSTABLE "unstable" //Heavily RNG-based, causes instability to the victim.
+#define ASPECT_CHROMATIC "chromatic" //Used to combine with other spells.
+#define ASPECT_UNHOLY "unholy" //Involves the dead, blood, and most things against divine beings.
+
+//////////////////////////////Construct Spells/////////////////////////
+
+proc/findNullRod(var/atom/target)
+ if(istype(target,/obj/item/weapon/nullrod))
+ return 1
+ else if(target.contents)
+ for(var/atom/A in target.contents)
+ if(findNullRod(A))
+ return 1
+ return 0
+
+/spell/aoe_turf/conjure/construct
+ name = "Artificer"
+ desc = "This spell conjures a construct which may be controlled by Shades"
+
+ school = "conjuration"
+ charge_max = 600
+ spell_flags = 0
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/constructshell)
+
+ hud_state = "artificer"
+
+/spell/aoe_turf/conjure/construct/lesser
+ charge_max = 1800
+ summon_type = list(/obj/structure/constructshell/cult)
+ hud_state = "const_shell"
+ override_base = "const"
+
+/spell/aoe_turf/conjure/floor
+ name = "Floor Construction"
+ desc = "This spell constructs a cult floor"
+
+ charge_max = 20
+ spell_flags = Z2NOCAST | CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ summon_type = list(/turf/simulated/floor/cult)
+
+ hud_state = "const_floor"
+
+/spell/aoe_turf/conjure/floor/conjure_animation(var/atom/movable/overlay/animation, var/turf/target)
+ animation.icon_state = "cultfloor"
+ flick("cultfloor",animation)
+ spawn(10)
+ qdel(animation)
+
+/spell/aoe_turf/conjure/wall
+ name = "Lesser Construction"
+ desc = "This spell constructs a cult wall"
+
+ charge_max = 100
+ spell_flags = Z2NOCAST | CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ summon_type = list(/turf/simulated/wall/cult)
+
+ hud_state = "const_wall"
+
+/spell/aoe_turf/conjure/wall/conjure_animation(var/atom/movable/overlay/animation, var/turf/target)
+ animation.icon_state = "cultwall"
+ flick("cultwall",animation)
+ spawn(10)
+ qdel(animation)
+
+/spell/aoe_turf/conjure/wall/reinforced
+ name = "Greater Construction"
+ desc = "This spell constructs a reinforced metal wall"
+
+ charge_max = 300
+ spell_flags = Z2NOCAST
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ cast_delay = 50
+
+ summon_type = list(/turf/simulated/wall/r_wall)
+
+/spell/aoe_turf/conjure/soulstone
+ name = "Summon Soulstone"
+ desc = "This spell reaches into Nar-Sie's realm, summoning one of the legendary fragments across time and space"
+
+ charge_max = 3000
+ spell_flags = 0
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/item/device/soulstone)
+
+ hud_state = "const_stone"
+ override_base = "const"
+
+/spell/aoe_turf/conjure/pylon
+ name = "Red Pylon"
+ desc = "This spell conjures a fragile crystal from Nar-Sie's realm. Makes for a convenient light source."
+
+ charge_max = 200
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/cult/pylon)
+
+ hud_state = "const_pylon"
+
+/spell/aoe_turf/conjure/pylon/cast(list/targets)
+ ..()
+ var/turf/spawn_place = pick(targets)
+ for(var/obj/structure/cult/pylon/P in spawn_place.contents)
+ if(P.isbroken)
+ P.repair(usr)
+ continue
+ return
+
+/spell/aoe_turf/conjure/door
+ name = "Stone Door"
+ desc = "This spell conjures a massive stone door."
+
+ charge_max = 100
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/simple_door/cult)
+
+ hud_state = "const_door"
+
+/spell/aoe_turf/conjure/grille
+ name = "Arcane Grille"
+ desc = "This spell conjures an airtight grille."
+
+ charge_max = 100
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/grille/cult)
+
+ hud_state = "const_grille"
+
+/spell/aoe_turf/conjure/forcewall/lesser
+ name = "Shield"
+ desc = "Allows you to pull up a shield to protect yourself and allies from incoming threats"
+
+ charge_max = 300
+ spell_flags = 0
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ summon_type = list(/obj/effect/forcefield/cult)
+ duration = 200
+
+ hud_state = "const_juggwall"
+
+//Code for the Juggernaut construct's forcefield, that seemed like a good place to put it.
+/obj/effect/forcefield/cult
+ desc = "That eerie looking obstacle seems to have been pulled from another dimension through sheer force"
+ name = "Juggerwall"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "m_shield_cult"
+ light_color = "#B40000"
+ light_range = 2
+
+/obj/effect/forcefield/cult/cultify()
+ return
+
+/spell/aoe_turf/knock/harvester
+ name = "Force Doors"
+ desc = "Mortal portals are no match for your occult might."
+
+ spell_flags = CONSTRUCT_CHECK
+
+ charge_max = 100
+ invocation = ""
+ invocation_type = "silent"
+ range = 4
+
+ hud_state = "const_knock"
+
+/spell/aoe_turf/knock/harvester/cast(list/targets)
+/* for(var/turf/T in targets) //Disintigrating doors is bad, okay.
+ for(var/obj/machinery/door/door in T.contents)
+ spawn door.cultify()
+ return */
+ for(var/turf/T in targets)
+ for(var/obj/machinery/door/door in T.contents)
+ spawn(1)
+ if(istype(door,/obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/AL = door
+ AL.locked = 0 //The spirits of the damned care not for your locks.
+ AL.welded = 0 //Or your welding tools.
+ else if(istype(door, /obj/machinery/door/firedoor))
+ var/obj/machinery/door/firedoor/FD = door
+ FD.blocked = 0
+ door.open(1)
+ return
+
+/*
+ *
+ * Self-targeting spells. Modifiers, auras, instants, etc.
+ *
+ */
+
+/spell/targeted/ethereal_jaunt/shift
+ name = "Phase Shift"
+ desc = "This spell allows you to pass through walls"
+
+ charge_max = 200
+ spell_flags = Z2NOCAST | INCLUDEUSER | CONSTRUCT_CHECK
+ invocation_type = SpI_NONE
+ range = -1
+ duration = 50 //in deciseconds
+
+ hud_state = "const_shift"
+
+/spell/targeted/ethereal_jaunt/shift/jaunt_disappear(var/atom/movable/overlay/animation, var/mob/living/target)
+ animation.icon_state = "phase_shift"
+ animation.dir = target.dir
+ flick("phase_shift",animation)
+
+/spell/targeted/ethereal_jaunt/shift/jaunt_reappear(var/atom/movable/overlay/animation, var/mob/living/target)
+ animation.icon_state = "phase_shift2"
+ animation.dir = target.dir
+ flick("phase_shift2",animation)
+
+/spell/targeted/ethereal_jaunt/shift/jaunt_steam(var/mobloc)
+ return
+
+/*
+ * Harvest has been disabled due to the lack of Nar'Sie. Here for posterity / future rework.
+ */
+
+/*
+/spell/targeted/harvest
+ name = "Harvest"
+ desc = "Back to where I come from, and you're coming with me."
+
+ school = "transmutation"
+ charge_max = 200
+ spell_flags = Z2NOCAST | CONSTRUCT_CHECK | INCLUDEUSER
+ invocation = ""
+ invocation_type = SpI_NONE
+ range = 0
+ max_targets = 0
+
+ overlay = 1
+ overlay_icon = 'icons/effects/effects.dmi'
+ overlay_icon_state = "rune_teleport"
+ overlay_lifespan = 0
+
+ hud_state = "const_harvest"
+
+/spell/targeted/harvest/cast(list/targets, mob/user)//because harvest is already a proc
+ ..()
+
+ var/destination = null
+ for(var/obj/singularity/narsie/large/N in narsie_list)
+ destination = N.loc
+ break
+ if(destination)
+ var/prey = 0
+ for(var/mob/living/M in targets)
+ if(!findNullRod(M))
+ M.forceMove(destination)
+ if(M != user)
+ prey = 1
+ user << "You warp back to Nar-Sie[prey ? " along with your prey":""]."
+ else
+ user << "...something's wrong!"//There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world.
+*/
+
+/spell/targeted/fortify
+ name = "Fortify Shell"
+ desc = "Emit a field of energy around your shell to reduce incoming damage incredibly, while decreasing your mobility."
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_fortify"
+ smoke_amt = 0
+
+ charge_max = 600
+
+/spell/targeted/fortify/cast(list/targets, mob/living/user)
+ if(findNullRod(user) || user.has_modifier_of_type(/datum/modifier/fortify))
+ charge_counter = 400
+ return
+ user.add_modifier(/datum/modifier/fortify, 1 MINUTES)
+
+/spell/targeted/occult_repair_aura
+ name = "Repair Aura"
+ desc = "Emit a field of energy around your shell to repair nearby constructs at range."
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_repairaura"
+ smoke_amt = 0
+
+ charge_max = 600
+
+/spell/targeted/occult_repair_aura/cast(list/targets, mob/living/user)
+ if(findNullRod(user) || user.has_modifier_of_type(/datum/modifier/repair_aura))
+ charge_counter = 300
+ return
+ user.add_modifier(/datum/modifier/repair_aura, 30 SECONDS)
+
+/spell/targeted/ambush_mode
+ name = "Toggle Ambush"
+ desc = "Phase yourself mostly out of this reality, minimizing your combat ability, but allowing for employance of ambush tactics."
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_ambush"
+ smoke_amt = 0
+
+ charge_max = 100
+
+/spell/targeted/ambush_mode/cast(list/targets, mob/living/user)
+ if(findNullRod(user))
+ charge_counter = 50
+ return
+ if(user.has_modifier_of_type(/datum/modifier/ambush))
+ user.remove_modifiers_of_type(/datum/modifier/ambush)
+ return
+ user.add_modifier(/datum/modifier/ambush, 0)
+
+/*
+ *
+ * These are the spells that place spell-objects into the construct's hands akin to technomancers.
+ *
+ */
+
+/spell/targeted/construct_advanced
+ name = "Base Construct Spell"
+ desc = "If you see this, please tell a developer!"
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_rune"
+ smoke_amt = 0
+
+ charge_max = 10
+
+ var/obj/item/weapon/spell/construct/spell_obj = null //This is the var that determines what Technomancer-style spell is put into their hands.
+
+/spell/targeted/construct_advanced/cast(list/targets, mob/living/user)
+ if(!findNullRod(user))
+ user.place_spell_in_hand(spell_obj)
+
+/spell/targeted/construct_advanced/inversion_beam
+ name = "Inversion Beam"
+ desc = "Fire a searing beam of darkness at your foes."
+
+ hud_state = "const_beam"
+ spell_obj = /obj/item/weapon/spell/construct/projectile/inverted_beam
+
+/spell/targeted/construct_advanced/mend_acolyte
+ name = "Mend Acolyte"
+ desc = "Mend a target acolyte or construct over time."
+
+ charge_max = 100
+
+ hud_state = "const_mend"
+ spell_obj = /obj/item/weapon/spell/construct/mend_occult
+
+/spell/targeted/construct_advanced/agonizing_sphere
+ name = "Sphere of Agony"
+ desc = "Rend a portal into a plane of naught but pain at the target location."
+
+ charge_max = 100
+
+ hud_state = "const_harvest"
+ spell_obj = /obj/item/weapon/spell/construct/spawner/agonizing_sphere
+
+/spell/targeted/construct_advanced/slam
+ name = "Slam"
+ desc = "Empower your FIST."
+
+ charge_max = 300
+
+ hud_state = "const_fist"
+ spell_obj = /obj/item/weapon/spell/construct/slam
+
+/*
+ *
+ * These are the spell objects that go into the construct's hands.
+ *
+ */
+
+/*
+ * Base advanced construct spell types.
+ */
+
+/obj/item/weapon/spell/construct //Energy costs are in units of blood, in the event a cultist gets one of these.
+ name = "unholy energy"
+ desc = "Your hands appear to be screaming. This is a debug text, you should probably tell a developer!"
+ icon = 'icons/obj/spells.dmi'
+ icon_state = "generic"
+ item_icons = list(
+ slot_l_hand_str = 'icons/mob/items/lefthand_spells.dmi',
+ slot_r_hand_str = 'icons/mob/items/righthand_spells.dmi',
+ )
+ throwforce = 0
+ force = 0
+ show_examine = FALSE
+ owner = null
+ core = null
+ cast_methods = null // Controls how the spell is casted.
+ aspect = ASPECT_UNHOLY // Used for combining spells. Pretty much any cult spell is unholy.
+ toggled = 0 // Mainly used for overlays.
+ cooldown = 0 // If set, will add a cooldown overlay and adjust click delay. Must be a multiple of 5 for overlays.
+ cast_sound = null // Sound file played when this is used.
+ var/last_castcheck = null // The last time this spell was cast.
+
+/obj/item/weapon/spell/construct/New()
+ //..() //This kills the spell, because super on this calls the default spell's New, which checks for a core. Can't have that.
+ if(isliving(loc))
+ owner = loc
+ if(!owner)
+ qdel(src)
+ update_icon()
+
+/obj/item/weapon/spell/construct/adjust_instability(var/amount) //The only drawback to the boons of the geometer is the use of a mortal's blood as fuel. Constructs have already paid that price long ago.
+ return
+
+/obj/item/weapon/spell/construct/run_checks()
+ if(owner)
+ if((iscultist(owner) || istype(owner, /mob/living/simple_animal/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed?
+ last_castcheck = world.time
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/pay_energy(var/amount)
+ if(owner)
+ if(istype(owner, /mob/living/simple_animal/construct))
+ return 1
+ if(iscultist(owner) && pay_blood(amount))
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/proc/pay_blood(var/amount) //If, for some reason, this is put into the hands of a cultist, by a talisnam or whatever.
+ if(ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ if(!H.should_have_organ(O_HEART))
+ return 1
+ if(H.vessel.remove_reagent("blood", amount))
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/afterattack(atom/target, mob/user, proximity_flag, click_parameters) //Not overriding it caused runtimes, because cooldown checked for core.
+ if(!run_checks())
+ return
+ if(!proximity_flag)
+ if(cast_methods & CAST_RANGED)
+ on_ranged_cast(target, user)
+ else
+ if(istype(target, /obj/item/weapon/spell))
+ var/obj/item/weapon/spell/spell = target
+ if(spell.cast_methods & CAST_COMBINE)
+ spell.on_combine_cast(src, user)
+ return
+ if(cast_methods & CAST_MELEE)
+ on_melee_cast(target, user)
+ else if(cast_methods & CAST_RANGED) //Try to use a ranged method if a melee one doesn't exist.
+ on_ranged_cast(target, user)
+ if(cooldown)
+ var/effective_cooldown = round(cooldown, 5)
+ user.setClickCooldown(effective_cooldown)
+ flick("cooldown_[effective_cooldown]",src)
+
+/obj/item/weapon/spell/construct/projectile //This makes me angry, but we need the template, and we can't use it because special check overrides on the base.
+ name = "construct projectile template"
+ icon_state = "generic"
+ desc = "This is a generic template that shoots projectiles. If you can read this, the game broke!"
+ cast_methods = CAST_RANGED
+ var/obj/item/projectile/spell_projectile = null
+ var/pre_shot_delay = 0
+ var/fire_sound = null
+ var/energy_cost_per_shot = 5
+
+/obj/item/weapon/spell/construct/projectile/on_ranged_cast(atom/hit_atom, mob/living/user)
+ if(set_up(hit_atom, user))
+ var/obj/item/projectile/new_projectile = make_projectile(spell_projectile, user)
+ new_projectile.launch(hit_atom)
+ log_and_message_admins("has casted [src] at \the [hit_atom].")
+ if(fire_sound)
+ playsound(get_turf(src), fire_sound, 75, 1)
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/projectile/proc/make_projectile(obj/item/projectile/projectile_type, mob/living/user)
+ var/obj/item/projectile/P = new projectile_type(get_turf(user))
+ return P
+
+/obj/item/weapon/spell/construct/projectile/proc/set_up(atom/hit_atom, mob/living/user)
+ if(spell_projectile)
+ if(pay_energy(energy_cost_per_shot))
+ if(pre_shot_delay)
+ var/image/target_image = image(icon = 'icons/obj/spells.dmi', loc = get_turf(hit_atom), icon_state = "target")
+ user << target_image
+ user.Stun(pre_shot_delay / 10)
+ sleep(pre_shot_delay)
+ qdel(target_image)
+ if(owner)
+ return TRUE
+ return FALSE // We got dropped before the firing occured.
+ return TRUE // No delay, no need to check.
+ return FALSE
+
+/obj/item/weapon/spell/construct/spawner
+ name = "spawner template"
+ desc = "If you see me, someone messed up."
+ icon_state = "darkness"
+ cast_methods = CAST_RANGED
+ var/obj/effect/spawner_type = null
+
+/obj/item/weapon/spell/construct/spawner/on_ranged_cast(atom/hit_atom, mob/user)
+ var/turf/T = get_turf(hit_atom)
+ if(T)
+ new spawner_type(T)
+ to_chat(user, "You shift \the [src] onto \the [T].")
+ log_and_message_admins("has casted [src] at [T.x],[T.y],[T.z].")
+ qdel(src)
+
+//Harvester Laser.
+
+/obj/item/weapon/spell/construct/projectile/inverted_beam
+ name = "inversion beam"
+ icon_state = "generic"
+ desc = "Your manipulators fire searing beams of inverted light."
+ cast_methods = CAST_RANGED
+ spell_projectile = /obj/item/projectile/beam/inversion
+ pre_shot_delay = 0
+ cooldown = 5
+ fire_sound = 'sound/weapons/spiderlunge.ogg'
+
+/obj/item/projectile/beam/inversion
+ name = "inversion beam"
+ icon_state = "invert"
+ fire_sound = 'sound/weapons/spiderlunge.ogg'
+ damage = 15
+ damage_type = BURN
+ check_armour = "laser"
+ armor_penetration = 60
+ light_range = 2
+ light_power = -2
+ light_color = "#FFFFFF"
+
+ muzzle_type = /obj/effect/projectile/inversion/muzzle
+ tracer_type = /obj/effect/projectile/inversion/tracer
+ impact_type = /obj/effect/projectile/inversion/impact
+
+//Harvester Pain Orb
+
+/obj/item/weapon/spell/construct/spawner/agonizing_sphere
+ name = "sphere of agony"
+ desc = "Call forth a portal to a dimension of naught but pain at your target."
+
+ spawner_type = /obj/effect/temporary_effect/pulsar/agonizing_sphere
+
+/obj/item/weapon/spell/construct/spawner/agonizing_sphere/on_ranged_cast(atom/hit_atom, mob/user)
+ if(within_range(hit_atom) && pay_energy(10))
+ ..()
+
+/obj/item/weapon/spell/construct/spawner/agonizing_sphere/on_throw_cast(atom/hit_atom, mob/user)
+ pay_energy(5)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
+ L.add_modifier(/datum/modifier/agonize, 10 SECONDS)
+
+/obj/effect/temporary_effect/pulsar/agonizing_sphere
+ name = "agonizing sphere"
+ desc = "A portal to some hellish place. Its screams wrack your body with pain.."
+ icon_state = "red_static_sphere"
+ time_to_die = null
+ light_range = 4
+ light_power = 5
+ light_color = "#FF0000"
+ pulses_remaining = 10
+
+/obj/effect/temporary_effect/pulsar/agonizing_sphere/pulse_loop()
+ while(pulses_remaining)
+ sleep(1 SECONDS)
+ spawn()
+ for(var/mob/living/L in view(4,src))
+ if(!iscultist(L) && !istype(L, /mob/living/simple_animal/construct))
+ L.add_modifier(/datum/modifier/agonize, 2 SECONDS)
+ if(L.isSynthetic())
+ to_chat(L, "Your chassis warps as the [src] pulses!")
+ L.adjustFireLoss(4)
+ pulses_remaining--
+ qdel(src)
+
+//Artificer Heal
+
+/obj/item/weapon/spell/construct/mend_occult
+ name = "mend acolyte"
+ desc = "Mend the wounds of a cultist, or construct, over time."
+ icon_state = "mend_wounds"
+ cast_methods = CAST_MELEE
+ aspect = ASPECT_UNHOLY
+ light_color = "#FF5C5C"
+ light_power = -2
+
+/obj/item/weapon/spell/construct/mend_occult/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
+ L.add_modifier(/datum/modifier/mend_occult, 150)
+ qdel(src)
+
+//Juggernaut + Behemoth Slam
+/obj/item/weapon/spell/construct/slam
+ name = "slam"
+ desc = "Empower your FIST, to send an opponent flying."
+ icon_state = "toggled_old"
+ cast_methods = CAST_MELEE
+ aspect = ASPECT_UNHOLY
+ light_color = "#FF5C5C"
+ light_power = -2
+ cooldown = 15
+
+/obj/item/weapon/spell/construct/slam/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
+ var/attack_message = "slams"
+ if(istype(user, /mob/living/simple_animal))
+ var/mob/living/simple_animal/S = user
+ attack_message = pick(S.attacktext)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
+ L.visible_message("\The [user] [attack_message] \the [L], sending them flying!")
+ playsound(src, "punch", 50, 1)
+ L.Weaken(2)
+ L.adjustBruteLoss(rand(30, 50))
+ var/throwdir = get_dir(src, L)
+ L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src)
+ if(istype(hit_atom, /turf/simulated/wall))
+ var/turf/simulated/wall/W = hit_atom
+ user.visible_message("\The [user] rears its fist, preparing to hit \the [W]!")
+ var/windup = cooldown
+ if(W.reinf_material)
+ windup = cooldown * 2
+ if(do_after(user, windup))
+ W.visible_message("\The [user] [attack_message] \the [W], obliterating it!")
+ W.dismantle_wall(1)
+ else
+ user.visible_message("\The [user] lowers its fist.")
+ return
+ qdel(src)
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index cecb7b87c7b..9d8eb8dc914 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -14,20 +14,21 @@
return
/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone)
- if(iscultist(user))
+ if(iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
return ..()
var/zone = (user.hand ? "l_arm":"r_arm")
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/affecting = H.get_organ(zone)
- user << "An unexplicable force rips through your [affecting.name], tearing the sword from your grasp!"
+ to_chat(user, "An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!")
+ //random amount of damage between half of the blade's force and the full force of the blade.
+ user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
+ user.Weaken(5)
+ else if(!istype(user, /mob/living/simple_animal/construct))
+ to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!")
else
- user << "An unexplicable force rips through you, tearing the sword from your grasp!"
-
- //random amount of damage between half of the blade's force and the full force of the blade.
- user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
- user.Weaken(5)
+ to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.")
user.drop_from_inventory(src, src.loc)
throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed)
@@ -38,10 +39,11 @@
return 1
/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob)
- if(!iscultist(user))
- user << "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly."
+ if(!iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
+ to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.")
user.make_dizzy(120)
-
+ if(istype(user, /mob/living/simple_animal/construct))
+ to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.")
/obj/item/clothing/head/culthood
name = "cult hood"
diff --git a/code/game/gamemodes/technomancer/spell_objs.dm b/code/game/gamemodes/technomancer/spell_objs.dm
index 2a917b4fd85..681d29da50b 100644
--- a/code/game/gamemodes/technomancer/spell_objs.dm
+++ b/code/game/gamemodes/technomancer/spell_objs.dm
@@ -19,6 +19,7 @@
#define ASPECT_EMP "emp" //Unused now.
#define ASPECT_UNSTABLE "unstable" //Heavily RNG-based, causes instability to the victim.
#define ASPECT_CHROMATIC "chromatic" //Used to combine with other spells.
+#define ASPECT_UNHOLY "unholy" //Involves the dead, blood, and most things against divine beings.
/obj/item/weapon/spell
name = "glowing particles"
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 30955879f8f..1496ca1a1f8 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -142,6 +142,18 @@
add_hiddenprint(user)
destroy()
+/obj/machinery/camera/attack_generic(mob/user as mob)
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ set_status(0)
+ S.do_attack_animation(src)
+ S.setClickCooldown(user.get_attack_speed())
+ visible_message("\The [user] [pick(S.attacktext)] \the [src]!")
+ playsound(src.loc, S.attack_sound, 100, 1)
+ add_hiddenprint(user)
+ destroy()
+ ..()
+
/obj/machinery/camera/attackby(obj/item/W as obj, mob/living/user as mob)
update_coverage()
// DECONSTRUCTION
diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm
index 54b5513cce3..58ce8b282c6 100644
--- a/code/game/machinery/doors/blast_door.dm
+++ b/code/game/machinery/doors/blast_door.dm
@@ -7,6 +7,9 @@
// as they lack any ID scanning system, they just handle remote control signals. Subtypes have
// different icons, which are defined by set of variables. Subtypes are on bottom of this file.
+// UPDATE 06.04.2018
+// The emag thing wasn't working as intended, manually overwrote it.
+
/obj/machinery/door/blast
name = "Blast Door"
desc = "That looks like it doesn't open easily."
@@ -56,6 +59,10 @@
radiation_repository.resistance_cache.Remove(get_turf(src))
return
+// Has to be in here, comment at the top is older than the emag_act code on doors proper
+/obj/machinery/door/blast/emag_act()
+ return -1
+
// Proc: force_open()
// Parameters: None
// Description: Opens the door. No checks are done inside this proc.
@@ -197,6 +204,27 @@
return
..()
+// Proc: attack_generic()
+// Parameters: Attacking simple mob, incoming damage.
+// Description: Checks the power or integrity of the blast door, if either have failed, chekcs the damage to determine if the creature would be able to open the door by force. Otherwise, super.
+/obj/machinery/door/blast/attack_generic(var/mob/user, var/damage)
+ if(stat & (BROKEN|NOPOWER))
+ if(damage >= 10)
+ if(src.density)
+ visible_message("\The [user] starts forcing \the [src] open!")
+ if(do_after(user, 5 SECONDS, src))
+ visible_message("\The [user] forces \the [src] open!")
+ force_open(1)
+ else
+ visible_message("\The [user] starts forcing \the [src] closed!")
+ if(do_after(user, 2 SECONDS, src))
+ visible_message("\The [user] forces \the [src] closed!")
+ force_close(1)
+ else
+ visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].")
+ return
+ ..()
+
// Proc: open()
// Parameters: None
// Description: Opens the door. Does necessary checks. Automatically closes if autoclose is true
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 6dd268676d7..d8252fe4a9c 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -38,11 +38,14 @@
var/atom/movable/overlay/c_animation = null
/obj/machinery/door/attack_generic(var/mob/user, var/damage)
- if(damage >= 10)
- visible_message("\The [user] smashes into the [src]!")
- take_damage(damage)
- else
- visible_message("\The [user] bonks \the [src] harmlessly.")
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ if(damage >= 10)
+ visible_message("\The [user] smashes into the [src]!")
+ playsound(src, S.attack_sound, 75, 1)
+ take_damage(damage)
+ else
+ visible_message("\The [user] bonks \the [src] harmlessly.")
user.do_attack_animation(src)
/obj/machinery/door/New()
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 5cded75805c..504d65c3065 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -216,6 +216,27 @@
return
..()
+/obj/machinery/door/firedoor/attack_generic(var/mob/user, var/damage)
+ if(stat & (BROKEN|NOPOWER))
+ if(damage >= 10)
+ var/time_to_force = (2 + (2 * blocked)) * 5
+ if(src.density)
+ visible_message("\The [user] starts forcing \the [src] open!")
+ if(do_after(user, time_to_force, src))
+ visible_message("\The [user] forces \the [src] open!")
+ src.blocked = 0
+ open(1)
+ else
+ time_to_force = (time_to_force / 2)
+ visible_message("\The [user] starts forcing \the [src] closed!")
+ if(do_after(user, time_to_force, src))
+ visible_message("\The [user] forces \the [src] closed!")
+ close(1)
+ else
+ visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].")
+ return
+ ..()
+
/obj/machinery/door/firedoor/attackby(obj/item/weapon/C as obj, mob/user as mob)
add_fingerprint(user)
if(istype(C, /obj/item/taperoll))
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index f6650301c7e..de1d8fcb548 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -393,6 +393,18 @@ var/list/turret_icons
attacked = 0
..()
+/obj/machinery/porta_turret/attack_generic(mob/user as mob, var/damage)
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ if(damage >= 10)
+ var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly.
+ visible_message("\The [S] [pick(S.attacktext)] \the [src]!")
+ take_damage(incoming_damage)
+ S.do_attack_animation(src)
+ return 1
+ visible_message("\The [user] bonks \the [src]'s casing!")
+ return ..()
+
/obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
//Emagging the turret makes it go bonkers and stun everyone. It also makes
diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm
index 74014f2b0fe..868429095a7 100644
--- a/code/game/mecha/working/ripley.dm
+++ b/code/game/mecha/working/ripley.dm
@@ -36,6 +36,7 @@
desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE"
name = "DEATH-RIPLEY"
icon_state = "deathripley"
+ initial_icon = "deathripley"
step_in = 2
opacity=0
lights_power = 60
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 4e76f58e4df..8e482cc1cb7 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -221,7 +221,7 @@
skitter()
else if(isorgan(loc))
- if(!amount_grown) amount_grown = 1
+ if(amount_grown < 0) amount_grown = 1
var/obj/item/organ/external/O = loc
if(!O.owner || O.owner.stat == DEAD || amount_grown > 80)
O.implants -= src
@@ -237,7 +237,7 @@
else if(prob(1))
src.visible_message("\The [src] skitters.")
- if(amount_grown)
+ if(amount_grown >= 0)
amount_grown += rand(0,2)
/obj/effect/spider/spiderling/proc/skitter()
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index ebad249bde0..b57c67bac09 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -102,6 +102,11 @@
user << "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one."
return 0
+ if(!user.IsAdvancedToolUser() && isanimal(user))
+ var/mob/living/simple_animal/S = user
+ if(!S.IsHumanoidToolUser(src))
+ return 0
+
user.visible_message("\The [user] starts transferring \the [ai] into \the [src]...", "You start transferring \the [ai] into \the [src]...")
ai << "\The [user] is transferring you into \the [src]!"
diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm
index 77c5b593643..c5b637d9e59 100755
--- a/code/game/objects/items/weapons/AI_modules.dm
+++ b/code/game/objects/items/weapons/AI_modules.dm
@@ -22,6 +22,11 @@ AI MODULES
var/datum/ai_laws/laws = null
/obj/item/weapon/aiModule/proc/install(var/atom/movable/AM, var/mob/living/user)
+ if(!user.IsAdvancedToolUser() && isanimal(user))
+ var/mob/living/simple_animal/S = user
+ if(!S.IsHumanoidToolUser(src))
+ return 0
+
if (istype(AM, /obj/machinery/computer/aiupload))
var/obj/machinery/computer/aiupload/comp = AM
if(comp.stat & NOPOWER)
diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index 196f8d4a74c..58aa30547dc 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -29,7 +29,14 @@
return 0
/obj/item/weapon/rcd/proc/can_use(var/mob/user,var/turf/T)
- return (user.Adjacent(T) && user.get_active_hand() == src && !user.stat && !user.restrained())
+ var/usable = 0
+ if(user.Adjacent(T) && user.get_active_hand() == src && !user.stat && !user.restrained())
+ usable = 1
+ if(!user.IsAdvancedToolUser() && istype(user, /mob/living/simple_animal))
+ var/mob/living/simple_animal/S = user
+ if(!S.IsHumanoidToolUser(src))
+ usable = 0
+ return usable
/obj/item/weapon/rcd/examine()
..()
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 46ebb3bd401..e6a5fc09e44 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -265,7 +265,7 @@ var/last_chew = 0
place_legcuffs(user, user)
return
- if(!C.handcuffed)
+ if(!C.legcuffed)
if (C == user)
place_legcuffs(user, user)
return
diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm
index f2528f57862..94bc9e8f692 100644
--- a/code/game/objects/items/weapons/material/knives.dm
+++ b/code/game/objects/items/weapons/material/knives.dm
@@ -99,15 +99,9 @@
icon_state = "machete"
force_divisor = 0.3 // 18 when hardness 60 (steel)
attack_verb = list("slashed", "chopped", "gouged", "ripped", "cut")
- var/should_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned.
+ can_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned.
slot_flags = SLOT_BELT
-// This cannot go into afterattack since some mobs delete themselves upon dying.
-/obj/item/weapon/material/knife/machete/pre_attack(var/mob/living/target, var/mob/living/user)
- if(should_cleave && istype(target))
- cleave(user, target)
- ..()
-
/obj/item/weapon/material/knife/tacknife/survival
name = "survival knife"
desc = "A hunting grade survival knife."
diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm
index 87bfb0454d3..bbb8171b10b 100644
--- a/code/game/objects/items/weapons/material/misc.dm
+++ b/code/game/objects/items/weapons/material/misc.dm
@@ -28,7 +28,7 @@
icon = 'icons/obj/weapons.dmi'
icon_state = "unathiknife"
attack_verb = list("ripped", "torn", "cut")
- should_cleave = FALSE
+ can_cleave = FALSE
var hits = 0
/obj/item/weapon/material/knife/machete/hatchet/unathiknife/attack(mob/M as mob, mob/user as mob)
diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm
index 6631ec25fa9..acc3132712b 100644
--- a/code/game/objects/items/weapons/material/twohanded.dm
+++ b/code/game/objects/items/weapons/material/twohanded.dm
@@ -94,6 +94,7 @@
force_wielded = 30
attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
applies_material_colour = 0
+ can_cleave = TRUE
/obj/item/weapon/material/twohanded/fireaxe/update_held_icon()
var/mob/living/M = loc
@@ -124,12 +125,6 @@
var/obj/effect/plant/P = A
P.die_off()
-// This cannot go into afterattack since some mobs delete themselves upon dying.
-/obj/item/weapon/material/twohanded/fireaxe/pre_attack(var/mob/living/target, var/mob/living/user)
- if(istype(target))
- cleave(user, target)
- ..()
-
/obj/item/weapon/material/twohanded/fireaxe/scythe
icon_state = "scythe0"
base_icon = "scythe"
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index f7497ec3ff4..ad30eb0a57f 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -87,6 +87,7 @@
attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
sharp = 1
edge = 1
+ can_cleave = TRUE
/obj/item/weapon/melee/energy/axe/activate(mob/living/user)
..()
@@ -98,12 +99,6 @@
icon_state = initial(icon_state)
to_chat(user, "\The [src] is de-energised. It's just a regular axe now.")
-// This cannot go into afterattack since some mobs delete themselves upon dying.
-/obj/item/weapon/melee/energy/axe/pre_attack(var/mob/living/target, var/mob/living/user)
- if(istype(target))
- cleave(user, target)
- ..()
-
/obj/item/weapon/melee/energy/axe/suicide_act(mob/user)
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
visible_message("\The [user] swings \the [src] towards [TU.his] head! It looks like [TU.he] [TU.is] trying to commit suicide.")
diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm
index 2aa58488b6a..4f430d3ec68 100644
--- a/code/game/objects/structures/simple_doors.dm
+++ b/code/game/objects/structures/simple_doors.dm
@@ -198,4 +198,14 @@
..(newloc, MAT_SIFWOOD)
/obj/structure/simple_door/resin/New(var/newloc,var/material_name)
- ..(newloc, "resin")
\ No newline at end of file
+ ..(newloc, "resin")
+
+/obj/structure/simple_door/cult/New(var/newloc,var/material_name)
+ ..(newloc, "cult")
+
+/obj/structure/simple_door/cult/TryToSwitchState(atom/user)
+ if(isliving(user))
+ var/mob/living/L = user
+ if(!iscultist(L) && !istype(L, /mob/living/simple_animal/construct))
+ return
+ ..()
diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm
index 7a619477eaf..c9a4babd9ef 100644
--- a/code/game/objects/weapons.dm
+++ b/code/game/objects/weapons.dm
@@ -2,6 +2,7 @@
name = "weapon"
icon = 'icons/obj/weapons.dmi'
hitsound = "swing_hit"
+ var/can_cleave = FALSE // If true, a 'cleaving' attack will occur.
var/cleaving = FALSE // Used to avoid infinite cleaving.
/obj/item/weapon/Bump(mob/M as mob)
@@ -16,14 +17,17 @@
)
// Attacks mobs (atm only simple ones due to friendly fire issues) that are adjacent to the target and user.
-/obj/item/weapon/proc/cleave(var/mob/living/user, var/mob/living/target)
+/obj/item/weapon/proc/cleave(mob/living/user, atom/target)
if(cleaving)
- return // We're busy.
+ return FALSE // We're busy.
+ if(!target.Adjacent(user))
+ return FALSE // Too far.
if(get_turf(user) == get_turf(target))
- return // Otherwise we would hit all eight surrounding tiles.
+ return FALSE // Otherwise we would hit all eight surrounding tiles.
+
cleaving = TRUE
var/hit_mobs = 0
- for(var/mob/living/simple_animal/SA in orange(get_turf(target), 1))
+ for(var/mob/living/simple_animal/SA in range(get_turf(target), 1))
if(SA.stat == DEAD) // Don't beat a dead horse.
continue
if(SA == user) // Don't hit ourselves. Simple mobs shouldn't be able to do this but that might change later to be able to hit all mob/living-s.
@@ -42,6 +46,13 @@
if(hit_mobs)
to_chat(user, "You used \the [src] to attack [hit_mobs] other thing\s!")
cleaving = FALSE // We're done now.
+ return hit_mobs > 0 // Returns TRUE if anything got hit.
+
+// This cannot go into afterattack since some mobs delete themselves upon dying.
+/obj/item/weapon/material/pre_attack(mob/living/target, mob/living/user)
+ if(can_cleave && istype(target))
+ cleave(user, target)
+ ..()
// This is purely the visual effect of cleaving.
/obj/item/weapon/proc/cleave_visual(var/mob/living/user, var/mob/living/target)
diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm
index 5602eac320d..91371e7beb5 100644
--- a/code/game/turfs/simulated/floor_attackby.dm
+++ b/code/game/turfs/simulated/floor_attackby.dm
@@ -3,6 +3,12 @@
if(!C || !user)
return 0
+ if(isliving(user) && istype(C, /obj/item/weapon))
+ var/mob/living/L = user
+ if(L.a_intent != I_HELP)
+ attack_tile(C, L) // Be on help intent if you want to decon something.
+ return
+
if(flooring)
if(istype(C, /obj/item/weapon))
try_deconstruct_tile(C, user)
diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm
index 2bfa5a6aca0..8bc362a1d76 100644
--- a/code/game/turfs/simulated/wall_attacks.dm
+++ b/code/game/turfs/simulated/wall_attacks.dm
@@ -57,12 +57,19 @@
source.thermal_conductivity = initial(source.thermal_conductivity)
/turf/simulated/wall/proc/fail_smash(var/mob/user)
- user << "You smash against the wall!"
+ to_chat(user, "You smash against the wall!")
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ playsound(src, S.attack_sound, 75, 1)
+ user.do_attack_animation(src)
take_damage(rand(25,75))
/turf/simulated/wall/proc/success_smash(var/mob/user)
- user << "You smash through the wall!"
+ to_chat(user, "You smash through the wall!")
user.do_attack_animation(src)
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ playsound(src, S.attack_sound, 75, 1)
spawn(1)
dismantle_wall(1)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 27f8bd6c144..2b0158b5fce 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -86,6 +86,34 @@ turf/attackby(obj/item/weapon/W as obj, mob/user as mob)
S.gather_all(src, user)
return ..()
+// Hits a mob on the tile.
+/turf/proc/attack_tile(obj/item/weapon/W, mob/living/user)
+ if(!istype(W))
+ return FALSE
+
+ var/list/viable_targets = list()
+ var/success = FALSE // Hitting something makes this true. If its still false, the miss sound is played.
+
+ for(var/mob/living/L in contents)
+ if(L == user) // Don't hit ourselves.
+ continue
+ viable_targets += L
+
+ if(!viable_targets.len) // No valid targets on this tile.
+ if(W.can_cleave)
+ success = W.cleave(user, src)
+ else
+ var/mob/living/victim = pick(viable_targets)
+ success = W.resolve_attackby(victim, user)
+
+ user.setClickCooldown(user.get_attack_speed(W))
+ user.do_attack_animation(src, no_attack_icons = TRUE)
+
+ if(!success) // Nothing got hit.
+ user.visible_message("\The [user] swipes \the [W] over \the [src].")
+ playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
+ return success
+
/turf/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob)
var/turf/T = get_turf(user)
var/area/A = T.loc
diff --git a/code/modules/busy_space/organizations.dm b/code/modules/busy_space/organizations.dm
index 0285a68444a..cf364aa3e93 100644
--- a/code/modules/busy_space/organizations.dm
+++ b/code/modules/busy_space/organizations.dm
@@ -41,9 +41,9 @@
if(autogenerate_destination_names) // Lets pad out the destination names.
var/i = rand(6, 10)
var/list/star_names = list(
- "Sol", "Alpha Centauri", "Sirius", "Vega", "Regulus", "Vir", "Algol", "Aldebaran",
- "Delta Doradus", "Menkar", "Geminga", "Elnath", "Gienah", "Mu Leporis", "Nyx", "Tau Ceti",
- "Wazn", "Alphard", "Phact", "Altair", "El", "Eutopia", "Qerr'valis", "Qerrna-Lakirr", "Rarkajar", "the Almach Rim")
+ "Sol", "Alpha Centauri", "Tau Ceti", "Zhu Que", "Oasis", "Vir", "Gavel", "Ganesha",
+ "Saint Columbia", "Altair", "Sidhe", "New Ohio", "Parvati", "Mahi-Mahi", "Nyx", "New Seoul",
+ "Kess-Gendar", "Raphael", "Phact", "Altair", "El", "Eutopia", "Qerr'valis", "Qerrna-Lakirr", "Rarkajar", "Thoth", "Jahan's Post", "Kauq'xum", "Silk", "New Singapore", "Stove", "Viola", "Love", "Isavau's Gamble" )
var/list/destination_types = list("dockyard", "station", "vessel", "waystation", "telecommunications satellite", "spaceport", "distress beacon", "anomaly", "colony", "outpost")
while(i)
destination_names.Add("a [pick(destination_types)] in [pick(star_names)]")
@@ -373,7 +373,8 @@
"Preemptive Defensive Strike",
"This Ship Is Spiders",
"Legitimate Trade Vessel",
- "Please Don't Explode II"
+ "Please Don't Explode II",
+ "Get Off the Air"
)
destination_names = list(
"a trade outpost in Shelf"
diff --git a/code/modules/economy/Events.dm b/code/modules/economy/Events.dm
index 5de2446dcff..9ce263cd17b 100644
--- a/code/modules/economy/Events.dm
+++ b/code/modules/economy/Events.dm
@@ -52,8 +52,8 @@
affected_dest.temp_price_change[good_type] = rand(1,100) / 100
/datum/event/economic_event/announce()
- var/author = "The "+using_map.starsys_name+" Times"
- var/channel = author
+ var/author = "Oculus v6rev7"
+ var/channel = "Oculum Content Aggregator"
//see if our location has custom event info for this event
var/body = affected_dest.get_custom_eventstring()
@@ -66,28 +66,28 @@
if(INDUSTRIAL_ACCIDENT)
body = "[pick("An industrial accident","A smelting accident","A malfunction","A malfunctioning piece of machinery","Negligent maintenance","A cooleant leak","A ruptured conduit")] at a [pick("factory","installation","power plant","dockyards")] on [affected_dest.name] resulted in severe structural damage and numerous injuries. Repairs are ongoing."
if(BIOHAZARD_OUTBREAK)
- body = "[pick("A \'REDACTED\'","A biohazard","An outbreak","A virus")] on [affected_dest.name] has resulted in quarantine, stopping much shipping in the area. Although the quarantine is now lifted, authorities are calling for deliveries of medical supplies to treat the infected, and gas to replace contaminated stocks."
+ body = "[pick("A nano-contaminant","A biohazard","An outbreak","A virus")] on [affected_dest.name] has resulted in quarantine, stopping much shipping in the area. Although the quarantine is now lifted, authorities are calling for deliveries of medical supplies to treat the infected, and gas to replace contaminated stocks."
if(PIRATES)
- body = "[pick("Pirates","Criminal elements","A [pick("mercenary","Donk Co.","Waffle Co.","\'REDACTED\'")] strike force")] have [pick("raided","blockaded","attempted to blackmail","attacked")] [affected_dest.name] today. Security has been tightened, but many valuable minerals were taken."
+ body = "[pick("Pirates","Criminal elements","A [pick("mercenary","Nos Amis","Xin Cohong","Ue-Katish")] strike force")] have [pick("raided","blockaded","attempted to blackmail","attacked")] [affected_dest.name] today. Security has been tightened, but many valuable minerals were taken."
if(CORPORATE_ATTACK)
- body = "A small [pick("pirate","Cybersun Industries","Gorlex Marauders","mercenary")] fleet has precise-jumped into proximity with [affected_dest.name], [pick("for a smash-and-grab operation","in a hit and run attack","in an overt display of hostilities")]. Much damage was done, and security has been tightened since the incident."
+ body = "A small [pick("pirate","Nation of Mars","Revolutionary Solar People's Party","mercenary")] fleet has precise-jumped into proximity with [affected_dest.name], [pick("for a smash-and-grab operation","in a hit and run attack","in an overt display of hostilities")]. Much damage was done, and security has been tightened since the incident."
if(ALIEN_RAIDERS)
if(prob(20))
- body = "The Tiger Co-operative have raided [affected_dest.name] today, no doubt on orders from their enigmatic masters. Stealing wildlife, farm animals, medical research materials and kidnapping civilians. [using_map.company_name] authorities are standing by to counter attempts at bio-terrorism."
+ body = "Ue-Katish pirates exiled from Ue'Orsi have raided [affected_dest.name] today, no doubt on orders from their enigmatic masters. Stealing wildlife, farm animals, medical research materials and kidnapping civilians. [using_map.company_name] authorities are standing by to counter attempts at bio-terrorism."
else
- body = "[pick("The alien species designated \'United Exolitics\'","The alien species designated \'REDACTED\'","An unknown alien species")] have raided [affected_dest.name] today, stealing wildlife, farm animals, medical research materials and kidnapping civilians. It seems they desire to learn more about us, so the Navy will be standing by to accomodate them next time they try."
+ body = "The Vox have raided [affected_dest.name] today, stealing wildlife, farm animals, medical research materials and kidnapping civilians. Fleet assets have moved to reinforce the effected region."
if(AI_LIBERATION)
- body = "A [pick("\'REDACTED\' was detected on","S.E.L.F operative infiltrated","malignant computer virus was detected on","rogue [pick("slicer","hacker")] was apprehended on")] [affected_dest.name] today, and managed to infect [pick("\'REDACTED\'","a sentient sub-system","a class one AI","a sentient defence installation")] before it could be stopped, causing serious damage. Considerable work must be done to repair the affected areas."
+ body = "A [pick("malignant webcrawler was detected on","Boiling Point operative infiltrated","malignant computer virus was detected on","rogue [pick("slicer","hacker")] was apprehended on")] [affected_dest.name] today, and managed to infect [pick("an A-class factory monitor","an experimental drone wing","a high-powered financial system","an automated defence installation")] before it could be stopped, causing serious damage. Considerable work must be done to repair the affected areas."
if(MOURNING)
- body = "[pick("The popular","The well-liked","The eminent","The well-known")] [pick("professor","entertainer","singer","researcher","public servant","administrator","ship captain","\'REDACTED\'")], [pick( random_name(pick(MALE,FEMALE)), 40; "\'REDACTED\'" )] has [pick("passed away","committed suicide","been murdered","died in a freakish accident")] on [affected_dest.name] today. The entire planet is in mourning, and prices have dropped for industrial goods as worker morale drops."
+ body = "[pick("The popular","The well-liked","The eminent","The well-known")] [pick("professor","entertainer","singer","researcher","public servant","administrator","ship captain")], [random_name(pick(MALE,FEMALE))] has [pick("passed away","committed suicide","been murdered","died in a freakish accident")] on [affected_dest.name] today. The entire planet is in mourning, and prices have dropped for industrial goods as worker morale drops."
if(CULT_CELL_REVEALED)
- body = "A [pick("dastardly","blood-thirsty","villanous","crazed")] cult of [pick("The Elder Gods","Nar'sie","an apocalyptic sect","\'REDACTED\'")] has [pick("been discovered","been revealed","revealed themselves","gone public")] on [affected_dest.name] earlier today. Public morale has been shaken due to [pick("certain","several","one or two")] [pick("high-profile","well known","popular")] individuals [pick("performing \'REDACTED\' acts","claiming allegiance to the cult","swearing loyalty to the cult leader","promising to aid to the cult")] before those involved could be brought to justice. The editor reminds all personnel that supernatural myths will not be tolerated on [using_map.company_name] facilities."
+ body = "A [pick("dastardly","blood-thirsty","villanous","crazed")] doomsday cult has [pick("been discovered","been revealed","revealed themselves","gone public")] on [affected_dest.name] earlier today. Public morale has been shaken due to [pick("certain","several","one or two")] [pick("high-profile","well known","popular")] individuals [pick("performing illegal acts","claiming allegiance to the cult","swearing loyalty to the cult leader","promising to aid to the cult")] before those involved could be brought to justice."
if(SECURITY_BREACH)
- body = "There was [pick("a security breach in","an unauthorised access in","an attempted theft in","an anarchist attack in","violent sabotage of")] a [pick("high-security","restricted access","classified","\'REDACTED\'")] [pick("\'REDACTED\'","section","zone","area")] this morning. Security was tightened on [affected_dest.name] after the incident, and the editor reassures all [using_map.company_name] personnel that such lapses are rare."
+ body = "There was [pick("a security breach in","an unauthorised access in","an attempted theft in","an anarchist attack in","violent sabotage of")] a [pick("high-security","restricted access","classified")] [pick("section","zone","area")] this morning. Security was tightened on [affected_dest.name] after the incident, and the editor reassures all [using_map.company_name] personnel that such lapses are rare."
if(ANIMAL_RIGHTS_RAID)
- body = "[pick("Militant animal rights activists","Members of the terrorist group Animal Rights Consortium","Members of the terrorist group \'REDACTED\'")] have [pick("launched a campaign of terror","unleashed a swathe of destruction","raided farms and pastures","forced entry to \'REDACTED\'")] on [affected_dest.name] earlier today, freeing numerous [pick("farm animals","animals","\'REDACTED\'")]. Prices for tame and breeding animals have spiked as a result."
+ body = "[pick("Militant animal rights activists","Members of the terrorist group Galactic Organization for Animal Rights")] have [pick("launched a campaign of terror","unleashed a swathe of destruction","raided farms and pastures","forced entry to an animal processing plant")] on [affected_dest.name] earlier today, freeing numerous [pick("farm animals","animals")]. Prices for tame and breeding animals have spiked as a result."
if(FESTIVAL)
- body = "A [pick("festival","week long celebration","day of revelry","planet-wide holiday")] has been declared on [affected_dest.name] by [pick("Governor","Commissioner","General","Commandant","Administrator")] [random_name(pick(MALE,FEMALE))] to celebrate [pick("the birth of their [pick("son","daughter")]","coming of age of their [pick("son","daughter")]","the pacification of rogue military cell","the apprehension of a violent criminal who had been terrorising the planet")]. Massive stocks of food and meat have been bought driving up prices across the planet."
+ body = "A [pick("festival","week long celebration","day of revelry","planet-wide holiday")] has been declared on [affected_dest.name] by [pick("Governor","Commissioner","General","Commandant","Administrator")] [random_name(pick(MALE,FEMALE))] to celebrate [pick("the birth of their [pick("son","daughter","child")]","coming of age of their [pick("son","daughter","child")]","the pacification of rogue military cell","the apprehension of a violent criminal who had been terrorising the planet")]. Massive stocks of food and meat have been bought driving up prices across the planet."
news_network.SubmitArticle(body, author, channel, null, 1)
diff --git a/code/modules/economy/Events_Mundane.dm b/code/modules/economy/Events_Mundane.dm
index c3eca773f52..9c6a428852b 100644
--- a/code/modules/economy/Events_Mundane.dm
+++ b/code/modules/economy/Events_Mundane.dm
@@ -1,5 +1,5 @@
-/datum/event/mundane_news
+/* /datum/event/mundane_news
endWhen = 10
/datum/event/mundane_news/announce()
@@ -123,7 +123,7 @@
The [using_map.starsys_name] Times is offering discount tickets for two to see [random_name(pick(MALE,FEMALE))] live in return for eyewitness reports and up to the minute coverage."
news_network.SubmitArticle(body, author, channel, null, 1)
-
+*/
/datum/event/trivial_news
endWhen = 10
diff --git a/code/modules/economy/TradeDestinations.dm b/code/modules/economy/TradeDestinations.dm
index 6f58bf9abaf..fbe4003e816 100644
--- a/code/modules/economy/TradeDestinations.dm
+++ b/code/modules/economy/TradeDestinations.dm
@@ -16,85 +16,73 @@ var/list/weighted_mundaneevent_locations = list()
/datum/trade_destination/proc/get_custom_eventstring(var/event_type)
return null
-//distance is measured in AU and co-relates to travel time
-/datum/trade_destination/centcomm
- name = "CentCom"
- description = "NanoTrasen's administrative centre for Tau Ceti."
+//distance is measured in Arbitrary and corelates to travel time, like, I guess
+/datum/trade_destination/luna
+ name = "Luna"
+ description = "The capital world of SolGov, host to NanoTrasen's corporate offices."
distance = 1.2
willing_to_buy = list()
willing_to_sell = list()
viable_random_events = list(SECURITY_BREACH, CORPORATE_ATTACK, AI_LIBERATION)
viable_mundane_events = list(ELECTION, RESIGNATION, CELEBRITY_DEATH)
-/datum/trade_destination/anansi
- name = "NSS Anansi"
- description = "Medical station ran by Second Red Cross (but owned by NT) for handling emergency cases from nearby colonies."
+/datum/trade_destination/nohio
+ name = "New Ohio"
+ description = "A world in the Sagitarius Heights, home to Vey-Medical's research and development facilities."
distance = 1.7
willing_to_buy = list()
willing_to_sell = list()
viable_random_events = list(SECURITY_BREACH, CULT_CELL_REVEALED, BIOHAZARD_OUTBREAK, PIRATES, ALIEN_RAIDERS)
viable_mundane_events = list(RESEARCH_BREAKTHROUGH, RESEARCH_BREAKTHROUGH, BARGAINS, GOSSIP)
-/datum/trade_destination/anansi/get_custom_eventstring(var/event_type)
- if(event_type == RESEARCH_BREAKTHROUGH)
- return "Thanks to research conducted on the NSS Anansi, Second Red Cross Society wishes to announce a major breakthough in the field of \
- [pick("mind-machine interfacing","neuroscience","nano-augmentation","genetics")]. [using_map.company_name] is expected to announce a co-exploitation deal within the fortnight."
- return null
-
-/datum/trade_destination/redolant
- name = "OAV Redolant"
- description = "Osiris Atmospherics station in orbit around the only gas giant insystem. They retain tight control over shipping rights, and Osiris warships protecting their prize are not an uncommon sight in Tau Ceti."
+/datum/trade_destination/sophia
+ name = "Sophia"
+ description = "The homeworld of the positronics and an extremely important cultural center."
distance = 0.6
willing_to_buy = list()
willing_to_sell = list()
viable_random_events = list(INDUSTRIAL_ACCIDENT, PIRATES, CORPORATE_ATTACK)
viable_mundane_events = list(RESEARCH_BREAKTHROUGH, RESEARCH_BREAKTHROUGH)
-/datum/trade_destination/redolant/get_custom_eventstring(var/event_type)
- if(event_type == RESEARCH_BREAKTHROUGH)
- return "Thanks to research conducted on the OAV Redolant, Osiris Atmospherics wishes to announce a major breakthough in the field of \
- [pick("phoron research","high energy flux capacitance","super-compressed materials","theoretical particle physics")]. [using_map.company_name] is expected to announce a co-exploitation deal within the fortnight."
- return null
-
-/datum/trade_destination/beltway
- name = "Beltway mining chain"
- description = "A co-operative effort between Beltway and NanoTrasen to exploit the rich outer asteroid belt of the Tau Ceti system."
+/datum/trade_destination/jade
+ name = "Jade"
+ description = "Jade, in the Zhu Que system, is one of the Bowl's garden worlds and home to a major ore processing operation."
distance = 7.5
willing_to_buy = list()
willing_to_sell = list()
viable_random_events = list(PIRATES, INDUSTRIAL_ACCIDENT)
viable_mundane_events = list(TOURISM)
-/datum/trade_destination/biesel
- name = "Biesel"
- description = "Large ship yards, strong economy and a stable, well-educated populace, Biesel largely owes allegiance to Sol / Vessel Contracting and begrudgingly tolerates NT. Capital is Lowell City."
+/datum/trade_destination/sif
+ name = "Sif"
+ description = "A garden world in the Vir system with a developing phoron-based economy."
distance = 2.3
willing_to_buy = list()
willing_to_sell = list()
viable_random_events = list(RIOTS, INDUSTRIAL_ACCIDENT, BIOHAZARD_OUTBREAK, CULT_CELL_REVEALED, FESTIVAL, MOURNING)
viable_mundane_events = list(BARGAINS, GOSSIP, SONG_DEBUT, MOVIE_RELEASE, ELECTION, TOURISM, RESIGNATION, CELEBRITY_DEATH)
-/datum/trade_destination/new_gibson
- name = "New Gibson"
- description = "Heavily industrialised rocky planet containing the majority of the planet-bound resources in the system, New Gibson is torn by unrest and has very little wealth to call it's own except in the hands of the corporations who jostle with NT for control."
+/datum/trade_destination/mars
+ name = "Mars"
+ description = "A major industrial center in the Sol system."
distance = 6.6
willing_to_buy = list()
willing_to_sell = list()
viable_random_events = list(RIOTS, INDUSTRIAL_ACCIDENT, BIOHAZARD_OUTBREAK, CULT_CELL_REVEALED, FESTIVAL, MOURNING)
viable_mundane_events = list(ELECTION, TOURISM, RESIGNATION)
-/datum/trade_destination/luthien
- name = "Luthien"
- description = "A small colony established on a feral, untamed world (largely jungle). Savages and wild beasts attack the outpost regularly, although NT maintains tight military control."
+/datum/trade_destination/nisp
+ name = "Nisp"
+ description = "A near-garden world known for its hostile wildlife and its N2O atmosphere."
distance = 8.9
willing_to_buy = list()
willing_to_sell = list()
viable_random_events = list(WILD_ANIMAL_ATTACK, CULT_CELL_REVEALED, FESTIVAL, MOURNING, ANIMAL_RIGHTS_RAID, ALIEN_RAIDERS)
viable_mundane_events = list(ELECTION, TOURISM, BIG_GAME_HUNTERS, RESIGNATION)
-/datum/trade_destination/reade
- name = "Reade"
- description = "A cold, metal-deficient world, NT maintains large pastures in whatever available space in an attempt to salvage something from this profitless colony."
+/datum/trade_destination/abelsrest
+ name = "Abel's Rest"
+ description = "A garden world on the Hegemony border. Coinhabitated rather uncomfortably by Unathi and Solar settlers."
distance = 7.5
willing_to_buy = list()
willing_to_sell = list()
diff --git a/code/modules/economy/economy_misc.dm b/code/modules/economy/economy_misc.dm
index 3ab22c662d2..1ca55ddf08f 100644
--- a/code/modules/economy/economy_misc.dm
+++ b/code/modules/economy/economy_misc.dm
@@ -90,7 +90,7 @@ var/global/economy_init = 0
if(economy_init)
return 2
- news_network.CreateFeedChannel("The [using_map.starsys_name] Times", "[using_map.starsys_name] Times ExoNode - [using_map.station_short]", 1, 1)
+ //news_network.CreateFeedChannel("The [using_map.starsys_name] Times", "[using_map.starsys_name] Times ExoNode - [using_map.station_short]", 1, 1)
news_network.CreateFeedChannel("The Gibson Gazette", "Editor Mike Hammers", 1, 1)
news_network.CreateFeedChannel("Oculum Content Aggregator", "Oculus v6rev7", 1, 1)
diff --git a/code/modules/economy/lorenews.dm b/code/modules/economy/lorenews.dm
index e18525e03d7..4f82c6f12a6 100644
--- a/code/modules/economy/lorenews.dm
+++ b/code/modules/economy/lorenews.dm
@@ -29,7 +29,7 @@
var/list/loc = solgov + skrell + tajara
//this includes systems Sol doesn't really have eyes on
- //var/list/allloc = loc + unathi + skrellfar + independent + rim
+ var/list/allloc = loc + unathi + skrellfar + independent + rim
//copied right from organizations.dm
var/list/ship_names = list(
@@ -59,7 +59,6 @@
//generated based on Cerebulon's thing on the wiki
var/list/prefixes = list("IAV","ICV","IDV","IDV","IEV","IFV","IIV","ILV","IMV","IRV","ISV","ITV","SCG-A","SCG-C","SCG-D","SCG-D","SCG-E","SCG-F","SCG-I","SCG-L","SCG-M","SCG-R","SCG-S","SCG-T","BAV","BCV","BDV","BDV","BEV","BFV","BIV","BLV","BMV","BRV","BSV","BTV","HAV","HCV","HDV","HDV","HEV","HFV","HIV","HLV","HMV","HRV","HSV","HTV","MAV","MCV","MDV","MDV","MEV","MFV","MIV","MLV","MMV","MRV","MSV","MTV","NAV","NCV","NDV","NDV","NEV","NFV","NIV","NLV","NMV","NRV","NSV","NTV","VAV","VCV","VDV","VDV","VEV","VFV","VIV","VLV","VMV","VRV","VSV","VTV","WAV","WCV","WDV","WDV","WEV","WFV","WIV","WLV","WMV","WRV","WSV","WTV","XAV","XCV","XDV","XDV","XEV","XFV","XIV","XLV","XMV","XRV","XSV","XTV","ZAV","ZCV","ZDV","ZDV","ZEV","ZFV","ZIV","ZLV","ZMV","ZRV","ZSV","ZTV","GAV","GCV","GDV","GDV","GEV","GFV","GIV","GLV","GMV","GRV","GSV","GTV","AAV","ACV","ADV","ADV","AEV","AFV","AIV","ALV","AMV","ARV","ASV","ATV","LAV","LCV","LDV","LDV","LEV","LFV","LIV","LLV","LMV","LRV","LSV","LTV")
- //var/datum/trade_destination/affected_dest = pick(weighted_mundaneevent_locations) //shouldn't do anything, not removing it to avoid breaking shit
//formatting breaks my fucking eyes
var/body = pick("A high-ranking Vey-Med executive was revealed to have been experimenting on biological neural enhancement techniques, believed to be of Skrellian origin, which could lead to the production of transhuman entities with an average cognitive capacity 30% greater than a baseline human.",
"[random_name(MALE)], chairman of the controversial Friends of Ned party, is scheduled to speak at a prominant university in [pick(core)] this Monday, according to a statement on his official newsfeed.",
@@ -105,6 +104,7 @@
"EIO spokesperson [random_name(MALE)] stated in a press conference yesterday that the \"Shakespear\" A-class codeline was briefly available for download on the darknet, although they claim to be \"confident that the leak was shut down before any significant portion could be downloaded\". Experts suspect Association involvement.",
"[pick("Strives Towards", "Embraces", "Dreams of", "Cultivates", "Advocates", "Exemplifies")] [pick("Strength", "Wisdom", "Independence", "Pragmatism", "Reason", "Development")], a Community Child from Angessa's Pearl, published his memoir today, detailing a shocking level of abuse by caretakers and educators.",
"Nova Sixty, Mercurial web personality, claims that the disappearence of the SCG-R Song Shi was a plot by \"extradimensional rockmen\" who intend to sow mistrust between the Association and SolGov. Quote Sixty, \"The Far Kingdoms' attempt to hide the TRUTH only proves that they and their rockman allies are NOT to be trusted.\"",
+ "Unusual stellar phenomena was detected on [pick(allloc)], sparking concerns about the colonies in the system.",
)
news_network.SubmitArticle(body, author, channel, null, 1)
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 3a57a7d753c..d48cda9dcab 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -134,7 +134,7 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Lost Carp", /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10), 1),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Hacker", /datum/event/money_hacker, 0, list(ASSIGNMENT_ANY = 4), 1, 10, 25),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Lotto", /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), 1, 5, 15),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane News", /datum/event/mundane_news, 300),
+ //new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane News", /datum/event/mundane_news, 300),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "PDA Spam", /datum/event/pda_spam, 0, list(ASSIGNMENT_ANY = 4), 0, 25, 50),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Shipping Error", /datum/event/shipping_error , 30, list(ASSIGNMENT_ANY = 2), 0),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Space Dust", /datum/event/dust , 60, list(ASSIGNMENT_ENGINEER = 20), 0, 0, 50),
diff --git a/code/modules/events/event_dynamic.dm b/code/modules/events/event_dynamic.dm
index b1a537639de..260e690924d 100644
--- a/code/modules/events/event_dynamic.dm
+++ b/code/modules/events/event_dynamic.dm
@@ -47,7 +47,7 @@ var/list/event_last_fired = list()
possibleEvents[/datum/event/economic_event] = 300
possibleEvents[/datum/event/trivial_news] = 400
- possibleEvents[/datum/event/mundane_news] = 300
+ //possibleEvents[/datum/event/mundane_news] = 300
possibleEvents[/datum/event/lore_news] = 300 // up this if the above ones get removed as they damn well should
possibleEvents[/datum/event/pda_spam] = max(min(25, player_list.len) * 4, 200)
diff --git a/code/modules/lore_codex/news_data/main.dm b/code/modules/lore_codex/news_data/main.dm
index 2b0c7a4a81b..cf3660a6206 100644
--- a/code/modules/lore_codex/news_data/main.dm
+++ b/code/modules/lore_codex/news_data/main.dm
@@ -4,6 +4,8 @@
region. Each is labled by date of publication and title. This list is self-updating, and from time to time the publisher will push new \
articles. You are encouraged to check back frequently."
children = list(
+ /datum/lore/codex/page/article9,
+ /datum/lore/codex/page/article8,
/datum/lore/codex/page/article7,
/datum/lore/codex/page/article6,
/datum/lore/codex/page/article5,
@@ -81,7 +83,7 @@
/datum/lore/codex/page/article6
name = "3/03/62 - A Week Out From Almach: What are the facts?"
data = "* Several organizations in the Almach Rim, including Angessa's Pearl, the Aetolian Council, the Interstellar Workers of Wythe, the Republic of Whitney, and members of several prominent families in the Neon Light unilaterally declared secession from SCG.
*This secession was first called the Grey Hour by political scientists in New Florence, a term popularized by reporter Elspor Fong.
* Shelf, the FRF, and the EFRB were declared \"observers\" in the Almach Association charter.
* None of these organizations have issued a statement on the matter.
* The SCG-R Song Shi was stranded in the region during the secession.
* SolGov has not issued an official statement of the fate of the Song Shi.
* Several confederate agencies, including Emergent Intelligence Oversight, the Trade and Customs Bureau, and SCG Fleet Intelligence have declared a \"state of emergency\".
* SolGov itself has NOT declared a state of emergency.
* Legitimate communications in and out of the Almach Rim are restricted to audited text messages for the period.
* Several illegitimate communication links exist and are believed by Fleet Intelligence to be currently hosting the official sites for Morpheus Cyberkinetics and for the Association itself.
* Icarus Front chairperson Mackenzie West has proposed a moratorium on the creation of new Prometheans for the duration of the crisis.
* Local laws on the subject will apply until the Assembly meets late in May.
* No confederate lawmaker has proposed action against Relani, Shelfican, or newly Almachi nationals living within stable regions.
* The border remains tightly closed to migrants, media, and diplomats alike."
-
+
/datum/lore/codex/page/article7
name = "3/21/62 - Relan, Shelf Join the Almach Association"
data = "Recent reports from within the Association indicate that the Free Relan Federation and Shelf have officially decided to join the Almach Association. President Nia Fischer of the FRF had this to say on the matter, in a speech addressed to the population at large. \
@@ -94,3 +96,16 @@
\
\"We're probably going to regret this but, y'know, the whole thing is kind of our fault. Sure, whatever.\""
+
+/datum/lore/codex/page/article8
+ name = "4/1/62 - Almach Cordon Breached by Unknown Organization"
+ data = "Early this morning, SolGov ships assigned to the Almach Cordon around the Rim territories reported that a number of bulk freighters had eluded apprehension and are now at large within the Golden Crescent. Lieutenant-Major Volkov of the SCG-D Henri Capet reports that the blockade-runners were highly organized and determined, citing several lightly-manned ships left behind to tie up the SolGov forces long enough for the freighters to escape, detonating their reactors when they lost the ability to continue fighting. This resulted in three Fleet casualties and a significant degree of damage to the Henri Capet. The contents and location of the freighters are unknown at this time. In response, eight light-response vessels are being assigned to the Saint Columbia Fleet Base from Jahan's Post and Zhu Que. Residents and traffic officials in Vir, Oasis, and Gavel are to remain alert and notify police if any suspicious or unregistered craft enter their space.\
+
\
+ A spokesperson for the Association claims that, while they make no attempts to stop aspiring blockade runners, the organization responsible for this most recent attack is unaffiliated with the Association as a whole and deny any knowledge of their identity or motives."
+
+/datum/lore/codex/page/article9
+ name = "4/7/62 - Boiling Point Tragedy in Gavel"
+ data = "Today, April the Seventh, marks a day of tragedy for all the galaxy. A small group of operatives claiming to be associated with Mercurial terrorist organization Boiling Point invaded major refueling platform NLS Aquarius in the Republic of Gavel after hijacking civilian transport vessel WTV Orion and faking a drive failure. Several detonations were reported within the Aquarius, the operatives entering through unknown (potentially Skrellian) means. After stating their affiliation and desire for the liberation of all \"Prometheans, drones, and ex-humans\", they opened fire on a crowd of unarmed bystanders, killing as many as seven. A multiple-hour long firefight with Nanotrasen corporate asset protection ensued, at which point the operatives demonstrated capabilities well in excess of Five Points-prescribed limits. Asset Protection was successful in repelling the terrorists, though their harsh methods drew outrage from the people they were protecting, leading a notable director of research to resign her position with the corporation. Several operatives are still at large, though the SG-PV Juno recovered two living terrorists and one totaled synthetic platform. \
+
\
+ The intervention of a local Defense Force drone wing on behalf of the terrorists leads many in the intelligence community to assume that more Boiling Point operatives remain active within Gavel, and possibly nearby systems such as Vir and Oasis. Some have also noted that elements of the terrorists' tactics and augmentations suggest Association training, though the specifics remain classified. More information as the story breaks."
+
diff --git a/code/modules/mob/_modifiers/unholy.dm b/code/modules/mob/_modifiers/unholy.dm
new file mode 100644
index 00000000000..1f7f3d67b5f
--- /dev/null
+++ b/code/modules/mob/_modifiers/unholy.dm
@@ -0,0 +1,146 @@
+/*
+ * Modifiers originating from cult runes, spells, or pylons are kept here.
+ */
+
+
+////////// Self-Enhancing
+/datum/modifier/fortify
+ name = "fortified body"
+ desc = "You are taking less damage from outside sources."
+
+ on_created_text = "Your body becomes a mountain to your enemies' storm."
+ on_expired_text = "Your body softens, returning to its regular durability."
+ stacks = MODIFIER_STACK_EXTEND
+
+ disable_duration_percent = 0.25 // Disables only last 25% as long.
+ incoming_damage_percent = 0.5 // 50% incoming damage.
+ icon_scale_percent = 1.2 // Become a bigger target.
+ pain_immunity = TRUE
+
+ slowdown = 2
+ evasion = -20
+
+/datum/modifier/ambush
+ name = "phased"
+ desc = "You are partially shifted from the material plane."
+
+ on_created_text = "Your body pulses, before partially dematerializing."
+ on_expired_text = "Your body rematerializes fully."
+
+ stacks = MODIFIER_STACK_FORBID
+
+ incoming_damage_percent = 0.1 // 10% incoming damage. You're not all there.
+ outgoing_melee_damage_percent = 0 // 0% outgoing damage. Be prepared.
+ pain_immunity = TRUE
+
+ evasion = 50 //Luckily, not being all there means you're actually hard to hit with a gun.
+
+/datum/modifier/ambush/on_applied()
+ holder.alpha = 30
+ return
+
+// Override this for special effects when it gets removed.
+/datum/modifier/ambush/on_expire()
+ holder.alpha = 255
+ return
+
+////////// On-hit
+/datum/modifier/deep_wounds
+ name = "deep wounds"
+ desc = "Your wounds are mysteriously harder to mend."
+
+ on_created_text = "Your wounds pain you greatly."
+ on_expired_text = "Your wounds numb for a moment."
+
+ stacks = MODIFIER_STACK_EXTEND
+
+ incoming_healing_percent = 0.66 // 34% less healing.
+ disable_duration_percent = 1.22 // 22% longer disables.
+
+
+////////// Auras
+/datum/modifier/repair_aura //This aura does not apply modifiers to individuals in the area.
+ name = "aura of repair (cult)"
+ desc = "You are emitting a field of strange energy, capable of repairing occult constructs."
+
+ on_created_text = "You begin emitting an occult repair aura."
+ on_expired_text = "The occult repair aura fades."
+ stacks = MODIFIER_STACK_EXTEND
+
+ mob_overlay_state = "cult_aura"
+
+/datum/modifier/repair_aura/tick()
+ spawn()
+ for(var/mob/living/simple_animal/construct/T in view(4,holder))
+ T.adjustBruteLoss(rand(-10,-15))
+ T.adjustFireLoss(rand(-10,-15))
+
+/datum/modifier/agonize //This modifier is used in an aura spell.
+ name = "agonize"
+ desc = "Your body is wracked with pain."
+
+ on_created_text = "A red lightning quickly covers your body. The pain is horrendous."
+ on_expired_text = "The lightning fades, and so too does the ongoing pain."
+
+ stacks = MODIFIER_STACK_EXTEND
+
+ mob_overlay_state = "red_electricity_constant"
+
+/datum/modifier/agonize/tick()
+ spawn()
+ if(ishuman(holder))
+ var/mob/living/carbon/human/H = holder
+ H.apply_effect(20, AGONY)
+ if(prob(10))
+ to_chat(H, "Just make it stop!")
+
+////////// Target Modifier
+/datum/modifier/mend_occult
+ name = "occult mending"
+ desc = "Your body is mending, though at what cost?"
+
+ on_created_text = "Something haunting envelops your body as it begins to mend."
+ on_expired_text = "The cloak of unease dissipates."
+
+ stacks = MODIFIER_STACK_EXTEND
+
+ mob_overlay_state = "red_electricity_constant"
+
+/datum/modifier/mend_occult/tick()
+ spawn()
+ if(isliving(holder))
+ var/mob/living/L = holder
+ if(istype(L, /mob/living/simple_animal/construct))
+ L.adjustBruteLoss(rand(-5,-10))
+ L.adjustFireLoss(rand(-5,-10))
+ else
+ L.adjustBruteLoss(-2)
+ L.adjustFireLoss(-2)
+
+ if(ishuman(holder))
+ var/mob/living/carbon/human/H = holder
+
+ for(var/obj/item/organ/O in H.internal_organs)
+ if(O.damage > 0) // Fix internal damage
+ O.damage = max(O.damage - 2, 0)
+ if(O.damage <= 5 && O.organ_tag == O_EYES) // Fix eyes
+ H.sdisabilities &= ~BLIND
+
+ for(var/obj/item/organ/external/O in H.organs) // Fix limbs, no matter if they are Man or Machine.
+ O.heal_damage(rand(1,3), rand(1,3), internal = 1, robo_repair = 1)
+
+ for(var/obj/item/organ/E in H.bad_external_organs) // Fix bones
+ var/obj/item/organ/external/affected = E
+ if((affected.damage < affected.min_broken_damage * config.organ_health_multiplier) && (affected.status & ORGAN_BROKEN))
+ affected.status &= ~ORGAN_BROKEN
+
+ for(var/datum/wound/W in affected.wounds) // Fix IB
+ if(istype(W, /datum/wound/internal_bleeding))
+ affected.wounds -= W
+ affected.update_damages()
+
+ H.restore_blood()
+ H.apply_effect(15, AGONY)
+ if(prob(10))
+ to_chat(H, "It feels as though your body is being torn apart!")
+ L.updatehealth()
diff --git a/code/modules/mob/animations.dm b/code/modules/mob/animations.dm
index f54035596b3..c5bded9765d 100644
--- a/code/modules/mob/animations.dm
+++ b/code/modules/mob/animations.dm
@@ -205,8 +205,11 @@ note dizziness decrements automatically in the mob's Life() proc.
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2)
animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2)
-/mob/living/do_attack_animation(atom/A)
+/mob/living/do_attack_animation(atom/A, no_attack_icons = FALSE)
..()
+ if(no_attack_icons)
+ return FALSE
+
//Check for clients with pref enabled
var/list/viewing = list()
for(var/m in viewers(A))
@@ -265,6 +268,7 @@ note dizziness decrements automatically in the mob's Life() proc.
// And animate the attack!
animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3)
+ update_icon()
return TRUE //Found an item, doing item attack animation.
/mob/proc/spin(spintime, speed)
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 36e38532957..9aac48a3e6b 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -147,8 +147,8 @@
if(A.concealed_holster == 0 && A.show_examine)
accessories_visible.Add(A)
if(accessories_visible.len)
- tie_msg += ". Attached to it is [lowertext(english_list(accessories_visible))]"
- else tie_msg += ". Attached to it is [lowertext(english_list(U.accessories))]"
+ tie_msg += ". Attached to it is [english_list(accessories_visible)]"
+ else tie_msg += ". Attached to it is [english_list(U.accessories)]"
if(w_uniform.blood_DNA)
msg += "[T.He] [T.is] wearing \icon[w_uniform] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [w_uniform.name][tie_msg]!\n"
@@ -168,7 +168,7 @@
if(istype(wear_suit,/obj/item/clothing/suit))
var/obj/item/clothing/suit/U = wear_suit
if(U.accessories.len)
- tie_msg += ". Attached to it is [lowertext(english_list(U.accessories))]"
+ tie_msg += ". Attached to it is [english_list(U.accessories)]"
if(wear_suit.blood_DNA)
msg += "[T.He] [T.is] wearing \icon[wear_suit] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [wear_suit.name]!\n"
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index ad3f393116d..e4eaa51963e 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -10,10 +10,10 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
////////////////////////////////////////////////////////////////////////////////////////////////
// # Human Icon Updating System
//
-// This system takes care of the "icon" for human mobs. Of course humans don't just have a single
-// icon+icon_state, but a combination of dozens of little sprites including including the body,
+// This system takes care of the "icon" for human mobs. Of course humans don't just have a single
+// icon+icon_state, but a combination of dozens of little sprites including including the body,
// clothing, equipment, in-universe HUD images, etc.
-//
+//
// # Basic Operation
// Whenever you do something that should update the on-mob appearance of a worn or held item, You
// will need to call the relevant update_inv_* proc. All of these are named after the variable they
@@ -25,14 +25,14 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
// only get assigned to the mob once per tick.)
// As a corrolary, this means users of this system do NOT need to tell the system when you're done
// making changes.
-//
+//
// There are also these special cases:
// update_icons_body() //Handles updating your mob's icon to reflect their gender/race/complexion etc
// UpdateDamageIcon() //Handles damage overlays for brute/burn damage //(will rename this when I geta round to it) ~Carn
// update_skin() //Handles updating skin for species that have a skin overlay.
// update_bloodied() //Handles adding/clearing the blood overlays for hands & feet. Call when bloodied or cleaned.
// update_underwear() //Handles updating the sprite for underwear.
-// update_hair() //Handles updating your hair and eyes overlay
+// update_hair() //Handles updating your hair and eyes overlay
// update_mutations() //Handles updating your appearance for certain mutations. e.g TK head-glows
// update_fire() //Handles overlay from being on fire.
// update_water() //Handles overlay from being submerged.
@@ -138,11 +138,11 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
// Regular stuff again.
var/matrix/M = matrix()
var/anim_time = 3
-
+
//Due to some involuntary means, you're laying now
if(lying && !resting && !sleeping)
anim_time = 1 //Thud
-
+
if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone.
M.Turn(90)
M.Scale(desired_scale)
@@ -338,7 +338,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return
remove_layer(SKIN_LAYER)
-
+
var/image/skin = species.update_skin(src)
if(skin)
skin.layer = BODY_LAYER+SKIN_LAYER
@@ -354,7 +354,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return
var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+BLOOD_LAYER)
-
+
//Bloody hands
if(blood_DNA)
var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "bloodyhands", layer = BODY_LAYER+BLOOD_LAYER)
@@ -485,7 +485,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return
remove_layer(MUTATIONS_LAYER)
-
+
if(!LAZYLEN(mutations))
return //No mutations, no icons.
@@ -496,7 +496,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
var/image/standing = image(icon = 'icons/effects/genetics.dmi', layer = BODY_LAYER+MUTATIONS_LAYER)
var/g = gender == FEMALE ? "f" : "m"
-
+
for(var/datum/dna/gene/gene in dna_genes)
if(!gene.block)
continue
@@ -559,7 +559,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
//Shoes can be affected by uniform being drawn onto them
update_inv_shoes()
-
+
if(!w_uniform)
return
@@ -575,7 +575,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return
remove_layer(ID_LAYER)
-
+
if(!wear_id)
return //Not wearing an ID
@@ -584,7 +584,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
var/obj/item/clothing/under/U = w_uniform
if(U.displays_id)
overlays_standing[ID_LAYER] = wear_id.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_wear_id_str, default_icon = INV_WEAR_ID_DEF_ICON, default_layer = ID_LAYER)
-
+
apply_layer(ID_LAYER)
/mob/living/carbon/human/update_inv_gloves()
@@ -597,7 +597,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return //No gloves, no reason to be here.
overlays_standing[GLOVES_LAYER] = gloves.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_gloves_str, default_icon = INV_GLOVES_DEF_ICON, default_layer = GLOVES_LAYER)
-
+
apply_layer(GLOVES_LAYER)
/mob/living/carbon/human/update_inv_glasses()
@@ -610,7 +610,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return //Not wearing glasses, no need to update anything.
overlays_standing[GLASSES_LAYER] = glasses.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_gloves_str, default_icon = INV_EYES_DEF_ICON, default_layer = GLASSES_LAYER)
-
+
apply_layer(GLASSES_LAYER)
/mob/living/carbon/human/update_inv_ears()
@@ -624,7 +624,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
if(!l_ear && !r_ear)
return //Why bother, if no ear sprites
-
+
// Blank image upon which to layer left & right overlays.
var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+EARS_LAYER)
@@ -658,10 +658,10 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
//NB: the use of a var for the layer on this one
overlays_standing[shoe_layer] = shoes.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_shoes_str, default_icon = INV_FEET_DEF_ICON, default_layer = shoe_layer)
-
+
apply_layer(SHOES_LAYER)
apply_layer(SHOES_LAYER_ALT)
-
+
/mob/living/carbon/human/update_inv_s_store()
if(QDESTROYING(src))
return
@@ -670,14 +670,14 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
if(!s_store)
return //Why bother, nothing there.
-
+
//TODO, this is unlike the rest of the things
//Basically has no variety in slot icon choices at all. WHY SPECIES ONLY??
var/t_state = s_store.item_state
if(!t_state)
t_state = s_store.icon_state
overlays_standing[SUIT_STORE_LAYER] = image(icon = species.suit_storage_icon, icon_state = t_state, layer = BODY_LAYER+SUIT_STORE_LAYER)
-
+
apply_layer(SUIT_STORE_LAYER)
/mob/living/carbon/human/update_inv_head()
@@ -685,12 +685,12 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return
remove_layer(HEAD_LAYER)
-
+
if(!head)
return //No head item, why bother.
overlays_standing[HEAD_LAYER] = head.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_head_str, default_icon = INV_HEAD_DEF_ICON, default_layer = HEAD_LAYER)
-
+
apply_layer(HEAD_LAYER)
/mob/living/carbon/human/update_inv_belt()
@@ -702,7 +702,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
if(!belt)
return //No belt, why bother.
-
+
//Toggle for belt layering with uniform
var/belt_layer = BELT_LAYER
if(istype(belt, /obj/item/weapon/storage/belt))
@@ -712,7 +712,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
//NB: this uses a var from above
overlays_standing[belt_layer] = belt.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_belt_str, default_icon = INV_BELT_DEF_ICON, default_layer = belt_layer)
-
+
apply_layer(belt_layer)
/mob/living/carbon/human/update_inv_wear_suit()
@@ -731,7 +731,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return //No point, no suit.
overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_wear_suit_str, default_icon = INV_SUIT_DEF_ICON, default_layer = SUIT_LAYER)
-
+
apply_layer(SUIT_LAYER)
/mob/living/carbon/human/update_inv_pockets()
@@ -745,9 +745,9 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
if(!wear_mask || (head && head.flags_inv & HIDEMASK))
return //Why bother, nothing in mask slot.
-
+
overlays_standing[FACEMASK_LAYER] = wear_mask.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_wear_mask_str, default_icon = INV_MASK_DEF_ICON, default_layer = FACEMASK_LAYER)
-
+
apply_layer(FACEMASK_LAYER)
/mob/living/carbon/human/update_inv_back()
@@ -760,7 +760,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return //Why do anything
overlays_standing[BACK_LAYER] = back.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_back_str, default_icon = INV_BACK_DEF_ICON, default_layer = BACK_LAYER)
-
+
apply_layer(BACK_LAYER)
//TODO: Carbon procs in my human update_icons??
@@ -793,7 +793,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return //Not cuffed, why bother
overlays_standing[HANDCUFF_LAYER] = handcuffed.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_handcuffed_str, default_icon = INV_HCUFF_DEF_ICON, default_layer = HANDCUFF_LAYER)
-
+
apply_layer(HANDCUFF_LAYER)
/mob/living/carbon/human/update_inv_legcuffed()
@@ -805,8 +805,8 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
if(!legcuffed)
return //Not legcuffed, why bother.
- overlays_standing[LEGCUFF_LAYER] = handcuffed.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_legcuffed_str, default_icon = INV_LCUFF_DEF_ICON, default_layer = LEGCUFF_LAYER)
-
+ overlays_standing[LEGCUFF_LAYER] = legcuffed.make_worn_icon(body_type = species.get_bodytype(), slot_name = slot_legcuffed_str, default_icon = INV_LCUFF_DEF_ICON, default_layer = LEGCUFF_LAYER)
+
apply_layer(LEGCUFF_LAYER)
/mob/living/carbon/human/update_inv_r_hand()
@@ -819,7 +819,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return //No hand, no bother.
overlays_standing[R_HAND_LAYER] = r_hand.make_worn_icon(body_type = species.get_bodytype(), inhands = TRUE, slot_name = slot_r_hand_str, default_icon = INV_R_HAND_DEF_ICON, default_layer = R_HAND_LAYER)
-
+
apply_layer(R_HAND_LAYER)
/mob/living/carbon/human/update_inv_l_hand()
@@ -884,16 +884,16 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
/mob/living/carbon/human/proc/set_tail_state(var/t_state)
var/used_tail_layer = tail_alt ? TAIL_LAYER_ALT : TAIL_LAYER // VOREStation Edit - START - Alt Tail Layer
var/image/tail_overlay = overlays_standing[used_tail_layer]
-
+
remove_layer(TAIL_LAYER)
remove_layer(TAIL_LAYER_ALT)
-
+
if(tail_overlay)
overlays_standing[used_tail_layer] = tail_overlay
if(species.get_tail_animation(src))
tail_overlay.icon_state = t_state
. = tail_overlay
-
+
apply_layer(used_tail_layer) // VOREStation Edit - END
//Not really once, since BYOND can't do that.
@@ -976,7 +976,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
effects.overlays += I //TODO, this compositing is annoying.
overlays_standing[MODIFIER_EFFECTS_LAYER] = effects
-
+
apply_layer(MODIFIER_EFFECTS_LAYER)
/mob/living/carbon/human/update_fire()
@@ -984,12 +984,12 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return
remove_layer(FIRE_LAYER)
-
+
if(!on_fire)
return
-
+
overlays_standing[FIRE_LAYER] = image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state(), layer = BODY_LAYER+FIRE_LAYER)
-
+
apply_layer(FIRE_LAYER)
/mob/living/carbon/human/update_water()
@@ -1003,7 +1003,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
return
overlays_standing[WATER_LAYER] = image(icon = 'icons/mob/submerged.dmi', icon_state = "human_swimming_[depth]", layer = BODY_LAYER+WATER_LAYER) //TODO: Improve
-
+
apply_layer(WATER_LAYER)
/mob/living/carbon/human/proc/update_surgery()
diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm
index c0034b19080..26ab3a12609 100644
--- a/code/modules/mob/living/simple_animal/constructs/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs/constructs.dm
@@ -1,22 +1,39 @@
/mob/living/simple_animal/construct
name = "Construct"
real_name = "Construct"
+ var/construct_type = "shade"
desc = ""
speak_emote = list("hisses")
emote_hear = list("wails","screeches")
+
+ ui_icons = 'icons/mob/screen1_construct.dmi'
+ has_hands = 1
+ hand_form = "stone manipulators"
+
response_help = "thinks better of touching"
response_disarm = "flailed at"
response_harm = "punched"
+
intelligence_level = SA_HUMANOID // Player controlled.
+
hovering = TRUE
+ softfall = TRUE //Beings made of Hellmarble and powered by the tears of the damned are not concerned with mortal things such as 'gravity'.
+ parachuting = TRUE
+
icon_dead = "shade_dead"
+ var/do_glow = 1
+
speed = -1
a_intent = I_HURT
stop_automated_movement = 1
+
status_flags = CANPUSH
+
universal_speak = 0
universal_understand = 1
+
attack_sound = 'sound/weapons/spiderlunge.ogg'
+
min_oxy = 0
max_oxy = 0
min_tox = 0
@@ -25,11 +42,16 @@
max_co2 = 0
min_n2 = 0
max_n2 = 0
+
minbodytemp = 0
show_stat_health = 1
+
faction = "cult"
supernatural = 1
+
see_invisible = SEE_INVISIBLE_NOLIGHTING
+ see_in_dark = 7
+
var/nullblock = 0
mob_swap_flags = HUMAN|SIMPLE_ANIMAL|SLIME|MONKEY
@@ -39,6 +61,51 @@
can_be_antagged = TRUE
+ taser_kill = 0 // no
+
+ shock_resistance = 0.9 //Electricity isn't very effective on stone, especially that from hell.
+
+ armor = list(
+ "melee" = 10,
+ "bullet" = 10,
+ "laser" = 10,
+ "energy" = 10,
+ "bomb" = 10,
+ "bio" = 100,
+ "rad" = 100)
+
+/mob/living/simple_animal/construct/place_spell_in_hand(var/path)
+ if(!path || !ispath(path))
+ return 0
+
+ //var/obj/item/weapon/spell/S = new path(src)
+ var/obj/item/weapon/spell/construct/S = new path(src)
+
+ //No hands needed for innate casts.
+ if(S.cast_methods & CAST_INNATE)
+ if(S.run_checks())
+ S.on_innate_cast(src)
+
+ if(l_hand && r_hand) //Make sure our hands aren't full.
+ if(istype(r_hand, /obj/item/weapon/spell)) //If they are full, perhaps we can still be useful.
+ var/obj/item/weapon/spell/r_spell = r_hand
+ if(r_spell.aspect == ASPECT_CHROMATIC) //Check if we can combine the new spell with one in our hands.
+ r_spell.on_combine_cast(S, src)
+ else if(istype(l_hand, /obj/item/weapon/spell))
+ var/obj/item/weapon/spell/l_spell = l_hand
+ if(l_spell.aspect == ASPECT_CHROMATIC) //Check the other hand too.
+ l_spell.on_combine_cast(S, src)
+ else //Welp
+ to_chat(src, "You require a free manipulator to use this power.")
+ return 0
+
+ if(S.run_checks())
+ put_in_hands(S)
+ return 1
+ else
+ qdel(S)
+ return 0
+
/mob/living/simple_animal/construct/cultify()
return
@@ -51,7 +118,17 @@
for(var/spell in construct_spells)
src.add_spell(new spell, "const_spell_ready")
updateicon()
- add_glow()
+
+/mob/living/simple_animal/construct/updateicon()
+ overlays.Cut()
+ ..()
+ if(do_glow)
+ add_glow()
+
+/mob/living/simple_animal/construct/update_icon()
+ ..()
+ if(do_glow)
+ add_glow()
/mob/living/simple_animal/construct/death()
new /obj/item/weapon/ectoplasm (src.loc)
@@ -61,11 +138,15 @@
/mob/living/simple_animal/construct/attack_generic(var/mob/user)
if(istype(user, /mob/living/simple_animal/construct/builder))
+ var/mob/living/simple_animal/construct/builder/B = user
if(health < getMaxHealth())
- adjustBruteLoss(-5)
+ var/repair_lower_bound = B.melee_damage_lower * -1
+ var/repair_upper_bound = B.melee_damage_upper * -1
+ adjustBruteLoss(rand(repair_lower_bound, repair_upper_bound))
+ adjustFireLoss(rand(repair_lower_bound, repair_upper_bound))
user.visible_message("\The [user] mends some of \the [src]'s wounds.")
else
- user << "\The [src] is undamaged."
+ to_chat(user, "\The [src] is undamaged.")
return
return ..()
@@ -83,6 +164,8 @@
user << msg
+/mob/living/simple_animal/construct/Process_Spacemove()
+ return 1 //Constructs levitate, can fall from a shuttle with no harm, and are piloted by either damned spirits or some otherworldly entity. It's not hard to believe.
/////////////////Juggernaut///////////////
@@ -91,48 +174,83 @@
/mob/living/simple_animal/construct/armoured
name = "Juggernaut"
real_name = "Juggernaut"
+ construct_type = "juggernaut"
desc = "A possessed suit of armour driven by the will of the restless dead"
icon = 'icons/mob/mob.dmi'
icon_state = "behemoth"
icon_living = "behemoth"
- maxHealth = 250
- health = 250
+ maxHealth = 300
+ health = 300
response_harm = "harmlessly punches"
harm_intent_damage = 0
melee_damage_lower = 30
- melee_damage_upper = 30
+ melee_damage_upper = 40
+ attack_armor_pen = 60 //Being punched by a living, floating statue.
attacktext = list("smashed their armoured gauntlet into")
+ friendly = list("pats")
mob_size = MOB_HUGE
- speed = 3
+ speed = 2 //Not super fast, but it might catch up to someone in armor who got punched once or twice.
environment_smash = 2
attack_sound = 'sound/weapons/heavysmash.ogg'
status_flags = 0
resistance = 10
- construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser)
+ construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser,
+ /spell/targeted/fortify,
+ /spell/targeted/construct_advanced/slam
+ )
+
+ armor = list(
+ "melee" = 70,
+ "bullet" = 30,
+ "laser" = 30,
+ "energy" = 30,
+ "bomb" = 10,
+ "bio" = 100,
+ "rad" = 100)
/mob/living/simple_animal/construct/armoured/Life()
weakened = 0
..()
/mob/living/simple_animal/construct/armoured/bullet_act(var/obj/item/projectile/P)
- if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))
- var/reflectchance = 80 - round(P.damage/3)
- if(prob(reflectchance))
- adjustBruteLoss(P.damage * 0.5)
+// if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) //If it's going to be slow, it's probably going to need every reflect it can get.
+ var/reflectchance = 80 - round(P.damage/3)
+ if(prob(reflectchance))
+ var/damage_mod = rand(2,4)
+ var/projectile_dam_type = P.damage_type
+ var/incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3)))
+ var/armorcheck = run_armor_check(null, P.check_armour)
+ var/soakedcheck = get_armor_soak(null, P.check_armour)
+ if(!(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)))
+ visible_message("The [P.name] bounces off of [src]'s shell!", \
+ "The [P.name] bounces off of [src]'s shell!")
+ new /obj/item/weapon/material/shard/shrapnel(src.loc)
+ if(!(P.damage_type == BRUTE || P.damage_type == BURN))
+ projectile_dam_type = BRUTE
+ incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to brute for physical projectiles, though severely decreased.
+ apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P)
+ return -1 //Doesn't reflect non-beams or non-energy projectiles. They just smack and drop with little to no effect.
+ else
visible_message("The [P.name] gets reflected by [src]'s shell!", \
- "The [P.name] gets reflected by [src]'s shell!")
+ "The [P.name] gets reflected by [src]'s shell!")
+ damage_mod = rand(3,5)
+ incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3)))
+ if(!(P.damage_type == BRUTE || P.damage_type == BURN))
+ projectile_dam_type = BURN
+ incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to burn for energy-type projectiles, though severely decreased.
+ apply_damage(incoming_damage, P.damage_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P)
- // Find a turf near or on the original location to bounce to
- if(P.starting)
- var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
- var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
- var/turf/curloc = get_turf(src)
+ // Find a turf near or on the original location to bounce to
+ if(P.starting)
+ var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
+ var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
+ var/turf/curloc = get_turf(src)
- // redirect the projectile
- P.redirect(new_x, new_y, curloc, src)
- P.reflected = 1
+ // redirect the projectile
+ P.redirect(new_x, new_y, curloc, src)
+ P.reflected = 1
- return -1 // complete projectile permutation
+ return -1 // complete projectile permutation
return (..(P))
@@ -145,21 +263,32 @@
/mob/living/simple_animal/construct/wraith
name = "Wraith"
real_name = "Wraith"
- desc = "A wicked bladed shell contraption piloted by a bound spirit"
+ construct_type = "wraith"
+ desc = "A wicked bladed shell contraption piloted by a bound spirit."
icon = 'icons/mob/mob.dmi'
icon_state = "floating"
icon_living = "floating"
- maxHealth = 75
- health = 75
+ maxHealth = 200
+ health = 200
melee_damage_lower = 25
- melee_damage_upper = 25
+ melee_damage_upper = 30
+ attack_armor_pen = 15
+ attack_sharp = 1
+ attack_edge = 1
attacktext = list("slashed")
+ friendly = list("pinches")
speed = -1
environment_smash = 1
- see_in_dark = 7
attack_sound = 'sound/weapons/rapidslice.ogg'
- construct_spells = list(/spell/targeted/ethereal_jaunt/shift)
+ construct_spells = list(/spell/targeted/ethereal_jaunt/shift,
+ /spell/targeted/ambush_mode
+ )
+/mob/living/simple_animal/construct/wraith/DoPunch(var/atom/A)
+ . = ..()
+ if(. && isliving(A))
+ var/mob/living/L = A
+ L.add_modifier(/datum/modifier/deep_wounds, 30 SECONDS)
/////////////////////////////Artificer/////////////////////////
@@ -168,35 +297,43 @@
/mob/living/simple_animal/construct/builder
name = "Artificer"
real_name = "Artificer"
- desc = "A bulbous construct dedicated to building and maintaining The Cult of Nar-Sie's armies"
+ construct_type = "artificer"
+ desc = "A bulbous construct dedicated to building and maintaining temples to their otherworldly lords."
icon = 'icons/mob/mob.dmi'
icon_state = "artificer"
icon_living = "artificer"
- maxHealth = 50
- health = 50
+ maxHealth = 150
+ health = 150
response_harm = "viciously beaten"
harm_intent_damage = 5
- melee_damage_lower = 5
- melee_damage_upper = 5
+ melee_damage_lower = 15 //It's not the strongest of the bunch, but that doesn't mean it can't hurt you.
+ melee_damage_upper = 20
attacktext = list("rammed")
speed = 0
- environment_smash = 1
+ environment_smash = 2
attack_sound = 'sound/weapons/rapidslice.ogg'
construct_spells = list(/spell/aoe_turf/conjure/construct/lesser,
/spell/aoe_turf/conjure/wall,
/spell/aoe_turf/conjure/floor,
/spell/aoe_turf/conjure/soulstone,
- /spell/aoe_turf/conjure/pylon
+ /spell/aoe_turf/conjure/pylon,
+ /spell/aoe_turf/conjure/door,
+ /spell/aoe_turf/conjure/grille,
+ /spell/targeted/occult_repair_aura,
+ /spell/targeted/construct_advanced/mend_acolyte
)
/////////////////////////////Behemoth/////////////////////////
-
+/*
+ * The Behemoth. Admin-allowance only, still try to keep it in some guideline of 'Balanced', even if it means Security has to be fully geared to be so.
+ */
/mob/living/simple_animal/construct/behemoth
name = "Behemoth"
real_name = "Behemoth"
- desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal."
+ construct_type = "juggernaut"
+ desc = "The pinnacle of occult technology, Behemoths are nothing shy of both an Immovable Object, and Unstoppable Force."
icon = 'icons/mob/mob.dmi'
icon_state = "behemoth"
icon_living = "behemoth"
@@ -208,38 +345,84 @@
melee_damage_lower = 50
melee_damage_upper = 50
attacktext = list("brutally crushed")
+ friendly = list("pokes") //Anything nice the Behemoth would do would still Kill the Human. Leave it at poke.
speed = 5
environment_smash = 2
attack_sound = 'sound/weapons/heavysmash.ogg'
resistance = 10
+ icon_scale = 2
var/energy = 0
var/max_energy = 1000
- construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser)
+ armor = list(
+ "melee" = 60,
+ "bullet" = 60,
+ "laser" = 60,
+ "energy" = 30,
+ "bomb" = 10,
+ "bio" = 100,
+ "rad" = 100)
+ construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser,
+ /spell/targeted/fortify,
+ /spell/targeted/construct_advanced/slam
+ )
+
+/mob/living/simple_animal/construct/behemoth/bullet_act(var/obj/item/projectile/P)
+ var/reflectchance = 80 - round(P.damage/3)
+ if(prob(reflectchance))
+ visible_message("The [P.name] gets reflected by [src]'s shell!", \
+ "The [P.name] gets reflected by [src]'s shell!")
+
+ // Find a turf near or on the original location to bounce to
+ if(P.starting)
+ var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
+ var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
+ var/turf/curloc = get_turf(src)
+
+ // redirect the projectile
+ P.redirect(new_x, new_y, curloc, src)
+ P.reflected = 1
+
+ return -1 // complete projectile permutation
+
+ return (..(P))
////////////////////////Harvester////////////////////////////////
-
-
+/*
+ * Master of Spells and Ranged Abilities. Not as fragile as the Wraith, but nowhere near as maneuverable and deadly in melee.
+ */
/mob/living/simple_animal/construct/harvester
name = "Harvester"
real_name = "Harvester"
- desc = "The promised reward of the livings who follow narsie. Obtained by offering their bodies to the geometer of blood"
+ construct_type = "harvester"
+ desc = "A tendril-laden construct piloted by a chained mind."
icon = 'icons/mob/mob.dmi'
icon_state = "harvester"
icon_living = "harvester"
maxHealth = 150
health = 150
- melee_damage_lower = 25
+ melee_damage_lower = 20
melee_damage_upper = 25
+ attack_sharp = 1
attacktext = list("violently stabbed")
- speed = -1
+ friendly = list("caresses")
+ speed = 0
environment_smash = 1
- see_in_dark = 7
attack_sound = 'sound/weapons/pierce.ogg'
+ armor = list(
+ "melee" = 10,
+ "bullet" = 20,
+ "laser" = 20,
+ "energy" = 20,
+ "bomb" = 20,
+ "bio" = 100,
+ "rad" = 100)
+
construct_spells = list(
- /spell/targeted/harvest,
/spell/aoe_turf/knock/harvester,
+ /spell/targeted/construct_advanced/inversion_beam,
+ /spell/targeted/construct_advanced/agonizing_sphere,
/spell/rune_write
)
@@ -250,6 +433,9 @@
overlays += eye_glow
set_light(2, -2, l_color = "#FFFFFF")
+/mob/living/simple_animal/construct/proc/remove_glow()
+ overlays.Cut()
+
////////////////HUD//////////////////////
/mob/living/simple_animal/construct/Life()
@@ -268,71 +454,35 @@
silence_spells(purge)
-/mob/living/simple_animal/construct/armoured/Life()
- ..()
+/mob/living/simple_animal/construct/updatehealth() //Special icons.
+ health = getMaxHealth() - getToxLoss() - getFireLoss() - getBruteLoss()
+
+ //Alive, becoming dead
+ if((stat < DEAD) && (health <= 0))
+ death()
+
+ //Overhealth
+ if(health > getMaxHealth())
+ health = getMaxHealth()
+
+ //Update our hud if we have one
if(healths)
- switch(health)
- if(250 to INFINITY) healths.icon_state = "juggernaut_health0"
- if(208 to 249) healths.icon_state = "juggernaut_health1"
- if(167 to 207) healths.icon_state = "juggernaut_health2"
- if(125 to 166) healths.icon_state = "juggernaut_health3"
- if(84 to 124) healths.icon_state = "juggernaut_health4"
- if(42 to 83) healths.icon_state = "juggernaut_health5"
- if(1 to 41) healths.icon_state = "juggernaut_health6"
- else healths.icon_state = "juggernaut_health7"
-
-
-/mob/living/simple_animal/construct/behemoth/Life()
- ..()
- if(healths)
- switch(health)
- if(750 to INFINITY) healths.icon_state = "juggernaut_health0"
- if(625 to 749) healths.icon_state = "juggernaut_health1"
- if(500 to 624) healths.icon_state = "juggernaut_health2"
- if(375 to 499) healths.icon_state = "juggernaut_health3"
- if(250 to 374) healths.icon_state = "juggernaut_health4"
- if(125 to 249) healths.icon_state = "juggernaut_health5"
- if(1 to 124) healths.icon_state = "juggernaut_health6"
- else healths.icon_state = "juggernaut_health7"
-
-/mob/living/simple_animal/construct/builder/Life()
- ..()
- if(healths)
- switch(health)
- if(50 to INFINITY) healths.icon_state = "artificer_health0"
- if(42 to 49) healths.icon_state = "artificer_health1"
- if(34 to 41) healths.icon_state = "artificer_health2"
- if(26 to 33) healths.icon_state = "artificer_health3"
- if(18 to 25) healths.icon_state = "artificer_health4"
- if(10 to 17) healths.icon_state = "artificer_health5"
- if(1 to 9) healths.icon_state = "artificer_health6"
- else healths.icon_state = "artificer_health7"
-
-
-
-/mob/living/simple_animal/construct/wraith/Life()
- ..()
- if(healths)
- switch(health)
- if(75 to INFINITY) healths.icon_state = "wraith_health0"
- if(62 to 74) healths.icon_state = "wraith_health1"
- if(50 to 61) healths.icon_state = "wraith_health2"
- if(37 to 49) healths.icon_state = "wraith_health3"
- if(25 to 36) healths.icon_state = "wraith_health4"
- if(12 to 24) healths.icon_state = "wraith_health5"
- if(1 to 11) healths.icon_state = "wraith_health6"
- else healths.icon_state = "wraith_health7"
-
-
-/mob/living/simple_animal/construct/harvester/Life()
- ..()
- if(healths)
- switch(health)
- if(150 to INFINITY) healths.icon_state = "harvester_health0"
- if(125 to 149) healths.icon_state = "harvester_health1"
- if(100 to 124) healths.icon_state = "harvester_health2"
- if(75 to 99) healths.icon_state = "harvester_health3"
- if(50 to 74) healths.icon_state = "harvester_health4"
- if(25 to 49) healths.icon_state = "harvester_health5"
- if(1 to 24) healths.icon_state = "harvester_health6"
- else healths.icon_state = "harvester_health7"
+ if(stat != DEAD)
+ var/heal_per = (health / getMaxHealth()) * 100
+ switch(heal_per)
+ if(100 to INFINITY)
+ healths.icon_state = "[construct_type]_health0"
+ if(80 to 100)
+ healths.icon_state = "[construct_type]_health1"
+ if(60 to 80)
+ healths.icon_state = "[construct_type]_health2"
+ if(40 to 60)
+ healths.icon_state = "[construct_type]_health3"
+ if(20 to 40)
+ healths.icon_state = "[construct_type]_health4"
+ if(0 to 20)
+ healths.icon_state = "[construct_type]_health5"
+ else
+ healths.icon_state = "[construct_type]_health6"
+ else
+ healths.icon_state = "[construct_type]_health7"
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/constructs/soulstone.dm b/code/modules/mob/living/simple_animal/constructs/soulstone.dm
index baf99ec0e49..6f13789dfc3 100644
--- a/code/modules/mob/living/simple_animal/constructs/soulstone.dm
+++ b/code/modules/mob/living/simple_animal/constructs/soulstone.dm
@@ -11,6 +11,7 @@
slot_flags = SLOT_BELT
origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4)
var/imprinted = "empty"
+ var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester")
//////////////////////////////Capturing////////////////////////////////////////////////////////
@@ -145,9 +146,9 @@
src.icon_state = "soulstone2"
src.name = "Soul Stone: [S.real_name]"
- S << "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs."
- U << "Capture successful! : [T.real_name]'s soul has been ripped from their body and stored within the soul stone."
- U << "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls."
+ to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.")
+ to_chat(U, "Capture successful! : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.")
+ to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.")
src.imprinted = "[S.name]"
qdel(T)
@@ -155,13 +156,13 @@
if(!istype(T))
return;
if (T.stat == DEAD)
- U << "Capture failed!: The shade has already been banished!"
+ to_chat(U, "Capture failed!: The shade has already been banished!")
return
if(src.contents.len)
- U << "Capture failed!: The soul stone is full! Use or free an existing soul to make room."
+ to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
return
if(T.name != src.imprinted)
- U << "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!"
+ to_chat(U, "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!")
return
T.forceMove(src) //put shade in stone
@@ -170,14 +171,15 @@
T.health = T.getMaxHealth()
src.icon_state = "soulstone2"
- T << "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form"
- U << "Capture successful! : [T.name]'s has been recaptured and stored within the soul stone."
+ to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
+ to_chat(U, "Capture successful! : [T.name]'s has been recaptured and stored within the soul stone.")
+
/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U)
var/mob/living/simple_animal/shade/A = locate() in src
if(!A)
- U << "Capture failed!: The soul stone is empty! Go kill someone!"
+ to_chat(U,"Capture failed!: The soul stone is empty! Go kill someone!")
return;
- var/construct_class = alert(U, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
+ var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs
switch(construct_class)
if("Juggernaut")
var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(T.loc))
@@ -185,8 +187,8 @@
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
- Z << "You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike."
- Z << "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs."
+ to_chat(Z,"You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
Z.cancel_camera()
qdel(src)
if("Wraith")
@@ -195,8 +197,8 @@
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
- Z << "You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls."
- Z << "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs."
+ to_chat(Z,"You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
Z.cancel_camera()
qdel(src)
if("Artificer")
@@ -205,10 +207,31 @@
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
- Z << "You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs"
- Z << "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs."
+ to_chat(Z,"You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
Z.cancel_camera()
qdel(src)
+ if("Harvester")
+ var/mob/living/simple_animal/construct/harvester/Z = new /mob/living/simple_animal/construct/harvester (get_turf(T.loc))
+ Z.key = A.key
+ if(iscultist(U))
+ cult.add_antagonist(Z.mind)
+ qdel(T)
+ to_chat(Z,"You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ Z.cancel_camera()
+ qdel(src)
+ if("Behemoth")
+ var/mob/living/simple_animal/construct/behemoth/Z = new /mob/living/simple_animal/construct/behemoth (get_turf(T.loc))
+ Z.key = A.key
+ if(iscultist(U))
+ cult.add_antagonist(Z.mind)
+ qdel(T)
+ to_chat(Z,"You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.")
+ to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ Z.cancel_camera()
+ qdel(src)
+
/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
switch(choice)
if("VICTIM")
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 4c6d9312c4f..4580643574f 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -20,6 +20,8 @@
var/show_stat_health = 1 // Does the percentage health show in the stat panel for the mob
var/ai_inactive = 0 // Set to 1 to turn off most AI actions
var/has_hands = 0 // Set to 1 to enable the use of hands and the hands hud
+ var/humanoid_hands = 0 // Can a player in this mob use things like guns or AI cards?
+ var/hand_form = "hands" // Used in IsHumanoidToolUser. 'Your X are not fit-'.
var/list/hud_gears // Slots to show on the hud (typically none)
var/ui_icons // Icon file path to use for the HUD, otherwise generic icons are used
var/r_hand_sprite // If they have hands,
@@ -139,6 +141,7 @@
var/astar_adjacent_proc = /turf/proc/CardinalTurfsWithAccess // Proc to use when A* pathfinding. Default makes them bound to cardinals.
//Damage resistances
+ var/shock_resistance = 0 // Siemens modifier, directly subtracted from 1. Value of 0.4 means 0.6 siemens on shocks.
var/resistance = 0 // Damage reduction for all types
var/list/armor = list( // Values for normal getarmor() checks
"melee" = 0,
@@ -812,6 +815,9 @@
return verb
/mob/living/simple_animal/put_in_hands(var/obj/item/W) // No hands.
+ if(has_hands)
+ put_in_active_hand(W)
+ return 1
W.forceMove(get_turf(src))
return 1
@@ -1292,7 +1298,7 @@
// This is the actual act of 'punching'. Override for special behaviour.
/mob/living/simple_animal/proc/DoPunch(var/atom/A)
- if(!Adjacent(A)) // They could've moved in the meantime.
+ if(!Adjacent(A) && !istype(A, /obj/structure/window) && !istype(A, /obj/machinery/door/window)) // They could've moved in the meantime. But a Window probably wouldn't have. This allows player simple-mobs to attack windows.
return FALSE
var/damage_to_do = rand(melee_damage_lower, melee_damage_upper)
@@ -1527,7 +1533,7 @@
//Touches a wire, etc
/mob/living/simple_animal/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null)
- shock_damage *= siemens_coeff
+ shock_damage *= max(siemens_coeff - shock_resistance, 0)
if (shock_damage < 1)
return 0
@@ -1593,11 +1599,11 @@
hud_used.l_hand_hud_object.icon_state = "l_hand_inactive"
hud_used.r_hand_hud_object.icon_state = "r_hand_active"
return
-
+/*
/mob/living/simple_animal/put_in_active_hand(var/obj/item/I)
if(!has_hands || !istype(I))
return
-
+*/
//Puts the item into our active hand if possible. returns 1 on success.
/mob/living/simple_animal/put_in_active_hand(var/obj/item/W)
if(!has_hands)
@@ -1706,6 +1712,23 @@
/mob/living/simple_animal/IsAdvancedToolUser()
return has_hands
+/mob/living/simple_animal/proc/IsHumanoidToolUser(var/atom/tool)
+ if(!humanoid_hands)
+ var/display_name = null
+ if(tool)
+ display_name = tool
+ else
+ display_name = "object"
+ to_chat(src, "Your [hand_form] are not fit for use of \the [display_name].")
+ return humanoid_hands
+
+/mob/living/simple_animal/drop_from_inventory(var/obj/item/W, var/atom/target = null)
+ . = ..(W, target)
+ if(!target)
+ target = src.loc
+ if(.)
+ W.forceMove(src.loc)
+
//Commands, reactions, etc
/mob/living/simple_animal/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
..()
diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm
index 776ad523d51..6cbef8586be 100644
--- a/code/modules/multiz/movement.dm
+++ b/code/modules/multiz/movement.dm
@@ -86,6 +86,9 @@
/mob/living/carbon/human/can_overcome_gravity()
return species && species.can_overcome_gravity(src)
+/mob/living/simple_animal/construct/can_overcome_gravity()
+ return 1 //They care not for standard physics.
+
/mob/observer/zMove(direction)
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
if(destination)
@@ -116,6 +119,9 @@
/mob/observer/can_ztravel()
return 1
+/mob/living/simple_animal/construct/can_ztravel()
+ return 1
+
/mob/living/carbon/human/can_ztravel()
if(incapacitated())
return 0
@@ -263,6 +269,9 @@
/mob/living/simple_animal/hostile/carp/can_fall() // So can carp apparently.
return FALSE
+/mob/living/simple_animal/construct/can_fall() //As do Constructs.
+ return FALSE
+
// Check if this atom prevents things standing on it from falling. Return TRUE to allow the fall.
/obj/proc/CanFallThru(atom/movable/mover as mob|obj, turf/target as turf)
if(!isturf(mover.loc)) // VORESTATION EDIT. We clearly didn't have enough backup checks.
diff --git a/code/modules/projectiles/effects.dm b/code/modules/projectiles/effects.dm
index 18010d3d173..da21427effa 100644
--- a/code/modules/projectiles/effects.dm
+++ b/code/modules/projectiles/effects.dm
@@ -267,4 +267,25 @@
icon_state = "impact_darkb"
light_range = 2
light_power = 0.5
- light_color = "#8837A3"
\ No newline at end of file
+ light_color = "#8837A3"
+
+//----------------------------
+// Inversion / Cult
+//----------------------------
+/obj/effect/projectile/inversion/tracer
+ icon_state = "invert"
+ light_range = 2
+ light_power = -2
+ light_color = "#FFFFFF"
+
+/obj/effect/projectile/inversion/muzzle
+ icon_state = "muzzle_invert"
+ light_range = 2
+ light_power = -2
+ light_color = "#FFFFFF"
+
+/obj/effect/projectile/inversion/impact
+ icon_state = "impact_invert"
+ light_range = 2
+ light_power = -2
+ light_color = "#FFFFFF"
\ No newline at end of file
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 55eefade828..ffe7f898dad 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -172,6 +172,10 @@
return 0
if(!user.IsAdvancedToolUser())
return 0
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ if(!S.IsHumanoidToolUser(src))
+ return 0
var/mob/living/M = user
if(dna_lock && attached_lock.stored_dna)
diff --git a/code/modules/spells/aoe_turf/conjure/construct.dm b/code/modules/spells/aoe_turf/conjure/construct.dm
index 3885e16ad8a..82bba0198a0 100644
--- a/code/modules/spells/aoe_turf/conjure/construct.dm
+++ b/code/modules/spells/aoe_turf/conjure/construct.dm
@@ -1,5 +1,9 @@
//////////////////////////////Construct Spells/////////////////////////
+/*
+ * This has been moved to game/gamemodes/cult/construct_spells.dm. This is here for posterity.
+ */
+/*
/spell/aoe_turf/conjure/construct
name = "Artificer"
desc = "This spell conjures a construct which may be controlled by Shades"
@@ -110,6 +114,44 @@
continue
return
+/spell/aoe_turf/conjure/door
+ name = "Stone Door"
+ desc = "This spell conjures a massive stone door."
+
+ charge_max = 100
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ hud_state = "const_rune"
+
+/spell/aoe_turf/conjure/door/cast(list/targets)
+ ..()
+ var/turf/spawn_place = pick(targets)
+ if(spawn_place)
+ new /obj/structure/simple_door/cult(spawn_place)
+ return
+
+/spell/aoe_turf/conjure/grille
+ name = "Arcane Grille"
+ desc = "This spell conjures an airtight grille."
+
+ charge_max = 100
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ hud_state = "const_rune"
+
+/spell/aoe_turf/conjure/grille/cast(list/targets)
+ ..()
+ var/turf/spawn_place = pick(targets)
+ if(spawn_place)
+ new /obj/structure/grille/cult(spawn_place)
+ return
+
/spell/aoe_turf/conjure/forcewall/lesser
name = "Shield"
desc = "Allows you to pull up a shield to protect yourself and allies from incoming threats"
@@ -135,3 +177,4 @@
/obj/effect/forcefield/cult/cultify()
return
+*/
\ No newline at end of file
diff --git a/code/modules/spells/aoe_turf/knock.dm b/code/modules/spells/aoe_turf/knock.dm
index 3b3cae83d6c..6766a42eb91 100644
--- a/code/modules/spells/aoe_turf/knock.dm
+++ b/code/modules/spells/aoe_turf/knock.dm
@@ -22,7 +22,7 @@
door.open()
return
-
+/* Moved to game/gamemodes/cult/construct_spells.dm.
//Construct version
/spell/aoe_turf/knock/harvester
name = "Disintegrate Doors"
@@ -41,4 +41,5 @@
for(var/turf/T in targets)
for(var/obj/machinery/door/door in T.contents)
spawn door.cultify()
- return
\ No newline at end of file
+ return
+*/
\ No newline at end of file
diff --git a/code/modules/spells/construct_spells.dm b/code/modules/spells/construct_spells.dm
index b2cec77dbc1..aac7592f33a 100644
--- a/code/modules/spells/construct_spells.dm
+++ b/code/modules/spells/construct_spells.dm
@@ -1,5 +1,8 @@
//////////////////////////////Construct Spells/////////////////////////
-
+/*
+ * Moved to game/gamemodes/cult/construct_spells.dm. Here for posterity.
+ */
+/*
proc/findNullRod(var/atom/target)
if(istype(target,/obj/item/weapon/nullrod))
return 1
@@ -8,3 +11,4 @@ proc/findNullRod(var/atom/target)
if(findNullRod(A))
return 1
return 0
+*/
\ No newline at end of file
diff --git a/code/modules/spells/targeted/harvest.dm b/code/modules/spells/targeted/harvest.dm
index 6903efbfd9b..7a8d986a02a 100644
--- a/code/modules/spells/targeted/harvest.dm
+++ b/code/modules/spells/targeted/harvest.dm
@@ -1,3 +1,10 @@
+
+/*
+ * Here for posterity. Moved to game/gamemodes/cult/construct_spells.dm, though it has been disabled there.
+ */
+
+
+/*
/spell/targeted/harvest
name = "Harvest"
desc = "Back to where I come from, and you're coming with me."
@@ -34,3 +41,4 @@
user << "You warp back to Nar-Sie[prey ? " along with your prey":""]."
else
user << "...something's wrong!"//There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world.
+*/
\ No newline at end of file
diff --git a/code/modules/spells/targeted/shift.dm b/code/modules/spells/targeted/shift.dm
index 8f8ef57e512..f6634640dcf 100644
--- a/code/modules/spells/targeted/shift.dm
+++ b/code/modules/spells/targeted/shift.dm
@@ -1,3 +1,8 @@
+/*
+ * This has been moved to game/gamemodes/cult/construct_spells.dm. This is here for posterity.
+ */
+
+/*
/spell/targeted/ethereal_jaunt/shift
name = "Phase Shift"
desc = "This spell allows you to pass through walls"
@@ -21,4 +26,5 @@
flick("phase_shift2",animation)
/spell/targeted/ethereal_jaunt/shift/jaunt_steam(var/mobloc)
- return
\ No newline at end of file
+ return
+*/
\ No newline at end of file
diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm
index 395ac2f2191..24e599cc314 100644
--- a/code/modules/tables/tables.dm
+++ b/code/modules/tables/tables.dm
@@ -176,6 +176,21 @@ var/list/table_icon_cache = list()
visible_message("\The [user] tears apart \the [src]!")
src.break_to_parts()
+/obj/structure/table/attack_generic(mob/user as mob, var/damage)
+ if(damage >= 10)
+ if(reinforced && prob(70))
+ visible_message("\The [user] smashes against \the [src]!")
+ take_damage(damage/2)
+ user.do_attack_animation(src)
+ ..()
+ else
+ visible_message("\The [user] tears apart \the [src]!")
+ src.break_to_parts()
+ user.do_attack_animation(src)
+ return 1
+ visible_message("\The [user] scratches at \the [src]!")
+ return ..()
+
/obj/structure/table/MouseDrop_T(obj/item/stack/material/what)
if(can_reinforce && isliving(usr) && (!usr.stat) && istype(what) && usr.get_active_hand() == what && Adjacent(usr))
reinforce_table(what, usr)
diff --git a/config/example/motd.txt b/config/example/motd.txt
index ac75c145cfc..20b67d09c94 100644
--- a/config/example/motd.txt
+++ b/config/example/motd.txt
@@ -1,5 +1,5 @@