diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index eaff61e52f..a9f76f7f9d 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -1,469 +1,469 @@ -/* - Click code cleanup - ~Sayu -*/ - -// 1 decisecond click delay (above and beyond mob/next_move) -//This is mainly modified by click code, to modify click delays elsewhere, use next_move and changeNext_move() -/mob/var/next_click = 0 - -// THESE DO NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK -/mob/var/next_move_adjust = 0 //Amount to adjust action/click delays by, + or - -/mob/var/next_move_modifier = 1 //Value to multiply action/click delays by - - -//Delays the mob's next click/action by num deciseconds -// eg: 10-3 = 7 deciseconds of delay -// eg: 10*0.5 = 5 deciseconds of delay -// DOES NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK - -/mob/proc/changeNext_move(num) - next_move = world.time + ((num+next_move_adjust)*next_move_modifier) - - -/* - Before anything else, defer these calls to a per-mobtype handler. This allows us to - remove istype() spaghetti code, but requires the addition of other handler procs to simplify it. - - Alternately, you could hardcode every mob's variation in a flat ClickOn() proc; however, - that's a lot of code duplication and is hard to maintain. - - Note that this proc can be overridden, and is in the case of screen objects. -*/ -/atom/Click(location,control,params) - if(initialized) - usr.ClickOn(src, params) - -/atom/DblClick(location,control,params) - if(initialized) - usr.DblClickOn(src,params) - -/atom/MouseWheel(delta_x,delta_y,location,control,params) - if(initialized) - usr.MouseWheelOn(src, delta_x, delta_y, params) - -/* - Standard mob ClickOn() - Handles exceptions: Buildmode, middle click, modified clicks, mech actions - - After that, mostly just check your state, check whether you're holding an item, - check whether you're adjacent to the target, then pass off the click to whoever - is recieving it. - The most common are: - * mob/UnarmedAttack(atom,adjacent) - used here only when adjacent, with no item in hand; in the case of humans, checks gloves - * atom/attackby(item,user) - used only when adjacent - * item/afterattack(atom,user,adjacent,params) - used both ranged and adjacent - * mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed -*/ -/mob/proc/ClickOn( atom/A, params ) - if(world.time <= next_click) - return - next_click = world.time + 1 - - if(client && client.click_intercept) - if(call(client.click_intercept, "InterceptClickOn")(src, params, A)) - return - - var/list/modifiers = params2list(params) - if(modifiers["shift"] && modifiers["middle"]) - ShiftMiddleClickOn(A) - return - if(modifiers["shift"] && modifiers["ctrl"]) - CtrlShiftClickOn(A) - return - if(modifiers["middle"]) - MiddleClickOn(A) - return - if(modifiers["shift"]) - ShiftClickOn(A) - return - if(modifiers["alt"]) // alt and alt-gr (rightalt) - AltClickOn(A) - return - if(modifiers["ctrl"]) - CtrlClickOn(A) - return - - if(incapacitated(ignore_restraints = 1)) - return - - face_atom(A) - - if(next_move > world.time) // in the year 2000... - return - - if(A.IsObscured()) - return - - if(istype(loc,/obj/mecha)) - var/obj/mecha/M = loc - return M.click_action(A,src,params) - - if(restrained()) - changeNext_move(CLICK_CD_HANDCUFFED) //Doing shit in cuffs shall be vey slow - RestrainedClickOn(A) - return - - if(in_throw_mode) - throw_item(A) - return - - var/obj/item/W = get_active_held_item() - - if(W == A) - W.attack_self(src) - update_inv_hands() - return - - //These are always reachable. - //User itself, current loc, and user inventory - if(DirectAccess(A)) - if(W) - melee_item_attack_chain(src,W,A,params) - else - if(ismob(A)) - changeNext_move(CLICK_CD_MELEE) - UnarmedAttack(A) - return - - //Can't reach anything else in lockers or other weirdness - if(!loc.AllowClick()) - return - - //Standard reach turf to turf or reaching inside storage - if(CanReach(A,W)) - if(W) - melee_item_attack_chain(src,W,A,params) - else - if(ismob(A)) - changeNext_move(CLICK_CD_MELEE) - UnarmedAttack(A,1) - else - if(W) - W.afterattack(A,src,0,params) - else - RangedAttack(A,params) - -//Is the atom obscured by a PREVENT_CLICK_UNDER object above it -/atom/proc/IsObscured() - if(!isturf(loc)) //This only makes sense for things directly on turfs for now - return FALSE - var/turf/T = get_turf_pixel(src) - if(!T) - return FALSE - for(var/atom/movable/AM in T) - if(AM.flags & PREVENT_CLICK_UNDER && AM.density && AM.layer > layer) - return TRUE - return FALSE - -/turf/IsObscured() - for(var/atom/movable/AM in src) - if(AM.flags & PREVENT_CLICK_UNDER && AM.density) - return TRUE - return FALSE - -/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE) - if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms - if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks - return TRUE - else - //Things inside storage insde another storage - //Eg Contents of a box in a backpack - var/atom/outer_storage = get_atom_on_turf(target) - if(outer_storage == target) //whatever that is we don't want infinite loop. - return FALSE - if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH)) - return TRUE - return FALSE - -//Can [target] in this container be reached by [user], can't be more than [depth] levels deep -/atom/proc/CanReachStorage(atom/target,user,depth) - return FALSE - -/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth) - while(target && depth > 0) - target = target.loc - depth-- - if(target == src) - return TRUE - return FALSE - -/atom/movable/proc/DirectAccess(atom/target) - if(target == src) - return TRUE - if(target == loc) - return TRUE - -/mob/DirectAccess(atom/target) - if(..()) - return TRUE - if(target in contents) //This could probably use moving down and restricting to inventory only - return TRUE - return FALSE - -/mob/living/DirectAccess(atom/target) - if(..()) //Lightweight checks first - return TRUE - if(target in GetAllContents()) - return TRUE - -/atom/proc/AllowClick() - return FALSE - -/turf/AllowClick() - return TRUE - -/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach) - if(!here || !there) - return - switch(reach) - if(0) - return FALSE - if(1) - return FALSE //here.Adjacent(there) - if(2 to INFINITY) - var/obj/dummy = new(get_turf(here)) - dummy.pass_flags |= PASSTABLE - dummy.invisibility = INVISIBILITY_ABSTRACT - for(var/i in 1 to reach) //Limit it to that many tries - var/turf/T = get_step(dummy, get_dir(dummy, there)) - if(dummy.CanReach(there)) - qdel(dummy) - return TRUE - if(!dummy.Move(T)) //we're blocked! - qdel(dummy) - return - qdel(dummy) - -// Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click) -/mob/proc/DblClickOn(atom/A, params) - return - - -/* - Translates into attack_hand, etc. - - Note: proximity_flag here is used to distinguish between normal usage (flag=1), - and usage when clicking on things telekinetically (flag=0). This proc will - not be called at ranged except with telekinesis. - - proximity_flag is not currently passed to attack_hand, and is instead used - in human click code to allow glove touches only at melee range. -*/ -/mob/proc/UnarmedAttack(atom/A, proximity_flag) - if(ismob(A)) - changeNext_move(CLICK_CD_MELEE) - return - -/* - Ranged unarmed attack: - - This currently is just a default for all mobs, involving - laser eyes and telekinesis. You could easily add exceptions - for things like ranged glove touches, spitting alien acid/neurotoxin, - animals lunging, etc. -*/ -/mob/proc/RangedAttack(atom/A, params) -/* - Restrained ClickOn - - Used when you are handcuffed and click things. - Not currently used by anything but could easily be. -*/ -/mob/proc/RestrainedClickOn(atom/A) - return - -/* - Middle click - Only used for swapping hands -*/ -/mob/proc/MiddleClickOn(atom/A) - return - -/mob/living/carbon/MiddleClickOn(atom/A) - if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src)) - next_click = world.time + 5 - mind.changeling.chosen_sting.try_to_sting(src, A) - else - swap_hand() - -/mob/living/simple_animal/drone/MiddleClickOn(atom/A) - swap_hand() - -// In case of use break glass -/* -/atom/proc/MiddleClick(mob/M as mob) - return -*/ - -/* - Shift click - For most mobs, examine. - This is overridden in ai.dm -*/ -/mob/proc/ShiftClickOn(atom/A) - A.ShiftClick(src) - return -/atom/proc/ShiftClick(mob/user) - if(user.client && user.client.eye == user || user.client.eye == user.loc) - user.examinate(src) - return - -/* - Ctrl click - For most objects, pull -*/ - -/mob/proc/CtrlClickOn(atom/A) - A.CtrlClick(src) - return - -/atom/proc/CtrlClick(mob/user) - var/mob/living/ML = user - if(istype(ML)) - ML.pulled(src) - -/mob/living/carbon/human/CtrlClick(mob/user) - if(ishuman(user) && Adjacent(user)) - if(world.time < user.next_move) - return FALSE - var/mob/living/carbon/human/H = user - H.dna.species.grab(H, src, H.martial_art) - H.changeNext_move(CLICK_CD_MELEE) - else - ..() -/* - Alt click - Unused except for AI -*/ -/mob/proc/AltClickOn(atom/A) - A.AltClick(src) - return - -/mob/living/carbon/AltClickOn(atom/A) - if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src)) - next_click = world.time + 5 - mind.changeling.chosen_sting.try_to_sting(src, A) - else - ..() - -/atom/proc/AltClick(mob/user) - var/turf/T = get_turf(src) - if(T && user.TurfAdjacent(T)) - if(user.listed_turf == T) - user.listed_turf = null - else - user.listed_turf = T - user.client.statpanel = T.name - return - -/mob/proc/TurfAdjacent(turf/T) - return T.Adjacent(src) - -/* - Control+Shift click - Unused except for AI -*/ -/mob/proc/CtrlShiftClickOn(atom/A) - A.CtrlShiftClick(src) - return - -/mob/proc/ShiftMiddleClickOn(atom/A) - src.pointed(A) - return - -/atom/proc/CtrlShiftClick(mob/user) - return - -/* - Misc helpers - - Laser Eyes: as the name implies, handles this since nothing else does currently - face_atom: turns the mob towards what you clicked on -*/ -/mob/proc/LaserEyes(atom/A) - return - -/mob/living/LaserEyes(atom/A) - changeNext_move(CLICK_CD_RANGE) - var/turf/T = get_turf(src) - var/turf/U = get_turf(A) - - var/obj/item/projectile/beam/LE = new /obj/item/projectile/beam( loc ) - LE.icon = 'icons/effects/genetics.dmi' - LE.icon_state = "eyelasers" - playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1) - - LE.firer = src - LE.def_zone = get_organ_target() - LE.original = A - LE.current = T - LE.yo = U.y - T.y - LE.xo = U.x - T.x - LE.fire() - -// Simple helper to face what you clicked on, in case it should be needed in more than one place -/mob/proc/face_atom(atom/A) - if( buckled || stat != CONSCIOUS || !A || !x || !y || !A.x || !A.y ) - return - var/dx = A.x - x - var/dy = A.y - y - if(!dx && !dy) // Wall items are graphically shifted but on the floor - if(A.pixel_y > 16) - setDir(NORTH) - else if(A.pixel_y < -16) - setDir(SOUTH) - else if(A.pixel_x > 16) - setDir(EAST) - else if(A.pixel_x < -16) - setDir(WEST) - return - - if(abs(dx) < abs(dy)) - if(dy > 0) - setDir(NORTH) - else - setDir(SOUTH) - else - if(dx > 0) - setDir(EAST) - else - setDir(WEST) - -/obj/screen/click_catcher - icon = 'icons/mob/screen_gen.dmi' - icon_state = "click_catcher" - plane = CLICKCATCHER_PLANE - mouse_opacity = 2 - screen_loc = "CENTER" - -/obj/screen/click_catcher/New() - ..() - transform = matrix(200, 0, 0, 0, 200, 0) - -/obj/screen/click_catcher/Click(location, control, params) - var/list/modifiers = params2list(params) - if(modifiers["middle"] && istype(usr, /mob/living/carbon)) - var/mob/living/carbon/C = usr - C.swap_hand() - else - var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr)) - if(T) - T.Click(location, control, params) - . = 1 - - -/* MouseWheelOn */ - -/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params) - return - -/mob/dead/observer/MouseWheelOn(atom/A, delta_x, delta_y, params) - var/list/modifier = params2list(params) - if(modifier["shift"]) - var/view = 0 - if(delta_y > 0) - view = -1 - else - view = 1 - add_view_range(view) +/* + Click code cleanup + ~Sayu +*/ + +// 1 decisecond click delay (above and beyond mob/next_move) +//This is mainly modified by click code, to modify click delays elsewhere, use next_move and changeNext_move() +/mob/var/next_click = 0 + +// THESE DO NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK +/mob/var/next_move_adjust = 0 //Amount to adjust action/click delays by, + or - +/mob/var/next_move_modifier = 1 //Value to multiply action/click delays by + + +//Delays the mob's next click/action by num deciseconds +// eg: 10-3 = 7 deciseconds of delay +// eg: 10*0.5 = 5 deciseconds of delay +// DOES NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK + +/mob/proc/changeNext_move(num) + next_move = world.time + ((num+next_move_adjust)*next_move_modifier) + + +/* + Before anything else, defer these calls to a per-mobtype handler. This allows us to + remove istype() spaghetti code, but requires the addition of other handler procs to simplify it. + + Alternately, you could hardcode every mob's variation in a flat ClickOn() proc; however, + that's a lot of code duplication and is hard to maintain. + + Note that this proc can be overridden, and is in the case of screen objects. +*/ +/atom/Click(location,control,params) + if(initialized) + usr.ClickOn(src, params) + +/atom/DblClick(location,control,params) + if(initialized) + usr.DblClickOn(src,params) + +/atom/MouseWheel(delta_x,delta_y,location,control,params) + if(initialized) + usr.MouseWheelOn(src, delta_x, delta_y, params) + +/* + Standard mob ClickOn() + Handles exceptions: Buildmode, middle click, modified clicks, mech actions + + After that, mostly just check your state, check whether you're holding an item, + check whether you're adjacent to the target, then pass off the click to whoever + is recieving it. + The most common are: + * mob/UnarmedAttack(atom,adjacent) - used here only when adjacent, with no item in hand; in the case of humans, checks gloves + * atom/attackby(item,user) - used only when adjacent + * item/afterattack(atom,user,adjacent,params) - used both ranged and adjacent + * mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed +*/ +/mob/proc/ClickOn( atom/A, params ) + if(world.time <= next_click) + return + next_click = world.time + 1 + + if(client && client.click_intercept) + if(call(client.click_intercept, "InterceptClickOn")(src, params, A)) + return + + var/list/modifiers = params2list(params) + if(modifiers["shift"] && modifiers["middle"]) + ShiftMiddleClickOn(A) + return + if(modifiers["shift"] && modifiers["ctrl"]) + CtrlShiftClickOn(A) + return + if(modifiers["middle"]) + MiddleClickOn(A) + return + if(modifiers["shift"]) + ShiftClickOn(A) + return + if(modifiers["alt"]) // alt and alt-gr (rightalt) + AltClickOn(A) + return + if(modifiers["ctrl"]) + CtrlClickOn(A) + return + + if(incapacitated(ignore_restraints = 1)) + return + + face_atom(A) + + if(next_move > world.time) // in the year 2000... + return + + if(A.IsObscured()) + return + + if(istype(loc,/obj/mecha)) + var/obj/mecha/M = loc + return M.click_action(A,src,params) + + if(restrained()) + changeNext_move(CLICK_CD_HANDCUFFED) //Doing shit in cuffs shall be vey slow + RestrainedClickOn(A) + return + + if(in_throw_mode) + throw_item(A) + return + + var/obj/item/W = get_active_held_item() + + if(W == A) + W.attack_self(src) + update_inv_hands() + return + + //These are always reachable. + //User itself, current loc, and user inventory + if(DirectAccess(A)) + if(W) + melee_item_attack_chain(src,W,A,params) + else + if(ismob(A)) + changeNext_move(CLICK_CD_MELEE) + UnarmedAttack(A) + return + + //Can't reach anything else in lockers or other weirdness + if(!loc.AllowClick()) + return + + //Standard reach turf to turf or reaching inside storage + if(CanReach(A,W)) + if(W) + melee_item_attack_chain(src,W,A,params) + else + if(ismob(A)) + changeNext_move(CLICK_CD_MELEE) + UnarmedAttack(A,1) + else + if(W) + W.afterattack(A,src,0,params) + else + RangedAttack(A,params) + +//Is the atom obscured by a PREVENT_CLICK_UNDER object above it +/atom/proc/IsObscured() + if(!isturf(loc)) //This only makes sense for things directly on turfs for now + return FALSE + var/turf/T = get_turf_pixel(src) + if(!T) + return FALSE + for(var/atom/movable/AM in T) + if(AM.flags & PREVENT_CLICK_UNDER && AM.density && AM.layer > layer) + return TRUE + return FALSE + +/turf/IsObscured() + for(var/atom/movable/AM in src) + if(AM.flags & PREVENT_CLICK_UNDER && AM.density) + return TRUE + return FALSE + +/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE) + if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms + if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks + return TRUE + else + //Things inside storage insde another storage + //Eg Contents of a box in a backpack + var/atom/outer_storage = get_atom_on_turf(target) + if(outer_storage == target) //whatever that is we don't want infinite loop. + return FALSE + if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH)) + return TRUE + return FALSE + +//Can [target] in this container be reached by [user], can't be more than [depth] levels deep +/atom/proc/CanReachStorage(atom/target,user,depth) + return FALSE + +/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth) + while(target && depth > 0) + target = target.loc + depth-- + if(target == src) + return TRUE + return FALSE + +/atom/movable/proc/DirectAccess(atom/target) + if(target == src) + return TRUE + if(target == loc) + return TRUE + +/mob/DirectAccess(atom/target) + if(..()) + return TRUE + if(target in contents) //This could probably use moving down and restricting to inventory only + return TRUE + return FALSE + +/mob/living/DirectAccess(atom/target) + if(..()) //Lightweight checks first + return TRUE + if(target in GetAllContents()) + return TRUE + +/atom/proc/AllowClick() + return FALSE + +/turf/AllowClick() + return TRUE + +/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach) + if(!here || !there) + return + switch(reach) + if(0) + return FALSE + if(1) + return FALSE //here.Adjacent(there) + if(2 to INFINITY) + var/obj/dummy = new(get_turf(here)) + dummy.pass_flags |= PASSTABLE + dummy.invisibility = INVISIBILITY_ABSTRACT + for(var/i in 1 to reach) //Limit it to that many tries + var/turf/T = get_step(dummy, get_dir(dummy, there)) + if(dummy.CanReach(there)) + qdel(dummy) + return TRUE + if(!dummy.Move(T)) //we're blocked! + qdel(dummy) + return + qdel(dummy) + +// Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click) +/mob/proc/DblClickOn(atom/A, params) + return + + +/* + Translates into attack_hand, etc. + + Note: proximity_flag here is used to distinguish between normal usage (flag=1), + and usage when clicking on things telekinetically (flag=0). This proc will + not be called at ranged except with telekinesis. + + proximity_flag is not currently passed to attack_hand, and is instead used + in human click code to allow glove touches only at melee range. +*/ +/mob/proc/UnarmedAttack(atom/A, proximity_flag) + if(ismob(A)) + changeNext_move(CLICK_CD_MELEE) + return + +/* + Ranged unarmed attack: + + This currently is just a default for all mobs, involving + laser eyes and telekinesis. You could easily add exceptions + for things like ranged glove touches, spitting alien acid/neurotoxin, + animals lunging, etc. +*/ +/mob/proc/RangedAttack(atom/A, params) +/* + Restrained ClickOn + + Used when you are handcuffed and click things. + Not currently used by anything but could easily be. +*/ +/mob/proc/RestrainedClickOn(atom/A) + return + +/* + Middle click + Only used for swapping hands +*/ +/mob/proc/MiddleClickOn(atom/A) + return + +/mob/living/carbon/MiddleClickOn(atom/A) + if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src)) + next_click = world.time + 5 + mind.changeling.chosen_sting.try_to_sting(src, A) + else + swap_hand() + +/mob/living/simple_animal/drone/MiddleClickOn(atom/A) + swap_hand() + +// In case of use break glass +/* +/atom/proc/MiddleClick(mob/M as mob) + return +*/ + +/* + Shift click + For most mobs, examine. + This is overridden in ai.dm +*/ +/mob/proc/ShiftClickOn(atom/A) + A.ShiftClick(src) + return +/atom/proc/ShiftClick(mob/user) + if(user.client && user.client.eye == user || user.client.eye == user.loc) + user.examinate(src) + return + +/* + Ctrl click + For most objects, pull +*/ + +/mob/proc/CtrlClickOn(atom/A) + A.CtrlClick(src) + return + +/atom/proc/CtrlClick(mob/user) + var/mob/living/ML = user + if(istype(ML)) + ML.pulled(src) + +/mob/living/carbon/human/CtrlClick(mob/user) + if(ishuman(user) && Adjacent(user)) + if(world.time < user.next_move) + return FALSE + var/mob/living/carbon/human/H = user + H.dna.species.grab(H, src, H.mind.martial_art) + H.changeNext_move(CLICK_CD_MELEE) + else + ..() +/* + Alt click + Unused except for AI +*/ +/mob/proc/AltClickOn(atom/A) + A.AltClick(src) + return + +/mob/living/carbon/AltClickOn(atom/A) + if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src)) + next_click = world.time + 5 + mind.changeling.chosen_sting.try_to_sting(src, A) + else + ..() + +/atom/proc/AltClick(mob/user) + var/turf/T = get_turf(src) + if(T && user.TurfAdjacent(T)) + if(user.listed_turf == T) + user.listed_turf = null + else + user.listed_turf = T + user.client.statpanel = T.name + return + +/mob/proc/TurfAdjacent(turf/T) + return T.Adjacent(src) + +/* + Control+Shift click + Unused except for AI +*/ +/mob/proc/CtrlShiftClickOn(atom/A) + A.CtrlShiftClick(src) + return + +/mob/proc/ShiftMiddleClickOn(atom/A) + src.pointed(A) + return + +/atom/proc/CtrlShiftClick(mob/user) + return + +/* + Misc helpers + + Laser Eyes: as the name implies, handles this since nothing else does currently + face_atom: turns the mob towards what you clicked on +*/ +/mob/proc/LaserEyes(atom/A) + return + +/mob/living/LaserEyes(atom/A) + changeNext_move(CLICK_CD_RANGE) + var/turf/T = get_turf(src) + var/turf/U = get_turf(A) + + var/obj/item/projectile/beam/LE = new /obj/item/projectile/beam( loc ) + LE.icon = 'icons/effects/genetics.dmi' + LE.icon_state = "eyelasers" + playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1) + + LE.firer = src + LE.def_zone = get_organ_target() + LE.original = A + LE.current = T + LE.yo = U.y - T.y + LE.xo = U.x - T.x + LE.fire() + +// Simple helper to face what you clicked on, in case it should be needed in more than one place +/mob/proc/face_atom(atom/A) + if( buckled || stat != CONSCIOUS || !A || !x || !y || !A.x || !A.y ) + return + var/dx = A.x - x + var/dy = A.y - y + if(!dx && !dy) // Wall items are graphically shifted but on the floor + if(A.pixel_y > 16) + setDir(NORTH) + else if(A.pixel_y < -16) + setDir(SOUTH) + else if(A.pixel_x > 16) + setDir(EAST) + else if(A.pixel_x < -16) + setDir(WEST) + return + + if(abs(dx) < abs(dy)) + if(dy > 0) + setDir(NORTH) + else + setDir(SOUTH) + else + if(dx > 0) + setDir(EAST) + else + setDir(WEST) + +/obj/screen/click_catcher + icon = 'icons/mob/screen_gen.dmi' + icon_state = "click_catcher" + plane = CLICKCATCHER_PLANE + mouse_opacity = 2 + screen_loc = "CENTER" + +/obj/screen/click_catcher/New() + ..() + transform = matrix(200, 0, 0, 0, 200, 0) + +/obj/screen/click_catcher/Click(location, control, params) + var/list/modifiers = params2list(params) + if(modifiers["middle"] && istype(usr, /mob/living/carbon)) + var/mob/living/carbon/C = usr + C.swap_hand() + else + var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr)) + if(T) + T.Click(location, control, params) + . = 1 + + +/* MouseWheelOn */ + +/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params) + return + +/mob/dead/observer/MouseWheelOn(atom/A, delta_x, delta_y, params) + var/list/modifier = params2list(params) + if(modifier["shift"]) + var/view = 0 + if(delta_y > 0) + view = -1 + else + view = 1 + add_view_range(view) diff --git a/code/datums/martial.dm b/code/datums/martial.dm index b20bf556d5..fa02a2f092 100644 --- a/code/datums/martial.dm +++ b/code/datums/martial.dm @@ -79,681 +79,17 @@ /datum/martial_art/proc/teach(mob/living/carbon/human/H,make_temporary=0) if(make_temporary) temporary = 1 - if(temporary && H.martial_art) - if(!H.martial_art.allow_temp_override) + if(temporary && H.mind.martial_art) + if(!H.mind.martial_art.allow_temp_override) return - base = H.martial_art + base = H.mind.martial_art if(help_verb) H.verbs += help_verb - H.martial_art = src + H.mind.martial_art = src /datum/martial_art/proc/remove(mob/living/carbon/human/H) - if(H.martial_art != src) + if(H.mind.martial_art != src) return - H.martial_art = base + H.mind.martial_art = base if(help_verb) H.verbs -= help_verb - -/datum/martial_art/boxing - name = "Boxing" - -/datum/martial_art/boxing/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - to_chat(A, "Can't disarm while boxing!") - return 1 - -/datum/martial_art/boxing/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - to_chat(A, "Can't grab while boxing!") - return 1 - -/datum/martial_art/boxing/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - - var/atk_verb = pick("left hook","right hook","straight punch") - - var/damage = rand(5, 8) + A.dna.species.punchdamagelow - if(!damage) - playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1) - D.visible_message("[A] has attempted to [atk_verb] [D]!", \ - "[A] has attempted to [atk_verb] [D]!", null, COMBAT_MESSAGE_RANGE) - add_logs(A, D, "attempted to hit", atk_verb) - return 0 - - - var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) - var/armor_block = D.run_armor_check(affecting, "melee") - - playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1) - - D.visible_message("[A] has [atk_verb]ed [D]!", \ - "[A] has [atk_verb]ed [D]!", null, COMBAT_MESSAGE_RANGE) - - D.apply_damage(damage, STAMINA, affecting, armor_block) - add_logs(A, D, "punched (boxing) ") - if(D.getStaminaLoss() > 50) - var/knockout_prob = D.getStaminaLoss() + rand(-15,15) - if((D.stat != DEAD) && prob(knockout_prob)) - D.visible_message("[A] has knocked [D] out with a haymaker!", \ - "[A] has knocked [D] out with a haymaker!") - D.apply_effect(10,WEAKEN,armor_block) - D.SetSleeping(5) - D.forcesay(GLOB.hit_appends) - add_logs(A, D, "knocked out (boxing) ") - else if(D.lying) - D.forcesay(GLOB.hit_appends) - return 1 - -/mob/living/carbon/human/proc/wrestling_help() - set name = "Recall Teachings" - set desc = "Remember how to wrestle." - set category = "Wrestling" - - to_chat(usr, "You flex your muscles and have a revelation...") - to_chat(usr, "Clinch: Grab. Passively gives you a chance to immediately aggressively grab someone. Not always successful.") - to_chat(usr, "Suplex: Disarm someone you are grabbing. Suplexes your target to the floor. Greatly injures them and leaves both you and your target on the floor.") - to_chat(usr, "Advanced grab: Grab. Passively causes stamina damage when grabbing someone.") - -#define TORNADO_COMBO "HHD" -#define THROWBACK_COMBO "DHD" -#define PLASMA_COMBO "HDDDH" - -/datum/martial_art/plasma_fist - name = "Plasma Fist" - help_verb = /mob/living/carbon/human/proc/plasma_fist_help - - -/datum/martial_art/plasma_fist/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(findtext(streak,TORNADO_COMBO)) - streak = "" - Tornado(A,D) - return 1 - if(findtext(streak,THROWBACK_COMBO)) - streak = "" - Throwback(A,D) - return 1 - if(findtext(streak,PLASMA_COMBO)) - streak = "" - Plasma(A,D) - return 1 - return 0 - -/datum/martial_art/plasma_fist/proc/TornadoAnimate(mob/living/carbon/human/A) - set waitfor = FALSE - for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH)) - if(!A) - break - A.setDir(i) - playsound(A.loc, 'sound/weapons/punch1.ogg', 15, 1, -1) - sleep(1) - -/datum/martial_art/plasma_fist/proc/Tornado(mob/living/carbon/human/A, mob/living/carbon/human/D) - A.say("TORNADO SWEEP!") - TornadoAnimate(A) - var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null) - var/list/turfs = list() - for(var/turf/T in range(1,A)) - turfs.Add(T) - R.cast(turfs) - add_logs(A, D, "tornado sweeped(Plasma Fist)") - return - -/datum/martial_art/plasma_fist/proc/Throwback(mob/living/carbon/human/A, mob/living/carbon/human/D) - D.visible_message("[A] has hit [D] with Plasma Punch!", \ - "[A] has hit [D] with Plasma Punch!") - playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) - var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A))) - D.throw_at(throw_target, 200, 4,A) - A.say("HYAH!") - add_logs(A, D, "threw back (Plasma Fist)") - return - -/datum/martial_art/plasma_fist/proc/Plasma(mob/living/carbon/human/A, mob/living/carbon/human/D) - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) - A.say("PLASMA FIST!") - D.visible_message("[A] has hit [D] with THE PLASMA FIST TECHNIQUE!", \ - "[A] has hit [D] with THE PLASMA FIST TECHNIQUE!") - D.gib() - add_logs(A, D, "gibbed (Plasma Fist)") - return - -/datum/martial_art/plasma_fist/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("H",D) - if(check_streak(A,D)) - return 1 - basic_hit(A,D) - return 1 - -/datum/martial_art/plasma_fist/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("D",D) - if(check_streak(A,D)) - return 1 - basic_hit(A,D) - return 1 - -/datum/martial_art/plasma_fist/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("G",D) - if(check_streak(A,D)) - return 1 - basic_hit(A,D) - return 1 - -/mob/living/carbon/human/proc/plasma_fist_help() - set name = "Recall Teachings" - set desc = "Remember the martial techniques of the Plasma Fist." - set category = "Plasma Fist" - - to_chat(usr, "You clench your fists and have a flashback of knowledge...") - to_chat(usr, "Tornado Sweep: Harm Harm Disarm. Repulses target and everyone back.") - to_chat(usr, "Throwback: Disarm Harm Disarm. Throws the target and an item at them.") - to_chat(usr, "The Plasma Fist: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body.") - -//Used by the gang of the same name. Uses combos. Basic attacks bypass armor and never miss -#define WRIST_WRENCH_COMBO "DD" -#define BACK_KICK_COMBO "HG" -#define STOMACH_KNEE_COMBO "GH" -#define HEAD_KICK_COMBO "DHH" -#define ELBOW_DROP_COMBO "HDHDH" -/datum/martial_art/the_sleeping_carp - name = "The Sleeping Carp" - deflection_chance = 100 - no_guns = TRUE - allow_temp_override = FALSE - help_verb = /mob/living/carbon/human/proc/sleeping_carp_help - -/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(findtext(streak,WRIST_WRENCH_COMBO)) - streak = "" - wristWrench(A,D) - return 1 - if(findtext(streak,BACK_KICK_COMBO)) - streak = "" - backKick(A,D) - return 1 - if(findtext(streak,STOMACH_KNEE_COMBO)) - streak = "" - kneeStomach(A,D) - return 1 - if(findtext(streak,HEAD_KICK_COMBO)) - streak = "" - headKick(A,D) - return 1 - if(findtext(streak,ELBOW_DROP_COMBO)) - streak = "" - elbowDrop(A,D) - return 1 - return 0 - -/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat && !D.stunned && !D.weakened) - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - D.visible_message("[A] grabs [D]'s wrist and wrenches it sideways!", \ - "[A] grabs your wrist and violently wrenches it to the side!") - playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - D.emote("scream") - D.drop_item() - D.apply_damage(5, BRUTE, pick("l_arm", "r_arm")) - D.Stun(3) - return 1 - add_logs(A, D, "wrist wrenched (Sleeping Carp)") - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(A.dir == D.dir && !D.stat && !D.weakened) - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - D.visible_message("[A] kicks [D] in the back!", \ - "[A] kicks you in the back, making you stumble and fall!") - step_to(D,get_step(D,D.dir),1) - D.Weaken(4) - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - return 1 - add_logs(A, D, "back-kicked (Sleeping Carp)") - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat && !D.weakened) - A.do_attack_animation(D, ATTACK_EFFECT_KICK) - D.visible_message("[A] knees [D] in the stomach!", \ - "[A] winds you with a knee in the stomach!") - D.audible_message("[D] gags!") - D.losebreath += 3 - D.Stun(2) - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - return 1 - add_logs(A, D, "stomach kneed (Sleeping Carp)") - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat && !D.weakened) - A.do_attack_animation(D, ATTACK_EFFECT_KICK) - D.visible_message("[A] kicks [D] in the head!", \ - "[A] kicks you in the jaw!") - D.apply_damage(20, BRUTE, "head") - D.drop_item() - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - D.Stun(4) - return 1 - add_logs(A, D, "head kicked (Sleeping Carp)") - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(D.weakened || D.resting || D.stat) - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - D.visible_message("[A] elbow drops [D]!", \ - "[A] piledrives you with their elbow!") - if(D.stat) - D.death() //FINISH HIM! - D.apply_damage(50, BRUTE, "chest") - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1) - return 1 - add_logs(A, D, "elbow dropped (Sleeping Carp)") - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("G",D) - if(check_streak(A,D)) - return 1 - if(A.grab_state >= GRAB_AGGRESSIVE) - D.grabbedby(A, 1) - else - A.start_pulling(D, 1) - if(A.pulling) - D.drop_all_held_items() - D.stop_pulling() - if(A.a_intent == INTENT_GRAB) - add_logs(A, D, "grabbed", addition="aggressively") - A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab - else - add_logs(A, D, "grabbed", addition="passively") - A.grab_state = GRAB_PASSIVE - return 1 - -/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("H",D) - if(check_streak(A,D)) - return 1 - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams") - D.visible_message("[A] [atk_verb] [D]!", \ - "[A] [atk_verb] you!") - D.apply_damage(rand(10,15), BRUTE) - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, 1, -1) - if(prob(D.getBruteLoss()) && !D.lying) - D.visible_message("[D] stumbles and falls!", "The blow sends you to the ground!") - D.Weaken(4) - add_logs(A, D, "[atk_verb] (Sleeping Carp)") - return 1 - - -/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("D",D) - if(check_streak(A,D)) - return 1 - return ..() - -/mob/living/carbon/human/proc/sleeping_carp_help() - set name = "Recall Teachings" - set desc = "Remember the martial techniques of the Sleeping Carp clan." - set category = "Sleeping Carp" - - to_chat(usr, "You retreat inward and recall the teachings of the Sleeping Carp...") - - to_chat(usr, "Wrist Wrench: Disarm Disarm. Forces opponent to drop item in hand.") - to_chat(usr, "Back Kick: Harm Grab. Opponent must be facing away. Knocks down.") - to_chat(usr, "Stomach Knee: Grab Harm. Knocks the wind out of opponent and stuns.") - to_chat(usr, "Head Kick: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.") - to_chat(usr, "Elbow Drop: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.") - -//CQC -#define SLAM_COMBO "GH" -#define KICK_COMBO "HH" -#define RESTRAIN_COMBO "GG" -#define PRESSURE_COMBO "DG" -#define CONSECUTIVE_COMBO "DDH" -/datum/martial_art/cqc - name = "CQC" - help_verb = /mob/living/carbon/human/proc/CQC_help - block_chance = 75 - -/datum/martial_art/cqc/proc/drop_restraining() - restraining = 0 - -/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(findtext(streak,SLAM_COMBO)) - streak = "" - Slam(A,D) - return 1 - if(findtext(streak,KICK_COMBO)) - streak = "" - Kick(A,D) - return 1 - if(findtext(streak,RESTRAIN_COMBO)) - streak = "" - Restrain(A,D) - return 1 - if(findtext(streak,PRESSURE_COMBO)) - streak = "" - Pressure(A,D) - return 1 - if(findtext(streak,CONSECUTIVE_COMBO)) - streak = "" - Consecutive(A,D) - return 0 - -/datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat || !D.weakened) - D.visible_message("[A] slams [D] into the ground!", \ - "[A] slams you into the ground!") - playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1) - D.apply_damage(10, BRUTE) - D.Weaken(6) - add_logs(A, D, "cqc slammed") - return 1 - -/datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat || !D.weakened) - D.visible_message("[A] kicks [D] back!", \ - "[A] kicks you back!") - playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) - var/atom/throw_target = get_edge_target_turf(D, A.dir) - D.throw_at(throw_target, 1, 14, A) - D.apply_damage(10, BRUTE) - add_logs(A, D, "cqc kicked") - if(D.weakened && !D.stat) - D.visible_message("[A] kicks [D]'s head, knocking them out!", \ - "[A] kicks your head, knocking you out!") - playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1) - D.SetSleeping(15) - D.adjustBrainLoss(25) - return 1 - -/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D) - D.visible_message("[A] forces their arm on [D]'s neck!") - D.adjustStaminaLoss(60) - playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) - return 1 - -/datum/martial_art/cqc/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(restraining) - return - if(!D.stat) - D.visible_message("[A] locks [D] into a restraining position!", \ - "[A] locks you into a restraining position!") - D.adjustStaminaLoss(20) - D.Stun(5) - restraining = 1 - addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE) - return 1 - -/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat) - D.visible_message("[A] strikes [D]'s abdomen, neck and back consecutively", \ - "[A] strikes your abdomen, neck and back consecutively!") - playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1) - var/obj/item/I = D.get_active_held_item() - if(I && D.drop_item()) - A.put_in_hands(I) - D.adjustStaminaLoss(50) - D.apply_damage(25, BRUTE) - return 1 - -/datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("G",D) - if(check_streak(A,D)) - return 1 - if(A.grab_state >= GRAB_AGGRESSIVE) - D.grabbedby(A, 1) - else - A.start_pulling(D, 1) - if(A.pulling) - D.stop_pulling() - add_logs(A, D, "grabbed", addition="aggressively") - A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab - - return 1 - -/datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("H",D) - if(check_streak(A,D)) - return 1 - add_logs(A, D, "CQC'd") - A.do_attack_animation(D) - var/picked_hit_type = pick("CQC'd", "Big Bossed") - var/bonus_damage = 13 - if(D.weakened || D.resting || D.lying) - bonus_damage += 5 - picked_hit_type = "stomps on" - D.apply_damage(bonus_damage, BRUTE) - if(picked_hit_type == "kicks" || picked_hit_type == "stomps on") - playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1) - else - playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1) - D.visible_message("[A] [picked_hit_type] [D]!", \ - "[A] [picked_hit_type] you!") - add_logs(A, D, "[picked_hit_type] with CQC") - if(A.resting && !D.stat && !D.weakened) - D.visible_message("[A] leg sweeps [D]!", \ - "[A] leg sweeps you!") - playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1) - D.apply_damage(10, BRUTE) - D.Weaken(3) - add_logs(A, D, "cqc sweeped") - return 1 - -/datum/martial_art/cqc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("D",D) - var/obj/item/I = null - if(check_streak(A,D)) - return 1 - if(prob(65)) - if(!D.stat || !D.weakened || !restraining) - I = D.get_active_held_item() - D.visible_message("[A] strikes [D]'s jaw with their hand!", \ - "[A] strikes your jaw, disorienting you!") - playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1) - if(I && D.drop_item()) - A.put_in_hands(I) - D.Jitter(2) - D.apply_damage(5, BRUTE) - else - D.visible_message("[A] attempted to disarm [D]!", \ - "[A] attempted to disarm [D]!") - playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - add_logs(A, D, "disarmed with CQC", "[I ? " grabbing \the [I]" : ""]") - if(restraining && A.pulling == D) - D.visible_message("[A] puts [D] into a chokehold!", \ - "[A] puts you into a chokehold!") - D.SetSleeping(20) - restraining = 0 - if(A.grab_state < GRAB_NECK) - A.grab_state = GRAB_NECK - else - restraining = 0 - return 0 - return 1 - -/mob/living/carbon/human/proc/CQC_help() - set name = "Remember The Basics" - set desc = "You try to remember some of the basics of CQC." - set category = "CQC" - - to_chat(usr, "You try to remember some of the basics of CQC.") - - to_chat(usr, "Slam: Grab Harm. Slam opponent into the ground, weakens and knocks down.") - to_chat(usr, "CQC Kick: Harm Disarm Harm. Knocks opponent away. Knocks out stunned or weakened opponents.") - to_chat(usr, "Restrain: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a choke hold.") - to_chat(usr, "Pressure: Disarm Grab. Decent stamina damage.") - to_chat(usr, "Consecutive CQC: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.") - - to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.") - -//ITEMS - -/obj/item/clothing/gloves/boxing - var/datum/martial_art/boxing/style = new - -/obj/item/clothing/gloves/boxing/equipped(mob/user, slot) - if(!ishuman(user)) - return - if(slot == slot_gloves) - var/mob/living/carbon/human/H = user - style.teach(H,1) - return - -/obj/item/clothing/gloves/boxing/dropped(mob/user) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(H.get_item_by_slot(slot_gloves) == src) - style.remove(H) - return - -/obj/item/weapon/storage/belt/champion/wrestling - name = "Wrestling Belt" - var/datum/martial_art/wrestling/style = new - -/obj/item/weapon/storage/belt/champion/wrestling/equipped(mob/user, slot) - if(!ishuman(user)) - return - if(slot == slot_belt) - var/mob/living/carbon/human/H = user - style.teach(H,1) - return - -/obj/item/weapon/storage/belt/champion/wrestling/dropped(mob/user) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(H.get_item_by_slot(slot_belt) == src) - style.remove(H) - return - -/obj/item/weapon/plasma_fist_scroll - name = "frayed scroll" - desc = "An aged and frayed scrap of paper written in shifting runes. There are hand-drawn illustrations of pugilism." - icon = 'icons/obj/wizard.dmi' - icon_state ="scroll2" - var/used = 0 - -/obj/item/weapon/plasma_fist_scroll/attack_self(mob/user) - if(!ishuman(user)) - return - if(!used) - var/mob/living/carbon/human/H = user - var/datum/martial_art/plasma_fist/F = new/datum/martial_art/plasma_fist(null) - F.teach(H) - to_chat(H, "You have learned the ancient martial art of Plasma Fist.") - used = 1 - desc = "It's completely blank." - name = "empty scroll" - icon_state = "blankscroll" - -/obj/item/weapon/cqc_manual - name = "old manual" - desc = "A small, black manual. There are drawn instructions of tactical hand-to-hand combat." - icon = 'icons/obj/library.dmi' - icon_state ="cqcmanual" - -/obj/item/weapon/cqc_manual/attack_self(mob/living/carbon/human/user) - if(!istype(user) || !user) - return - to_chat(user, "You remember the basics of CQC.") - var/datum/martial_art/cqc/D = new(null) - D.teach(user) - user.drop_item() - visible_message("[src] beeps ominously, and a moment later it bursts up in flames.") - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) - -/obj/item/weapon/sleeping_carp_scroll - name = "mysterious scroll" - desc = "A scroll filled with strange markings. It seems to be drawings of some sort of martial art." - icon = 'icons/obj/wizard.dmi' - icon_state = "scroll2" - -/obj/item/weapon/sleeping_carp_scroll/attack_self(mob/living/carbon/human/user) - if(!istype(user) || !user) - return - var/message = "You have learned the ancient martial art of the Sleeping Carp! Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles \ - directed toward you. However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab." - to_chat(user, message) - var/datum/martial_art/the_sleeping_carp/theSleepingCarp = new(null) - theSleepingCarp.teach(user) - user.drop_item() - visible_message("[src] lights up in fire and quickly burns to ash.") - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) - -/obj/item/weapon/twohanded/bostaff - name = "bo staff" - desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate." - force = 10 - w_class = WEIGHT_CLASS_BULKY - slot_flags = SLOT_BACK - force_unwielded = 10 - force_wielded = 24 - throwforce = 20 - throw_speed = 2 - attack_verb = list("smashed", "slammed", "whacked", "thwacked") - icon = 'icons/obj/weapons.dmi' - icon_state = "bostaff0" - block_chance = 50 - -/obj/item/weapon/twohanded/bostaff/update_icon() - icon_state = "bostaff[wielded]" - return - -/obj/item/weapon/twohanded/bostaff/attack(mob/target, mob/living/user) - add_fingerprint(user) - if((CLUMSY in user.disabilities) && prob(50)) - to_chat(user, "You club yourself over the head with [src].") - user.Weaken(3) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, "head") - else - user.take_bodypart_damage(2*force) - return - if(iscyborg(target)) - return ..() - if(!isliving(target)) - return ..() - var/mob/living/carbon/C = target - if(C.stat) - to_chat(user, "It would be dishonorable to attack a foe while they cannot retaliate.") - return - if(user.a_intent == INTENT_DISARM) - if(!wielded) - return ..() - if(!ishuman(target)) - return ..() - var/mob/living/carbon/human/H = target - var/list/fluffmessages = list("[user] clubs [H] with [src]!", \ - "[user] smacks [H] with the butt of [src]!", \ - "[user] broadsides [H] with [src]!", \ - "[user] smashes [H]'s head with [src]!", \ - "[user] beats [H] with front of [src]!", \ - "[user] twirls and slams [H] with [src]!") - H.visible_message("[pick(fluffmessages)]", \ - "[pick(fluffmessages)]") - playsound(get_turf(user), 'sound/effects/woodhit.ogg', 75, 1, -1) - H.adjustStaminaLoss(rand(13,20)) - if(prob(10)) - H.visible_message("[H] collapses!", \ - "Your legs give out!") - H.Weaken(4) - if(H.staminaloss && !H.sleeping) - var/total_health = (H.health - H.staminaloss) - if(total_health <= HEALTH_THRESHOLD_CRIT && !H.stat) - H.visible_message("[user] delivers a heavy hit to [H]'s head, knocking them out cold!", \ - "[user] knocks you unconscious!") - H.SetSleeping(30) - H.adjustBrainLoss(25) - else - return ..() - -/obj/item/weapon/twohanded/bostaff/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance) - if(wielded) - return ..() - return 0 - diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm new file mode 100644 index 0000000000..6a3a059890 --- /dev/null +++ b/code/datums/martial/boxing.dm @@ -0,0 +1,67 @@ +/datum/martial_art/boxing + name = "Boxing" + +/datum/martial_art/boxing/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + to_chat(A, "Can't disarm while boxing!") + return 1 + +/datum/martial_art/boxing/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + to_chat(A, "Can't grab while boxing!") + return 1 + +/datum/martial_art/boxing/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + + var/atk_verb = pick("left hook","right hook","straight punch") + + var/damage = rand(5, 8) + A.dna.species.punchdamagelow + if(!damage) + playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1) + D.visible_message("[A] has attempted to [atk_verb] [D]!", \ + "[A] has attempted to [atk_verb] [D]!", null, COMBAT_MESSAGE_RANGE) + add_logs(A, D, "attempted to hit", atk_verb) + return 0 + + + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/armor_block = D.run_armor_check(affecting, "melee") + + playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1) + + D.visible_message("[A] has [atk_verb]ed [D]!", \ + "[A] has [atk_verb]ed [D]!", null, COMBAT_MESSAGE_RANGE) + + D.apply_damage(damage, STAMINA, affecting, armor_block) + add_logs(A, D, "punched (boxing) ") + if(D.getStaminaLoss() > 50) + var/knockout_prob = D.getStaminaLoss() + rand(-15,15) + if((D.stat != DEAD) && prob(knockout_prob)) + D.visible_message("[A] has knocked [D] out with a haymaker!", \ + "[A] has knocked [D] out with a haymaker!") + D.apply_effect(10,WEAKEN,armor_block) + D.SetSleeping(5) + D.forcesay(GLOB.hit_appends) + add_logs(A, D, "knocked out (boxing) ") + else if(D.lying) + D.forcesay(GLOB.hit_appends) + return 1 + +/obj/item/clothing/gloves/boxing + var/datum/martial_art/boxing/style = new + +/obj/item/clothing/gloves/boxing/equipped(mob/user, slot) + if(!ishuman(user)) + return + if(slot == slot_gloves) + var/mob/living/carbon/human/H = user + style.teach(H,1) + return + +/obj/item/clothing/gloves/boxing/dropped(mob/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.get_item_by_slot(slot_gloves) == src) + style.remove(H) + return diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm new file mode 100644 index 0000000000..ebb570fa0c --- /dev/null +++ b/code/datums/martial/cqc.dm @@ -0,0 +1,199 @@ +#define SLAM_COMBO "GH" +#define KICK_COMBO "HH" +#define RESTRAIN_COMBO "GG" +#define PRESSURE_COMBO "DG" +#define CONSECUTIVE_COMBO "DDH" + +/datum/martial_art/cqc + name = "CQC" + help_verb = /mob/living/carbon/human/proc/CQC_help + block_chance = 75 + +/datum/martial_art/cqc/proc/drop_restraining() + restraining = 0 + +/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak,SLAM_COMBO)) + streak = "" + Slam(A,D) + return 1 + if(findtext(streak,KICK_COMBO)) + streak = "" + Kick(A,D) + return 1 + if(findtext(streak,RESTRAIN_COMBO)) + streak = "" + Restrain(A,D) + return 1 + if(findtext(streak,PRESSURE_COMBO)) + streak = "" + Pressure(A,D) + return 1 + if(findtext(streak,CONSECUTIVE_COMBO)) + streak = "" + Consecutive(A,D) + return 0 + +/datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!D.stat || !D.weakened) + D.visible_message("[A] slams [D] into the ground!", \ + "[A] slams you into the ground!") + playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1) + D.apply_damage(10, BRUTE) + D.Weaken(6) + add_logs(A, D, "cqc slammed") + return 1 + +/datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!D.stat || !D.weakened) + D.visible_message("[A] kicks [D] back!", \ + "[A] kicks you back!") + playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + var/atom/throw_target = get_edge_target_turf(D, A.dir) + D.throw_at(throw_target, 1, 14, A) + D.apply_damage(10, BRUTE) + add_logs(A, D, "cqc kicked") + if(D.weakened && !D.stat) + D.visible_message("[A] kicks [D]'s head, knocking them out!", \ + "[A] kicks your head, knocking you out!") + playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1) + D.SetSleeping(15) + D.adjustBrainLoss(25) + return 1 + +/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D) + D.visible_message("[A] forces their arm on [D]'s neck!") + D.adjustStaminaLoss(60) + playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + return 1 + +/datum/martial_art/cqc/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(restraining) + return + if(!D.stat) + D.visible_message("[A] locks [D] into a restraining position!", \ + "[A] locks you into a restraining position!") + D.adjustStaminaLoss(20) + D.Stun(5) + restraining = 1 + addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE) + return 1 + +/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!D.stat) + D.visible_message("[A] strikes [D]'s abdomen, neck and back consecutively", \ + "[A] strikes your abdomen, neck and back consecutively!") + playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1) + var/obj/item/I = D.get_active_held_item() + if(I && D.drop_item()) + A.put_in_hands(I) + D.adjustStaminaLoss(50) + D.apply_damage(25, BRUTE) + return 1 + +/datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("G",D) + if(check_streak(A,D)) + return 1 + if(A.grab_state >= GRAB_AGGRESSIVE) + D.grabbedby(A, 1) + else + A.start_pulling(D, 1) + if(A.pulling) + D.stop_pulling() + add_logs(A, D, "grabbed", addition="aggressively") + A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab + + return 1 + +/datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("H",D) + if(check_streak(A,D)) + return 1 + add_logs(A, D, "CQC'd") + A.do_attack_animation(D) + var/picked_hit_type = pick("CQC'd", "Big Bossed") + var/bonus_damage = 13 + if(D.weakened || D.resting || D.lying) + bonus_damage += 5 + picked_hit_type = "stomps on" + D.apply_damage(bonus_damage, BRUTE) + if(picked_hit_type == "kicks" || picked_hit_type == "stomps on") + playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1) + else + playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + D.visible_message("[A] [picked_hit_type] [D]!", \ + "[A] [picked_hit_type] you!") + add_logs(A, D, "[picked_hit_type] with CQC") + if(A.resting && !D.stat && !D.weakened) + D.visible_message("[A] leg sweeps [D]!", \ + "[A] leg sweeps you!") + playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1) + D.apply_damage(10, BRUTE) + D.Weaken(3) + add_logs(A, D, "cqc sweeped") + return 1 + +/datum/martial_art/cqc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("D",D) + var/obj/item/I = null + if(check_streak(A,D)) + return 1 + if(prob(65)) + if(!D.stat || !D.weakened || !restraining) + I = D.get_active_held_item() + D.visible_message("[A] strikes [D]'s jaw with their hand!", \ + "[A] strikes your jaw, disorienting you!") + playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + if(I && D.drop_item()) + A.put_in_hands(I) + D.Jitter(2) + D.apply_damage(5, BRUTE) + else + D.visible_message("[A] attempted to disarm [D]!", \ + "[A] attempted to disarm [D]!") + playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + add_logs(A, D, "disarmed with CQC", "[I ? " grabbing \the [I]" : ""]") + if(restraining && A.pulling == D) + D.visible_message("[A] puts [D] into a chokehold!", \ + "[A] puts you into a chokehold!") + D.SetSleeping(20) + restraining = 0 + if(A.grab_state < GRAB_NECK) + A.grab_state = GRAB_NECK + else + restraining = 0 + return 0 + return 1 + +/mob/living/carbon/human/proc/CQC_help() + set name = "Remember The Basics" + set desc = "You try to remember some of the basics of CQC." + set category = "CQC" + + to_chat(usr, "You try to remember some of the basics of CQC.") + + to_chat(usr, "Slam: Grab Harm. Slam opponent into the ground, weakens and knocks down.") + to_chat(usr, "CQC Kick: Harm Disarm Harm. Knocks opponent away. Knocks out stunned or weakened opponents.") + to_chat(usr, "Restrain: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a choke hold.") + to_chat(usr, "Pressure: Disarm Grab. Decent stamina damage.") + to_chat(usr, "Consecutive CQC: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.") + + to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.") + +/obj/item/weapon/cqc_manual + name = "old manual" + desc = "A small, black manual. There are drawn instructions of tactical hand-to-hand combat." + icon = 'icons/obj/library.dmi' + icon_state ="cqcmanual" + +/obj/item/weapon/cqc_manual/attack_self(mob/living/carbon/human/user) + if(!istype(user) || !user) + return + to_chat(user, "You remember the basics of CQC.") + var/datum/martial_art/cqc/D = new(null) + D.teach(user) + user.drop_item() + visible_message("[src] beeps ominously, and a moment later it bursts up in flames.") + new /obj/effect/decal/cleanable/ash(get_turf(src)) + qdel(src) diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm index 5bc9b0588b..eb5f74b411 100644 --- a/code/datums/martial/krav_maga.dm +++ b/code/datums/martial/krav_maga.dm @@ -13,12 +13,12 @@ to_chat(owner, "You can't use Krav Maga while you're incapacitated.") return var/mob/living/carbon/human/H = owner - if (H.martial_art.streak == "neck_chop") + if (H.mind.martial_art.streak == "neck_chop") owner.visible_message("[owner] assumes a neutral stance.", "Your next attack is cleared.") - H.martial_art.streak = "" + H.mind.martial_art.streak = "" else owner.visible_message("[owner] assumes the Neck Chop stance!", "Your next attack will be a Neck Chop.") - H.martial_art.streak = "neck_chop" + H.mind.martial_art.streak = "neck_chop" /datum/action/leg_sweep name = "Leg Sweep - Trips the victim, knocking them down for a brief moment." @@ -29,12 +29,12 @@ to_chat(owner, "You can't use Krav Maga while you're incapacitated.") return var/mob/living/carbon/human/H = owner - if (H.martial_art.streak == "leg_sweep") + if (H.mind.martial_art.streak == "leg_sweep") owner.visible_message("[owner] assumes a neutral stance.", "Your next attack is cleared.") - H.martial_art.streak = "" + H.mind.martial_art.streak = "" else owner.visible_message("[owner] assumes the Leg Sweep stance!", "Your next attack will be a Leg Sweep.") - H.martial_art.streak = "leg_sweep" + H.mind.martial_art.streak = "leg_sweep" /datum/action/lung_punch//referred to internally as 'quick choke' name = "Lung Punch - Delivers a strong punch just above the victim's abdomen, constraining the lungs. The victim will be unable to breathe for a short time." @@ -45,12 +45,12 @@ to_chat(owner, "You can't use Krav Maga while you're incapacitated.") return var/mob/living/carbon/human/H = owner - if (H.martial_art.streak == "quick_choke") + if (H.mind.martial_art.streak == "quick_choke") owner.visible_message("[owner] assumes a neutral stance.", "Your next attack is cleared.") - H.martial_art.streak = "" + H.mind.martial_art.streak = "" else owner.visible_message("[owner] assumes the Lung Punch stance!", "Your next attack will be a Lung Punch.") - H.martial_art.streak = "quick_choke"//internal name for lung punch + H.mind.martial_art.streak = "quick_choke"//internal name for lung punch /datum/martial_art/krav_maga/teach(var/mob/living/carbon/human/H,var/make_temporary=0) ..() diff --git a/code/datums/martial/plasma_fist.dm b/code/datums/martial/plasma_fist.dm new file mode 100644 index 0000000000..c79ef032ee --- /dev/null +++ b/code/datums/martial/plasma_fist.dm @@ -0,0 +1,114 @@ +#define TORNADO_COMBO "HHD" +#define THROWBACK_COMBO "DHD" +#define PLASMA_COMBO "HDDDH" + +/datum/martial_art/plasma_fist + name = "Plasma Fist" + help_verb = /mob/living/carbon/human/proc/plasma_fist_help + + +/datum/martial_art/plasma_fist/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak,TORNADO_COMBO)) + streak = "" + Tornado(A,D) + return 1 + if(findtext(streak,THROWBACK_COMBO)) + streak = "" + Throwback(A,D) + return 1 + if(findtext(streak,PLASMA_COMBO)) + streak = "" + Plasma(A,D) + return 1 + return 0 + +/datum/martial_art/plasma_fist/proc/TornadoAnimate(mob/living/carbon/human/A) + set waitfor = FALSE + for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH)) + if(!A) + break + A.setDir(i) + playsound(A.loc, 'sound/weapons/punch1.ogg', 15, 1, -1) + sleep(1) + +/datum/martial_art/plasma_fist/proc/Tornado(mob/living/carbon/human/A, mob/living/carbon/human/D) + A.say("TORNADO SWEEP!") + TornadoAnimate(A) + var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null) + var/list/turfs = list() + for(var/turf/T in range(1,A)) + turfs.Add(T) + R.cast(turfs) + add_logs(A, D, "tornado sweeped(Plasma Fist)") + return + +/datum/martial_art/plasma_fist/proc/Throwback(mob/living/carbon/human/A, mob/living/carbon/human/D) + D.visible_message("[A] has hit [D] with Plasma Punch!", \ + "[A] has hit [D] with Plasma Punch!") + playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) + var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A))) + D.throw_at(throw_target, 200, 4,A) + A.say("HYAH!") + add_logs(A, D, "threw back (Plasma Fist)") + return + +/datum/martial_art/plasma_fist/proc/Plasma(mob/living/carbon/human/A, mob/living/carbon/human/D) + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) + A.say("PLASMA FIST!") + D.visible_message("[A] has hit [D] with THE PLASMA FIST TECHNIQUE!", \ + "[A] has hit [D] with THE PLASMA FIST TECHNIQUE!") + D.gib() + add_logs(A, D, "gibbed (Plasma Fist)") + return + +/datum/martial_art/plasma_fist/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("H",D) + if(check_streak(A,D)) + return 1 + basic_hit(A,D) + return 1 + +/datum/martial_art/plasma_fist/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("D",D) + if(check_streak(A,D)) + return 1 + basic_hit(A,D) + return 1 + +/datum/martial_art/plasma_fist/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("G",D) + if(check_streak(A,D)) + return 1 + basic_hit(A,D) + return 1 + +/mob/living/carbon/human/proc/plasma_fist_help() + set name = "Recall Teachings" + set desc = "Remember the martial techniques of the Plasma Fist." + set category = "Plasma Fist" + + to_chat(usr, "You clench your fists and have a flashback of knowledge...") + to_chat(usr, "Tornado Sweep: Harm Harm Disarm. Repulses target and everyone back.") + to_chat(usr, "Throwback: Disarm Harm Disarm. Throws the target and an item at them.") + to_chat(usr, "The Plasma Fist: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body.") + +/obj/item/weapon/plasma_fist_scroll + name = "frayed scroll" + desc = "An aged and frayed scrap of paper written in shifting runes. There are hand-drawn illustrations of pugilism." + icon = 'icons/obj/wizard.dmi' + icon_state ="scroll2" + var/used = 0 + +/obj/item/weapon/plasma_fist_scroll/attack_self(mob/user) + if(!ishuman(user)) + return + if(!used) + var/mob/living/carbon/human/H = user + var/datum/martial_art/plasma_fist/F = new/datum/martial_art/plasma_fist(null) + F.teach(H) + to_chat(H, "You have learned the ancient martial art of Plasma Fist.") + used = 1 + desc = "It's completely blank." + name = "empty scroll" + icon_state = "blankscroll" diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm new file mode 100644 index 0000000000..f079d2a7db --- /dev/null +++ b/code/datums/martial/sleeping_carp.dm @@ -0,0 +1,247 @@ +#define WRIST_WRENCH_COMBO "DD" +#define BACK_KICK_COMBO "HG" +#define STOMACH_KNEE_COMBO "GH" +#define HEAD_KICK_COMBO "DHH" +#define ELBOW_DROP_COMBO "HDHDH" + +/datum/martial_art/the_sleeping_carp + name = "The Sleeping Carp" + deflection_chance = 100 + no_guns = TRUE + allow_temp_override = FALSE + help_verb = /mob/living/carbon/human/proc/sleeping_carp_help + +/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak,WRIST_WRENCH_COMBO)) + streak = "" + wristWrench(A,D) + return 1 + if(findtext(streak,BACK_KICK_COMBO)) + streak = "" + backKick(A,D) + return 1 + if(findtext(streak,STOMACH_KNEE_COMBO)) + streak = "" + kneeStomach(A,D) + return 1 + if(findtext(streak,HEAD_KICK_COMBO)) + streak = "" + headKick(A,D) + return 1 + if(findtext(streak,ELBOW_DROP_COMBO)) + streak = "" + elbowDrop(A,D) + return 1 + return 0 + +/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!D.stat && !D.stunned && !D.weakened) + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + D.visible_message("[A] grabs [D]'s wrist and wrenches it sideways!", \ + "[A] grabs your wrist and violently wrenches it to the side!") + playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + D.emote("scream") + D.drop_item() + D.apply_damage(5, BRUTE, pick("l_arm", "r_arm")) + D.Stun(3) + return 1 + add_logs(A, D, "wrist wrenched (Sleeping Carp)") + return basic_hit(A,D) + +/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(A.dir == D.dir && !D.stat && !D.weakened) + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + D.visible_message("[A] kicks [D] in the back!", \ + "[A] kicks you in the back, making you stumble and fall!") + step_to(D,get_step(D,D.dir),1) + D.Weaken(4) + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) + return 1 + add_logs(A, D, "back-kicked (Sleeping Carp)") + return basic_hit(A,D) + +/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!D.stat && !D.weakened) + A.do_attack_animation(D, ATTACK_EFFECT_KICK) + D.visible_message("[A] knees [D] in the stomach!", \ + "[A] winds you with a knee in the stomach!") + D.audible_message("[D] gags!") + D.losebreath += 3 + D.Stun(2) + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) + return 1 + add_logs(A, D, "stomach kneed (Sleeping Carp)") + return basic_hit(A,D) + +/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!D.stat && !D.weakened) + A.do_attack_animation(D, ATTACK_EFFECT_KICK) + D.visible_message("[A] kicks [D] in the head!", \ + "[A] kicks you in the jaw!") + D.apply_damage(20, BRUTE, "head") + D.drop_item() + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) + D.Stun(4) + return 1 + add_logs(A, D, "head kicked (Sleeping Carp)") + return basic_hit(A,D) + +/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(D.weakened || D.resting || D.stat) + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + D.visible_message("[A] elbow drops [D]!", \ + "[A] piledrives you with their elbow!") + if(D.stat) + D.death() //FINISH HIM! + D.apply_damage(50, BRUTE, "chest") + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1) + return 1 + add_logs(A, D, "elbow dropped (Sleeping Carp)") + return basic_hit(A,D) + +/datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("G",D) + if(check_streak(A,D)) + return 1 + if(A.grab_state >= GRAB_AGGRESSIVE) + D.grabbedby(A, 1) + else + A.start_pulling(D, 1) + if(A.pulling) + D.drop_all_held_items() + D.stop_pulling() + if(A.a_intent == INTENT_GRAB) + add_logs(A, D, "grabbed", addition="aggressively") + A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab + else + add_logs(A, D, "grabbed", addition="passively") + A.grab_state = GRAB_PASSIVE + return 1 + +/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("H",D) + if(check_streak(A,D)) + return 1 + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams") + D.visible_message("[A] [atk_verb] [D]!", \ + "[A] [atk_verb] you!") + D.apply_damage(rand(10,15), BRUTE) + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, 1, -1) + if(prob(D.getBruteLoss()) && !D.lying) + D.visible_message("[D] stumbles and falls!", "The blow sends you to the ground!") + D.Weaken(4) + add_logs(A, D, "[atk_verb] (Sleeping Carp)") + return 1 + + +/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + add_to_streak("D",D) + if(check_streak(A,D)) + return 1 + return ..() + +/mob/living/carbon/human/proc/sleeping_carp_help() + set name = "Recall Teachings" + set desc = "Remember the martial techniques of the Sleeping Carp clan." + set category = "Sleeping Carp" + + to_chat(usr, "You retreat inward and recall the teachings of the Sleeping Carp...") + + to_chat(usr, "Wrist Wrench: Disarm Disarm. Forces opponent to drop item in hand.") + to_chat(usr, "Back Kick: Harm Grab. Opponent must be facing away. Knocks down.") + to_chat(usr, "Stomach Knee: Grab Harm. Knocks the wind out of opponent and stuns.") + to_chat(usr, "Head Kick: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.") + to_chat(usr, "Elbow Drop: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.") + +/obj/item/weapon/sleeping_carp_scroll + name = "mysterious scroll" + desc = "A scroll filled with strange markings. It seems to be drawings of some sort of martial art." + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll2" + +/obj/item/weapon/sleeping_carp_scroll/attack_self(mob/living/carbon/human/user) + if(!istype(user) || !user) + return + var/message = "You have learned the ancient martial art of the Sleeping Carp! Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles \ + directed toward you. However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab." + to_chat(user, message) + var/datum/martial_art/the_sleeping_carp/theSleepingCarp = new(null) + theSleepingCarp.teach(user) + user.drop_item() + visible_message("[src] lights up in fire and quickly burns to ash.") + new /obj/effect/decal/cleanable/ash(get_turf(src)) + qdel(src) + +/obj/item/weapon/twohanded/bostaff + name = "bo staff" + desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate." + force = 10 + w_class = WEIGHT_CLASS_BULKY + slot_flags = SLOT_BACK + force_unwielded = 10 + force_wielded = 24 + throwforce = 20 + throw_speed = 2 + attack_verb = list("smashed", "slammed", "whacked", "thwacked") + icon = 'icons/obj/weapons.dmi' + icon_state = "bostaff0" + block_chance = 50 + +/obj/item/weapon/twohanded/bostaff/update_icon() + icon_state = "bostaff[wielded]" + return + +/obj/item/weapon/twohanded/bostaff/attack(mob/target, mob/living/user) + add_fingerprint(user) + if((CLUMSY in user.disabilities) && prob(50)) + to_chat(user, "You club yourself over the head with [src].") + user.Weaken(3) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(2*force, BRUTE, "head") + else + user.take_bodypart_damage(2*force) + return + if(iscyborg(target)) + return ..() + if(!isliving(target)) + return ..() + var/mob/living/carbon/C = target + if(C.stat) + to_chat(user, "It would be dishonorable to attack a foe while they cannot retaliate.") + return + if(user.a_intent == INTENT_DISARM) + if(!wielded) + return ..() + if(!ishuman(target)) + return ..() + var/mob/living/carbon/human/H = target + var/list/fluffmessages = list("[user] clubs [H] with [src]!", \ + "[user] smacks [H] with the butt of [src]!", \ + "[user] broadsides [H] with [src]!", \ + "[user] smashes [H]'s head with [src]!", \ + "[user] beats [H] with front of [src]!", \ + "[user] twirls and slams [H] with [src]!") + H.visible_message("[pick(fluffmessages)]", \ + "[pick(fluffmessages)]") + playsound(get_turf(user), 'sound/effects/woodhit.ogg', 75, 1, -1) + H.adjustStaminaLoss(rand(13,20)) + if(prob(10)) + H.visible_message("[H] collapses!", \ + "Your legs give out!") + H.Weaken(4) + if(H.staminaloss && !H.sleeping) + var/total_health = (H.health - H.staminaloss) + if(total_health <= HEALTH_THRESHOLD_CRIT && !H.stat) + H.visible_message("[user] delivers a heavy hit to [H]'s head, knocking them out cold!", \ + "[user] knocks you unconscious!") + H.SetSleeping(30) + H.adjustBrainLoss(25) + else + return ..() + +/obj/item/weapon/twohanded/bostaff/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance) + if(wielded) + return ..() + return 0 diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index 5554ceeaeb..fc1c7a9b6d 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -1,3 +1,13 @@ +/mob/living/carbon/human/proc/wrestling_help() + set name = "Recall Teachings" + set desc = "Remember how to wrestle." + set category = "Wrestling" + + to_chat(usr, "You flex your muscles and have a revelation...") + to_chat(usr, "Clinch: Grab. Passively gives you a chance to immediately aggressively grab someone. Not always successful.") + to_chat(usr, "Suplex: Disarm someone you are grabbing. Suplexes your target to the floor. Greatly injures them and leaves both you and your target on the floor.") + to_chat(usr, "Advanced grab: Grab. Passively causes stamina damage when grabbing someone.") + /datum/martial_art/wrestling name = "Wrestling" var/datum/action/slam/slam = new/datum/action/slam() @@ -40,7 +50,7 @@ return owner.visible_message("[owner] prepares to BODY SLAM!", "Your next attack will be a BODY SLAM.") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "slam" + H.mind.martial_art.streak = "slam" /datum/action/throw_wrassle name = "Throw (Cinch) - Spin a cinched opponent around and throw them." @@ -52,7 +62,7 @@ return owner.visible_message("[owner] prepares to THROW!", "Your next attack will be a THROW.") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "throw" + H.mind.martial_art.streak = "throw" /datum/action/kick name = "Kick - A powerful kick, sends people flying away from you. Also useful for escaping from bad situations." @@ -64,7 +74,7 @@ return owner.visible_message("[owner] prepares to KICK!", "Your next attack will be a KICK.") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "kick" + H.mind.martial_art.streak = "kick" /datum/action/strike name = "Strike - Hit a neaby opponent with a quick attack." @@ -76,7 +86,7 @@ return owner.visible_message("[owner] prepares to STRIKE!", "Your next attack will be a STRIKE.") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "strike" + H.mind.martial_art.streak = "strike" /datum/action/drop name = "Drop - Smash down onto an opponent." @@ -88,7 +98,7 @@ return owner.visible_message("[owner] prepares to LEG DROP!", "Your next attack will be a LEG DROP.") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "drop" + H.mind.martial_art.streak = "drop" /datum/martial_art/wrestling/teach(var/mob/living/carbon/human/H,var/make_temporary=0) ..() @@ -430,3 +440,23 @@ D.Stun(rand(3,5)) add_logs(A, D, "cinched") return 1 + +/obj/item/weapon/storage/belt/champion/wrestling + name = "Wrestling Belt" + var/datum/martial_art/wrestling/style = new + +/obj/item/weapon/storage/belt/champion/wrestling/equipped(mob/user, slot) + if(!ishuman(user)) + return + if(slot == slot_belt) + var/mob/living/carbon/human/H = user + style.teach(H,1) + return + +/obj/item/weapon/storage/belt/champion/wrestling/dropped(mob/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.get_item_by_slot(slot_belt) == src) + style.remove(H) + return diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 04bec0a7a4..7f7310665b 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -50,7 +50,8 @@ var/datum/faction/faction //associated faction var/datum/changeling/changeling //changeling holder var/linglink - + var/datum/martial_art/martial_art = null + var/static/default_martial_art = new/datum/martial_art var/miming = 0 // Mime's vow of silence var/list/antag_datums var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state diff --git a/code/game/objects/items/weapons/implants/implant_krav_maga.dm b/code/game/objects/items/weapons/implants/implant_krav_maga.dm index 1db7601255..60823f3279 100644 --- a/code/game/objects/items/weapons/implants/implant_krav_maga.dm +++ b/code/game/objects/items/weapons/implants/implant_krav_maga.dm @@ -19,7 +19,9 @@ var/mob/living/carbon/human/H = imp_in if(!ishuman(H)) return - if(istype(H.martial_art, /datum/martial_art/krav_maga)) + if(!H.mind) + return + if(istype(H.mind.martial_art, /datum/martial_art/krav_maga)) style.remove(H) else style.teach(H,1) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index be7e851a8e..8f597f5531 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -36,7 +36,8 @@ //initialise organs create_internal_organs() - martial_art = default_martial_art + if(mind) + mind.martial_art = mind.default_martial_art handcrafting = new() diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 0623921d63..46f5bc3d4f 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -40,12 +40,13 @@ if(spec_return) return spec_return - if(martial_art && martial_art.deflection_chance) //Some martial arts users can deflect projectiles! - if(prob(martial_art.deflection_chance)) - if(!lying && dna && !dna.check_mutation(HULK)) //But only if they're not lying down, and hulks can't do it - visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!", "You deflect the projectile!") - playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1) - return 0 + if(mind) + if(mind.martial_art && mind.martial_art.deflection_chance) //Some martial arts users can deflect projectiles! + if(prob(mind.martial_art.deflection_chance)) + if(!lying && dna && !dna.check_mutation(HULK)) //But only if they're not lying down, and hulks can't do it + visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!", "You deflect the projectile!") + playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1) + return 0 if(!(P.original == src && P.firer == src)) //can't block or reflect when shooting yourself if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) @@ -103,10 +104,11 @@ return 0 /mob/living/carbon/human/proc/check_block() - if(martial_art && martial_art.block_chance \ - && prob(martial_art.block_chance) && in_throw_mode \ - && !stat && !weakened && !stunned) - return TRUE + if(mind) + if(mind.martial_art && mind.martial_art.block_chance \ + && prob(mind.martial_art.block_chance) && in_throw_mode \ + && !stat && !weakened && !stunned) + return TRUE return FALSE /mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index f697215a4a..c1f5075b43 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -39,9 +39,6 @@ var/bleed_rate = 0 //how much are we bleeding var/bleedsuppress = 0 //for stopping bloodloss, eventually this will be limb-based like bleeding - var/datum/martial_art/martial_art = null - var/static/default_martial_art = new/datum/martial_art - var/name_override //For temporary visible name changes var/drunkenness = 0 //Overall drunkenness - check handle_alcohol() in life.dm for effects diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 3a4b241f10..0620ebd062 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -144,9 +144,9 @@ if(NOGUNS in src.dna.species.species_traits) to_chat(src, "Your fingers don't fit in the trigger guard!") return 0 - - if(martial_art && martial_art.no_guns) //great dishonor to famiry - to_chat(src, "Use of ranged weaponry would bring dishonor to the clan.") - return 0 + if(mind) + if(mind.martial_art && mind.martial_art.no_guns) //great dishonor to famiry + to_chat(src, "Use of ranged weaponry would bring dishonor to the clan.") + return 0 return . diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm new file mode 100644 index 0000000000..1ac24cffa9 --- /dev/null +++ b/code/modules/mob/living/carbon/human/login.dm @@ -0,0 +1,9 @@ +/mob/living/carbon/human/Login() + ..() + if(src.martial_art == default_martial_art && mind.stored_martial_art) //If the mind has a martial art stored and the body has the default one. + src.mind.stored_martial_art.teach(src) //Running teach so that it deals with help verbs. + else if(src.martial_art != default_martial_art && src.martial_art != mind.stored_martial_art) //If the body has a martial art which is not the default one and is not stored in the mind. + if(src.martial_art_owner != mind) + src.martial_art.remove(src) + else + src.mind.stored_martial_art = src.martial_art diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index b3f436ec68..cf92de53e1 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1304,7 +1304,7 @@ /datum/species/proc/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) return -/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) +/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) if(!istype(M)) return CHECK_DNA_AND_SPECIES(M) @@ -1312,6 +1312,8 @@ if(!istype(M)) //sanity check for drones. return + if(M.mind) + attacker_style = M.mind.martial_art if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(0, M.name, attack_type = UNARMED_ATTACK)) add_logs(M, H, "attempted to touch") H.visible_message("[M] attempted to touch [H]!") diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index c60b99a9b0..09c566e6a4 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -406,7 +406,7 @@ else reactive_teleport(H) -/datum/species/golem/bluespace/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) +/datum/species/golem/bluespace/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) ..() if(world.time > last_teleport + teleport_cooldown && M != H && M.a_intent != INTENT_HELP) reactive_teleport(H) @@ -490,7 +490,7 @@ var/golem_name = "[uppertext(clown_name)]" return golem_name -/datum/species/golem/bananium/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) +/datum/species/golem/bananium/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) ..() if(world.time > last_banana + banana_cooldown && M != H && M.a_intent != INTENT_HELP) new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 2ae7f02247..fd42582317 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -123,8 +123,9 @@ Difficulty: Very Hard /mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L) if(ishuman(L)) var/mob/living/carbon/human/H = L - if(H.martial_art && prob(H.martial_art.deflection_chance)) - . = TRUE + if(H.mind) + if(H.mind.martial_art && prob(H.mind.martial_art.deflection_chance)) + . = TRUE /mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() dir_shots(GLOB.diagonals) diff --git a/tgstation.dme b/tgstation.dme index 8244a11d5b..154540e10d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -323,7 +323,11 @@ #include "code\datums\helper_datums\icon_snapshot.dm" #include "code\datums\helper_datums\teleport.dm" #include "code\datums\helper_datums\topic_input.dm" +#include "code\datums\martial\boxing.dm" +#include "code\datums\martial\cqc.dm" #include "code\datums\martial\krav_maga.dm" +#include "code\datums\martial\plasma_fist.dm" +#include "code\datums\martial\sleeping_carp.dm" #include "code\datums\martial\wrestling.dm" #include "code\datums\ruins\lavaland.dm" #include "code\datums\ruins\space.dm"