diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 2298976d955..d666a93f0d6 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -429,6 +429,10 @@ #define COMSIG_PROJECTILE_RANGE_OUT "projectile_range_out" ///from the base of /obj/projectile/process(): () #define COMSIG_PROJECTILE_BEFORE_MOVE "projectile_before_move" +///sent to firer at the end of /mob/living/apply_projectile_effects(): (mob/living/target, hit_limb, blocked) +#define COMSIG_PROJECTILE_POST_HIT_LIVING "projectile_post_hit_living" +///sent to projectile at the end of /mob/living/apply_projectile_effects(): (mob/living/target, hit_limb, blocked) +#define COMSIG_PROJECTILE_SELF_POST_HIT_LIVING "projectile_post_hit_living" // FROM [/obj/item/proc/set_embed] sent when an item's embedding properties are changed : () #define COMSIG_ITEM_EMBEDDING_UPDATE "item_embedding_update" @@ -634,3 +638,7 @@ /// Sent from /obj/item/mob_holder/purple_raptor/proc/toggle_wings() : (mob/living/carbon/human/user) #define COMSIG_RAPTOR_WINGS_CLOSED "raptor_wings_closed" + +/// Sent from /obj/effect/rune/convert/try_sacrifice_item(obj/effect/rune/convert/rune) +#define COMSIG_ITEM_CULT_SACRIFICE "item_cult_sacrifice" + #define COMPONENT_SACRIFICE_SUCCESSFUL (1<<0) diff --git a/code/datums/components/cult_kill_tracker.dm b/code/datums/components/cult_kill_tracker.dm new file mode 100644 index 00000000000..00023ab23e3 --- /dev/null +++ b/code/datums/components/cult_kill_tracker.dm @@ -0,0 +1,85 @@ +/// Component to handle the behavior of a nullrod keeping track of cultists it has crit or killed, and converting the item into a cult weapon when sacrificed +/datum/component/cult_kill_tracker + /// Lazylist, tracks weakrefs()s to all cultists which have been crit or killed by this nullrod. + var/list/cultists_slain + /// Ref to the last mob hit with this rod. + var/last_ref + /// The stat of the target being hit with this rod, before actually performing damage calculations. + var/last_stat = DEAD + +/datum/component/cult_kill_tracker/Initialize(...) + if(!istype(parent, /obj/item)) + return COMPONENT_INCOMPATIBLE + +/datum/component/cult_kill_tracker/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(on_attack_zone)) + RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(post_hit)) + RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent, COMSIG_ITEM_CULT_SACRIFICE, PROC_REF(on_sacrificed)) + if(isgun(parent)) + RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, PROC_REF(on_projectile_hit)) + RegisterSignal(parent, COMSIG_PROJECTILE_POST_HIT_LIVING, PROC_REF(post_hit)) + +/datum/component/cult_kill_tracker/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, list(COMSIG_ITEM_ATTACK_ZONE, COMSIG_ITEM_AFTERATTACK, COMSIG_ATOM_EXAMINE, COMSIG_ITEM_CULT_SACRIFICE, COMSIG_PROJECTILE_ON_HIT, COMSIG_PROJECTILE_POST_HIT_LIVING)) + +/datum/component/cult_kill_tracker/proc/on_attack_zone(obj/item/source, mob/living/target, mob/living/user) + SIGNAL_HANDLER + if(!user.mind?.holy_role) + return + if(!IS_CULTIST(target) || istype(target, /mob/living/carbon/human/cult_ghost)) + return + last_ref = WEAKREF(target) + last_stat = target.stat + +/datum/component/cult_kill_tracker/proc/post_hit(source, mob/living/target) + SIGNAL_HANDLER + if(!last_ref) + return + var/mob/living/resolved_mob = locate(last_ref) + //If they got deleted during the processing of the attack, they're probably fucking dead. + if(!istype(resolved_mob) || (resolved_mob == target && resolved_mob.stat > last_stat)) + LAZYOR(cultists_slain, last_ref) + last_ref = null + last_stat = DEAD + +/datum/component/cult_kill_tracker/proc/on_examine(obj/item/source, mob/viewer, list/examine_list) + SIGNAL_HANDLER + if(!IS_CULTIST(viewer) || !GET_ATOM_BLOOD_DNA_LENGTH(source)) + return + + var/num_slain = LAZYLEN(cultists_slain) + examine_list += span_cult_italic("It has the blood of [num_slain] fallen cultist[num_slain == 1 ? "" : "s"] on it. \ + Offering it to Nar'sie will transform it into a [num_slain >= 3 ? "powerful" : "standard"] cult weapon.") + +/datum/component/cult_kill_tracker/proc/on_sacrificed(obj/item/source, obj/effect/rune/convert/rune) + SIGNAL_HANDLER + var/num_slain = LAZYLEN(cultists_slain) + var/displayed_message = "[source] glows an unholy red and begins to transform..." + if(num_slain && GET_ATOM_BLOOD_DNA_LENGTH(source)) + displayed_message += " The blood of [num_slain] fallen cultist[num_slain == 1 ? "":"s"] is absorbed into [source]!" + + source.visible_message(span_cult_italic(displayed_message)) + switch(num_slain) + if(0) + rune.animate_convert_item(source, /obj/item/melee/cultblade/dagger) + if(1) + rune.animate_convert_item(source, /obj/item/melee/cultblade) + else + rune.animate_convert_item(source, /obj/item/melee/cultblade/halberd) + return COMPONENT_SACRIFICE_SUCCESSFUL + +/datum/component/cult_kill_tracker/proc/on_projectile_hit(obj/projectile/source, atom/movable/firer, atom/target) + SIGNAL_HANDLER + if(!isliving(firer) || !isliving(target)) + return + var/mob/living/firer_mob = firer + var/mob/living/target_mob = target + if(!firer_mob.mind.holy_role) + return + if(!IS_CULTIST(target_mob) || istype(target_mob, /mob/living/carbon/human/cult_ghost)) + return + last_ref = WEAKREF(target_mob) + last_stat = target_mob.stat diff --git a/code/datums/elements/nullrod_core.dm b/code/datums/elements/nullrod_core.dm index 020874777d5..fa48e421f8e 100644 --- a/code/datums/elements/nullrod_core.dm +++ b/code/datums/elements/nullrod_core.dm @@ -18,6 +18,7 @@ on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed), target), \ effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \ ) + target.AddComponent(/datum/component/cult_kill_tracker) target.AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE) ADD_TRAIT(target, TRAIT_NULLROD_ITEM, ELEMENT_TRAIT(type)) diff --git a/code/game/objects/items/skateboards.dm b/code/game/objects/items/skateboards.dm index 0d860ef11f9..9f18d42ebe0 100644 --- a/code/game/objects/items/skateboards.dm +++ b/code/game/objects/items/skateboards.dm @@ -15,9 +15,9 @@ var/board_item_type = /obj/vehicle/ridden/scooter/skateboard /obj/item/melee/skateboard/attack_self(mob/user) - var/obj/vehicle/ridden/scooter/skateboard/S = new board_item_type(get_turf(user))//this probably has fucky interactions with telekinesis but for the record it wasn't my fault - S.buckle_mob(user) - qdel(src) + var/obj/vehicle/ridden/scooter/skateboard/board = new board_item_type(get_turf(user), src)//this probably has fucky interactions with telekinesis but for the record it wasn't my fault + board.buckle_mob(user) + forceMove(board) /obj/item/melee/skateboard/improvised name = "improvised skateboard" @@ -55,6 +55,7 @@ force = 18 throwforce = 6 w_class = WEIGHT_CLASS_NORMAL + obj_flags = parent_type::obj_flags | UNIQUE_RENAME attack_verb_continuous = list("bashes", "crashes", "grinds", "skates") attack_verb_simple = list("bash", "crash", "grind", "skate") board_item_type = /obj/vehicle/ridden/scooter/skateboard/hoverboard/holyboarded diff --git a/code/game/objects/items/weaponry/melee/energy.dm b/code/game/objects/items/weaponry/melee/energy.dm index 0c6ed38d674..ca7f75e93a9 100644 --- a/code/game/objects/items/weaponry/melee/energy.dm +++ b/code/game/objects/items/weaponry/melee/energy.dm @@ -556,6 +556,7 @@ armour_penetration = 0 wound_bonus = -10 demolition_mod = 1 + obj_flags = parent_type::obj_flags | UNIQUE_RENAME sword_color_icon = "blue" light_color = LIGHT_COLOR_LIGHT_CYAN active_force = 18 diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index fbadeba3518..be3a5b92d2d 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -242,7 +242,7 @@ structure_check() searches for nearby cultist structures required for the invoca if(!IS_CULTIST(non_cultist)) myriad_targets += non_cultist - if(!length(myriad_targets) && !try_spawn_sword()) + if(!length(myriad_targets) && !try_sacrifice_item()) fail_invoke() return @@ -400,57 +400,38 @@ structure_check() searches for nearby cultist structures required for the invoca sacrificial.investigate_log("has been sacrificially gibbed by the cult.", INVESTIGATE_DEATHS) sacrificial.gib(DROP_ALL_REMAINS) - try_spawn_sword() // after sharding and gibbing, which potentially dropped a null rod + try_sacrifice_item() // after sharding and gibbing, which potentially dropped a sacrificable item return TRUE -/// Tries to convert a null rod over the rune to a cult sword -/obj/effect/rune/convert/proc/try_spawn_sword() - for(var/obj/item/potential_rod in loc) - if(!HAS_TRAIT(potential_rod, TRAIT_NULLROD_ITEM)) +/// Tries to convert a valid item over the rune to something else +/obj/effect/rune/convert/proc/try_sacrifice_item() + for(var/obj/item/checked_item in loc) + if(checked_item.anchored || (checked_item.resistance_flags & INDESTRUCTIBLE)) continue - if(potential_rod.anchored || (potential_rod.resistance_flags & INDESTRUCTIBLE)) - continue - - var/num_slain = 0 - if (istype(potential_rod, /obj/item/nullrod)) - var/obj/item/nullrod/actual_rod = potential_rod - num_slain = LAZYLEN(actual_rod.cultists_slain) - - var/displayed_message = "[potential_rod] glows an unholy red and begins to transform..." - if(num_slain && GET_ATOM_BLOOD_DNA_LENGTH(potential_rod)) - displayed_message += " The blood of [num_slain] fallen cultist[num_slain == 1 ? "":"s"] is absorbed into [potential_rod]!" - - potential_rod.visible_message(span_cult_italic(displayed_message)) - switch(num_slain) - if(0) - animate_spawn_sword(potential_rod, /obj/item/melee/cultblade/dagger) - if(1) - animate_spawn_sword(potential_rod, /obj/item/melee/cultblade) - else - animate_spawn_sword(potential_rod, /obj/item/melee/cultblade/halberd) - return TRUE + if(SEND_SIGNAL(checked_item, COMSIG_ITEM_CULT_SACRIFICE, src) & COMPONENT_SACRIFICE_SUCCESSFUL) + return TRUE return FALSE -/// Does an animation of a null rod transforming into a cult sword -/obj/effect/rune/convert/proc/animate_spawn_sword(obj/item/former_rod, new_blade_typepath) +/// Does an animation of a sacrificable item transforming into something else +/obj/effect/rune/convert/proc/animate_convert_item(obj/item/old_item, new_movable_typepath) playsound(src, 'sound/effects/magic.ogg', 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.66) - former_rod.anchored = TRUE - former_rod.Shake() - animate(former_rod, alpha = 0, transform = matrix(former_rod.transform).Scale(0.01), time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) - QDEL_IN(former_rod, 2 SECONDS) + old_item.anchored = TRUE + old_item.Shake() + animate(old_item, alpha = 0, transform = matrix(old_item.transform).Scale(0.01), time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) + QDEL_IN(old_item, 2 SECONDS) - var/obj/item/new_blade = new new_blade_typepath(loc) - var/matrix/blade_matrix_on_spawn = matrix(new_blade.transform) - new_blade.name = "converted [new_blade.name]" - new_blade.anchored = TRUE - new_blade.alpha = 0 - new_blade.transform = matrix(new_blade.transform).Scale(0.01) - new_blade.Shake() - animate(new_blade, alpha = 255, transform = blade_matrix_on_spawn, time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) - addtimer(VARSET_CALLBACK(new_blade, anchored, FALSE), 2 SECONDS) + var/atom/movable/new_movable = new new_movable_typepath(loc) + var/matrix/matrix_on_spawn = matrix(new_movable.transform) + new_movable.name = "converted [new_movable.name]" + new_movable.anchored = TRUE + new_movable.alpha = 0 + new_movable.transform = matrix(new_movable.transform).Scale(0.01) + new_movable.Shake() + animate(new_movable, alpha = 255, transform = matrix_on_spawn, time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) + addtimer(VARSET_CALLBACK(new_movable, anchored, FALSE), 2 SECONDS) /obj/effect/rune/empower cultist_name = "Empower" diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm index becd993f5e8..5e67c8de35b 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm @@ -47,8 +47,6 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) var/chaplain_spawnable = TRUE /// Short description of what this item is capable of, for radial menu uses. var/menu_description = "A standard chaplain's weapon. Fits in pockets. Can be worn on the belt." - /// Lazylist, tracks refs()s to all cultists which have been crit or killed by this nullrod. - var/list/cultists_slain /// Affects GLOB.holy_weapon_type. Disable to allow null rods to change at will and without affecting the station's type. var/station_holy_item = TRUE @@ -72,26 +70,6 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) user.visible_message(span_suicide("[user] is killing [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to get closer to god!")) return (BRUTELOSS|FIRELOSS) -/obj/item/nullrod/attack(mob/living/target_mob, mob/living/user, list/modifiers, list/attack_modifiers) - if(!user.mind?.holy_role) - return ..() - if(!IS_CULTIST(target_mob) || istype(target_mob, /mob/living/carbon/human/cult_ghost)) - return ..() - - var/old_stat = target_mob.stat - . = ..() - if(old_stat < target_mob.stat) - LAZYOR(cultists_slain, REF(target_mob)) - return . - -/obj/item/nullrod/examine(mob/user) - . = ..() - if(!IS_CULTIST(user) || !GET_ATOM_BLOOD_DNA_LENGTH(src)) - return - - var/num_slain = LAZYLEN(cultists_slain) - . += span_cult_italic("It has the blood of [num_slain] fallen cultist[num_slain == 1 ? "" : "s"] on it. \ - Offering it to Nar'sie will transform it into a [num_slain >= 3 ? "powerful" : "standard"] cult weapon.") /obj/item/nullrod/non_station station_holy_item = FALSE @@ -613,6 +591,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' force = 15 + obj_flags = parent_type::obj_flags | UNIQUE_RENAME offspring_type = /obj/item/toy/plush/carpplushie divine = TRUE diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm index f2c7ed53d40..91823a073c8 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm @@ -45,6 +45,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and armour_penetration = 50 //Very good armor penetration to make up for our abysmal force reach = 2 //why yes, this does have reach slot_flags = null + obj_flags = UNIQUE_RENAME sharpness = SHARP_EDGED attack_verb_continuous = list("chops", "slices", "cuts", "reaps") attack_verb_simple = list("chop", "slice", "cut", "reap") diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 884fbe51d8e..68bcf1fee29 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -179,6 +179,10 @@ if (proj.damage && armor_check < 100) create_projectile_hit_effects(proj, def_zone, armor_check) + if(proj.fired_from) + SEND_SIGNAL(proj.fired_from, COMSIG_PROJECTILE_POST_HIT_LIVING, src, def_zone, armor_check) + SEND_SIGNAL(proj, COMSIG_PROJECTILE_SELF_POST_HIT_LIVING, src, def_zone, armor_check) + /mob/living/proc/create_projectile_hit_effects(obj/projectile/proj, def_zone, blocked) if (proj.damage_type != BRUTE) return diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm index 1e4ac046762..5d1cc61f92c 100644 --- a/code/modules/religion/burdened/psyker.dm +++ b/code/modules/religion/burdened/psyker.dm @@ -181,28 +181,10 @@ /obj/item/gun/ballistic/revolver/chaplain/Initialize(mapload) . = ..() - AddComponent(/datum/component/anti_magic, MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) - AddComponent(/datum/component/effect_remover, \ - success_feedback = "You disrupt the magic of %THEEFFECT with %THEWEAPON.", \ - success_forcesay = "BEGONE FOUL MAGIKS!!", \ - tip_text = "Clear rune", \ - on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed)), \ - effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \ - ) - AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25) + AddElement(/datum/element/nullrod_core, FALSE) name = pick(possible_names) desc = possible_names[name] -/obj/item/gun/ballistic/revolver/chaplain/proc/on_cult_rune_removed(obj/effect/target, mob/living/user) - SIGNAL_HANDLER - if(!istype(target, /obj/effect/rune)) - return - - var/obj/effect/rune/target_rune = target - if(target_rune.log_when_erased) - user.log_message("erased [target_rune.cultist_name] rune using [src]", LOG_GAME) - SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_NARNAR] = TRUE - /obj/item/gun/ballistic/revolver/chaplain/suicide_act(mob/living/user) . = ..() name = "Habemus Papam" diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm index 3e1b6cf86cb..16d084cf518 100644 --- a/code/modules/vehicles/scooter.dm +++ b/code/modules/vehicles/scooter.dm @@ -53,11 +53,17 @@ var/instability = 10 ///If true, riding the skateboard with walk intent on will prevent crashing. var/can_slow_down = TRUE + ///The actual item for the skateboard + var/obj/item/melee/skateboard/board_item -/obj/vehicle/ridden/scooter/skateboard/Initialize(mapload) +/obj/vehicle/ridden/scooter/skateboard/Initialize(mapload, obj/item/melee/skateboard/board_item) . = ..() sparks = new(src, 1, FALSE) sparks.attach(src) + if(!istype(board_item)) + src.board_item = new board_item_type(src) + else + src.board_item = board_item /obj/vehicle/ridden/scooter/skateboard/make_ridable() AddElement(/datum/element/ridable, /datum/component/riding/vehicle/scooter/skateboard) @@ -174,7 +180,7 @@ if(has_buckled_mobs()) to_chat(skater, span_warning("You can't lift this up when somebody's on it.")) return - skater.put_in_hands(new board_item_type(get_turf(skater))) + skater.put_in_hands(board_item) qdel(src) /obj/vehicle/ridden/scooter/skateboard/pro diff --git a/tgstation.dme b/tgstation.dme index fa648bd841c..39baa1f4c1a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1152,6 +1152,7 @@ #include "code\datums\components\crank_recharge.dm" #include "code\datums\components\crate_carrier.dm" #include "code\datums\components\cuff_n_stun.dm" +#include "code\datums\components\cult_kill_tracker.dm" #include "code\datums\components\cult_ritual_item.dm" #include "code\datums\components\curse_of_hunger.dm" #include "code\datums\components\curse_of_polymorph.dm"