From 38cc7f9ba5aa353324d996682baa4fb553a42855 Mon Sep 17 00:00:00 2001 From: Jacquerel Date: Wed, 21 Jan 2026 20:31:05 +0000 Subject: [PATCH] Makes killing yourself with wands more fun (#94876) --- code/datums/components/fantasy/suffixes.dm | 30 +-- code/game/objects/items.dm | 2 +- code/game/objects/structures.dm | 2 +- .../objects/structures/petrified_statue.dm | 2 +- code/modules/projectiles/gun.dm | 2 +- code/modules/projectiles/guns/magic.dm | 57 +++-- code/modules/projectiles/guns/magic/staff.dm | 163 +++++++++++++- code/modules/projectiles/guns/magic/wand.dm | 204 ++++++++++++------ .../projectiles/guns/special/hand_of_midas.dm | 50 +++-- code/modules/projectiles/projectile/magic.dm | 34 ++- 10 files changed, 408 insertions(+), 138 deletions(-) diff --git a/code/datums/components/fantasy/suffixes.dm b/code/datums/components/fantasy/suffixes.dm index 5a89a6125fe..2b25e442b23 100644 --- a/code/datums/components/fantasy/suffixes.dm +++ b/code/datums/components/fantasy/suffixes.dm @@ -148,20 +148,22 @@ /datum/fantasy_affix/shrapnel/apply(datum/component/fantasy/comp, newName) . = ..() // higher means more likely - var/list/weighted_projectile_types = list(/obj/projectile/meteor = 1, - /obj/projectile/energy/nuclear_particle = 1, - /obj/projectile/beam/pulse = 1, - /obj/projectile/bullet/honker = 15, - /obj/projectile/temp = 15, - /obj/projectile/ion = 15, - /obj/projectile/magic/door = 15, - /obj/projectile/magic/locker = 15, - /obj/projectile/magic/fetch = 15, - /obj/projectile/beam/emitter = 15, - /obj/projectile/magic/flying = 15, - /obj/projectile/bullet/incendiary/c9mm = 15, - /obj/projectile/temp/hot = 15, - /obj/projectile/beam/disabler = 15) + var/list/weighted_projectile_types = list( + /obj/projectile/beam/disabler = 15, + /obj/projectile/beam/emitter = 15, + /obj/projectile/bullet/honker = 15, + /obj/projectile/beam/pulse = 1, + /obj/projectile/bullet/incendiary/c9mm = 15, + /obj/projectile/energy/nuclear_particle = 1, + /obj/projectile/ion = 15, + /obj/projectile/magic/door = 15, + /obj/projectile/magic/fetch = 15, + /obj/projectile/magic/flying = 15, + /obj/projectile/magic/locker = 15, + /obj/projectile/meteor = 1, + /obj/projectile/temp = 15, + /obj/projectile/temp/hot = 15, + ) var/obj/projectile/picked_projectiletype = pick_weight(weighted_projectile_types) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 446dc45ab66..6c56a42b65b 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1896,7 +1896,7 @@ return null /obj/item/animate_atom_living(mob/living/owner) - new /mob/living/basic/mimic/copy(drop_location(), src, owner) + return new /mob/living/basic/mimic/copy(drop_location(), src, owner) /** * Used to update the weight class of the item in a way that other atoms can react to the change. diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 9d4442238ac..84ca7cc046c 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -68,7 +68,7 @@ . = ..() /obj/structure/animate_atom_living(mob/living/owner) - new /mob/living/basic/mimic/copy(drop_location(), src, owner) + return new /mob/living/basic/mimic/copy(drop_location(), src, owner) /// For when a mob comes flying through the window, smash it and damage the mob /obj/structure/proc/smash_and_injure(mob/living/flying_mob, atom/oldloc, direction) diff --git a/code/game/objects/structures/petrified_statue.dm b/code/game/objects/structures/petrified_statue.dm index 73f3ac942f1..e6327a1a811 100644 --- a/code/game/objects/structures/petrified_statue.dm +++ b/code/game/objects/structures/petrified_statue.dm @@ -105,7 +105,7 @@ petrified_mob.mind?.transfer_to(new_statue) to_chat(new_statue, span_userdanger("You are an animate statue. You cannot move when monitored, but are nearly invincible and deadly when unobserved! [owner ? "Do not harm [owner], your creator" : ""].")) forceMove(new_statue) - + return new_statue /mob/proc/petrify(statue_timer) return diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 277cadb5ab2..d998760b601 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -656,7 +656,7 @@ return TRUE /obj/item/gun/animate_atom_living(mob/living/owner) - new /mob/living/basic/mimic/copy/ranged(drop_location(), src, owner) + return new /mob/living/basic/mimic/copy/ranged(drop_location(), src, owner) /obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer) if(!ishuman(user) || !ishuman(target)) diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index 510d815d79d..b1dea590268 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -10,21 +10,27 @@ obj_flags = CONDUCTS_ELECTRICITY w_class = WEIGHT_CLASS_HUGE can_muzzle_flash = FALSE - ///what kind of magic is this - var/school = SCHOOL_EVOCATION - var/antimagic_flags = MAGIC_RESISTANCE - var/max_charges = 6 - var/charges = 0 - var/recharge_rate = 8 - var/charge_timer = 0 - /// Whether this wand/staff recharges on its own over time. - /// (This is not related to the spell "Charge" whatsoever!) - var/can_charge = TRUE - var/ammo_type - var/no_den_usage - clumsy_check = 0 + clumsy_check = FALSE trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses magic instead pin = /obj/item/firing_pin/magic + /// What kind of magic is this + var/school = SCHOOL_EVOCATION + /// What kind of antimagic resists this + var/antimagic_flags = MAGIC_RESISTANCE + /// How many charges can we hold at most + var/max_charges = 6 + /// How many charges do we currently have + var/charges = 0 + /// How fast do we recharge charges? In seconds + var/recharge_rate = 8 + /// How much have we currently recharged? + var/charge_timer = 0 + /// Whether this wand/staff recharges on its own over time. + var/self_charging = TRUE + /// What kind of projectile do we fire? + var/ammo_type + /// If set to TRUE, wizards can't use this until they leave home + var/no_den_usage = FALSE /obj/item/gun/magic/Initialize(mapload) . = ..() @@ -60,7 +66,7 @@ . = COMPONENT_ITEM_CHARGED // Non-self charging staves and wands can potentially expire - if(!can_charge && max_charges && prob(80)) + if(!self_charging && max_charges && prob(80)) max_charges-- if(max_charges <= 0) @@ -81,11 +87,11 @@ to_chat(user, span_warning("You know better than to violate the security of The Den, best wait until you leave to use [src].")) return else - no_den_usage = 0 + no_den_usage = FALSE // Well you're probably not going back if(!user.can_cast_magic(antimagic_flags)) add_fingerprint(user) return - . = ..() + return ..() /obj/item/gun/magic/can_shoot() return charges @@ -104,17 +110,15 @@ charges = max_charges if(ammo_type) chambered = new ammo_type(src) - if(can_charge) + if(self_charging) START_PROCESSING(SSobj, src) RegisterSignal(src, COMSIG_ITEM_RECHARGED, PROC_REF(instant_recharge)) - /obj/item/gun/magic/Destroy() - if(can_charge) + if(self_charging) STOP_PROCESSING(SSobj, src) return ..() - /obj/item/gun/magic/process(seconds_per_tick) if (charges >= max_charges) charge_timer = 0 @@ -128,15 +132,26 @@ recharge_newshot() return 1 - /obj/item/gun/magic/shoot_with_empty_chamber(mob/living/user as mob|obj) to_chat(user, span_warning("\The [src] whizzles quietly.")) /obj/item/gun/magic/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is twisting [src] above [user.p_their()] head, releasing a magical blast! It looks like [user.p_theyre()] trying to commit suicide!")) + if (can_user_shoot(user)) + charges-- + return do_suicide(user) + user.visible_message(span_suicide("...but nothing happens.")) + return SHAME + +/// Extend to do something funny +/obj/item/gun/magic/proc/do_suicide(mob/living/user) playsound(loc, fire_sound, 50, TRUE, -1) return FIRELOSS +/// Returns true if specified mob can fire this weapon +/obj/item/gun/magic/proc/can_user_shoot(mob/living/user) + return can_shoot() && user.can_cast_magic(antimagic_flags) + /obj/item/gun/magic/vv_edit_var(var_name, var_value) . = ..() switch(var_name) diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index b1d664a6271..2a912988c05 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -31,6 +31,7 @@ /obj/item/gun/magic/staff/proc/on_intruder_use(mob/living/user, atom/target) return TRUE +/// Turns mobs into other mobs /obj/item/gun/magic/staff/change name = "staff of change" desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." @@ -53,15 +54,27 @@ to_chat(user, span_hypnophrase("You don't feel strong enough to properly wield this staff!")) balloon_alert(user, "you feel weak holding this staff") -/obj/item/gun/magic/staff/change/on_intruder_use(mob/living/user, atom/target) +/// Transforms the user +/obj/item/gun/magic/staff/change/proc/transform_self(mob/living/user) user.dropItemToGround(src, TRUE) var/wabbajack_into = preset_wabbajack_type || pick(WABBAJACK_MONKEY, WABBAJACK_HUMAN, WABBAJACK_ANIMAL) var/mob/living/new_body = user.wabbajack(wabbajack_into, preset_wabbajack_changeflag) if(!new_body) return - balloon_alert(new_body, "wabbajack, wabbajack!") + return new_body +/obj/item/gun/magic/staff/change/on_intruder_use(mob/living/user, atom/target) + transform_self(user) + +/obj/item/gun/magic/staff/change/do_suicide(mob/living/user) + playsound(loc, fire_sound, 50, TRUE, -1) + var/mob/living/transformed = transform_self(user) + transformed.death() + transformed.set_suicide(TRUE) + return MANUAL_SUICIDE + +/// Brings objects to life /obj/item/gun/magic/staff/animate name = "staff of animation" desc = "An artefact that spits bolts of life-force which causes objects which are hit by it to animate and come to life! This magic doesn't affect machines." @@ -71,6 +84,36 @@ inhand_icon_state = "staffofanimation" school = SCHOOL_EVOCATION +/obj/item/gun/magic/staff/animate/do_suicide(mob/living/user) + user.Stun(20 SECONDS, ignore_canstun = TRUE) + var/list/my_shit = user.unequip_everything() + my_shit -= src + user.put_in_active_hand(src) // Keep this one for now + charges++ // We already subtracted one + user.Paralyze(2 SECONDS, ignore_canstun = TRUE) // We have at most 6 charges so this is slightly longer than necessary + while (length(my_shit) && charges > 0) + if(QDELETED(user) || user.stat == DEAD) + break + charges-- + playsound(loc, fire_sound, 50, TRUE, -1) + var/obj/item/my_thing = pop(my_shit) + user.dropItemToGround(my_thing) + var/mob/living/angry_thing = my_thing.animate_atom_living() + angry_thing.ai_controller?.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, user) + angry_thing.ai_controller?.set_blackboard_key(BB_TARGET_MINIMUM_STAT, HARD_CRIT) + angry_thing.ai_controller?.ai_interact(user, combat_mode = TRUE) + user.apply_damage(35, BRUTE, forced = TRUE) // Mimics are not actually very strong so we pretend that it just bit us so we die faster, at least 3 charges & worn items should do it + sleep(0.25 SECONDS) + + if (QDELETED(user)) + return MANUAL_SUICIDE + if (user.stat == CONSCIOUS) + return SHAME + if (user.stat != DEAD) + user.death() // If you got put into crit by the mobs we'll finish you off + return MANUAL_SUICIDE + +/// Heals people and even raises the dead /obj/item/gun/magic/staff/healing name = "staff of healing" desc = "An artefact that spits bolts of restoring magic which can remove ailments of all kinds and even raise the dead." @@ -115,9 +158,18 @@ healing_beam.LoseTarget() return ..() -/obj/item/gun/magic/staff/healing/handle_suicide() //Stops people trying to commit suicide to heal themselves - return +/obj/item/gun/magic/staff/healing/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer) + return //Stops people clicking on themselves for self-healing +/obj/item/gun/magic/staff/healing/do_suicide(mob/living/user) + playsound(loc, fire_sound, 50, TRUE, -1) + if(user.mob_biotypes & MOB_UNDEAD) + user.dust(drop_items = TRUE) + return MANUAL_SUICIDE + user.visible_message(span_suicide("...but nothing happens.")) + return SHAME + +/// Does random shit when fired /obj/item/gun/magic/staff/chaos name = "staff of chaos" desc = "An artefact that spits bolts of chaotic magic that can potentially do anything." @@ -127,7 +179,7 @@ inhand_icon_state = "staffofchaos" max_charges = 10 recharge_rate = 2 - no_den_usage = 1 + no_den_usage = TRUE school = SCHOOL_FORBIDDEN //this staff is evil. okay? it just is. look at this projectile type list. this is wrong. /// List of all projectiles we can fire from our staff. @@ -173,6 +225,18 @@ process_fire(user, user, FALSE) return FALSE +/obj/item/gun/magic/staff/chaos/do_suicide(mob/living/user) + charges++ // We already subtracted one + for (var/i in 1 to 5) + if (!charges || user.stat == DEAD || QDELETED(user)) + break + playsound(loc, fire_sound, 50, TRUE, -1) + process_fire(user, user, FALSE) + charges-- + sleep(0.25 SECONDS) + + return FIRELOSS + /** * Staff of chaos given to the wizard upon completing a cheesy grand ritual. Is completely evil and if something * breaks, it's completely intended. Fuck off. @@ -212,6 +276,7 @@ /obj/projectile/plasma, ) //if you ever try to expand this list, avoid adding bullets/energy projectiles, this ain't supposed to be a gun... unless it's funny +/// Creates and opens doors /obj/item/gun/magic/staff/door name = "staff of door creation" desc = "An artefact that spits bolts of transformative magic that can create doors in walls." @@ -221,9 +286,22 @@ inhand_icon_state = "staffofdoor" max_charges = 10 recharge_rate = 2 - no_den_usage = 1 + no_den_usage = TRUE school = SCHOOL_TRANSMUTATION +/obj/item/gun/magic/staff/door/do_suicide(mob/living/user) + . = ..() + var/obj/machinery/door/airlock/material/door = new(user.drop_location()) + door.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat) = SHEET_MATERIAL_AMOUNT)) + door.update_appearance(updates = UPDATE_ICON) + door.name = user.real_name + addtimer(CALLBACK(door, TYPE_PROC_REF(/obj/machinery/door, open)), 1.5 SECONDS) + user.drop_everything() + user.ghostize() + qdel(user) + return MANUAL_SUICIDE + +/// Makes people slip really hard /obj/item/gun/magic/staff/honk name = "staff of the honkmother" desc = "Honk." @@ -235,6 +313,17 @@ recharge_rate = 8 school = SCHOOL_EVOCATION +/obj/item/gun/magic/staff/honk/do_suicide(mob/living/user) + . = ..() + new /obj/effect/decal/cleanable/food/pie_smudge(user.drop_location()) + user.AddComponent(\ + /datum/component/face_decal/splat,\ + icon_state = "creampie",\ + layers = EXTERNAL_FRONT,\ + ) + return SHAME + +/// Dismembers people, and is a passable melee weapon /obj/item/gun/magic/staff/spellblade name = "spellblade" desc = "A deadly combination of laziness and bloodlust, this blade allows the user to dismember their enemies without all the hard work of actually swinging the sword." @@ -268,6 +357,18 @@ final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() +/obj/item/gun/magic/staff/spellblade/do_suicide(mob/living/user) + . = ..() + if (!iscarbon(user)) + return BRUTELOSS + var/mob/living/carbon/suicider = user + for (var/obj/item/bodypart/limb in suicider.bodyparts) + limb.dismember(BRUTE, silent = FALSE, wounding_type = WOUND_SLASH) + sleep(0.25 SECONDS) + + return BRUTELOSS + +/// Welds people into flying lockers /obj/item/gun/magic/staff/locker name = "staff of the locker" desc = "An artefact that expels encapsulating bolts, for incapacitating thy enemy." @@ -280,8 +381,15 @@ recharge_rate = 4 school = SCHOOL_TRANSMUTATION //in a way -//yes, they don't have sounds. they're admin staves, and their projectiles will play the chaos bolt sound anyway so why bother? +/obj/item/gun/magic/staff/locker/do_suicide(mob/living/user) + . = ..() + var/obj/structure/closet/decay/locker = new(user.drop_location()) + locker.insert(user) + locker.welded = TRUE + locker.update_appearance(UPDATE_ICON_STATE) + return BRUTELOSS +/// Makes targets "fly" by throwing them away /obj/item/gun/magic/staff/flying name = "staff of flying" desc = "An artefact that spits bolts of graceful magic that can make something fly." @@ -292,6 +400,13 @@ worn_icon_state = "flightstaff" school = SCHOOL_EVOCATION +/obj/item/gun/magic/staff/flying/do_suicide(mob/living/user) + . = ..() + var/atom/throw_target = get_edge_target_turf(user, pick(GLOB.alldirs)) + user.throw_at(throw_target, 200, 4) + return BRUTELOSS + +/// Scrambles languages /obj/item/gun/magic/staff/babel name = "staff of babel" desc = "An artefact that spits bolts of confusion magic that can make something depressed and incoherent." @@ -302,6 +417,13 @@ worn_icon_state = "babelstaff" school = SCHOOL_FORBIDDEN //evil +/obj/item/gun/magic/staff/babel/do_suicide(mob/living/user) + . = ..() + process_fire(user, user, FALSE) + user.say("I wish I was dead.", forced = "failed babel staff suicide") + return SHAME + +/// Deals damage to the target and recharges their spells if they have any /obj/item/gun/magic/staff/necropotence name = "staff of necropotence" desc = "An artefact that spits bolts of death magic that can repurpose the soul." @@ -312,6 +434,15 @@ worn_icon_state = "necrostaff" school = SCHOOL_NECROMANCY //REALLY evil +/obj/item/gun/magic/staff/necropotence/do_suicide(mob/living/user) + . = ..() + user.unequip_everything() + var/obj/item/soulstone/anybody/stone = new(user.drop_location()) + stone.capture_soul(user, user = null, forced = TRUE) + user.death() + return MANUAL_SUICIDE + +/// Asks a ghost to start playing as the poor victim /obj/item/gun/magic/staff/wipe name = "staff of possession" desc = "An artefact that spits bolts of mind-unlocking magic that can let ghosts invade the victim's mind." @@ -322,6 +453,12 @@ worn_icon_state = "wipestaff" school = SCHOOL_FORBIDDEN //arguably the worst staff in the entire game effect wise +/obj/item/gun/magic/staff/wipe/do_suicide(mob/living/user) + . = ..() + process_fire(user, user, FALSE) + return MANUAL_SUICIDE_NONLETHAL // Someone else is you now + +/// Makes the target really small /obj/item/gun/magic/staff/shrink name = "staff of shrinking" desc = "An artefact that spits bolts of tiny magic that makes things small. It's easily mistaken for a wand." @@ -335,3 +472,15 @@ school = SCHOOL_TRANSMUTATION slot_flags = NONE //too small to wear on your back w_class = WEIGHT_CLASS_NORMAL //but small enough for a bag + +/obj/item/gun/magic/staff/shrink/do_suicide(mob/living/user) + . = ..() + playsound(user, fire_sound, 50, TRUE) + user.unequip_everything() + user.visible_message(span_suicide("[user] shrinks into nothing!"), span_suicide("You shrink into nothing!")) + user.Stun(20 SECONDS, ignore_canstun = TRUE) + user.set_suicide(TRUE) + user.ghostize() + animate(user, transform = matrix() * 0, time = 1 SECONDS) + QDEL_IN(user, 1 SECONDS) + return MANUAL_SUICIDE diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index 6f59998ba61..f7ff0ddf2aa 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -9,16 +9,19 @@ righthand_file = 'icons/mob/inhands/items_righthand.dmi' base_icon_state = "nothingwand" w_class = WEIGHT_CLASS_SMALL - can_charge = FALSE + self_charging = FALSE max_charges = 100 //100, 50, 50, 34 (max charge distribution by 25%ths) + /// If true, we have a 25% chance of listed max charges, 50% chance of 1/2 max charges, 25% chance of 1/3 max charges var/variable_charges = TRUE /obj/item/gun/magic/wand/Initialize(mapload) - if(prob(75) && variable_charges) //25% chance of listed max charges, 50% chance of 1/2 max charges, 25% chance of 1/3 max charges - if(prob(33)) - max_charges = CEILING(max_charges / 3, 1) - else - max_charges = CEILING(max_charges / 2, 1) + if (!variable_charges || prob(25)) + return ..() + + if(prob(33)) // 33% of the remaining 75% so another 25% + max_charges = CEILING(max_charges / 3, 1) + else + max_charges = CEILING(max_charges / 2, 1) return ..() /obj/item/gun/magic/wand/examine(mob/user) @@ -32,7 +35,7 @@ /obj/item/gun/magic/wand/attack(atom/target, mob/living/user) if(target == user) return - ..() + return ..() /obj/item/gun/magic/wand/try_fire_gun(atom/target, mob/living/user, params) if(!charges) @@ -52,16 +55,17 @@ update_appearance() return . -/obj/item/gun/magic/wand/proc/zap_self(mob/living/user) +/// Called if we poke ourselves with the wand +/obj/item/gun/magic/wand/proc/zap_self(mob/living/user, suicide = FALSE) user.visible_message(span_danger("[user] zaps [user.p_them()]self with [src].")) playsound(user, fire_sound, 50, TRUE) user.log_message("zapped [user.p_them()]self with a [src]", LOG_ATTACK) +/obj/item/gun/magic/wand/do_suicide(mob/living/user) + zap_self(user, suicide = TRUE) + return FIRELOSS -///////////////////////////////////// -//WAND OF DEATH -///////////////////////////////////// - +/// Wand which kills people and heals skeletons /obj/item/gun/magic/wand/death name = "wand of death" desc = "This deadly wand overwhelms the victim's body with pure energy, slaying them without fail." @@ -72,34 +76,38 @@ base_icon_state = "deathwand" max_charges = 3 //3, 2, 2, 1 -/obj/item/gun/magic/wand/death/zap_self(mob/living/user) - ..() +/obj/item/gun/magic/wand/death/zap_self(mob/living/user, suicide = FALSE) + . = ..() charges-- if(user.can_block_magic()) user.visible_message(span_warning("[src] has no effect on [user]!")) return if(isliving(user)) - var/mob/living/L = user - if(L.mob_biotypes & MOB_UNDEAD) //negative energy heals the undead + if(user.mob_biotypes & MOB_UNDEAD) //negative energy heals the undead user.revive(ADMIN_HEAL_ALL, force_grab_ghost = TRUE) // This heals suicides - to_chat(user, span_notice("You feel great!")) + if (!suicide) + to_chat(user, span_notice("You feel great!")) return to_chat(user, span_warning("You irradiate yourself with pure negative energy! \ [pick("Do not pass go. Do not collect 200 zorkmids.","You feel more confident in your spell casting skills.","You die...","Do you want your possessions identified?")]")) user.death(FALSE) +/obj/item/gun/magic/wand/death/do_suicide(mob/living/user) + . = ..() + if (user.stat == DEAD) + return MANUAL_SUICIDE + user.visible_message(span_suicide("...but if anything [user.p_they()] look healthier than before.")) + return SHAME + /obj/item/gun/magic/wand/death/debug desc = "In some obscure circles, this is known as the 'cloning tester's friend'." max_charges = 500 variable_charges = FALSE - can_charge = TRUE + self_charging = TRUE recharge_rate = 1 -///////////////////////////////////// -//WAND OF HEALING -///////////////////////////////////// - +/// Wand which kills skeletons and heals people /obj/item/gun/magic/wand/resurrection name = "wand of healing" desc = "This wand uses healing magics to heal and revive. They are rarely utilized within the Wizard Federation for some reason." @@ -110,7 +118,7 @@ base_icon_state = "revivewand" max_charges = 10 //10, 5, 5, 4 -/obj/item/gun/magic/wand/resurrection/zap_self(mob/living/user) +/obj/item/gun/magic/wand/resurrection/zap_self(mob/living/user, suicide = FALSE) ..() charges-- if(user.can_block_magic()) @@ -125,19 +133,24 @@ user.death(FALSE) return user.revive(ADMIN_HEAL_ALL, force_grab_ghost = TRUE) // This heals suicides - to_chat(user, span_notice("You feel great!")) + if (!suicide) + to_chat(user, span_notice("You feel great!")) + +/obj/item/gun/magic/wand/resurrection/do_suicide(mob/living/user) + . = ..() + if (user.stat == DEAD) + return MANUAL_SUICIDE + user.visible_message(span_suicide("...but if anything [user.p_they()] look healthier than before.")) + return SHAME /obj/item/gun/magic/wand/resurrection/debug //for testing desc = "Is it possible for something to be even more powerful than regular magic? This wand is." max_charges = 500 variable_charges = FALSE - can_charge = TRUE + self_charging = TRUE recharge_rate = 1 -///////////////////////////////////// -//WAND OF POLYMORPH -///////////////////////////////////// - +/// Wand which turns mobs into other mobs /obj/item/gun/magic/wand/polymorph name = "wand of polymorph" desc = "This wand is attuned to chaos and will radically alter the victim's form." @@ -148,16 +161,27 @@ fire_sound = 'sound/effects/magic/staff_change.ogg' max_charges = 10 //10, 5, 5, 4 -/obj/item/gun/magic/wand/polymorph/zap_self(mob/living/user) +/obj/item/gun/magic/wand/polymorph/zap_self(mob/living/user, suicide = FALSE) . = ..() //because the user mob ceases to exists by the time wabbajack fully resolves - user.wabbajack() charges-- -///////////////////////////////////// -//WAND OF TELEPORTATION -///////////////////////////////////// +/obj/item/gun/magic/wand/polymorph/do_suicide(mob/living/user) + var/static/list/corpse_types = list( + /obj/effect/decal/cleanable/insectguts, + /obj/item/food/deadmouse, + /obj/item/trash/bee, + ) + playsound(loc, fire_sound, 50, TRUE, -1) + var/corpse_path = pick(corpse_types) + var/atom/corpse = new corpse_path(user.drop_location()) + corpse.name = user.real_name + user.unequip_everything() + user.ghostize() + qdel(user) + return MANUAL_SUICIDE +/// Wand of go somewhere else /obj/item/gun/magic/wand/teleport name = "wand of teleportation" desc = "This wand will wrench targets through space and time to move them somewhere else." @@ -169,14 +193,45 @@ max_charges = 10 //10, 5, 5, 4 no_den_usage = TRUE -/obj/item/gun/magic/wand/teleport/zap_self(mob/living/user) +/obj/item/gun/magic/wand/teleport/zap_self(mob/living/user, suicide = FALSE) if(do_teleport(user, user, 10, channel = TELEPORT_CHANNEL_MAGIC)) var/datum/effect_system/fluid_spread/smoke/smoke = new smoke.set_up(3, holder = src, location = user.loc) smoke.start() charges-- - ..() + return ..() +/obj/item/gun/magic/wand/teleport/do_suicide(mob/living/user) + playsound(loc, fire_sound, 50, TRUE, -1) + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.set_up(3, holder = src, location = user.loc) + smoke.start() + if (!iscarbon(user)) + return SHAME + + var/mob/living/carbon/suicider = user + var/obj/item/teleport_part = suicider.get_organ_slot(ORGAN_SLOT_BRAIN) + if (!teleport_part) + teleport_part = suicider.get_bodypart(BODY_ZONE_HEAD) + if (!teleport_part) + return SHAME + + var/turf/destination = user.drop_location() // Grab this first in case moving the brain out dusts you or something + + if (isorgan(teleport_part)) + var/obj/item/organ/brain = teleport_part + brain.Remove(user, special = FALSE) + else + var/obj/item/bodypart/head = teleport_part + head.dismember(BRUTE) + + teleport_part.forceMove(destination) + do_teleport(teleport_part, destination, 10, channel = TELEPORT_CHANNEL_MAGIC) + if (user.stat != DEAD) + return SHAME + return MANUAL_SUICIDE + +/// Wand of go somewhere else which is safe-ish /obj/item/gun/magic/wand/safety name = "wand of safety" desc = "This wand will use the lightest of bluespace currents to gently place the target somewhere safe." @@ -188,7 +243,7 @@ max_charges = 10 //10, 5, 5, 4 no_den_usage = FALSE -/obj/item/gun/magic/wand/safety/zap_self(mob/living/user) +/obj/item/gun/magic/wand/safety/zap_self(mob/living/user, suicide = FALSE) var/turf/origin = get_turf(user) var/turf/destination = find_safe_turf(extended_safety_checks = TRUE) @@ -197,20 +252,21 @@ var/datum/effect_system/fluid_spread/smoke/smoke = new smoke.set_up(0, holder = src, location = t) smoke.start() - ..() + return ..() + +/obj/item/gun/magic/wand/safety/do_suicide(mob/living/user) + . = ..() + return SHAME // It's a safety wand sorry /obj/item/gun/magic/wand/safety/debug desc = "This wand has 'find_safe_turf()' engraved into its blue wood. Perhaps it's a secret message?" max_charges = 500 variable_charges = FALSE - can_charge = TRUE + self_charging = TRUE recharge_rate = 1 -///////////////////////////////////// -//WAND OF DOOR CREATION -///////////////////////////////////// - +/// Wand of making doors /obj/item/gun/magic/wand/door name = "wand of door creation" desc = "This particular wand can create doors in any wall for the unscrupulous wizard who shuns teleportation magics." @@ -220,17 +276,35 @@ base_icon_state = "doorwand" fire_sound = 'sound/effects/magic/staff_door.ogg' max_charges = 20 //20, 10, 10, 7 - no_den_usage = 1 + no_den_usage = TRUE -/obj/item/gun/magic/wand/door/zap_self(mob/living/user) +/obj/item/gun/magic/wand/door/zap_self(mob/living/user, suicide = FALSE) to_chat(user, span_notice("You feel vaguely more open with your feelings.")) charges-- - ..() + return ..() -///////////////////////////////////// -//WAND OF FIREBALL -///////////////////////////////////// +/obj/item/gun/magic/wand/door/do_suicide(mob/living/user) + if (!iscarbon(user)) + . = ..() + var/static/list/shared_feelings = list( + "I can't take it any more!!", + "I can't do this any more!!", + "I don't want to live in this world!!", + "I wish I was dead!!", + "Nothing matters to me any more!!", + "Someone please kill me!!", + "The pain is unbearable!!", + ) + user.say(pick(shared_feelings), forced = "failed wand suicide") + return SHAME + playsound(loc, fire_sound, 50, TRUE, -1) + var/mob/living/carbon/suicider = user + var/obj/item/bodypart/chest = suicider.get_bodypart(BODY_ZONE_CHEST) // I think it's impossible not to have a chest so we'll just assume they have one + user.visible_message(span_suicide("[user]'s chest swings open like a door!")) + chest.dismember(BRUTE, silent = FALSE, wounding_type = WOUND_SLASH) + return BRUTELOSS +/// Wand of blowing shit up /obj/item/gun/magic/wand/fireball name = "wand of fireball" desc = "This wand shoots scorching balls of fire that explode into destructive flames." @@ -241,25 +315,22 @@ base_icon_state = "firewand" max_charges = 8 //8, 4, 4, 3 -/obj/item/gun/magic/wand/fireball/zap_self(mob/living/user) +/obj/item/gun/magic/wand/fireball/zap_self(mob/living/user, suicide = FALSE) ..() explosion(user, devastation_range = -1, light_impact_range = 2, flame_range = 2, flash_range = 3, adminlog = FALSE, explosion_cause = src) charges-- -///////////////////////////////////// -//WAND OF NOTHING -///////////////////////////////////// - +/// Wand of doing fuck all /obj/item/gun/magic/wand/nothing name = "wand of nothing" desc = "It's not just a stick, it's a MAGIC stick?" ammo_type = /obj/item/ammo_casing/magic/nothing +/obj/item/gun/magic/wand/nothing/do_suicide(mob/living/user) + . = ..() + return SHAME -///////////////////////////////////// -//WAND OF SHRINKING -///////////////////////////////////// - +/// Wand of making things small /obj/item/gun/magic/wand/shrink name = "wand of shrinking" desc = "Feel the tiny eldritch terror of an itty... bitty... head!" @@ -271,13 +342,24 @@ no_den_usage = TRUE w_class = WEIGHT_CLASS_TINY -/obj/item/gun/magic/wand/shrink/zap_self(mob/living/user) +/obj/item/gun/magic/wand/shrink/zap_self(mob/living/user, suicide = FALSE) to_chat(user, span_notice("The world grows large...")) charges-- user.AddComponent(/datum/component/shrink, -1) // small forever return ..() -// Wand of debugging +/obj/item/gun/magic/wand/shrink/do_suicide(mob/living/user) + playsound(user, fire_sound, 50, TRUE) + user.unequip_everything() + user.visible_message(span_suicide("[user] shrinks into nothing!"), span_suicide("You shrink into nothing!")) + user.Stun(20 SECONDS, ignore_canstun = TRUE) + user.set_suicide(TRUE) + user.ghostize() + animate(user, transform = matrix() * 0, time = 1 SECONDS) + QDEL_IN(user, 1 SECONDS) + return MANUAL_SUICIDE + +// Wands of debugging #ifdef TESTING @@ -291,7 +373,7 @@ color = COLOR_ADMIN_PINK max_charges = 99999 -/obj/item/gun/magic/wand/antag/zap_self(mob/living/user) +/obj/item/gun/magic/wand/antag/zap_self(mob/living/user, suicide = FALSE) . = ..() var/obj/item/ammo_casing/magic/antag/casing = new ammo_type() var/obj/projectile/magic/magic_proj = casing.projectile_type diff --git a/code/modules/projectiles/guns/special/hand_of_midas.dm b/code/modules/projectiles/guns/special/hand_of_midas.dm index 80aa94d31a5..863b791672d 100644 --- a/code/modules/projectiles/guns/special/hand_of_midas.dm +++ b/code/modules/projectiles/guns/special/hand_of_midas.dm @@ -12,7 +12,7 @@ fire_sound = 'sound/items/weapons/gun/rifle/shot.ogg' pinless = TRUE max_charges = 1 - can_charge = FALSE + self_charging = FALSE item_flags = NEEDS_PERMIT w_class = WEIGHT_CLASS_BULKY // Should fit on a belt. force = 3 @@ -100,10 +100,27 @@ /obj/item/gun/magic/midas_hand/proc/check_gold_range(mob/living/user, mob/living/victim) return IN_GIVEN_RANGE(user, victim, gold_suck_range*2) +/obj/item/gun/magic/midas_hand/suicide_act(mob/living/user) + if(!ishuman(user)) + return + + var/mob/living/carbon/human/victim = user + victim.visible_message(span_suicide("[victim] holds the barrel of [src] to [victim.p_their()] head, lighting the fuse. It looks like [user.p_theyre()] trying to commit suicide!")) + if(!do_after(victim, 1.5 SECONDS)) + return SHAME + playsound(src, 'sound/items/weapons/gun/rifle/shot.ogg', 75, TRUE) + to_chat(victim, span_danger("You don't even have the time to register the gunshot by the time your body has completely converted into a golden statue.")) + var/newcolors = list(rgb(206, 164, 50), rgb(146, 146, 139), rgb(28,28,28), rgb(0,0,0)) + victim.petrify(statue_timer = INFINITY, save_brain = FALSE, colorlist = newcolors) + playsound(victim, 'sound/effects/coin2.ogg', 75, TRUE) + charges = 0 + gold_timer = 0 + return OXYLOSS + /obj/item/ammo_casing/magic/midas_round projectile_type = /obj/projectile/magic/midas_round - +/// Turns people into gold /obj/projectile/magic/midas_round name = "gold pellet" desc = "A typical flintlock ball, save for the fact it's made of cursed Egyptian gold." @@ -117,37 +134,18 @@ /// The gold charge in this pellet var/gold_charge = 0 - /obj/projectile/magic/midas_round/fire(setAngle) /// Transfer the gold energy to our bullet var/obj/item/gun/magic/midas_hand/my_gun = fired_from gold_charge = my_gun.gold_timer my_gun.gold_timer = 0 - ..() + return ..() // Gives human targets Midas Blight. /obj/projectile/magic/midas_round/on_hit(atom/target, blocked = 0, pierce_hit) . = ..() - if(ishuman(target)) - var/mob/living/carbon/human/my_guy = target - if(isskeleton(my_guy)) // No cheap farming - return + if(!ishuman(target)) + return + var/mob/living/carbon/human/my_guy = target + if(!isskeleton(my_guy)) // No cheap farming my_guy.apply_status_effect(/datum/status_effect/midas_blight, min(30 SECONDS, round(gold_charge, 0.2))) // 100u gives 10 seconds - return - -/obj/item/gun/magic/midas_hand/suicide_act(mob/living/user) - if(!ishuman(user)) - return - - var/mob/living/carbon/human/victim = user - victim.visible_message(span_suicide("[victim] holds the barrel of [src] to [victim.p_their()] head, lighting the fuse. It looks like [user.p_theyre()] trying to commit suicide!")) - if(!do_after(victim, 1.5 SECONDS)) - return - playsound(src, 'sound/items/weapons/gun/rifle/shot.ogg', 75, TRUE) - to_chat(victim, span_danger("You don't even have the time to register the gunshot by the time your body has completely converted into a golden statue.")) - var/newcolors = list(rgb(206, 164, 50), rgb(146, 146, 139), rgb(28,28,28), rgb(0,0,0)) - victim.petrify(statue_timer = INFINITY, save_brain = FALSE, colorlist = newcolors) - playsound(victim, 'sound/effects/coin2.ogg', 75, TRUE) - charges = 0 - gold_timer = 0 - return OXYLOSS diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 54791311f9d..33d8e98b373 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -27,6 +27,7 @@ visible_message(span_warning("[src] fizzles on contact with [plant_tray]!")) return PROJECTILE_DELETE_WITHOUT_HITTING +/// Straight up kills you, unless you're undead /obj/projectile/magic/death name = "bolt of death" icon_state = "pulse1_bl" @@ -53,6 +54,7 @@ plant_tray.set_weedlevel(0) // even the weeds perish plant_tray.plantdies() +/// Brings you back from the dead or makes you back into the dead if you're undead /obj/projectile/magic/resurrection name = "bolt of resurrection" icon_state = "ion" @@ -79,6 +81,7 @@ return plant_tray.set_plant_health(plant_tray.myseed.endurance, forced = TRUE) +/// Teleports you somewhere randomly within range /obj/projectile/magic/teleport name = "bolt of teleportation" icon_state = "bluespace" @@ -100,6 +103,7 @@ smoke.set_up(smoke_range, holder = src, location = stuff.loc) //Smoke drops off if a lot of stuff is moved for the sake of sanity smoke.start() +/// Teleports you somewhere on the station where the local conditions won't kill you /obj/projectile/magic/safety name = "bolt of safety" icon_state = "bluespace" @@ -118,6 +122,7 @@ smoke.set_up(0, holder = src, location = t) smoke.start() +/// Turns walls into doors, or opens doors /obj/projectile/magic/door name = "bolt of door creation" icon_state = "energy" @@ -144,6 +149,7 @@ A.unlock() D.open() +/// Turns mobs into other mobs, or plants into other plants /obj/projectile/magic/change name = "bolt of change" icon_state = "ice_1" @@ -166,6 +172,7 @@ return plant_tray.polymorph() +/// Makes objects come to life /obj/projectile/magic/animate name = "bolt of animation" icon_state = "red_1" @@ -176,10 +183,11 @@ if(!is_type_in_typecache(target, GLOB.animatable_blacklist)) target.animate_atom_living(firer) -///proc to animate the target into a living creature +///proc to animate the target into a living creature, should return the mob if possible /atom/proc/animate_atom_living(mob/living/owner) return +/// Slices you /obj/projectile/magic/spellblade name = "blade energy" icon_state = "lavastaff" @@ -187,6 +195,7 @@ damage_type = BURN dismemberment = 50 +/// Generic magic bullet /obj/projectile/magic/arcane_barrage name = "arcane bolt" icon_state = "arcane_barrage" @@ -194,6 +203,7 @@ damage_type = BURN hitsound = 'sound/items/weapons/barragespellhit.ogg' +/// Welds targets inside lockers, and throws the locker /obj/projectile/magic/locker name = "locker bolt" icon_state = "locker" @@ -244,8 +254,9 @@ AM.forceMove(get_turf(src)) . = ..() +/// Magic locker which breaks itself open after a while /obj/structure/closet/decay - breakout_time = 600 + breakout_time = 1 MINUTES icon_welded = null icon_state = "cursed" paint_jobs = null @@ -281,6 +292,7 @@ dump_contents() qdel(src) +/// Throws the target far away /obj/projectile/magic/flying name = "bolt of flying" icon_state = "flight" @@ -291,6 +303,7 @@ var/atom/throw_target = get_edge_target_turf(target, angle2dir(angle)) target.throw_at(throw_target, 200, 4) +/// Marks you for death, rewards the caster if they kill you /obj/projectile/magic/bounty name = "bolt of bounty" icon_state = "bounty" @@ -300,6 +313,7 @@ if(isliving(target)) target.apply_status_effect(/datum/status_effect/bounty, firer) +/// Makes whatever it hits immune to magic, except for the magic that makes them immune to magic /obj/projectile/magic/antimagic name = "bolt of antimagic" icon_state = "antimagic" @@ -309,6 +323,7 @@ if(istype(target)) target.apply_status_effect(/datum/status_effect/song/antimagic) +/// Throws the target at the caster /obj/projectile/magic/fetch name = "bolt of fetching" icon_state = "fetch" @@ -319,6 +334,7 @@ var/atom/throw_target = get_edge_target_turf(target, get_dir(target, firer)) target.throw_at(throw_target, 200, 4) +/// Scrambles the languages of the target /obj/projectile/magic/babel name = "bolt of babel" icon_state = "babel" @@ -329,6 +345,7 @@ if(curse_of_babel(target)) target.add_mood_event("curse_of_babel", /datum/mood_event/tower_of_babel) +/// Hurts the target and uses their life energy to recharge the spells they probably don't have /obj/projectile/magic/necropotence name = "bolt of necropotence" icon_state = "necropotence" @@ -346,6 +363,7 @@ qdel(tap) +/// Puts someone else in control of the target /obj/projectile/magic/wipe name = "bolt of possession" icon_state = "wipe" @@ -435,6 +453,7 @@ QDEL_IN(trail_effect, trail_lifespan) COOLDOWN_START(src, trail_cooldown, trail_lifespan) +/// Arcs a chain of lightning from hit targets /obj/projectile/magic/aoe/lightning name = "lightning bolt" icon_state = "tesla_projectile" //Better sprites are REALLY needed and appreciated!~ @@ -469,11 +488,12 @@ zap_range = 4 zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_LOW_POWER_GEN +/// Classic exploding fireball /obj/projectile/magic/fireball name = "bolt of fireball" icon_state = "fireball" damage = 10 - damage_type = BRUTE + damage_type = BURN /// Heavy explosion range of the fireball var/exp_heavy = 0 @@ -505,6 +525,7 @@ explosion_cause = src, ) +/// Slow moving, homing, stunning projectile /obj/projectile/magic/aoe/magic_missile name = "magic missile" icon_state = "magicm" @@ -523,6 +544,7 @@ color = "red" //Looks more culty this way range = 10 +/// Delivers a powerful slap and converts turfs to cult turfs /obj/projectile/magic/aoe/juggernaut name = "Gauntlet Echo" icon_state = "cultfist" @@ -551,7 +573,7 @@ new /obj/effect/temp_visual/cult/turf/floor(get_turf(adjacent_object)) //still magic related, but a different path - +/// Makes you cold /obj/projectile/temp/chill name = "bolt of chills" icon_state = "ice_2" @@ -559,9 +581,11 @@ armour_penetration = 100 temperature = -200 // Cools you down greatly per hit +/// Doesn't do anything /obj/projectile/magic/nothing name = "bolt of nothing" +/// Homing projectile /obj/projectile/magic/spellcard name = "enchanted card" desc = "A piece of paper enchanted to give it extreme durability and stiffness, along with a very hot burn to anyone unfortunate enough to get hit by a charged one." @@ -570,7 +594,7 @@ damage = 2 antimagic_charge_cost = 0 // since the cards gets spammed like a shotgun -//a shrink ray that shrinks stuff, which grows back after a short while. +/// a shrink ray that shrinks stuff, which grows back after a short while. /obj/projectile/magic/shrink name = "shrink ray" icon_state = "blue_laser"