diff --git a/code/WorkInProgress/experimental/newcombatsystem.dm b/code/WorkInProgress/experimental/newcombatsystem.dm new file mode 100644 index 00000000000..26a138726c6 --- /dev/null +++ b/code/WorkInProgress/experimental/newcombatsystem.dm @@ -0,0 +1,191 @@ +//It's not a very big change, but I think melee will benefit from it. +//Currently will only be restricted to special training weapons to test the balancedness of the system. +//1)Knockdown, stun and weaken chances are separate and dependant on the part of the body you're aiming at +//eg a mop will be better applied to legs since it has a higher base knockdown chance than the other disabling states +//while an energy gun would be better applied to the chest because of the stunning chance. +//2)Weapons also have a parry chance which is checked every time the one wielding the weapon is attacked in melee +//in the area is currently aiming at and is able to defend himself. +//More ideas to come. + +//NOTES: doesn't work with armor yet + +/obj/item/weapon/training //subclass of weapons that is currently the only one that uses the alternate combat system + name = "training weapon" + desc = "A weapon for training the advanced fighting technicues" + var/chance_parry = 0 + var/chance_weaken = 0 + var/chance_stun = 0 + var/chance_knockdown = 0 + var/chance_knockout = 0 + var/chance_disarm = 0 + +//chances - 5 is low, 10 is medium, 15 is good + +/obj/item/weapon/training/axe //hard-hitting, but doesn't have much in terms of disabling people (except by killing) + name = "training axe" + icon_state = "training_axe" + /*combat stats*/ + force = 15 + chance_parry = 5 + chance_weaken = 10 + chance_stun = 5 + chance_knockdown = 5 + chance_knockout = 5 + chance_disarm = 0 + +/obj/item/weapon/training/sword //not bad attack, good at parrying and disarming + name = "training sword" + icon_state = "training_sword" + /*combat stats*/ + force = 10 + chance_parry = 15 + chance_weaken = 5 + chance_stun = 0 + chance_knockdown = 5 + chance_knockout = 0 + chance_disarm = 15 + +/obj/item/weapon/training/staff //not bad attack either, good at tripping and parrying + name = "training staff" + icon_state = "training_staff" + /*combat stats*/ + force = 10 + chance_parry = 15 + chance_weaken = 5 + chance_stun = 0 + chance_knockdown = 15 + chance_knockout = 0 + chance_disarm = 5 + +/obj/item/weapon/training/mace //worst attack, but has a good chance of stun, knockout or weaken + name = "training mace" + icon_state = "training_mace" + /*combat stats*/ + force = 5 + chance_parry = 0 + chance_weaken = 15 + chance_stun = 10 + chance_knockdown = 0 + chance_knockout = 10 + chance_disarm = 0 + +/obj/item/weapon/training/attack(target as mob, mob/user as mob) + var/target_area = attack_location(user.zone_sel.selecting) + for(var/mob/O in viewers(src,7)) + O << "\red \b [user.name] attacks [target.name] in the [target_area] with [src.name]!" + if(!target.stat && target.zone_sel.selecting == target_area) //parrying occurs here + if(istype(target.r_hand,/obj/item/weapon/training) + if(prob(target.r_hand:chance_parry)) + for(var/mob/O in viewers(src,7)) + O << "\red \b [target.name] deftly parries the attack with [target.r_hand.name]!" + return + if(istype(target.l_hand,/obj/item/weapon/training) + if(prob(target.l_hand:chance_parry)) + for(var/mob/O in viewers(src,7)) + O << "\red \b [target.name] deftly parries the attack with [target.l_hand.name]!" + return + target.bruteloss -= src.force + + var/modifier_knockdown = 1.0 + var/modifier_knockout = 1.0 + var/modifier_stun = 1.0 + var/modifier_weaken = 1.0 + var/modifier_disarm = 0.0 + + switch(target_area) + if("eyes") + modifier_weaken = 2.0 + modifier_stun = 0.5 + modifier_knockdown = 0.0 + if("head") + modifier_stun = 0.8 + modifier_knockout = 1.5 + modifier_weaken = 1.2 + modifier_knockdown = 0.0 + if("chest") + if("right arm","r_arm") + if("left arm","l_arm") + if("right hand","r_hand") + if("left hand","l_hand") + if("groin") + if("right leg","r_leg") + if("left leg","l_leg") + if("right foot","r_foot") + if("left foot","l_foot") + + +/proc/attack_location(var/initloc = "chest") //proc to randomise actual hit loc based on where you're aiming at + var/resultloc = "chest" //also forgot hands/feet. bleh + var/percentage = rand(1,100) + switch(initloc) + if("eyes") + switch(percentage) + if(1 to 10) + resultloc = "eyes" + if(11 to 30) + resultloc = "head" + if(31 to 100) + resultloc = "chest" + if("head") + switch(percentage) + if(1 to 5) + resultloc = "eyes" + if(6 to 40) + resultloc = "head" + if(41 to 100) + resultloc = "chest" + if("chest") + switch(percentage) + if(1 to 80) + resultloc = "chest" + if(81 to 84) + resultloc = "right arm" + if(85 to 88) + resultloc = "left arm" + if(89 to 92) + resultloc = "right leg" + if(93 to 96) + resultloc = "left leg" + if(97 to 98) + resultloc = "groin" + if(99 to 100) + resultloc = "head" + if("l_arm") + switch(percentage) + if(1 to 60) + resultloc = "left arm" + if(61 to 100) + resultloc = "chest" + if("r_arm") + switch(percentage) + if(1 to 60) + resultloc = "right arm" + if(61 to 100) + resultloc = "chest" + if("groin") + switch(percentage) + if(1 to 35) + resultloc = "groin" + if(36 to 50) + resultloc = "left leg" + if(51 to 65) + resultloc = "right leg" + if(66 to 100) + resultloc = "chest" + if("l_leg") + switch(percentage) + if(1 to 60) + resultloc = "left leg" + if(61 to 70) + resultloc = "groin" + if(71 to 100) + resultloc = "chest" + if("r_leg") + switch(percentage) + if(1 to 60) + resultloc = "right leg" + if(61 to 70) + resultloc = "groin" + if(71 to 100) + resultloc = "chest" + return resultloc \ No newline at end of file diff --git a/code/datums/spell.dm b/code/datums/spell.dm new file mode 100644 index 00000000000..14c9e46c2b6 --- /dev/null +++ b/code/datums/spell.dm @@ -0,0 +1,56 @@ +var/list/spells = list(/obj/spell/blind,/obj/spell/blink,/obj/spell/disintegrate,/obj/spell/ethereal_jaunt,/obj/spell/fireball,/obj/spell/forcewall,/obj/spell/knock,/obj/spell/magic_missile,/obj/spell/mutate,/obj/spell/teleport) //needed for the badmin verb for now + +/obj/spell + name = "Spell" + desc = "A wizard spell" + + var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? + var/recharge = 100 //recharge time in deciseconds + var/clothes_req = 1 //see if it requires clothes + var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell + var/invocation_type = "none" //can be none, whisper and shout + var/range = 7 //the range of the spell + var/cast = 0 //the only way I could think of making it temporarily disable + var/message = "derp herp" //whatever it says to the guy affected by it. not always needed + +/obj/spell/proc/cast_check() //checks if the spell can be cast based on its settings, plus handles chanting and recharge + if(!(src in usr.spell_list)) + usr << "\red You shouldn't have this spell! Something's wrong." + return 0 + if(cast) + usr << "[name] is still recharging." + return 0 + if(usr.stat) + usr << "Not when you're incapacitated." + return 0 + if(clothes_req) //clothes check + if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe)) + usr << "I don't feel strong enough without my robe." + return 0 + if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal)) + usr << "I don't feel strong enough without my sandals." + return 0 + if(!istype(usr:head, /obj/item/clothing/head/wizard)) + usr << "I don't feel strong enough without my hat." + return 0 + + return 1 + +/obj/spell/proc/invocation() //spelling the spell out and setting it on recharge + + src.cast = 1 + var/old_name = src.name + src.name += " (cast)" + spawn(recharge) + src.cast = 0 + src.name = old_name + + switch(invocation_type) + if("shout") + usr.say(invocation) + if(usr.gender=="male") + playsound(usr.loc, pick('vs_chant_conj_hm.wav','vs_chant_conj_lm.wav','vs_chant_ench_hm.wav','vs_chant_ench_lm.wav','vs_chant_evoc_hm.wav','vs_chant_evoc_lm.wav','vs_chant_illu_hm.wav','vs_chant_illu_lm.wav','vs_chant_necr_hm.wav','vs_chant_necr_lm.wav'), 100, 1) + else + playsound(usr.loc, pick('vs_chant_conj_hf.wav','vs_chant_conj_lf.wav','vs_chant_ench_hf.wav','vs_chant_ench_lf.wav','vs_chant_evoc_hf.wav','vs_chant_evoc_lf.wav','vs_chant_illu_hf.wav','vs_chant_illu_lf.wav','vs_chant_necr_hf.wav','vs_chant_necr_lf.wav'), 100, 1) + if("whisper") + usr.whisper(invocation) \ No newline at end of file diff --git a/code/datums/spells/blind.dm b/code/datums/spells/blind.dm new file mode 100644 index 00000000000..660d42e7be4 --- /dev/null +++ b/code/datums/spells/blind.dm @@ -0,0 +1,43 @@ +/obj/spell/blind + name = "Blind" + desc = "This spell temporarly blinds a single person and does not require wizard garb." + + school = "transmutation" + recharge = 300 + clothes_req = 0 + invocation = "STI KALY" + invocation_type = "whisper" + message = "\blue Your eyes cry out in pain!" + var/blind_time = 300 //in deciseconds + var/eye_blind = 10 //I have no idea what these two do + var/eye_blurry = 20 //but eh, they're in the code + //I would add the ability to choose different disabilities (do ho ho), but I have no idea which bit corresponds to which disability + +/obj/spell/blind/Click() + ..() + + if(!cast_check()) + return + + var/mob/M = input("Choose whom to blind", "ABRAKADABRA") as mob in oview(usr,range) + + invocation() + + var/obj/overlay/B = new /obj/overlay( M.loc ) + B.icon_state = "blspell" + B.icon = 'wizard.dmi' + B.name = "spell" + B.anchored = 1 + B.density = 0 + B.layer = 4 + M.canmove = 0 + spawn(5) + del(B) + M.canmove = 1 + M << text("[message]") + M.disabilities |= 1 + spawn(blind_time) + M.disabilities &= ~1 + M.eye_blind = eye_blind + M.eye_blurry = eye_blurry + return \ No newline at end of file diff --git a/code/datums/spells/blink.dm b/code/datums/spells/blink.dm new file mode 100644 index 00000000000..ae8f524ad87 --- /dev/null +++ b/code/datums/spells/blink.dm @@ -0,0 +1,50 @@ +/obj/spell/blink + name = "Blink" + desc = "This spell randomly teleports you a short distance." + + school = "abjuration" + recharge = 20 + clothes_req = 1 + invocation = "none" + invocation_type = "none" + range = -1 //can affect only the user by default, but with var editing can be a teleport other spell + var/outer_teleport_radius = 6 //the radius of the area in which it picks turfs to teleport to + var/inner_teleport_radius = 0 //so with var fuckery you can have it teleport in a ring, not in a circle + var/smoke_spread = 1 //if set to 0, no smoke spreads when teleporting + +/obj/spell/blink/Click() + ..() + + if(!cast_check()) + return + + var/mob/M + + if(range>=0) + M = input("Choose whom to blink", "ABRAKADABRA") as mob in view(usr,range) + else + M = usr + + invocation() + + var/list/turfs = new/list() + for(var/turf/T in orange(M,outer_teleport_radius)) + if(T in orange(M,inner_teleport_radius)) continue + if(istype(T,/turf/space)) continue + if(T.density) continue + if(T.x>world.maxx-outer_teleport_radius || T.xworld.maxy-outer_teleport_radius || T.y=0) + M = input("Choose whom to jaunt", "ABRAKADABRA") as mob in view(usr,range) + else + M = usr + + invocation() + + spawn(0) + var/mobloc = get_turf(M.loc) + var/obj/dummy/spell_jaunt/holder = new /obj/dummy/spell_jaunt( mobloc ) + var/atom/movable/overlay/animation = new /atom/movable/overlay( mobloc ) + animation.name = "water" + animation.density = 0 + animation.anchored = 1 + animation.icon = 'mob.dmi' + animation.icon_state = "liquify" + animation.layer = 5 + animation.master = holder + flick("liquify",animation) + M.loc = holder + M.client.eye = holder + var/datum/effects/system/steam_spread/steam = new /datum/effects/system/steam_spread() + steam.set_up(10, 0, mobloc) + steam.start() + sleep(jaunt_duration) + mobloc = get_turf(M.loc) + animation.loc = mobloc + steam.location = mobloc + steam.start() + M.canmove = 0 + sleep(20) + flick("reappear",animation) + sleep(5) + M.loc = mobloc + M.canmove = 1 + M.client.eye = M + del(animation) + del(holder) + +/obj/dummy/spell_jaunt + name = "water" + icon = 'effects.dmi' + icon_state = "nothing" + var/canmove = 1 + density = 0 + anchored = 1 + +/obj/dummy/spell_jaunt/relaymove(var/mob/user, direction) + if (!src.canmove) return + switch(direction) + if(NORTH) + src.y++ + if(SOUTH) + src.y-- + if(EAST) + src.x++ + if(WEST) + src.x-- + if(NORTHEAST) + src.y++ + src.x++ + if(NORTHWEST) + src.y++ + src.x-- + if(SOUTHEAST) + src.y-- + src.x++ + if(SOUTHWEST) + src.y-- + src.x-- + src.canmove = 0 + spawn(2) src.canmove = 1 + +/obj/dummy/spell_jaunt/ex_act(blah) + return +/obj/dummy/spell_jaunt/bullet_act(blah,blah) + return \ No newline at end of file diff --git a/code/datums/spells/fireball.dm b/code/datums/spells/fireball.dm new file mode 100644 index 00000000000..1b6319a0861 --- /dev/null +++ b/code/datums/spells/fireball.dm @@ -0,0 +1,46 @@ +/obj/spell/fireball + name = "Fireball" + desc = "This spell fires a fireball at a target and does not require wizard garb." + + school = "evocation" + recharge = 200 + clothes_req = 0 + invocation = "ONI SOMA" + invocation_type = "shout" + var/radius_devastation = -1 + var/radius_heavy = -1 + var/radius_light = 2 + var/radius_flash = 2 + var/bruteloss = 20 // apparently fireball deals damage in addition to the explosion + var/fireloss = 25 // huh + var/lifetime = 200 // in deciseconds + +/obj/spell/fireball/Click() + ..() + + if(!cast_check()) + return + + var/mob/M = input("Choose whom to fireball", "ABRAKADABRA") as mob|obj|turf in oview(usr,range) + + invocation() + + var/obj/overlay/A = new /obj/overlay( usr.loc ) + A.icon_state = "fireball" + A.icon = 'wizard.dmi' + A.name = "a fireball" + A.anchored = 0 + A.density = 0 + var/i + for(i=0, i= max_targets) + break + spawn(0) + var/obj/overlay/A = new /obj/overlay( usr.loc ) + A.icon_state = "magicm" + A.icon = 'wizard.dmi' + A.name = "a magic missile" + A.anchored = 0 + A.density = 0 + A.layer = 4 + var/i + for(i=0, i=0) + M = input("Choose whom to mutate", "ABRAKADABRA") as mob in view(usr,range) + else + M = usr + + invocation() + + M << text("[message]") + var/mutation = 0 + for(var/MT in mutation_types) + switch(MT) + if("tk") + mutation |= 1 + if("cold resist") + mutation |= 2 + if("xray") + mutation |= 4 + if("hulk") + mutation |= 8 + if("clown") + mutation |= 16 + M.mutations |= mutation + spawn (mutate_duration) + M.mutations &= ~mutation + return \ No newline at end of file diff --git a/code/datums/spells/teleport.dm b/code/datums/spells/teleport.dm new file mode 100644 index 00000000000..066adaf2f11 --- /dev/null +++ b/code/datums/spells/teleport.dm @@ -0,0 +1,61 @@ +/obj/spell/teleport + name = "Teleport" + desc = "This spell teleports you to a type of area of your selection." + + school = "abjuration" + recharge = 600 + clothes_req = 1 + invocation = "SCYAR NILA" + invocation_type = "none" //hardcoded into the spell due to its specifics + range = -1 //can affect only the user by default, but with var editing can be a teleport other spell + var/smoke_spread = 1 //if set to 0, no smoke spreads when teleporting + +/obj/spell/blink/Click() + ..() + + if(!cast_check()) + return + + var/mob/M + + if(range>=0) + M = input("Choose whom to teleport", "ABRAKADABRA") as mob in view(usr,range) + else + M = usr + + invocation() + + var/A + + A = input("Area to jump to", "BOOYEA", A) in teleportlocs + + var/area/thearea = teleportlocs[A] + + usr.say("SCYAR NILA [uppertext(A)]") + if(usr.gender=="male") + playsound(usr.loc, pick('vs_chant_conj_hm.wav','vs_chant_conj_lm.wav','vs_chant_ench_hm.wav','vs_chant_ench_lm.wav','vs_chant_evoc_hm.wav','vs_chant_evoc_lm.wav','vs_chant_illu_hm.wav','vs_chant_illu_lm.wav','vs_chant_necr_hm.wav','vs_chant_necr_lm.wav'), 100, 1) + else + playsound(usr.loc, pick('vs_chant_conj_hf.wav','vs_chant_conj_lf.wav','vs_chant_ench_hf.wav','vs_chant_ench_lf.wav','vs_chant_evoc_hf.wav','vs_chant_evoc_lf.wav','vs_chant_illu_hf.wav','vs_chant_illu_lf.wav','vs_chant_necr_hf.wav','vs_chant_necr_lf.wav'), 100, 1) + + var/datum/effects/system/harmless_smoke_spread/smoke = new /datum/effects/system/harmless_smoke_spread() + + if(smoke_spread) + smoke.set_up(5, 0, usr.loc) + smoke.attach(usr) + smoke.start() + + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + M.loc = pick(L) + + if(smoke_spread) + smoke.start() \ No newline at end of file diff --git a/code/defines/mob/mob.dm b/code/defines/mob/mob.dm index f5efc53439a..3f3ab135571 100644 --- a/code/defines/mob/mob.dm +++ b/code/defines/mob/mob.dm @@ -159,6 +159,9 @@ var/voice_message = null // When you are not understood by others (replaced with just screeches, hisses, chimpers etc.) var/say_message = null // When you are understood by others. Currently only used by aliens and monkeys in their say_quote procs +//Wizard mode, but can be used in other modes thanks to the brand new "Give Spell" badmin button + var/obj/spell/list/spell_list = list() + //Monkey/infected mode var/list/resistances = list() var/datum/disease/virus = null diff --git a/code/game/gamemodes/wizard/spells.dm b/code/game/gamemodes/wizard/spells.dm index de3d992a451..9fee0a8ab01 100644 --- a/code/game/gamemodes/wizard/spells.dm +++ b/code/game/gamemodes/wizard/spells.dm @@ -566,7 +566,7 @@ H.client.eye = H del(animation) del(holder) - +/* /obj/dummy/spell_jaunt name = "water" icon = 'effects.dmi' @@ -605,7 +605,7 @@ return /obj/dummy/spell_jaunt/bullet_act(blah,blah) return - +*/ //MUTATE /client/proc/mutate() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 992f301bb59..7e70924cf67 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -99,6 +99,7 @@ src.verbs += /obj/admins/proc/vmode //start vote src.verbs += /obj/admins/proc/votekill //abort vote + src.verbs += /client/proc/give_spell src.verbs += /client/proc/cmd_admin_alienize src.verbs += /client/proc/cmd_admin_changelinginize src.verbs += /client/proc/cmd_admin_abominize // -- TLE @@ -222,6 +223,7 @@ src.verbs += /obj/admins/proc/vmode //start vote src.verbs += /obj/admins/proc/votekill //abort vote + src.verbs += /client/proc/give_spell src.verbs += /client/proc/cmd_admin_alienize src.verbs += /client/proc/cmd_admin_changelinginize src.verbs += /client/proc/cmd_admin_abominize // -- TLE @@ -1053,6 +1055,7 @@ src.verbs -= /obj/admins/proc/vmode //start vote src.verbs -= /obj/admins/proc/votekill //abort vote + src.verbs -= /client/proc/give_spell src.verbs -= /client/proc/cmd_admin_alienize src.verbs -= /client/proc/cmd_admin_changelinginize src.verbs -= /client/proc/cmd_admin_abominize // -- TLE @@ -1334,6 +1337,13 @@ runerandom() usr << "[wordtravel] is travel, [wordblood] is blood, [wordjoin] is join, [wordhell] is Hell, [worddestr] is destroy, [wordtech] is technology, [wordself] is self, [wordsee] is see" +/client/proc/give_spell(mob/T as mob in world) // -- Urist + set category = "Fun" + set name = "Give Spell" + set desc = "Gives a spell to a mob." + var/obj/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") in spells + T.spell_list += new S + /client/proc/make_sound(var/obj/O in world) // -- TLE set category = "Special Verbs" set name = "Make Sound" diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index e01dd986bd6..ee5cbb5130b 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -2148,6 +2148,11 @@ note dizziness decrements automatically in the mob's Life() proc. //if (master_controller) // stat(null, "Loop: [master_controller.loop_freq]") + if (src.spell_list.len) + + for(var/obj/spell/S in src.spell_list) + statpanel("Spells","",S) + /client/proc/station_explosion_cinematic(var/derp) if(src.mob) var/mob/M = src.mob diff --git a/tgstation.dme b/tgstation.dme index 8d3b6df6639..edb0889d214 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -13,6 +13,7 @@ #define FILE_DIR "code/ATMOSPHERICS/components/unary" #define FILE_DIR "code/datums" #define FILE_DIR "code/datums/diseases" +#define FILE_DIR "code/datums/spells" #define FILE_DIR "code/defines" #define FILE_DIR "code/defines/area" #define FILE_DIR "code/defines/mob" @@ -102,6 +103,7 @@ #define FILE_DIR "code/unused" #define FILE_DIR "code/WorkInProgress" #define FILE_DIR "code/WorkInProgress/computer2" +#define FILE_DIR "code/WorkInProgress/experimental" #define FILE_DIR "code/WorkInProgress/optics" #define FILE_DIR "code/WorkInProgress/pda2" #define FILE_DIR "code/WorkInProgress/recycling" @@ -176,6 +178,7 @@ #include "code\datums\modules.dm" #include "code\datums\organs.dm" #include "code\datums\shuttle_controller.dm" +#include "code\datums\spell.dm" #include "code\datums\sun.dm" #include "code\datums\vote.dm" #include "code\datums\diseases\alien_embryo.dm" @@ -194,6 +197,18 @@ #include "code\datums\diseases\robotic_transformation.dm" #include "code\datums\diseases\wizarditis.dm" #include "code\datums\diseases\xeno_transformation.dm" +#include "code\datums\spells\blind.dm" +#include "code\datums\spells\blink.dm" +#include "code\datums\spells\body_swap.dm" +#include "code\datums\spells\disable_tech.dm" +#include "code\datums\spells\disintegrate.dm" +#include "code\datums\spells\ethereal_jaunt.dm" +#include "code\datums\spells\fireball.dm" +#include "code\datums\spells\forcewall.dm" +#include "code\datums\spells\knock.dm" +#include "code\datums\spells\magic_missile.dm" +#include "code\datums\spells\mutate.dm" +#include "code\datums\spells\teleport.dm" #include "code\defines\atom.dm" #include "code\defines\client.dm" #include "code\defines\global.dm"