diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 34082be122e..8fa1726ba52 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -36,6 +36,7 @@ var/global/list/all_species[0] var/global/list/all_languages[0] var/global/list/language_keys[0] // Table of say codes for all languages var/global/list/all_nations[0] +var/global/list/all_superheroes[0] var/global/list/whitelisted_species = list() //global var of unsafe-to-spawn-on-reaction mobs @@ -141,6 +142,11 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al var/datum/nations/N = new T all_nations[N.name] = N + paths = typesof(/datum/superheroes)-/datum/superheroes + for(var/T in paths) + var/datum/superheroes/S = new T + all_superheroes[S.name] = S + //Languages and species. paths = typesof(/datum/language)-/datum/language for(var/T in paths) diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 1320ca0a855..32aa784dbc0 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -269,6 +269,7 @@ client body += "" body += "" body += "" + body += "" body += "" body += "" if(isobj(D)) @@ -764,6 +765,19 @@ client return holder.Topic(href, list("makeslime"=href_list["makeslime"])) + else if(href_list["makesuper"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["makesuper"]) + if(!istype(H)) + usr << "This can only be done to instances of type /mob/living/carbon/human" + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + usr << "Mob doesn't exist anymore" + return + else if(href_list["makeai"]) if(!check_rights(R_SPAWN)) return diff --git a/code/datums/spell.dm b/code/datums/spell.dm index e03f172f2a2..7aeee98f92e 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -1,3 +1,6 @@ +#define TARGET_CLOSEST 1 +#define TARGET_RANDOM 2 + /obj/effect/proc_holder var/panel = "Debug"//What panel the proc holder needs to go on. @@ -235,6 +238,9 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast var/include_user = 0 //if it includes usr in the target list + var/random_target = 0 // chooses random viable target instead of asking the caster + var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target + /obj/effect/proc_holder/spell/wizard/aoe_turf //affects all turfs in view or range (depends) var/inner_radius = -1 //for all your ring spell needs @@ -260,7 +266,26 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin continue possible_targets += M - targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets + //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets + //Adds a safety check post-input to make sure those targets are actually in range. + var/mob/M + if(!random_target) + M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets + else + switch(random_target_priority) + if(TARGET_RANDOM) + M = pick(possible_targets) + if(TARGET_CLOSEST) + for(var/mob/living/L in possible_targets) + if(M) + if(get_dist(user,L) < get_dist(user,M)) + if(los_check(user,L)) + M = L + else + if(los_check(user,L)) + M = L + if(M in view_or_range(range, user, selection_type)) targets += M + else var/list/possible_targets = list() for(var/mob/living/target in view_or_range(range, user, selection_type)) @@ -300,3 +325,16 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin perform(targets) return + + +/obj/effect/proc_holder/spell/wizard/targeted/proc/los_check(mob/A,mob/B) + //Checks for obstacles from A to B + var/obj/dummy = new(A.loc) + dummy.pass_flags |= PASSTABLE + for(var/turf/turf in getline(A,B)) + for(var/atom/movable/AM in turf) + if(!AM.CanPass(dummy,turf,1)) + qdel(dummy) + return 0 + qdel(dummy) + return 1 \ No newline at end of file diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm new file mode 100644 index 00000000000..3c658775af1 --- /dev/null +++ b/code/datums/spells/lightning.dm @@ -0,0 +1,103 @@ +/obj/effect/proc_holder/spell/wizard/targeted/lightning + name = "Lightning Bolt" + desc = "Throws a lightning bolt at the nearby enemy. Classic." + charge_type = "recharge" + charge_max = 300 + clothes_req = 1 + invocation = "UN'LTD P'WAH!" + invocation_type = "shout" + range = 7 + cooldown_min = 30 + selection_type = "view" + random_target = 1 + var/energy = 0 + var/ready = 0 + var/image/halo = null + icon_power_button = "spell_tech" + +/obj/effect/proc_holder/spell/wizard/targeted/lightning/lightnian + clothes_req = 0 + + +/obj/effect/proc_holder/spell/wizard/targeted/lightning/Click() + if(!ready) + if(cast_check()) + StartChargeup() + else + if(cast_check(skipcharge=1)) + choose_targets() + return 1 + +/obj/effect/proc_holder/spell/wizard/targeted/lightning/proc/StartChargeup(mob/user = usr) + ready = 1 + user << "You start gathering the power." + halo = image("icon"='icons/effects/effects.dmi',"icon_state" ="electricity","layer" = EFFECTS_LAYER) + user.overlays.Add(halo) + spawn(0) + while(ready) + sleep(1) + energy++ + if(energy >= 100 && ready) + Discharge() + +obj/effect/proc_holder/spell/wizard/targeted/lightning/proc/Reset(mob/user = usr) + ready = 0 + energy = 0 + if(halo) + user.overlays.Remove(halo) + +/obj/effect/proc_holder/spell/wizard/targeted/lightning/revert_cast(mob/user = usr) + user << "No target found in range." + Reset(user) + ..() + +/obj/effect/proc_holder/spell/wizard/targeted/lightning/proc/Discharge(mob/user = usr) + var/mob/living/M = user + //M.electrocute_act(25,"Lightning Bolt") + M << "You lose control over the spell." + Reset(user) + start_recharge() + + +/obj/effect/proc_holder/spell/wizard/targeted/lightning/cast(list/targets, mob/user = usr) + + var/mob/living/carbon/target = targets[1] + + if(get_dist(user,target)>range) + user << "They are too far away!" + Reset(user) + return + + user.Beam(target,icon_state="lightning",icon='icons/effects/effects.dmi',time=5) + + switch(energy) + if(0 to 25) + target.electrocute_act(10,"Lightning Bolt") + playsound(get_turf(target), 'sound/machines/defib_zap.ogg', 50, 1, -1) + if(25 to 75) + target.electrocute_act(25,"Lightning Bolt") + playsound(get_turf(target), 'sound/machines/defib_zap.ogg', 50, 1, -1) + if(75 to 100) + //CHAIN LIGHTNING + Bolt(user,target,energy,user) + Reset(user) + +/obj/effect/proc_holder/spell/wizard/targeted/lightning/proc/Bolt(mob/origin,mob/target,bolt_energy,mob/user = usr) + origin.Beam(target,icon_state="lightning",icon='icons/effects/effects.dmi',time=5) + var/mob/living/carbon/current = target + if(bolt_energy < 75) + current.electrocute_act(25,"Lightning Bolt") + playsound(get_turf(current), 'sound/machines/defib_zap.ogg', 50, 1, -1) + else + current.electrocute_act(25,"Lightning Bolt") + playsound(get_turf(current), 'sound/machines/defib_zap.ogg', 50, 1, -1) + var/list/possible_targets = new + for(var/mob/living/M in view_or_range(range,target,"view")) + if(user == M || target == M && los_check(current,M)) // || origin == M ? Not sure double shockings is good or not + continue + possible_targets += M + if(!possible_targets.len) + return + var/mob/living/next = pick(possible_targets) + if(next) + Bolt(current,next,bolt_energy-6,user) // 5 max bounces \ No newline at end of file diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index ecdc79fd71d..b873e56fa3a 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -266,6 +266,104 @@ SA.health -= 20 ..() +/obj/item/weapon/legcuffs/bolas + name = "bolas" + desc = "An entangling bolas. Throw at your foes to trip them and prevent them from running." + gender = NEUTER + icon = 'icons/obj/weapons.dmi' + icon_override = 'icons/mob/in-hand/swords.dmi' + icon_state = "bolas" + siemens_coefficient = 1 + slot_flags = SLOT_BELT + throwforce = 2 + w_class = 2 + origin_tech = "materials=1" + attack_verb = list("lashed", "bludgeoned", "whipped") + force = 4 + breakouttime = 50 //10 seconds + throw_speed = 1 + throw_range = 10 + var/dispenser = 0 + var/throw_sound = 'sound/weapons/whip.ogg' + var/trip_prob = 60 + var/thrown_from + +/obj/item/weapon/legcuffs/bolas/suicide_act(mob/living/user) + viewers(user) << "[user] is wrapping the [src.name] around \his neck! It looks like \he's trying to commit suicide." + return(OXYLOSS) + +/obj/item/weapon/legcuffs/bolas/throw_at(var/atom/A, throw_range, throw_speed) + if(usr && !istype(thrown_from, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/bolas)) //if there is a user, but not a mech + if(istype(usr, /mob/living/carbon/human)) //if the user is human + var/mob/living/carbon/human/H = usr + if((CLUMSY in H.mutations) && prob(50)) + H <<"You smack yourself in the face while swinging the [src]!" + H.Stun(2) + H.drop_item(src) + return + if (!thrown_from && usr) //if something hasn't set it already (like a mech does when it launches) + thrown_from = usr //then the user must have thrown it + if (!istype(thrown_from, /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/bolas)) + playsound(src, throw_sound, 20, 1) //because mechs play the sound anyways + var/turf/target = get_turf(A) + var/atom/movable/adjtarget = new /atom/movable + var/xadjust = 0 + var/yadjust = 0 + var/scaler = 0 //used to changed the normalised vector to the proper size + scaler = throw_range / max(abs(target.x - src.x), abs(target.y - src.y)) //whichever is larger magnitude is what we normalise to + if (target.x - src.x != 0) //just to avoid fucking with math for no reason + xadjust = round((target.x - src.x) * scaler) //normalised vector is now scaled up to throw_range + adjtarget.x = src.x + xadjust //the new target at max range + else + adjtarget.x = src.x + if (target.y - src.y != 0) + yadjust = round((target.y - src.y) * scaler) + adjtarget.y = src.y + yadjust + else + adjtarget.y = src.y + // log_admin("Adjusted target of [adjtarget.x] and [adjtarget.y], adjusted with [xadjust] and [yadjust] from [scaler]") + ..(get_turf(adjtarget), throw_range, throw_speed) + thrown_from = null + +/obj/item/weapon/legcuffs/bolas/throw_impact(atom/hit_atom) //Pomf was right, I was wrong - Comic + if(isliving(hit_atom) && hit_atom != usr) //if the target is a live creature other than the thrower + var/mob/living/M = hit_atom + if(ishuman(M)) //if they're a human species + var/mob/living/carbon/human/H = M + if(H.m_intent == "run") //if they're set to run (though not necessarily running at that moment) + if(prob(trip_prob)) //this probability is up for change and mostly a placeholder - Comic + step(H, H.dir) + H.visible_message("[H] was tripped by the bolas!","Your legs have been tangled!"); + H.Stun(2) //used instead of setting damage in vars to avoid non-human targets being affected + H.Weaken(4) + H.legcuffed = src //applies legcuff properties inherited through legcuffs + src.loc = H + H.update_inv_legcuffed() + if(!H.legcuffed) //in case it didn't happen, we need a safety net + throw_failed() + else if(H.legcuffed) //if the target is already legcuffed (has to be walking) + throw_failed() + return + else //walking, but uncuffed, or the running prob() failed + H << "You stumble over the thrown bolas" + step(H, H.dir) + H.Stun(1) + throw_failed() + return + else + M.Stun(2) //minor stun damage to anything not human + throw_failed() + return + +/obj/item/weapon/legcuffs/bolas/proc/throw_failed() //called when the throw doesn't entangle + //log_admin("Logged as [thrown_from]") + if(!thrown_from || !istype(thrown_from, /mob/living)) //in essence, if we don't know whether a person threw it + qdel(src) //destroy it, to stop infinite bolases + +/obj/item/weapon/legcuffs/bolas/Bump() + ..() + throw_failed() //allows a mech bolas to be destroyed + /obj/item/weapon/holosign_creator name = "holographic sign projector" desc = "A handy-dandy hologaphic projector that displays a janitorial sign." diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index d7620449593..486a65763fc 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -99,6 +99,9 @@ dat += "Flesh to Stone (60)
" dat += "This spell will curse a person to immediately turn into an unmoving statue. The effect will eventually wear off if the statue is not destroyed.
" + dat += "Lightning Bolt (30)
" + dat += "Charge up and throw lightning bolt at the nearby enemy. Classic. The longer you charge the more powerful the spell, beware of overcharge however!
" + dat += "Instant Summons (10)
" dat += "This spell can be used to bind a valuable item to you, bringing it to your hand at will. Using this spell while holding the bound item will allow you to unbind it. It does not require wizard garb.
" @@ -202,7 +205,7 @@ uses-- /* */ - var/list/available_spells = list(magicmissile = "Magic Missile", fireball = "Fireball", disintegrate = "Disintegrate", disabletech = "Disable Tech", repulse = "Repulse", smoke = "Smoke", blind = "Blind", mindswap = "Mind Transfer", forcewall = "Forcewall", blink = "Blink", teleport = "Teleport", mutate = "Mutate", etherealjaunt = "Ethereal Jaunt", knock = "Knock", horseman = "Curse of the Horseman", fleshtostone = "Flesh to Stone", summonitem = "Instant Summons", summonguns = "Summon Guns", summonmagic = "Summon Magic", staffchange = "Staff of Change", soulstone = "Six Soul Stone Shards and the spell Artificer", armor = "Mastercrafted Armor Set", staffanimate = "Staff of Animation", staffchaos = "Staff of Chaos", staffdoor = "Staff of Door Creation", wands = "Wand Assortment") + var/list/available_spells = list(magicmissile = "Magic Missile", fireball = "Fireball", disintegrate = "Disintegrate", disabletech = "Disable Tech", repulse = "Repulse", smoke = "Smoke", blind = "Blind", mindswap = "Mind Transfer", forcewall = "Forcewall", blink = "Blink", teleport = "Teleport", mutate = "Mutate", etherealjaunt = "Ethereal Jaunt", knock = "Knock", horseman = "Curse of the Horseman", fleshtostone = "Flesh to Stone", lightningbolt = "Lightning Bolt", summonitem = "Instant Summons", summonguns = "Summon Guns", summonmagic = "Summon Magic", staffchange = "Staff of Change", soulstone = "Six Soul Stone Shards and the spell Artificer", armor = "Mastercrafted Armor Set", staffanimate = "Staff of Animation", staffchaos = "Staff of Chaos", staffdoor = "Staff of Door Creation", wands = "Wand Assortment") var/already_knows = 0 for(var/obj/effect/proc_holder/spell/wizard/aspell in H.spell_list) if(available_spells[href_list["spell_choice"]] == initial(aspell.name)) @@ -305,6 +308,10 @@ feedback_add_details("wizard_spell_learned","FS") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells H.spell_list += new /obj/effect/proc_holder/spell/wizard/targeted/inflict_handler/flesh_to_stone(H) temp = "You have learned flesh to stone." + if("lightningbolt") + feedback_add_details("wizard_spell_learned","LB") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells + H.spell_list += new /obj/effect/proc_holder/spell/wizard/targeted/lightning(null) + temp = "You have learned lightning bolt." if("summonitem") feedback_add_details("wizard_spell_learned","IS") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells H.spell_list += new /obj/effect/proc_holder/spell/wizard/targeted/summonitem(null) diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm index 1f14f105557..e5dbe73f4d0 100644 --- a/code/game/machinery/wishgranter.dm +++ b/code/game/machinery/wishgranter.dm @@ -8,7 +8,13 @@ anchored = 1 density = 1 var/datum/mind/target - var/list/types = list("Owlman", "The Griffin") + var/list/types = list() + +/obj/machinery/wish_granter/New() + for(var/supname in all_superheroes) + types |= supname + ..() + /obj/machinery/wish_granter/attack_hand(var/mob/user as mob) usr.set_machine(src) @@ -22,109 +28,18 @@ else user << "The power of the Wish Granter have turned you into the superhero the station deserves. You are a masked vigilante, and answer to no man. Will you use your newfound strength to protect the innocent, or will you hunt the guilty?" + var/wish if(types.len == 1) wish = pick(types) else wish = input("You want to become...","Wish") as null|anything in types + if(!wish) return var/mob/living/carbon/human/M = user - switch(wish) - if("Owlman") - types -= "Owlman" - M.fully_replace_character_name(M.real_name, "Owlman") - - for(var/obj/item/W in M) - M.unEquip(W) - - M.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(M), slot_shoes) - M.equip_to_slot_or_del(new /obj/item/clothing/under/owl(M), slot_w_uniform) - M.equip_to_slot_or_del(new /obj/item/clothing/suit/toggle/owlwings(M), slot_wear_suit) - M.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/owl_mask(M), slot_wear_mask) - - var/obj/item/weapon/card/id/syndicate/W = new(M) - W.name = "[M.real_name]'s ID Card (Superhero)" - W.access = get_all_accesses() - W.assignment = "Superhero" - W.registered_name = M.real_name - M.equip_to_slot_or_del(W, slot_wear_id) - - M.regenerate_icons() - - var/datum/objective/protect/protect = new - protect.owner = user.mind - if(target) - protect.target = target - else - protect.find_target() - target = protect.target - user.mind.objectives += protect - - if("The Griffin") - types -= "The Griffin" - M.fully_replace_character_name(M.real_name, "The Griffin") - - for(var/obj/item/W in M) - M.unEquip(W) - - M.equip_to_slot_or_del(new /obj/item/clothing/shoes/griffin(M), slot_shoes) - M.equip_to_slot_or_del(new /obj/item/clothing/under/griffin(M), slot_w_uniform) - M.equip_to_slot_or_del(new /obj/item/clothing/suit/toggle/owlwings/griffinwings(M), slot_wear_suit) - M.equip_to_slot_or_del(new /obj/item/clothing/head/griffin(M), slot_head) - - var/obj/item/weapon/card/id/syndicate/W = new(M) - W.name = "[M.real_name]'s ID Card (Supervillain)" - W.access = get_all_accesses() - W.assignment = "Supervillain" - W.registered_name = M.real_name - M.equip_to_slot_or_del(W, slot_wear_id) - - M.regenerate_icons() - - var/datum/objective/assassinate/assasinate = new - assasinate.owner = user.mind - if(target) - assasinate.target = target - else - assasinate.find_target() - target = assasinate.target - user.mind.objectives += assasinate - - ticker.mode.traitors += user.mind - user.mind.special_role = wish - - var/obj_count = 1 - for(var/datum/objective/OBJ in user.mind.objectives) - user << "Objective #[obj_count]: [OBJ.explanation_text]" - obj_count++ - - - //Time to hand out the powers, since they are currently generic - if (!(HULK in user.mutations)) - user.dna.SetSEState(HULKBLOCK,1) - - if (!(LASER in user.mutations)) - user.mutations.Add(LASER) - - if (!(XRAY in user.mutations)) - user.mutations.Add(XRAY) - user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) - user.see_in_dark = 8 - user.see_invisible = SEE_INVISIBLE_LEVEL_TWO - - if (!(RESIST_COLD in user.mutations)) - user.mutations.Add(RESIST_COLD) - - if (!(RESIST_HEAT in user.mutations)) - user.mutations.Add(RESIST_HEAT) - - if (!(TK in user.mutations)) - user.mutations.Add(TK) - - if(!(REGEN in user.mutations)) - user.mutations.Add(REGEN) - - user.update_mutations() + var/datum/superheroes/S = all_superheroes[wish] + if(S) + S.create(M) //Remove the wishgranter or teleport it randomly on the station if(!types.len) diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index fc8c5ee658a..3f82973767d 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -468,3 +468,32 @@ log_message("Launched a mouse-trap from [src.name], targeting [target]. HONK!") do_after_cooldown() return + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/bolas + name = "PCMK-6 Bolas Launcher" + icon_state = "mecha_bolas" + projectile = /obj/item/weapon/legcuffs/bolas + fire_sound = 'sound/weapons/whip.ogg' + projectiles = 10 + missile_speed = 1 + missile_range = 30 + projectile_energy_cost = 50 + equip_cooldown = 10 + + can_attach(obj/mecha/combat/gygax/M as obj) + if(..()) + if(istype(M)) + return 1 + return 0 + + action(target) + if(!action_checks(target)) return + set_ready_state(0) + var/obj/item/weapon/legcuffs/bolas/M = new projectile(chassis.loc) + playsound(chassis, fire_sound, 50, 1) + M.thrown_from = src + M.throw_at(target, missile_range, missile_speed) + projectiles-- + log_message("Fired from [src.name], targeting [target].") + do_after_cooldown() + return \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index b1286930c42..db68c73ee56 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -191,7 +191,7 @@ "/obj/item/weapon/soap", "/obj/item/weapon/holosign_creator" ) - + /obj/item/weapon/storage/belt/janitor/full/New() ..() new /obj/item/device/lightreplacer(src) @@ -352,15 +352,15 @@ origin_tech = "bluespace=4;syndicate=2" allow_quick_empty = 1 can_hold = list() + flags = NODROP New() ..() - new /obj/item/clothing/mask/gas/owl_mask(src) - new /obj/item/clothing/under/owl(src) new /obj/item/weapon/grenade/smokebomb(src) new /obj/item/weapon/grenade/smokebomb(src) - new /obj/item/device/detective_scanner(src) - - + new /obj/item/weapon/grenade/smokebomb(src) + new /obj/item/weapon/grenade/smokebomb(src) + new /obj/item/weapon/legcuffs/bolas(src) + new /obj/item/weapon/legcuffs/bolas(src) // As a last resort, the belt can be used as a plastic explosive with a fixed timer (15 seconds). Naturally, you'll lose all your gear... // Of course, it could be worse. It could spawn a singularity! diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index d905730dbf8..a50dd50f6fb 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -49,6 +49,9 @@ new /obj/item/device/analyzer(src) new /obj/item/weapon/wirecutters(src) +/obj/item/weapon/storage/toolbox/mechanical/greytide + flags = NODROP + /obj/item/weapon/storage/toolbox/electrical name = "electrical toolbox" icon_state = "yellow" diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 1ea81011406..42849785723 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -116,6 +116,7 @@ var/global/nologevent = 0 Make Robot | Make Alien | Make slime + Make Superhero "} //Simple Animals diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 55890046c42..07616048e2f 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -516,6 +516,15 @@ if(ticker.mode.xenos.len) dat += check_role_table("Xenos", ticker.mode.xenos, src) + if(ticker.mode.superheroes.len) + dat += check_role_table("Superheroes", ticker.mode.superheroes, src) + + if(ticker.mode.supervillains.len) + dat += check_role_table("Supervillains", ticker.mode.supervillains, src) + + if(ticker.mode.greyshirts.len) + dat += check_role_table("Greyshirts", ticker.mode.greyshirts, src) + var/datum/game_mode/mutiny/mutiny = get_mutiny_mode() if(mutiny) dat += mutiny.check_antagonists_ui(src) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index ebc48a70d0c..c0331ef1997 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1397,6 +1397,16 @@ usr.client.cmd_admin_slimeize(H) + else if(href_list["makeslime"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["makesuper"]) + if(!istype(H)) + usr << "This can only be used on instances of type /mob/living/carbon/human" + return + + usr.client.cmd_admin_super(H) + else if(href_list["makerobot"]) if(!check_rights(R_SPAWN)) return diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index c88f518766d..76240d2f627 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -281,6 +281,23 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that else alert("Invalid mob") +/client/proc/cmd_admin_super(var/mob/M in mob_list) + set category = "Event" + set name = "Make Superhero" + + if(!ticker) + alert("Wait until the game starts") + return + if(ishuman(M)) + var/type = input("Pick the Superhero","Superhero") as null|anything in all_superheroes + var/datum/superheroes/S = all_superheroes[type] + if(S) + S.create(M) + log_admin("[key_name(src)] has turned [M.key] into a Superhero.") + message_admins("\blue [key_name_admin(usr)] made [key_name(M)] into a Superhero.", 1) + else + alert("Invalid mob") + /* /client/proc/cmd_admin_monkeyize(var/mob/M in world) set category = "Event" diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index e0741ed1ac7..753f0454fe7 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -237,7 +237,7 @@ desc = "Woof!" icon_state = "corgihead" item_state = "chickensuit" - flags = BLOCKHAIR + flags = BLOCKHAIR | NODROP siemens_coefficient = 2.0 /obj/item/clothing/head/bearpelt diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index ef92c5837df..119971e5be0 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -13,6 +13,9 @@ redcoat _color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. +/obj/item/clothing/shoes/black/greytide + flags = NODROP + /obj/item/clothing/shoes/brown name = "brown shoes" desc = "A pair of brown shoes." diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 1655618eb8f..6b5b86e00f5 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -156,6 +156,7 @@ body_parts_covered = UPPER_TORSO|ARMS|LOWER_TORSO|LEGS|FEET flags_inv = HIDESHOES|HIDEJUMPSUIT siemens_coefficient = 2.0 + flags = NODROP /obj/item/clothing/suit/monkeysuit name = "Monkey Suit" diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index b5f276c2539..54fbe20c87b 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -50,6 +50,9 @@ flags = ONESIZEFITSALL species_fit = list("Vox") +/obj/item/clothing/under/color/grey/greytide + flags = ONESIZEFITSALL | NODROP + /obj/item/clothing/under/color/orange name = "orange jumpsuit" desc = "Don't wear this near paranoid security officers" diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 9bdd895a14a..cf74bb7e96a 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -310,6 +310,20 @@ This function restores all organs. damage = damage*species.brute_mod if(organ.take_damage(damage, 0, sharp, edge, used_weapon)) UpdateDamageIcon() + if(LAssailant && ishuman(LAssailant)) + var/mob/living/carbon/human/H = LAssailant + if(H.mind && H.mind in (ticker.mode.superheroes || ticker.mode.supervillains || ticker.mode.greyshirts)) + var/list/attack_bubble_recipients = list() + var/mob/living/user + for(var/mob/O in viewers(user, src)) + if (O.client && !( O.blinded )) + attack_bubble_recipients.Add(O.client) + spawn(0) + var/image/dmgIcon = image('icons/effects/hit_blips.dmi', src, "dmg[rand(1,2)]",MOB_LAYER+1) + dmgIcon.pixel_x = (!lying) ? rand(-3,3) : rand(-11,12) + dmgIcon.pixel_y = (!lying) ? rand(-11,9) : rand(-10,1) + //world << "x: [dmgIcon.pixel_x] AND y: [dmgIcon.pixel_y]" + flick_overlay(dmgIcon, attack_bubble_recipients, 9) receive_damage() if(BURN) damageoverlaytemp = 20 diff --git a/code/modules/mob/living/carbon/superheroes.dm b/code/modules/mob/living/carbon/superheroes.dm new file mode 100644 index 00000000000..e25dc9911e9 --- /dev/null +++ b/code/modules/mob/living/carbon/superheroes.dm @@ -0,0 +1,188 @@ +/datum/game_mode + var/list/datum/mind/superheroes = list() + var/list/datum/mind/supervillains = list() + var/list/datum/mind/greyshirts = list() + +/datum/superheroes + var/name + var/class + var/list/default_genes = list(REGEN, NO_BREATH, RESIST_COLD) + var/list/default_spells = list() + +/datum/superheroes/proc/create(var/mob/living/carbon/human/H) + assign_genes(H) + assign_spells(H) + equip(H) + assign_id(H) + +/datum/superheroes/proc/equip(var/mob/living/carbon/human/H) + H.fully_replace_character_name(H.real_name, name) + for(var/obj/item/W in H) + if(istype(W,/obj/item/organ)) continue + H.unEquip(W) + +/datum/superheroes/proc/assign_genes(var/mob/living/carbon/human/H) + if(default_genes.len) + for(var/gene in default_genes) + H.mutations |= gene + H.update_mutations() + +/datum/superheroes/proc/assign_spells(var/mob/living/carbon/human/H) + if(default_spells.len) + for(var/spell in default_spells) + var/obj/effect/proc_holder/spell/wizard/S = spell + if(!S) return + H.spell_list += new S + H.update_power_buttons() + +/datum/superheroes/proc/assign_id(var/mob/living/carbon/human/H) + var/obj/item/weapon/card/id/syndicate/W = new(H) + W.registered_name = H.real_name + W.access = get_all_accesses() + if(class == "Superhero") + W.name = "[H.real_name]'s ID Card (Superhero)" + W.assignment = "Superhero" + ticker.mode.superheroes += H.mind + else if(class == "Supervillain") + W.name = "[H.real_name]'s ID Card (Supervillain)" + W.assignment = "Supervillain" + ticker.mode.supervillains += H.mind + + H.equip_to_slot_or_del(W, slot_wear_id) + H.regenerate_icons() + +/datum/superheroes/owlman + name = "Owlman" + class = "Superhero" + +/datum/superheroes/owlman/equip(var/mob/living/carbon/human/H) + ..() + + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/under/owl(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/toggle/owlwings(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/owl_mask(H), slot_wear_mask) + H.equip_to_slot_or_del(new /obj/item/weapon/storage/belt/bluespace/owlman(H), slot_belt) + H.equip_to_slot_or_del(new /obj/item/clothing/glasses/night(H), slot_glasses) + + +/datum/superheroes/griffin + name = "The Griffin" + default_spells = list(/obj/effect/proc_holder/spell/wizard/targeted/recruit) + class = "Supervillain" + +/datum/superheroes/griffin/equip(var/mob/living/carbon/human/H) + ..() + + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/griffin(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/under/griffin(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/toggle/owlwings/griffinwings(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/griffin(H), slot_head) + + +/datum/superheroes/lightnian + name = "LightnIan" + default_spells = list(/obj/effect/proc_holder/spell/wizard/targeted/lightning/lightnian) + class = "Superhero" + +/datum/superheroes/lightnian/equip(var/mob/living/carbon/human/H) + ..() + + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/brown(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/brown(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/corgisuit(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/corgi(H), slot_head) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/color/yellow(H), slot_gloves) + H.equip_to_slot_or_del(new /obj/item/weapon/bedsheet/orange(H), slot_back) + + + + + + + +///////////////////////////////POWERS/ABILITIES CODE///////////////////////////////////////// + + +//The Griffin's special recruit abilitiy +/obj/effect/proc_holder/spell/wizard/targeted/recruit + name = "Recruit Greyshirt" + desc = "Allows you to recruit a conscious, non-braindead, non-catatonic human to be part of the Greyshirts, your personal henchmen. This works on Civilians only and you can recruit a maximum of 3!." + panel = "Shadowling Abilities" + charge_max = 450 + clothes_req = 0 + range = 1 //Adjacent to user + var/recruiting = 0 + +/obj/effect/proc_holder/spell/wizard/targeted/recruit/cast(list/targets) + for(var/mob/living/carbon/human/target in targets) + if(ticker.mode.greyshirts.len >= 3) + usr << "You have already recruited the maximum number of henchmen." + if(!in_range(usr, target)) + usr << "You need to be closer to enthrall [target]." + charge_counter = charge_max + return + if(!target.ckey) + usr << "The target has no mind." + charge_counter = charge_max + return + if(target.stat) + usr << "The target must be conscious." + charge_counter = charge_max + return + if(!ishuman(target)) + usr << "You can only recruit humans." + charge_counter = charge_max + return + if(target.mind.assigned_role != "Civilian") + usr << "You can only recruit Civilians." + if(recruiting) + usr << "You are already recruiting!" + charge_counter = charge_max + return + recruiting = 1 + usr << "This target is valid. You begin the recruiting process." + target << "[usr] focuses in concentration. Your head begins to ache." + + for(var/progress = 0, progress <= 3, progress++) + switch(progress) + if(1) + usr << "You begin by introducing yourself and explaining what you're about." + usr.visible_message("[usr]'s introduces himself and explains his plans.") + if(2) + usr << "You begin the recruitment of [target]." + usr.visible_message("[usr] leans over towards [target], whispering excitedly as he gives a speech.") + target << "You feel yourself agreeing with [usr], and a surge of loyalty begins building." + target.Weaken(12) + sleep(20) + if(isloyal(target)) + usr << "They are enslaved by Nanotrasen. You feel their interest in your cause wane and disappear." + usr.visible_message("[usr] stops talking for a moment, then moves back away from [target].") + target << "Your loyalty implant activates and a sharp pain reminds you of your loyalties to Nanotrasen." + return + if(3) + usr << "You begin filling out the application form with [target]." + usr.visible_message("[usr] pulls out a pen and paper and begins filling an application form with [target].") + target << "You are being convinced by [usr] to fill out an application form to become a henchman." //Ow the edge + if(!do_mob(usr, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a loyalty implant + usr << "The enrollment process has been interrupted - you have lost the attention of [target]." + target << "You move away and are no longer under the charm of [usr]. The application form is null and void." + recruiting = 0 + return + + recruiting = 0 + usr << "You have recruited [target] as your henchman!" + target << "You have decided to enroll as a henchman for [usr]. You are now part of the feared 'Greyshirts'." + target << "You must follow the orders of [usr], and help him succeed in his dastardly schemes." + target << "You may not harm other Greyshirt or [usr]. However, you do not need to obey other Greyshirts." + ticker.mode.greyshirts += target.mind + target.set_species("Human") + target.h_style = "Bald" + target.fully_replace_character_name(target.real_name, "Generic Henchman ([rand(1, 1000)])") + for(var/obj/item/W in target) + if(istype(W,/obj/item/organ)) continue + target.unEquip(W) + target.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey/greytide(target), slot_w_uniform) + target.equip_to_slot_or_del(new /obj/item/clothing/shoes/black/greytide(target), slot_shoes) + target.equip_to_slot_or_del(new /obj/item/weapon/storage/toolbox/mechanical/greytide(target), slot_l_hand) + target.regenerate_icons() \ No newline at end of file diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index b7d49332314..f794e062c64 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -741,6 +741,17 @@ construction_time = 100 category = list("Exosuit Equipment") +/datum/design/mech_bolas + name = "Exosuit Weapon Design (PCMK-6 Bolas Launcher)" + desc = "Allows for the construction of PCMK-6 Bolas Launcher." + id = "mech_bolas" + build_type = MECHFAB + req_tech = list("combat" = 3) + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/bolas + materials = list("$metal"=10000) + construction_time = 100 + category = list("Exosuit Equipment") + /datum/design/mech_teleporter name = "Exosuit Module (Teleporter Module)" desc = "An exosuit module that allows exosuits to teleport to any position in view." diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 97b9a24eb2f..62998f37b7c 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/hit_blips.dmi b/icons/effects/hit_blips.dmi new file mode 100644 index 00000000000..53ec7b3fd07 Binary files /dev/null and b/icons/effects/hit_blips.dmi differ diff --git a/icons/mecha/mecha_equipment.dmi b/icons/mecha/mecha_equipment.dmi index 1aaacb76193..de73cb8d088 100644 Binary files a/icons/mecha/mecha_equipment.dmi and b/icons/mecha/mecha_equipment.dmi differ diff --git a/icons/mob/in-hand/swords.dmi b/icons/mob/in-hand/swords.dmi index 89d35809a9e..b4a7a6724d5 100644 Binary files a/icons/mob/in-hand/swords.dmi and b/icons/mob/in-hand/swords.dmi differ diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi index f686858b569..67dbeb84163 100644 Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ diff --git a/paradise.dme b/paradise.dme index b15100499bf..87370cb5516 100644 --- a/paradise.dme +++ b/paradise.dme @@ -167,6 +167,7 @@ #include "code\datums\spells\horsemask.dm" #include "code\datums\spells\inflict_handler.dm" #include "code\datums\spells\knock.dm" +#include "code\datums\spells\lightning.dm" #include "code\datums\spells\mind_transfer.dm" #include "code\datums\spells\projectile.dm" #include "code\datums\spells\summonitem.dm" @@ -1183,6 +1184,7 @@ #include "code\modules\mob\living\carbon\life.dm" #include "code\modules\mob\living\carbon\shock.dm" #include "code\modules\mob\living\carbon\species.dm" +#include "code\modules\mob\living\carbon\superheroes.dm" #include "code\modules\mob\living\carbon\alien\alien.dm" #include "code\modules\mob\living\carbon\alien\alien_defenses.dm" #include "code\modules\mob\living\carbon\alien\death.dm" diff --git a/sound/weapons/whip.ogg b/sound/weapons/whip.ogg new file mode 100644 index 00000000000..e138ec8578a Binary files /dev/null and b/sound/weapons/whip.ogg differ