diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 7caf7b32229..e978c388a8e 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -357,6 +357,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NO_EXTINGUISH "no_extinguish" /// Indicates if the mob is currently speaking with sign language #define TRAIT_SIGN_LANG "sign_language" +/// Trait given to mobs to indicate that they can catch papers thrown at them midair without trying, +/// and make syndicate airplanes when folding paper up. +#define TRAIT_PAPER_MASTER "paper_master" /// This mob is able to use sign language over the radio. #define TRAIT_CAN_SIGN_ON_COMMS "can_sign_on_comms" /// nobody can use martial arts on this mob diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index f009b6ccf0f..ae3d7ae24be 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -41,6 +41,9 @@ /// Trait from light debugging #define LIGHT_DEBUG_TRAIT "light-debug" +/// Trait given by an Action datum +#define ACTION_TRAIT "action" + #define CLOTHING_TRAIT "clothing" #define HELMET_TRAIT "helmet" /// inherited from the mask diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 1f944f8629e..e09c8841c3a 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -420,6 +420,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SHOCKIMMUNE" = TRAIT_SHOCKIMMUNE, "TRAIT_SHOVE_KNOCKDOWN_BLOCKED" = TRAIT_SHOVE_KNOCKDOWN_BLOCKED, "TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG, + "TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER, "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS, "TRAIT_SIXTHSENSE" = TRAIT_SIXTHSENSE, "TRAIT_SKITTISH" = TRAIT_SKITTISH, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index e3ff341412d..8fe99e95c6e 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -188,6 +188,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_SHIFTY_EYES" = TRAIT_SHIFTY_EYES, "TRAIT_SHOCKIMMUNE" = TRAIT_SHOCKIMMUNE, "TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG, + "TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER, "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS, "TRAIT_SIXTHSENSE" = TRAIT_SIXTHSENSE, "TRAIT_SKITTISH" = TRAIT_SKITTISH, diff --git a/code/datums/actions/mobs/assume_form.dm b/code/datums/actions/mobs/assume_form.dm index a335d0e745d..03a1a38d3c8 100644 --- a/code/datums/actions/mobs/assume_form.dm +++ b/code/datums/actions/mobs/assume_form.dm @@ -64,7 +64,7 @@ // important: do this at the very end because we might have SIGNAL_ADDTRAIT for this on the mob that's dependent on the above logic SEND_SIGNAL(owner, COMSIG_ACTION_DISGUISED_APPEARANCE, target_atom) - ADD_TRAIT(owner, TRAIT_DISGUISED, REF(src)) + ADD_TRAIT(owner, TRAIT_DISGUISED, ACTION_TRAIT) /// Resets the appearances of the mob to the default. /datum/action/cooldown/mob_cooldown/assume_form/proc/reset_appearances() @@ -85,4 +85,4 @@ owner.cut_overlays() // important: do this very end because we might have SIGNAL_REMOVETRAIT for this on the mob that's dependent on the above logic - REMOVE_TRAIT(owner, TRAIT_DISGUISED, REF(src)) + REMOVE_TRAIT(owner, TRAIT_DISGUISED, ACTION_TRAIT) diff --git a/code/datums/actions/mobs/lava_swoop.dm b/code/datums/actions/mobs/lava_swoop.dm index b86a710fbf2..a6c8282fd10 100644 --- a/code/datums/actions/mobs/lava_swoop.dm +++ b/code/datums/actions/mobs/lava_swoop.dm @@ -14,11 +14,11 @@ /datum/action/cooldown/mob_cooldown/lava_swoop/Grant(mob/M) . = ..() - M.add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_NOFIRE), REF(src)) + M.add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_NOFIRE), ACTION_TRAIT) /datum/action/cooldown/mob_cooldown/lava_swoop/Remove(mob/M) . = ..() - M.remove_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_NOFIRE), REF(src)) + M.remove_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_NOFIRE), ACTION_TRAIT) /datum/action/cooldown/mob_cooldown/lava_swoop/Activate(atom/target_atom) disable_cooldown_actions() diff --git a/code/datums/actions/mobs/sign_language.dm b/code/datums/actions/mobs/sign_language.dm index da375fd1f85..20c1157f326 100644 --- a/code/datums/actions/mobs/sign_language.dm +++ b/code/datums/actions/mobs/sign_language.dm @@ -42,17 +42,17 @@ SIGNAL_ADDTRAIT(TRAIT_MUTE), SIGNAL_REMOVETRAIT(TRAIT_MUTE) )) - REMOVE_TRAIT(grant_to, TRAIT_SIGN_LANG, TRAIT_GENERIC) + REMOVE_TRAIT(grant_to, TRAIT_SIGN_LANG, ACTION_TRAIT) /datum/action/innate/sign_language/Activate() active = TRUE - ADD_TRAIT(owner, TRAIT_SIGN_LANG, TRAIT_GENERIC) + ADD_TRAIT(owner, TRAIT_SIGN_LANG, ACTION_TRAIT) to_chat(owner, span_green("You are now communicating with sign language.")) build_all_button_icons(UPDATE_BUTTON_BACKGROUND) /datum/action/innate/sign_language/Deactivate() active = FALSE - REMOVE_TRAIT(owner, TRAIT_SIGN_LANG, TRAIT_GENERIC) + REMOVE_TRAIT(owner, TRAIT_SIGN_LANG, ACTION_TRAIT) to_chat(owner, span_green("You have stopped using sign language.")) build_all_button_icons(UPDATE_BUTTON_BACKGROUND) diff --git a/code/datums/actions/mobs/sneak.dm b/code/datums/actions/mobs/sneak.dm index 738bb7b70cf..521181fa19b 100644 --- a/code/datums/actions/mobs/sneak.dm +++ b/code/datums/actions/mobs/sneak.dm @@ -16,7 +16,7 @@ /datum/action/cooldown/mob_cooldown/sneak/Remove(mob/living/remove_from) if(HAS_TRAIT(remove_from, TRAIT_SNEAK)) remove_from.alpha = initial(remove_from.alpha) - REMOVE_TRAIT(remove_from, TRAIT_SNEAK, name) + REMOVE_TRAIT(remove_from, TRAIT_SNEAK, ACTION_TRAIT) return ..() @@ -26,11 +26,11 @@ // Otherwise we get permanent invisbility exploits. animate(owner, alpha = initial(owner.alpha), time = animation_time) owner.balloon_alert(owner, "you reveal yourself") - REMOVE_TRAIT(owner, TRAIT_SNEAK, name) + REMOVE_TRAIT(owner, TRAIT_SNEAK, ACTION_TRAIT) else animate(owner, alpha = sneak_alpha, time = animation_time) owner.balloon_alert(owner, "you blend into the environment") - ADD_TRAIT(owner, TRAIT_SNEAK, name) + ADD_TRAIT(owner, TRAIT_SNEAK, ACTION_TRAIT) return TRUE diff --git a/code/game/objects/items/granters/oragami.dm b/code/game/objects/items/granters/oragami.dm index 0b7d6d92615..0691349756c 100644 --- a/code/game/objects/items/granters/oragami.dm +++ b/code/game/objects/items/granters/oragami.dm @@ -21,11 +21,13 @@ check_flags = NONE /datum/action/innate/origami/Activate() + ADD_TRAIT(owner, TRAIT_PAPER_MASTER, ACTION_TRAIT) to_chat(owner, span_notice("You will now fold origami planes.")) active = TRUE build_all_button_icons(UPDATE_BUTTON_ICON) /datum/action/innate/origami/Deactivate() + REMOVE_TRAIT(owner, TRAIT_PAPER_MASTER, ACTION_TRAIT) to_chat(owner, span_notice("You will no longer fold origami planes.")) active = FALSE build_all_button_icons(UPDATE_BUTTON_ICON) diff --git a/code/modules/paperwork/carbonpaper.dm b/code/modules/paperwork/carbonpaper.dm index b2f985dc357..1dfe4ea7821 100644 --- a/code/modules/paperwork/carbonpaper.dm +++ b/code/modules/paperwork/carbonpaper.dm @@ -20,6 +20,12 @@ return . += span_notice("Right-click to tear off the carbon-copy (you must use both hands).") +/obj/item/paper/carbon/AltClick(mob/living/user) + if(!copied) + to_chat(user, span_notice("Take off the carbon copy first.")) + return + return ..() + /obj/item/paper/carbon/proc/removecopy(mob/living/user) if(copied) to_chat(user, span_notice("There are no more carbon copies attached to this paper!")) diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 82517f72494..1c1daaad511 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -319,6 +319,7 @@ /obj/item/paper/examine(mob/user) . = ..() + . += span_notice("Alt-click [src] to fold it into a paper plane.") if(!in_range(user, src) && !isobserver(user)) . += span_warning("You're too far away to read it!") return @@ -358,6 +359,31 @@ return TRUE return ..() +/obj/item/paper/AltClick(mob/living/user) + . = ..() + if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) + return + if(HAS_TRAIT(user, TRAIT_PAPER_MASTER)) + return make_plane(user, /obj/item/paperplane/syndicate) + return make_plane(user, /obj/item/paperplane) + + +/** + * Paper plane folding + * Makes a paperplane depending on args and returns it. + * + * Arguments: + * * mob/living/user - who's folding + * * obj/item/paperplane/plane_type - what it will be folded into (path) + */ +/obj/item/paper/proc/make_plane(mob/living/user, obj/item/paperplane/plane_type = /obj/item/paperplane) + balloon_alert(user, "folded into a plane") + user.temporarilyRemoveItemFromInventory(src) + var/obj/item/paperplane/new_plane = new plane_type(loc, src) + if(user.Adjacent(new_plane)) + user.put_in_hands(new_plane) + return new_plane + /obj/item/proc/burn_paper_product_attackby_check(obj/item/attacking_item, mob/living/user, bypass_clumsy = FALSE) //can't be put on fire! if((resistance_flags & FIRE_PROOF) || !(resistance_flags & FLAMMABLE)) diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm index 3b377ddd6a9..9f1eec94ed0 100644 --- a/code/modules/paperwork/paperplane.dm +++ b/code/modules/paperwork/paperplane.dm @@ -3,6 +3,7 @@ desc = "Paper, folded in the shape of a plane." icon = 'icons/obj/service/bureaucracy.dmi' icon_state = "paperplane" + base_icon_state = "paperplane" custom_fire_overlay = "paperplane_onfire" throw_range = 7 throw_speed = 1 @@ -11,8 +12,10 @@ resistance_flags = FLAMMABLE max_integrity = 50 - var/hit_probability = 2 //% - var/obj/item/paper/internalPaper + ///The chance of hitting a mob in the eye when thrown, in percentage. + var/hit_probability = 2 + ///Reference to the paper that's folded up in this paperplane, which we return when unfolded. + var/obj/item/paper/internal_paper // NOVA EDIT START - Better paper planes /// How long does getting shot in the eyes knock you down for? @@ -27,38 +30,38 @@ /obj/item/paperplane/syndicate desc = "Paper, masterfully folded in the shape of a plane." - throwforce = 20 //same as throwing stars, but no chance of embedding. - hit_probability = 100 //guaranteed to cause eye damage when it hits a mob. + throwforce = 20 + hit_probability = 100 -/obj/item/paperplane/Initialize(mapload, obj/item/paper/newPaper) +/obj/item/paperplane/Initialize(mapload, obj/item/paper/paper_made_of) . = ..() pixel_x = base_pixel_x + rand(-9, 9) pixel_y = base_pixel_y + rand(-8, 8) - if(newPaper) - internalPaper = newPaper - flags_1 = newPaper.flags_1 - color = newPaper.color - newPaper.forceMove(src) + if(paper_made_of) + internal_paper = paper_made_of + flags_1 = paper_made_of.flags_1 + color = paper_made_of.color + paper_made_of.forceMove(src) else - internalPaper = new(src) - if(internalPaper.icon_state == "cpaper" || internalPaper.icon_state == "cpaper_words") - icon_state = "paperplane_carbon" // It's the purple carbon copy. Use the purple paper plane - update_appearance() + internal_paper = new(src) + if(istype(internal_paper, /obj/item/paper/carbon_copy)) + icon_state = "[base_icon_state]_carbon" + update_appearance(UPDATE_ICON) /obj/item/paperplane/Exited(atom/movable/gone, direction) . = ..() - if (internalPaper == gone) - internalPaper = null + if (internal_paper == gone) + internal_paper = null if(!QDELETED(src)) qdel(src) /obj/item/paperplane/Destroy() - internalPaper = null + internal_paper = null return ..() /obj/item/paperplane/suicide_act(mob/living/user) var/obj/item/organ/internal/eyes/eyes = user.get_organ_slot(ORGAN_SLOT_EYES) - user.Stun(200) + user.Stun(20 SECONDS) user.visible_message(span_suicide("[user] jams [src] in [user.p_their()] nose. It looks like [user.p_theyre()] trying to commit suicide!")) user.adjust_eye_blur(12 SECONDS) if(eyes) @@ -68,8 +71,8 @@ /obj/item/paperplane/update_overlays() . = ..() - for(var/stamp in internalPaper.stamp_cache) - . += "paperplane_[stamp]" + for(var/stamp in internal_paper.stamp_cache) + . += "[base_icon_state]_[stamp]" /obj/item/paperplane/attack_self(mob/user) balloon_alert(user, "unfolded") @@ -77,94 +80,45 @@ var/atom/location = drop_location() // Need to keep a reference to the internal paper // when we move it out of the plane, our ref gets set to null - var/obj/item/paper/internal_paper = internalPaper - internal_paper.forceMove(location) + var/obj/item/paper/released_paper = internal_paper + released_paper.forceMove(location) // This will as a side effect, qdel the paper plane, making the user's hands empty - user.put_in_hands(internal_paper) + user.put_in_hands(released_paper) -/obj/item/paperplane/attackby(obj/item/P, mob/living/carbon/human/user, params) - if(burn_paper_product_attackby_check(P, user)) +/obj/item/paperplane/attackby(obj/item/attacking_item, mob/user, params) + if(burn_paper_product_attackby_check(attacking_item, user)) return - if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) + if(istype(attacking_item, /obj/item/pen) || istype(attacking_item, /obj/item/toy/crayon)) to_chat(user, span_warning("You should unfold [src] before changing it!")) return - - else if(istype(P, /obj/item/stamp)) //we don't randomize stamps on a paperplane - internalPaper.attackby(P, user) //spoofed attack to update internal paper. + else if(istype(attacking_item, /obj/item/stamp)) //we don't randomize stamps on a paperplane + internal_paper.attackby(attacking_item, user) //spoofed attack to update internal paper. update_appearance() add_fingerprint(user) return - return ..() - -/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback, gentle, quickstart = TRUE) - . = ..(target, range, speed, thrower, FALSE, diagonals_first, callback, quickstart = quickstart) - /obj/item/paperplane/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) - if(iscarbon(hit_atom)) - var/mob/living/carbon/C = hit_atom - if(C.can_catch_item(TRUE)) - var/datum/action/innate/origami/origami_action = locate() in C.actions - if(origami_action?.active) //if they're a master of origami and have the ability turned on, force throwmode on so they'll automatically catch the plane. - C.throw_mode_on(THROW_MODE_TOGGLE) - - if(..() || !ishuman(hit_atom))//if the plane is caught or it hits a nonhuman - // NOVA EDIT START - Better paper planes - if(delete_on_impact) - qdel(src) - // NOVA EDIT END - return - var/mob/living/carbon/human/H = hit_atom - var/obj/item/organ/internal/eyes/eyes = H.get_organ_slot(ORGAN_SLOT_EYES) - if(prob(hit_probability)) - if(H.is_eyes_covered()) - // NOVA EDIT START - Better paper planes - if(delete_on_impact) - qdel(src) - // NOVA EDIT END - return - visible_message(span_danger("\The [src] hits [H] in the eye[eyes ? "" : " socket"]!")) - H.adjust_eye_blur(12 SECONDS) - eyes?.apply_organ_damage(rand(impact_eye_damage_lower, impact_eye_damage_higher)) - H.Knockdown(40) - H.emote("scream") + if(iscarbon(hit_atom) && HAS_TRAIT(hit_atom, TRAIT_PAPER_MASTER)) + var/mob/living/carbon/hit_carbon = hit_atom + if(hit_carbon.can_catch_item(TRUE)) + hit_carbon.throw_mode_on(THROW_MODE_TOGGLE) - if(delete_on_impact) - qdel(src) - // NOVA EDIT END - -/obj/item/paper/examine(mob/user) . = ..() - . += span_notice("Alt-click [src] to fold it into a paper plane.") - -/obj/item/paper/AltClick(mob/living/user, obj/item/I) - if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) + if(. || !ishuman(hit_atom)) //if the plane is caught or it hits a nonhuman return - if(istype(src, /obj/item/paper/carbon)) - var/obj/item/paper/carbon/Carbon = src - if(!Carbon.copied) - to_chat(user, span_notice("Take off the carbon copy first.")) - return - //Origami Master - var/datum/action/innate/origami/origami_action = locate() in user.actions - if(origami_action?.active) - make_plane(user, I, /obj/item/paperplane/syndicate) - else - make_plane(user, I, /obj/item/paperplane) + var/mob/living/carbon/human/hit_human = hit_atom + var/obj/item/organ/internal/eyes/eyes = hit_human.get_organ_slot(ORGAN_SLOT_EYES) + if(!prob(hit_probability)) + return + if(hit_human.is_eyes_covered()) + return + visible_message(span_danger("\The [src] hits [hit_human] in the eye[eyes ? "" : " socket"]!")) + hit_human.adjust_eye_blur(12 SECONDS) + eyes?.apply_organ_damage(rand(6, 8)) + hit_human.Paralyze(4 SECONDS) + hit_human.emote("scream") -/** - * Paper plane folding - * - * Arguments: - * * mob/living/user - who's folding - * * obj/item/I - what's being folded - * * obj/item/paperplane/plane_type - what it will be folded into (path) - */ -/obj/item/paper/proc/make_plane(mob/living/user, obj/item/I, obj/item/paperplane/plane_type = /obj/item/paperplane) - balloon_alert(user, "folded into a plane") - user.temporarilyRemoveItemFromInventory(src) - I = new plane_type(loc, src) - if(user.Adjacent(I)) - user.put_in_hands(I) +/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback, gentle, quickstart = TRUE) + return ..(target, range, speed, thrower, FALSE, diagonals_first, callback, quickstart = quickstart) diff --git a/modular_nova/master_files/code/modules/paperwork/paperplane.dm b/modular_nova/master_files/code/modules/paperwork/paperplane.dm new file mode 100644 index 00000000000..d21571ed171 --- /dev/null +++ b/modular_nova/master_files/code/modules/paperwork/paperplane.dm @@ -0,0 +1,5 @@ +// Better paper planes +/obj/item/paperplane/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if(delete_on_impact) + qdel(src) diff --git a/tgstation.dme b/tgstation.dme index 4dd13ce4940..8c5f18b7572 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -6505,6 +6505,7 @@ #include "modular_nova\master_files\code\modules\modular_computers\file_system\programs\maintenance\camera.dm" #include "modular_nova\master_files\code\modules\pai\card.dm" #include "modular_nova\master_files\code\modules\paperwork\employment_contract.dm" +#include "modular_nova\master_files\code\modules\paperwork\paperplane.dm" #include "modular_nova\master_files\code\modules\paperwork\stamps.dm" #include "modular_nova\master_files\code\modules\power\cable.dm" #include "modular_nova\master_files\code\modules\power\powernet.dm"