diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 9da5143c6100..65aaa36fbb9f 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -331,6 +331,10 @@ GLOBAL_LIST_INIT(human_invader_antagonists, list( #define ANTAG_GROUP_SYNDICATE "Syndicate" #define ANTAG_GROUP_WIZARDS "Wizard Federation" #define ANTAG_GROUP_XENOS "Xenomorph Infestation" +#define ANTAG_GROUP_FUGITIVES "Escaped Fugitives" +#define ANTAG_GROUP_HUNTERS "Bounty Hunters" +#define ANTAG_GROUP_PARADOX "Spacetime Aberrations" +#define ANTAG_GROUP_CREW "Deviant Crew" #define HUNTER_PACK_COPS "Spacepol Officers" #define HUNTER_PACK_RUSSIAN "Russian Smugglers" diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 14715f7a7a90..6d73b04a78e6 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -33,6 +33,13 @@ #define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" #define COLOR_RED "#FF0000" +#define COLOR_CHRISTMAS_RED "#D6001C" +#define COLOR_OLD_GLORY_RED "#B22234" +#define COLOR_FRENCH_RED "#EF4135" +#define COLOR_ETHIOPIA_RED "#DA121A" +#define COLOR_UNION_JACK_RED "#C8102E" +#define COLOR_MEDIUM_DARK_RED "#CC0000" +#define COLOR_PINK_RED "EF3340" #define COLOR_SYNDIE_RED "#F10303" #define COLOR_SYNDIE_RED_HEAD "#760500" #define COLOR_MOSTLY_PURE_RED "#FF3300" @@ -50,7 +57,10 @@ #define COLOR_YELLOW "#FFFF00" #define COLOR_VIVID_YELLOW "#FBFF23" +#define COLOR_TANGERINE_YELLOW "#FFCC00" #define COLOR_VERY_SOFT_YELLOW "#FAE48E" +#define COLOR_GOLD "#FFD700" +#define COLOR_ETHIOPIA_YELLOW "#FCDD09" #define COLOR_OLIVE "#808000" #define COLOR_ASSISTANT_OLIVE "#828163" @@ -63,6 +73,9 @@ #define COLOR_VERY_PALE_LIME_GREEN "#DDFFD3" #define COLOR_VERY_DARK_LIME_GREEN "#003300" #define COLOR_GREEN "#008000" +#define COLOR_CHRISTMAS_GREEN "#00873E" +#define COLOR_IRISH_GREEN "#169B62" +#define COLOR_ETHIOPIA_GREEN "#078930" #define COLOR_DARK_MODERATE_LIME_GREEN "#44964A" #define COLOR_PAI_GREEN "#00FF88" #define COLOR_PALE_GREEN "#20e28e" @@ -72,6 +85,10 @@ #define COLOR_DARK_CYAN "#00A2FF" #define COLOR_TEAL "#008080" #define COLOR_BLUE "#0000FF" +#define COLOR_OLD_GLORY_BLUE "#3C3B6E" +#define COLOR_FRENCH_BLUE "#0055A4" +#define COLOR_UNION_JACK_BLUE "#012169" +#define COLOR_TRUE_BLUE "#0066CC" #define COLOR_STRONG_BLUE "#1919c8" #define COLOR_CENTCOM_BLUE "#134975" #define COLOR_BRIGHT_BLUE "#2CB2E8" @@ -99,6 +116,7 @@ #define COLOR_DARK_PURPLE "#551A8B" #define COLOR_ORANGE "#FF9900" +#define COLOR_IRISH_ORANGE "#FF883E" #define COLOR_ENGINEERING_ORANGE "#FFA62B" #define COLOR_MOSTLY_PURE_ORANGE "#ff8000" #define COLOR_TAN_ORANGE "#FF7B00" @@ -248,12 +266,13 @@ #define CIRCUIT_COLOR_ENGINEERING "#F8D700" #define CIRCUIT_COLOR_SUPPLY "#C47749" -/// Highly Saturated Colors -#define COLOR_BRIGHT_RED "#FF6666" -#define COLOR_BRIGHT_YELLOW "#EAFF51" -#define COLOR_BRIGHT_GREEN "#41FC66" -#define COLOR_BRIGHT_TEAL "#42FFF2" -#define COLOR_BRIGHT_PURPLE "#5D5DFC" +/// Colors for pride week +#define COLOR_PRIDE_RED "#FF6666" +#define COLOR_PRIDE_ORANGE "#FC9F3C" +#define COLOR_PRIDE_YELLOW "#EAFF51" +#define COLOR_PRIDE_GREEN "#41FC66" +#define COLOR_PRIDE_BLUE "#42FFF2" +#define COLOR_PRIDE_PURPLE "#5D5DFC" /// The default color for admin say, used as a fallback when the preference is not enabled #define DEFAULT_ASAY_COLOR COLOR_MOSTLY_PURE_RED diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm index 09ac2c73e2cb..9ff14135e717 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm @@ -28,3 +28,5 @@ #define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" ///from base of mob/MouseWheelOn(): (/atom, delta_x, delta_y, params) #define COMSIG_MOUSE_SCROLL_ON "mousescroll_on" +/// From /atom/movable/screen/click(): (atom/target, atom/location, control, params, mob/user) +#define COMSIG_SCREEN_ELEMENT_CLICK "screen_element_click" diff --git a/code/__DEFINES/dcs/signals/signals_storage.dm b/code/__DEFINES/dcs/signals/signals_storage.dm index b30039dc12f2..45b6ec6bfe3f 100644 --- a/code/__DEFINES/dcs/signals/signals_storage.dm +++ b/code/__DEFINES/dcs/signals/signals_storage.dm @@ -5,3 +5,5 @@ /// Sent after dumping into some other storage object: (atom/dest_object, mob/user) #define COMSIG_STORAGE_DUMP_POST_TRANSFER "storage_dump_into_storage" +/// Sent to the STORAGE when an ITEM is STORED INSIDE. +#define COMSIG_STORAGE_STORED_ITEM "storage_storing_item" diff --git a/code/__DEFINES/holiday.dm b/code/__DEFINES/holiday.dm new file mode 100644 index 000000000000..1c35940e7188 --- /dev/null +++ b/code/__DEFINES/holiday.dm @@ -0,0 +1 @@ +#define HOLIDAY_HAT_CHANCE 20 diff --git a/code/__DEFINES/interaction_flags.dm b/code/__DEFINES/interaction_flags.dm index 0b4e95887294..6ce2928b741b 100644 --- a/code/__DEFINES/interaction_flags.dm +++ b/code/__DEFINES/interaction_flags.dm @@ -20,6 +20,14 @@ #define INTERACT_ATOM_ALLOW_USER_LOCATION (1<<9) /// ignores mobility check #define INTERACT_ATOM_IGNORE_MOBILITY (1<<10) +// Bypass all adjacency checks for mouse drop +#define INTERACT_ATOM_MOUSEDROP_IGNORE_ADJACENT (1<<11) +/// Bypass all can_perform_action checks for mouse drop +#define INTERACT_ATOM_MOUSEDROP_IGNORE_USABILITY (1<<12) +/// Bypass all adjacency and other checks for mouse drop +#define INTERACT_ATOM_MOUSEDROP_IGNORE_CHECKS (INTERACT_ATOM_MOUSEDROP_IGNORE_ADJACENT | INTERACT_ATOM_MOUSEDROP_IGNORE_USABILITY) +/// calls try_interact() on attack_paw() and returns that. +#define INTERACT_ATOM_ATTACK_PAW (1<<13) /// attempt pickup on attack_hand for items #define INTERACT_ITEM_ATTACK_HAND_PICKUP (1<<0) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index c743a6d5a673..db9e10b6ae17 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -286,7 +286,7 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list( GLOBAL_LIST_INIT(book_types, typecacheof(list( /obj/item/book, /obj/item/spellbook, -))) + /obj/item/infuser_book))) // Jobs #define is_job(job_type) (istype(job_type, /datum/job)) diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 88005dacbd4a..65b0d3c49837 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -207,8 +207,11 @@ #define JOB_ASSIGN_QUIRKS (1<<7) /// Whether this job can be an intern. #define JOB_CAN_BE_INTERN (1<<8) -/// Whether this job is enabled/disabled by the spooktober config -#define JOB_SPOOKTOBER (1<<9) +/// This job cannot have more slots opened by the Head of Personnel (but admins or other random events can still do this). +#define JOB_CANNOT_OPEN_SLOTS (1<<9) + +/// Combination flag for jobs which are considered regular crew members of the station. +#define STATION_JOB_FLAGS (JOB_ANNOUNCE_ARRIVAL|JOB_CREW_MANIFEST|JOB_EQUIP_RANK|JOB_CREW_MEMBER|JOB_NEW_PLAYER_JOINABLE|JOB_REOPEN_ON_ROUNDSTART_LOSS|JOB_ASSIGN_QUIRKS|JOB_CAN_BE_INTERN) #define FACTION_NONE "None" #define FACTION_STATION "Station" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index fb0c39de245c..714d416b75b8 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -590,14 +590,15 @@ ///Squash flags. For squashable element -///Whether or not the squashing requires the squashed mob to be lying down +/// Squashing will not occur if the mob is not lying down (bodyposition is LYING_DOWN) #define SQUASHED_SHOULD_BE_DOWN (1<<0) -///Whether or not to gib when the squashed mob is moved over +/// If present, outright gibs the squashed mob instead of just dealing damage #define SQUASHED_SHOULD_BE_GIBBED (1<<1) - - +/// If squashing always passes if the mob is dead +#define SQUASHED_ALWAYS_IF_DEAD (1<<2) /// Don't squash our mob if its not located in a turf #define SQUASHED_DONT_SQUASH_IN_CONTENTS (1<<3) + /* * Defines for "AI emotions", allowing the AI to expression emotions * with status displays via emotes. diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 229433bc0f86..8348f1d0fe3e 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -36,6 +36,7 @@ #define FESTIVE_SEASON "Festive Season" #define GARBAGEDAY "Garbage Day" #define MONKEYDAY "Monkey Day" +#define PRIDE_WEEK "Pride Week" #define MOTH_WEEK "Moth Week" #define IAN_HOLIDAY "Ian's Birthday" /* diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 6f5d4e3e78a3..281c2ba5f246 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -125,13 +125,13 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// This human is immune to the effects of being exploded. (ex_act) #define TRAIT_BOMBIMMUNE "bomb_immunity" /// This mob won't get gibbed by nukes going off -/* #define TRAIT_NUKEIMMUNE "nuke_immunity" */ +#define TRAIT_NUKEIMMUNE "nuke_immunity" /// Can't be given viruses #define TRAIT_VIRUSIMMUNE "virus_immunity" /// Won't become a husk under any circumstances /* #define TRAIT_UNHUSKABLE "trait_unhuskable" */ /// Reduces the chance viruses will spread to this mob, and if the mob has a virus, slows its advancement -/* #define TRAIT_VIRUS_RESISTANCE "virus_resistance" */ +#define TRAIT_VIRUS_RESISTANCE "virus_resistance" #define TRAIT_GENELESS "geneless" #define TRAIT_PIERCEIMMUNE "pierce_immunity" #define TRAIT_NODISMEMBER "dismember_immunity" @@ -1022,9 +1022,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Isn't attacked harmfully by blob structures #define TRAIT_BLOB_ALLY "blob_ally" /// Has the chuuni component -/* #define TRAIT_CHUUNIBYOU "chuunibyou" */ +#define TRAIT_CHUUNIBYOU "chuunibyou" /// Has splattercasting -/* #define TRAIT_SPLATTERCASTER "splattercaster" */ +#define TRAIT_SPLATTERCASTER "splattercaster" ///Traits given by station traits /* #define STATION_TRAIT_ASSISTANT_GIMMICKS "station_trait_assistant_gimmicks" */ @@ -1154,7 +1154,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// This atom can have spells cast from it if a mob is within it /// This means the "caster" of the spell is changed to the mob's loc /// Note this doesn't mean all spells are guaranteed to work or the mob is guaranteed to cast -/* #define TRAIT_CASTABLE_LOC "castable_loc" */ +#define TRAIT_CASTABLE_LOC "castable_loc" /// Needs above trait to work. /// This trait makes it so that any cast spells will attempt to transfer to the location's location. diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index 48844e079c16..492248acae60 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -96,3 +96,14 @@ #define TURF_PATHING_PASS_PROC 1 /// Turf is never passable #define TURF_PATHING_PASS_NO 2 + +/// Define the alpha for holiday/colored tile decals +#define DECAL_ALPHA 60 +/// Generate horizontal striped color turf decals +#define PATTERN_DEFAULT "default" +/// Generate vertical striped color turf decals +#define PATTERN_VERTICAL_STRIPE "vertical" +/// Generate random color turf decals +#define PATTERN_RANDOM "random" +/// Generate rainbow color turf decals +#define PATTERN_RAINBOW "rainbow" diff --git a/code/__DEFINES/~monkestation/blueshift.dm b/code/__DEFINES/~monkestation/blueshift.dm index c327e4e56a0f..a52313a85ad5 100644 --- a/code/__DEFINES/~monkestation/blueshift.dm +++ b/code/__DEFINES/~monkestation/blueshift.dm @@ -270,14 +270,6 @@ See the examinemore module for information. #define BOOT_UNSEAL_MESSAGE "relax their grip on your legs" #define BOOT_SEAL_MESSAGE "seal around your feet" -/// Colors for pride week -#define COLOR_PRIDE_RED "#FF6666" -#define COLOR_PRIDE_ORANGE "#FC9F3C" -#define COLOR_PRIDE_YELLOW "#EAFF51" -#define COLOR_PRIDE_GREEN "#41FC66" -#define COLOR_PRIDE_BLUE "#42FFF2" -#define COLOR_PRIDE_PURPLE "#5D5DFC" - /// Trait that changes the ending effects of twitch leaving your system #define TRAIT_TWITCH_ADAPTED "twitch_adapted" diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_object.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_object.dm new file mode 100644 index 000000000000..7e3bfcc3e769 --- /dev/null +++ b/code/__DEFINES/~monkestation/dcs/signals/signals_object.dm @@ -0,0 +1,2 @@ +//flag to block the qdel that normally happens when a projectile is blocked +#define PROJECTILE_INTERRUPT_BLOCK_QDEL (4<<0) diff --git a/code/__DEFINES/~monkestation/jobs.dm b/code/__DEFINES/~monkestation/jobs.dm index 2a2c5ae5e3a4..1902ca9ced9b 100644 --- a/code/__DEFINES/~monkestation/jobs.dm +++ b/code/__DEFINES/~monkestation/jobs.dm @@ -1 +1,3 @@ #define JOB_SECURITY_ASSISTANT "Security Assistant" +/// Whether this job is enabled/disabled by the spooktober config +#define JOB_SPOOKTOBER (1<<16) diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index 6b7e5543d579..9fe3db3ada11 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -68,6 +68,20 @@ //doesn't have an object argument because this is "Stacking" with the animate call above //3 billion% intentional +/// Similar to shake but more spasm-y and jerk-y +/atom/proc/spasm_animation(loops = -1) + var/list/transforms = list( + matrix(transform).Translate(-1, 0), + matrix(transform).Translate(0, 1), + matrix(transform).Translate(1, 0), + matrix(transform).Translate(0, -1), + ) + + animate(src, transform = transforms[1], time = 0.2, loop = loops) + animate(transform = transforms[2], time = 0.1) + animate(transform = transforms[3], time = 0.2) + animate(transform = transforms[4], time = 0.3) + /** * Shear the transform on either or both axes. * * x - X axis shearing diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index dd15e825f550..bcb83d5d97e5 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -197,9 +197,12 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items /obj/item/pen/screwdriver = 1, ) = 8, +//monkestation edit start list(//artifacts /obj/effect/artifact_spawner = 4, + /obj/item/a_gift/anything/wiz_name = 2, ) = 8, +//monkestation edit end list(//construction and crafting /obj/item/beacon = 1, @@ -349,14 +352,15 @@ GLOBAL_LIST_INIT(rarity_loot, list(//rare: really good items ) = 1, list(//misc - /obj/item/book/granter/crafting_recipe/maint_gun/pipegun_prime = 1, + /obj/item/book/granter/crafting_recipe/maint_gun/pipegun_prime = 1, //monkestation edit: added maint_gun /obj/item/book/granter/crafting_recipe/trash_cannon = 1, - /obj/item/book/granter/crafting_recipe/maint_gun/laser_musket_prime = 1, + /obj/item/book/granter/crafting_recipe/maint_gun/laser_musket_prime = 1, //monkestation edit /obj/item/book/granter/sign_language = 1, /obj/item/disk/nuclear/fake = 1, /obj/item/skillchip/brainwashing = 1, /obj/item/tattoo_kit = 1, /obj/item/folder/ancient_paperwork = 1, + /obj/item/seeds/tree/money = 1, //monkestation edit ) = 1, )) diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index ad5832caf686..c11a1b6a1cf5 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -33,6 +33,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_AREA_SENSITIVE" = TRAIT_AREA_SENSITIVE, "TRAIT_ASHSTORM_IMMUNE" = TRAIT_ASHSTORM_IMMUNE, "TRAIT_BLOCKING_EXPLOSIVES" = TRAIT_BLOCKING_EXPLOSIVES, + "TRAIT_CASTABLE_LOC" = TRAIT_CASTABLE_LOC, "TRAIT_DEL_ON_SPACE_DUMP" = TRAIT_DEL_ON_SPACE_DUMP, "TRAIT_FISH_CASE_COMPATIBILE" = TRAIT_FISH_CASE_COMPATIBILE, "TRAIT_FISH_SAFE_STORAGE" = TRAIT_FISH_SAFE_STORAGE, @@ -56,7 +57,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_VOIDSTORM_IMMUNE" = TRAIT_VOIDSTORM_IMMUNE, "TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE, /* "TRAIT_BOULDER_BREAKER" = TRAIT_BOULDER_BREAKER, */ - /* "TRAIT_CASTABLE_LOC" = TRAIT_CASTABLE_LOC, */ /* "TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER, */ /* "TRAIT_HAS_LABEL" = TRAIT_HAS_LABEL, */ /* "TRAIT_IMMERSED" = TRAIT_IMMERSED, */ @@ -179,6 +179,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CHEF_KISS" = TRAIT_CHEF_KISS, "TRAIT_CHUNKYFINGERS" = TRAIT_CHUNKYFINGERS, "TRAIT_CHUNKYFINGERS_IGNORE_BATON" = TRAIT_CHUNKYFINGERS_IGNORE_BATON, + "TRAIT_CHUUNIBYOU" = TRAIT_CHUUNIBYOU, "TRAIT_CLEANBOT_WHISPERER" = TRAIT_CLEANBOT_WHISPERER, "TRAIT_CLIFF_WALKER" = TRAIT_CLIFF_WALKER, "TRAIT_CLOWN_ENJOYER" = TRAIT_CLOWN_ENJOYER, @@ -365,6 +366,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NO_TRANSFORM" = TRAIT_NO_TRANSFORM, "TRAIT_NO_UNDERWEAR" = TRAIT_NO_UNDERWEAR, "TRAIT_NO_ZOMBIFY" = TRAIT_NO_ZOMBIFY, + "TRAIT_NUKEIMMUNE" = TRAIT_NUKEIMMUNE, "TRAIT_OCCULTIST" = TRAIT_OCCULTIST, "TRAIT_OIL_FRIED" = TRAIT_OIL_FRIED, "TRAIT_ON_ELEVATED_SURFACE" = TRAIT_ON_ELEVATED_SURFACE, @@ -443,6 +445,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SPECIAL_TRAUMA_BOOST" = TRAIT_SPECIAL_TRAUMA_BOOST, "TRAIT_SPIDER_CONSUMED" = TRAIT_SPIDER_CONSUMED, "TRAIT_SPIRITUAL" = TRAIT_SPIRITUAL, + "TRAIT_SPLATTERCASTER" = TRAIT_SPLATTERCASTER, "TRAIT_SPRAY_PAINTABLE" = TRAIT_SPRAY_PAINTABLE, "TRAIT_SPRINTING" = TRAIT_SPRINTING, "TRAIT_STABLEHEART" = TRAIT_STABLEHEART, @@ -491,6 +494,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_VENTCRAWLER_ALWAYS" = TRAIT_VENTCRAWLER_ALWAYS, "TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE, "TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE, + "TRAIT_VIRUS_RESISTANCE" = TRAIT_VIRUS_RESISTANCE, "TRAIT_VORACIOUS" = TRAIT_VORACIOUS, "TRAIT_WAS_EVOLVED" = TRAIT_WAS_EVOLVED, "TRAIT_WATER_BREATHING" = TRAIT_WATER_BREATHING, @@ -516,7 +520,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( /* "TRAIT_BOXING_READY" = TRAIT_BOXING_READY, */ /* "TRAIT_BRAWLING_KNOCKDOWN_BLOCKED" = TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, */ /* "TRAIT_CATLIKE_GRACE" = TRAIT_CATLIKE_GRACE, */ - /* "TRAIT_CHUUNIBYOU" = TRAIT_CHUUNIBYOU, */ /* "TRAIT_DETECTIVES_TASTE" = TRAIT_DETECTIVES_TASTE, */ /* "TRAIT_DISCO_DANCER" = TRAIT_DISCO_DANCER, */ /* "TRAIT_DISPLAY_JOB_IN_BINARY" = TRAIT_DISPLAY_JOB_IN_BINARY, */ @@ -559,7 +562,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( /* "TRAIT_NO_STAGGER" = TRAIT_NO_STAGGER, */ /* "TRAIT_NO_THROWING" = TRAIT_NO_THROWING, */ /* "TRAIT_NO_TWOHANDING" = TRAIT_NO_TWOHANDING, */ - /* "TRAIT_NUKEIMMUNE" = TRAIT_NUKEIMMUNE, */ /* "TRAIT_OFF_BALANCE_TACKLER" = TRAIT_OFF_BALANCE_TACKLER, */ /* "TRAIT_OVERDOSEIMMUNE" = TRAIT_OVERDOSEIMMUNE, */ /* "TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER, */ @@ -578,7 +580,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( /* "TRAIT_SLOW_FLIP" = TRAIT_SLOW_FLIP, */ /* "TRAIT_SPEECH_BOOSTER" = TRAIT_SPEECH_BOOSTER, */ /* "TRAIT_SPELLS_LOTTERY" = TRAIT_SPELLS_LOTTERY, */ - /* "TRAIT_SPLATTERCASTER" = TRAIT_SPLATTERCASTER, */ /* "TRAIT_STIMMED" = TRAIT_STIMMED, */ /* "TRAIT_STIMULATED" = TRAIT_STIMULATED, */ /* "TRAIT_STRENGTH" = TRAIT_STRENGTH, */ @@ -595,7 +596,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( /* "TRAIT_TOSS_GUN_HARD" = TRAIT_TOSS_GUN_HARD, */ /* "TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE, */ /* "TRAIT_USER_SCOPED" = TRAIT_USER_SCOPED, */ - /* "TRAIT_VIRUS_RESISTANCE" = TRAIT_VIRUS_RESISTANCE, */ /* "TRAIT_WOUND_LICKER" = TRAIT_WOUND_LICKER, */ /* "TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING, */ ), diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index cedbeb28f2d7..302cd10c28f0 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -1059,6 +1059,9 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." return 1 /atom/movable/screen/alert/Click(location, control, params) + SHOULD_CALL_PARENT(TRUE) + + ..() if(!usr || !usr.client) return FALSE if(usr != owner) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 87fee0d97f30..13c02c7f9533 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -35,12 +35,24 @@ /// If FALSE, this will not be cleared when calling /client/clear_screen() var/clear_with_screen = TRUE + /// If TRUE, clicking the screen element will fall through and perform a default "Click" call + /// Obviously this requires your Click override, if any, to call parent on their own. + /// This is set to FALSE to default to dissade you from doing this. + /// Generally we don't want default Click stuff, which results in bugs like using Telekinesis on a screen element + /// or trying to point your gun at your screen. + var/default_click = FALSE /atom/movable/screen/Destroy() master = null hud = null return ..() +/atom/movable/screen/Click(location, control, params) + if(flags_1 & INITIALIZED_1) + SEND_SIGNAL(src, COMSIG_SCREEN_ELEMENT_CLICK, location, control, params, usr) + if(default_click) + return ..() + /atom/movable/screen/examine(mob/user) return list() @@ -646,9 +658,6 @@ icon_state = "mood5" screen_loc = ui_mood -/atom/movable/screen/mood/attack_tk() - return - /atom/movable/screen/splash icon = 'icons/blanks/blank_title.png' icon_state = "" diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 5f1667d38c35..5a124d14493c 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -194,7 +194,8 @@ /atom/proc/attack_paw(mob/user, list/modifiers) if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_PAW, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN) return TRUE - return FALSE + if(interaction_flags_atom & INTERACT_ATOM_ATTACK_PAW) + . = _try_interact(user) /* diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm index 5d11d479dd26..46cced9ef7bb 100644 --- a/code/controllers/subsystem/explosions.dm +++ b/code/controllers/subsystem/explosions.dm @@ -353,9 +353,8 @@ SUBSYSTEM_DEF(explosions) if(adminlog) message_admins("Explosion with size (Devast: [devastation_range], Heavy: [heavy_impact_range], Light: [light_impact_range], Flame: [flame_range]) in [ADMIN_VERBOSEJMP(epicenter)]. Possible cause: [explosion_cause]. Last fingerprints: [who_did_it].") log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in [loc_name(epicenter)]. Possible cause: [explosion_cause]. Last fingerprints: [who_did_it_game_log].") - //monkestation edit start - deadchat_broadcast("Explosion with size: Devast: [devastation_range], Heavy: [heavy_impact_range], Light: [light_impact_range], Flame: [flame_range].", \ + deadchat_broadcast("Explosion with size: Devast: [devastation_range], Heavy: [heavy_impact_range], Light: [light_impact_range], Flame: [flame_range].", \ turf_target = epicenter, message_type = DEADCHAT_ANNOUNCEMENT) //monkestation edit end diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 562d0361a27c..7a9915fdd229 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -100,6 +100,7 @@ SUBSYSTEM_DEF(job) new_overflow.allow_bureaucratic_error = FALSE new_overflow.spawn_positions = cap new_overflow.total_positions = cap + new_overflow.job_flags |= JOB_CANNOT_OPEN_SLOTS if(new_overflow.type == overflow_role) return @@ -107,6 +108,8 @@ SUBSYSTEM_DEF(job) old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error) old_overflow.spawn_positions = initial(old_overflow.spawn_positions) old_overflow.total_positions = initial(old_overflow.total_positions) + if(!(initial(old_overflow.job_flags) & JOB_CANNOT_OPEN_SLOTS)) + old_overflow.job_flags &= ~JOB_CANNOT_OPEN_SLOTS overflow_role = new_overflow.type JobDebug("Overflow role set to : [new_overflow.type]") diff --git a/code/datums/components/bloody_spreader.dm b/code/datums/components/bloody_spreader.dm new file mode 100644 index 000000000000..2a90a1e27c80 --- /dev/null +++ b/code/datums/components/bloody_spreader.dm @@ -0,0 +1,46 @@ +/datum/component/bloody_spreader + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + // How many bloodening instances are left. Deleted on zero. + var/blood_left + // We will spread this blood DNA to targets! + var/list/blood_dna + // Blood splashed around everywhere will carry these diseases. Oh no... + var/list/diseases + +/datum/component/bloody_spreader/Initialize(blood_left, list/blood_dna, list/diseases) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + var/list/signals_to_add = list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACKBY) + if(ismovable(parent)) + signals_to_add += list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT) + if(isitem(parent)) + //monkestation temp edit: replaced COMSIG_ITEM_ATTACK_ATOM with COMSIG_ITEM_ATTACK_OBJ + signals_to_add += list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ, COMSIG_ITEM_HIT_REACT, COMSIG_ITEM_ATTACK_SELF, COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED) + var/atom/atom_parent = parent + if(atom_parent.atom_storage) + signals_to_add += list(COMSIG_STORAGE_STORED_ITEM) + else if(isstructure(parent)) + signals_to_add += list(COMSIG_ATOM_ATTACK_HAND) + + RegisterSignals(parent, signals_to_add, PROC_REF(spread_yucky_blood)) + + if(isclothing(parent)) + parent.AddComponent(/datum/component/bloodysoles) + + src.blood_left = blood_left + src.blood_dna = blood_dna + src.diseases = diseases + +/datum/component/bloody_spreader/proc/spread_yucky_blood(atom/parent, atom/bloody_fool) + SIGNAL_HANDLER + bloody_fool.add_blood_DNA(blood_dna, diseases) + +/datum/component/bloody_spreader/InheritComponent(/datum/component/new_comp, i_am_original, blood_left = 0) + + if(!i_am_original) + return + + if(src.blood_left >= INFINITY) + return + + src.blood_left += blood_left diff --git a/code/datums/components/chuunibyou.dm b/code/datums/components/chuunibyou.dm index 00396a289b83..54e65a1400cc 100644 --- a/code/datums/components/chuunibyou.dm +++ b/code/datums/components/chuunibyou.dm @@ -47,6 +47,7 @@ RegisterSignal(parent, COMSIG_MOB_PRE_INVOCATION, PROC_REF(on_pre_invocation)) RegisterSignal(parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(parent, COMSIG_MOB_AFTER_SPELL_CAST, PROC_REF(on_after_spell_cast)) + ADD_TRAIT(parent, TRAIT_CHUUNIBYOU, REF(src)) /datum/component/chuunibyou/UnregisterFromParent() . = ..() @@ -56,6 +57,7 @@ COMSIG_LIVING_TRY_SPEECH, COMSIG_MOB_AFTER_SPELL_CAST, )) + REMOVE_TRAIT(parent, TRAIT_CHUUNIBYOU, REF(src)) /// signal sent when the parent tries to speak. we let speech pass if we are casting a spell so mimes still chuuni their spellcasts /// (this may end in the mime dying) diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index 4b3e84a26ab6..55c5cb598cc7 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -10,7 +10,7 @@ C.icon = H.ui_style H.static_inventory += C CL.screen += C - RegisterSignal(C, COMSIG_CLICK, PROC_REF(component_ui_interact)) + RegisterSignal(C, COMSIG_SCREEN_ELEMENT_CLICK, PROC_REF(component_ui_interact)) #define COOKING TRUE #define CRAFTING FALSE diff --git a/code/datums/components/lock_on_cursor.dm b/code/datums/components/lock_on_cursor.dm index 624028a4671d..7174256d8529 100644 --- a/code/datums/components/lock_on_cursor.dm +++ b/code/datums/components/lock_on_cursor.dm @@ -43,6 +43,7 @@ datum/callback/on_click_callback, datum/callback/on_lock, datum/callback/can_target_callback, + catcher_default_click = TRUE, //monkestation edit ) if(!ismob(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/mind_linker.dm b/code/datums/components/mind_linker.dm index 73011707cd95..8cc2fb266590 100644 --- a/code/datums/components/mind_linker.dm +++ b/code/datums/components/mind_linker.dm @@ -10,10 +10,6 @@ var/network_name = "Mind Link" /// The color of the network when talking in chat var/chat_color - /// The message sent to someone when linked up. - var/link_message - /// The message sent to someone when unlinked. - var/unlink_message /// A list of all signals that will call qdel() on our component if triggered. Optional. var/list/signals_which_destroy_us /// A callback invoked after an unlink is done. Optional. @@ -24,25 +20,25 @@ var/speech_action_icon_state = "link_speech" /// The icon background for the speech action handed out. var/speech_action_background_icon_state = "bg_alien" - /// The master's linking action, which allows them to link people to the network. - var/datum/action/linker_action + /// The border icon state for the speech action handed out. + var/speech_action_overlay_state = "bg_alien_border" /// The master's speech action. The owner of the link shouldn't lose this as long as the link remains. - var/datum/action/innate/linked_speech/master_speech + VAR_FINAL/datum/action/innate/linked_speech/master_speech /// An assoc list of [mob/living]s to [datum/action/innate/linked_speech]s. All the mobs that are linked to our network. - var/list/mob/living/linked_mobs = list() + VAR_FINAL/list/mob/living/linked_mobs = list() /datum/component/mind_linker/Initialize( + // Customization related settings network_name = "Mind Link", chat_color = "#008CA2", - linker_action_path, - link_message, - unlink_message, - signals_which_destroy_us, - datum/callback/post_unlink_callback, speech_action_icon = 'icons/mob/actions/actions_slime.dmi', speech_action_icon_state = "link_speech", speech_action_background_icon_state = "bg_alien", - ) + speech_action_overlay_state = "bg_alien_border", + // Optional + signals_which_destroy_us, + datum/callback/post_unlink_callback, +) if(!isliving(parent)) return COMPONENT_INCOMPATIBLE @@ -51,24 +47,15 @@ src.network_name = network_name src.chat_color = chat_color - src.link_message = link_message || "You are now connected to [owner.real_name]'s [network_name]." - src.unlink_message = unlink_message || "You are no longer connected to [owner.real_name]'s [network_name]." + src.speech_action_icon = speech_action_icon + src.speech_action_icon_state = speech_action_icon_state + src.speech_action_background_icon_state = speech_action_background_icon_state if(islist(signals_which_destroy_us)) src.signals_which_destroy_us = signals_which_destroy_us if(post_unlink_callback) src.post_unlink_callback = post_unlink_callback - src.speech_action_icon = speech_action_icon - src.speech_action_icon_state = speech_action_icon_state - src.speech_action_background_icon_state = speech_action_background_icon_state - - if(ispath(linker_action_path)) - linker_action = new linker_action_path(src) - linker_action.Grant(owner) - else - stack_trace("[type] was created without a valid linker_action_path. No one will be able to link to it.") - master_speech = new(src) master_speech.Grant(owner) @@ -78,7 +65,6 @@ for(var/mob/living/remaining_mob as anything in linked_mobs) unlink_mob(remaining_mob) linked_mobs.Cut() - QDEL_NULL(linker_action) QDEL_NULL(master_speech) post_unlink_callback = null return ..() @@ -99,10 +85,6 @@ /datum/component/mind_linker/proc/link_mob(mob/living/to_link) if(QDELETED(to_link) || to_link.stat == DEAD) return FALSE - if(HAS_TRAIT(to_link, TRAIT_MINDSHIELD)) // Mindshield implant - no dice - return FALSE - if(to_link.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) - return FALSE if(linked_mobs[to_link]) return FALSE @@ -110,20 +92,19 @@ if(to_link == owner) return FALSE - to_chat(to_link, span_notice(link_message)) - to_chat(owner, span_notice("You connect [to_link]'s mind to your [network_name].")) - - for(var/mob/living/other_link as anything in linked_mobs) - to_chat(other_link, span_notice("You feel a new presence within [owner.real_name]'s [network_name].")) - var/datum/action/innate/linked_speech/new_link = new(src) new_link.Grant(to_link) linked_mobs[to_link] = new_link - RegisterSignals(to_link, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING, COMSIG_MINDSHIELD_IMPLANTED), PROC_REF(unlink_mob)) + RegisterSignals(to_link, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING), PROC_REF(sig_unlink_mob)) return TRUE +/datum/component/mind_linker/proc/sig_unlink_mob(mob/living/to_unlink) + SIGNAL_HANDLER + + unlink_mob(to_unlink) + /** * Unlinks [to_unlink] from our network, deleting their speech action * and cleaning up anything involved. @@ -131,25 +112,17 @@ * Also invokes post_unlink_callback, if supplied. */ /datum/component/mind_linker/proc/unlink_mob(mob/living/to_unlink) - SIGNAL_HANDLER - if(!linked_mobs[to_unlink]) - return + return FALSE - to_chat(to_unlink, span_warning(unlink_message)) post_unlink_callback?.Invoke(to_unlink) - UnregisterSignal(to_unlink, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING, COMSIG_MINDSHIELD_IMPLANTED)) + UnregisterSignal(to_unlink, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING)) var/datum/action/innate/linked_speech/old_link = linked_mobs[to_unlink] linked_mobs -= to_unlink qdel(old_link) - - var/mob/living/owner = parent - - to_chat(owner, span_warning("You feel someone disconnect from your [network_name].")) - for(var/mob/living/other_link as anything in linked_mobs) - to_chat(other_link, span_warning("You feel a pressence disappear from [owner.real_name]'s [network_name].")) + return TRUE /** * Signal proc sent from any signals given to us initialize. @@ -164,6 +137,84 @@ qdel(src) +/// Subtype of mind linker (I know) which is more active rather than passive, +/// which involves the master linking people manually rather than people being added automatically. +/datum/component/mind_linker/active_linking + /// The message sent to someone when linked up. + var/link_message + /// The message sent to someone when unlinked. + var/unlink_message + /// The master's linking action, which allows them to link people to the network. + VAR_FINAL/datum/action/linker_action + +/datum/component/mind_linker/active_linking/Initialize( + // Customization related settings + network_name = "Mind Link", + chat_color = "#008CA2", + speech_action_icon = 'icons/mob/actions/actions_slime.dmi', + speech_action_icon_state = "link_speech", + speech_action_background_icon_state = "bg_alien", + speech_action_overlay_state = "bg_alien_border", + // Optional + signals_which_destroy_us, + datum/callback/post_unlink_callback, + // Optional for this subtype + link_message, + unlink_message, + // Required for this subtype + linker_action_path, +) + + . = ..() + if(. == COMPONENT_INCOMPATIBLE) + return + + var/mob/living/owner = parent + src.link_message = link_message || "You are now connected to [owner.real_name]'s [network_name]." + src.unlink_message = unlink_message || "You are no longer connected to [owner.real_name]'s [network_name]." + + if(ispath(linker_action_path)) + linker_action = new linker_action_path(src) + linker_action.Grant(owner) + else + stack_trace("[type] was created without a valid linker_action_path. No one will be able to link to it.") + + to_chat(owner, span_boldnotice("You establish a [network_name], allowing you to link minds to communicate telepathically.")) + +/datum/component/mind_linker/active_linking/Destroy() + QDEL_NULL(linker_action) + return ..() + +/datum/component/mind_linker/active_linking/link_mob(mob/living/to_link) + if(HAS_TRAIT(to_link, TRAIT_MINDSHIELD)) // Mindshield implant - no dice + return FALSE + if(to_link.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) + return FALSE + + . = ..() + if(!.) + return + + RegisterSignal(to_link, COMSIG_MINDSHIELD_IMPLANTED, PROC_REF(sig_unlink_mob)) + var/mob/living/owner = parent + to_chat(to_link, span_notice(link_message)) + to_chat(owner, span_notice("You connect [to_link]'s mind to your [network_name].")) + for(var/mob/living/other_link as anything in linked_mobs) + to_chat(other_link, span_notice("You feel a new presence within [owner.real_name]'s [network_name].")) + +/datum/component/mind_linker/active_linking/unlink_mob(mob/living/to_unlink) + . = ..() + if(!.) + return + + UnregisterSignal(to_unlink, COMSIG_MINDSHIELD_IMPLANTED) + var/mob/living/owner = parent + to_chat(to_unlink, span_warning(unlink_message)) + to_chat(owner, span_warning("You feel someone disconnect from your [network_name].")) + for(var/mob/living/other_link as anything in linked_mobs) + to_chat(other_link, span_warning("You feel a pressence disappear from [owner.real_name]'s [network_name].")) + +// Used in mind linker to talk to everyone in the network. /datum/action/innate/linked_speech name = "Mind Link Speech" desc = "Send a psychic message to everyone connected to your Link." diff --git a/code/datums/components/scope.dm b/code/datums/components/scope.dm index 7f66a930d24e..2e10f7663a73 100644 --- a/code/datums/components/scope.dm +++ b/code/datums/components/scope.dm @@ -152,8 +152,16 @@ /atom/movable/screen/fullscreen/cursor_catcher/scope/calculate_params() var/list/modifiers = params2list(mouse_params) - var/icon_x = text2num(LAZYACCESS(modifiers, VIS_X)) || view_list[1]*world.icon_size/2 - var/icon_y = text2num(LAZYACCESS(modifiers, VIS_Y)) || view_list[2]*world.icon_size/2 + var/icon_x = text2num(LAZYACCESS(modifiers, VIS_X)) + if(isnull(icon_x)) + icon_x = text2num(LAZYACCESS(modifiers, ICON_X)) + if(isnull(icon_x)) + icon_x = view_list[1]*world.icon_size/2 + var/icon_y = text2num(LAZYACCESS(modifiers, VIS_Y)) + if(isnull(icon_y)) + icon_y = text2num(LAZYACCESS(modifiers, ICON_Y)) + if(isnull(icon_y)) + icon_y = view_list[2]*world.icon_size/2 given_x = round(range_modifier * (icon_x - view_list[1]*world.icon_size/2)) given_y = round(range_modifier * (icon_y - view_list[2]*world.icon_size/2)) given_turf = locate(owner.x+round(given_x/world.icon_size, 1),owner.y+round(given_y/world.icon_size, 1),owner.z) diff --git a/code/datums/components/shielded.dm b/code/datums/components/shielded.dm index 13fc5d0412d7..26198b0f33f1 100644 --- a/code/datums/components/shielded.dm +++ b/code/datums/components/shielded.dm @@ -186,5 +186,5 @@ . = COMPONENT_NO_AFTERATTACK adjust_charge(charge_recovery) - to_chat(user, span_notice("You charge \the [parent]. It can now absorb [current_charges] hits.")) + to_chat(user, span_notice("You charge \the [parent]. It can now absorb [current_charges] [lose_multiple_charges ? "damage" : "hits"].")) //monke edit: adds the multiple charges check qdel(recharge_rune) diff --git a/code/datums/components/shrink.dm b/code/datums/components/shrink.dm index 67cd3d39e23c..5b017e4cca63 100644 --- a/code/datums/components/shrink.dm +++ b/code/datums/components/shrink.dm @@ -1,6 +1,8 @@ /datum/component/shrink var/olddens var/oldopac + /// Tracks the squashable component we apply when we make the small mob squashable + var/datum/component/squashable/newsquash dupe_mode = COMPONENT_DUPE_HIGHLANDER /datum/component/shrink/Initialize(shrink_time) @@ -24,13 +26,23 @@ if(ishuman(C)) var/mob/living/carbon/human/H = C H.physiology.damage_resistance -= 100//carbons take double damage while shrunk + if(!L.GetComponent(/datum/component/squashable)) + newsquash = L.AddComponent( \ + /datum/component/squashable, \ + squash_chance = 75, \ + squash_damage = 10, \ + squash_flags = SQUASHED_ALWAYS_IF_DEAD|SQUASHED_DONT_SQUASH_IN_CONTENTS, \ + ) else parent_atom.set_density(FALSE) // this is handled by the UNDENSE trait on mobs parent_atom.visible_message(span_warning("[parent_atom] shrinks down to a tiny size!"), span_userdanger("Everything grows bigger!")) - QDEL_IN(src, shrink_time) + if(shrink_time >= 0) // negative shrink time is permanent + QDEL_IN(src, shrink_time) /datum/component/shrink/Destroy() + if(newsquash) + qdel(newsquash) var/atom/parent_atom = parent parent_atom.transform = parent_atom.transform.Scale(2,2) parent_atom.set_opacity(oldopac) diff --git a/code/datums/components/splattercasting.dm b/code/datums/components/splattercasting.dm index 1721c8fbf53c..2a0eb0dc2c36 100644 --- a/code/datums/components/splattercasting.dm +++ b/code/datums/components/splattercasting.dm @@ -21,10 +21,12 @@ RegisterSignal(parent, COMSIG_MOB_SPELL_PROJECTILE, PROC_REF(on_spell_projectile)) RegisterSignal(parent, COMSIG_MOB_BEFORE_SPELL_CAST, PROC_REF(on_before_spell_cast)) RegisterSignal(parent, COMSIG_MOB_AFTER_SPELL_CAST, PROC_REF(on_after_spell_cast)) + ADD_TRAIT(parent, TRAIT_SPLATTERCASTER, REF(src)) /datum/component/splattercasting/UnregisterFromParent() . = ..() UnregisterSignal(parent, list(COMSIG_SPECIES_LOSS, COMSIG_MOB_SPELL_PROJECTILE, COMSIG_MOB_BEFORE_SPELL_CAST, COMSIG_MOB_AFTER_SPELL_CAST)) + REMOVE_TRAIT(parent, TRAIT_SPLATTERCASTER, REF(src)) ///signal sent when a spell casts a projectile /datum/component/splattercasting/proc/on_species_change(mob/living/carbon/source, datum/species/lost_species) diff --git a/code/datums/components/squashable.dm b/code/datums/components/squashable.dm index b79cccff471f..d4fbbcba61f7 100644 --- a/code/datums/components/squashable.dm +++ b/code/datums/components/squashable.dm @@ -44,10 +44,10 @@ if((squash_flags & SQUASHED_DONT_SQUASH_IN_CONTENTS) && !isturf(parent_as_living.loc)) return - if(squash_flags & SQUASHED_SHOULD_BE_DOWN && parent_as_living.body_position != LYING_DOWN) + if((squash_flags & SQUASHED_SHOULD_BE_DOWN) && parent_as_living.body_position != LYING_DOWN) return - var/should_squash = prob(squash_chance) + var/should_squash = ((squash_flags & SQUASHED_ALWAYS_IF_DEAD) && parent_as_living.stat == DEAD) || prob(squash_chance) if(should_squash && on_squash_callback) if(on_squash_callback.Invoke(parent_as_living, crossing_movable)) diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index 666711762c19..ce0e6169a73d 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -64,7 +64,7 @@ if(ishuman(src)) var/mob/living/carbon/human/infecting_human = src - if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) && prob(75)) + if(HAS_TRAIT(infecting_human, TRAIT_VIRUS_RESISTANCE) && prob(75)) return switch(target_zone) @@ -95,10 +95,8 @@ disease.try_infect(src) /mob/living/proc/AirborneContractDisease(datum/disease/disease, force_spread) - if(ishuman(src)) - var/mob/living/carbon/human/infecting_human = src - if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) && prob(75)) - return + if(HAS_TRAIT(src, TRAIT_VIRUS_RESISTANCE) && prob(75)) + return if(((disease.spread_flags & DISEASE_SPREAD_AIRBORNE) || force_spread) && prob((50*disease.spreading_modifier) - 1)) ForceContractDisease(disease) diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 4bf7af82207c..129e9b9b5fb5 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(inspectable_diseases, list()) ///Proc to process the disease and decide on whether to advance, cure or make the sympthoms appear. Returns a boolean on whether to continue acting on the symptoms or not. /datum/disease/proc/stage_act(seconds_per_tick, times_fired) - var/slowdown = affected_mob.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) ? 0.5 : 1 // spaceacillin slows stage speed by 50% + var/slowdown = HAS_TRAIT(affected_mob, TRAIT_VIRUS_RESISTANCE) ? 0.5 : 1 // spaceacillin slows stage speed by 50% if(has_cure()) if(SPT_PROB(cure_chance, seconds_per_tick)) @@ -106,7 +106,7 @@ GLOBAL_LIST_INIT(inspectable_diseases, list()) if(!(spread_flags & DISEASE_SPREAD_AIRBORNE) && !force_spread) return - if(affected_mob.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) || (affected_mob.satiety > 0 && prob(affected_mob.satiety/10))) + if(HAS_TRAIT(affected_mob, TRAIT_VIRUS_RESISTANCE) || (affected_mob.satiety > 0 && prob(affected_mob.satiety/10))) return affected_mob.spread_airborne_diseases() diff --git a/code/datums/elements/bugkiller_reagent.dm b/code/datums/elements/bugkiller_reagent.dm new file mode 100644 index 000000000000..57f2ae65d920 --- /dev/null +++ b/code/datums/elements/bugkiller_reagent.dm @@ -0,0 +1,88 @@ +/// Simple element to be applied to reagents +/// When those reagents are exposed to mobs with the bug biotype, causes toxins damage +/// If this delivers the killing blow on a non-humanoid mob, it applies a special status effect that does a funny animation +/datum/element/bugkiller_reagent + +/datum/element/bugkiller_reagent/Attach(datum/target) + . = ..() + if(!istype(target, /datum/reagent)) + return + + RegisterSignal(target, COMSIG_REAGENT_EXPOSE_MOB, PROC_REF(on_expose)) + +/datum/element/bugkiller_reagent/Detach(datum/source, ...) + . = ..() + UnregisterSignal(source, COMSIG_REAGENT_EXPOSE_MOB) + +/datum/element/bugkiller_reagent/proc/on_expose( + datum/reagent/source, + mob/living/exposed_mob, + methods = TOUCH, + reac_volume, + show_message = TRUE, + touch_protection = 0, +) + SIGNAL_HANDLER + + if(exposed_mob.stat == DEAD) + return + if(!(exposed_mob.mob_biotypes & MOB_BUG)) + return + + // capping damage so splashing a beaker on a moth is not an instant crit + var/damage = min(round(0.4 * reac_volume * (1 - touch_protection), 0.1), 12) + if(damage < 1) + return + + if(!(exposed_mob.mob_biotypes & MOB_HUMANOID) && exposed_mob.health <= damage) + // no-ops if they are already in the process of dying + exposed_mob.apply_status_effect(/datum/status_effect/bugkiller_death) + return + + if(exposed_mob.apply_damage(damage, TOX) && damage >= 6) + // yes i know it's not burn damage. the burning is on the inside. + to_chat(exposed_mob, span_danger("You feel a burning sensation.")) + +/// If bugkiller delivers a lethal dosage, applies this effect which does a funny animation THEN kills 'em +/// Also makes it so simplemobs / basicmobs no longer delete when they die (if they do) +/datum/status_effect/bugkiller_death + id = "bugkiller_death" + alert_type = /atom/movable/screen/alert/status_effect/bugkiller_death + /// How many times the spasm loops + var/spasm_loops = 0 + +/datum/status_effect/bugkiller_death/on_creation(mob/living/new_other, duration = 4 SECONDS) + src.duration = duration + src.spasm_loops = ROUND_UP(duration / 0.8) // one spasm ~= 0.8 deciseconds (yes deciseconds) + return ..() + +/datum/status_effect/bugkiller_death/on_apply() + if(owner.stat == DEAD) + return FALSE + playsound(owner, 'sound/voice/human/malescream_1.ogg', 25, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 5) + to_chat(owner, span_userdanger("The world begins to go dark...")) + owner.spasm_animation(spasm_loops) + owner.adjust_eye_blur(duration) + return TRUE + +/datum/status_effect/bugkiller_death/on_remove() + if(owner.stat == DEAD || QDELETED(owner)) + return + + if(isbasicmob(owner)) + var/mob/living/basic/basic_owner = owner + basic_owner.basic_mob_flags &= ~DEL_ON_DEATH + basic_owner.basic_mob_flags |= FLIP_ON_DEATH + + if(isanimal(owner)) + var/mob/living/simple_animal/simple_owner = owner + simple_owner.del_on_death = FALSE + simple_owner.flip_on_death = TRUE + + owner.investigate_log("died to being sprayed with bugkiller.", INVESTIGATE_DEATHS) + owner.death() + +/atom/movable/screen/alert/status_effect/bugkiller_death + name = "Overwhelming Toxicity" + desc = "Don't go into the light!" + icon_state = "paralysis" diff --git a/code/datums/greyscale/json_configs/mutant_organs.json b/code/datums/greyscale/json_configs/mutant_organs.json index 93dd66c9e64c..2e4aa1da8842 100644 --- a/code/datums/greyscale/json_configs/mutant_organs.json +++ b/code/datums/greyscale/json_configs/mutant_organs.json @@ -1,4 +1,18 @@ { + "appendix": [ + { + "type": "icon_state", + "icon_state": "appendix", + "blend_mode": "overlay", + "color_ids": [ 1 ] + }, + { + "type": "icon_state", + "icon_state": "appendix_insides", + "blend_mode": "overlay", + "color_ids": [ 2 ] + } + ], "brain": [ { "type": "icon_state", diff --git a/code/datums/materials/meat.dm b/code/datums/materials/meat.dm index 162f0f2643e8..b9000dfd173c 100644 --- a/code/datums/materials/meat.dm +++ b/code/datums/materials/meat.dm @@ -18,25 +18,45 @@ /datum/material/meat/on_removed(atom/source, amount, material_flags) . = ..() qdel(source.GetComponent(/datum/component/edible)) + qdel(source.GetComponent(/datum/component/blood_walk)) + qdel(source.GetComponent(/datum/component/bloody_spreader)) /datum/material/meat/on_applied_obj(obj/O, amount, material_flags) . = ..() - make_edible(O, amount, material_flags) + make_meaty(O, amount, material_flags) /datum/material/meat/on_applied_turf(turf/T, amount, material_flags) . = ..() - make_edible(T, amount, material_flags) + make_meaty(T, amount, material_flags) -/datum/material/meat/proc/make_edible(atom/source, amount, material_flags) +/datum/material/meat/proc/make_meaty(atom/source, amount, material_flags) var/nutriment_count = 3 * (amount / SHEET_MATERIAL_AMOUNT) var/oil_count = 2 * (amount / SHEET_MATERIAL_AMOUNT) source.AddComponent(/datum/component/edible, \ initial_reagents = list(/datum/reagent/consumable/nutriment = nutriment_count, /datum/reagent/consumable/cooking_oil = oil_count), \ foodtypes = RAW | MEAT | GROSS, \ eat_time = 3 SECONDS, \ - tastes = list("Fleshy")) + tastes = list("Meaty")) + source.AddComponent( + /datum/component/bloody_spreader,\ + blood_left = (nutriment_count + oil_count) * 0.3,\ + blood_dna = list("meaty DNA" = "MT-"),\ + diseases = null,\ + ) + + // Turfs can't handle the meaty goodness of blood walk. + if(!ismovable(source)) + return + + source.AddComponent( + /datum/component/blood_walk,\ + blood_type = /obj/effect/decal/cleanable/blood,\ + blood_spawn_chance = 35,\ + max_blood = (nutriment_count + oil_count) * 0.3,\ + ) + /datum/material/meat/mob_meat init_flags = MATERIAL_INIT_BESPOKE var/subjectname = "" diff --git a/code/datums/mood.dm b/code/datums/mood.dm index 40ed994785bb..9cce6e98a8f3 100644 --- a/code/datums/mood.dm +++ b/code/datums/mood.dm @@ -292,7 +292,7 @@ mood_screen_object.color = "#4b96c4" hud.infodisplay += mood_screen_object RegisterSignal(hud, COMSIG_QDELETING, PROC_REF(unmodify_hud)) - RegisterSignal(mood_screen_object, COMSIG_CLICK, PROC_REF(hud_click)) + RegisterSignal(mood_screen_object, COMSIG_SCREEN_ELEMENT_CLICK, PROC_REF(hud_click)) /// Removes the mood HUD object /datum/mood/proc/unmodify_hud(datum/source) diff --git a/code/datums/mutations/cold.dm b/code/datums/mutations/cold.dm index f999eed421d5..57c4f854fc7a 100644 --- a/code/datums/mutations/cold.dm +++ b/code/datums/mutations/cold.dm @@ -18,6 +18,7 @@ item_type = /obj/item/stack/sheet/mineral/snow delete_old = FALSE + delete_on_failure = FALSE /datum/mutation/human/cryokinesis name = "Cryokinesis" diff --git a/code/datums/proximity_monitor/fields/gravity.dm b/code/datums/proximity_monitor/fields/gravity.dm index ac9b143c2083..f970b0d53b6f 100644 --- a/code/datums/proximity_monitor/fields/gravity.dm +++ b/code/datums/proximity_monitor/fields/gravity.dm @@ -1,3 +1,4 @@ +// Proximity monitor applies forced gravity to all turfs in range. /datum/proximity_monitor/advanced/gravity edge_is_a_field = TRUE var/gravity_value = 0 @@ -10,7 +11,9 @@ /datum/proximity_monitor/advanced/gravity/setup_field_turf(turf/target) . = ..() - if (isnull(modified_turfs[target])) + if(!isnull(modified_turfs[target])) + return + if(HAS_TRAIT(target, TRAIT_FORCED_GRAVITY)) return target.AddElement(/datum/element/forced_gravity, gravity_value, can_override = TRUE) modified_turfs[target] = gravity_value @@ -19,7 +22,8 @@ . = ..() if(isnull(modified_turfs[target])) return - target.RemoveElement(/datum/element/forced_gravity, modified_turfs[target]) + var/grav_value = modified_turfs[target] || 0 + target.RemoveElement(/datum/element/forced_gravity, grav_value) modified_turfs -= target // Subtype which pops up a balloon alert when a mob enters the field diff --git a/code/datums/station_traits/_station_trait.dm b/code/datums/station_traits/_station_trait.dm index 44aaffb24910..b979aaf5330e 100644 --- a/code/datums/station_traits/_station_trait.dm +++ b/code/datums/station_traits/_station_trait.dm @@ -55,3 +55,15 @@ REMOVE_TRAIT(SSstation, trait_to_give, STATION_TRAIT) qdel(src) + +///Called by decals if they can be colored, to see if we got some cool colors for them. Only takes the first station trait +/proc/request_station_colors(atom/thing_to_color, pattern) + for(var/datum/station_trait/trait in SSstation.station_traits) + var/decal_color = trait.get_decal_color(thing_to_color, pattern || PATTERN_DEFAULT) + if(decal_color) + return decal_color + return null + +///Return a color for the decals, if any +/datum/station_trait/proc/get_decal_color(thing_to_color, pattern) + return diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm index 1600bdc6c19a..78955436f5a4 100644 --- a/code/datums/station_traits/neutral_traits.dm +++ b/code/datums/station_traits/neutral_traits.dm @@ -308,12 +308,12 @@ flags_inv = 0 armor_type = /datum/armor/none var/static/list/hat_colors = list( - COLOR_BRIGHT_RED, - COLOR_BRIGHT_ORANGE, - COLOR_BRIGHT_YELLOW, - COLOR_BRIGHT_GREEN, - COLOR_BRIGHT_TEAL, - COLOR_BRIGHT_PURPLE, + COLOR_PRIDE_RED, + COLOR_PRIDE_ORANGE, + COLOR_PRIDE_YELLOW, + COLOR_PRIDE_GREEN, + COLOR_PRIDE_BLUE, + COLOR_PRIDE_PURPLE, ) /obj/item/clothing/head/costume/party/Initialize(mapload) diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index d1d29681131c..4fe68d281eae 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -852,6 +852,9 @@ icon_state = "antalert" /atom/movable/screen/alert/status_effect/ants/Click() + . = ..() + if(!.) + return var/mob/living/living = owner if(!istype(living) || !living.can_resist() || living != owner) return diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm index 01a37b4c80c6..5fffb2f0adb5 100644 --- a/code/datums/storage/storage.dm +++ b/code/datums/storage/storage.dm @@ -404,6 +404,8 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) if(!can_insert(to_insert, user, force = force)) return FALSE + SEND_SIGNAL(resolve_location, COMSIG_STORAGE_STORED_ITEM, to_insert, user, force) + to_insert.item_flags |= IN_STORAGE to_insert.forceMove(resolve_location) item_insertion_feedback(user, to_insert, override) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 453e9fe19940..b9c8cdad258d 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -585,8 +585,6 @@ var/mob/living/basic/space_dragon/S = new (pick(spawn_locs)) player_mind.transfer_to(S) - player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon)) - player_mind.special_role = ROLE_SPACE_DRAGON player_mind.add_antag_datum(/datum/antagonist/space_dragon) playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1) diff --git a/code/game/machinery/dna_infuser/dna_infuser.dm b/code/game/machinery/dna_infuser/dna_infuser.dm index b151ce9bdc69..286d4879594f 100644 --- a/code/game/machinery/dna_infuser/dna_infuser.dm +++ b/code/game/machinery/dna_infuser/dna_infuser.dm @@ -154,6 +154,7 @@ skillchip.set_metadata(chip) // monkestation end check_tier_progression(target) + return TRUE /// Picks a random mutated organ from the infuser entry which is also compatible with the target mob. /// Tries to return a typepath of a valid mutant organ if all of the following criteria are true: diff --git a/code/game/machinery/dna_infuser/infuser_book.dm b/code/game/machinery/dna_infuser/infuser_book.dm index 84a78a4899a7..ea5dbcbf4117 100644 --- a/code/game/machinery/dna_infuser/infuser_book.dm +++ b/code/game/machinery/dna_infuser/infuser_book.dm @@ -8,12 +8,22 @@ throw_speed = 2 throw_range = 5 w_class = WEIGHT_CLASS_TINY + drop_sound = 'sound/items/handling/book_drop.ogg' + pickup_sound = 'sound/items/handling/book_pickup.ogg' /obj/item/infuser_book/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "InfuserBook") ui.open() + playsound(src, SFX_PAGE_TURN, 30, TRUE) + +/obj/item/infuser_book/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + if(action == "play_flip_sound") + playsound(src, SFX_PAGE_TURN, 30, TRUE) /obj/item/infuser_book/ui_static_data(mob/user) var/list/data = list() diff --git a/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_one_entries.dm b/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_one_entries.dm index fa345e5bae49..d24a951d76b5 100644 --- a/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_one_entries.dm +++ b/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_one_entries.dm @@ -75,3 +75,33 @@ infusion_desc = "skittish" tier = DNA_MUTANT_TIER_ONE status_effect_type = /datum/status_effect/organ_set_bonus/rat + +/datum/infuser_entry/roach + name = "Roach" + infuse_mob_name = "cockroach" + desc = "It seems as if you're a fan of ancient literature by your interest in this. Assuredly, merging cockroach DNA into your genome \ + will not cause you to become incapable of leaving your bed. These creatures are incredibly resilient against many things \ + humans are weak to, and we can use that! Who wouldn't like to survive a nuclear blast? \ + NOTE: Squished roaches will not work for the infuser, if that wasn't obvious. Try spraying them with some pestkiller from botany!" + threshold_desc = "you will no longer be gibbed by explosions, and gain incredible resistance to viruses and radiation." + qualities = list( + "resilience to attacks from behind", + "healthier organs", + "get over disgust very quickly", + "the ability to survive a nuclear apocalypse", + "harder to pick yourself up from falling over", + "avoid toxins at all costs", + "always down to find a snack", + ) + input_obj_or_mob = list( + /mob/living/basic/cockroach, + ) + output_organs = list( + /obj/item/organ/internal/heart/roach, + /obj/item/organ/internal/stomach/roach, + /obj/item/organ/internal/liver/roach, + /obj/item/organ/internal/appendix/roach, + ) + infusion_desc = "kafkaesque" // Gregor Samsa !! + tier = DNA_MUTANT_TIER_ONE + status_effect_type = /datum/status_effect/organ_set_bonus/roach diff --git a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm new file mode 100644 index 000000000000..49b7f01c7c76 --- /dev/null +++ b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm @@ -0,0 +1,226 @@ +#define ROACH_ORGAN_COLOR "#7c4200" +// Yeah i'm lazy and we don't use any of the other color slots +#define ROACH_COLORS ROACH_ORGAN_COLOR + ROACH_ORGAN_COLOR + ROACH_ORGAN_COLOR + +/datum/armor/roach_internal_armor + bomb = 100 + bio = 90 + +/datum/status_effect/organ_set_bonus/roach + id = "organ_set_bonus_roach" + organs_needed = 4 + bonus_activate_text = span_notice("Roach DNA is deeply infused with you! \ + You feel increasingly resistant to explosives, radiation, and viral agents.") + bonus_deactivate_text = span_notice("You are no longer majority roach, \ + and you feel much more vulnerable to nuclear apocalypses.") + // - Immunity to nuke gibs + // - Nukes come with radiation (not actually but yknow) + bonus_traits = list(TRAIT_NUKEIMMUNE, TRAIT_RADIMMUNE, TRAIT_VIRUS_RESISTANCE) + /// Armor type attached to the owner's physiology + var/datum/armor/given_armor = /datum/armor/roach_internal_armor + /// Storing biotypes pre-organ bonus applied so we don't remove bug from mobs which should have it. + var/old_biotypes = NONE + +/datum/status_effect/organ_set_bonus/roach/enable_bonus() + . = ..() + if(!ishuman(owner)) + return + + var/mob/living/carbon/human/human_owner = owner + human_owner.physiology.armor = human_owner.physiology.armor.add_other_armor(given_armor) + + old_biotypes = human_owner.mob_biotypes + human_owner.mob_biotypes |= MOB_BUG + +/datum/status_effect/organ_set_bonus/roach/disable_bonus() + . = ..() + if(!ishuman(owner) || QDELETED(owner)) + return + + var/mob/living/carbon/human/human_owner = owner + human_owner.physiology.armor = human_owner.physiology.armor.subtract_other_armor(given_armor) + + if(!(old_biotypes & MOB_BUG)) // only remove bug if it wasn't there before + human_owner.mob_biotypes &= ~MOB_BUG + +/// Roach heart: +/// Reduces damage taken from brute attacks from behind, +/// but increases duration of knockdowns +/obj/item/organ/internal/heart/roach + name = "mutated roach-heart" + desc = "Roach DNA infused into what was once a normal heart." + maxHealth = 2 * STANDARD_ORGAN_THRESHOLD + + icon = 'icons/obj/medical/organs/infuser_organs.dmi' + icon_state = "heart" + greyscale_config = /datum/greyscale_config/mutant_organ + greyscale_colors = ROACH_COLORS + + /// Timer ID for resetting the damage resistance applied from attacks from behind + var/defense_timerid + /// Bodypart overlay applied to the chest the heart is in + var/datum/bodypart_overlay/simple/roach_shell/roach_shell + +/obj/item/organ/internal/heart/roach/Initialize(mapload) + . = ..() + AddElement(/datum/element/noticable_organ, "has hardened, somewhat translucent skin.") + AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach) + roach_shell = new() + +/obj/item/organ/internal/heart/roach/Destroy() + QDEL_NULL(roach_shell) + return ..() + +/obj/item/organ/internal/heart/roach/on_insert(mob/living/carbon/organ_owner, special) + . = ..() + if(!ishuman(organ_owner)) + return + + var/mob/living/carbon/human/human_owner = organ_owner + + RegisterSignal(human_owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(modify_damage)) + human_owner.physiology.knockdown_mod *= 3 + + var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST) + chest.add_bodypart_overlay(roach_shell) + human_owner.update_body_parts() + +/obj/item/organ/internal/heart/roach/on_remove(mob/living/carbon/organ_owner, special) + . = ..() + if(!ishuman(organ_owner) || QDELETED(organ_owner)) + return + + var/mob/living/carbon/human/human_owner = organ_owner + + UnregisterSignal(human_owner, COMSIG_MOB_APPLY_DAMAGE) + human_owner.physiology.knockdown_mod /= 3 + + if(defense_timerid) + reset_damage(human_owner) + + var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST) + chest.remove_bodypart_overlay(roach_shell) + human_owner.update_body_parts() + +/** + * Signal proc for [COMSIG_MOB_APPLY_DAMAGE] + * + * Being hit with brute damage in the back will impart a large damage resistance bonus for a very short period. + */ +/obj/item/organ/internal/heart/roach/proc/modify_damage(datum/source, damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction, obj/item/attacking_item) + SIGNAL_HANDLER + + if(!ishuman(owner) || !attack_direction || damagetype != BRUTE || owner.stat >= UNCONSCIOUS) + return + + var/mob/living/carbon/human/human_owner = owner + // No tactical spinning + if(human_owner.flags_1 & IS_SPINNING_1) + return + + // If we're lying down, or were attacked from the back, we get armor. + var/should_armor_up = (human_owner.body_position == LYING_DOWN) || (human_owner.dir & attack_direction) + if(!should_armor_up) + return + + // Take 50% less damage from attack behind us + if(!defense_timerid) + human_owner.physiology.brute_mod /= 2 + human_owner.visible_message(span_warning("[human_owner]'s back hardens against the blow!")) + playsound(human_owner, 'sound/effects/constructform.ogg', 25, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) + + defense_timerid = addtimer(CALLBACK(src, PROC_REF(reset_damage), owner), 5 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) + +/obj/item/organ/internal/heart/roach/proc/reset_damage(mob/living/carbon/human/human_owner) + defense_timerid = null + if(!QDELETED(human_owner)) + human_owner.physiology.brute_mod *= 2 + human_owner.visible_message(span_warning("[human_owner]'s back softens again.")) + +// Simple overlay so we can add a roach shell to guys with roach hearts +/datum/bodypart_overlay/simple/roach_shell + icon_state = "roach_shell" + layers = EXTERNAL_FRONT|EXTERNAL_BEHIND + +/datum/bodypart_overlay/simple/roach_shell/get_image(image_layer, obj/item/bodypart/limb) + return image( + icon = icon, + icon_state = "[icon_state]_[mutant_bodyparts_layertext(image_layer)]", + layer = image_layer, + ) + +/// Roach stomach: +/// Makes disgust a non-issue, very slightly worse at passing off reagents +/// Also makes you more hungry +/obj/item/organ/internal/stomach/roach + name = "mutated roach-stomach" + desc = "Roach DNA infused into what was once a normal stomach." + maxHealth = 2 * STANDARD_ORGAN_THRESHOLD + disgust_metabolism = 32 // Demolishes any disgust we have + metabolism_efficiency = 0.033 // Slightly worse at transferring reagents +// hunger_modifier = 3 //monkestation temp removal + + icon = 'icons/obj/medical/organs/infuser_organs.dmi' + icon_state = "stomach" + greyscale_config = /datum/greyscale_config/mutant_organ + greyscale_colors = ROACH_COLORS + +/obj/item/organ/internal/stomach/roach/Initialize(mapload) + . = ..() + AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach) + +/// Roach liver: +/// Purges toxins at a higher threshold, but takes more damage from them if not purged +/obj/item/organ/internal/liver/roach + name = "mutated roach-liver" + desc = "Roach DNA infused into what was once a normal liver." + maxHealth = 2 * STANDARD_ORGAN_THRESHOLD + toxTolerance = 5 // More tolerance for toxins + liver_resistance = 0.25 // But if they manage to get in you're screwed + + icon = 'icons/obj/medical/organs/infuser_organs.dmi' + icon_state = "liver" + greyscale_config = /datum/greyscale_config/mutant_organ + greyscale_colors = ROACH_COLORS + +/obj/item/organ/internal/liver/roach/Initialize(mapload) + . = ..() + AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach) + +/obj/item/organ/internal/liver/roach/on_insert(mob/living/carbon/organ_owner, special) + . = ..() + if(!ishuman(organ_owner)) + return + + var/mob/living/carbon/human/human_owner = owner + human_owner.physiology.tox_mod *= 2 + +/obj/item/organ/internal/liver/roach/on_remove(mob/living/carbon/organ_owner, special) + . = ..() + if(!ishuman(organ_owner) || QDELETED(organ_owner)) + return + + var/mob/living/carbon/human/human_owner = organ_owner + human_owner.physiology.tox_mod /= 2 + +/// Roach appendix: +/// No appendicitus! weee! +/obj/item/organ/internal/appendix/roach + name = "mutated roach-appendix" + desc = "Roach DNA infused into what was once a normal appendix. It could get worse?" + maxHealth = 2 * STANDARD_ORGAN_THRESHOLD + + icon = 'icons/obj/medical/organs/infuser_organs.dmi' + icon_state = "appendix" + greyscale_config = /datum/greyscale_config/mutant_organ + greyscale_colors = ROACH_COLORS + +/obj/item/organ/internal/appendix/roach/Initialize(mapload) + . = ..() + AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach) + +/obj/item/organ/internal/appendix/roach/become_inflamed() + return + +#undef ROACH_ORGAN_COLOR +#undef ROACH_COLORS diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 6e45bb3b4db9..a0d432cefd9a 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -563,7 +563,7 @@ . = ..() /// Signal proc for [COMSIG_ATOM_MAGICALLY_UNLOCKED]. Open up when someone casts knock. -/obj/machinery/door/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, mob/living/caster) +/obj/machinery/door/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, atom/caster) SIGNAL_HANDLER INVOKE_ASYNC(src, PROC_REF(open)) diff --git a/code/game/objects/effects/cursor_catcher.dm b/code/game/objects/effects/cursor_catcher.dm index 779cad0e2a0f..3229cd44b708 100644 --- a/code/game/objects/effects/cursor_catcher.dm +++ b/code/game/objects/effects/cursor_catcher.dm @@ -3,6 +3,7 @@ icon_state = "fullscreen_blocker" // Fullscreen semi transparent icon plane = HUD_PLANE mouse_opacity = MOUSE_OPACITY_ICON + default_click = TRUE /// The mob whose cursor we are tracking. var/mob/owner /// Client view size of the scoping mob. @@ -54,7 +55,11 @@ /atom/movable/screen/fullscreen/cursor_catcher/proc/calculate_params() var/list/modifiers = params2list(mouse_params) var/icon_x = text2num(LAZYACCESS(modifiers, VIS_X)) + if(isnull(icon_x)) + icon_x = text2num(LAZYACCESS(modifiers, ICON_X)) var/icon_y = text2num(LAZYACCESS(modifiers, VIS_Y)) + if(isnull(icon_y)) + icon_y = text2num(LAZYACCESS(modifiers, ICON_Y)) var/our_x = round(icon_x / world.icon_size) var/our_y = round(icon_y / world.icon_size) given_turf = locate(owner.x + our_x - round(view_list[1]/2), owner.y + our_y - round(view_list[2]/2), owner.z) diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm index fa6a178b5944..cf42e3a2c0d8 100644 --- a/code/game/objects/effects/decals/decal.dm +++ b/code/game/objects/effects/decals/decal.dm @@ -45,6 +45,10 @@ plane = FLOOR_PLANE layer = TURF_DECAL_LAYER anchored = TRUE + /// Does this decal change colors on holidays + var/use_holiday_colors = FALSE + /// The pattern used when recoloring the decal. If null, it'll use the def of the station or holiday. + var/pattern // This is with the intent of optimizing mapload // See spawners for more details since we use the same pattern @@ -55,6 +59,13 @@ stack_trace("Warning: [src]([type]) initialized multiple times!") flags_1 |= INITIALIZED_1 + // If the tile uses holiday colors, apply them here + if(use_holiday_colors) + var/current_holiday_color = request_holiday_colors(src, pattern) + if(current_holiday_color) + color = current_holiday_color + alpha = DECAL_ALPHA + var/turf/T = loc if(!istype(T)) //you know this will happen somehow CRASH("Turf decal initialized in an object/nullspace") diff --git a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm index 24bb508d4775..b63c88567645 100644 --- a/code/game/objects/effects/decals/turfdecal/tilecoloring.dm +++ b/code/game/objects/effects/decals/turfdecal/tilecoloring.dm @@ -3,11 +3,10 @@ icon_state = "tile_corner" layer = TURF_PLATING_DECAL_LAYER alpha = 110 + use_holiday_colors = TRUE -/obj/effect/turf_decal/tile/Initialize(mapload) - if (check_holidays(APRIL_FOOLS)) - color = "#[random_short_color()]" - return ..() +/obj/effect/turf_decal/tile/neutral/tram + pattern = PATTERN_VERTICAL_STRIPE /// Automatically generates all subtypes for a decal with the given path. #define TILE_DECAL_SUBTYPE_HELPER(path)\ @@ -37,18 +36,21 @@ }\ ##path/diagonal_edge {\ icon_state = "diagonal_edge";\ +}\ +##path/tram {\ + icon_state = "tile_tram";\ } /// Blue tiles /obj/effect/turf_decal/tile/blue - name = "blue corner" + name = "blue tile decal" color = "#52B4E9" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/blue) /// Dark blue tiles /obj/effect/turf_decal/tile/dark_blue - name = "dark blue corner" + name = "dark blue tile decal" color = "#486091" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/dark_blue) @@ -56,7 +58,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/dark_blue) /// Green tiles /obj/effect/turf_decal/tile/green - name = "green corner" + name = "green tile decal" color = "#9FED58" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/green) @@ -64,7 +66,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/green) /// Dark green tiles /obj/effect/turf_decal/tile/dark_green - name = "dark green corner" + name = "dark green tile decal" color = "#439C1E" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/dark_green) @@ -72,7 +74,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/dark_green) /// Yellow tiles /obj/effect/turf_decal/tile/yellow - name = "yellow corner" + name = "yellow tile decal" color = "#EFB341" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/yellow) @@ -80,7 +82,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/yellow) /// Red tiles /obj/effect/turf_decal/tile/red - name = "red corner" + name = "red tile decal" color = "#DE3A3A" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/red) @@ -88,7 +90,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/red) /// Dark red tiles /obj/effect/turf_decal/tile/dark_red - name = "dark red corner" + name = "dark red tile decal" color = "#B11111" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/dark_red) @@ -96,7 +98,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/dark_red) /// Bar tiles /obj/effect/turf_decal/tile/bar - name = "bar corner" + name = "bar tile decal" color = "#791500" alpha = 130 @@ -105,7 +107,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/bar) /// Purple tiles /obj/effect/turf_decal/tile/purple - name = "purple corner" + name = "purple tile decal" color = "#D381C9" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/purple) @@ -113,7 +115,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/purple) /// Brown tiles /obj/effect/turf_decal/tile/brown - name = "brown corner" + name = "brown tile decal" color = "#A46106" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/brown) @@ -121,7 +123,7 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/brown) /// Neutral tiles /obj/effect/turf_decal/tile/neutral - name = "neutral corner" + name = "neutral tile decal" color = "#D4D4D4" alpha = 50 @@ -130,22 +132,35 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/neutral) /// Dark tiles /obj/effect/turf_decal/tile/dark - name = "dark corner" + name = "dark tile decal" color = "#0e0f0f" TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/dark) -/// Random tiles +/// Date-specific tiles +/obj/effect/turf_decal/tile/holiday + name = "ERROR tile decal" + color = "#FF0000" -/obj/effect/turf_decal/tile/random // so many colors - name = "colorful corner" - color = "#E300FF" //bright pink as default for mapping +/obj/effect/turf_decal/tile/holiday/Initialize(mapload) + color = request_holiday_colors(src, pattern) + alpha = DECAL_ALPHA + return ..() -TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/random) +/// Pattern tiles +/obj/effect/turf_decal/tile/holiday/rainbow + name = "rainbow tile decal" + color = "#75C9EB" //bright blue as default for mapping + pattern = PATTERN_RAINBOW -/obj/effect/turf_decal/tile/random/Initialize(mapload) - color = "#[random_short_color()]" - return ..() +TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/holiday/rainbow) + +/obj/effect/turf_decal/tile/holiday/random // so many colors + name = "colorful tile decal" + color = "#E300FF" //bright pink as default for mapping + pattern = PATTERN_RANDOM + +TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/holiday/random) #undef TILE_DECAL_SUBTYPE_HELPER @@ -154,10 +169,29 @@ TILE_DECAL_SUBTYPE_HELPER(/obj/effect/turf_decal/tile/random) layer = TURF_PLATING_DECAL_LAYER alpha = 110 icon_state = "trimline_box" + use_holiday_colors = TRUE + +/obj/effect/turf_decal/trimline/tram + pattern = PATTERN_VERTICAL_STRIPE + +/obj/effect/turf_decal/trimline/tram/filled/corner/Initialize(mapload) + if(use_holiday_colors) + var/current_holiday_color = request_holiday_colors(src, pattern) + if(current_holiday_color) + color = current_holiday_color + alpha = DECAL_ALPHA + else + color = "#ffc875" + return ..() -/obj/effect/turf_decal/trimline/Initialize(mapload) - if(check_holidays(APRIL_FOOLS)) - color = "#[random_short_color()]" +/obj/effect/turf_decal/trimline/tram/filled/line/Initialize(mapload) + if(use_holiday_colors) + var/current_holiday_color = request_holiday_colors(src, pattern) + if(current_holiday_color) + color = current_holiday_color + alpha = DECAL_ALPHA + else + color = "#ffc875" return ..() /// Automatically generates all trimlines for a decal with the given path. @@ -285,6 +319,13 @@ TRIMLINE_SUBTYPE_HELPER(/obj/effect/turf_decal/trimline/brown) TRIMLINE_SUBTYPE_HELPER(/obj/effect/turf_decal/trimline/neutral) +/// Tram trimlines +/obj/effect/turf_decal/trimline/tram + color = "#D4D4D4" + alpha = 50 + +TRIMLINE_SUBTYPE_HELPER(/obj/effect/turf_decal/trimline/tram) + /// Dark trimlines /obj/effect/turf_decal/trimline/dark color = "#0e0f0f" @@ -292,3 +333,4 @@ TRIMLINE_SUBTYPE_HELPER(/obj/effect/turf_decal/trimline/neutral) TRIMLINE_SUBTYPE_HELPER(/obj/effect/turf_decal/trimline/dark) #undef TRIMLINE_SUBTYPE_HELPER +#undef DECAL_ALPHA diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 18c414f3033b..e65fcf02d60d 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -173,7 +173,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/choke_forever = FALSE /// When choking, what is the maximum amount of time we COULD choke for var/choke_time_max = 30 SECONDS // I am mean - /// What type of pollution does this produce on smoking, changed to weed pollution sometimes + /// What type of pollution does this produce on smoking, changed to weed pollution sometimes, monkestation edit var/pollution_type = /datum/pollutant/smoke /// The particle effect of the smoke rising out of the cigarette when lit VAR_PRIVATE/obj/effect/abstract/particle_holder/cig_smoke diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 71b6080f6cea..c748d70e6fcf 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -14,6 +14,10 @@ var/flush = FALSE var/mob/living/silicon/ai/AI +/obj/item/aicard/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT) + /obj/item/aicard/Destroy(force) if(AI) AI.ghostize(can_reenter_corpse = FALSE) diff --git a/code/game/objects/items/food/meatslab.dm b/code/game/objects/items/food/meatslab.dm index 4d4cf0bbac0b..50b777885464 100644 --- a/code/game/objects/items/food/meatslab.dm +++ b/code/game/objects/items/food/meatslab.dm @@ -4,6 +4,28 @@ icon = 'icons/obj/food/meat.dmi' var/subjectname = "" var/subjectjob = null + var/blood_decal_type = /obj/effect/decal/cleanable/blood + +/obj/item/food/meat/Initialize(mapload) + . = ..() + + if(!blood_decal_type) + return + + AddComponent( + /datum/component/blood_walk,\ + blood_type = blood_decal_type,\ + blood_spawn_chance = 45,\ + max_blood = custom_materials[custom_materials[1]],\ + ) + + AddComponent( + /datum/component/bloody_spreader,\ + blood_left = custom_materials[custom_materials[1]],\ + blood_dna = list("meaty DNA" = "MT-"),\ + diseases = null,\ + ) + /obj/item/food/meat/slab name = "meat" @@ -54,6 +76,7 @@ tastes = list("slime" = 1, "jelly" = 1) foodtypes = MEAT | RAW | TOXIC venue_value = FOOD_MEAT_MUTANT_RARE + blood_decal_type = null /obj/item/food/meat/slab/human/mutant/golem icon_state = "golemmeat" @@ -65,6 +88,7 @@ tastes = list("rock" = 1) foodtypes = MEAT | RAW | GROSS venue_value = FOOD_MEAT_MUTANT_RARE + blood_decal_type = null /obj/item/food/meat/slab/human/mutant/golem/adamantine icon_state = "agolemmeat" @@ -87,6 +111,7 @@ tastes = list("salad" = 1, "wood" = 1) foodtypes = VEGETABLES venue_value = FOOD_MEAT_MUTANT_RARE + blood_decal_type = /obj/effect/decal/cleanable/food/plant_smudge /obj/item/food/meat/slab/human/mutant/shadow icon_state = "shadowmeat" @@ -105,6 +130,7 @@ tastes = list("maggots" = 1, "the inside of a reactor" = 1) foodtypes = MEAT | RAW | GROSS | BUGS | GORE venue_value = FOOD_MEAT_MUTANT + blood_decal_type = /obj/effect/decal/cleanable/insectguts /obj/item/food/meat/slab/human/mutant/moth icon_state = "mothmeat" @@ -120,6 +146,7 @@ tastes = list("bone" = 1) foodtypes = GROSS | GORE venue_value = FOOD_MEAT_MUTANT_RARE + blood_decal_type = null /obj/item/food/meat/slab/human/mutant/skeleton/make_processable() return //skeletons dont have cutlets @@ -138,6 +165,7 @@ tastes = list("pure electricity" = 2, "glass" = 1) //MONKE EDIT: "meat" to "glass" foodtypes = RAW | MEAT | TOXIC | GORE venue_value = FOOD_MEAT_MUTANT + blood_decal_type = null ////////////////////////////////////// OTHER MEATS //////////////////////////////////////////////////////// @@ -169,6 +197,7 @@ name = "bug meat" icon_state = "spidermeat" foodtypes = RAW | MEAT | BUGS + blood_decal_type = /obj/effect/decal/cleanable/insectguts /obj/item/food/meat/slab/mouse name = "mouse meat" @@ -214,6 +243,7 @@ food_reagents = list(/datum/reagent/consumable/nutriment = 2) tastes = list("tomato" = 1) foodtypes = FRUIT + blood_decal_type = /obj/effect/decal/cleanable/food/tomato_smudge /obj/item/food/meat/slab/killertomato/make_grillable() AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/killertomato, rand(70 SECONDS, 85 SECONDS), TRUE, TRUE) @@ -255,6 +285,7 @@ bite_consumption = 4 tastes = list("meat" = 1, "acid" = 1) foodtypes = RAW | MEAT + blood_decal_type = /obj/effect/decal/cleanable/xenoblood /obj/item/food/meat/slab/xeno/make_processable() AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/meat/rawcutlet/xeno, 3, 3 SECONDS, table_required = TRUE, screentip_verb = "Cut") @@ -273,6 +304,7 @@ ) tastes = list("cobwebs" = 1) foodtypes = RAW | MEAT | TOXIC + blood_decal_type = /obj/effect/decal/cleanable/insectguts /obj/item/food/meat/slab/spider/make_processable() AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/meat/rawcutlet/spider, 3, 3 SECONDS, table_required = TRUE, screentip_verb = "Cut") @@ -489,10 +521,12 @@ /obj/item/food/meat/steak/xeno name = "xeno steak" tastes = list("meat" = 1, "acid" = 1) + blood_decal_type = /obj/effect/decal/cleanable/xenoblood /obj/item/food/meat/steak/spider name = "spider steak" tastes = list("cobwebs" = 1) + blood_decal_type = /obj/effect/decal/cleanable/insectguts /obj/item/food/meat/steak/goliath name = "goliath steak" @@ -595,6 +629,7 @@ name = "raw killer tomato cutlet" tastes = list("tomato" = 1) foodtypes = FRUIT + blood_decal_type = /obj/effect/decal/cleanable/food/tomato_smudge /obj/item/food/meat/rawcutlet/killertomato/make_grillable() AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/killertomato, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE) @@ -613,6 +648,7 @@ /obj/item/food/meat/rawcutlet/xeno name = "raw xeno cutlet" tastes = list("meat" = 1, "acid" = 1) + blood_decal_type = /obj/effect/decal/cleanable/xenoblood /obj/item/food/meat/rawcutlet/xeno/make_grillable() AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/xeno, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat) @@ -620,6 +656,7 @@ /obj/item/food/meat/rawcutlet/spider name = "raw spider cutlet" tastes = list("cobwebs" = 1) + blood_decal_type = /obj/effect/decal/cleanable/insectguts /obj/item/food/meat/rawcutlet/spider/make_grillable() AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/spider, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat) @@ -696,6 +733,7 @@ name = "killer tomato cutlet" tastes = list("tomato" = 1) foodtypes = FRUIT + blood_decal_type = /obj/effect/decal/cleanable/food/tomato_smudge /obj/item/food/meat/cutlet/bear name = "bear cutlet" @@ -704,10 +742,12 @@ /obj/item/food/meat/cutlet/xeno name = "xeno cutlet" tastes = list("meat" = 1, "acid" = 1) + blood_decal_type = /obj/effect/decal/cleanable/xenoblood /obj/item/food/meat/cutlet/spider name = "spider cutlet" tastes = list("cobwebs" = 1) + blood_decal_type = /obj/effect/decal/cleanable/insectguts /obj/item/food/meat/cutlet/gondola name = "gondola cutlet" diff --git a/code/game/objects/items/his_grace.dm b/code/game/objects/items/his_grace.dm index 96f07bd8a233..fe0e12d4d50d 100644 --- a/code/game/objects/items/his_grace.dm +++ b/code/game/objects/items/his_grace.dm @@ -164,22 +164,8 @@ if(!awakened) return - var/static/list/transforms - if(!transforms) - var/matrix/M1 = matrix() - var/matrix/M2 = matrix() - var/matrix/M3 = matrix() - var/matrix/M4 = matrix() - M1.Translate(-1, 0) - M2.Translate(0, 1) - M3.Translate(1, 0) - M4.Translate(0, -1) - transforms = list(M1, M2, M3, M4) - animate(src, transform=transforms[1], time=0.2, loop=-1) - animate(transform=transforms[2], time=0.1) - animate(transform=transforms[3], time=0.2) - animate(transform=transforms[4], time=0.3) + spasm_animation() /obj/item/his_grace/proc/drowse() //Good night, Mr. Grace. if(!awakened || ascended) diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index e47cbd0ebdda..2677016f9f93 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -1026,6 +1026,33 @@ /obj/item/stack/tile/noslip/thirty amount = 30 +/obj/item/stack/tile/noslip/tram + name = "high-traction platform tile" + singular_name = "high-traction platform tile" + desc = "A titanium-aluminium induction plate that powers the tram." + icon_state = "tile_noslip" + inhand_icon_state = "tile-noslip" + turf_type = /turf/open/floor/noslip/tram + merge_type = /obj/item/stack/tile/noslip/tram + +/obj/item/stack/tile/noslip/tram_platform + name = "tram platform tiles" + singular_name = "tram platform" + desc = "A tile used for tram platforms." + icon_state = "darkiron_catwalk" + inhand_icon_state = "tile-neon" + turf_type = /turf/open/floor/noslip/tram_platform + merge_type = /obj/item/stack/tile/noslip/tram_platform + +/obj/item/stack/tile/noslip/tram_plate + name = "high-traction platform tile" + singular_name = "high-traction platform tile" + desc = "A high-traction tile used for tram platforms." + icon_state = "darkiron_plate" + inhand_icon_state = "tile-neon" + turf_type = /turf/open/floor/noslip/tram_plate + merge_type = /obj/item/stack/tile/noslip/tram_plate + //Circuit /obj/item/stack/tile/circuit name = "blue circuit tile" diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 57d4a2219dd0..37e6720dd06e 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -255,6 +255,7 @@ throwforce = 15 attack_verb_continuous = list("MEATS", "MEAT MEATS") attack_verb_simple = list("MEAT", "MEAT MEAT") + custom_materials = list(/datum/material/meat = SHEET_MATERIAL_AMOUNT * 25) // MEAT ///Sounds used in the squeak component var/list/meat_sounds = list('sound/effects/blobattack.ogg' = 1) ///Reagents added to the edible component, ingested when you EAT the MEAT @@ -271,13 +272,26 @@ /obj/item/storage/backpack/meat/Initialize(mapload) . = ..() - AddComponent(/datum/component/edible,\ + AddComponent( + /datum/component/edible,\ initial_reagents = meat_reagents,\ foodtypes = foodtypes,\ tastes = tastes,\ eatverbs = eatverbs,\ ) AddComponent(/datum/component/squeak, meat_sounds) + AddComponent( + /datum/component/blood_walk,\ + blood_type = /obj/effect/decal/cleanable/blood,\ + blood_spawn_chance = 15,\ + max_blood = 300,\ + ) + AddComponent( + /datum/component/bloody_spreader,\ + blood_left = INFINITY,\ + blood_dna = list("MEAT DNA" = "MT+"),\ + diseases = null,\ + ) /* * Satchel Types diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 61da8337811f..e0dcefc072dd 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -639,7 +639,7 @@ /obj/item/storage/belt/wands/Initialize(mapload) . = ..() - atom_storage.max_slots = 6 + atom_storage.max_slots = 7 atom_storage.set_holdable(list( /obj/item/gun/magic/wand, )) @@ -651,6 +651,7 @@ new /obj/item/gun/magic/wand/teleport(src) new /obj/item/gun/magic/wand/door(src) new /obj/item/gun/magic/wand/fireball(src) + new /obj/item/gun/magic/wand/shrink(src) for(var/obj/item/gun/magic/wand/W in contents) //All wands in this pack come in the best possible condition W.max_charges = initial(W.max_charges) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 728e1504531b..e9d319803b7b 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -813,7 +813,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) return COMSIG_CARBON_SHOVE_HANDLED /// Signal proc for [COMSIG_ATOM_MAGICALLY_UNLOCKED]. Unlock and open up when we get knock casted. -/obj/structure/closet/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, mob/living/caster) +/obj/structure/closet/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, atom/caster) SIGNAL_HANDLER locked = FALSE diff --git a/code/game/turfs/open/floor/misc_floor.dm b/code/game/turfs/open/floor/misc_floor.dm index 4e83c9acb054..81249fe0ace6 100644 --- a/code/game/turfs/open/floor/misc_floor.dm +++ b/code/game/turfs/open/floor/misc_floor.dm @@ -155,6 +155,14 @@ /turf/open/floor/noslip/MakeSlippery(wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent) return +/turf/open/floor/noslip/tram/Initialize(mapload) + . = ..() + var/current_holiday_color = request_holiday_colors(src, PATTERN_VERTICAL_STRIPE) + if(current_holiday_color) + color = current_holiday_color + else + color = "#EFB341" + /turf/open/floor/oldshuttle icon = 'icons/turf/shuttleold.dmi' icon_state = "floor" diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index 2adf60b77cbd..a172f3d84542 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -1,4 +1,4 @@ -GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) +GLOBAL_DATUM(everyone_an_antag, /datum/everyone_is_an_antag_controller) /client/proc/secrets() //Creates a verb for admins to open up the ui set name = "Secrets" @@ -453,24 +453,35 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) for(var/i in GLOB.human_list) var/mob/living/carbon/human/H = i INVOKE_ASYNC(H, TYPE_PROC_REF(/mob/living/carbon, monkeyize)) - if("traitor_all") + if("antag_all") if(!is_funmin) return if(!SSticker.HasRoundStarted()) tgui_alert(usr,"The game hasn't started yet!") return - if(GLOB.everyone_a_traitor) - tgui_alert(usr, "The everyone is a traitor secret has already been triggered") + if(GLOB.everyone_an_antag) + var/are_we_antagstacking = tgui_alert(usr, "The everyone is antag secret has already been triggered. Do you want to stack antags?", "DANGER ZONE. Are you sure about this?", list("Confirm", "Abort")) + if(are_we_antagstacking != "Confirm") + return + + var/chosen_antag = tgui_input_list(usr, "Choose antag", "Chose antag", list(ROLE_TRAITOR, ROLE_CHANGELING, ROLE_HERETIC, ROLE_CULTIST, ROLE_NINJA, ROLE_WIZARD, ROLE_NIGHTMARE)) + if(!chosen_antag) return - var/objective = tgui_input_text(holder, "Enter an objective", "Objective") + var/objective = tgui_input_text(usr, "Enter an objective", "Objective") if(!objective) return - GLOB.everyone_a_traitor = new /datum/everyone_is_a_traitor_controller(objective) - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Traitor All", "[objective]")) + var/confirmation = tgui_alert(usr, "Make everyone in to [chosen_antag] with objective: [objective]", "Are you sure about this?", list("Confirm", "Abort")) + if(confirmation != "Confirm") + return + var/keep_generic_objecives = tgui_alert(usr, "Generate normal objectives?", "Give default objectives?", list("Yes", "No")) + keep_generic_objecives = (keep_generic_objecives != "Yes") ? FALSE : TRUE + + GLOB.everyone_an_antag = new /datum/everyone_is_an_antag_controller(chosen_antag, objective, keep_generic_objecives) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("[chosen_antag] All", "[objective]")) for(var/mob/living/player in GLOB.player_list) - GLOB.everyone_a_traitor.make_traitor(null, player) - message_admins(span_adminnotice("[key_name_admin(holder)] used everyone is a traitor secret. Objective is [objective]")) - log_admin("[key_name(holder)] used everyone is a traitor secret. Objective is [objective]") + GLOB.everyone_an_antag.make_antag(null, player) + message_admins(span_adminnotice("[key_name_admin(holder)] used everyone is antag secret. Antag is [chosen_antag]. Objective is [objective]. Generate default objectives: [keep_generic_objecives]")) + log_admin("[key_name(holder)] used everyone is antag secret: [chosen_antag] . Objective is [objective]. Generate default objectives: [keep_generic_objecives]. ") if("massbraindamage") if(!is_funmin) return @@ -623,38 +634,79 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) T.flick_overlay_static(portal_appearance[GET_TURF_PLANE_OFFSET(T) + 1], 15) playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), TRUE) -///Makes sure latejoining crewmembers also become traitors. -/datum/everyone_is_a_traitor_controller +/datum/everyone_is_an_antag_controller + var/chosen_antag = "" var/objective = "" + var/keep_generic_objecives -/datum/everyone_is_a_traitor_controller/New(objective) +/datum/everyone_is_an_antag_controller/New(chosen_antag, objective, keep_generic_objecives) + . = ..() + src.chosen_antag = chosen_antag src.objective = objective - RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(make_traitor)) + src.keep_generic_objecives = keep_generic_objecives + RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(make_antag_delay)) -/datum/everyone_is_a_traitor_controller/Destroy() +/datum/everyone_is_an_antag_controller/Destroy() UnregisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED) return ..() -/datum/everyone_is_a_traitor_controller/proc/make_traitor(datum/source, mob/living/player) +/datum/everyone_is_an_antag_controller/proc/assign_admin_objective_and_antag(mob/living/player, datum/antagonist/antag_datum) + var/datum/objective/new_objective = new(objective) + new_objective.team = player + new_objective.team_explanation_text = objective + antag_datum.objectives += new_objective + player.mind.add_antag_datum(antag_datum) + +/datum/everyone_is_an_antag_controller/proc/make_antag_delay(datum/source, mob/living/player) SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(make_antag), source, player) + + +/datum/everyone_is_an_antag_controller/proc/make_antag(datum/source, mob/living/player) if(player.stat == DEAD || !player.mind) return - if(is_special_character(player)) - return + sleep(1) if(ishuman(player)) - var/datum/antagonist/traitor/traitor_datum = new(give_objectives = FALSE) - var/datum/objective/new_objective = new - new_objective.owner = player - new_objective.explanation_text = objective - traitor_datum.objectives += new_objective - player.mind.add_antag_datum(traitor_datum) - var/datum/uplink_handler/uplink = traitor_datum.uplink_handler - uplink.has_progression = FALSE - uplink.has_objectives = FALSE + switch(chosen_antag) + if(ROLE_TRAITOR) + var/datum/antagonist/traitor/antag_datum = new(give_objectives = keep_generic_objecives) + assign_admin_objective_and_antag(player, antag_datum) + var/datum/uplink_handler/uplink = antag_datum.uplink_handler + uplink.has_progression = FALSE + uplink.has_objectives = FALSE + if(ROLE_CHANGELING) + var/datum/antagonist/changeling/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_HERETIC) + var/datum/antagonist/heretic/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_CULTIST) + var/datum/antagonist/cult/antag_datum = new + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_NINJA) + var/datum/antagonist/ninja/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + for(var/obj/item/item_to_drop in player) + if(!istype(item_to_drop, /obj/item/implant)) //avoid removing implanted uplinks + player.dropItemToGround(item_to_drop, FALSE) + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_WIZARD) + var/datum/antagonist/wizard/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + antag_datum.move_to_lair = FALSE + for(var/obj/item/item_to_drop in player) //avoid deleting player's items + if(!istype(item_to_drop, /obj/item/implant)) + player.dropItemToGround(item_to_drop, FALSE) + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_NIGHTMARE) + var/datum/antagonist/nightmare/antag_datum = new + assign_admin_objective_and_antag(player, antag_datum) + player.set_species(/datum/species/shadow/nightmare) + else if(isAI(player)) - var/datum/antagonist/malf_ai/malfunction_datum = new(give_objectives = FALSE) - var/datum/objective/new_objective = new - new_objective.owner = player - new_objective.explanation_text = objective - malfunction_datum.objectives += new_objective - player.mind.add_antag_datum(malfunction_datum) + var/datum/antagonist/malf_ai/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + assign_admin_objective_and_antag(player, antag_datum) + diff --git a/code/modules/antagonists/brainwashing/brainwashing.dm b/code/modules/antagonists/brainwashing/brainwashing.dm index e74e7b32f7f9..7f60de3a1436 100644 --- a/code/modules/antagonists/brainwashing/brainwashing.dm +++ b/code/modules/antagonists/brainwashing/brainwashing.dm @@ -35,7 +35,7 @@ roundend_category = "brainwashed victims" show_in_antagpanel = TRUE antag_hud_name = "brainwashed" - antagpanel_category = "Other" + antagpanel_category = ANTAG_GROUP_CREW show_name_in_check_antagonists = TRUE count_against_dynamic_roll_chance = FALSE ui_name = "AntagInfoBrainwashed" diff --git a/code/modules/antagonists/fugitive/fugitive.dm b/code/modules/antagonists/fugitive/fugitive.dm index 1ef9989dc607..7f2cc4a9f3d5 100644 --- a/code/modules/antagonists/fugitive/fugitive.dm +++ b/code/modules/antagonists/fugitive/fugitive.dm @@ -5,6 +5,8 @@ job_rank = ROLE_FUGITIVE silent = TRUE //greet called by the event show_in_antagpanel = FALSE + show_to_ghosts = TRUE + antagpanel_category = ANTAG_GROUP_FUGITIVES prevent_roundtype_conversion = FALSE antag_hud_name = "fugitive" suicide_cry = "FOR FREEDOM!!" diff --git a/code/modules/antagonists/fugitive/hunters/hunter.dm b/code/modules/antagonists/fugitive/hunters/hunter.dm index 32d18998e748..fd81d588d9fe 100644 --- a/code/modules/antagonists/fugitive/hunters/hunter.dm +++ b/code/modules/antagonists/fugitive/hunters/hunter.dm @@ -4,6 +4,8 @@ roundend_category = "Fugitive" silent = TRUE //greet called by the spawn show_in_antagpanel = FALSE + show_to_ghosts = TRUE + antagpanel_category = ANTAG_GROUP_HUNTERS prevent_roundtype_conversion = FALSE antag_hud_name = "fugitive_hunter" suicide_cry = "FOR GLORY!!" diff --git a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm index 596420ba8f4c..47dbcbb21b25 100644 --- a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm +++ b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm @@ -138,6 +138,19 @@ if(gored) name = gored.real_name + AddComponent( + /datum/component/blood_walk,\ + blood_type = /obj/effect/decal/cleanable/blood,\ + blood_spawn_chance = 66.6,\ + max_blood = INFINITY,\ + ) + + AddComponent(/datum/component/bloody_spreader,\ + blood_left = INFINITY,\ + blood_dna = list("meaty DNA" = "MT-"),\ + diseases = null,\ + ) + /obj/structure/bouncy_castle/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) switch(damage_type) if(BRUTE) diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm index c83fde235797..ac2a6400ae56 100644 --- a/code/modules/antagonists/heretic/items/heretic_blades.dm +++ b/code/modules/antagonists/heretic/items/heretic_blades.dm @@ -90,6 +90,23 @@ inhand_icon_state = "flesh_blade" after_use_message = "The Marshal hears your call..." +/obj/item/melee/sickly_blade/flesh/Initialize(mapload) + . = ..() + + AddComponent( + /datum/component/blood_walk,\ + blood_type = /obj/effect/decal/cleanable/blood,\ + blood_spawn_chance = 66.6,\ + max_blood = INFINITY,\ + ) + + AddComponent( + /datum/component/bloody_spreader,\ + blood_left = INFINITY,\ + blood_dna = list("Unknown DNA" = "X*"),\ + diseases = null,\ + ) + // Path of Void's blade /obj/item/melee/sickly_blade/void name = "\improper void blade" diff --git a/code/modules/antagonists/heretic/magic/aggressive_spread.dm b/code/modules/antagonists/heretic/magic/aggressive_spread.dm index c15ae041a47a..de1233382f64 100644 --- a/code/modules/antagonists/heretic/magic/aggressive_spread.dm +++ b/code/modules/antagonists/heretic/magic/aggressive_spread.dm @@ -17,11 +17,7 @@ aoe_radius = 3 /datum/action/cooldown/spell/aoe/rust_conversion/get_things_to_cast_on(atom/center) - var/list/things = list() - for(var/turf/nearby_turf in range(aoe_radius, center)) - things += nearby_turf - - return things + return RANGE_TURFS(aoe_radius, center) /datum/action/cooldown/spell/aoe/rust_conversion/cast_on_thing_in_aoe(turf/victim, atom/caster) // We have less chance of rusting stuff that's further diff --git a/code/modules/antagonists/hypnotized/hypnotized.dm b/code/modules/antagonists/hypnotized/hypnotized.dm index 2ee17b671aaa..4f1f49aa3be7 100644 --- a/code/modules/antagonists/hypnotized/hypnotized.dm +++ b/code/modules/antagonists/hypnotized/hypnotized.dm @@ -6,7 +6,7 @@ antag_hud_name = "brainwashed" ui_name = "AntagInfoBrainwashed" show_in_antagpanel = TRUE - antagpanel_category = "Other" + antagpanel_category = ANTAG_GROUP_CREW show_name_in_check_antagonists = TRUE count_against_dynamic_roll_chance = FALSE diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm index 863b32e28d8f..6adcf901aaa3 100644 --- a/code/modules/antagonists/malf_ai/malf_ai.dm +++ b/code/modules/antagonists/malf_ai/malf_ai.dm @@ -57,8 +57,6 @@ /// Generates a complete set of malf AI objectives up to the traitor objective limit. /datum/antagonist/malf_ai/proc/forge_ai_objectives() - objectives.Cut() - if(prob(PROB_SPECIAL)) forge_special_objective() diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm index 82d59ba02925..a94821f71f86 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm @@ -618,6 +618,9 @@ GLOBAL_VAR(station_nuke_source) * Helper proc that handles gibbing someone who has been nuked. */ /proc/nuke_gib(mob/living/gibbed, atom/source) + if(HAS_TRAIT(gibbed, TRAIT_NUKEIMMUNE)) + return FALSE + if(istype(gibbed.loc, /obj/structure/closet/secure_closet/freezer)) var/obj/structure/closet/secure_closet/freezer/freezer = gibbed.loc if(!freezer.jones) diff --git a/code/modules/antagonists/obsessed/obsessed.dm b/code/modules/antagonists/obsessed/obsessed.dm index 585867d5767b..a079f95cfa0e 100644 --- a/code/modules/antagonists/obsessed/obsessed.dm +++ b/code/modules/antagonists/obsessed/obsessed.dm @@ -1,8 +1,9 @@ /datum/antagonist/obsessed name = "Obsessed" show_in_antagpanel = TRUE - antagpanel_category = "Other" + antagpanel_category = ANTAG_GROUP_CREW job_rank = ROLE_OBSESSED + show_to_ghosts = TRUE antag_hud_name = "obsessed" show_name_in_check_antagonists = TRUE roundend_category = "obsessed" diff --git a/code/modules/antagonists/paradox_clone/paradox_clone.dm b/code/modules/antagonists/paradox_clone/paradox_clone.dm index 8ec0a75e8c9f..0773d949d25c 100644 --- a/code/modules/antagonists/paradox_clone/paradox_clone.dm +++ b/code/modules/antagonists/paradox_clone/paradox_clone.dm @@ -2,7 +2,9 @@ name = "\improper Paradox Clone" roundend_category = "Paradox Clone" job_rank = ROLE_PARADOX_CLONE + antagpanel_category = ANTAG_GROUP_PARADOX antag_hud_name = "paradox_clone" + show_to_ghosts = TRUE suicide_cry = "THERE CAN BE ONLY ONE!!" preview_outfit = /datum/outfit/paradox_clone diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm index 88df5c03a197..a2a6eb12ad0b 100644 --- a/code/modules/antagonists/space_dragon/carp_rift.dm +++ b/code/modules/antagonists/space_dragon/carp_rift.dm @@ -18,13 +18,13 @@ if(!dragon) return var/area/rift_location = get_area(owner) - if(!(rift_location.area_flags & VALID_TERRITORY)) - to_chat(owner, span_warning("You can't summon a rift here! Try summoning somewhere secure within the station!")) + if(!(rift_location in dragon.chosen_rift_areas)) + owner.balloon_alert(owner, "can't summon a rift here!") return for(var/obj/structure/carp_rift/rift as anything in dragon.rift_list) var/area/used_location = get_area(rift) if(used_location == rift_location) - to_chat(owner, span_warning("You've already summoned a rift in this area! You have to summon again somewhere else!")) + owner.balloon_alert(owner, "already summoned a rift here!") return var/turf/rift_spawn_turf = get_turf(dragon) if(isopenspaceturf(rift_spawn_turf)) @@ -70,7 +70,7 @@ light_color = LIGHT_COLOR_PURPLE light_outer_range = 10 anchored = TRUE - density = FALSE + density = TRUE plane = MASSIVE_OBJ_PLANE /// The amount of time the rift has charged for. var/time_charged = 0 @@ -83,11 +83,13 @@ /// Current charge state of the rift. var/charge_state = CHARGE_ONGOING /// The interval for adding additional space carp spawns to the rift. - var/carp_interval = 60 + var/carp_interval = 45 /// The time since an extra carp was added to the ghost role spawning pool. var/last_carp_inc = 0 /// A list of all the ckeys which have used this carp rift to spawn in as carps. var/list/ckey_list = list() + /// Gravity aura for the rift, makes all turfs nearby forced grav. + var/datum/proximity_monitor/advanced/gravity/warns_on_entrance/gravity_aura /datum/armor/structure_carp_rift energy = 100 @@ -101,14 +103,25 @@ AddComponent( \ /datum/component/aura_healing, \ - range = 0, \ + range = 1, \ simple_heal = 5, \ limit_to_trait = TRAIT_HEALS_FROM_CARP_RIFTS, \ healing_color = COLOR_BLUE, \ ) + gravity_aura = new( + /* host = */src, + /* range = */15, + /* ignore_if_not_on_turf = */TRUE, + /* gravity = */1, + ) + START_PROCESSING(SSobj, src) +/obj/structure/carp_rift/Destroy() + QDEL_NULL(gravity_aura) + return ..() + // Carp rifts always take heavy explosion damage. Discourages the use of maxcaps // and favours more weaker explosives to destroy the portal // as they have the same effect on the portal. @@ -239,12 +252,13 @@ to_chat(user, span_warning("The rift already summoned enough carp!")) return FALSE - if(!dragon) + if(isnull(dragon)) return var/mob/living/newcarp = new dragon.minion_to_spawn(loc) newcarp.faction = dragon.owner.current.faction newcarp.AddElement(/datum/element/nerfed_pulling, GLOB.typecache_general_bad_things_to_easily_move) newcarp.AddElement(/datum/element/prevent_attacking_of_types, GLOB.typecache_general_bad_hostile_attack_targets, "this tastes awful!") + dragon.wavespeak?.link_mob(newcarp) if(!is_listed) ckey_list += user.ckey diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index 66d1e13461aa..3338c62612b1 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -21,9 +21,13 @@ /// Whether or not Space Dragon has completed their objective, and thus triggered the ending sequence. var/objective_complete = FALSE /// What mob to spawn from ghosts using this dragon's rifts - var/minion_to_spawn = /mob/living/basic/carp + var/minion_to_spawn = /mob/living/basic/carp/advanced /// What AI mobs to spawn from this dragon's rifts var/ai_to_spawn = /mob/living/basic/carp + /// Wavespeak mind linker, to allow telepathy between dragon and carps + var/datum/component/mind_linker/wavespeak + /// What areas are we allowed to place rifts in? + var/list/chosen_rift_areas = list() /datum/antagonist/space_dragon/greet() . = ..() @@ -33,16 +37,42 @@ Today, we will snuff out one of those lights.") to_chat(owner, span_boldwarning("You have five minutes to find a safe location to place down the first rift. If you take longer than five minutes to place a rift, you will be returned from whence you came.")) owner.announce_objectives() - SEND_SOUND(owner.current, sound('sound/magic/demon_attack1.ogg')) + owner.current.playsound_local(get_turf(owner.current), 'sound/magic/demon_attack1.ogg', 80) /datum/antagonist/space_dragon/forge_objectives() + var/static/list/area/allowed_areas + if(!allowed_areas) + // Areas that will prove a challeng for the dragon and are provocative to the crew. + allowed_areas = typecacheof(list( + /area/station/command, + /area/station/engineering, + /area/station/science, + /area/station/security, + )) + + var/list/possible_areas = typecache_filter_list(get_sorted_areas(), allowed_areas) + for(var/area/possible_area as anything in possible_areas) + if(initial(possible_area.outdoors) || !(possible_area.area_flags & VALID_TERRITORY)) + possible_areas -= possible_area + + for(var/i in 1 to 5) + chosen_rift_areas += pick_n_take(possible_areas) + var/datum/objective/summon_carp/summon = new - summon.dragon = src objectives += summon + summon.owner = owner + summon.update_explanation_text() /datum/antagonist/space_dragon/on_gain() forge_objectives() rift_ability = new() + owner.special_role = ROLE_SPACE_DRAGON + owner.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon)) + return ..() + +/datum/antagonist/space_dragon/on_removal() + owner.special_role = null + owner.set_assigned_role(SSjob.GetJobType(/datum/job/unassigned)) return ..() /datum/antagonist/space_dragon/apply_innate_effects(mob/living/mob_override) @@ -52,6 +82,15 @@ antag.faction |= FACTION_CARP // Give the ability over if we have one rift_ability?.Grant(antag) + wavespeak = antag.AddComponent( \ + /datum/component/mind_linker, \ + network_name = "Wavespeak", \ + chat_color = "#635BAF", \ + signals_which_destroy_us = list(COMSIG_LIVING_DEATH), \ + speech_action_icon = 'icons/mob/actions/actions_space_dragon.dmi', \ + speech_action_icon_state = "wavespeak", \ + ) + RegisterSignal(wavespeak, COMSIG_QDELETING, PROC_REF(clear_wavespeak)) /datum/antagonist/space_dragon/remove_innate_effects(mob/living/mob_override) var/mob/living/antag = mob_override || owner.current @@ -59,11 +98,14 @@ UnregisterSignal(antag, COMSIG_LIVING_DEATH) antag.faction -= FACTION_CARP rift_ability?.Remove(antag) + QDEL_NULL(wavespeak) /datum/antagonist/space_dragon/Destroy() rift_list = null carp = null QDEL_NULL(rift_ability) + QDEL_NULL(wavespeak) + chosen_rift_areas.Cut() return ..() /datum/antagonist/space_dragon/get_preview_icon() @@ -77,6 +119,10 @@ return icon +/datum/antagonist/space_dragon/proc/clear_wavespeak() + SIGNAL_HANDLER + wavespeak = null + /** * Checks to see if we need to do anything with the current state of the dragon's rifts. * @@ -133,8 +179,7 @@ objective_complete = TRUE permanant_empower() var/datum/objective/summon_carp/main_objective = locate() in objectives - if(main_objective) - main_objective.completed = TRUE + main_objective?.completed = TRUE priority_announce("A large amount of lifeforms have been detected approaching [station_name()] at extreme speeds. \ Remaining crew are advised to evacuate as soon as possible.", "[command_name()] Wildlife Observations", has_important_message = TRUE) sound_to_playing_players('sound/creatures/space_dragon_roar.ogg') @@ -178,8 +223,19 @@ owner.current.remove_movespeed_modifier(/datum/movespeed_modifier/dragon_rage) /datum/objective/summon_carp - var/datum/antagonist/space_dragon/dragon - explanation_text = "Summon and protect the rifts to flood the station with carp." + explanation_text = "Summon 3 rifts in order to flood the station with carp." + +/datum/objective/summon_carp/update_explanation_text() + var/datum/antagonist/space_dragon/dragon_owner = owner.has_antag_datum(/datum/antagonist/space_dragon) + if(isnull(dragon_owner)) + return + + var/list/converted_names = list() + for(var/area/possible_area as anything in dragon_owner.chosen_rift_areas) + converted_names += possible_area.get_original_area_name() + + explanation_text = initial(explanation_text) + explanation_text += " Your possible rift locations are: [english_list(converted_names)]" /datum/antagonist/space_dragon/roundend_report() var/list/parts = list() @@ -198,7 +254,17 @@ parts += "The [name] was successful!" else parts += "The [name] has failed!" - if(carp.len) - parts += "The [name] was assisted by:" - parts += printplayerlist(carp) + + if(length(carp)) + parts += "
The [name] was assisted by:" + parts += "" + return "
[parts.Join("
")]
" diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 2aade03ae3fb..84a2f92ab9a6 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -205,7 +205,6 @@ /// Generates a complete set of traitor objectives up to the traitor objective limit, including non-generic objectives such as martyr and hijack. /datum/antagonist/traitor/proc/forge_traitor_objectives() - objectives.Cut() var/objective_count = 0 if((GLOB.joined_player_list.len >= HIJACK_MIN_PLAYERS) && prob(HIJACK_PROB)) diff --git a/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm b/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm index 3222deecd84c..623971276937 100644 --- a/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm +++ b/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm @@ -9,6 +9,7 @@ cooldown_time = 30 SECONDS cooldown_reduction_per_rank = 2 SECONDS delete_old = FALSE + delete_on_failure = FALSE /// Amount of time it takes you to rummage around in there var/cast_time = 3 SECONDS /// True while currently casting the spell @@ -56,7 +57,7 @@ return . | SPELL_CANCEL_CAST casting = FALSE -/datum/action/cooldown/spell/conjure_item/clown_pockets/make_item() +/datum/action/cooldown/spell/conjure_item/clown_pockets/make_item(atom/caster) item_type = pick_weight(clown_items) return ..() diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm index e576d7738c3d..03b38e10f56a 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm @@ -1,44 +1,45 @@ +#define SPELLBOOK_CATEGORY_ASSISTANCE "Assistance" // Wizard spells that assist the caster in some way /datum/spellbook_entry/summonitem name = "Summon Item" desc = "Recalls a previously marked item to your hand from anywhere in the universe." spell_type = /datum/action/cooldown/spell/summonitem - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE cost = 1 /datum/spellbook_entry/charge name = "Charge" desc = "This spell can be used to recharge a variety of things in your hands, from magical artifacts to electrical components. A creative wizard can even use it to grant magical power to a fellow magic user." spell_type = /datum/action/cooldown/spell/charge - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE cost = 1 /datum/spellbook_entry/shapeshift name = "Wild Shapeshift" desc = "Take on the shape of another for a time to use their natural abilities. Once you've made your choice it cannot be changed." spell_type = /datum/action/cooldown/spell/shapeshift/wizard - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE cost = 1 /datum/spellbook_entry/tap name = "Soul Tap" desc = "Fuel your spells using your own soul!" spell_type = /datum/action/cooldown/spell/tap - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE cost = 1 /datum/spellbook_entry/item/staffanimation name = "Staff of Animation" desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." item_path = /obj/item/gun/magic/staff/animate - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE /datum/spellbook_entry/item/soulstones name = "Soulstone Shard Kit" desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. \ The spell Artificer allows you to create arcane machines for the captured souls to pilot." item_path = /obj/item/storage/belt/soulstone/full - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE /datum/spellbook_entry/item/soulstones/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip) var/was_equipped = user.equip_to_slot_if_possible(to_equip, ITEM_SLOT_BELT, disable_warning = TRUE) @@ -56,13 +57,13 @@ name = "A Necromantic Stone" desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." item_path = /obj/item/necromantic_stone - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE /datum/spellbook_entry/item/contract name = "Contract of Apprenticeship" desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." item_path = /obj/item/antag_spawner/contract - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE refundable = TRUE /datum/spellbook_entry/item/guardian @@ -70,7 +71,7 @@ desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \ It would be wise to avoid buying these with anything capable of causing you to swap bodies with others." item_path = /obj/item/guardian_creator/wizard - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE /datum/spellbook_entry/item/bloodbottle name = "Bottle of Blood" @@ -80,7 +81,7 @@ in their killing, and you yourself may become a victim." item_path = /obj/item/antag_spawner/slaughter_demon limit = 3 - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE refundable = TRUE /datum/spellbook_entry/item/hugbottle @@ -95,5 +96,7 @@ item_path = /obj/item/antag_spawner/slaughter_demon/laughter cost = 1 //non-destructive; it's just a jape, sibling! limit = 3 - category = "Assistance" + category = SPELLBOOK_CATEGORY_ASSISTANCE refundable = TRUE + +#undef SPELLBOOK_CATEGORY_ASSISTANCE diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm index 46f78dfc6c4e..d03dc224e4e7 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm @@ -1,49 +1,50 @@ +#define SPELLBOOK_CATEGORY_DEFENSIVE "Defensive" // Defensive wizard spells /datum/spellbook_entry/magicm name = "Magic Missile" desc = "Fires several, slow moving, magic projectiles at nearby targets." spell_type = /datum/action/cooldown/spell/aoe/magic_missile - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/disabletech name = "Disable Tech" desc = "Disables all weapons, cameras and most other technology in range." spell_type = /datum/action/cooldown/spell/emp/disable_tech - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 1 /datum/spellbook_entry/repulse name = "Repulse" desc = "Throws everything around the user away." spell_type = /datum/action/cooldown/spell/aoe/repulse/wizard - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/lightning_packet name = "Thrown Lightning" desc = "Forged from eldrich energies, a packet of pure power, \ known as a spell packet will appear in your hand, that when thrown will stun the target." spell_type = /datum/action/cooldown/spell/conjure_item/spellpacket - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/timestop name = "Time Stop" desc = "Stops time for everyone except for you, allowing you to move freely \ while your enemies and even projectiles are frozen." spell_type = /datum/action/cooldown/spell/timestop - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/smoke name = "Smoke" desc = "Spawns a cloud of choking smoke at your location." spell_type = /datum/action/cooldown/spell/smoke - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 1 /datum/spellbook_entry/forcewall name = "Force Wall" desc = "Create a magical barrier that only you can pass through." spell_type = /datum/action/cooldown/spell/forcewall - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 1 /datum/spellbook_entry/lichdom @@ -60,21 +61,21 @@ name = "Chuuni Invocations" desc = "Makes all your spells shout invocations, and the invocations become... stupid. You heal slightly after casting a spell." spell_type = /datum/action/cooldown/spell/chuuni_invocations - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/spacetime_dist name = "Spacetime Distortion" desc = "Entangle the strings of space-time in an area around you, \ randomizing the layout and making proper movement impossible. The strings vibrate..." spell_type = /datum/action/cooldown/spell/spacetime_dist - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 1 /datum/spellbook_entry/the_traps name = "The Traps!" desc = "Summon a number of traps around you. They will damage and enrage any enemies that step on them." spell_type = /datum/action/cooldown/spell/conjure/the_traps - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 1 /datum/spellbook_entry/bees @@ -82,7 +83,7 @@ desc = "This spell magically kicks a transdimensional beehive, \ instantly summoning a swarm of bees to your location. These bees are NOT friendly to anyone." spell_type = /datum/action/cooldown/spell/conjure/bee - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/duffelbag name = "Bestow Cursed Duffel Bag" @@ -91,7 +92,7 @@ if it is not fed regularly, and regardless of whether or not it's been fed, \ it will slow the person wearing it down significantly." spell_type = /datum/action/cooldown/spell/touch/duffelbag - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 1 /datum/spellbook_entry/item/staffhealing @@ -99,26 +100,26 @@ desc = "An altruistic staff that can heal the lame and raise the dead." item_path = /obj/item/gun/magic/staff/healing cost = 1 - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/item/lockerstaff name = "Staff of the Locker" desc = "A staff that shoots lockers. It eats anyone it hits on its way, leaving a welded locker with your victims behind." item_path = /obj/item/gun/magic/staff/locker - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/item/scryingorb name = "Scrying Orb" desc = "An incandescent orb of crackling energy. Using it will allow you to release your ghost while alive, allowing you to spy upon the station and talk to the deceased. In addition, buying it will permanently grant you X-ray vision." item_path = /obj/item/scrying - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/item/wands name = "Wand Assortment" desc = "A collection of wands that allow for a wide variety of utility. \ Wands have a limited number of charges, so be conservative with their use. Comes in a handy belt." item_path = /obj/item/storage/belt/wands/full - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 3 //monkestation edit /datum/spellbook_entry/item/wands/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip) @@ -131,7 +132,7 @@ while providing more protection against attacks and the void of space. \ Also grants a battlemage shield." item_path = /obj/item/mod/control/pre_equipped/enchanted - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE /datum/spellbook_entry/item/armor/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip) var/obj/item/mod/control/mod = to_equip @@ -150,7 +151,9 @@ /datum/spellbook_entry/item/battlemage_charge name = "Battlemage Armour Charges" - desc = "A powerful defensive rune, it will grant twelve additional charges to a battlemage shield." //monkestation edit: reaplced eight with twelve + desc = "A powerful defensive rune, it will grant a battlemage shield the ability to absorb 50 more damage." //monkestation edited item_path = /obj/item/wizard_armour_charge - category = "Defensive" + category = SPELLBOOK_CATEGORY_DEFENSIVE cost = 1 + +#undef SPELLBOOK_CATEGORY_DEFENSIVE diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm index d5112a59611b..2893bf5614e4 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm @@ -1,47 +1,48 @@ +#define SPELLBOOK_CATEGORY_MOBILITY "Mobility" // Wizard spells that aid mobiilty(or stealth?) /datum/spellbook_entry/mindswap name = "Mindswap" desc = "Allows you to switch bodies with a target next to you. You will both fall asleep when this happens, and it will be quite obvious that you are the target's body if someone watches you do it." spell_type = /datum/action/cooldown/spell/pointed/mind_transfer - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY /datum/spellbook_entry/knock name = "Knock" desc = "Opens nearby doors and closets." spell_type = /datum/action/cooldown/spell/aoe/knock - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY cost = 1 /datum/spellbook_entry/blink name = "Blink" desc = "Randomly teleports you a short distance." spell_type = /datum/action/cooldown/spell/teleport/radius_turf/blink - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY /datum/spellbook_entry/teleport name = "Teleport" desc = "Teleports you to an area of your selection." spell_type = /datum/action/cooldown/spell/teleport/area_teleport/wizard - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY /datum/spellbook_entry/jaunt name = "Ethereal Jaunt" desc = "Turns your form ethereal, temporarily making you invisible and able to pass through walls." spell_type = /datum/action/cooldown/spell/jaunt/ethereal_jaunt - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY /datum/spellbook_entry/swap name = "Swap" desc = "Switch places with any living target within nine tiles. Right click to mark a secondary target. You will always swap to your primary target." spell_type = /datum/action/cooldown/spell/pointed/swap - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY cost = 1 /datum/spellbook_entry/item/warpwhistle name = "Warp Whistle" desc = "A strange whistle that will transport you to a distant safe place on the station. There is a window of vulnerability at the beginning of every use." item_path = /obj/item/warp_whistle - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY cost = 1 /datum/spellbook_entry/item/staffdoor @@ -49,4 +50,6 @@ desc = "A particular staff that can mold solid walls into ornate doors. Useful for getting around in the absence of other transportation. Does not work on glass." item_path = /obj/item/gun/magic/staff/door cost = 1 - category = "Mobility" + category = SPELLBOOK_CATEGORY_MOBILITY + +#undef SPELLBOOK_CATEGORY_MOBILITY diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm index 57aa2ad7af18..c30ca31915b5 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm @@ -1,27 +1,28 @@ +#define SPELLBOOK_CATEGORY_OFFENSIVE "Offensive" // Offensive wizard spells /datum/spellbook_entry/fireball - name = "Fireball" - desc = "Fires an explosive fireball at a target. Considered a classic among all wizards." - spell_type = /datum/action/cooldown/spell/pointed/projectile/fireball - category = "Offensive" + name = "Fire Ball" //monkestation edit: added the space + desc = "Fires a fire ball at a target. The Wizard Federation got hit hard by the budget cuts." //monkestation edit: edited to reflect the new version of the given spell + spell_type = /datum/action/cooldown/spell/pointed/projectile/fireball/bouncy //monkestation edit: adds the bouncy subtype + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/spell_cards name = "Spell Cards" desc = "Blazing hot rapid-fire homing cards. Send your foes to the shadow realm with their mystical power!" spell_type = /datum/action/cooldown/spell/pointed/projectile/spell_cards - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/rod_form name = "Rod Form" desc = "Take on the form of an immovable rod, destroying all in your path. Purchasing this spell multiple times will also increase the rod's damage and travel range." spell_type = /datum/action/cooldown/spell/rod_form - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /*/datum/spellbook_entry/disintegrate //monkestation edit: replaced with a pointed version with a different effect name = "Smite" desc = "Charges your hand with an unholy energy that can be used to cause a touched victim to violently explode." spell_type = /datum/action/cooldown/spell/touch/smite - category = "Offensive"*/ + category = SPELLBOOK_CATEGORY_OFFENSIVE*/ /datum/spellbook_entry/summon_simians name = "Summon Simians" @@ -29,45 +30,52 @@ summons primal monkeys and lesser gorillas that will promptly flip out and attack everything in sight. Fun! \ Their lesser, easily manipulable minds will be convinced you are one of their allies, but only for a minute. Unless you also are a monkey." spell_type = /datum/action/cooldown/spell/conjure/simian - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/blind name = "Blind" desc = "Temporarily blinds a single target." spell_type = /datum/action/cooldown/spell/pointed/blind - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE + cost = 1 + +/datum/spellbook_entry/tie_shoes + name = "Tie Shoes" + desc = "This unassuming spell first unties, then knots the target's shoes. While weak at first glance, each upgrade quietens the spell, allowing it to untie laceless footwear and even summon shoes to knot!" + spell_type = /datum/action/cooldown/spell/pointed/untie_shoes + category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 1 /datum/spellbook_entry/mutate name = "Mutate" desc = "Causes you to turn into a hulk and gain laser vision for a short while." spell_type = /datum/action/cooldown/spell/apply_mutations/mutate - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/fleshtostone name = "Flesh to Stone" desc = "Charges your hand with the power to turn victims into inert statues for a long period of time." spell_type = /datum/action/cooldown/spell/touch/flesh_to_stone - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/teslablast name = "Tesla Blast" desc = "Charge up a tesla arc and release it at a random nearby target! You can move freely while it charges. The arc jumps between targets and can knock them down." spell_type = /datum/action/cooldown/spell/charged/beam/tesla - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/lightningbolt name = "Lightning Bolt" desc = "Fire a lightning bolt at your foes! It will jump between targets, but can't knock them down." spell_type = /datum/action/cooldown/spell/pointed/projectile/lightningbolt - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 1 /datum/spellbook_entry/infinite_guns name = "Lesser Summon Guns" desc = "Why reload when you have infinite guns? Summons an unending stream of bolt action rifles that deal little damage, but will knock targets down. Requires both hands free to use. Learning this spell makes you unable to learn Arcane Barrage." spell_type = /datum/action/cooldown/spell/conjure_item/infinite_guns/gun - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 3 no_coexistance_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/arcane_barrage) @@ -75,7 +83,7 @@ name = "Arcane Barrage" desc = "Fire a torrent of arcane energy at your foes with this (powerful) spell. Deals much more damage than Lesser Summon Guns, but won't knock targets down. Requires both hands free to use. Learning this spell makes you unable to learn Lesser Summon Gun." spell_type = /datum/action/cooldown/spell/conjure_item/infinite_guns/arcane_barrage - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 3 no_coexistance_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/gun) @@ -83,7 +91,7 @@ name = "Barnyard Curse" desc = "This spell dooms an unlucky soul to possess the speech and facial attributes of a barnyard animal." spell_type = /datum/action/cooldown/spell/pointed/barnyardcurse - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 1 //monkestation edit /datum/spellbook_entry/splattercasting @@ -91,55 +99,64 @@ desc = "Dramatically lowers the cooldown on all spells, but each one will cost blood, as well as it naturally \ draining from you over time. You can replenish it from your victims, specifically their necks." spell_type = /datum/action/cooldown/spell/splattercasting - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE no_coexistance_typecache = list(/datum/action/cooldown/spell/lichdom) /datum/spellbook_entry/sanguine_strike name = "Exsanguinating Strike" desc = "Sanguine spell that enchants your next weapon strike to deal more damage, heal you for damage dealt, and refill blood." spell_type = /datum/action/cooldown/spell/sanguine_strike - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/scream_for_me name = "Scream For Me" desc = "Sadistic sanguine spell that inflicts numerous severe blood wounds all over the victim's body." spell_type = /datum/action/cooldown/spell/touch/scream_for_me cost = 1 - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/item/staffchaos name = "Staff of Chaos" desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." item_path = /obj/item/gun/magic/staff/chaos - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/item/staffchange name = "Staff of Change" desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." item_path = /obj/item/gun/magic/staff/change - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /*/datum/spellbook_entry/item/mjolnir //monkestation edit: replaced with the spell summon mjollnir name = "Mjolnir" desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power." item_path = /obj/item/mjollnir - category = "Offensive"*/ + category = SPELLBOOK_CATEGORY_OFFENSIVE*/ /datum/spellbook_entry/item/singularity_hammer name = "Singularity Hammer" desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everything nearby to the point of impact." item_path = /obj/item/singularityhammer - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/item/spellblade name = "Spellblade" desc = "A sword capable of firing blasts of energy which rip targets limb from limb." item_path = /obj/item/gun/magic/staff/spellblade - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE /datum/spellbook_entry/item/highfrequencyblade name = "High Frequency Blade" desc = "An incredibly swift enchanted blade resonating at a frequency high enough to be able to slice through anything." item_path = /obj/item/highfrequencyblade/wizard - category = "Offensive" + category = SPELLBOOK_CATEGORY_OFFENSIVE cost = 3 + +/datum/spellbook_entry/item/staffshrink + name = "Staff of Shrinking" + desc = "An artefact that can shrink anything for a reasonable duration. Small structures can be walked over, and small people are very vulnerable (often because their armour no longer fits)." + item_path = /obj/item/gun/magic/staff/shrink + category = SPELLBOOK_CATEGORY_OFFENSIVE + + +#undef SPELLBOOK_CATEGORY_OFFENSIVE diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm index 942a3d656ac3..3e71dbcbe8d1 100644 --- a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm +++ b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm @@ -12,7 +12,7 @@ YOU WILL NOT SURVIVE THIS." icon = 'icons/hud/screen_alert.dmi' icon_state = "wounded" - minimum_time = 90 MINUTES // This will probably immediately end the round if it gets finished. + minimum_time = 80 MINUTES // This will probably immediately end the round if it gets finished. //monkestation edit: from 90 to 80 minutes ritual_invoke_time = 60 SECONDS // Really give the crew some time to interfere with this one. dire_warning = TRUE glow_colour = "#be000048" diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm index 4caf8b87dca6..c4f33543878c 100644 --- a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm +++ b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm @@ -9,7 +9,16 @@ /datum/grand_finale/clown/trigger(mob/living/carbon/human/invoker) for(var/mob/living/carbon/human/victim as anything in GLOB.human_list) victim.Unconscious(3 SECONDS) - if (!victim.mind || IS_HUMAN_INVADER(victim) || victim == invoker) + if (victim == invoker) + if(locate(/datum/action/cooldown/spell/pointed/untie_shoes) in invoker.actions) + continue + var/datum/action/cooldown/spell/pointed/untie_shoes/newer_spell = new(invoker) + newer_spell.Grant(invoker) + for(var/i in 1 to newer_spell.spell_max_level) + newer_spell.level_spell() + newer_spell.invocation_type = INVOCATION_SHOUT + continue + if (!victim.mind || IS_HUMAN_INVADER(victim)) continue if (HAS_TRAIT(victim, TRAIT_CLOWN_ENJOYER)) victim.add_mood_event("clown_world", /datum/mood_event/clown_world) @@ -23,6 +32,8 @@ if (is_clown_job(victim.mind.assigned_role)) var/datum/action/cooldown/spell/conjure_item/clown_pockets/new_spell = new(victim) new_spell.Grant(victim) + var/datum/action/cooldown/spell/pointed/untie_shoes/newer_spell = new(victim) + newer_spell.Grant(victim) continue if (!ismonkey(victim)) // Monkeys cannot yet wear clothes dress_as_magic_clown(victim) diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm index bfb0e01201b7..b1e5fae978f6 100644 --- a/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm +++ b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm @@ -71,7 +71,7 @@ name = "\improper Wizard Prank Victim" roundend_category = "wizard prank victims" show_in_antagpanel = FALSE - antagpanel_category = "Other" + antagpanel_category = ANTAG_GROUP_CREW show_name_in_check_antagonists = TRUE count_against_dynamic_roll_chance = FALSE silent = TRUE diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm index 305171cca73b..4daa3d77f45f 100644 --- a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm +++ b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm @@ -18,7 +18,7 @@ pixel_x = -28 pixel_y = -33 anchored = TRUE - interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND + interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_ATTACK_PAW resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF layer = SIGIL_LAYER /// How many prior grand rituals have been completed? diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index 87fe2f3e4325..f86c46f83b6c 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -34,7 +34,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key) /datum/antagonist/wizard_minion name = "Wizard Minion" - antagpanel_category = "Wizard Federation" + antagpanel_category = ANTAG_GROUP_WIZARDS antag_hud_name = "apprentice" show_in_roundend = FALSE show_name_in_check_antagonists = TRUE @@ -113,7 +113,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key) /// Initialises the grand ritual action for this mob /datum/antagonist/wizard/proc/assign_ritual() - ritual = new(owner.current) + ritual = new(owner) //monkestation edit: adds directly to owner instead of owner.current RegisterSignal(ritual, COMSIG_GRAND_RITUAL_FINAL_COMPLETE, PROC_REF(on_ritual_complete)) /datum/antagonist/wizard/proc/send_to_lair() @@ -255,6 +255,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key) name = "Wizard Apprentice" antag_hud_name = "apprentice" can_assign_self_objectives = FALSE + move_to_lair = FALSE var/datum/mind/master var/school = APPRENTICE_DESTRUCTION outfit_type = /datum/outfit/wizard/apprentice @@ -279,7 +280,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key) if(APPRENTICE_DESTRUCTION) spells_to_grant = list( /datum/action/cooldown/spell/aoe/magic_missile, - /datum/action/cooldown/spell/pointed/projectile/fireball, + /datum/action/cooldown/spell/pointed/projectile/fireball/bouncy, //monkestation edit: adds the bouncy subtype ) to_chat(owner, span_bold("Your service has not gone unrewarded, however. \ Studying under [master.current.real_name], you have learned powerful, \ diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index c4e8f9133175..d69460e6a1ce 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -114,9 +114,10 @@ * * * * state: SHOES_UNTIED, SHOES_TIED, or SHOES_KNOTTED, depending on what you want them to become * * user: used to check to see if we're the ones unknotting our own laces + * * force_lacing: boolean. if TRUE, ignores can_be_tied */ -/obj/item/clothing/shoes/proc/adjust_laces(state, mob/user) - if(!can_be_tied) +/obj/item/clothing/shoes/proc/adjust_laces(state, mob/user, force_lacing = FALSE) + if(!can_be_tied && !force_lacing) return var/mob/living/carbon/human/our_guy diff --git a/code/modules/clothing/shoes/sneakers.dm b/code/modules/clothing/shoes/sneakers.dm index f07ea00c5089..954925ffad5b 100644 --- a/code/modules/clothing/shoes/sneakers.dm +++ b/code/modules/clothing/shoes/sneakers.dm @@ -12,6 +12,11 @@ greyscale_config_inhand_right = /datum/greyscale_config/sneakers_inhand_right flags_1 = IS_PLAYER_COLORABLE_1 +/obj/item/clothing/shoes/sneakers/random/Initialize(mapload) + . = ..() + greyscale_colors = "#" + random_color() + "#" + random_color() + update_greyscale() + /obj/item/clothing/shoes/sneakers/black name = "black shoes" desc = "A pair of black shoes." diff --git a/code/modules/events/ghost_role/space_dragon.dm b/code/modules/events/ghost_role/space_dragon.dm index cfcd2df52dcf..8d09fee4f4f8 100644 --- a/code/modules/events/ghost_role/space_dragon.dm +++ b/code/modules/events/ghost_role/space_dragon.dm @@ -33,8 +33,6 @@ var/mob/living/basic/space_dragon/dragon = new (spawn_location) dragon.key = key - dragon.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon)) - dragon.mind.special_role = ROLE_SPACE_DRAGON dragon.mind.add_antag_datum(/datum/antagonist/space_dragon) playsound(dragon, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1) message_admins("[ADMIN_LOOKUPFLW(dragon)] has been made into a Space Dragon by an event.") diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm index cc63c15cda5d..68e74ac30143 100644 --- a/code/modules/events/wizard/curseditems.dm +++ b/code/modules/events/wizard/curseditems.dm @@ -1,3 +1,16 @@ +/// Turns them into a psuedo-wizard costume. +#define WIZARD_MIMICRY "wizardmimic" +/// Gives them a cursed sword. +#define CURSED_SWORDS "swords" +/// Gives them a blunt that they need to smoke +#define BIG_FAT_DOOBIE "bigfatdoobie" +/// Gives them boxing gloves and a luchador mask +#define BOXING "boxing" +/// Gives them a chameleon mask +#define VOICE_MODULATORS "voicemodulators" +/// Gives them kitty ears and also modifies their gender to FEMALE +#define CATGIRLS_2015 "catgirls2015" + /datum/round_event_control/wizard/cursed_items //fashion disasters name = "Cursed Items" weight = 3 @@ -11,51 +24,72 @@ //item you want to equip to the hand, and set its slots_flags = null. Only items equiped to hands need do this. /datum/round_event/wizard/cursed_items/start() - var/item_set = pick("wizardmimic", "swords", "bigfatdoobie", "boxing", "voicemodulators", "catgirls2015") + var/item_set = pick( + BIG_FAT_DOOBIE, + BOXING, + CATGIRLS_2015, + CURSED_SWORDS, + VOICE_MODULATORS, + WIZARD_MIMICRY, + ) var/list/loadout[SLOTS_AMT] - var/ruins_spaceworthiness - var/ruins_wizard_loadout + var/ruins_spaceworthiness = FALSE + var/ruins_wizard_loadout = FALSE switch(item_set) - if("wizardmimic") - loadout[ITEM_SLOT_OCLOTHING] = /obj/item/clothing/suit/wizrobe - loadout[ITEM_SLOT_FEET] = /obj/item/clothing/shoes/sandal/magic - loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/wizard - ruins_spaceworthiness = 1 - if("swords") - loadout[ITEM_SLOT_HANDS] = /obj/item/katana/cursed - if("bigfatdoobie") + if(BIG_FAT_DOOBIE) loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/cigarette/rollie/trippy - ruins_spaceworthiness = 1 - if("boxing") + ruins_spaceworthiness = TRUE + if(BOXING) loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/luchador loadout[ITEM_SLOT_GLOVES] = /obj/item/clothing/gloves/boxing - ruins_spaceworthiness = 1 - if("voicemodulators") - loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/chameleon - if("catgirls2015") + ruins_spaceworthiness = TRUE + if(CATGIRLS_2015) loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/costume/kitty - ruins_spaceworthiness = 1 - ruins_wizard_loadout = 1 + ruins_spaceworthiness = TRUE + ruins_wizard_loadout = TRUE + if(CURSED_SWORDS) + loadout[ITEM_SLOT_HANDS] = /obj/item/katana/cursed + if(VOICE_MODULATORS) + loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/chameleon + if(WIZARD_MIMICRY) + loadout[ITEM_SLOT_OCLOTHING] = /obj/item/clothing/suit/wizrobe + loadout[ITEM_SLOT_FEET] = /obj/item/clothing/shoes/sandal/magic + loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/wizard + ruins_spaceworthiness = TRUE - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - if(ruins_spaceworthiness && !is_station_level(H.z) || isspaceturf(H.loc) || isplasmaman(H)) + var/list/mob/living/carbon/human/victims = list() + + for(var/mob/living/carbon/human/target in GLOB.alive_mob_list) + if(isspaceturf(target.loc) || !isnull(target.dna.species.outfit_important_for_life) || (ruins_spaceworthiness && !is_station_level(target.z))) continue //#savetheminers - if(ruins_wizard_loadout && IS_WIZARD(H)) + if(ruins_wizard_loadout && IS_WIZARD(target)) continue - if(item_set == "catgirls2015") //Wizard code means never having to say you're sorry - H.gender = FEMALE - for(var/i in 1 to loadout.len) - if(loadout[i]) - var/obj/item/J = loadout[i] - var/obj/item/I = new J //dumb but required because of byond throwing a fit anytime new gets too close to a list - H.dropItemToGround(H.get_item_by_slot(i), TRUE) - H.equip_to_slot_or_del(I, i) - ADD_TRAIT(I, TRAIT_NODROP, CURSED_ITEM_TRAIT(I)) - I.item_flags |= DROPDEL - I.name = "cursed " + I.name - - for(var/mob/living/carbon/human/victim in GLOB.alive_mob_list) + if(item_set == CATGIRLS_2015) //Wizard code means never having to say you're sorry + target.gender = FEMALE + for(var/iterable in 1 to loadout.len) + if(!loadout[iterable]) + continue + + var/obj/item/item_type = loadout[iterable] + var/obj/item/thing = new item_type //dumb but required because of byond throwing a fit anytime new gets too close to a list + + target.dropItemToGround(target.get_item_by_slot(iterable), TRUE) + target.equip_to_slot_or_del(thing, iterable) + ADD_TRAIT(thing, TRAIT_NODROP, CURSED_ITEM_TRAIT(thing)) + thing.item_flags |= DROPDEL + thing.name = "cursed " + thing.name + + victims += target + + for(var/mob/living/carbon/human/victim as anything in victims) var/datum/effect_system/fluid_spread/smoke/smoke = new smoke.set_up(0, holder = victim, location = victim.loc) smoke.start() + +#undef BIG_FAT_DOOBIE +#undef BOXING +#undef CATGIRLS_2015 +#undef CURSED_SWORDS +#undef VOICE_MODULATORS +#undef WIZARD_MIMICRY diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm index 7ff1e07db07d..627ff85fca0f 100644 --- a/code/modules/food_and_drinks/machinery/processor.dm +++ b/code/modules/food_and_drinks/machinery/processor.dm @@ -153,9 +153,12 @@ log_admin("DEBUG: [movable_input] in processor doesn't have a suitable recipe. How did it get in there? Please report it immediately!!!") continue total_time += recipe.time - var/offset = prob(50) ? -2 : 2 - animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = (total_time / rating_speed)*5) //start shaking - sleep(total_time / rating_speed) + + var/duration = (total_time / rating_speed) + INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, Shake), 1, 0, duration) + addtimer(CALLBACK(src, PROC_REF(complete_processing)), duration) + +/obj/machinery/processor/proc/complete_processing() for(var/atom/movable/content_item in processor_contents) var/datum/food_processor_process/recipe = PROCESSOR_SELECT_RECIPE(content_item) if (!recipe) diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index 21545dc49b65..8749875221aa 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -18,13 +18,17 @@ var/year_offset = 0 ///Timezones this holiday is celebrated in (defaults to three timezones spanning a 50 hour window covering all timezones) var/list/timezones = list(TIMEZONE_LINT, TIMEZONE_UTC, TIMEZONE_ANYWHERE_ON_EARTH) - ///If this is defined, drones without a default hat will spawn with this one during the holiday; check drones_as_items.dm to see this used - var/obj/item/drone_hat + ///If this is defined, drones/assistants without a default hat will spawn with this item in their head clothing slot. + var/obj/item/holiday_hat ///When this holiday is active, does this prevent mail from arriving to cargo? Try not to use this for longer holidays. var/mail_holiday = FALSE var/poster_name = "generic celebration poster" var/poster_desc = "A poster for celebrating some holiday. Unfortunately, its unfinished, so you can't see what the holiday is." var/poster_icon = "holiday_unfinished" + /// Color scheme for this holiday + var/list/holiday_colors + /// The default pattern of the holiday, if the requested pattern is null. + var/holiday_pattern = PATTERN_DEFAULT // This proc gets run before the game starts when the holiday is activated. Do festive shit here. /datum/holiday/proc/celebrate() @@ -75,6 +79,31 @@ return FALSE +/// Procs to return holiday themed colors for recoloring atoms +/datum/holiday/proc/get_holiday_colors(atom/thing_to_color, pattern = holiday_pattern) + if(!holiday_colors) + return + switch(pattern) + if(PATTERN_DEFAULT) + return holiday_colors[(thing_to_color.y % holiday_colors.len) + 1] + if(PATTERN_VERTICAL_STRIPE) + return holiday_colors[(thing_to_color.x % holiday_colors.len) + 1] + +/proc/request_holiday_colors(atom/thing_to_color, pattern) + switch(pattern) + if(PATTERN_RANDOM) + return "#[random_short_color()]" + if(PATTERN_RAINBOW) + var/datum/holiday/pride_week/rainbow_datum = new() + return rainbow_datum.get_holiday_colors(thing_to_color, PATTERN_DEFAULT) + if(!length(GLOB.holidays)) + return + for(var/holiday_key in GLOB.holidays) + var/datum/holiday/holiday_real = GLOB.holidays[holiday_key] + if(!holiday_real.holiday_colors) + continue + return holiday_real.get_holiday_colors(thing_to_color, pattern || holiday_real.holiday_pattern) + // The actual holidays // JANUARY @@ -84,6 +113,7 @@ name = "Fleet Day" begin_month = JANUARY begin_day = 19 + holiday_hat = /obj/item/clothing/head/mothcap /datum/holiday/fleet_day/greet() return "This day commemorates another year of successful survival aboard the Mothic Grand Nomad Fleet. Moths galaxywide are encouraged to eat, drink, and be merry." @@ -106,6 +136,12 @@ timezones = list(TIMEZONE_NZDT, TIMEZONE_CHADT) begin_day = 6 begin_month = FEBRUARY + holiday_colors = list( + COLOR_UNION_JACK_BLUE, + COLOR_WHITE, + COLOR_UNION_JACK_RED, + COLOR_WHITE, + ) /datum/holiday/nz/getStationPrefix() return pick("Aotearoa","Kiwi","Fish 'n' Chips","Kākāpō","Southern Cross") @@ -130,7 +166,7 @@ name = "Birthday of Space Station 13" begin_day = 16 begin_month = FEBRUARY - drone_hat = /obj/item/clothing/head/costume/festive + holiday_hat = /obj/item/clothing/head/costume/festive poster_name = "station birthday poster" poster_desc = "A poster celebrating another year of the station's operation. Why anyone would be happy to be here is byond you." poster_icon = "holiday_cake" // is a lie @@ -195,7 +231,13 @@ name = "St. Patrick's Day" begin_day = 17 begin_month = MARCH - drone_hat = /obj/item/clothing/head/soft/green + holiday_hat = /obj/item/clothing/head/soft/green + holiday_colors = list( + COLOR_IRISH_GREEN, + COLOR_WHITE, + COLOR_IRISH_ORANGE, + ) + holiday_pattern = PATTERN_VERTICAL_STRIPE /datum/holiday/no_this_is_patrick/getStationPrefix() return pick("Blarney","Green","Leprechaun","Booze") @@ -210,6 +252,7 @@ begin_month = APRIL begin_day = 1 end_day = 2 + holiday_hat = /obj/item/clothing/head/chameleon/broken /datum/holiday/april_fools/celebrate() . = ..() @@ -220,13 +263,19 @@ var/mob/dead/new_player/P = i if(P.client) P.client.playtitlemusic() - */ + */ //monkestation removal end + +/datum/holiday/april_fools/get_holiday_colors(atom/thing_to_color) + return "#[random_short_color()]" + +/datum/holiday/april_fools/get_holiday_colors(atom/thing_to_color) + return "#[random_short_color()]" /datum/holiday/spess name = "Cosmonautics Day" begin_day = 12 begin_month = APRIL - drone_hat = /obj/item/clothing/head/syndicatefake + holiday_hat = /obj/item/clothing/head/syndicatefake /datum/holiday/spess/greet() return "On this day over 600 years ago, Comrade Yuri Gagarin first ventured into space!" @@ -235,6 +284,12 @@ name = "Four-Twenty" begin_day = 20 begin_month = APRIL + holiday_hat = /obj/item/clothing/head/rasta + holiday_colors = list( + COLOR_ETHIOPIA_GREEN, + COLOR_ETHIOPIA_YELLOW, + COLOR_ETHIOPIA_RED, + ) /datum/holiday/fourtwenty/getStationPrefix() return pick("Snoop","Blunt","Toke","Dank","Cheech","Chong") @@ -257,7 +312,7 @@ timezones = list(TIMEZONE_TKT, TIMEZONE_TOT, TIMEZONE_NZST, TIMEZONE_NFT, TIMEZONE_LHST, TIMEZONE_AEST, TIMEZONE_ACST, TIMEZONE_ACWST, TIMEZONE_AWST, TIMEZONE_CXT, TIMEZONE_CCT, TIMEZONE_CKT, TIMEZONE_NUT) begin_day = 25 begin_month = APRIL - drone_hat = /obj/item/food/grown/poppy + holiday_hat = /obj/item/food/grown/poppy /datum/holiday/anz/getStationPrefix() return pick("Australian","New Zealand","Poppy", "Southern Cross") @@ -268,7 +323,7 @@ name = "Labor Day" begin_day = 1 begin_month = MAY - drone_hat = /obj/item/clothing/head/utility/hardhat + holiday_hat = /obj/item/clothing/head/utility/hardhat mail_holiday = TRUE //Draconic Day is celebrated on May 3rd, the date on which the Draconic language was merged (#26780) @@ -287,7 +342,7 @@ name = "Firefighter's Day" begin_day = 4 begin_month = MAY - drone_hat = /obj/item/clothing/head/utility/hardhat/red + holiday_hat = /obj/item/clothing/head/utility/hardhat/red /datum/holiday/firefighter/getStationPrefix() return pick("Burning","Blazing","Plasma","Fire") @@ -296,7 +351,6 @@ name = "Bee Day" begin_day = 20 begin_month = MAY - drone_hat = /obj/item/clothing/mask/animal/small/bee /datum/holiday/bee/getStationPrefix() return pick("Bee","Honey","Hive","Africanized","Mead","Buzz") @@ -329,6 +383,22 @@ name = "Summer Solstice" begin_day = 21 begin_month = JUNE + holiday_hat = /obj/item/clothing/head/costume/garland + +/datum/holiday/pride_week + name = PRIDE_WEEK + begin_month = JUNE + // Stonewall was June 28th, this captures its week. + begin_day = 23 + end_day = 29 + holiday_colors = list( + COLOR_PRIDE_PURPLE, + COLOR_PRIDE_BLUE, + COLOR_PRIDE_GREEN, + COLOR_PRIDE_YELLOW, + COLOR_PRIDE_ORANGE, + COLOR_PRIDE_RED, + ) // JULY @@ -336,13 +406,13 @@ name = "Doctor's Day" begin_day = 1 begin_month = JULY - drone_hat = /obj/item/clothing/head/costume/nursehat + holiday_hat = /obj/item/clothing/head/costume/nursehat /datum/holiday/ufo name = "UFO Day" begin_day = 2 begin_month = JULY - drone_hat = /obj/item/clothing/mask/facehugger/dead + holiday_hat = /obj/item/clothing/head/collectable/xenom /datum/holiday/ufo/getStationPrefix() //Is such a thing even possible? return pick("Ayy","Truth","Tsoukalos","Mulder","Scully") //Yes it is! @@ -353,6 +423,15 @@ begin_day = 4 begin_month = JULY mail_holiday = TRUE + holiday_hat = /obj/item/clothing/head/cowboy/brown + holiday_colors = list( + COLOR_OLD_GLORY_BLUE, + COLOR_OLD_GLORY_RED, + COLOR_WHITE, + COLOR_OLD_GLORY_RED, + COLOR_WHITE, + ) + /datum/holiday/usa/getStationPrefix() return pick("Independent","American","Burger","Bald Eagle","Star-Spangled", "Fireworks") @@ -367,11 +446,17 @@ timezones = list(TIMEZONE_CEST) begin_day = 14 begin_month = JULY - drone_hat = /obj/item/clothing/head/beret + holiday_hat = /obj/item/clothing/head/beret mail_holiday = TRUE + holiday_colors = list( + COLOR_FRENCH_BLUE, + COLOR_WHITE, + COLOR_FRENCH_RED + ) + holiday_pattern = PATTERN_VERTICAL_STRIPE /datum/holiday/france/getStationPrefix() - return pick("Francais","Fromage", "Zut", "Merde") + return pick("Francais", "Fromage", "Zut", "Merde", "Sacrebleu") /datum/holiday/france/greet() return "Do you hear the people sing?" @@ -389,7 +474,7 @@ name = "Wizard's Day" begin_month = JULY begin_day = 27 - drone_hat = /obj/item/clothing/head/wizard + holiday_hat = /obj/item/clothing/head/wizard /datum/holiday/wizards_day/getStationPrefix() return pick("Dungeon", "Elf", "Magic", "D20", "Edition") @@ -421,6 +506,7 @@ name = "Tiziran Unification Day" begin_month = SEPTEMBER begin_day = 1 + holiday_hat = /obj/item/clothing/head/costume/lizard /datum/holiday/tiziran_unification/greet() return "On this day over 400 years ago, Lizardkind first united under a single banner, ready to face the stars as one unified people." @@ -444,7 +530,7 @@ name = "Talk-Like-a-Pirate Day" begin_day = 19 begin_month = SEPTEMBER - drone_hat = /obj/item/clothing/head/costume/pirate + holiday_hat = /obj/item/clothing/head/costume/pirate /datum/holiday/pirate/greet() return "Ye be talkin' like a pirate today or else ye'r walkin' tha plank, matey!" @@ -474,13 +560,13 @@ name = "Smiling Day" begin_day = 7 begin_month = OCTOBER - drone_hat = /obj/item/clothing/head/costume/papersack/smiley + holiday_hat = /obj/item/clothing/head/costume/papersack/smiley /datum/holiday/boss name = "Boss' Day" begin_day = 16 begin_month = OCTOBER - drone_hat = /obj/item/clothing/head/hats/tophat + holiday_hat = /obj/item/clothing/head/hats/tophat /datum/holiday/un_day name = "Anniversary of the Foundation of the United Nations" @@ -516,12 +602,25 @@ /datum/holiday/vegan/getStationPrefix() return pick("Tofu", "Tempeh", "Seitan", "Tofurkey") +/datum/holiday/october_revolution + name = "October Revolution" + begin_day = 6 + begin_month = NOVEMBER + end_day = 7 + holiday_colors = list( + COLOR_MEDIUM_DARK_RED, + COLOR_GOLD, + COLOR_MEDIUM_DARK_RED, + ) + +/datum/holiday/october_revolution/getStationPrefix() + return pick("Communist", "Soviet", "Bolshevik", "Socialist", "Red", "Workers'") /datum/holiday/remembrance_day name = "Remembrance Day" begin_month = NOVEMBER begin_day = 11 - drone_hat = /obj/item/food/grown/poppy + holiday_hat = /obj/item/food/grown/poppy /datum/holiday/remembrance_day/getStationPrefix() return pick("Peace", "Armistice", "Poppy") @@ -543,7 +642,7 @@ name = "Flowers Day" begin_day = 19 begin_month = NOVEMBER - drone_hat = /obj/item/food/grown/moonflower + holiday_hat = /obj/item/food/grown/moonflower /datum/holiday/hello name = "Saying-'Hello' Day" @@ -572,7 +671,7 @@ begin_day = 1 begin_month = DECEMBER end_day = 31 - drone_hat = /obj/item/clothing/head/costume/santa + holiday_hat = /obj/item/clothing/head/costume/santa /datum/holiday/festive_season/greet() return "Have a nice festive season!" @@ -586,21 +685,23 @@ name = MONKEYDAY begin_day = 14 begin_month = DECEMBER - drone_hat = /obj/item/clothing/mask/gas/monkeymask /datum/holiday/doomsday name = "Mayan Doomsday Anniversary" begin_day = 21 begin_month = DECEMBER - drone_hat = /obj/item/clothing/mask/animal/small/tribal /datum/holiday/xmas name = CHRISTMAS begin_day = 23 begin_month = DECEMBER end_day = 27 - drone_hat = /obj/item/clothing/head/costume/santa + holiday_hat = /obj/item/clothing/head/costume/santa mail_holiday = TRUE + holiday_colors = list( + COLOR_CHRISTMAS_GREEN, + COLOR_CHRISTMAS_RED, + ) /datum/holiday/xmas/greet() return "Have a merry Christmas!" @@ -616,7 +717,7 @@ begin_month = DECEMBER end_day = 2 end_month = JANUARY - drone_hat = /obj/item/clothing/head/costume/festive + holiday_hat = /obj/item/clothing/head/costume/festive mail_holiday = TRUE /datum/holiday/new_year/getStationPrefix() @@ -706,7 +807,6 @@ /datum/holiday/hebrew/passover/getStationPrefix() return pick("Matzah", "Moses", "Red Sea") - // HOLIDAY ADDONS /datum/holiday/xmas/celebrate() @@ -732,7 +832,7 @@ /datum/holiday/easter name = EASTER - drone_hat = /obj/item/clothing/head/costume/rabbitears + holiday_hat = /obj/item/clothing/head/costume/rabbitears var/const/days_early = 1 //to make editing the holiday easier var/const/days_extra = 1 diff --git a/code/modules/holiday/nth_week.dm b/code/modules/holiday/nth_week.dm index 55cfec74be6e..ef4815de0664 100644 --- a/code/modules/holiday/nth_week.dm +++ b/code/modules/holiday/nth_week.dm @@ -35,7 +35,7 @@ begin_week = 4 begin_month = NOVEMBER begin_weekday = THURSDAY - drone_hat = /obj/item/clothing/head/hats/tophat //This is the closest we can get to a pilgrim's hat + holiday_hat = /obj/item/clothing/head/hats/tophat //This is the closest we can get to a pilgrim's hat /datum/holiday/nth_week/thanksgiving/canada name = "Thanksgiving in Canada" diff --git a/code/modules/industrial_lift/tram/tram_floors.dm b/code/modules/industrial_lift/tram/tram_floors.dm index 83f82e2d131d..3d4cff43ccbd 100644 --- a/code/modules/industrial_lift/tram/tram_floors.dm +++ b/code/modules/industrial_lift/tram/tram_floors.dm @@ -1,8 +1,15 @@ +/turf/open/floor/noslip/tram + name = "high-traction platform" + icon_state = "noslip_tram" + base_icon_state = "noslip_tram" + floor_tile = /obj/item/stack/tile/noslip/tram + /turf/open/floor/noslip/tram_plate name = "linear induction plate" desc = "The linear induction plate that powers the tram." icon_state = "tram_plate" base_icon_state = "tram_plate" + floor_tile = /obj/item/stack/tile/noslip/tram_plate slowdown = 0 flags_1 = NONE @@ -15,9 +22,9 @@ /turf/open/floor/noslip/tram_platform name = "tram platform" - desc = "A sturdy looking tram platform." icon_state = "tram_platform" base_icon_state = "tram_platform" + floor_tile = /obj/item/stack/tile/noslip/tram_platform slowdown = 0 /turf/open/floor/noslip/tram_plate/broken_states() diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index a39f45555cb9..6960ab4b8a94 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -103,7 +103,7 @@ /// List of family heirlooms this job can get with the family heirloom quirk. List of types. var/list/family_heirlooms - /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN) + /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS) var/job_flags = NONE /// Multiplier for general usage of the voice of god. diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index 22763cee110d..8c813fa156a9 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -19,7 +19,7 @@ /datum/job_department/silicon, ) random_spawns_possible = FALSE - job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_BOLD_SELECT_TEXT + job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS var/do_special_check = TRUE config_tag = "AI" diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index f009bd938856..037b86f03fe4 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -31,7 +31,7 @@ Assistant /obj/item/crowbar/large = 1 ) - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS rpg_title = "Lout" config_tag = "ASSISTANT" @@ -43,6 +43,12 @@ Assistant /datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/target) ..() + for(var/holidayname in GLOB.holidays) + var/datum/holiday/holiday_today = GLOB.holidays[holidayname] + var/obj/item/special_hat = holiday_today.holiday_hat + if(prob(HOLIDAY_HAT_CHANCE) && !isnull(special_hat) && isnull(head)) + head = special_hat + give_jumpsuit(target) /datum/outfit/job/assistant/proc/give_jumpsuit(mob/living/carbon/human/target) diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm index b5c38f4fe589..77f52fd8ab60 100644 --- a/code/modules/jobs/job_types/atmospheric_technician.dm +++ b/code/modules/jobs/job_types/atmospheric_technician.dm @@ -35,7 +35,7 @@ /obj/item/grenade/gas_crystal/nitrous_oxide_crystal = 5, ) - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS rpg_title = "Aeromancer" /datum/outfit/job/atmos diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm index ec526e10d915..0d33c968e563 100644 --- a/code/modules/jobs/job_types/bartender.dm +++ b/code/modules/jobs/job_types/bartender.dm @@ -29,7 +29,7 @@ /obj/item/stack/sheet/mineral/uranium = 10, ) - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS rpg_title = "Tavernkeeper" /datum/job/bartender/award_service(client/winner, award) diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm index 9486cc6e4c30..d977b221b474 100644 --- a/code/modules/jobs/job_types/botanist.dm +++ b/code/modules/jobs/job_types/botanist.dm @@ -36,7 +36,7 @@ /obj/item/food/monkeycube/bee = 2 ) - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS rpg_title = "Gardener" /datum/outfit/job/botanist diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index d4e41d63b364..48f0d569f5e1 100755 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -42,7 +42,7 @@ /obj/item/skillchip/sabrage = 5, ) - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS rpg_title = "Star Duke" voice_of_god_power = 1.4 //Command staff has authority diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm index b277200c03f3..27c9723af780 100644 --- a/code/modules/jobs/job_types/cargo_technician.dm +++ b/code/modules/jobs/job_types/cargo_technician.dm @@ -33,7 +33,7 @@ /obj/item/gun/ballistic/automatic/wt550 = 1, ) rpg_title = "Merchantman" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/cargo_tech diff --git a/code/modules/jobs/job_types/chaplain/chaplain.dm b/code/modules/jobs/job_types/chaplain/chaplain.dm index 803aa0019f95..bb5cfcd67538 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain.dm @@ -31,7 +31,7 @@ /obj/item/toy/plush/ratplush = 1 ) rpg_title = "Paladin" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS voice_of_god_power = 2 //Chaplains are very good at speaking with the voice of god diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm index 4393066100a7..e4e545553b32 100644 --- a/code/modules/jobs/job_types/chemist.dm +++ b/code/modules/jobs/job_types/chemist.dm @@ -35,7 +35,7 @@ /obj/item/paper/secretrecipe = 1 ) rpg_title = "Alchemist" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/chemist diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm index 9ef561013e1b..ce4d6c9e4745 100644 --- a/code/modules/jobs/job_types/chief_engineer.dm +++ b/code/modules/jobs/job_types/chief_engineer.dm @@ -43,7 +43,7 @@ /obj/effect/spawner/random/engineering/tool_advanced = 3 ) rpg_title = "Head Crystallomancer" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS voice_of_god_power = 1.4 //Command staff has authority diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index daebcfa9d845..a89172e44e66 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -40,7 +40,7 @@ ) family_heirlooms = list(/obj/item/storage/medkit/ancient/heirloom, /obj/item/scalpel, /obj/item/hemostat, /obj/item/circular_saw, /obj/item/retractor, /obj/item/cautery) rpg_title = "High Cleric" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS voice_of_god_power = 1.4 //Command staff has authority diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm index 0bd6ad84a1b1..b535d0f2b783 100644 --- a/code/modules/jobs/job_types/clown.dm +++ b/code/modules/jobs/job_types/clown.dm @@ -33,7 +33,7 @@ family_heirlooms = list(/obj/item/bikehorn/golden) rpg_title = "Jester" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS job_tone = "honk" @@ -113,4 +113,3 @@ var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN] fan.show_to(H) H.faction |= FACTION_CLOWN - diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm index edbc4f625bdb..a7544a1a5eb9 100644 --- a/code/modules/jobs/job_types/cook.dm +++ b/code/modules/jobs/job_types/cook.dm @@ -46,7 +46,7 @@ ) rpg_title = "Tavern Chef" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/job/cook/award_service(client/winner, award) winner.give_award(award, winner.mob) diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm index dd2ca0563808..41d989ed6b9f 100644 --- a/code/modules/jobs/job_types/curator.dm +++ b/code/modules/jobs/job_types/curator.dm @@ -30,7 +30,7 @@ family_heirlooms = list(/obj/item/pen/fountain, /obj/item/storage/dice) - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS voice_of_god_silence_power = 3 rpg_title = "Veteran Adventurer" diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm index 43885a875e4e..c51dc10a1eea 100644 --- a/code/modules/jobs/job_types/cyborg.dm +++ b/code/modules/jobs/job_types/cyborg.dm @@ -19,7 +19,7 @@ /datum/job_department/silicon, ) random_spawns_possible = FALSE - job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK + job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_CANNOT_OPEN_SLOTS /datum/job/cyborg/after_spawn(mob/living/spawned, client/player_client) diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm index 4b44a3a4ae24..b6984e24b422 100644 --- a/code/modules/jobs/job_types/detective.dm +++ b/code/modules/jobs/job_types/detective.dm @@ -40,7 +40,7 @@ family_heirlooms = list(/obj/item/reagent_containers/cup/glass/bottle/whiskey) rpg_title = "Thiefcatcher" //I guess they caught them all rip thief... - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS job_tone = "objection" diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm index be1af0f60edc..7966128f02c5 100644 --- a/code/modules/jobs/job_types/geneticist.dm +++ b/code/modules/jobs/job_types/geneticist.dm @@ -29,7 +29,7 @@ family_heirlooms = list(/obj/item/clothing/under/shorts/purple) rpg_title = "Genemancer" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/geneticist diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm index cda5e183e811..2ad677fc97c1 100644 --- a/code/modules/jobs/job_types/head_of_personnel.dm +++ b/code/modules/jobs/job_types/head_of_personnel.dm @@ -40,7 +40,7 @@ family_heirlooms = list(/obj/item/reagent_containers/cup/glass/trophy/silver_cup) rpg_title = "Guild Questgiver" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS voice_of_god_power = 1.4 //Command staff has authority diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm index 259c828f47c0..c3372bacfc67 100644 --- a/code/modules/jobs/job_types/head_of_security.dm +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -34,7 +34,7 @@ family_heirlooms = list(/obj/item/book/manual/wiki/security_space_law) rpg_title = "Guard Leader" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS voice_of_god_power = 1.4 //Command staff has authority diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm index e4bc41d2eb06..13a3496c609d 100644 --- a/code/modules/jobs/job_types/janitor.dm +++ b/code/modules/jobs/job_types/janitor.dm @@ -28,7 +28,7 @@ /obj/item/lightreplacer = 10 ) rpg_title = "Groundskeeper" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS job_tone = "slip" diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm index 38d55e85c285..a25a1d86d3ad 100644 --- a/code/modules/jobs/job_types/lawyer.dm +++ b/code/modules/jobs/job_types/lawyer.dm @@ -25,7 +25,7 @@ rpg_title = "Magistrate" family_heirlooms = list(/obj/item/gavelhammer, /obj/item/book/manual/wiki/security_space_law) - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS job_tone = "objection" diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm index e8f064fd5456..049ca7489578 100644 --- a/code/modules/jobs/job_types/medical_doctor.dm +++ b/code/modules/jobs/job_types/medical_doctor.dm @@ -36,7 +36,7 @@ /obj/effect/spawner/random/medical/memeorgans = 1 ) rpg_title = "Cleric" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/doctor diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm index 9be0af70b651..97a84511a2ad 100644 --- a/code/modules/jobs/job_types/mime.dm +++ b/code/modules/jobs/job_types/mime.dm @@ -29,7 +29,7 @@ /obj/item/book/mimery = 1, ) rpg_title = "Fool" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS voice_of_god_power = 0.5 //Why are you speaking voice_of_god_silence_power = 3 diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm index c9dad2aa0a66..76c6e10dabf4 100644 --- a/code/modules/jobs/job_types/paramedic.dm +++ b/code/modules/jobs/job_types/paramedic.dm @@ -35,7 +35,7 @@ /obj/item/reagent_containers/hypospray/medipen/survival/luxury = 5 ) rpg_title = "Corpse Runner" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/paramedic diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm index 2737b405802f..e931a34f5ae6 100644 --- a/code/modules/jobs/job_types/prisoner.dm +++ b/code/modules/jobs/job_types/prisoner.dm @@ -27,7 +27,7 @@ family_heirlooms = list(/obj/item/pen/blue) rpg_title = "Defeated Miniboss" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS /datum/job/prisoner/New() . = ..() diff --git a/code/modules/jobs/job_types/psychologist.dm b/code/modules/jobs/job_types/psychologist.dm index b39babc00450..440adbda9d0c 100644 --- a/code/modules/jobs/job_types/psychologist.dm +++ b/code/modules/jobs/job_types/psychologist.dm @@ -31,7 +31,7 @@ /obj/item/gun/syringe = 1 ) rpg_title = "Snake Oil Salesman" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/psychologist diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm index 49d26f09d62a..7088b76c35e7 100644 --- a/code/modules/jobs/job_types/quartermaster.dm +++ b/code/modules/jobs/job_types/quartermaster.dm @@ -32,7 +32,7 @@ /obj/item/circuitboard/machine/emitter = 3 ) rpg_title = "Steward" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS ignore_human_authority = TRUE /datum/outfit/job/quartermaster diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm index d0d711411061..b8ba0f237606 100644 --- a/code/modules/jobs/job_types/research_director.dm +++ b/code/modules/jobs/job_types/research_director.dm @@ -41,7 +41,7 @@ family_heirlooms = list(/obj/item/toy/plush/slimeplushie) rpg_title = "Archmagister" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS voice_of_god_power = 1.4 //Command staff has authority diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm index 83ea82c129dd..becff790c07a 100644 --- a/code/modules/jobs/job_types/roboticist.dm +++ b/code/modules/jobs/job_types/roboticist.dm @@ -31,7 +31,7 @@ family_heirlooms = list(/obj/item/toy/plush/pkplush) rpg_title = "Necromancer" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/job/roboticist/New() diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm index 5d4cc75b9b8b..06be18a02b6e 100644 --- a/code/modules/jobs/job_types/scientist.dm +++ b/code/modules/jobs/job_types/scientist.dm @@ -32,7 +32,7 @@ /obj/item/disk/design_disk/bepis = 2, ) rpg_title = "Thaumaturgist" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS job_tone = "boom" diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index 5afe5e51d057..527f1d81246c 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -38,7 +38,7 @@ /obj/item/melee/baton/security/boomerang/loaded = 1 ) rpg_title = "Guard" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY)) diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm index 4e4e55496d0d..db7fff237cdd 100644 --- a/code/modules/jobs/job_types/shaft_miner.dm +++ b/code/modules/jobs/job_types/shaft_miner.dm @@ -26,7 +26,7 @@ family_heirlooms = list(/obj/item/pickaxe/mini, /obj/item/shovel) rpg_title = "Adventurer" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/miner diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm index 343752ea2fb3..f636058677ec 100644 --- a/code/modules/jobs/job_types/station_engineer.dm +++ b/code/modules/jobs/job_types/station_engineer.dm @@ -36,7 +36,7 @@ /obj/item/clothing/head/utility/hardhat/red/upgraded = 1 ) rpg_title = "Crystallomancer" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/engineer diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm index b8b68b478b74..5ea786330b18 100644 --- a/code/modules/jobs/job_types/virologist.dm +++ b/code/modules/jobs/job_types/virologist.dm @@ -44,7 +44,7 @@ // End Monkestation Addition ) rpg_title = "Plague Doctor" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/virologist diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm index 2179908c57d7..62e7dc9e2058 100644 --- a/code/modules/jobs/job_types/warden.dm +++ b/code/modules/jobs/job_types/warden.dm @@ -40,8 +40,7 @@ /obj/item/storage/box/lethalshot = 5 ) rpg_title = "Jailor" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN - + job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT /datum/outfit/job/warden name = "Warden" diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm index bea6c8cce77f..8dfd57199e6e 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm @@ -44,6 +44,14 @@ icon = 'icons/mob/simple/meteor_heart.dmi' anchored = TRUE +/obj/structure/meateor_fluff/Initialize(mapload) + . = ..() + AddComponent(/datum/component/bloody_spreader,\ + blood_left = INFINITY,\ + blood_dna = list("meaty DNA" = "MT-"),\ + diseases = null,\ + ) + /obj/structure/meateor_fluff/play_attack_sound(damage_amount, damage_type, damage_flag) switch(damage_type) if(BRUTE) diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm index cf0e71553abe..27c328617c4d 100644 --- a/code/modules/mob/living/basic/drone/_drone.dm +++ b/code/modules/mob/living/basic/drone/_drone.dm @@ -187,7 +187,8 @@ equip_to_slot_or_del(storage, ITEM_SLOT_DEX_STORAGE) for(var/holiday_name in GLOB.holidays) - var/obj/item/potential_hat + var/datum/holiday/holiday_today = GLOB.holidays[holiday_name] + var/obj/item/potential_hat = holiday_today.holiday_hat if(!isnull(potential_hat) && isnull(default_headwear)) //If our drone type doesn't start with a hat, we take the holiday one. default_headwear = potential_hat diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index 24fb0b582642..b889305f811e 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -269,6 +269,10 @@ disk_overlay = mutable_appearance('icons/mob/simple/carp.dmi', "disk_overlay") new_overlays += disk_overlay +/mob/living/basic/carp/advanced + health = 40 + obj_damage = 15 + #undef RARE_CAYENNE_CHANCE ///Wild carp that just vibe ya know diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm index 0d218d73ec14..15cc2b7747b0 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm @@ -65,11 +65,7 @@ return TRUE /datum/action/cooldown/spell/aoe/revenant/get_things_to_cast_on(atom/center) - var/list/things = list() - for(var/turf/nearby_turf in range(aoe_radius, center)) - things += nearby_turf - - return things + return RANGE_TURFS(aoe_radius, center) /datum/action/cooldown/spell/aoe/revenant/before_cast(mob/living/basic/revenant/cast_on) . = ..() diff --git a/code/modules/mob/living/basic/vermin/cockroach.dm b/code/modules/mob/living/basic/vermin/cockroach.dm index c55b94cd0182..51652ae179ae 100644 --- a/code/modules/mob/living/basic/vermin/cockroach.dm +++ b/code/modules/mob/living/basic/vermin/cockroach.dm @@ -2,13 +2,15 @@ name = "cockroach" desc = "This station is just crawling with bugs." icon_state = "cockroach" - icon_dead = "cockroach" //Make this work + icon_dead = "cockroach_no_animation" density = FALSE mob_biotypes = MOB_ORGANIC|MOB_BUG mob_size = MOB_SIZE_TINY + held_w_class = WEIGHT_CLASS_TINY health = 1 maxHealth = 1 speed = 1.25 + can_be_held = TRUE gold_core_spawnable = FRIENDLY_SPAWN pass_flags = PASSTABLE | PASSGRILLE | PASSMOB @@ -38,17 +40,22 @@ var/static/list/roach_drops = list(/obj/effect/decal/cleanable/insectguts) AddElement(/datum/element/death_drops, roach_drops) AddElement(/datum/element/swabable, cockroach_cell_line, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 7) - AddComponent(/datum/component/squashable, squash_chance = 50, squash_damage = 1) + AddComponent( \ + /datum/component/squashable, \ + squash_chance = 50, \ + squash_damage = 1, \ + squash_flags = SQUASHED_SHOULD_BE_GIBBED|SQUASHED_ALWAYS_IF_DEAD|SQUASHED_DONT_SQUASH_IN_CONTENTS, \ + ) ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) - -/mob/living/basic/cockroach/death(gibbed) - if(GLOB.station_was_nuked) //If the nuke is going off, then cockroaches are invincible. Keeps the nuke from killing them, cause cockroaches are immune to nukes. - return - ..() + ADD_TRAIT(src, TRAIT_NUKEIMMUNE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_RADIMMUNE, INNATE_TRAIT) /mob/living/basic/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach. return FALSE +// Roach goop is the gibs to drop +/mob/living/basic/cockroach/spawn_gibs() + return /datum/ai_controller/basic_controller/cockroach blackboard = list( @@ -137,7 +144,13 @@ /mob/living/basic/cockroach/hauberoach/Initialize(mapload) . = ..() AddComponent(/datum/component/caltrop, min_damage = 10, max_damage = 15, flags = (CALTROP_BYPASS_SHOES | CALTROP_SILENT)) - AddComponent(/datum/component/squashable, squash_chance = 100, squash_damage = 1, squash_callback = TYPE_PROC_REF(/mob/living/basic/cockroach/hauberoach, on_squish)) + AddComponent( \ + /datum/component/squashable, \ + squash_chance = 100, \ + squash_damage = 1, \ + squash_flags = SQUASHED_SHOULD_BE_GIBBED|SQUASHED_ALWAYS_IF_DEAD|SQUASHED_DONT_SQUASH_IN_CONTENTS, \ + squash_callback = TYPE_PROC_REF(/mob/living/basic/cockroach/hauberoach, on_squish), \ + ) ///Proc used to override the squashing behavior of the normal cockroach. /mob/living/basic/cockroach/hauberoach/proc/on_squish(mob/living/cockroach, mob/living/living_target) @@ -148,6 +161,7 @@ return TRUE living_target.visible_message(span_notice("[living_target] squashes [cockroach], not even noticing its spike."), span_notice("You squashed [cockroach], not even noticing its spike.")) return FALSE + /datum/ai_controller/basic_controller/cockroach/hauberoach planning_subtrees = list( /datum/ai_planning_subtree/pet_planning, diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 91821aa049a5..727f17c455e8 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -65,9 +65,9 @@ if(stage < 6) INVOKE_ASYNC(src, PROC_REF(RefreshInfectionImage)) var/slowdown = 1 - if(ishuman(owner)) - var/mob/living/carbon/human/baby_momma = owner - slowdown = baby_momma.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) ? 2 : 1 // spaceacillin doubles the time it takes to grow + if(!isnull(owner)) // it gestates out of bodies. + if(HAS_TRAIT(owner, TRAIT_VIRUS_RESISTANCE)) + slowdown *= 2 // spaceacillin doubles the time it takes to grow if(owner.has_status_effect(/datum/status_effect/nest_sustenance)) slowdown *= 0.80 //egg gestates 20% faster if you're trapped in a nest diff --git a/code/modules/mob/living/carbon/human/physiology.dm b/code/modules/mob/living/carbon/human/physiology.dm index 3d52aab08928..6975ece08ca5 100644 --- a/code/modules/mob/living/carbon/human/physiology.dm +++ b/code/modules/mob/living/carbon/human/physiology.dm @@ -32,7 +32,11 @@ var/siemens_coeff = 1 // resistance to shocks - var/stun_mod = 1 // % stun modifier + /// Multiplier applied to all incapacitating stuns (knockdown, stun, paralyze, immobilize) + var/stun_mod = 1 + /// Multiplied aplpied to just knockdowns, stacks with above multiplicatively + var/knockdown_mod = 1 + var/bleed_mod = 1 // % bleeding modifier var/datum/armor/armor // internal armor datum diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index fc2719f8f7b5..3fb5d55cf93a 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -682,10 +682,11 @@ project_action = new(src) project_action.Grant(grant_to) - grant_to.AddComponent(/datum/component/mind_linker, \ + grant_to.AddComponent( \ + /datum/component/mind_linker/active_linking, \ network_name = "Slime Link", \ - linker_action_path = /datum/action/innate/link_minds, \ signals_which_destroy_us = list(COMSIG_SPECIES_LOSS), \ + linker_action_path = /datum/action/innate/link_minds, \ ) //Species datums don't normally implement destroy, but JELLIES SUCK ASS OUT OF A STEEL STRAW diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm index 2d41b333f5d7..90cd1774596c 100644 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm @@ -65,7 +65,6 @@ if(istype(attacking_item, /obj/item/melee/flyswatter)) damage_mods += 10 // Yes, a 10x damage modifier - /datum/species/moth/randomize_features(mob/living/carbon/human/human_mob) human_mob.dna.features["moth_markings"] = pick(GLOB.moth_markings_list) randomize_external_organs(human_mob) diff --git a/code/modules/mob/living/carbon/human/status_procs.dm b/code/modules/mob/living/carbon/human/status_procs.dm index 5eb42e042246..acbb3c528c6f 100644 --- a/code/modules/mob/living/carbon/human/status_procs.dm +++ b/code/modules/mob/living/carbon/human/status_procs.dm @@ -1,10 +1,10 @@ /mob/living/carbon/human/Stun(amount, ignore_canstun = FALSE) - amount = dna.species.spec_stun(src,amount) + amount = dna.species.spec_stun(src, amount) return ..() /mob/living/carbon/human/Knockdown(amount, ignore_canstun = FALSE) - amount = dna.species.spec_stun(src,amount) + amount = dna.species.spec_stun(src, amount) * physiology.knockdown_mod return ..() /mob/living/carbon/human/Paralyze(amount, ignore_canstun = FALSE) @@ -16,7 +16,7 @@ return ..() /mob/living/carbon/human/Unconscious(amount, ignore_canstun = FALSE) - amount = dna.species.spec_stun(src,amount) + amount = dna.species.spec_stun(src, amount) if(HAS_TRAIT(src, TRAIT_HEAVY_SLEEPER)) amount *= (rand(125, 130) * 0.01) return ..() diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm index 2c69dac697ca..48094fcf406b 100644 --- a/code/modules/mod/modules/modules_antag.dm +++ b/code/modules/mod/modules/modules_antag.dm @@ -148,9 +148,9 @@ icon_state = "battlemage_shield" idle_power_cost = DEFAULT_CHARGE_DRAIN * 0 //magic use_power_cost = DEFAULT_CHARGE_DRAIN * 0 //magic too - max_charges = 15 - recharge_start_delay = 0 SECONDS - charge_recovery = 12 //monkestation edit: from 8 to 12 + max_charges = 25 //monkestation edit: from 15 to 25 + recharge_start_delay = 1 MINUTES //monkestation edit: from 0 SECONDS to 1 MINUTES + charge_recovery = 25 //monkestation edit: from 8 to 25 shield_icon_file = 'icons/effects/magic.dmi' shield_icon = "mageshield" recharge_path = /obj/item/wizard_armour_charge diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm index cb059cf9663e..b394b0a2ad9f 100644 --- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm +++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm @@ -14,19 +14,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) program_icon = "address-book" var/change_position_cooldown = 30 - ///Jobs blacklisted from having their slots edited. - var/static/list/blacklisted = list( - JOB_CAPTAIN, - JOB_HEAD_OF_PERSONNEL, - JOB_HEAD_OF_SECURITY, - JOB_RESEARCH_DIRECTOR, - JOB_CHIEF_ENGINEER, - JOB_CHIEF_MEDICAL_OFFICER, - JOB_QUARTERMASTER, - JOB_AI, - JOB_CYBORG, - JOB_ASSISTANT, - ) //The scaling factor of max total positions in relation to the total amount of people on board the station in % var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players @@ -41,14 +28,16 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) /datum/computer_file/program/job_management/proc/can_edit_job(datum/job/job) - if(!job || !(job.job_flags & JOB_CREW_MEMBER) || (job.title in blacklisted)) + if(!istype(job)) + return FALSE + if(!(job.job_flags & JOB_CREW_MEMBER)) + return FALSE + if(job.job_flags & JOB_CANNOT_OPEN_SLOTS) return FALSE return TRUE /datum/computer_file/program/job_management/proc/can_open_job(datum/job/job) - if(!can_edit_job(job)) - return FALSE if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100))) var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] < 0)) @@ -57,8 +46,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) /datum/computer_file/program/job_management/proc/can_close_job(datum/job/job) - if(!can_edit_job(job)) - return FALSE if(job.total_positions > length(GLOB.player_list) * (max_relative_positions / 100)) var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] > 0)) @@ -75,7 +62,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if("PRG_open_job") var/edit_job_target = params["target"] var/datum/job/j = SSjob.GetJob(edit_job_target) - if(!j || !can_open_job(j)) + if(!can_edit_job(j) || !can_open_job(j)) return TRUE if(opened_positions[edit_job_target] >= 0) GLOB.time_last_changed_position = world.time / 10 @@ -87,7 +74,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if("PRG_close_job") var/edit_job_target = params["target"] var/datum/job/j = SSjob.GetJob(edit_job_target) - if(!j || !can_close_job(j)) + if(!can_edit_job(j) || !can_close_job(j)) return TRUE //Allow instant closing without cooldown if a position has been opened before if(opened_positions[edit_job_target] <= 0) @@ -100,7 +87,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if("PRG_priority") var/priority_target = params["target"] var/datum/job/j = SSjob.GetJob(priority_target) - if(!j || !can_edit_job(j)) + if(!can_edit_job(j)) return TRUE if(j.total_positions <= j.current_positions) return TRUE @@ -128,7 +115,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) var/list/pos = list() var/list/priority = list() for(var/datum/job/job as anything in SSjob.joinable_occupations) - if(job.title in blacklisted) + if(!can_edit_job(job)) continue if(job in SSjob.prioritized_jobs) priority += job.title @@ -145,4 +132,3 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) var/delta = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1) data["cooldown"] = delta < 0 ? 0 : delta return data - diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm index c2778d19471b..892b928db9ee 100644 --- a/code/modules/movespeed/modifiers/mobs.dm +++ b/code/modules/movespeed/modifiers/mobs.dm @@ -172,3 +172,6 @@ /datum/movespeed_modifier/basilisk_overheat multiplicative_slowdown = -18 + +/datum/movespeed_modifier/magic_ties + multiplicative_slowdown = 0.5 diff --git a/code/modules/pai/card.dm b/code/modules/pai/card.dm index 1c42beac3412..3e11c0b7bcb8 100644 --- a/code/modules/pai/card.dm +++ b/code/modules/pai/card.dm @@ -68,6 +68,7 @@ . = ..() update_appearance() SSpai.pai_card_list += src + ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT) /obj/item/pai_card/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is staring sadly at [src]! [user.p_they()] can't keep living without real human intimacy!")) diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm index 47f36877f053..684f18e77efa 100644 --- a/code/modules/projectiles/ammunition/energy/special.dm +++ b/code/modules/projectiles/ammunition/energy/special.dm @@ -65,7 +65,7 @@ projectile_type = /obj/projectile/energy/tesla_cannon /obj/item/ammo_casing/energy/shrink - projectile_type = /obj/projectile/beam/shrink + projectile_type = /obj/projectile/magic/shrink/alien select_name = "shrink ray" e_cost = 200 diff --git a/code/modules/projectiles/ammunition/special/magic.dm b/code/modules/projectiles/ammunition/special/magic.dm index 0965aae2fddd..70afbc169256 100644 --- a/code/modules/projectiles/ammunition/special/magic.dm +++ b/code/modules/projectiles/ammunition/special/magic.dm @@ -83,3 +83,9 @@ /obj/item/ammo_casing/magic/nothing projectile_type = /obj/projectile/magic/nothing harmful = FALSE + +/obj/item/ammo_casing/magic/shrink + projectile_type = /obj/projectile/magic/shrink + +/obj/item/ammo_casing/magic/shrink/wand + projectile_type = /obj/projectile/magic/shrink/wand diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index e23a1e9121a4..dc729ab3a1f9 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -148,6 +148,7 @@ /obj/projectile/magic/teleport, /obj/projectile/magic/wipe, /obj/projectile/temp/chill, + /obj/projectile/magic/shrink ) /obj/item/gun/magic/staff/chaos/unrestricted @@ -318,3 +319,17 @@ inhand_icon_state = "pharoah_sceptre" worn_icon_state = "wipestaff" school = SCHOOL_FORBIDDEN //arguably the worst staff in the entire game effect wise + +/obj/item/gun/magic/staff/shrink + name = "staff of shrinking" + desc = "An artefact that spits bolts of tiny magic that makes things small. It's easily mistaken for a wand." + fire_sound = 'sound/magic/staff_shrink.ogg' + ammo_type = /obj/item/ammo_casing/magic/shrink + icon_state = "shrinkstaff" + inhand_icon_state = "staff" + max_charges = 10 // slightly more/faster charges since this will be used on walls and such + recharge_rate = 5 + no_den_usage = TRUE + school = SCHOOL_TRANSMUTATION + slot_flags = NONE //too small to wear on your back + w_class = WEIGHT_CLASS_NORMAL //but small enough for a bag diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index a078c4ae00b0..82b78a4859ed 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -256,3 +256,25 @@ name = "wand of nothing" desc = "It's not just a stick, it's a MAGIC stick?" ammo_type = /obj/item/ammo_casing/magic/nothing + + +///////////////////////////////////// +//WAND OF SHRINKING +///////////////////////////////////// + +/obj/item/gun/magic/wand/shrink + name = "wand of shrinking" + desc = "Feel the tiny eldritch terror of an itty... bitty... head!" + ammo_type = /obj/item/ammo_casing/magic/shrink/wand + icon_state = "shrinkwand" + base_icon_state = "shrinkwand" + fire_sound = 'sound/magic/staff_shrink.ogg' + max_charges = 10 //10, 5, 5, 4 + no_den_usage = TRUE + w_class = WEIGHT_CLASS_TINY + +/obj/item/gun/magic/wand/shrink/zap_self(mob/living/user) + to_chat(user, span_notice("The world grows large...")) + charges-- + user.AddComponent(/datum/component/shrink, -1) // small forever + return ..() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 28166514b241..35ca181378e7 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -519,8 +519,10 @@ return process_hit(T, select_target(T, target, bumped), bumped, hit_something) // try to hit something else // at this point we are going to hit the thing // in which case send signal to it - if (SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) & PROJECTILE_INTERRUPT_HIT) - qdel(src) + var/signal_bitfield = SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) //monkestation edit + if (signal_bitfield & PROJECTILE_INTERRUPT_HIT) + if(!(signal_bitfield & PROJECTILE_INTERRUPT_BLOCK_QDEL)) //monkestation edit + qdel(src) return BULLET_ACT_BLOCK if(mode == PROJECTILE_PIERCE_HIT) ++pierces @@ -919,7 +921,7 @@ process_homing() var/forcemoved = FALSE for(var/i in 1 to SSprojectiles.global_iterations_per_move) - if(QDELETED(src)) + if(QDELETED(src) || !trajectory) //monkestation edit: adds the trajectory check return trajectory.increment(trajectory_multiplier) var/turf/T = trajectory.return_turf() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 8d5d22da3cdc..96c99fca0298 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -226,23 +226,6 @@ /obj/projectile/beam/lasertag/bluetag/hitscan hitscan = TRUE -//a shrink ray that shrinks stuff, which grows back after a short while. -/obj/projectile/beam/shrink - name = "shrink ray" - icon_state = "blue_laser" - hitsound = 'sound/weapons/shrink_hit.ogg' - damage = 0 - damage_type = STAMINA - armor_flag = ENERGY - impact_effect_type = /obj/effect/temp_visual/impact_effect/shrink - light_color = LIGHT_COLOR_BLUE - var/shrink_time = 90 - -/obj/projectile/beam/shrink/on_hit(atom/target, blocked = 0, pierce_hit) - . = ..() - if(isopenturf(target) || isindestructiblewall(target))//shrunk floors wouldnt do anything except look weird, i-walls shouldn't be bypassable - return - target.AddComponent(/datum/component/shrink, shrink_time) - -/obj/projectile/beam/shrink/is_hostile_projectile() - return TRUE +/obj/projectile/magic/shrink/alien + antimagic_flags = NONE + shrink_time = 9 SECONDS diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 3b0104ac55c7..ea38aedb0021 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -611,3 +611,31 @@ damage_type = BURN damage = 2 antimagic_charge_cost = 0 // since the cards gets spammed like a shotgun + +//a shrink ray that shrinks stuff, which grows back after a short while. +/obj/projectile/magic/shrink + name = "shrink ray" + icon_state = "blue_laser" + hitsound = 'sound/weapons/shrink_hit.ogg' + damage = 0 + damage_type = STAMINA + armor_flag = ENERGY + impact_effect_type = /obj/effect/temp_visual/impact_effect/shrink + light_color = LIGHT_COLOR_BLUE + var/shrink_time = -1 + +/obj/projectile/magic/shrink/on_hit(atom/target, blocked = 0, pierce_hit) + . = ..() + if(isopenturf(target) || isindestructiblewall(target))//shrunk floors wouldnt do anything except look weird, i-walls shouldn't be bypassable + return + target.AddComponent(/datum/component/shrink, shrink_time) + +/obj/projectile/magic/shrink/is_hostile_projectile() + return TRUE + +/obj/projectile/magic/shrink/wand + shrink_time = 90 SECONDS + +/obj/projectile/magic/shrink/wand/on_hit(atom/target, blocked = 0, pierce_hit) + shrink_time = rand(60 SECONDS, 90 SECONDS) + return ..() diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index 79d9a191e845..d91620353710 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -2015,17 +2015,18 @@ taste_description = "the pain of ten thousand slain mosquitos" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/consumable/ethanol/bug_spray/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) - //Bugs should not drink Bug spray. - if(ismoth(drinker) || isflyperson(drinker)) - drinker.adjustToxLoss(1 * REM * seconds_per_tick, FALSE, required_biotype = affected_biotype) - return ..() - -/datum/reagent/consumable/ethanol/bug_spray/on_mob_metabolize(mob/living/carbon/drinker) +/datum/reagent/consumable/ethanol/bug_spray/on_new(data) + . = ..() + AddElement(/datum/element/bugkiller_reagent) - if(ismoth(drinker) || isflyperson(drinker)) +/datum/reagent/consumable/ethanol/bug_spray/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) + // Does some damage to bug biotypes + var/did_damage = drinker.adjustToxLoss(1 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = MOB_BUG) + // Random chance of causing a screm if we did some damage + if(did_damage && SPT_PROB(2, seconds_per_tick)) drinker.emote("scream") - return ..() + + return ..() || did_damage /datum/reagent/consumable/ethanol/applejack name = "Applejack" diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index f7dd0808abba..d1bfbf5a97df 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -241,6 +241,14 @@ ph = 8.1 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/medicine/spaceacillin/on_mob_metabolize(mob/living/L) + . = ..() + ADD_TRAIT(L, TRAIT_VIRUS_RESISTANCE, type) + +/datum/reagent/medicine/spaceacillin/on_mob_end_metabolize(mob/living/L) + . = ..() + REMOVE_TRAIT(L, TRAIT_VIRUS_RESISTANCE, type) + //Goon Chems. Ported mainly from Goonstation. Easily mixable (or not so easily) and provide a variety of effects. /datum/reagent/medicine/oxandrolone diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index d7d309fe5e9d..54e6461a701f 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -389,11 +389,13 @@ ph = 3.2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/toxin/pestkiller/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume) +/datum/reagent/toxin/pestkiller/on_new(data) . = ..() - if(exposed_mob.mob_biotypes & MOB_BUG) - var/damage = min(round(0.4*reac_volume, 0.1),10) - exposed_mob.adjustToxLoss(damage, required_biotype = affected_biotype) + AddElement(/datum/element/bugkiller_reagent) + +/datum/reagent/toxin/pestkiller/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = affected_mob.adjustToxLoss(2 * toxpwr * REM * seconds_per_tick, updating_health = FALSE, required_biotype = MOB_BUG) + return ..() || . /datum/reagent/toxin/pestkiller/organic name = "Natural Pest Killer" diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 43838df140bd..7b05e2f6b56f 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -141,7 +141,9 @@ /datum/action/cooldown/spell/PreActivate(atom/target) if(SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_STARTED, src) & COMPONENT_BLOCK_ABILITY_START) return FALSE - if(!is_valid_target(target)) + if(target == owner) + target = get_caster_from_target(target) + if(isnull(target) || !is_valid_target(target)) return FALSE return Activate(target) @@ -213,10 +215,6 @@ to_chat(owner, span_warning("[src] can't be cast in this state!")) return FALSE - // Being put into a card form breaks a lot of spells, so we'll just forbid them in these states - if(ispAI(owner) || (isAI(owner) && istype(owner.loc, /obj/item/aicard))) - return FALSE - return TRUE /** @@ -229,6 +227,28 @@ /datum/action/cooldown/spell/proc/is_valid_target(atom/cast_on) return TRUE +/** + * Used to get the cast_on atom if a self cast spell is being cast. + * + * Allows for some atoms to be used as casting sources if a spell caster is located within. + */ +/datum/action/cooldown/spell/proc/get_caster_from_target(atom/target) + var/atom/cast_loc = target.loc + if(isnull(cast_loc)) + return null // No magic in nullspace + + if(isturf(cast_loc)) + return target // They're just standing around, proceed as normal + + if(HAS_TRAIT(cast_loc, TRAIT_CASTABLE_LOC)) + /*if(HAS_TRAIT(cast_loc, TRAIT_SPELLS_TRANSFER_TO_LOC) && ismob(cast_loc.loc)) + return cast_loc.loc + else*/ //monkestation temp removal + return cast_loc + // They're in an atom which allows casting, so redirect the caster to loc + + return null + // The actual cast chain occurs here, in Activate(). // You should generally not be overriding or extending Activate() for spells. // Defer to any of the cast chain procs instead. diff --git a/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm b/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm index 4d16ae379714..92442bd0e40a 100644 --- a/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm +++ b/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm @@ -12,6 +12,9 @@ /// The radius of the aoe. var/aoe_radius = 7 +/datum/action/cooldown/spell/aoe/is_valid_target(atom/cast_on) + return isturf(cast_on.loc) + // At this point, cast_on == owner. Either works. // Don't extend this for your spell! Look at cast_on_thing_in_aoe. /datum/action/cooldown/spell/aoe/cast(atom/cast_on) diff --git a/code/modules/spells/spell_types/aoe_spell/area_conversion.dm b/code/modules/spells/spell_types/aoe_spell/area_conversion.dm index f75c39586852..a03b4c9ab21a 100644 --- a/code/modules/spells/spell_types/aoe_spell/area_conversion.dm +++ b/code/modules/spells/spell_types/aoe_spell/area_conversion.dm @@ -16,11 +16,7 @@ aoe_radius = 2 /datum/action/cooldown/spell/aoe/area_conversion/get_things_to_cast_on(atom/center) - var/list/things = list() - for(var/turf/nearby_turf in range(aoe_radius, center)) - things += nearby_turf - - return things + return RANGE_TURFS(aoe_radius, center) /datum/action/cooldown/spell/aoe/area_conversion/cast_on_thing_in_aoe(turf/victim, atom/caster) playsound(victim, 'sound/items/welder.ogg', 75, TRUE) diff --git a/code/modules/spells/spell_types/aoe_spell/knock.dm b/code/modules/spells/spell_types/aoe_spell/knock.dm index fd9e4503de8f..ede1462633b0 100644 --- a/code/modules/spells/spell_types/aoe_spell/knock.dm +++ b/code/modules/spells/spell_types/aoe_spell/knock.dm @@ -13,6 +13,23 @@ spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC aoe_radius = 3 +/datum/action/cooldown/spell/aoe/knock/get_caster_from_target(atom/target) + if(istype(target.loc, /obj/structure/closet)) + return target + + return ..() + +/datum/action/cooldown/spell/aoe/knock/is_valid_target(atom/cast_on) + return ..() || istype(cast_on.loc, /obj/structure/closet) + +/datum/action/cooldown/spell/aoe/knock/cast(atom/cast_on) + if(istype(cast_on.loc, /obj/structure/closet)) + var/obj/structure/closet/open_closet = cast_on.loc + open_closet.locked = FALSE + open_closet.open() + + return ..() + /datum/action/cooldown/spell/aoe/knock/get_things_to_cast_on(atom/center) return RANGE_TURFS(aoe_radius, center) diff --git a/code/modules/spells/spell_types/aoe_spell/repulse.dm b/code/modules/spells/spell_types/aoe_spell/repulse.dm index d0074a9f3866..259f20ee9ba0 100644 --- a/code/modules/spells/spell_types/aoe_spell/repulse.dm +++ b/code/modules/spells/spell_types/aoe_spell/repulse.dm @@ -6,6 +6,23 @@ /// The moveforce of the throw done by the repulsion. var/repulse_force = MOVE_FORCE_EXTREMELY_STRONG +/datum/action/cooldown/spell/aoe/repulse/get_caster_from_target(atom/target) + if(istype(target.loc, /obj/structure/closet)) + return target + + return ..() + +/datum/action/cooldown/spell/aoe/repulse/is_valid_target(atom/cast_on) + return ..() || istype(cast_on.loc, /obj/structure/closet) + +/datum/action/cooldown/spell/aoe/repulse/cast(atom/cast_on) + if(istype(cast_on.loc, /obj/structure/closet)) + var/obj/structure/closet/open_closet = cast_on.loc + open_closet.open(force = TRUE) + open_closet.visible_message(span_warning("[open_closet] suddenly flies open!")) + + return ..() + /datum/action/cooldown/spell/aoe/repulse/get_things_to_cast_on(atom/center) var/list/things = list() for(var/atom/movable/nearby_movable in view(aoe_radius, center)) @@ -44,7 +61,13 @@ to_chat(victim, span_userdanger("You're thrown back by [caster]!")) // So stuff gets tossed around at the same time. - victim.safe_throw_at(throwtarget, ((clamp((max_throw - (clamp(dist_from_caster - 2, 0, dist_from_caster))), 3, max_throw))), 1, caster, force = repulse_force) + victim.safe_throw_at( + target = throwtarget, + range = clamp((max_throw - (clamp(dist_from_caster - 2, 0, dist_from_caster))), 3, max_throw), + speed = 1, + thrower = ismob(caster) ? caster : null, + force = repulse_force, + ) /datum/action/cooldown/spell/aoe/repulse/wizard name = "Repulse" diff --git a/code/modules/spells/spell_types/conjure/_conjure.dm b/code/modules/spells/spell_types/conjure/_conjure.dm index ffab08871118..3afe7c525575 100644 --- a/code/modules/spells/spell_types/conjure/_conjure.dm +++ b/code/modules/spells/spell_types/conjure/_conjure.dm @@ -16,6 +16,9 @@ /// If TRUE, no two summons can be spawned in the same turf. var/summon_respects_prev_spawn_points = TRUE +/datum/action/cooldown/spell/conjure/is_valid_target(atom/cast_on) + return isturf(cast_on.loc) + /datum/action/cooldown/spell/conjure/cast(atom/cast_on) . = ..() var/list/to_summon_in = list() diff --git a/code/modules/spells/spell_types/conjure_item/_conjure_item.dm b/code/modules/spells/spell_types/conjure_item/_conjure_item.dm index 45b4ff1a11bb..181fd118871e 100644 --- a/code/modules/spells/spell_types/conjure_item/_conjure_item.dm +++ b/code/modules/spells/spell_types/conjure_item/_conjure_item.dm @@ -8,6 +8,10 @@ var/delete_old = TRUE /// List of weakrefs to items summoned var/list/datum/weakref/item_refs + /// If TRUE, deletes the item if no mob picks it up on cast + var/delete_on_failure = TRUE + /// If TRUE, requires the caster be able to pick it up afterwards + var/requires_hands = FALSE /datum/action/cooldown/spell/conjure_item/Destroy() // If we delete_old, clean up all of our items on delete @@ -20,29 +24,56 @@ return ..() +/datum/action/cooldown/spell/conjure_item/can_cast_spell(feedback) + . = ..() + if(!.) + return FALSE + + if(!requires_hands) + return TRUE + + if(!isliving(owner)) + return FALSE + + var/mob/living/living_owner = owner + if(living_owner.usable_hands < 1) + if(feedback) + owner.balloon_alert(owner, "no free hands!") + return FALSE + + return TRUE + /datum/action/cooldown/spell/conjure_item/is_valid_target(atom/cast_on) - return iscarbon(cast_on) + if(!requires_hands) + return TRUE + if(!isliving(cast_on)) + return FALSE + var/mob/living/living_cast_on = cast_on + return living_cast_on.usable_hands >= 1 -/datum/action/cooldown/spell/conjure_item/cast(mob/living/carbon/cast_on) +/datum/action/cooldown/spell/conjure_item/cast(atom/cast_on) if(delete_old && LAZYLEN(item_refs)) QDEL_LAZYLIST(item_refs) - var/obj/item/existing_item = cast_on.get_active_held_item() - if(existing_item) - cast_on.dropItemToGround(existing_item) + var/mob/mob_caster = cast_on + if(istype(mob_caster)) + var/obj/item/existing_item = mob_caster.get_active_held_item() + if(existing_item) + mob_caster.dropItemToGround(existing_item) - var/obj/item/created = make_item() + var/obj/item/created = make_item(cast_on) if(QDELETED(created)) CRASH("[type] tried to create an item, but failed. It's item type is [item_type].") - cast_on.put_in_hands(created, del_on_fail = TRUE) + if(istype(mob_caster)) + mob_caster.put_in_hands(created, del_on_fail = delete_on_failure) post_created(cast_on, created) //monkestation edit: im just gonna call this here return ..() /// Instantiates the item we're conjuring and returns it. -/// Item is made in nullspace and moved out in cast(). -/datum/action/cooldown/spell/conjure_item/proc/make_item() - var/obj/item/made_item = new item_type() +/// Item is made in at the caster's. +/datum/action/cooldown/spell/conjure_item/proc/make_item(atom/caster) + var/obj/item/made_item = new item_type(caster.loc) LAZYADD(item_refs, WEAKREF(made_item)) return made_item diff --git a/code/modules/spells/spell_types/conjure_item/infinite_guns.dm b/code/modules/spells/spell_types/conjure_item/infinite_guns.dm index 98921da4879d..35d83a9987f6 100644 --- a/code/modules/spells/spell_types/conjure_item/infinite_guns.dm +++ b/code/modules/spells/spell_types/conjure_item/infinite_guns.dm @@ -8,6 +8,7 @@ item_type = /obj/item/gun/ballistic/rifle // Enchanted guns self delete / do wacky stuff, anyways delete_old = FALSE + requires_hands = TRUE /datum/action/cooldown/spell/conjure_item/infinite_guns/Remove(mob/living/remove_from) var/obj/item/existing = remove_from.is_holding_item_of_type(item_type) @@ -18,8 +19,8 @@ // Because enchanted guns self-delete and regenerate themselves, // override make_item here and let's not bother with tracking their weakrefs. -/datum/action/cooldown/spell/conjure_item/infinite_guns/make_item() - return new item_type() +/datum/action/cooldown/spell/conjure_item/infinite_guns/make_item(atom/caster) + return new item_type(caster.loc) /datum/action/cooldown/spell/conjure_item/infinite_guns/gun name = "Lesser Summon Guns" diff --git a/code/modules/spells/spell_types/conjure_item/invisible_box.dm b/code/modules/spells/spell_types/conjure_item/invisible_box.dm index af6d5586af25..42da02121d3c 100644 --- a/code/modules/spells/spell_types/conjure_item/invisible_box.dm +++ b/code/modules/spells/spell_types/conjure_item/invisible_box.dm @@ -30,7 +30,7 @@ . = ..() invocation = span_notice("[cast_on] moves [cast_on.p_their()] hands in the shape of a cube, pressing a box out of the air.") -/datum/action/cooldown/spell/conjure_item/invisible_box/make_item() +/datum/action/cooldown/spell/conjure_item/invisible_box/make_item(atom/caster) . = ..() var/obj/item/made_box = . made_box.alpha = 255 diff --git a/code/modules/spells/spell_types/conjure_item/lighting_packet.dm b/code/modules/spells/spell_types/conjure_item/lighting_packet.dm index 2badfdd46dff..2df0c85f470e 100644 --- a/code/modules/spells/spell_types/conjure_item/lighting_packet.dm +++ b/code/modules/spells/spell_types/conjure_item/lighting_packet.dm @@ -9,6 +9,7 @@ spell_max_level = 1 item_type = /obj/item/spellpacket/lightningbolt + requires_hands = TRUE /datum/action/cooldown/spell/conjure_item/spellpacket/cast(mob/living/carbon/cast_on) . = ..() diff --git a/code/modules/spells/spell_types/conjure_item/snowball.dm b/code/modules/spells/spell_types/conjure_item/snowball.dm index ffd6d8d5e549..80b4aad9361c 100644 --- a/code/modules/spells/spell_types/conjure_item/snowball.dm +++ b/code/modules/spells/spell_types/conjure_item/snowball.dm @@ -8,3 +8,4 @@ antimagic_flags = NONE cooldown_time = 1.5 SECONDS item_type = /obj/item/toy/snowball + requires_hands = TRUE diff --git a/code/modules/spells/spell_types/jaunt/_jaunt.dm b/code/modules/spells/spell_types/jaunt/_jaunt.dm index e0463c02a10f..0378a5efee45 100644 --- a/code/modules/spells/spell_types/jaunt/_jaunt.dm +++ b/code/modules/spells/spell_types/jaunt/_jaunt.dm @@ -19,6 +19,12 @@ /// What dummy mob type do we put jaunters in on jaunt? var/jaunt_type = /obj/effect/dummy/phased_mob +/datum/action/cooldown/spell/jaunt/get_caster_from_target(atom/target) + if(istype(target.loc, jaunt_type)) + return target + + return ..() + /datum/action/cooldown/spell/jaunt/before_cast(atom/cast_on) return ..() | SPELL_NO_FEEDBACK // Don't do the feedback until after we're jaunting diff --git a/code/modules/spells/spell_types/jaunt/shadow_walk.dm b/code/modules/spells/spell_types/jaunt/shadow_walk.dm index 29bb80633673..de03f8e15e02 100644 --- a/code/modules/spells/spell_types/jaunt/shadow_walk.dm +++ b/code/modules/spells/spell_types/jaunt/shadow_walk.dm @@ -9,6 +9,9 @@ spell_requirements = NONE jaunt_type = /obj/effect/dummy/phased_mob/shadow + /// The max amount of lumens on a turf allowed before we can no longer enter jaunt with this + var/light_threshold = SHADOW_SPECIES_LIGHT_THRESHOLD + /datum/action/cooldown/spell/jaunt/shadow_walk/Grant(mob/grant_to) . = ..() RegisterSignal(grant_to, COMSIG_MOVABLE_MOVED, PROC_REF(update_status_on_signal)) @@ -17,6 +20,12 @@ . = ..() UnregisterSignal(remove_from, COMSIG_MOVABLE_MOVED) +/datum/action/cooldown/spell/jaunt/shadow_walk/enter_jaunt(mob/living/jaunter, turf/loc_override) + var/obj/effect/dummy/phased_mob/shadow/shadow = ..() + if(istype(shadow)) + shadow.light_max = light_threshold + return shadow + /datum/action/cooldown/spell/jaunt/shadow_walk/can_cast_spell(feedback = TRUE) . = ..() if(!.) @@ -24,7 +33,7 @@ if(is_jaunting(owner)) return TRUE var/turf/cast_turf = get_turf(owner) - if(cast_turf.get_lumcount() >= SHADOW_SPECIES_LIGHT_THRESHOLD) + if(cast_turf.get_lumcount() >= light_threshold) if(feedback) to_chat(owner, span_warning("It isn't dark enough here!")) return FALSE @@ -44,6 +53,8 @@ /obj/effect/dummy/phased_mob/shadow name = "shadows" + /// Max amount of light permitted before being kicked out + var/light_max = SHADOW_SPECIES_LIGHT_THRESHOLD /// The amount that shadow heals us per SSobj tick (times seconds_per_tick) var/healing_rate = 1.5 /// When cooldown is active, you are prevented from moving into tiles that would eject you from your jaunt @@ -109,11 +120,9 @@ * * location_to_check - The location to have its light level checked. */ -/obj/effect/dummy/phased_mob/shadow/proc/check_light_level(location_to_check) - var/turf/T = get_turf(location_to_check) - var/light_amount = T.get_lumcount() - if(light_amount > 0.2) // jaunt ends - return TRUE +/obj/effect/dummy/phased_mob/shadow/proc/check_light_level(atom/location_to_check) + var/turf/light_turf = get_turf(location_to_check) + return light_turf.get_lumcount() > light_max // jaunt ends on TRUE /** * Checks if the user should recieve a warning that they're moving into light. diff --git a/code/modules/spells/spell_types/pointed/_pointed.dm b/code/modules/spells/spell_types/pointed/_pointed.dm index 5f4077908fa8..c220f5573b35 100644 --- a/code/modules/spells/spell_types/pointed/_pointed.dm +++ b/code/modules/spells/spell_types/pointed/_pointed.dm @@ -133,10 +133,11 @@ // cast_on is a turf, or atom target, that we clicked on to fire at. /datum/action/cooldown/spell/pointed/projectile/cast(atom/cast_on) . = ..() - if(!isturf(owner.loc)) + var/atom/caster = get_caster_from_target(owner) + if(!isturf(caster.loc)) return FALSE - var/turf/caster_turf = get_turf(owner) + var/turf/caster_turf = caster.loc // Get the tile infront of the caster, based on their direction var/turf/caster_front_turf = get_step(owner, owner.dir) diff --git a/code/modules/spells/spell_types/pointed/spell_cards.dm b/code/modules/spells/spell_types/pointed/spell_cards.dm index 47700d2c09a8..d2f12092ff59 100644 --- a/code/modules/spells/spell_types/pointed/spell_cards.dm +++ b/code/modules/spells/spell_types/pointed/spell_cards.dm @@ -7,7 +7,8 @@ school = SCHOOL_EVOCATION cooldown_time = 5 SECONDS - cooldown_reduction_per_rank = 1 SECONDS + cooldown_reduction_per_rank = 2 SECONDS + spell_max_level = 3 invocation = "Sigi'lu M'Fan 'Tasia!" invocation_type = INVOCATION_SHOUT @@ -21,7 +22,7 @@ /// A weakref to the mob we're currently targeting with the lockon component. var/datum/weakref/current_target_weakref /// The turn rate of the spell cards in flight. (They track onto locked on targets) - var/projectile_turnrate = 10 + var/projectile_turnrate = 15 //monkestation edit: from 10 to 15 /// The homing spread of the spell cards in flight. var/projectile_pixel_homing_spread = 32 /// The initial spread of the spell cards when fired. @@ -47,6 +48,7 @@ target_typecache = GLOB.typecache_living, \ lock_amount = 1, \ on_lock = CALLBACK(src, PROC_REF(on_lockon_component)), \ + catcher_default_click = FALSE, /*monkestation edit*/ \ ) /datum/action/cooldown/spell/pointed/projectile/spell_cards/proc/on_lockon_component(list/locked_weakrefs) diff --git a/code/modules/spells/spell_types/pointed/swap.dm b/code/modules/spells/spell_types/pointed/swap.dm index 0af5738a945e..8c1b152127f9 100644 --- a/code/modules/spells/spell_types/pointed/swap.dm +++ b/code/modules/spells/spell_types/pointed/swap.dm @@ -8,11 +8,11 @@ active_overlay_icon_state = "bg_spell_border_active_blue" school = SCHOOL_TRANSLOCATION - cooldown_time = 30 SECONDS - cooldown_reduction_per_rank = 6 SECONDS + cooldown_time = 25 SECONDS + cooldown_reduction_per_rank = 10 SECONDS + spell_max_level = 3 cast_range = 9 - invocation = "FRO' BRT'TRO, DA!" - invocation_type = INVOCATION_SHOUT + invocation_type = INVOCATION_NONE spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC|SPELL_REQUIRES_STATION active_msg = "You prepare to swap locations with a target..." diff --git a/code/modules/spells/spell_types/pointed/tie_shoes.dm b/code/modules/spells/spell_types/pointed/tie_shoes.dm new file mode 100644 index 000000000000..5783717d0e72 --- /dev/null +++ b/code/modules/spells/spell_types/pointed/tie_shoes.dm @@ -0,0 +1,137 @@ + +/datum/action/cooldown/spell/pointed/untie_shoes + name = "Untie Shoes" + desc = "This unassuming spell unties and then knots the target's shoes." + ranged_mousepointer = 'icons/effects/mouse_pointers/lace.dmi' + button_icon_state = "lace" + + school = SCHOOL_CONJURATION + cooldown_time = 3 SECONDS + cooldown_reduction_per_rank = 0.2 SECONDS + + spell_max_level = 4 + invocation = "Acetato!" + invocation_type = INVOCATION_SHOUT + spell_requirements = NONE + antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY + + cast_range = INFINITY + active_msg = "You prepare to tie your target's shoes!" + + /// Ignores inability to tie laces, such as jackboots, magboots, or sandals. + var/bypass_tie_status = FALSE + /// Summons shoes to untie if the target has none. + var/summons_shoes = FALSE + +/datum/action/cooldown/spell/pointed/untie_shoes/New(Target) + . = ..() + // tgs first spell with multiple invocations!!!!!! + invocation = pick("Acetato!", "Agaletto!") + +/datum/action/cooldown/spell/pointed/untie_shoes/level_spell(bypass_cap) + . = ..() + if(spell_level == 2) + bypass_tie_status = TRUE + to_chat(owner, span_notice("You will now summon laces on laceless shoes, such as jackboots.")) + + if(spell_level == 3) + summons_shoes = TRUE + to_chat(owner, span_notice("You will now summon shoes if your target has none.")) + + if(spell_level == 4) + invocation_type = INVOCATION_NONE + to_chat(owner, span_boldnotice("Your invocations are now silent!")) + +/datum/action/cooldown/spell/pointed/untie_shoes/is_valid_target(atom/cast_on) + return isliving(cast_on) + +// We need to override this, as trying to change next_use_time in cast() will just result in it being overridden. +/datum/action/cooldown/spell/touch/before_cast(atom/cast_on) + return ..() | SPELL_NO_IMMEDIATE_COOLDOWN + +/datum/action/cooldown/spell/pointed/untie_shoes/cast(mob/living/carbon/cast_on) + . = ..() + if(cast_on.can_block_magic(antimagic_flags)) + to_chat(owner, span_warning("The spell had no effect!")) + return FALSE + + if(isanimal_or_basicmob(cast_on)) + cast_on.add_movespeed_modifier(/datum/movespeed_modifier/magic_ties) + addtimer(CALLBACK(cast_on, TYPE_PROC_REF(/mob/living, remove_movespeed_modifier), /datum/movespeed_modifier/magic_ties), 3 SECONDS * spell_level, TIMER_UNIQUE|TIMER_OVERRIDE) + to_chat(owner, span_warning("You tie [cast_on] with weak, magic laces!")) + if(invocation_type != INVOCATION_NONE) // extra feedback since it's weird for them + cast_on.balloon_alert_to_viewers("magically tied!") + else + cast_on.balloon_alert(owner, "magically tied!") + playsound(cast_on, 'sound/magic/summonitems_generic.ogg', 50, TRUE) + return TRUE + + var/shoe_to_cast = /obj/item/clothing/shoes/sneakers/random + + if(HAS_TRAIT(owner, TRAIT_CHUUNIBYOU)) + shoe_to_cast = /obj/item/clothing/shoes/sneakers/marisa + if(HAS_TRAIT(owner, TRAIT_SPLATTERCASTER)) + shoe_to_cast = /obj/item/clothing/shoes/laceup + + var/obj/item/clothing/shoes/shoes_to_tie = cast_on.shoes + + if(isnull(shoes_to_tie)) + if(!summons_shoes) + to_chat(owner, span_warning("[cast_on] isn't wearing any shoes!")) + return FALSE + + shoes_to_tie = new shoe_to_cast(cast_on) + if(!cast_on.equip_to_slot_or_del(shoes_to_tie, ITEM_SLOT_FEET)) + to_chat(owner, span_warning("Couldn't equip shoes on [cast_on]!")) + return FALSE + + if(invocation_type != INVOCATION_NONE) + playsound(cast_on, 'sound/magic/summonitems_generic.ogg', 50, TRUE) + + switch(shoes_to_tie.tied) + if(SHOES_TIED) + if(!shoes_to_tie.can_be_tied) + if(bypass_tie_status) + to_chat(owner, span_warning("You magically grant laces to [cast_on]'s shoes!")) + cast_on.balloon_alert(owner, "laced!") + shoes_to_tie.can_be_tied = TRUE + if(invocation_type != INVOCATION_NONE) + playsound(cast_on, 'sound/magic/summonitems_generic.ogg', 50, TRUE) + return TRUE + else + to_chat(owner, span_warning("[cast_on] is wearing laceless shoes!")) + cast_on.balloon_alert(owner, "laceless!") + return FALSE + + to_chat(owner, span_warning("You untie [cast_on]'s shoes!")) + cast_on.balloon_alert(owner, "untied!") + shoes_to_tie.adjust_laces(SHOES_UNTIED, force_lacing = TRUE) + if(SHOES_UNTIED) + to_chat(owner, span_warning("You knot [cast_on]'s laces!")) + cast_on.balloon_alert(owner, "knotted!") + shoes_to_tie.adjust_laces(SHOES_KNOTTED, force_lacing = TRUE) + if(SHOES_KNOTTED) + to_chat(owner, span_warning("[cast_on]'s laces are already knotted!")) + return FALSE + +// We need to override this, as trying to change next_use_time in cast() will just result in it being overridden. +/datum/action/cooldown/spell/pointed/untie_shoes/after_cast(atom/cast_on) + . = ..() + var/extra_time = 0 SECONDS + if((cast_on.z != owner.z) || get_dist(cast_on, owner) > 7) + extra_time += cooldown_time * 10 // :) + + StartCooldown(cooldown_time + extra_time) + +/datum/action/cooldown/spell/pointed/untie_shoes/get_spell_title() + switch(spell_level) + if(2) + return "Laceless " + if(3) + return "Prankster's " + if(4) + return "Sneakerly " + if(5) + return "Clown's Own " + + return "" diff --git a/code/modules/spells/spell_types/self/basic_heal.dm b/code/modules/spells/spell_types/self/basic_heal.dm index a4acba2d8845..135b80942062 100644 --- a/code/modules/spells/spell_types/self/basic_heal.dm +++ b/code/modules/spells/spell_types/self/basic_heal.dm @@ -17,6 +17,9 @@ /// Amount of burn to heal to the spell caster on cast var/burn_to_heal = 10 +/datum/action/cooldown/spell/basic_heal/is_valid_target(atom/cast_on) + return isliving(cast_on) + /datum/action/cooldown/spell/basic_heal/cast(mob/living/cast_on) . = ..() cast_on.visible_message( diff --git a/code/modules/spells/spell_types/self/mutate.dm b/code/modules/spells/spell_types/self/mutate.dm index 9f4bf3db6c8e..59f8f6ddc37f 100644 --- a/code/modules/spells/spell_types/self/mutate.dm +++ b/code/modules/spells/spell_types/self/mutate.dm @@ -38,9 +38,10 @@ /datum/action/cooldown/spell/apply_mutations/mutate name = "Mutate" - desc = "This spell causes you to turn into a gigantic hulk and gain laser vision for a short while. Unlike the lesser nonmagical version, it works on non-humans and mantains hand dexterity as well!" - cooldown_time = 60 SECONDS //monkestation edit: from 40 to 60 seconds - cooldown_reduction_per_rank = 5 SECONDS //monkestation edit: from 2.5 to 5 seconds + desc = "This spell causes you to turn into a hulk and gain laser vision for a short while." + cooldown_time = 40 SECONDS + cooldown_reduction_per_rank = 5 SECONDS + spell_max_level = 3 invocation = "BIRUZ BENNAR" invocation_type = INVOCATION_SHOUT @@ -56,7 +57,7 @@ cast_on.add_atom_colour("#00FF00", TEMPORARY_COLOUR_PRIORITY) /datum/action/cooldown/spell/apply_mutations/mutate/remove_mutations(mob/living/carbon/human/cast_on) - if(QDELETED(cast_on) || !is_valid_target(cast_on)) + if(QDELETED(cast_on) || !is_valid_target(cast_on)) // Not 100% sure if this check is still needed, leaving it just in case return - + ..() cast_on.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY) diff --git a/code/modules/spells/spell_types/self/soultap.dm b/code/modules/spells/spell_types/self/soultap.dm index 0c114575c4be..cb611de0a21a 100644 --- a/code/modules/spells/spell_types/self/soultap.dm +++ b/code/modules/spells/spell_types/self/soultap.dm @@ -45,6 +45,8 @@ cast_on.health = min(cast_on.health, cast_on.maxHealth) for(var/datum/action/cooldown/spell/spell in cast_on.actions) + if(istype(spell, /datum/action/cooldown/spell/pointed/mind_transfer)) //monkestation edit + continue //monkestation edit spell.reset_spell_cooldown() // If the tap took all of our life, we die and lose our soul! diff --git a/code/modules/spells/spell_types/shapeshift/shapechange.dm b/code/modules/spells/spell_types/shapeshift/shapechange.dm index d7ff71e0425d..dd2597d00970 100644 --- a/code/modules/spells/spell_types/shapeshift/shapechange.dm +++ b/code/modules/spells/spell_types/shapeshift/shapechange.dm @@ -4,7 +4,8 @@ Once you've made your choice, it cannot be changed." cooldown_time = 20 SECONDS - cooldown_reduction_per_rank = 3.75 SECONDS + cooldown_reduction_per_rank = 8 SECONDS + spell_max_level = 3 invocation = "RAC'WA NO!" invocation_type = INVOCATION_SHOUT diff --git a/code/modules/spells/spell_types/teleport/teleport.dm b/code/modules/spells/spell_types/teleport/teleport.dm index 1dfcf6f2608e..d48615720428 100644 --- a/code/modules/spells/spell_types/teleport/teleport.dm +++ b/code/modules/spells/spell_types/teleport/teleport.dm @@ -7,7 +7,8 @@ school = SCHOOL_TRANSLOCATION cooldown_time = 1 MINUTES - cooldown_reduction_per_rank = 10 SECONDS + cooldown_reduction_per_rank = 20 SECONDS + spell_max_level = 3 invocation = "SCYAR NILA" invocation_type = INVOCATION_SHOUT diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm old mode 100644 new mode 100755 diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index fa2bc18ae8c8..0d373b931c25 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -210,6 +210,7 @@ #include "species_unique_id.dm" #include "species_whitelists.dm" #include "spell_invocations.dm" +#include "spell_jaunt.dm" #include "spell_mindswap.dm" #include "spell_names.dm" #include "spell_shapeshift.dm" diff --git a/code/modules/unit_tests/spell_jaunt.dm b/code/modules/unit_tests/spell_jaunt.dm new file mode 100644 index 000000000000..41446b71a591 --- /dev/null +++ b/code/modules/unit_tests/spell_jaunt.dm @@ -0,0 +1,21 @@ +/// Tests Shadow Walk can be entered and exited +/datum/unit_test/shadow_jaunt + +/datum/unit_test/shadow_jaunt/Run() + var/mob/living/carbon/human/jaunter = allocate(/mob/living/carbon/human/consistent) + var/datum/action/cooldown/spell/jaunt/shadow_walk/walk = allocate(/datum/action/cooldown/spell/jaunt/shadow_walk, jaunter) + walk.Grant(jaunter) + + var/turf/jaunt_turf = jaunter.loc + TEST_ASSERT(istype(jaunt_turf), "Jaunter was not allocated to a turf, instead to [jaunt_turf || "nullspace"].") + TEST_ASSERT(walk.IsAvailable(), "Unit test room is not suitable to test [walk].") + + walk.Trigger() + + TEST_ASSERT_NOTEQUAL(jaunter.loc, jaunt_turf, "Jaunter's loc did not change on casting [walk].") + TEST_ASSERT(istype(jaunter.loc, walk.jaunt_type), "Jaunter failed to enter jaunt on casting [walk].") + + walk.next_use_time = -1 + walk.Trigger() + + TEST_ASSERT_EQUAL(jaunter.loc, jaunt_turf, "Jaunter failed to exit jaunt on exiting [walk].") diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm index 03554ac081d4..ca666292a3f5 100644 --- a/code/modules/vehicles/_vehicle.dm +++ b/code/modules/vehicles/_vehicle.dm @@ -51,6 +51,7 @@ autogrant_actions_controller = list() occupant_actions = list() generate_actions() + ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT) /obj/vehicle/Destroy(force) QDEL_NULL(trailer) diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index 376bef8fbfce..464bf9f3740c 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -35,7 +35,7 @@ return // spaceacillin has a 75% chance to block infection - if(istype(target) && target.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) && prob(75)) + if(HAS_TRAIT(target, TRAIT_VIRUS_RESISTANCE) && prob(75)) return var/obj/item/bodypart/actual_limb = target.get_bodypart(def_zone) diff --git a/icons/effects/mouse_pointers/lace.dmi b/icons/effects/mouse_pointers/lace.dmi new file mode 100644 index 000000000000..68aad755c627 Binary files /dev/null and b/icons/effects/mouse_pointers/lace.dmi differ diff --git a/icons/mob/actions/actions_space_dragon.dmi b/icons/mob/actions/actions_space_dragon.dmi index a4e33eef1eb2..48d73fd6f47a 100644 Binary files a/icons/mob/actions/actions_space_dragon.dmi and b/icons/mob/actions/actions_space_dragon.dmi differ diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi index 39ea58104173..d66c2e22011a 100644 Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ diff --git a/icons/mob/simple/animal.dmi b/icons/mob/simple/animal.dmi index 602648205a26..51fc02ff8b0e 100644 Binary files a/icons/mob/simple/animal.dmi and b/icons/mob/simple/animal.dmi differ diff --git a/icons/mob/species/misc/bodypart_overlay_simple.dmi b/icons/mob/species/misc/bodypart_overlay_simple.dmi index 2bc1dda5663a..2c1739fd1687 100644 Binary files a/icons/mob/species/misc/bodypart_overlay_simple.dmi and b/icons/mob/species/misc/bodypart_overlay_simple.dmi differ diff --git a/icons/obj/medical/organs/infuser_organs.dmi b/icons/obj/medical/organs/infuser_organs.dmi index 49ac2751aae2..c2551b41f666 100644 Binary files a/icons/obj/medical/organs/infuser_organs.dmi and b/icons/obj/medical/organs/infuser_organs.dmi differ diff --git a/icons/obj/tiles.dmi b/icons/obj/tiles.dmi index 4ba733b29dcc..fdddb793362a 100644 Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ diff --git a/icons/obj/weapons/guns/magic.dmi b/icons/obj/weapons/guns/magic.dmi index 92210a4e0542..0434b5b6d852 100644 Binary files a/icons/obj/weapons/guns/magic.dmi and b/icons/obj/weapons/guns/magic.dmi differ diff --git a/icons/turf/decals.dmi b/icons/turf/decals.dmi index 278db3ad3cd1..c4a9d8858c5a 100644 Binary files a/icons/turf/decals.dmi and b/icons/turf/decals.dmi differ diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi index 580a53fcb3ad..153fe582544f 100644 Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ diff --git a/monkestation/code/datums/components/charge_adjuster.dm b/monkestation/code/datums/components/charge_adjuster.dm new file mode 100644 index 000000000000..2fa3c1d3b097 --- /dev/null +++ b/monkestation/code/datums/components/charge_adjuster.dm @@ -0,0 +1,37 @@ +/datum/component/charge_adjuster + ///The typepath of atom to give charges to + var/type_to_charge_to + ///How many charges to give + var/charges_given = 1 + ///TYPE_PROC_REF() to call on the hit item if its type_to_charge_to, proc MUST take amount to get adjusted by as first arg + var/called_proc_name + +/datum/component/charge_adjuster/Initialize(type_to_charge_to, charges_given = 1, called_proc_name) + if(!isitem(parent) || !type_to_charge_to || !called_proc_name) + return COMPONENT_INCOMPATIBLE + + src.type_to_charge_to = type_to_charge_to + src.charges_given = charges_given + src.called_proc_name = called_proc_name + +/datum/component/charge_adjuster/Destroy(force, silent) + called_proc_name = null + return ..() + +/datum/component/charge_adjuster/RegisterWithParent() + RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(check_hit_atom)) + +/datum/component/charge_adjuster/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK) + +/datum/component/charge_adjuster/proc/check_hit_atom(obj/item/source, atom/target, mob/user, proximity_flag) + SIGNAL_HANDLER + if(!proximity_flag || !istype(target, type_to_charge_to)) + return + + if(!call(target, called_proc_name)(charges_given)) + return + + to_chat(user, span_notice("You insert \the [source] in \the [target].")) + qdel(parent) + return COMPONENT_CANCEL_ATTACK_CHAIN diff --git a/monkestation/code/datums/components/lock_on_cursor.dm b/monkestation/code/datums/components/lock_on_cursor.dm new file mode 100644 index 000000000000..99d6fa447604 --- /dev/null +++ b/monkestation/code/datums/components/lock_on_cursor.dm @@ -0,0 +1,12 @@ +/datum/component/lock_on_cursor/Initialize(lock_cursor_range, + lock_amount, + list/target_typecache, + list/immune, + icon, + icon_state, + datum/callback/on_click_callback, + datum/callback/on_lock, + datum/callback/can_target_callback, + catcher_default_click) + . = ..() + mouse_tracker.default_click = catcher_default_click diff --git a/monkestation/code/modules/antagonists/brother/gear/misc.dm b/monkestation/code/modules/antagonists/brother/gear/misc.dm new file mode 100644 index 000000000000..a937231b73d6 --- /dev/null +++ b/monkestation/code/modules/antagonists/brother/gear/misc.dm @@ -0,0 +1,5 @@ +/datum/bb_gear/money_tree_seeds + name = "Money Tree Seeds" + desc = "Contains a pack of seeds of the rare money tree." + spawn_path = /obj/item/seeds/tree/money + preview_path = /obj/item/seeds/tree/money diff --git a/monkestation/code/modules/antagonists/brother/gear/recipes.dm b/monkestation/code/modules/antagonists/brother/gear/recipes.dm index a9693a5ef6e2..4d5f035eff0c 100644 --- a/monkestation/code/modules/antagonists/brother/gear/recipes.dm +++ b/monkestation/code/modules/antagonists/brother/gear/recipes.dm @@ -41,6 +41,6 @@ /datum/bb_gear/granter/elance name = "Recipe: Explosive Lance (Grenade)" desc = "Contains a recipe book, allowing you to learn the knowledge to build an explosive lance (grenade)." - spawn_path = /obj/item/spear/explosive - preview_path = /obj/item/book/granter/crafting_recipe/maint_gun/explosive_lance + spawn_path = /obj/item/book/granter/crafting_recipe/maint_gun/explosive_lance + preview_path = /obj/item/spear/explosive diff --git a/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm b/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm index 436ce5f3c9da..7b716141997c 100644 --- a/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm +++ b/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm @@ -21,7 +21,7 @@ var/list/target_belongings = list() /datum/traitor_objective/target_player/kidnapping/common - progression_reward = list(2 MINUTES, 4 MINUTES) + progression_reward = list(10 MINUTES, 15 MINUTES) telecrystal_reward = list(2, 3) target_jobs = list( // Cargo @@ -57,7 +57,7 @@ telecrystal_reward = 3 //go bully the assistants /datum/traitor_objective/target_player/kidnapping/uncommon //Hard to fish out targets - progression_reward = list(4 MINUTES, 8 MINUTES) + progression_reward = list(15 MINUTES, 20 MINUTES) telecrystal_reward = list(3, 4) given_contractor_rep = 2 @@ -75,7 +75,7 @@ alive_bonus = 4 /datum/traitor_objective/target_player/kidnapping/rare - progression_reward = list(8 MINUTES, 12 MINUTES) + progression_reward = list(20 MINUTES, 25 MINUTES) telecrystal_reward = list(4, 5) given_contractor_rep = 3 @@ -93,7 +93,7 @@ alive_bonus = 5 /datum/traitor_objective/target_player/kidnapping/captain - progression_reward = list(12 MINUTES, 16 MINUTES) + progression_reward = list(25 MINUTES, 30 MINUTES) telecrystal_reward = list(5, 6) given_contractor_rep = 4 diff --git a/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm b/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm index 6d984316c65d..47f0a3cafbec 100644 --- a/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm @@ -27,6 +27,33 @@ reagents.add_reagent_list(list_reagents) return +//wizard bio suit +/obj/item/clothing/head/wizard/bio_suit + name = "gem encrusted bio hood" + desc = "A hood that protects the head and face from biological contaminants. It's covered in small gemstones." + icon = 'monkestation/icons/obj/clothing/head/bio.dmi' + icon_state = "bio_wizard" + worn_icon = 'monkestation/icons/mob/clothing/head/bio.dmi' + worn_icon_state = "bio_wizard" + inhand_icon_state = "bio_hood" + clothing_flags = THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT | SNUG_FIT | PLASMAMAN_HELMET_EXEMPT | HEADINTERNALS | CASTING_CLOTHES + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEHAIR|HIDEFACIALHAIR|HIDEFACE|HIDESNOUT + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF + +/obj/item/clothing/suit/wizrobe/bio_suit + name = "gem encrusted bio suit" + desc = "A suit that protects against biological contamination. It's covered in small gemstones." + icon = 'monkestation/icons/obj/clothing/suits/bio.dmi' + icon_state = "bio_wizard" + worn_icon = 'monkestation/icons/mob/clothing/suits/bio.dmi' + worn_icon_state = "bio_wizard" + inhand_icon_state = "bio_suit" + clothing_flags = THICKMATERIAL | CASTING_CLOTHES + body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT + strip_delay = 7 SECONDS + equip_delay_other = 7 SECONDS + //reactive talisman #define REACTION_COOLDOWN_DURATION 10 SECONDS /obj/item/clothing/neck/neckless/wizard_reactive //reactive armor for wizards that casts a spell when it reacts @@ -125,11 +152,39 @@ desc = "An artifact that when inserted into a spellbook increases its power by 100." value = 100 -/obj/item/spellbook_charge/afterattack(obj/item/spellbook/book, mob/user) +/obj/item/spellbook_charge/Initialize(mapload) + . = ..() + AddComponent(/datum/component/charge_adjuster, type_to_charge_to = /obj/item/spellbook, charges_given = value, called_proc_name = TYPE_PROC_REF(/obj/item/spellbook, adjust_charge)) + +//wizard shield charges +#define ADDED_MAX_CHARGE 50 +#define MAX_CHARGES_ABSORBED 3 +/obj/item/wizard_armour_charge/Initialize(mapload) . = ..() - if(!istype(book)) - to_chat(user, "The charge can only increase the power of spellbooks!") + AddComponent(/datum/component/charge_adjuster, type_to_charge_to = /obj/item/spellbook, charges_given = 1, called_proc_name = TYPE_PROC_REF(/obj/item/spellbook, adjust_charge)) + +/obj/item/wizard_armour_charge/pre_attack(atom/A, mob/living/user, params) + . = ..() + if(.) return - book.uses += value - to_chat(user, "You increase the power of the spellbook by [value] points.") - qdel(src) + + var/obj/item/mod/module/energy_shield/wizard/shield = istype(A, /obj/item/mod/module/energy_shield/wizard) || locate(/obj/item/mod/module/energy_shield/wizard) in A.contents + if(shield) + if(isnum(shield)) + shield = A + if(shield.max_charges >= (initial(shield.max_charges) + (ADDED_MAX_CHARGE * MAX_CHARGES_ABSORBED))) + balloon_alert(user, "\The [shield] cannot take more charges, you can put this back into your spellbook to refund it.") + return TRUE + + shield.max_charges += ADDED_MAX_CHARGE + var/datum/component/shielded/shield_comp = shield.mod?.GetComponent(/datum/component/shielded) + if(shield_comp) + shield_comp.max_charges += ADDED_MAX_CHARGE + shield_comp.current_charges += (ADDED_MAX_CHARGE - initial(shield_comp.charge_recovery)) + qdel(src) //should still be able to finish the attack chain + +#undef ADDED_MAX_CHARGE +#undef MAX_CHARGES_ABSORBED + +/obj/item/mod/module/energy_shield/wizard + lose_multiple_charges = TRUE //I dont think we have anything else that uses this var, so all the numbers for this are subject to change diff --git a/monkestation/code/modules/antagonists/wizard/equipment/mirror_shield.dm b/monkestation/code/modules/antagonists/wizard/equipment/mirror_shield.dm new file mode 100644 index 000000000000..59cee2c7e692 --- /dev/null +++ b/monkestation/code/modules/antagonists/wizard/equipment/mirror_shield.dm @@ -0,0 +1,124 @@ +#define PROJECTILE_HIT_EFFECT_CHANCE 80 +#define NORMAL_BLOCK_CHANCE 30 +#define REACTION_MODE_ABSORB 0 +#define REACTION_MODE_REFLECT 1 + +//a "shield" that can absorb projectiles and then shoot them back at attackers +/obj/item/gun/magic/mirror_shield + name = "mirror shield" + desc = "A strange mirror adorned with various gemstones. If you look close enough it almost seems as if the surface is... rippling?" + icon = 'monkestation/icons/obj/weapons/shields.dmi' + icon_state = "wizard_mirror_shield" + inhand_icon_state = "wizard_mirror_shield" + lefthand_file = 'monkestation/icons/mob/inhands/equipment/shields_lefthand.dmi' + righthand_file = 'monkestation/icons/mob/inhands/equipment/shields_righthand.dmi' + worn_icon = 'monkestation/icons/mob/clothing/back.dmi' + worn_icon_state = "wizard_mirror_shield" + force = 16 + slot_flags = ITEM_SLOT_BACK + w_class = WEIGHT_CLASS_BULKY + attack_verb_continuous = list("bumps", "prods") + attack_verb_simple = list("bump", "prod") + hitsound = 'sound/weapons/smash.ogg' + fire_sound = 'sound/magic/cosmic_expansion.ogg' + ammo_type = /obj/item/ammo_casing/mirror_shield_dummy + can_charge = TRUE + ///Up to how many projectiles can we "have stored" + var/max_stored_projectiles = 10 + ///Do we absorb or reflect projectiles when hit + var/reaction_mode = REACTION_MODE_ABSORB + ///The list of projectiles we have stored ready to fire + var/list/stored_projectiles = list() + ///Cannot absorb projectile types in here + var/static/list/blacklisted_projectile_types = list() + +/obj/item/gun/magic/mirror_shield/Initialize(mapload) + . = ..() + STOP_PROCESSING(SSobj, src) //we want can_charge set to TRUE but dont actually use the processing it gives so just disable it + +/obj/item/gun/magic/mirror_shield/Destroy() + for(var/projectile in stored_projectiles) + qdel(projectile) //could also have them shoot off in random directions + stored_projectiles -= projectile + return ..() + +/obj/item/gun/magic/mirror_shield/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + if(ismob(old_loc)) + UnregisterSignal(old_loc, COMSIG_PROJECTILE_PREHIT) + + if(ismob(loc)) + RegisterSignal(loc, COMSIG_PROJECTILE_PREHIT, PROC_REF(handle_hit)) + +/obj/item/gun/magic/mirror_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type) + if(attack_type != PROJECTILE_ATTACK && prob(NORMAL_BLOCK_CHANCE)) + return TRUE + +/obj/item/gun/magic/mirror_shield/attack_self(mob/user, modifiers) + . = ..() + reaction_mode = !reaction_mode + balloon_alert(user, "you hold \the [src] in such a way as to [reaction_mode == REACTION_MODE_ABSORB ? "absorb" : "reflect"] projectiles.") + +/obj/item/gun/magic/mirror_shield/examine(mob/user) + . = ..() + if(HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED)) + . += "
It currently contains: [english_list(stored_projectiles, comma_text = ",
")]." + +/obj/item/gun/magic/mirror_shield/recharge_newshot() + if(!chambered.loaded_projectile && length(stored_projectiles)) + var/obj/projectile/loaded = stored_projectiles[1] + loaded.forceMove(chambered) + chambered.loaded_projectile = loaded + stored_projectiles -= loaded + +/obj/item/gun/magic/mirror_shield/can_shoot() + return chambered.loaded_projectile + +/obj/item/gun/magic/mirror_shield/handle_chamber(mob/living/user, empty_chamber, from_firing, chamber_next_round) + recharge_newshot() + +/obj/item/gun/magic/mirror_shield/proc/absorb_projectile(obj/projectile/absorbed) + STOP_PROCESSING(SSprojectiles, absorbed) + absorbed.fired = FALSE + QDEL_NULL(absorbed.trajectory) + if(!chambered.loaded_projectile) + absorbed.forceMove(chambered) + chambered.loaded_projectile = absorbed + else + absorbed.forceMove(src) + stored_projectiles += absorbed + absorbed.update_appearance() + visible_message(span_notice("\The [src] absorbs [absorbed]!")) + +/obj/item/gun/magic/mirror_shield/proc/handle_hit(mob/held_by, list/projectile_args, obj/projectile/hit_by) + SIGNAL_HANDLER + if(!prob((src in held_by.held_items) ? PROJECTILE_HIT_EFFECT_CHANCE : NORMAL_BLOCK_CHANCE)) //turns out its harder to block with something when your not holding it + return + + hit_by.impacted = list() + var/turf/firer_turf = get_turf(hit_by.firer) + if(hit_by.firer && get_dist(firer_turf, get_turf(src)) <= 1) //this is due to some jank I cant figure out, if you want to go ahead + hit_by.process_hit(firer_turf, hit_by.firer) + else if(reaction_mode == REACTION_MODE_ABSORB && length(stored_projectiles) <= max_stored_projectiles && !(hit_by.type in blacklisted_projectile_types)) + absorb_projectile(hit_by) + else + hit_by.set_angle_centered(get_angle(held_by, hit_by.firer)) + hit_by.firer = held_by + hit_by.speed *= 0.8 + hit_by.damage *= 1.15 + + playsound(src, 'sound/magic/cosmic_expansion.ogg', vol = 120, channel = CHANNEL_SOUND_EFFECTS) + return PROJECTILE_INTERRUPT_HIT | PROJECTILE_INTERRUPT_BLOCK_QDEL + +//a dummy casing type to get filled with absorbed projectiles +/obj/item/ammo_casing/mirror_shield_dummy + loaded_projectile = null + firing_effect_type = null + +/obj/item/ammo_casing/mirror_shield_dummy/newshot() + return + +#undef PROJECTILE_HIT_EFFECT_CHANCE +#undef NORMAL_BLOCK_CHANCE +#undef REACTION_MODE_ABSORB +#undef REACTION_MODE_REFLECT diff --git a/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm index 4199e85c6e73..f6db030d5c46 100644 --- a/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm +++ b/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm @@ -1,13 +1,34 @@ /datum/spellbook_entry/item/magical_chemsprayer name = "Magic Chem Sprayer" - desc = "A magic chemical sprayer that will fill itself with unlimited random chemicals." + desc = "A magic chemical sprayer that will fill itself with unlimited random chemicals. Now with protective gear!" item_path = /obj/item/reagent_containers/spray/chemsprayer/magical category = "Defensive" cost = 1 +//if still too weak I could also give galoshes +/datum/spellbook_entry/item/magical_chemsprayer/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip) + . = ..() + var/turf/user_turf = get_turf(user) + if(!user_turf) + return + + for(var/obj/item/thing as anything in list(/obj/item/clothing/gloves/combat/wizard, /obj/item/clothing/head/wizard/bio_suit, /obj/item/clothing/suit/wizrobe/bio_suit)) + thing = new thing(user_turf) + user.equip_to_appropriate_slot(thing) + /datum/spellbook_entry/item/reactive_talisman name = "Reactive Talisman" desc = "An enchanted talisman that has a chance to cast a spell if it's wearer is hit." item_path = /obj/item/clothing/neck/neckless/wizard_reactive category = "Defensive" cost = 1 + +/datum/spellbook_entry/item/mirror_shield + name = "Mirror Shield" + desc = "A mirror that will absorb projectiles shot into it to later be shot back out at your convenience." + item_path = /obj/item/gun/magic/mirror_shield + category = "Defensive" + cost = 2 + +/datum/spellbook_entry/item/armor + cost = 1 diff --git a/monkestation/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm b/monkestation/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm new file mode 100644 index 000000000000..bc0348f39c1e --- /dev/null +++ b/monkestation/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm @@ -0,0 +1,4 @@ +/obj/item/spellbook/proc/adjust_charge(adjust_by) + log_spellbook("[src] charges adjusted by [adjust_by]. [usr ? "user: [usr]." : ""]") + uses += adjust_by + return TRUE diff --git a/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm b/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm index c0da0a38941d..10685cd7c179 100644 --- a/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm +++ b/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm @@ -6,4 +6,12 @@ ///How many times this rune needs to be invoked to complete var/invokes_needed = GRAND_RUNE_INVOKES_TO_COMPLETE +/obj/effect/grand_rune/add_channel_effect(mob/living/user) + . = ..() + ADD_TRAIT(user, TRAIT_MOVE_FLYING, REF(src)) + +/obj/effect/grand_rune/remove_channel_effect(mob/living/user) + . = ..() + REMOVE_TRAIT(user, TRAIT_MOVE_FLYING, REF(src)) + #undef GRAND_RUNE_INVOKES_TO_COMPLETE diff --git a/monkestation/code/modules/botany/new_seeds/mutations.dm b/monkestation/code/modules/botany/new_seeds/mutations.dm index 11f700bdc0b0..ac90b54534de 100644 --- a/monkestation/code/modules/botany/new_seeds/mutations.dm +++ b/monkestation/code/modules/botany/new_seeds/mutations.dm @@ -4,12 +4,12 @@ created_product = /obj/item/paper created_seed = /obj/item/seeds/tree/paper - -/datum/hydroponics/plant_mutation/money +//money treee seeds have been moved to the rare maint pool +/*/datum/hydroponics/plant_mutation/money mutates_from = list(/obj/item/seeds/tree/paper) required_potency = list(30, INFINITY) created_product = /obj/item/stack/spacecash/c10 - created_seed = /obj/item/seeds/tree/money + created_seed = /obj/item/seeds/tree/money*/ /datum/hydroponics/plant_mutation/steel mutates_from = list(/obj/item/seeds/tree) diff --git a/monkestation/code/modules/botany/new_seeds/seeds.dm b/monkestation/code/modules/botany/new_seeds/seeds.dm index d54ec1d4d37a..906d075b10a4 100644 --- a/monkestation/code/modules/botany/new_seeds/seeds.dm +++ b/monkestation/code/modules/botany/new_seeds/seeds.dm @@ -37,7 +37,7 @@ icon_grow = "TreePaper-G" product = /obj/item/paper - possible_mutations = list(/datum/hydroponics/plant_mutation/money) +// possible_mutations = list(/datum/hydroponics/plant_mutation/money) //money trees moved to the maint pool genes = list(/datum/plant_gene/trait/repeated_harvest) /obj/item/seeds/tree/money diff --git a/monkestation/code/modules/projectiles/projectile/spells.dm b/monkestation/code/modules/projectiles/projectile/spells.dm new file mode 100644 index 000000000000..cf25d6d2c289 --- /dev/null +++ b/monkestation/code/modules/projectiles/projectile/spells.dm @@ -0,0 +1,79 @@ +/obj/projectile/magic/fire_ball + name = "fire ball" + icon = 'monkestation/icons/obj/weapons/guns/projectiles.dmi' + icon_state = "fire_ball" + damage = 20 + damage_type = BURN + hitsound = null + projectile_piercing = PASSMOB + + ricochets_max = 4 + ricochet_chance = 100 + ricochet_decay_chance = 1 + ricochet_decay_damage = 1 + ricochet_incidence_leeway = 0 + ricochet_shoots_firer = FALSE + ///A weakref to our "true" firer because ricochet changes firer + var/datum/weakref/true_firer + +/obj/projectile/magic/fire_ball/fire(angle, atom/direct_target) + . = ..() + if(firer) + true_firer = WEAKREF(firer) + +/obj/projectile/magic/fire_ball/prehit_pierce(atom/target) + . = ..() + if(. != PROJECTILE_DELETE_WITHOUT_HITTING || !ismob(target)) + return + true_firer = WEAKREF(target) + handle_bounce(target) + visible_message(span_warning("[src] bounces off the aura around [target]!")) + return PROJECTILE_PIERCE_PHASE + +/obj/projectile/magic/fire_ball/Impact(atom/A) + . = ..() + if(.) + playsound(src, 'sound/items/dodgeball.ogg', 200, channel = CHANNEL_SOUND_EFFECTS) //this is a very quiet sound + +/obj/projectile/magic/fire_ball/on_hit(mob/living/target, blocked, pierce_hit) + if(target == true_firer?.resolve()) //we do this here instead of on_hit_target due to us having specific logic here with handle_bounce() + handle_bounce(target) + return BULLET_ACT_BLOCK + + . = ..() + if(. != BULLET_ACT_HIT || !istype(target) ) + return + + if(pierces >= ricochets_max) + projectile_piercing = NONE + target.adjust_fire_stacks(2) + target.ignite_mob() + target.Knockdown(3 SECONDS) + target.Paralyze(0.5 SECONDS) + handle_bounce(target) + +/obj/projectile/magic/fire_ball/check_ricochet_flag(atom/A) + return !ismob(A) //we handle mobs ourselves but besides that can ALWAYS ricochet + +/obj/projectile/magic/fire_ball/check_ricochet(atom/A) + return TRUE //this handles the prob checks which is always 100, so lets just skip the step to save resources + +///Find a tile within 1 range() of a valid mob in our view, if we cant find any then return FALSE +/obj/projectile/magic/fire_ball/proc/get_new_target() + var/list/possible_targets = list() + var/mob/resolved_true_firer = true_firer?.resolve() + for(var/mob/living/possible_target in view()) + if(possible_target == resolved_true_firer || impacted[possible_target]) + continue + possible_targets += possible_target + + if(!length(possible_targets)) + return FALSE + return pick(RANGE_TURFS(1, get_turf(pick(possible_targets)))) + +/obj/projectile/magic/fire_ball/proc/handle_bounce(atom/target) + var/new_target = get_new_target() + if(new_target) + set_angle_centered(get_angle(target, new_target)) + else + reflect(target) diff --git a/monkestation/code/modules/spells/spell_types/pointed/fire_ball.dm b/monkestation/code/modules/spells/spell_types/pointed/fire_ball.dm new file mode 100644 index 000000000000..9f401c5aeff8 --- /dev/null +++ b/monkestation/code/modules/spells/spell_types/pointed/fire_ball.dm @@ -0,0 +1,24 @@ +/datum/action/cooldown/spell/pointed/projectile/fireball/bouncy + name = "Fire Ball" + desc = "This spell fires a ball of fire at a target. Watch out for collateral." + button_icon = 'monkestation/icons/obj/weapons/guns/projectiles.dmi' + button_icon_state = "fire_ball" + + active_msg = "You prepare to cast your fire ball spell!" + deactive_msg = "You extinguish your fire ball... for now." + cooldown_reduction_per_rank = -1 SECONDS //bit too strong otherwise + spell_max_level = 3 + projectile_type = /obj/projectile/magic/fire_ball + +/datum/action/cooldown/spell/pointed/projectile/fireball/bouncy/level_spell(bypass_cap) + . = ..() + projectile_amount++ //become the schoolyard bully + unset_after_click = FALSE + if(spell_level == spell_max_level) + projectiles_per_fire++ + +/datum/action/cooldown/spell/pointed/projectile/fireball/bouncy/ready_projectile(obj/projectile/to_fire, atom/target, mob/user, iteration) + . = ..() + to_fire.ricochets_max += spell_level - 1 + if(iteration > 1) + to_fire.set_angle(dir2angle(user.dir) + rand(-15, 15)) diff --git a/monkestation/code/modules/storytellers/converted_events/_base_event.dm b/monkestation/code/modules/storytellers/converted_events/_base_event.dm index 90e1ab856fd6..84ae021d7840 100644 --- a/monkestation/code/modules/storytellers/converted_events/_base_event.dm +++ b/monkestation/code/modules/storytellers/converted_events/_base_event.dm @@ -289,7 +289,15 @@ if(!event_type) return var/datum/round_event_control/triggered_event = locate(event_type) in SSgamemode.control - addtimer(CALLBACK(triggered_event, TYPE_PROC_REF(/datum/round_event_control, run_event), FALSE, null, FALSE, "storyteller"), 1 SECONDS) // wait a second to avoid any potential omnitraitor bs + //wait a second to avoid any potential omnitraitor bs + addtimer(CALLBACK(triggered_event, TYPE_PROC_REF(/datum/round_event_control, run_event), FALSE, null, FALSE, "storyteller"), 1 SECONDS) + +/datum/round_event/antagonist/solo/start() + for(var/datum/mind/antag_mind as anything in setup_minds) + add_datum_to_mind(antag_mind, antag_mind.current) + +/datum/round_event/antagonist/solo/proc/add_datum_to_mind(datum/mind/antag_mind) + antag_mind.add_antag_datum(antag_datum) /datum/round_event/antagonist/solo/proc/spawn_extra_events() if(!LAZYLEN(extra_spawned_events)) @@ -297,6 +305,24 @@ var/datum/round_event_control/event = pick_weight(extra_spawned_events) event?.run_event(random = FALSE, event_cause = "storyteller") +/datum/round_event/antagonist/solo/proc/create_human_mob_copy(turf/create_at, mob/living/carbon/human/old_mob, qdel_old_mob = TRUE) + if(!old_mob?.client) + return + + var/mob/living/carbon/human/new_character = new(create_at) + if(!create_at) + SSjob.SendToLateJoin(new_character) + + old_mob.client.prefs.safe_transfer_prefs_to(new_character) + new_character.dna.update_dna_identity() + old_mob.mind.transfer_to(new_character) + if(qdel_old_mob) + qdel(old_mob) + return new_character + +/datum/round_event/antagonist/solo/ghost/start() + for(var/datum/mind/antag_mind as anything in setup_minds) + add_datum_to_mind(antag_mind) /datum/round_event/antagonist/solo/ghost/setup() var/datum/round_event_control/antagonist/solo/cast_control = control @@ -346,16 +372,3 @@ new_human.mind.restricted_roles = restricted_roles setup_minds += new_human.mind setup = TRUE - - -/datum/round_event/antagonist/solo/start() - for(var/datum/mind/antag_mind as anything in setup_minds) - add_datum_to_mind(antag_mind, antag_mind.current) - -/datum/round_event/antagonist/solo/proc/add_datum_to_mind(datum/mind/antag_mind) - antag_mind.add_antag_datum(antag_datum) - -/datum/round_event/antagonist/solo/ghost/start() - for(var/datum/mind/antag_mind as anything in setup_minds) - add_datum_to_mind(antag_mind) - diff --git a/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm b/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm index d61c09ddfe94..2843b335d454 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm @@ -31,12 +31,11 @@ required_enemies = 5 base_antags = 4 maximum_antags = 4 - // I give up, just there should be enough heads with 35 players... - min_players = 35 + min_players = 45 roundstart = TRUE earliest_start = 0 SECONDS weight = 4 - max_occurrences = 0 + max_occurrences = 1 /datum/round_event/antagonist/solo/clockcult end_when = 60000 diff --git a/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm b/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm index 65950c93feca..b51cb8b8ae27 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/clown_operative.dm @@ -65,6 +65,7 @@ for(var/obj/item/item as anything in items) qdel(item) + create_human_mob_copy(get_turf(current_mob), current_mob) antag_mind.set_assigned_role(SSjob.GetJobType(/datum/job/clown_operative)) antag_mind.special_role = ROLE_CLOWN_OPERATIVE @@ -75,10 +76,11 @@ if(!set_leader) set_leader = TRUE var/datum/antagonist/nukeop/leader/leader_antag_datum = new() + var/mob/living/carbon/human/leader_mob = most_experienced.current + leader_mob = create_human_mob_copy(get_turf(leader_mob), leader_mob) nuke_team = leader_antag_datum.nuke_team most_experienced.add_antag_datum(leader_antag_datum) - var/mob/living/carbon/human/leader = most_experienced.current - leader.equip_species_outfit(/datum/outfit/syndicate/clownop/leader) + leader_mob.equip_species_outfit(/datum/outfit/syndicate/clownop/leader) if(antag_mind == most_experienced) return diff --git a/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm b/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm index 54685b4f4871..f95ffd7c64ae 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm @@ -58,11 +58,9 @@ for(var/obj/item/item as anything in items) qdel(item) + create_human_mob_copy(get_turf(current_mob), current_mob) if(!most_experienced) - most_experienced = get_most_experienced(setup_minds, required_role) - - if(!most_experienced) - most_experienced = antag_mind + most_experienced = get_most_experienced(setup_minds, required_role) || antag_mind if(!set_leader) set_leader = TRUE @@ -73,6 +71,7 @@ leader_mob.unequip_everything() for(var/obj/item/item as anything in leader_items) qdel(item) + leader_mob = create_human_mob_copy(get_turf(leader_mob), leader_mob) most_experienced.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_operative)) most_experienced.special_role = ROLE_NUCLEAR_OPERATIVE var/datum/antagonist/nukeop/leader/leader_antag_datum = most_experienced.add_antag_datum(/datum/antagonist/nukeop/leader) diff --git a/monkestation/icons/mob/clothing/back.dmi b/monkestation/icons/mob/clothing/back.dmi index 169859c9430b..45c1a44aa2a1 100644 Binary files a/monkestation/icons/mob/clothing/back.dmi and b/monkestation/icons/mob/clothing/back.dmi differ diff --git a/monkestation/icons/mob/clothing/head/bio.dmi b/monkestation/icons/mob/clothing/head/bio.dmi new file mode 100644 index 000000000000..0d96ef13e192 Binary files /dev/null and b/monkestation/icons/mob/clothing/head/bio.dmi differ diff --git a/monkestation/icons/mob/clothing/suits/bio.dmi b/monkestation/icons/mob/clothing/suits/bio.dmi new file mode 100644 index 000000000000..21ea4945074b Binary files /dev/null and b/monkestation/icons/mob/clothing/suits/bio.dmi differ diff --git a/monkestation/icons/mob/inhands/equipment/shields_lefthand.dmi b/monkestation/icons/mob/inhands/equipment/shields_lefthand.dmi new file mode 100644 index 000000000000..9d68d9bc028d Binary files /dev/null and b/monkestation/icons/mob/inhands/equipment/shields_lefthand.dmi differ diff --git a/monkestation/icons/mob/inhands/equipment/shields_righthand.dmi b/monkestation/icons/mob/inhands/equipment/shields_righthand.dmi new file mode 100644 index 000000000000..e3712b9bd95d Binary files /dev/null and b/monkestation/icons/mob/inhands/equipment/shields_righthand.dmi differ diff --git a/monkestation/icons/obj/clothing/head/bio.dmi b/monkestation/icons/obj/clothing/head/bio.dmi new file mode 100644 index 000000000000..1cfd3c66d180 Binary files /dev/null and b/monkestation/icons/obj/clothing/head/bio.dmi differ diff --git a/monkestation/icons/obj/clothing/suits/bio.dmi b/monkestation/icons/obj/clothing/suits/bio.dmi new file mode 100644 index 000000000000..a14fd71a3cf4 Binary files /dev/null and b/monkestation/icons/obj/clothing/suits/bio.dmi differ diff --git a/monkestation/icons/obj/weapons/guns/projectiles.dmi b/monkestation/icons/obj/weapons/guns/projectiles.dmi new file mode 100644 index 000000000000..69dbc9a4ffa2 Binary files /dev/null and b/monkestation/icons/obj/weapons/guns/projectiles.dmi differ diff --git a/monkestation/icons/obj/weapons/shields.dmi b/monkestation/icons/obj/weapons/shields.dmi new file mode 100644 index 000000000000..c7aa290346f6 Binary files /dev/null and b/monkestation/icons/obj/weapons/shields.dmi differ diff --git a/sound/magic/staff_shrink.ogg b/sound/magic/staff_shrink.ogg new file mode 100644 index 000000000000..f2268130fd81 Binary files /dev/null and b/sound/magic/staff_shrink.ogg differ diff --git a/tgstation.dme b/tgstation.dme index e52323a473a9..03945169b298 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -115,6 +115,7 @@ #include "code\__DEFINES\ghost.dm" #include "code\__DEFINES\gravity.dm" #include "code\__DEFINES\guardian_defines.dm" +#include "code\__DEFINES\holiday.dm" #include "code\__DEFINES\holopads.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\icon_smoothing.dm" @@ -467,6 +468,7 @@ #include "code\__DEFINES\~monkestation\dcs\signals\signals_food.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_guns.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_item.dm" +#include "code\__DEFINES\~monkestation\dcs\signals\signals_object.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_traitor.dm" #include "code\__DEFINES\~monkestation\dcs\signals\signals_mob\signals_mob_main.dm" #include "code\__HELPERS\_auxtools_api.dm" @@ -1082,6 +1084,7 @@ #include "code\datums\components\beetlejuice.dm" #include "code\datums\components\blob_minion.dm" #include "code\datums\components\blood_walk.dm" +#include "code\datums\components\bloody_spreader.dm" #include "code\datums\components\bloodysoles.dm" #include "code\datums\components\boomerang.dm" #include "code\datums\components\breeding.dm" @@ -1368,6 +1371,7 @@ #include "code\datums\elements\blocks_explosives.dm" #include "code\datums\elements\bonus_damage.dm" #include "code\datums\elements\bsa_blocker.dm" +#include "code\datums\elements\bugkiller_reagent.dm" #include "code\datums\elements\bump_click.dm" #include "code\datums\elements\can_barricade.dm" #include "code\datums\elements\chemical_transfer.dm" @@ -1923,6 +1927,7 @@ #include "code\game\machinery\dna_infuser\organ_sets\goliath_organs.dm" #include "code\game\machinery\dna_infuser\organ_sets\gondola_organs.dm" #include "code\game\machinery\dna_infuser\organ_sets\rat_organs.dm" +#include "code\game\machinery\dna_infuser\organ_sets\roach_organs.dm" #include "code\game\machinery\doors\airlock.dm" #include "code\game\machinery\doors\airlock_electronics.dm" #include "code\game\machinery\doors\airlock_types.dm" @@ -5372,6 +5377,7 @@ #include "code\modules\spells\spell_types\pointed\spell_cards.dm" #include "code\modules\spells\spell_types\pointed\swap.dm" #include "code\modules\spells\spell_types\pointed\terrorize.dm" +#include "code\modules\spells\spell_types\pointed\tie_shoes.dm" #include "code\modules\spells\spell_types\projectile\_basic_projectile.dm" #include "code\modules\spells\spell_types\projectile\juggernaut.dm" #include "code\modules\spells\spell_types\self\basic_heal.dm" @@ -5823,8 +5829,10 @@ #include "monkestation\code\datums\brain_damage\phobia.dm" #include "monkestation\code\datums\changelog\changelog.dm" #include "monkestation\code\datums\components\carbon_sprint.dm" +#include "monkestation\code\datums\components\charge_adjuster.dm" #include "monkestation\code\datums\components\crafting.dm" #include "monkestation\code\datums\components\irradiated.dm" +#include "monkestation\code\datums\components\lock_on_cursor.dm" #include "monkestation\code\datums\components\multi_hit.dm" #include "monkestation\code\datums\components\pixel_shift.dm" #include "monkestation\code\datums\components\throw_bounce.dm" @@ -6052,6 +6060,7 @@ #include "monkestation\code\modules\antagonists\brother\actions\communicate.dm" #include "monkestation\code\modules\antagonists\brother\actions\gear.dm" #include "monkestation\code\modules\antagonists\brother\gear\_gear.dm" +#include "monkestation\code\modules\antagonists\brother\gear\misc.dm" #include "monkestation\code\modules\antagonists\brother\gear\recipes.dm" #include "monkestation\code\modules\antagonists\changeling\powers\tiny_prick.dm" #include "monkestation\code\modules\antagonists\clock_cult\area.dm" @@ -6199,6 +6208,8 @@ #include "monkestation\code\modules\antagonists\traitor\objectives\final_objective\final_objective.dm" #include "monkestation\code\modules\antagonists\wizard\wizard.dm" #include "monkestation\code\modules\antagonists\wizard\equipment\artefact.dm" +#include "monkestation\code\modules\antagonists\wizard\equipment\mirror_shield.dm" +#include "monkestation\code\modules\antagonists\wizard\equipment\wizard_spellbook.dm" #include "monkestation\code\modules\antagonists\wizard\equipment\spellbook_entries\defensive.dm" #include "monkestation\code\modules\antagonists\wizard\equipment\spellbook_entries\mobility.dm" #include "monkestation\code\modules\antagonists\wizard\equipment\spellbook_entries\offensive.dm" @@ -7482,6 +7493,7 @@ #include "monkestation\code\modules\projectiles\guns\ballistic\revolver.dm" #include "monkestation\code\modules\projectiles\guns\ballistic\ryanecorp_whispering_jester.dm" #include "monkestation\code\modules\projectiles\guns\special\meat_hook.dm" +#include "monkestation\code\modules\projectiles\projectile\spells.dm" #include "monkestation\code\modules\projectiles\projectile\bullets\c45_caseless.dm" #include "monkestation\code\modules\ranching\_animations.dm" #include "monkestation\code\modules\ranching\chicken_book.dm" @@ -7787,6 +7799,7 @@ #include "monkestation\code\modules\smithing\TEG\teg_states\worked_materials.dm" #include "monkestation\code\modules\spells\spell_types\aoe_spell\mind_swap.dm" #include "monkestation\code\modules\spells\spell_types\conjure_item\summon_mjollnir.dm" +#include "monkestation\code\modules\spells\spell_types\pointed\fire_ball.dm" #include "monkestation\code\modules\spells\spell_types\pointed\smite.dm" #include "monkestation\code\modules\store\admin\admin_coin_modification.dm" #include "monkestation\code\modules\store\atm\_atm.dm" diff --git a/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx b/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx index e41c923b8dbe..1b4d2d6f8bbc 100644 --- a/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx +++ b/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx @@ -131,11 +131,7 @@ const IntroductionSection = (props) => { const { act, data } = useBackend(); const { true_name, hive_name, objectives, can_change_objective } = data; return ( -
4} - > +
You are {true_name} from the diff --git a/tgui/packages/tgui/interfaces/InfuserBook.tsx b/tgui/packages/tgui/interfaces/InfuserBook.tsx index e81d1204ad3d..4e4f796068fa 100644 --- a/tgui/packages/tgui/interfaces/InfuserBook.tsx +++ b/tgui/packages/tgui/interfaces/InfuserBook.tsx @@ -28,7 +28,7 @@ type TierData = { name: string; }; -const PAGE_HEIGHT = '235px'; +const PAGE_HEIGHT = 30; const TIER2TIERDATA: TierData[] = [ { @@ -68,8 +68,8 @@ const TIER2TIERDATA: TierData[] = [ }, ]; -export const InfuserBook = (props) => { - const { data } = useBackend(); +export const InfuserBook = (props, context) => { + const { data, act } = useBackend(); const { entries } = data; const [bookPosition, setBookPosition] = useLocalState( @@ -86,7 +86,11 @@ export const InfuserBook = (props) => { let currentEntry = paginatedEntries[chapter][pageInChapter]; const switchChapter = (newChapter) => { + if (chapter === newChapter) { + return; + } setBookPosition({ chapter: newChapter, pageInChapter: 0 }); + act('play_flip_sound'); // just so we can play a sound fx on page turn }; const setPage = (newPage) => { @@ -111,6 +115,7 @@ export const InfuserBook = (props) => { newBookPosition.pageInChapter = newPage; } setBookPosition(newBookPosition); + act('play_flip_sound'); // just so we can play a sound fx on page turn }; const tabs = [ @@ -126,7 +131,7 @@ export const InfuserBook = (props) => { const restrictedNext = chapter === 3 && pageInChapter === 0; return ( - + @@ -216,7 +221,7 @@ export const InfuserInstructions = (props) => {
3. Have someone activate the machine externally.
- + And you're done! Note that the infusion source will be obliterated in the process. diff --git a/tgui/packages/tgui/interfaces/Orbit/constants.ts b/tgui/packages/tgui/interfaces/Orbit/constants.ts index 5c73e17ed723..28501ad361eb 100644 --- a/tgui/packages/tgui/interfaces/Orbit/constants.ts +++ b/tgui/packages/tgui/interfaces/Orbit/constants.ts @@ -8,6 +8,8 @@ export const ANTAG2COLOR = { 'Emergency Response Team': 'teal', 'Escaped Fugitives': 'orange', 'Xenomorph Infestation': 'violet', + 'Spacetime Aberrations': 'white', + 'Deviant Crew': 'white', } as const; export const THREAT = { diff --git a/tgui/packages/tgui/interfaces/Secrets.jsx b/tgui/packages/tgui/interfaces/Secrets.jsx index 9af68a897aef..146a40b427f6 100644 --- a/tgui/packages/tgui/interfaces/Secrets.jsx +++ b/tgui/packages/tgui/interfaces/Secrets.jsx @@ -480,8 +480,8 @@ const FunForYouTab = (props) => { color="red" icon="user-secret" fluid - content="Everyone is the traitor" - onClick={() => act('traitor_all')} + content="Everyone is the antag" + onClick={() => act('antag_all')} />