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"