diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 9e55ae1ff00..6a14e6fae66 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -17,6 +17,8 @@
#define STATUS_EFFECT_VOID_PRICE /datum/status_effect/void_price //The price of healing yourself with void energy. Deals 3 brute damage every 3 seconds for 30 seconds.
#define STATUS_EFFECT_EXERCISED /datum/status_effect/exercised //Prevents heart disease
+#define STATUS_EFFECT_HIPPOCRATIC_OATH /datum/status_effect/hippocraticOath //Gives you an aura of healing as well as regrowing the Rod of Asclepius if lost
+
#define STATUS_EFFECT_REGENERATIVE_CORE /datum/status_effect/regenerative_core
//#define STATUS_EFFECT_VANGUARD /datum/status_effect/vanguard_shield //Grants temporary stun absorption, but will stun the user based on how many stuns they absorbed.
diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm
new file mode 100644
index 00000000000..472caea4490
--- /dev/null
+++ b/code/__HELPERS/traits.dm
@@ -0,0 +1,67 @@
+// trait accessor defines
+#define ADD_TRAIT(target, trait, source) \
+ do { \
+ var/list/_L; \
+ if (!target.status_traits) { \
+ target.status_traits = list(); \
+ _L = target.status_traits; \
+ _L[trait] = list(source); \
+ } else { \
+ _L = target.status_traits; \
+ if (_L[trait]) { \
+ _L[trait] |= list(source); \
+ } else { \
+ _L[trait] = list(source); \
+ } \
+ } \
+ } while (0)
+#define REMOVE_TRAIT(target, trait, sources) \
+ do { \
+ var/list/_L = target.status_traits; \
+ var/list/_S; \
+ if (sources && !islist(sources)) { \
+ _S = list(sources); \
+ } else { \
+ _S = sources\
+ }; \
+ if (_L && _L[trait]) { \
+ for (var/_T in _L[trait]) { \
+ if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \
+ _L[trait] -= _T \
+ } \
+ };\
+ if (!length(_L[trait])) { \
+ _L -= trait \
+ }; \
+ if (!length(_L)) { \
+ target.status_traits = null \
+ }; \
+ } \
+ } while (0)
+#define REMOVE_TRAITS_NOT_IN(target, sources) \
+ do { \
+ var/list/_L = target.status_traits; \
+ var/list/_S = sources; \
+ if (_L) { \
+ for (var/_T in _L) { \
+ _L[_T] &= _S;\
+ if (!length(_L[_T])) { \
+ _L -= _T } \
+ };\
+ if (!length(_L)) { \
+ target.status_traits = null\
+ };\
+ }\
+ } while (0)
+#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
+#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
+
+/*
+Remember to update _globalvars/traits.dm if you're adding/removing/renaming traits.
+*/
+
+//mob traits
+#define TRAIT_PACIFISM "pacifism"
+
+// common trait sources
+#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention
\ No newline at end of file
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
new file mode 100644
index 00000000000..d545a2aac1a
--- /dev/null
+++ b/code/_globalvars/traits.dm
@@ -0,0 +1,19 @@
+/*
+ FUN ZONE OF ADMIN LISTINGS
+ Try to keep this in sync with __DEFINES/traits.dm
+ quirks have it's own panel so we don't need them here.
+*/
+GLOBAL_LIST_INIT(traits_by_type, list(
+ /mob = list(
+ "TRAIT_PACIFISM" = TRAIT_PACIFISM
+ )))
+
+/// value -> trait name, generated on use from trait_by_type global
+GLOBAL_LIST(trait_name_map)
+
+/proc/generate_trait_name_map()
+ . = list()
+ for(var/key in GLOB.traits_by_type)
+ for(var/tname in GLOB.traits_by_type[key])
+ var/val = GLOB.traits_by_type[key][tname]
+ .[val] = tname
\ No newline at end of file
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index bed8c64b054..47b5f2ba337 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -62,6 +62,10 @@
else
return 1
+ if(force && HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to harm other living beings!")
+ return
+
if(!force)
playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), 1, -1)
else
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index 9b11e1c23d1..6e716f057c4 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -2,6 +2,8 @@
var/gc_destroyed //Time when this object was destroyed.
var/list/active_timers //for SStimer
var/list/datum_components //for /datum/components
+ /// Status traits attached to this datum
+ var/list/status_traits
var/list/comp_lookup
var/list/signal_procs
var/signal_enabled = FALSE
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 138c670290a..a41e58fa28c 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -55,6 +55,7 @@
.["Mark Object"] = "?_src_=vars;mark_object=[UID()]"
.["Jump to Object"] = "?_src_=vars;jump_to=[UID()]"
.["Delete"] = "?_src_=vars;delete=[UID()]"
+ .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]"
. += "---"
/client/vv_get_dropdown()
@@ -63,6 +64,7 @@
.["Call Proc"] = "?_src_=vars;proc_call=[UID()]"
.["Mark Object"] = "?_src_=vars;mark_object=[UID()]"
.["Delete"] = "?_src_=vars;delete=[UID()]"
+ .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]"
. += "---"
/client/proc/debug_variables(datum/D in world)
@@ -1219,25 +1221,25 @@
switch(Text)
if("brute")
if(ishuman(L))
- var/mob/living/carbon/human/H = L
+ var/mob/living/carbon/human/H = L
H.adjustBruteLoss(amount, robotic = TRUE)
else
L.adjustBruteLoss(amount)
- if("fire")
+ if("fire")
if(ishuman(L))
- var/mob/living/carbon/human/H = L
+ var/mob/living/carbon/human/H = L
H.adjustFireLoss(amount, robotic = TRUE)
else
L.adjustFireLoss(amount)
- if("toxin")
+ if("toxin")
L.adjustToxLoss(amount)
if("oxygen")
L.adjustOxyLoss(amount)
- if("brain")
+ if("brain")
L.adjustBrainLoss(amount)
- if("clone")
+ if("clone")
L.adjustCloneLoss(amount)
- if("stamina")
+ if("stamina")
L.adjustStaminaLoss(amount)
else
to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
@@ -1248,6 +1250,14 @@
message_admins("[key_name_admin(usr)] dealt [amount] amount of [Text] damage to [L]")
href_list["datumrefresh"] = href_list["mobToDamage"]
+ else if(href_list["traitmod"])
+ if(!check_rights(NONE))
+ return
+ var/datum/A = locateUID(href_list["traitmod"])
+ if(!istype(A))
+ return
+ holder.modify_traits(A)
+
if(href_list["datumrefresh"])
var/datum/DAT = locateUID(href_list["datumrefresh"])
if(!istype(DAT, /datum) && !isclient(DAT))
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 26f2da6a78f..c4ba55552fa 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -99,6 +99,109 @@
. = ..()
STOP_PROCESSING(SSprocessing, src)
+//Hippocratic Oath: Applied when the Rod of Asclepius is activated.
+/datum/status_effect/hippocraticOath
+ id = "Hippocratic Oath"
+ status_type = STATUS_EFFECT_UNIQUE
+ duration = -1
+ tick_interval = 25
+ examine_text = "They seem to have an aura of healing and helpfulness about them."
+ alert_type = null
+ var/hand
+ var/deathTick = 0
+
+/datum/status_effect/hippocraticOath/on_apply()
+ //Makes the user passive, it's in their oath not to harm!
+ ADD_TRAIT(owner, TRAIT_PACIFISM, "hippocraticOath")
+ var/datum/atom_hud/H = huds[DATA_HUD_MEDICAL_ADVANCED]
+ H.add_hud_to(owner)
+ return ..()
+
+/datum/status_effect/hippocraticOath/on_remove()
+ REMOVE_TRAIT(owner, TRAIT_PACIFISM, "hippocraticOath")
+ var/datum/atom_hud/H = huds[DATA_HUD_MEDICAL_ADVANCED]
+ H.remove_hud_from(owner)
+
+/datum/status_effect/hippocraticOath/tick()
+ if(owner.stat == DEAD)
+ if(deathTick < 4)
+ deathTick += 1
+ else
+ owner.visible_message("[owner]'s soul is absorbed into the rod, relieving the previous snake of its duty.")
+ var/mob/living/simple_animal/hostile/retaliate/poison/snake/healSnake = new(owner.loc)
+ var/list/chems = list("bicaridine", "perfluorodecalin", "kelotane")
+ healSnake.poison_type = pick(chems)
+ healSnake.name = "Asclepius's Snake"
+ healSnake.real_name = "Asclepius's Snake"
+ healSnake.desc = "A mystical snake previously trapped upon the Rod of Asclepius, now freed of its burden. Unlike the average snake, its bites contain chemicals with minor healing properties."
+ new /obj/effect/decal/cleanable/ash(owner.loc)
+ new /obj/item/rod_of_asclepius(owner.loc)
+ qdel(owner)
+ else
+ if(ishuman(owner))
+ var/mob/living/carbon/human/itemUser = owner
+ var/obj/item/heldItem = (hand == 1 ? itemUser.l_hand : itemUser.r_hand)
+ if(!heldItem || !istype(heldItem, /obj/item/rod_of_asclepius)) //Checks to make sure the rod is still in their hand
+ var/obj/item/rod_of_asclepius/newRod = new(itemUser.loc)
+ newRod.activated()
+ if(hand)
+ itemUser.drop_l_hand()
+ if(itemUser.put_in_l_hand(newRod))
+ to_chat(itemUser, "The Rod of Asclepius suddenly grows back out of your arm!")
+ else
+ if(!itemUser.has_organ("l_arm"))
+ new /obj/item/organ/external/arm(itemUser)
+ new /obj/item/organ/external/hand(itemUser)
+ itemUser.update_body()
+ itemUser.put_in_l_hand(newRod)
+ to_chat(itemUser, "Your arm suddenly grows back with the Rod of Asclepius still attached!")
+ else
+ itemUser.drop_r_hand()
+ if(itemUser.put_in_r_hand(newRod))
+ to_chat(itemUser, "The Rod of Asclepius suddenly grows back out of your arm!")
+ else
+ if(!itemUser.has_organ("r_arm"))
+ new /obj/item/organ/external/arm/right(itemUser)
+ new /obj/item/organ/external/hand/right(itemUser)
+ itemUser.update_body()
+ itemUser.put_in_r_hand(newRod)
+ to_chat(itemUser, "Your arm suddenly grows back with the Rod of Asclepius still attached!")
+
+ //Because a servant of medicines stops at nothing to help others, lets keep them on their toes and give them an additional boost.
+ if(itemUser.health < itemUser.maxHealth)
+ new /obj/effect/temp_visual/heal(get_turf(itemUser), "#375637")
+ itemUser.adjustBruteLoss(-1.5)
+ itemUser.adjustFireLoss(-1.5)
+ itemUser.adjustToxLoss(-1.5)
+ itemUser.adjustOxyLoss(-1.5)
+ itemUser.adjustStaminaLoss(-1.5)
+ itemUser.adjustBrainLoss(-1.5)
+ itemUser.adjustCloneLoss(-0.5) //Becasue apparently clone damage is the bastion of all health
+ //Heal all those around you, unbiased
+ for(var/mob/living/L in view(7, owner))
+ if(L.health < L.maxHealth)
+ new /obj/effect/temp_visual/heal(get_turf(L), "#375637")
+ if(iscarbon(L))
+ L.adjustBruteLoss(-3.5)
+ L.adjustFireLoss(-3.5)
+ L.adjustToxLoss(-3.5)
+ L.adjustOxyLoss(-3.5)
+ L.adjustStaminaLoss(-3.5)
+ L.adjustBrainLoss(-3.5)
+ L.adjustCloneLoss(-1) //Becasue apparently clone damage is the bastion of all health
+ if(ishuman(L))
+ var/var/mob/living/carbon/human/H = L
+ for(var/obj/item/organ/external/E in H.bodyparts)
+ if(prob(10))
+ E.mend_fracture()
+ E.internal_bleeding = FALSE
+ else if(issilicon(L))
+ L.adjustBruteLoss(-3.5)
+ L.adjustFireLoss(-3.5)
+ else if(isanimal(L))
+ var/mob/living/simple_animal/SM = L
+ SM.adjustHealth(-3.5)
+
/obj/screen/alert/status_effect/regenerative_core
name = "Reinforcing Tendrils"
desc = "You can move faster than your broken body could normally handle!"
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 6ff1c35e6db..cf7f931ff32 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -17,6 +17,7 @@
var/moved_recently = 0
var/mob/pulledby = null
var/atom/movable/pulling
+ var/throwforce = 0
var/canmove = 1
var/inertia_dir = 0
diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm
index e7aa4d45578..be0424b4ede 100644
--- a/code/game/mecha/equipment/mecha_equipment.dm
+++ b/code/game/mecha/equipment/mecha_equipment.dm
@@ -15,6 +15,7 @@
var/range = MECHA_MELEE //bitflags
var/salvageable = 1
var/selectable = 1 // Set to 0 for passive equipment such as mining scanner or armor plates
+ var/harmful = FALSE //Controls if equipment can be used to attack by a pacifist.
/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page()
diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm
index f61a1383309..bb299146dca 100644
--- a/code/game/mecha/equipment/tools/mining_tools.dm
+++ b/code/game/mecha/equipment/tools/mining_tools.dm
@@ -11,6 +11,7 @@
equip_cooldown = 15
energy_drain = 10
force = 15
+ harmful = TRUE
sharp = TRUE
var/drill_delay = 7
var/drill_level = DRILL_BASIC
diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm
index 8a155660b8c..63eeb21e165 100644
--- a/code/game/mecha/equipment/tools/work_tools.dm
+++ b/code/game/mecha/equipment/tools/work_tools.dm
@@ -8,6 +8,7 @@
energy_drain = 10
var/dam_force = 20
var/obj/mecha/working/ripley/cargo_holder
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/can_attach(obj/mecha/working/ripley/M)
if(..())
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index a69847ccb56..e4bc9526e4f 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -77,6 +77,7 @@
energy_drain = 30
projectile = /obj/item/projectile/beam
fire_sound = 'sound/weapons/laser.ogg'
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/disabler
name = "CH-PD Disabler"
@@ -84,6 +85,7 @@
projectile = /obj/item/projectile/beam/disabler
projectiles_per_shot = 2
projectile_delay = 1
+ harmful = FALSE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy
equip_cooldown = 10
@@ -112,6 +114,7 @@
energy_drain = 500
projectile = /obj/item/projectile/energy/shock_revolver
fire_sound = 'sound/magic/lightningbolt.ogg'
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/xray
equip_cooldown = 35
@@ -122,6 +125,7 @@
energy_drain = 80
projectile = /obj/item/projectile/beam/xray
fire_sound = 'sound/weapons/laser3.ogg'
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/xray/triple
name = "X-XR Triple-barrel X-Ray Stream Projector"
@@ -137,6 +141,7 @@
energy_drain = 80
projectile = /obj/item/projectile/beam/immolator
fire_sound = 'sound/weapons/laser3.ogg'
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse
equip_cooldown = 30
@@ -146,6 +151,7 @@
origin_tech = "materials=3;combat=6;powerstorage=4"
projectile = /obj/item/projectile/beam/pulse/heavy
fire_sound = 'sound/weapons/marauder.ogg'
+ harmful = TRUE
/obj/item/projectile/beam/pulse/heavy
@@ -280,6 +286,7 @@
fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg'
projectiles = 24
projectile_energy_cost = 15
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/carbine/silenced
name = "\improper S.H.H. \"Quietus\" Carbine"
@@ -307,6 +314,7 @@
projectile_energy_cost = 25
projectiles_per_shot = 4
variance = 25
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg
name = "Ultra AC 2"
@@ -320,6 +328,7 @@
projectiles_per_shot = 3
variance = 6
projectile_delay = 2
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg/dual
name = "XMG-9 Autocannon"
@@ -337,6 +346,7 @@
var/missile_speed = 2
var/missile_range = 30
var/heavy_missile = 0
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/action(target, params)
if(!action_checks(target))
@@ -429,6 +439,7 @@
missile_speed = 1.5
projectile_energy_cost = 100
equip_cooldown = 20
+ harmful = FALSE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/banana_mortar/can_attach(obj/mecha/combat/honker/M as obj)
if(..())
@@ -458,6 +469,7 @@
missile_speed = 1.5
projectile_energy_cost = 100
equip_cooldown = 10
+ harmful = FALSE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/mousetrap_mortar/can_attach(obj/mecha/combat/honker/M as obj)
if(..())
@@ -489,6 +501,7 @@
missile_range = 30
projectile_energy_cost = 50
equip_cooldown = 10
+ harmful = FALSE
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/bola/can_attach(obj/mecha/combat/gygax/M as obj)
if(..())
@@ -520,6 +533,7 @@
origin_tech = "materials=3;plasmatech=4;engineering=3"
projectile = /obj/item/projectile/plasma/adv/mech
fire_sound = 'sound/weapons/laser.ogg'
+ harmful = TRUE
/obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma/can_attach(obj/mecha/M)
if(istype(M, /obj/mecha/working))
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 6e86bbed7de..aed3216332c 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -210,10 +210,17 @@
if(!target)
return
+ var/mob/living/L = user
if(!target.Adjacent(src))
if(selected && selected.is_ranged())
+ if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful)
+ to_chat(L, "You don't want to harm other living beings!")
+ return
selected.action(target, params)
else if(selected && selected.is_melee())
+ if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to harm other living beings!")
+ return
selected.action(target, params)
else
if(internal_damage & MECHA_INT_CONTROL_LOST)
diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm
index 224d3bc2727..f6c6c0ea150 100644
--- a/code/game/objects/items/weapons/tools.dm
+++ b/code/game/objects/items/weapons/tools.dm
@@ -163,6 +163,9 @@
return ..()
if(user.zone_selected != "eyes" && user.zone_selected != "head")
return ..()
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to harm [M]!")
+ return
if((CLUMSY in user.mutations) && prob(50))
M = user
return eyestab(M,user)
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index cf025ba4746..9323412e830 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -48,13 +48,7 @@
/obj/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
..()
- var/tforce = 0
- if(ismob(AM))
- tforce = 10
- else if(istype(AM, /obj))
- var/obj/O = AM
- tforce = O.throwforce
- take_damage(tforce, BRUTE, "melee", 1, get_dir(src, AM))
+ take_damage(AM.throwforce, BRUTE, "melee", 1, get_dir(src, AM))
/obj/ex_act(severity)
if(resistance_flags & INDESTRUCTIBLE)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 0fcfedc07a8..c1afed03b96 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -3,7 +3,6 @@
var/origin_tech = null //Used by R&D to determine what research bonuses it grants.
var/crit_fail = 0
animate_movement = 2
- var/throwforce = 1
var/list/attack_verb = list() //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
var/list/species_exception = null // list() of species types, if a species cannot put items in a certain slot, but species type is in list, it will be able to wear that item
var/sharp = 0 // whether this object cuts
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 2d8c44c2a48..c4c0f9b7965 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -181,9 +181,11 @@
step(O, get_dir(O, src))
return
-/obj/structure/table/proc/tablepush(obj/item/I, mob/user)
+/obj/structure/table/proc/tablepush(obj/item/grab/G, mob/user)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "Throwing [G.affecting] onto the table might hurt them!")
+ return
if(get_dist(src, user) < 2)
- var/obj/item/grab/G = I
if(G.affecting.buckled)
to_chat(user, "[G.affecting] is buckled to [G.affecting.buckled]!")
return FALSE
@@ -202,9 +204,9 @@
G.affecting.visible_message("[G.assailant] pushes [G.affecting] onto [src].", \
"[G.assailant] pushes [G.affecting] onto [src].")
add_attack_logs(G.assailant, G.affecting, "Pushed onto a table")
- qdel(I)
+ qdel(G)
return TRUE
- qdel(I)
+ qdel(G)
/obj/structure/table/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/grab))
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index a638bae3503..caf7fdf2663 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1111,3 +1111,47 @@ Traitors and the like can also be revived with the previous role mostly intact.
dat += "[S.name] - Announce | Remove
"
dat += "
Add New Goal"
usr << browse(dat, "window=goals;size=400x400")
+
+/// Allow admin to add or remove traits of datum
+/datum/admins/proc/modify_traits(datum/D)
+ if(!D)
+ return
+
+ var/add_or_remove = input("Remove/Add?", "Trait Remove/Add") as null|anything in list("Add","Remove")
+ if(!add_or_remove)
+ return
+ var/list/availible_traits = list()
+
+ switch(add_or_remove)
+ if("Add")
+ for(var/key in GLOB.traits_by_type)
+ if(istype(D,key))
+ availible_traits += GLOB.traits_by_type[key]
+ if("Remove")
+ if(!GLOB.trait_name_map)
+ GLOB.trait_name_map = generate_trait_name_map()
+ for(var/trait in D.status_traits)
+ var/name = GLOB.trait_name_map[trait] || trait
+ availible_traits[name] = trait
+
+ var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in availible_traits
+ if(!chosen_trait)
+ return
+ chosen_trait = availible_traits[chosen_trait]
+
+ var/source = "adminabuse"
+ switch(add_or_remove)
+ if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it.
+ ADD_TRAIT(D, chosen_trait, source)
+ if("Remove")
+ var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific")
+ if(!specific)
+ return
+ switch(specific)
+ if("All")
+ source = null
+ if("Specific")
+ source = input("Source to be removed","Trait Remove/Add") as null|anything in D.status_traits[chosen_trait]
+ if(!source)
+ return
+ REMOVE_TRAIT(D, chosen_trait, source)
\ No newline at end of file
diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
index 2bcc8812ef7..c2e703d3978 100644
--- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
@@ -50,6 +50,10 @@
if(user.a_intent != INTENT_HARM || !isGlass)
return ..()
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to harm [target]!")
+ return
+
force = 15 //Smashing bottles over someoen's head hurts.
var/obj/item/organ/external/affecting = user.zone_selected //Find what the player is aiming at
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index ae436214b03..ffe7a062bbe 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -17,7 +17,7 @@
if(!add_loot)
return
- var/loot = rand(1, 26)
+ var/loot = rand(1, 27)
switch(loot)
if(1)
new /obj/item/shared_storage/red(src)
@@ -41,44 +41,46 @@
else
new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
if(9)
- new /obj/item/organ/internal/heart/cursed/wizard(src)
+ new /obj/item/rod_of_asclepius(src)
if(10)
- new /obj/item/ship_in_a_bottle(src)
+ new /obj/item/organ/internal/heart/cursed/wizard(src)
if(11)
- new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/berserker(src)
+ new /obj/item/ship_in_a_bottle(src)
if(12)
- new /obj/item/jacobs_ladder(src)
+ new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/berserker(src)
if(13)
- new /obj/item/nullrod/scythe/talking(src)
+ new /obj/item/jacobs_ladder(src)
if(14)
- new /obj/item/nullrod/armblade(src)
+ new /obj/item/nullrod/scythe/talking(src)
if(15)
- new /obj/item/guardiancreator(src)
+ new /obj/item/nullrod/armblade(src)
if(16)
+ new /obj/item/guardiancreator(src)
+ if(17)
if(prob(50))
new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src)
else
new /obj/item/disk/design_disk/modkit_disc/bounty(src)
- if(17)
- new /obj/item/warp_cube/red(src)
if(18)
- new /obj/item/wisp_lantern(src)
+ new /obj/item/warp_cube/red(src)
if(19)
- new /obj/item/immortality_talisman(src)
+ new /obj/item/wisp_lantern(src)
if(20)
- new /obj/item/gun/magic/hook(src)
+ new /obj/item/immortality_talisman(src)
if(21)
- new /obj/item/voodoo(src)
+ new /obj/item/gun/magic/hook(src)
if(22)
- new /obj/item/grenade/clusterbuster/inferno(src)
+ new /obj/item/voodoo(src)
if(23)
+ new /obj/item/grenade/clusterbuster/inferno(src)
+ if(24)
new /obj/item/reagent_containers/food/drinks/bottle/holywater/hell(src)
new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor(src)
- if(24)
- new /obj/item/spellbook/oneuse/summonitem(src)
if(25)
- new /obj/item/book_of_babel(src)
+ new /obj/item/spellbook/oneuse/summonitem(src)
if(26)
+ new /obj/item/book_of_babel(src)
+ if(27)
new /obj/item/borg/upgrade/modkit/lifesteal(src)
new /obj/item/bedsheet/cult(src)
@@ -159,3 +161,61 @@
reagents_list = list("blood" = 40)
build_path = /obj/item/borg/upgrade/modkit/bounty
category = list("Mining", "Cyborg Upgrade Modules")
+
+//Spooky special loot
+
+//Rod of Asclepius
+/obj/item/rod_of_asclepius
+ name = "\improper Rod of Asclepius"
+ desc = "A wooden rod about the size of your forearm with a snake carved around it, winding its way up the sides of the rod. Something about it seems to inspire in you the responsibilty and duty to help others."
+ icon = 'icons/obj/lavaland/artefacts.dmi'
+ icon_state = "asclepius_dormant"
+ var/activated = FALSE
+ var/usedHand
+
+/obj/item/rod_of_asclepius/attack_self(mob/user)
+ if(activated)
+ return
+ if(!iscarbon(user))
+ to_chat(user, "The snake carving seems to come alive, if only for a moment, before returning to its dormant state, almost as if it finds you incapable of holding its oath.")
+ return
+ var/mob/living/carbon/itemUser = user
+ if(itemUser.l_hand == src)
+ usedHand = 1
+ if(itemUser.r_hand == src)
+ usedHand = 0
+ if(itemUser.has_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH))
+ to_chat(user, "You can't possibly handle the responsibility of more than one rod!")
+ return
+ var/failText = "The snake seems unsatisfied with your incomplete oath and returns to its previous place on the rod, returning to its dormant, wooden state. You must stand still while completing your oath!"
+ to_chat(itemUser, "The wooden snake that was carved into the rod seems to suddenly come alive and begins to slither down your arm! The compulsion to help others grows abnormally strong...")
+ if(do_after(itemUser, 40, target = itemUser))
+ itemUser.say("I swear to fulfill, to the best of my ability and judgment, this covenant:")
+ else
+ to_chat(itemUser, failText)
+ return
+ if(do_after(itemUser, 20, target = itemUser))
+ itemUser.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.")
+ else
+ to_chat(itemUser, failText)
+ return
+ if(do_after(itemUser, 30, target = itemUser))
+ itemUser.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.")
+ else
+ to_chat(itemUser, failText)
+ return
+ if(do_after(itemUser, 30, target = itemUser))
+ itemUser.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.")
+ else
+ to_chat(itemUser, failText)
+ return
+ to_chat(itemUser, "The snake, satisfied with your oath, attaches itself and the rod to your forearm with an inseparable grip. Your thoughts seem to only revolve around the core idea of helping others, and harm is nothing more than a distant, wicked memory...")
+ var/datum/status_effect/hippocraticOath/effect = itemUser.apply_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH)
+ effect.hand = usedHand
+ activated()
+
+/obj/item/rod_of_asclepius/proc/activated()
+ flags = NODROP | DROPDEL
+ desc = "A short wooden rod with a mystical snake inseparably gripping itself and the rod to your forearm. It flows with a healing energy that disperses amongst yourself and those around you. "
+ icon_state = "asclepius_active"
+ activated = TRUE
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm
index cef087f343c..e7f783acd72 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm
@@ -1,5 +1,8 @@
/mob/living/carbon/alien/humanoid/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to hurt [src]!")
+ return FALSE
..(user, TRUE)
adjustBruteLoss(15)
var/hitverb = "punched"
diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
index 919eecee8d8..79c8ed8207f 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
@@ -19,6 +19,9 @@
/mob/living/carbon/alien/larva/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to hurt [src]!")
+ return FALSE
..(user, TRUE)
adjustBruteLoss(5 + rand(1, 9))
spawn(0)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 7bc9ffd4a0d..fd60d5d49bd 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -582,6 +582,9 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
qdel(G) //We delete the grab.
if(throwable_mob)
thrown_thing = throwable_mob
+ if(HAS_TRAIT(src, TRAIT_PACIFISM))
+ to_chat(src, "You gently let go of [throwable_mob].")
+ return
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
var/turf/end_T = get_turf(target)
throwable_mob.forceMove(start_T)
@@ -595,6 +598,10 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
thrown_thing = I
unEquip(I)
+ if(HAS_TRAIT(src, TRAIT_PACIFISM) && I.throwforce)
+ to_chat(src, "You set [I] down gently on the ground.")
+ return
+
if(thrown_thing)
visible_message("[src] has thrown [thrown_thing].")
newtonian_move(get_dir(target, src))
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 1f78983560d..433a6adcc8f 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -510,6 +510,9 @@ emp_act
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to hurt [src]!")
+ return FALSE
var/hulk_verb = pick("smash", "pummel")
if(check_shields(user, 15, "the [hulk_verb]ing"))
return
@@ -616,6 +619,9 @@ emp_act
/mob/living/carbon/human/mech_melee_attack(obj/mecha/M)
if(M.occupant.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(M.occupant, TRAIT_PACIFISM))
+ to_chat(M.occupant, "You don't want to harm other living beings!")
+ return
M.do_attack_animation(src)
if(M.damtype == "brute")
step_away(src,M,15)
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 84c65f7d356..c6c8b75518a 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -393,6 +393,9 @@
return TRUE
/datum/species/proc/harm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to harm [target]!")
+ return FALSE
//Vampire code
if(user.mind && user.mind.vampire && (user.mind in SSticker.mode.vampires) && !user.mind.vampire.draining && user.zone_selected == "head" && target != user)
if((NO_BLOOD in target.dna.species.species_traits) || target.dna.species.exotic_blood || !target.blood_volume)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 44e2761708e..67ea6e7f887 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -124,6 +124,9 @@
/mob/living/mech_melee_attack(obj/mecha/M)
if(M.occupant.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(M.occupant, TRAIT_PACIFISM))
+ to_chat(M.occupant, "You don't want to harm other living beings!")
+ return
M.do_attack_animation(src)
if(M.damtype == "brute")
step_away(src,M,15)
@@ -288,6 +291,10 @@
M.Feedstop()
return // can't attack while eating!
+ if(HAS_TRAIT(src, TRAIT_PACIFISM))
+ to_chat(M, "You don't want to hurt anyone!")
+ return FALSE
+
if(stat != DEAD)
add_attack_logs(src, M, "Slime'd")
M.do_attack_animation(src)
@@ -299,6 +306,10 @@
if((M.a_intent == INTENT_HELP && M.ckey) || M.melee_damage_upper == 0)
M.custom_emote(1, "[M.friendly] [src].")
return FALSE
+ if(HAS_TRAIT(M, TRAIT_PACIFISM))
+ to_chat(M, "You don't want to hurt anyone!")
+ return FALSE
+
if(M.attack_sound)
playsound(loc, M.attack_sound, 50, 1, 1)
M.do_attack_animation(src)
@@ -314,6 +325,10 @@
return 0
else
+ if(HAS_TRAIT(L, TRAIT_PACIFISM))
+ to_chat(L, "You don't want to hurt anyone!")
+ return
+
L.do_attack_animation(src)
if(prob(90))
add_attack_logs(L, src, "Larva attacked")
@@ -335,6 +350,9 @@
grabbedby(M)
return FALSE
if(INTENT_HARM)
+ if(HAS_TRAIT(M, TRAIT_PACIFISM))
+ to_chat(M, "You don't want to hurt anyone!")
+ return FALSE
M.do_attack_animation(src)
return TRUE
if(INTENT_DISARM)
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index d88faff76f6..e720400abd5 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -44,6 +44,9 @@
/mob/living/silicon/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to hurt [src]!")
+ return FALSE
..(user, TRUE)
adjustBruteLoss(rand(10, 15))
playsound(loc, "punch", 25, 1, -1)
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index d827bf03692..6e94accb100 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -18,6 +18,9 @@
grabbedby(M)
if(INTENT_HARM, INTENT_DISARM)
+ if(HAS_TRAIT(M, TRAIT_PACIFISM))
+ to_chat(M, "You don't want to hurt [src]!")
+ return
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
visible_message("[M] [response_harm] [src]!", "[M] [response_harm] you!")
playsound(loc, attacked_sound, 25, 1, -1)
@@ -28,6 +31,9 @@
/mob/living/simple_animal/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to hurt [src]!")
+ return FALSE
..(user, TRUE)
playsound(loc, "punch", 25, 1, -1)
visible_message("[user] has punched [src]!", "[user] has punched [src]!")
diff --git a/code/modules/mob/living/simple_animal/friendly/snake.dm b/code/modules/mob/living/simple_animal/friendly/snake.dm
new file mode 100644
index 00000000000..1cd85c1b622
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/snake.dm
@@ -0,0 +1,61 @@
+/mob/living/simple_animal/hostile/retaliate/poison
+ var/poison_per_bite = 0
+ var/poison_type = "toxin"
+
+/mob/living/simple_animal/hostile/retaliate/poison/AttackingTarget()
+ . = ..()
+ if(. && isliving(target))
+ var/mob/living/L = target
+ if(L.reagents && !poison_per_bite == 0)
+ L.reagents.add_reagent(poison_type, poison_per_bite)
+ return .
+
+/mob/living/simple_animal/hostile/retaliate/poison/snake
+ name = "snake"
+ desc = "A slithery snake. These legless reptiles are the bane of mice and adventurers alike."
+ icon_state = "snake"
+ icon_living = "snake"
+ icon_dead = "snake_dead"
+ speak_emote = list("hisses")
+ health = 20
+ maxHealth = 20
+ attacktext = "bites"
+ melee_damage_lower = 5
+ melee_damage_upper = 6
+ response_help = "pets"
+ response_disarm = "shoos"
+ response_harm = "steps on"
+ faction = list("hostile")
+ ventcrawler = VENTCRAWLER_ALWAYS
+ density = FALSE
+ pass_flags = PASSTABLE | PASSMOB
+ mob_size = MOB_SIZE_SMALL
+ gold_core_spawnable = FRIENDLY_SPAWN
+ obj_damage = 0
+ environment_smash = ENVIRONMENT_SMASH_NONE
+
+
+/mob/living/simple_animal/hostile/retaliate/poison/snake/ListTargets(atom/the_target)
+ . = oview(vision_range, targets_from) //get list of things in vision range
+ var/list/living_mobs = list()
+ var/list/mice = list()
+ for(var/HM in .)
+ //Yum a tasty mouse
+ if(istype(HM, /mob/living/simple_animal/mouse))
+ mice += HM
+ if(isliving(HM))
+ living_mobs += HM
+
+ // if no tasty mice to chase, lets chase any living mob enemies in our vision range
+ if(length(mice) == 0)
+ //Filter living mobs (in range mobs) by those we consider enemies (retaliate behaviour)
+ return living_mobs & enemies
+ return mice
+
+/mob/living/simple_animal/hostile/retaliate/poison/snake/AttackingTarget()
+ if(istype(target, /mob/living/simple_animal/mouse))
+ visible_message("[name] consumes [target] in a single gulp!", "You consume [target] in a single gulp!")
+ QDEL_NULL(target)
+ adjustHealth(-2)
+ else
+ return ..()
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm
index 2884e17f5ce..79d6c42cb11 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm
@@ -42,45 +42,4 @@
if(prob(15) && iscarbon(target))
var/mob/living/carbon/C = target
C.Weaken(3)
- C.visible_message("\the [src] knocks down \the [C]!")
-
-//*******//
-// Snake //
-//*******//
-
-/mob/living/simple_animal/hostile/snake
- name = "snake"
- desc = "A sinuously coiled, venomous looking reptile."
- icon_state = "snake"
- icon_living = "snake"
- icon_dead = "snake_dead"
- icon_gib = "snake_dead"
- speak_chance = 0
- turns_per_move = 1
- butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 2)
- response_help = "pets the"
- response_disarm = "gently pushes aside the"
- response_harm = "hits the"
- stop_automated_movement_when_pulled = 0
- maxHealth = 25
- health = 25
-
- emote_taunt = list("hisses wickedly")
- taunt_chance = 20
-
- harm_intent_damage = 2
- melee_damage_lower = 3
- melee_damage_upper = 10
- attacktext = "bites"
- attack_sound = 'sound/weapons/bite.ogg'
-
- layer = 3.1 //so they can stay hidde under the /obj/structure/bush
- var/stalk_tick_delay = 3
- gold_core_spawnable = HOSTILE_SPAWN
-
-/mob/living/simple_animal/hostile/snake/AttackingTarget()
- . =..()
- if(.)
- if(iscarbon(target))
- var/mob/living/carbon/C = target
- C.apply_damage(rand(3, 12), TOX)
+ C.visible_message("\the [src] knocks down \the [C]!")
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index e8cec4d3e22..f42bc2b6b74 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -299,6 +299,9 @@
/mob/living/simple_animal/slime/attack_hulk(mob/living/carbon/human/user)
if(user.a_intent == INTENT_HARM)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to hurt [src]!")
+ return FALSE
discipline_slime(user)
return ..()
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 09084236551..4091b8df9d7 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -3,6 +3,7 @@
layer = MOB_LAYER
animate_movement = 2
pressure_resistance = 8
+ throwforce = 10
dont_save = TRUE //to avoid it messing up in buildmode saving
var/datum/mind/mind
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 53e6b467165..329a01907e2 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -139,8 +139,9 @@
hud.icon_state = "!reinforce"
if(state >= GRAB_AGGRESSIVE)
- affecting.drop_r_hand()
- affecting.drop_l_hand()
+ if(!HAS_TRAIT(assailant, TRAIT_PACIFISM))
+ affecting.drop_r_hand()
+ affecting.drop_l_hand()
//var/announce = 0
@@ -235,6 +236,9 @@
/obj/item/grab/proc/s_click(obj/screen/S)
if(!affecting)
return
+ if(state >= GRAB_AGGRESSIVE && HAS_TRAIT(assailant, TRAIT_PACIFISM))
+ to_chat(assailant, "You don't want to risk hurting [affecting]!")
+ return
if(state == GRAB_UPGRADING)
return
if(assailant.next_move > world.time)
diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm
index 0269dd76199..726580573e3 100644
--- a/code/modules/projectiles/ammunition.dm
+++ b/code/modules/projectiles/ammunition.dm
@@ -17,6 +17,7 @@
var/delay = 0 //Delay for energy weapons
var/randomspread = 0 //Randomspread for automatics
var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown.
+ var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal
/obj/item/ammo_casing/New()
..()
diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm
index 354359fee29..92ecf815104 100644
--- a/code/modules/projectiles/ammunition/ammo_casings.dm
+++ b/code/modules/projectiles/ammunition/ammo_casings.dm
@@ -325,6 +325,7 @@
icon = 'icons/obj/guns/toy.dmi'
icon_state = "foamdart"
var/modified = 0
+ harmful = FALSE
/obj/item/ammo_casing/caseless/foam_dart/update_icon()
..()
@@ -348,6 +349,7 @@
else if((istype(A, /obj/item/pen)) && modified && !FD.pen)
if(!user.unEquip(A))
return
+ harmful = TRUE
A.loc = FD
FD.log_override = FALSE
FD.pen = A
diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm
index 74cd356f18e..e0eb509aa9d 100644
--- a/code/modules/projectiles/ammunition/energy.dm
+++ b/code/modules/projectiles/ammunition/energy.dm
@@ -25,6 +25,7 @@
/obj/item/ammo_casing/energy/laser/practice
projectile_type = /obj/item/projectile/beam/practice
select_name = "practice"
+ harmful = FALSE
/obj/item/ammo_casing/energy/laser/scatter
projectile_type = /obj/item/projectile/beam/scatter
@@ -52,10 +53,12 @@
/obj/item/ammo_casing/energy/laser/bluetag
projectile_type = /obj/item/projectile/beam/lasertag/bluetag
select_name = "bluetag"
+ harmful = FALSE
/obj/item/ammo_casing/energy/laser/redtag
projectile_type = /obj/item/projectile/beam/lasertag/redtag
select_name = "redtag"
+ harmful = FALSE
/obj/item/ammo_casing/energy/xray
projectile_type = /obj/item/projectile/beam/xray
@@ -85,6 +88,7 @@
fire_sound = 'sound/weapons/taser.ogg'
e_cost = 200
delay = 15
+ harmful = FALSE
/obj/item/ammo_casing/energy/electrode/gun
fire_sound = 'sound/weapons/gunshots/gunshot.ogg'
@@ -110,6 +114,7 @@
/obj/item/ammo_casing/energy/flora
fire_sound = 'sound/effects/stealthoff.ogg'
+ harmful = FALSE
/obj/item/ammo_casing/energy/flora/yield
projectile_type = /obj/item/projectile/energy/florayield
@@ -140,6 +145,7 @@
select_name = "disable"
e_cost = 50
fire_sound = 'sound/weapons/taser2.ogg'
+ harmful = FALSE
/obj/item/ammo_casing/energy/disabler/cyborg //seperate balancing for cyborg, again
e_cost = 250
@@ -162,6 +168,7 @@
fire_sound = 'sound/weapons/pulse3.ogg'
var/obj/item/gun/energy/wormhole_projector/gun = null
select_name = "blue"
+ harmful = FALSE
/obj/item/ammo_casing/energy/wormhole/New(var/obj/item/gun/energy/wormhole_projector/wh)
gun = wh
diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special.dm
index 101c8310b8a..5fa53566fe5 100644
--- a/code/modules/projectiles/ammunition/special.dm
+++ b/code/modules/projectiles/ammunition/special.dm
@@ -11,15 +11,18 @@
/obj/item/ammo_casing/magic/heal
projectile_type = /obj/item/projectile/magic/resurrection
+ harmful = FALSE
/obj/item/ammo_casing/magic/death
projectile_type = /obj/item/projectile/magic/death
/obj/item/ammo_casing/magic/teleport
projectile_type = /obj/item/projectile/magic/teleport
+ harmful = FALSE
/obj/item/ammo_casing/magic/door
projectile_type = /obj/item/projectile/magic/door
+ harmful = FALSE
/obj/item/ammo_casing/magic/fireball
projectile_type = /obj/item/projectile/magic/fireball
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index ec011bad472..4df4e46da20 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -207,6 +207,10 @@
var/randomized_bonus_spread = rand(0, bonus_spread)
if(burst_size > 1)
+ if(chambered && chambered.harmful)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal.
+ to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...")
+ return
firing_burst = 1
for(var/i = 1 to burst_size)
if(!user)
@@ -236,6 +240,10 @@
firing_burst = 0
else
if(chambered)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal.
+ if(chambered.harmful) // Is the bullet chambered harmful?
+ to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...")
+ return
sprd = round((pick(1,-1)) * (randomized_gun_spread + randomized_bonus_spread))
if(!chambered.fire(target, user, params, , suppressed, zone_override, sprd))
shoot_with_empty_chamber(user)
diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm
index b310ce6d884..de3f4c02a87 100644
--- a/code/modules/projectiles/guns/projectile/sniper.dm
+++ b/code/modules/projectiles/guns/projectile/sniper.dm
@@ -102,6 +102,7 @@
caliber = ".50"
projectile_type = /obj/item/projectile/bullet/sniper/soporific
icon_state = ".50"
+ harmful = FALSE
/obj/item/projectile/bullet/sniper/soporific
armour_penetration = 0
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index b594743baef..e8f7d16cc37 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -649,6 +649,8 @@ var/const/INGEST = 2
if(data)
R.data = data
+ if(isliving(my_atom))
+ R.on_mob_add(my_atom) //Must occur befor it could posibly run on_mob_delete
update_total()
if(my_atom)
my_atom.on_reagent_change()
diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm
index 195a6efec90..05967be04ca 100644
--- a/code/modules/reagents/chemistry/reagents.dm
+++ b/code/modules/reagents/chemistry/reagents.dm
@@ -81,6 +81,10 @@
/datum/reagent/proc/on_mob_death(mob/living/M) //use this to have chems have a "death-triggered" effect
return
+// Called when this reagent is first added to a mob
+/datum/reagent/proc/on_mob_add(mob/living/L)
+ return
+
// Called when this reagent is removed while inside a mob
/datum/reagent/proc/on_mob_delete(mob/living/M)
return
diff --git a/code/modules/reagents/chemistry/reagents/admin.dm b/code/modules/reagents/chemistry/reagents/admin.dm
index a7a8098aa49..6f22078b278 100644
--- a/code/modules/reagents/chemistry/reagents/admin.dm
+++ b/code/modules/reagents/chemistry/reagents/admin.dm
@@ -38,6 +38,7 @@
M.SetParalysis(0, FALSE)
M.SetSilence(0, FALSE)
M.SetHallucinate(0)
+ REMOVE_TRAITS_NOT_IN(M, list(ROUNDSTART_TRAIT))
M.SetDizzy(0)
M.SetDrowsy(0)
M.SetStuttering(0)
diff --git a/code/modules/reagents/chemistry/reagents/misc.dm b/code/modules/reagents/chemistry/reagents/misc.dm
index 359af633383..cc39fb6c18d 100644
--- a/code/modules/reagents/chemistry/reagents/misc.dm
+++ b/code/modules/reagents/chemistry/reagents/misc.dm
@@ -404,11 +404,13 @@
process_flags = ORGANIC | SYNTHETIC // That's the power of love~
taste_description = "love"
-/datum/reagent/love/on_mob_life(mob/living/M)
- if(M.a_intent != INTENT_HELP)
- M.a_intent_change(INTENT_HELP)
- M.can_change_intents = 0 //Now you have no choice but to be helpful.
+/datum/reagent/love/on_mob_add(mob/living/L)
+ ..()
+ if(L.a_intent != INTENT_HELP)
+ L.a_intent_change(INTENT_HELP)
+ L.can_change_intents = FALSE //Now you have no choice but to be helpful.
+/datum/reagent/love/on_mob_life(mob/living/M)
if(prob(8))
var/lovely_phrase = pick("appreciated", "loved", "pretty good", "really nice", "pretty happy with yourself, even though things haven't always gone as well as they could")
to_chat(M, "You feel [lovely_phrase].")
@@ -425,7 +427,7 @@
return ..()
/datum/reagent/love/on_mob_delete(mob/living/M)
- M.can_change_intents = 1
+ M.can_change_intents = TRUE
..()
/datum/reagent/love/reaction_mob(mob/living/M, method=TOUCH, volume)
@@ -543,6 +545,22 @@
M.update_transform()
..()
+/datum/reagent/pax
+ name = "Pax"
+ id = "pax"
+ description = "A colorless liquid that suppresses violence in its subjects."
+ color = "#AAAAAA55"
+ taste_description = "water"
+ metabolization_rate = 0.25 * REAGENTS_METABOLISM
+
+/datum/reagent/pax/on_mob_add(mob/living/M)
+ ..()
+ ADD_TRAIT(M, TRAIT_PACIFISM, id)
+
+/datum/reagent/pax/on_mob_delete(mob/living/M)
+ REMOVE_TRAIT(M, TRAIT_PACIFISM, id)
+ ..()
+
/datum/reagent/toxin/coffeepowder
name = "Coffee Grounds"
id = "coffeepowder"
diff --git a/code/modules/spacepods/equipment.dm b/code/modules/spacepods/equipment.dm
index c52eca0b342..8ca4d1be65c 100644
--- a/code/modules/spacepods/equipment.dm
+++ b/code/modules/spacepods/equipment.dm
@@ -1,4 +1,7 @@
/obj/item/spacepod_equipment/weaponry/proc/fire_weapons()
+ if(HAS_TRAIT(usr, TRAIT_PACIFISM) && harmful)
+ to_chat(usr, "You don't want to harm other living beings!")
+ return
if(my_atom.next_firetime > world.time)
to_chat(usr, "Your weapons are recharging.")
return
@@ -83,6 +86,7 @@
var/shots_per = 1
var/fire_sound
var/fire_delay = 15
+ var/harmful = TRUE
/obj/item/spacepod_equipment/weaponry/taser
name = "disabler system"
@@ -91,6 +95,7 @@
projectile_type = /obj/item/projectile/beam/disabler
shot_cost = 400
fire_sound = 'sound/weapons/taser.ogg'
+ harmful = FALSE
/obj/item/spacepod_equipment/weaponry/burst_taser
name = "burst taser system"
@@ -101,6 +106,7 @@
shots_per = 3
fire_sound = 'sound/weapons/taser.ogg'
fire_delay = 30
+ harmful = FALSE
/obj/item/spacepod_equipment/weaponry/laser
name = "laser system"
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index 1ead85e872d..c0c259af7ac 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/icons/obj/lavaland/artefacts.dmi b/icons/obj/lavaland/artefacts.dmi
index bc95bbc1fe3..03c0369f629 100644
Binary files a/icons/obj/lavaland/artefacts.dmi and b/icons/obj/lavaland/artefacts.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 4d1a6500e33..abd9b1372a3 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -89,6 +89,7 @@
#include "code\__HELPERS\sanitize_values.dm"
#include "code\__HELPERS\text.dm"
#include "code\__HELPERS\time.dm"
+#include "code\__HELPERS\traits.dm"
#include "code\__HELPERS\type2type.dm"
#include "code\__HELPERS\typelists.dm"
#include "code\__HELPERS\unique_ids.dm"
@@ -106,6 +107,7 @@
#include "code\_globalvars\logging.dm"
#include "code\_globalvars\mapping.dm"
#include "code\_globalvars\misc.dm"
+#include "code\_globalvars\traits.dm"
#include "code\_globalvars\unused.dm"
#include "code\_globalvars\lists\flavor_misc.dm"
#include "code\_globalvars\lists\fortunes.dm"
@@ -1935,6 +1937,7 @@
#include "code\modules\mob\living\simple_animal\friendly\penguin.dm"
#include "code\modules\mob\living\simple_animal\friendly\pet.dm"
#include "code\modules\mob\living\simple_animal\friendly\sloth.dm"
+#include "code\modules\mob\living\simple_animal\friendly\snake.dm"
#include "code\modules\mob\living\simple_animal\friendly\spiderbot.dm"
#include "code\modules\mob\living\simple_animal\hostile\alien.dm"
#include "code\modules\mob\living\simple_animal\hostile\bat.dm"