diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index 73a07a866d..94767f6fa6 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -17967,8 +17967,7 @@ }, /obj/machinery/camera{ c_tag = "Kitchen Coldroom"; - dir = 4; - network = list("mine") + dir = 4 }, /turf/open/floor/plasteel/freezer, /area/crew_quarters/kitchen) @@ -28475,8 +28474,7 @@ }, /obj/machinery/camera{ c_tag = "Crematorium"; - dir = 4; - network = list("mine") + dir = 4 }, /turf/open/floor/plasteel/vault/side{ dir = 4 @@ -30395,11 +30393,6 @@ /obj/structure/chair{ dir = 8 }, -/obj/machinery/camera{ - c_tag = "Shuttle Docking Foyer"; - dir = 8; - network = list("mine") - }, /obj/machinery/camera{ c_tag = "Escape Arm Airlocks"; dir = 8 diff --git a/code/__DEFINES/cinematics.dm b/code/__DEFINES/cinematics.dm index 4bfc8dd3c4..22e0943802 100644 --- a/code/__DEFINES/cinematics.dm +++ b/code/__DEFINES/cinematics.dm @@ -8,4 +8,5 @@ #define CINEMATIC_CULT 8 #define CINEMATIC_NUKE_FAKE 9 #define CINEMATIC_NUKE_NO_CORE 10 -#define CINEMATIC_NUKE_FAR 11 \ No newline at end of file +#define CINEMATIC_NUKE_FAR 11 +#define CINEMATIC_NUKE_CLOWNOP 12 \ No newline at end of file diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm index d6822a37c6..261e7c7586 100644 --- a/code/__DEFINES/citadel_defines.dm +++ b/code/__DEFINES/citadel_defines.dm @@ -110,6 +110,11 @@ #define CITADEL_MENTOR_OOC_COLOUR "#224724" +//xenobio console upgrade stuff +#define XENOBIO_UPGRADE_MONKEYS 1 +#define XENOBIO_UPGRADE_SLIMEBASIC 2 +#define XENOBIO_UPGRADE_SLIMEADV 4 + //stamina stuff #define STAMINA_SOFTCRIT 100 //softcrit for stamina damage. prevents standing up, prevents performing actions that cost stamina, etc, but doesn't force a rest or stop movement #define STAMINA_CRIT 140 //crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index b67c084e10..750c107980 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -142,3 +142,7 @@ #define EMP_HEAVY 1 #define EMP_LIGHT 2 + +#define GRENADE_CLUMSY_FUMBLE 1 +#define GRENADE_NONCLUMSY_FUMBLE 2 +#define GRENADE_NO_FUMBLE 3 diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 5b4ff7e378..bbe29f3e85 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -86,3 +86,8 @@ #define CALTROP_BYPASS_SHOES 1 #define CALTROP_IGNORE_WALKERS 2 + +//Component Specific Signals +//Wet floors +#define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values. +#define COMSIG_TURF_MAKE_DRY "make_turf_try" //(max_strength, immediate, duration_decrease = INFINITY): Returns bool. diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index f33f38cb70..2d088552bd 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -85,3 +85,16 @@ #define SUPERMATTER_DANGER 4 // Integrity < 50% #define SUPERMATTER_EMERGENCY 5 // Integrity < 25% #define SUPERMATTER_DELAMINATING 6 // Pretty obvious. + +//Nuclear bomb stuff +#define NUKESTATE_INTACT 5 +#define NUKESTATE_UNSCREWED 4 +#define NUKESTATE_PANEL_REMOVED 3 +#define NUKESTATE_WELDED 2 +#define NUKESTATE_CORE_EXPOSED 1 +#define NUKESTATE_CORE_REMOVED 0 + +#define NUKE_OFF_LOCKED 0 +#define NUKE_OFF_UNLOCKED 1 +#define NUKE_ON_TIMING 2 +#define NUKE_ON_EXPLODING 3 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 30672189b4..3be912dc96 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -185,15 +185,17 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache) #define HAS_SENSORS 1 #define LOCKED_SENSORS 2 -//Turf wet states +//Wet floor type flags. Stronger ones should be higher in number. #define TURF_DRY 0 #define TURF_WET_WATER 1 #define TURF_WET_LUBE 2 -#define TURF_WET_ICE 3 -#define TURF_WET_PERMAFROST 4 +#define TURF_WET_ICE 4 +#define TURF_WET_PERMAFROST 8 -//Maximum amount of time, (in approx. seconds.) a tile can be wet for. -#define MAXIMUM_WET_TIME 300 +#define IS_WET_OPEN_TURF(O) O.GetComponent(/datum/component/wet_floor) + +//Maximum amount of time, (in deciseconds) a tile can be wet for. +#define MAXIMUM_WET_TIME 3000 //unmagic-strings for types of polls #define POLLTYPE_OPTION "OPTION" diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 7925edb76a..15459f7f71 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -86,6 +86,7 @@ #define FIRE_PRIORITY_SERVER_MAINT 10 #define FIRE_PRIORITY_RESEARCH 10 #define FIRE_PRIORITY_GARBAGE 15 +#define FIRE_PRIORITY_WET_FLOORS 20 #define FIRE_PRIORITY_AIR 20 #define FIRE_PRIORITY_NPC 20 #define FIRE_PRIORITY_PROCESS 25 diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index 8fbb5d805b..a1791a55db 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -1,5 +1,3 @@ -#define ION_FILE "ion_laws.json" - /proc/lizard_name(gender) if(gender == MALE) return "[pick(GLOB.lizard_names_male)]-[pick(GLOB.lizard_names_male)]" diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 14c48fbcaa..0090a69cc6 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -10,9 +10,8 @@ Note that AI have no need for the adjacency proc, and so this proc is a lot cleaner. */ /mob/living/silicon/ai/DblClickOn(var/atom/A, params) - if(client.click_intercept) - if(call(client.click_intercept, "InterceptClickOn")(src, params, A)) - return + if(check_click_intercept(params,A)) + return if(control_disabled || incapacitated()) return @@ -27,9 +26,8 @@ return next_click = world.time + 1 - if(client.click_intercept) - if(call(client.click_intercept, "InterceptClickOn")(src, params, A)) - return + if(check_click_intercept(params,A)) + return if(control_disabled || incapacitated()) return diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 4c588c96fe..73ad2a94e3 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -61,9 +61,8 @@ return next_click = world.time + 1 - if(client && client.click_intercept) - if(call(client.click_intercept, "InterceptClickOn")(src, params, A)) - return + if(check_click_intercept(params,A)) + return var/list/modifiers = params2list(params) if(modifiers["shift"] && modifiers["middle"]) @@ -476,7 +475,7 @@ var/mob/living/carbon/C = usr C.swap_hand() else - var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr)) + var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr.client ? usr.client.eye : usr)) params += "&catcher=1" if(T) T.Click(location, control, params) @@ -496,3 +495,16 @@ else view = 1 add_view_range(view) + +/mob/proc/check_click_intercept(params,A) + //Client level intercept + if(client && client.click_intercept) + if(call(client.click_intercept, "InterceptClickOn")(src, params, A)) + return TRUE + + //Mob level intercept + if(click_intercept) + if(call(click_intercept, "InterceptClickOn")(src, params, A)) + return TRUE + + return FALSE diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 6ed126cf37..95f1a50968 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -11,9 +11,8 @@ return next_click = world.time + 1 - if(client.click_intercept) - if(call(client.click_intercept,"InterceptClickOn")(src,params,A)) - return + if(check_click_intercept(params,A)) + return if(stat || lockcharge || IsKnockdown() || IsStun() || IsUnconscious()) return diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index da21f43ce7..43db4fa552 100755 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -21,6 +21,7 @@ C.parallax_layers_cached = list() C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_1(null, C.view) C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_2(null, C.view) + C.parallax_layers_cached += new /obj/screen/parallax_layer/planet(null, C.view) C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_3(null, C.view) C.parallax_layers = C.parallax_layers_cached.Copy() @@ -86,6 +87,7 @@ /datum/hud/proc/update_parallax_pref(mob/viewmob) remove_parallax(viewmob) create_parallax(viewmob) + update_parallax() // This sets which way the current shuttle is moving (returns true if the shuttle has stopped moving so the caller can append their animation) /datum/hud/proc/set_parallax_movedir(new_parallax_movedir, skip_windups) @@ -194,18 +196,27 @@ var/obj/screen/parallax_layer/L = thing if (L.view_sized != C.view) L.update_o(C.view) - var/change_x = offset_x * L.speed - L.offset_x -= change_x - var/change_y = offset_y * L.speed - L.offset_y -= change_y - if(L.offset_x > 240) - L.offset_x -= 480 - if(L.offset_x < -240) - L.offset_x += 480 - if(L.offset_y > 240) - L.offset_y -= 480 - if(L.offset_y < -240) - L.offset_y += 480 + + var/change_x + var/change_y + + if(L.absolute) + L.offset_x = -(posobj.x - SSparallax.planet_x_offset) * L.speed + L.offset_y = -(posobj.y - SSparallax.planet_y_offset) * L.speed + else + change_x = offset_x * L.speed + L.offset_x -= change_x + change_y = offset_y * L.speed + L.offset_y -= change_y + + if(L.offset_x > 240) + L.offset_x -= 480 + if(L.offset_x < -240) + L.offset_x += 480 + if(L.offset_y > 240) + L.offset_y -= 480 + if(L.offset_y < -240) + L.offset_y += 480 if(!areaobj.parallax_movedir && C.dont_animate_parallax <= world.time && (offset_x || offset_y) && abs(offset_x) <= max(C.parallax_throttle/world.tick_lag+1,1) && abs(offset_y) <= max(C.parallax_throttle/world.tick_lag+1,1) && (round(abs(change_x)) > 1 || round(abs(change_y)) > 1)) @@ -232,6 +243,7 @@ var/offset_x = 0 var/offset_y = 0 var/view_sized + var/absolute = FALSE blend_mode = BLEND_ADD plane = PLANE_SPACE_PARALLAX screen_loc = "CENTER-7,CENTER-7" @@ -247,7 +259,7 @@ /obj/screen/parallax_layer/proc/update_o(view) if (!view) view = world.view - + var/list/viewscales = getviewsize(view) var/countx = CEILING((viewscales[1]/2)/(480/world.icon_size), 1)+1 var/county = CEILING((viewscales[2]/2)/(480/world.icon_size), 1)+1 @@ -278,6 +290,16 @@ speed = 1.4 layer = 3 +/obj/screen/parallax_layer/planet + icon_state = "planet" + blend_mode = BLEND_OVERLAY + absolute = TRUE //Status of seperation + speed = 3 + layer = 30 + +/obj/screen/parallax_layer/planet/update_o() + return //Shit wont move + #undef LOOP_NONE #undef LOOP_NORMAL #undef LOOP_REVERSE diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index f124e2779b..19c933952c 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -1,7 +1,6 @@ /mob/dead/observer/DblClickOn(var/atom/A, var/params) - if(client.click_intercept) - if(call(client.click_intercept,"InterceptClickOn")(src,params,A)) - return + if(check_click_intercept(params,A)) + return if(can_reenter_corpse && mind && mind.current) if(A == mind.current || (mind.current in A)) // double click your corpse or whatever holds it @@ -18,9 +17,8 @@ update_parallax_contents() /mob/dead/observer/ClickOn(var/atom/A, var/params) - if(client.click_intercept) - if(call(client.click_intercept,"InterceptClickOn")(src,params,A)) - return + if(check_click_intercept(params,A)) + return var/list/modifiers = params2list(params) if(modifiers["shift"] && modifiers["middle"]) diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm index f8f2d463ba..64299fda38 100644 --- a/code/controllers/subsystem/parallax.dm +++ b/code/controllers/subsystem/parallax.dm @@ -1,10 +1,17 @@ SUBSYSTEM_DEF(parallax) name = "Parallax" wait = 2 - flags = SS_POST_FIRE_TIMING | SS_BACKGROUND | SS_NO_INIT + flags = SS_POST_FIRE_TIMING | SS_BACKGROUND priority = FIRE_PRIORITY_PARALLAX runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT var/list/currentrun + var/planet_x_offset = 128 + var/planet_y_offset = 128 + +/datum/controller/subsystem/parallax/Initialize(timeofday) + . = ..() + planet_y_offset = rand(100, 160) + planet_x_offset = rand(100, 160) /datum/controller/subsystem/parallax/fire(resumed = 0) if (!resumed) diff --git a/code/controllers/subsystem/processing/wet_floors.dm b/code/controllers/subsystem/processing/wet_floors.dm new file mode 100644 index 0000000000..e125b5849c --- /dev/null +++ b/code/controllers/subsystem/processing/wet_floors.dm @@ -0,0 +1,5 @@ +PROCESSING_SUBSYSTEM_DEF(wet_floors) + name = "Wet floors" + priority = FIRE_PRIORITY_WET_FLOORS + wait = 15 + stat_tag = "WFP" //Used for logging diff --git a/code/datums/cinematic.dm b/code/datums/cinematic.dm index be7e6360aa..f5d65df110 100644 --- a/code/datums/cinematic.dm +++ b/code/datums/cinematic.dm @@ -51,7 +51,7 @@ GLOBAL_LIST_EMPTY(cinematics) for(var/A in GLOB.cinematics) var/datum/cinematic/C = A if(C == src) - continue + continue if(C.is_global || !is_global) return //Can't play two global or local cinematics at the same time @@ -73,7 +73,7 @@ GLOBAL_LIST_EMPTY(cinematics) if(is_global) M.notransform = TRUE locked += M - + //Actually play it content() //Cleanup @@ -209,6 +209,17 @@ GLOBAL_LIST_EMPTY(cinematics) cinematic_sound(sound('sound/effects/explosion_distant.ogg')) special() +/datum/cinematic/clownop + id = CINEMATIC_NUKE_CLOWNOP + cleanup_time = 100 + +/datum/cinematic/clownop/content() + flick("intro_nuke",screen) + sleep(35) + cinematic_sound(sound('sound/items/airhorn.ogg')) + flick("summary_selfdes",screen) //??? + special() + /* Intended usage. Nuke.Explosion() -> Cinematic(NUKE_BOOM,world) diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm new file mode 100644 index 0000000000..92f0250e4f --- /dev/null +++ b/code/datums/components/wet_floor.dm @@ -0,0 +1,171 @@ +/datum/component/wet_floor + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + var/highest_strength = TURF_DRY + var/lube_flags = NONE //why do we have this? + var/list/time_left_list //In deciseconds. + var/static/mutable_appearance/permafrost_overlay = mutable_appearance('icons/effects/water.dmi', "ice_floor") + var/static/mutable_appearance/ice_overlay = mutable_appearance('icons/turf/overlays.dmi', "snowfloor") + var/static/mutable_appearance/water_overlay = mutable_appearance('icons/effects/water.dmi', "wet_floor_static") + var/static/mutable_appearance/generic_turf_overlay = mutable_appearance('icons/effects/water.dmi', "wet_static") + var/current_overlay + var/permanent = FALSE + +/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, argslist) + if(!newcomp) //We are getting passed the arguments of a would-be new component, but not a new component + add_wet(arglist(argslist)) + else //We are being passed in a full blown component + var/datum/component/wet_floor/WF = newcomp //Lets make an assumption + if(WF.gc()) //See if it's even valid, still. Also does LAZYLEN and stuff for us. + CRASH("Wet floor component tried to inherit another, but the other was able to garbage collect while being inherited! What a waste of time!") + return + for(var/i in WF.time_left_list) + add_wet(text2num(i), WF.time_left_list[i]) + +/datum/component/wet_floor/Initialize(strength, duration_minimum, duration_add, duration_maximum, permanent = FALSE) + if(!isopenturf(parent)) + . = COMPONENT_INCOMPATIBLE + CRASH("Wet floor component attempted to be applied to a non open turf!") + add_wet(strength, duration_minimum, duration_add, duration_maximum) + RegisterSignal(COMSIG_TURF_IS_WET, .proc/is_wet) + RegisterSignal(COMSIG_TURF_MAKE_DRY, .proc/dry) + if(!permanent) + START_PROCESSING(SSwet_floors, src) + if(gc()) + stack_trace("Warning: Wet floor component added and immediately deleted! What a waste of time!") + +/datum/component/wet_floor/Destroy() + STOP_PROCESSING(SSwet_floors, src) + var/turf/T = parent + qdel(T.GetComponent(/datum/component/slippery)) + if(istype(T)) //If this is false there is so many things wrong with it. + T.cut_overlay(current_overlay) + else + stack_trace("Warning: Wet floor component wasn't on a turf when being destroyed! This is really bad!") + return ..() + +/datum/component/wet_floor/proc/update_overlay() + var/intended + if(!istype(parent, /turf/open/floor)) + intended = generic_turf_overlay + else + switch(highest_strength) + if(TURF_WET_PERMAFROST) + intended = permafrost_overlay + if(TURF_WET_ICE) + intended = ice_overlay + else + intended = water_overlay + if(current_overlay != intended) + var/turf/T = parent + T.cut_overlay(current_overlay) + T.add_overlay(intended) + current_overlay = intended + +/datum/component/wet_floor/proc/AfterSlip(mob/living/L) + if(highest_strength == TURF_WET_LUBE) + L.confused = max(L.confused, 8) + +/datum/component/wet_floor/proc/update_flags() + var/intensity + lube_flags = NONE + switch(highest_strength) + if(TURF_WET_WATER) + intensity = 60 + lube_flags = NO_SLIP_WHEN_WALKING + if(TURF_WET_LUBE) + intensity = 80 + lube_flags = SLIDE | GALOSHES_DONT_HELP + if(TURF_WET_ICE) + intensity = 120 + lube_flags = SLIDE | GALOSHES_DONT_HELP + if(TURF_WET_PERMAFROST) + intensity = 120 + lube_flags = SLIDE_ICE | GALOSHES_DONT_HELP + else + qdel(parent.GetComponent(/datum/component/slippery)) + return + + var/datum/component/slippery/S = parent.LoadComponent(/datum/component/slippery, NONE, CALLBACK(src, .proc/AfterSlip)) + S.intensity = intensity + S.lube_flags = lube_flags + +/datum/component/wet_floor/proc/dry(strength = TURF_WET_WATER, immediate = FALSE, duration_decrease = INFINITY) + for(var/i in time_left_list) + if(text2num(i) <= strength) + time_left_list[i] = max(0, time_left_list[i] - duration_decrease) + if(immediate) + check() + +/datum/component/wet_floor/proc/max_time_left() + . = 0 + for(var/i in time_left_list) + . = max(., time_left_list[i]) + +/datum/component/wet_floor/process() + var/turf/open/T = parent + var/decrease = 0 + var/t = T.GetTemperature() + switch(t) + if(-INFINITY to T0C) + add_wet(TURF_WET_ICE, max_time_left()) //Water freezes into ice! + if(T0C to T0C + 100) + decrease = (T.air.temperature - T0C) //one ds per degree. + if(T0C + 100 to INFINITY) + decrease = INFINITY + if((is_wet() & TURF_WET_ICE) && t > T0C) //Ice melts into water! + for(var/obj/O in T.contents) + if(O.flags_2 & FROZEN_2) + O.make_unfrozen() + add_wet(TURF_WET_WATER, max_time_left()) + dry(TURF_WET_ICE) + dry(ALL, FALSE, decrease) + check() + +/datum/component/wet_floor/proc/update_strength() + highest_strength = 0 //Not bitflag. + for(var/i in time_left_list) + highest_strength = max(highest_strength, text2num(i)) + +/datum/component/wet_floor/proc/is_wet() + . = 0 + for(var/i in time_left_list) + . |= text2num(i) + +/datum/component/wet_floor/OnTransfer(datum/to_datum) + if(!isopenturf(to_datum)) + . = COMPONENT_INCOMPATIBLE + CRASH("Wet floor component attempted to be transferred to a non open turf!") + var/turf/O = parent + O.cut_overlay(current_overlay) + var/turf/T = to_datum + T.add_overlay(current_overlay) + +/datum/component/wet_floor/proc/add_wet(type, duration_minimum = 0, duration_add = 0, duration_maximum = MAXIMUM_WET_TIME) + var/static/list/allowed_types = list(TURF_WET_WATER, TURF_WET_LUBE, TURF_WET_ICE, TURF_WET_PERMAFROST) + if(!duration_minimum || duration_minimum < 0 || !type || !(type in allowed_types)) + return FALSE + var/time = 0 + if(LAZYACCESS(time_left_list, "[type]")) + time = CLAMP(LAZYACCESS(time_left_list, "[type]") + duration_add, duration_minimum, duration_maximum) + else + time = min(duration_minimum, duration_maximum) + LAZYSET(time_left_list, "[type]", time) + check(TRUE) + return TRUE + +/datum/component/wet_floor/proc/gc() + if(!LAZYLEN(time_left_list)) + qdel(src) + return TRUE + +/datum/component/wet_floor/proc/check(force_update = FALSE) + var/changed = FALSE + for(var/i in time_left_list) + if(time_left_list[i] <= 0) + time_left_list -= i + changed = TRUE + if(changed || force_update) + update_strength() + update_overlay() + update_flags() + gc() diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 45152ee8b4..fff0db4c92 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -813,11 +813,6 @@ if(!mind.assigned_role) mind.assigned_role = "Unassigned" //default -//XENO -/mob/living/carbon/alien/mind_initialize() - ..() - mind.special_role = ROLE_ALIEN - //AI /mob/living/silicon/ai/mind_initialize() ..() diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm index 4c39083c62..c2db1e8a37 100644 --- a/code/datums/mood_events/generic_negative_events.dm +++ b/code/datums/mood_events/generic_negative_events.dm @@ -84,7 +84,7 @@ mood_change = -3 /datum/mood_event/brain_damage/add_effects() - var/damage_message = pick_list_replacements("BRAIN_DAMAGE_FILE", "brain_damage") + var/damage_message = pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage") description = "Hurr durr... [damage_message]\n" /datum/mood_event/hulk //Entire duration of having the hulk mutation diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index f968f860b0..63b5ca81ba 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -501,19 +501,15 @@ if(((hand % 2) == 0)) var/obj/item/bodypart/L = itemUser.newBodyPart("r_arm", FALSE, FALSE) L.attach_limb(itemUser) - itemUser.put_in_r_hand(newRod) + itemUser.put_in_hand(newRod, hand, forced = TRUE) else var/obj/item/bodypart/L = itemUser.newBodyPart("l_arm", FALSE, FALSE) L.attach_limb(itemUser) - itemUser.put_in_l_hand(newRod) + itemUser.put_in_hand(newRod, hand, forced = TRUE) to_chat(itemUser, "Your arm suddenly grows back with the Rod of Asclepius still attached!") else //Otherwise get rid of whatever else is in their hand and return the rod to said hand - itemUser.dropItemToGround(itemUser.get_item_for_held_index(hand)) - if(((hand % 2) == 0)) - itemUser.put_in_r_hand(newRod) - else - itemUser.put_in_l_hand(newRod) + itemUser.put_in_hand(newRod, hand, forced = TRUE) to_chat(itemUser, "The Rod of Asclepius suddenly grows back out of your arm!") //Because a servant of medicines stops at nothing to help others, lets keep them on their toes and give them an additional boost. if(itemUser.health < itemUser.maxHealth) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index b966fe2905..ef04bf9620 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -304,6 +304,9 @@ if(AM && isturf(AM.loc)) step(AM, turn(AM.dir, 180)) +/atom/proc/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube) + return + //returns the mob's dna info as a list, to be inserted in an object's blood_DNA list /mob/living/proc/get_blood_dna_list() if(get_blood_id() != "blood") @@ -354,9 +357,6 @@ /atom/proc/handle_fall() return -/atom/proc/handle_slip() - return - /atom/proc/singularity_act() return diff --git a/code/game/gamemodes/clown_ops/bananium_bomb.dm b/code/game/gamemodes/clown_ops/bananium_bomb.dm new file mode 100644 index 0000000000..86db2e94f9 --- /dev/null +++ b/code/game/gamemodes/clown_ops/bananium_bomb.dm @@ -0,0 +1,59 @@ +/obj/machinery/nuclearbomb/syndicate/bananium + name = "bananium fission explosive" + desc = "You probably shouldn't stick around to see if this is armed." + icon = 'icons/obj/machines/nuke.dmi' + icon_state = "bananiumbomb_base" + +/obj/machinery/nuclearbomb/syndicate/bananium/update_icon() + if(deconstruction_state == NUKESTATE_INTACT) + switch(get_nuke_state()) + if(NUKE_OFF_LOCKED, NUKE_OFF_UNLOCKED) + icon_state = "bananiumbomb_base" + update_icon_interior() + update_icon_lights() + if(NUKE_ON_TIMING) + cut_overlays() + icon_state = "bananiumbomb_timing" + if(NUKE_ON_EXPLODING) + cut_overlays() + icon_state = "bananiumbomb_exploding" + else + icon_state = "bananiumbomb_base" + update_icon_interior() + update_icon_lights() + +/obj/machinery/nuclearbomb/syndicate/bananium/get_cinematic_type(off_station) + switch(off_station) + if(0) + return CINEMATIC_NUKE_CLOWNOP + if(1) + return CINEMATIC_NUKE_MISS + if(2) + return CINEMATIC_NUKE_FAKE //it is farther away, so just a bikehorn instead of an airhorn + return CINEMATIC_NUKE_FAKE + +/obj/machinery/nuclearbomb/syndicate/bananium/really_actually_explode(off_station) + Cinematic(get_cinematic_type(off_station), world) + for(var/mob/living/carbon/human/H in GLOB.carbon_list) + var/turf/T = get_turf(H) + if(!T || T.z != z) + continue + H.Stun(10) + var/obj/item/clothing/C + if(!H.w_uniform || H.dropItemToGround(H.w_uniform)) + C = new /obj/item/clothing/under/rank/clown(H) + C.flags_1 |= NODROP_1 //mwahaha + H.equip_to_slot_or_del(C, slot_w_uniform) + + if(!H.shoes || H.dropItemToGround(H.shoes)) + C = new /obj/item/clothing/shoes/clown_shoes(H) + C.flags_1 |= NODROP_1 + H.equip_to_slot_or_del(C, slot_shoes) + + if(!H.wear_mask || H.dropItemToGround(H.wear_mask)) + C = new /obj/item/clothing/mask/gas/clown_hat(H) + C.flags_1 |= NODROP_1 + H.equip_to_slot_or_del(C, slot_wear_mask) + + H.dna.add_mutation(CLOWNMUT) + H.gain_trauma(/datum/brain_trauma/mild/phobia, TRAUMA_RESILIENCE_LOBOTOMY, "clowns") //MWA HA HA diff --git a/code/game/gamemodes/clown_ops/clown_ops.dm b/code/game/gamemodes/clown_ops/clown_ops.dm new file mode 100644 index 0000000000..6b96cefa13 --- /dev/null +++ b/code/game/gamemodes/clown_ops/clown_ops.dm @@ -0,0 +1,66 @@ +/datum/game_mode/nuclear/clown_ops + name = "clown ops" + config_tag = "clownops" + + announce_span = "danger" + announce_text = "Clown empire forces are approaching the station in an attempt to HONK it!\n\ + Operatives: Secure the nuclear authentication disk and use your bananium fission explosive to HONK the station.\n\ + Crew: Defend the nuclear authentication disk and ensure that it leaves with you on the emergency shuttle." + + operative_antag_datum_type = /datum/antagonist/nukeop/clownop + leader_antag_datum_type = /datum/antagonist/nukeop/leader/clownop + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/nuclear/clown_ops/pre_setup() + . = ..() + if(.) + for(var/obj/machinery/nuclearbomb/syndicate/S in GLOB.nuke_list) + var/turf/T = get_turf(S) + if(T) + qdel(S) + new /obj/machinery/nuclearbomb/syndicate/bananium(T) + for(var/V in pre_nukeops) + var/datum/mind/the_op = V + the_op.assigned_role = "Clown Operative" + the_op.special_role = "Clown Operative" + +/datum/game_mode/nuclear/clown_ops/generate_report() + return "One of Central Command's trading routes was recently disrupted by a raid carried out by the Gorlex Marauders. They seemed to only be after one ship - a highly-sensitive \ + transport containing a bananium fission explosive, although it is useless without the proper code and authorization disk. While the code was likely found in minutes, the only disk that \ + can activate this explosive is on your station. Ensure that it is protected at all times, and remain alert for possible intruders." + + +/datum/outfit/syndicate/clownop + name = "Clown Operative - Basic" + uniform = /obj/item/clothing/under/syndicate + shoes = /obj/item/clothing/shoes/clown_shoes/combat + mask = /obj/item/clothing/mask/gas/clown_hat + gloves = /obj/item/clothing/gloves/combat + back = /obj/item/storage/backpack/clown + ears = /obj/item/device/radio/headset/syndicate/alt + l_pocket = /obj/item/pinpointer/nuke/syndicate + r_pocket = /obj/item/bikehorn + id = /obj/item/card/id/syndicate + backpack_contents = list(/obj/item/storage/box/syndie=1,\ + /obj/item/kitchen/knife/combat/survival, + /obj/item/reagent_containers/spray/waterflower/lube) + implants = list(/obj/item/implant/sad_trombone) + + uplink_type = /obj/item/device/radio/uplink/clownop + +/datum/outfit/syndicate/clownop/no_crystals + tc = 0 + +/datum/outfit/syndicate/clownop/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + H.dna.add_mutation(CLOWNMUT) + +/datum/outfit/syndicate/clownop/leader + name = "Clown Operative Leader - Basic" + id = /obj/item/card/id/syndicate/nuke_leader + r_hand = /obj/item/device/nuclear_challenge/clownops + command_radio = TRUE diff --git a/code/game/gamemodes/clown_ops/clown_weapons.dm b/code/game/gamemodes/clown_ops/clown_weapons.dm new file mode 100644 index 0000000000..3b74d796be --- /dev/null +++ b/code/game/gamemodes/clown_ops/clown_weapons.dm @@ -0,0 +1,298 @@ +/obj/item/reagent_containers/spray/waterflower/lube + name = "water flower" + desc = "A seemingly innocent sunflower...with a twist. A slippery twist." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "sunflower" + item_state = "sunflower" + amount_per_transfer_from_this = 3 + spray_range = 1 + stream_range = 1 + volume = 30 + list_reagents = list("lube" = 30) + +//COMBAT CLOWN SHOES +//Clown shoes with combat stats and noslip. Of course they still squeek. +/obj/item/clothing/shoes/clown_shoes/combat + name = "combat clown shoes" + desc = "advanced clown shoes that protect the wearer and render them nearly immune to slipping on their own peels. They also squeek at 100% capacity." + flags_1 = NOSLIP_1 + slowdown = SHOES_SLOWDOWN + armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) + strip_delay = 70 + resistance_flags = NONE + permeability_coefficient = 0.05 + pockets = /obj/item/storage/internal/pocket/shoes + +//The super annoying version +/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat + name = "mk-honk combat shoes" + desc = "The culmination of years of clown combat research, these shoes leave a trail of chaos in their wake. They will slowly recharge themselves over time, or can be manually charged with bananium." + slowdown = SHOES_SLOWDOWN + armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) + strip_delay = 70 + resistance_flags = NONE + permeability_coefficient = 0.05 + pockets = /obj/item/storage/internal/pocket/shoes + always_noslip = TRUE + var/max_recharge = 3000 //30 peels worth + var/recharge_rate = 34 //about 1/3 of a peel per tick + +/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat/Initialize() + . = ..() + GET_COMPONENT(bananium, /datum/component/material_container) + bananium.insert_amount(max_recharge, MAT_BANANIUM) + START_PROCESSING(SSobj, src) + +/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat/process() + GET_COMPONENT(bananium, /datum/component/material_container) + var/bananium_amount = bananium.amount(MAT_BANANIUM) + if(bananium_amount < max_recharge) + bananium.insert_amount(min(recharge_rate, max_recharge - bananium_amount), MAT_BANANIUM) + +/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat/attack_self(mob/user) + ui_action_click(user) + +//BANANIUM SWORD + +/obj/item/melee/transforming/energy/sword/bananium + name = "bananium sword" + desc = "An elegant weapon, for a more civilized age." + force = 0 + throwforce = 0 + force_on = 0 + throwforce_on = 0 + hitsound = null + attack_verb_on = list("slipped") + clumsy_check = FALSE + sharpness = IS_BLUNT + item_color = "yellow" + heat = 0 + light_color = "#ffff00" + var/next_trombone_allowed = 0 + +/obj/item/melee/transforming/energy/sword/bananium/Initialize() + . = ..() + AddComponent(/datum/component/slippery, 60, GALOSHES_DONT_HELP) + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.enabled = active + +/obj/item/melee/transforming/energy/sword/bananium/attack(mob/living/M, mob/living/user) + ..() + if(active) + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.Slip(M) + +/obj/item/melee/transforming/energy/sword/bananium/throw_impact(atom/hit_atom, throwingdatum) + . = ..() + if(active) + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.Slip(hit_atom) + +/obj/item/melee/transforming/energy/sword/bananium/attackby(obj/item/I, mob/living/user, params) + if((world.time > next_trombone_allowed) && istype(I, /obj/item/melee/transforming/energy/sword/bananium)) + next_trombone_allowed = world.time + 50 + to_chat(user, "You slap the two swords together. Sadly, they do not seem to fit.") + playsound(src, 'sound/misc/sadtrombone.ogg', 50) + return TRUE + return ..() + +/obj/item/melee/transforming/energy/sword/bananium/transform_weapon(mob/living/user, supress_message_text) + ..() + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.enabled = active + +/obj/item/melee/transforming/energy/sword/bananium/ignition_effect(atom/A, mob/user) + return "" + +/obj/item/melee/transforming/energy/sword/bananium/suicide_act(mob/user) + if(!active) + transform_weapon(user, TRUE) + user.visible_message("[user] is [pick("slitting [user.p_their()] stomach open with", "falling on")] [src]! It looks like [user.p_theyre()] trying to commit seppuku, but the blade slips off of them harmlessly!") + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.Slip(user) + return SHAME + +//BANANIUM SHIELD + +/obj/item/shield/energy/bananium + name = "bananium energy shield" + desc = "A shield that stops most melee attacks, protects user from almost all energy projectiles, and can be thrown to slip opponents." + throw_speed = 1 + clumsy_check = 0 + base_icon_state = "bananaeshield" + force = 0 + throwforce = 0 + throw_range = 5 + on_force = 0 + on_throwforce = 0 + on_throw_speed = 1 + +/obj/item/shield/energy/bananium/Initialize() + . = ..() + AddComponent(/datum/component/slippery, 60, GALOSHES_DONT_HELP) + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.enabled = active + +/obj/item/shield/energy/bananium/attack_self(mob/living/carbon/human/user) + ..() + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.enabled = active + +/obj/item/shield/energy/bananium/throw_at(atom/target, range, speed, mob/thrower, spin=1) + if(active) + if(iscarbon(thrower)) + var/mob/living/carbon/C = thrower + C.throw_mode_on() //so they can catch it on the return. + return ..() + +/obj/item/shield/energy/bananium/throw_impact(atom/hit_atom) + if(active) + var/caught = hit_atom.hitby(src, 0, 0) + if(iscarbon(hit_atom) && !caught)//if they are a carbon and they didn't catch it + GET_COMPONENT(slipper, /datum/component/slippery) + slipper.Slip(hit_atom) + if(thrownby && !caught) + throw_at(thrownby, throw_range+2, throw_speed, null, 1) + else + return ..() + + +//BOMBANANA + +/obj/item/reagent_containers/food/snacks/grown/banana/bombanana + trash = /obj/item/grown/bananapeel/bombanana + bitesize = 1 + customfoodfilling = FALSE + seed = null + tastes = list("explosives" = 10) + list_reagents = list("vitamin" = 1) + +/obj/item/grown/bananapeel/bombanana + desc = "A peel from a banana. Why is it beeping?" + seed = null + var/det_time = 50 + var/obj/item/grenade/syndieminibomb/bomb + +/obj/item/grown/bananapeel/bombanana/Initialize() + . = ..() + AddComponent(/datum/component/slippery, det_time) + bomb = new /obj/item/grenade/syndieminibomb(src) + bomb.det_time = det_time + if(iscarbon(loc)) + to_chat(loc, "[src] begins to beep.") + var/mob/living/carbon/C = loc + C.throw_mode_on() + bomb.preprime(loc, null, FALSE) + +/obj/item/grown/bananapeel/bombanana/Destroy() + . = ..() + QDEL_NULL(bomb) + +/obj/item/grown/bananapeel/bombanana/suicide_act(mob/user) + user.visible_message("[user] is deliberately slipping on the [src.name]! It looks like \he's trying to commit suicide.") + playsound(loc, 'sound/misc/slip.ogg', 50, 1, -1) + bomb.preprime(user, 0, FALSE) + return (BRUTELOSS) + +//TEARSTACHE GRENADE + +/obj/item/grenade/chem_grenade/teargas/moustache + name = "tear-stache grenade" + desc = "A handsomely-attired teargas grenade." + icon_state = "moustacheg" + clumsy_check = GRENADE_NONCLUMSY_FUMBLE + +/obj/item/grenade/chem_grenade/teargas/moustache/prime() + var/myloc = get_turf(src) + . = ..() + for(var/mob/living/carbon/M in view(6, myloc)) + if(!istype(M.wear_mask, /obj/item/clothing/mask/gas/clown_hat) && !istype(M.wear_mask, /obj/item/clothing/mask/gas/mime) ) + if(!M.wear_mask || M.dropItemToGround(M.wear_mask)) + var/obj/item/clothing/mask/fakemoustache/sticky/the_stash = new /obj/item/clothing/mask/fakemoustache/sticky() + M.equip_to_slot_or_del(the_stash, slot_wear_mask, TRUE, TRUE, TRUE, TRUE) + +/obj/item/clothing/mask/fakemoustache/sticky + var/unstick_time = 600 + +/obj/item/clothing/mask/fakemoustache/sticky/Initialize() + . = ..() + flags_1 |= NODROP_1 + addtimer(CALLBACK(src, .proc/unstick), unstick_time) + +/obj/item/clothing/mask/fakemoustache/sticky/proc/unstick() + flags_1 &= ~NODROP_1 + +//DARK H.O.N.K. AND CLOWN MECH WEAPONS + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana + name = "bombanana mortar" + desc = "Equipment for clown exosuits. Launches exploding banana peels." + icon_state = "mecha_bananamrtr" + projectile = /obj/item/grown/bananapeel/bombanana + projectiles = 8 + projectile_energy_cost = 1000 + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana/can_attach(obj/mecha/combat/honker/M) + if(..()) + if(istype(M)) + return TRUE + return FALSE + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache + name = "\improper HONKeR-6 grenade launcher" + desc = "A weapon for combat exosuits. Launches primed tear-stache grenades." + icon_state = "mecha_grenadelnchr" + projectile = /obj/item/grenade/chem_grenade/teargas/moustache + fire_sound = 'sound/weapons/grenadelaunch.ogg' + projectiles = 6 + missile_speed = 1.5 + projectile_energy_cost = 800 + equip_cooldown = 60 + det_time = 20 + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache/can_attach(obj/mecha/combat/honker/M) + if(..()) + if(istype(M)) + return TRUE + return FALSE + +/obj/mecha/combat/honker/dark + desc = "Produced by \"Tyranny of Honk, INC\", this exosuit is designed as heavy clown-support. This one has been painted black for maximum fun. HONK!" + name = "\improper Dark H.O.N.K" + icon_state = "darkhonker" + max_integrity = 300 + deflect_chance = 15 + armor = list("melee" = 40, "bullet" = 40, "laser" = 50, "energy" = 35, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + max_temperature = 35000 + operation_req_access = list(ACCESS_SYNDICATE) + wreckage = /obj/structure/mecha_wreckage/honker/dark + max_equip = 3 + +/obj/mecha/combat/honker/dark/GrantActions(mob/living/user, human_occupant = 0) + ..() + thrusters_action.Grant(user, src) + + +/obj/mecha/combat/honker/dark/RemoveActions(mob/living/user, human_occupant = 0) + ..() + thrusters_action.Remove(user) + +/obj/mecha/combat/honker/dark/add_cell(obj/item/stock_parts/cell/C) + if(C) + C.forceMove(src) + cell = C + return + cell = new /obj/item/stock_parts/cell/hyper(src) + +/obj/mecha/combat/honker/dark/loaded/Initialize() + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/honker() + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/banana_mortar/bombanana()//Needed more offensive weapons. + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/tearstache()//The mousetrap mortar was not up-to-snuff. + ME.attach(src) + +/obj/structure/mecha_wreckage/honker/dark + name = "\improper Dark H.O.N.K wreckage" + icon_state = "darkhonker-broken" diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index c6857cd222..11e4815722 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -19,6 +19,9 @@ var/datum/team/nuclear/nuke_team + var/operative_antag_datum_type = /datum/antagonist/nukeop + var/leader_antag_datum_type = /datum/antagonist/nukeop/leader + /datum/game_mode/nuclear/pre_setup() var/n_agents = min(round(num_players() / 10), antag_candidates.len, agents_possible) if(n_agents >= required_enemies) @@ -37,12 +40,12 @@ /datum/game_mode/nuclear/post_setup() //Assign leader var/datum/mind/leader_mind = pre_nukeops[1] - var/datum/antagonist/nukeop/L = leader_mind.add_antag_datum(/datum/antagonist/nukeop/leader) + var/datum/antagonist/nukeop/L = leader_mind.add_antag_datum(leader_antag_datum_type) nuke_team = L.nuke_team //Assign the remaining operatives for(var/i = 2 to pre_nukeops.len) var/datum/mind/nuke_mind = pre_nukeops[i] - nuke_mind.add_antag_datum(/datum/antagonist/nukeop,nuke_team) + nuke_mind.add_antag_datum(operative_antag_datum_type) return ..() /datum/game_mode/nuclear/OnNukeExplosion(off_station) @@ -131,6 +134,7 @@ var/tc = 25 var/command_radio = FALSE + var/uplink_type = /obj/item/device/radio/uplink/nuclear /datum/outfit/syndicate/leader @@ -150,7 +154,7 @@ R.command = TRUE if(tc) - var/obj/item/device/radio/uplink/nuclear/U = new(H, H.key, tc) + var/obj/item/device/radio/uplink/U = new uplink_type(H, H.key, tc) H.equip_to_slot_or_del(U, slot_in_backpack) var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(H) diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index c8202b4e10..37efa70b33 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -76,6 +76,11 @@ var/wall_turret_direction //The turret will try to shoot from a turf in that direction when in a wall + var/manual_control = FALSE // + var/datum/action/turret_quit/quit_action + var/datum/action/turret_toggle/toggle_action + var/mob/remote_controller + /obj/machinery/porta_turret/Initialize() . = ..() if(!base) @@ -154,6 +159,7 @@ cp = null QDEL_NULL(stored_gun) QDEL_NULL(spark_system) + remove_control() return ..() @@ -179,6 +185,14 @@ dat += "Neutralize All Non-Security and Non-Command Personnel: [stun_all ? "Yes" : "No"]
" dat += "Neutralize All Unidentified Life Signs: [check_anomalies ? "Yes" : "No"]
" dat += "Neutralize All Non-Loyalty Implanted Personnel: [shoot_unloyal ? "Yes" : "No"]
" + if(issilicon(user)) + if(!manual_control) + var/mob/living/silicon/S = user + if(S.hack_software) + dat += "Assume direct control : Manual Control
" + else + dat += "Warning! Remote control protocol enabled.
" + var/datum/browser/popup = new(user, "autosec", "Automatic Portable Turret Installation", 300, 300) popup.set_content(dat) @@ -212,6 +226,9 @@ check_anomalies = !check_anomalies if("checkloyal") shoot_unloyal = !shoot_unloyal + if("manual") + if(issilicon(usr) && !manual_control) + give_control(usr) interact(usr) /obj/machinery/porta_turret/power_change() @@ -359,6 +376,8 @@ popDown() return + if(manual_control) + return var/list/targets = list() var/static/things_to_scan = typecacheof(list(/mob/living, /obj/mecha)) @@ -566,6 +585,66 @@ src.mode = mode power_change() + +/datum/action/turret_toggle + name = "Toggle Mode" + icon_icon = 'icons/mob/actions/actions_mecha.dmi' + button_icon_state = "mech_cycle_equip_off" + +/datum/action/turret_toggle/Trigger() + var/obj/machinery/porta_turret/P = target + if(!istype(P)) + return + P.setState(P.on,!P.mode) + +/datum/action/turret_quit + name = "Release Control" + icon_icon = 'icons/mob/actions/actions_mecha.dmi' + button_icon_state = "mech_eject" + +/datum/action/turret_quit/Trigger() + var/obj/machinery/porta_turret/P = target + if(!istype(P)) + return + P.remove_control(owner) + +/obj/machinery/porta_turret/proc/give_control(mob/A) + if(manual_control) + return FALSE + remote_controller = A + if(!quit_action) + quit_action = new(src) + quit_action.Grant(remote_controller) + if(!toggle_action) + toggle_action = new(src) + toggle_action.Grant(remote_controller) + remote_controller.reset_perspective(src) + remote_controller.click_intercept = src + manual_control = TRUE + always_up = TRUE + popUp() + return TRUE + +/obj/machinery/porta_turret/proc/remove_control() + if(!manual_control) + return FALSE + if(remote_controller) + quit_action.Remove(remote_controller) + toggle_action.Remove(remote_controller) + remote_controller.click_intercept = null + remote_controller.reset_perspective() + always_up = initial(always_up) + manual_control = FALSE + remote_controller = null + return TRUE + +/obj/machinery/porta_turret/proc/InterceptClickOn(mob/living/caller, params, atom/A) + if(!manual_control) + return FALSE + add_logs(caller,A,"fired with manual turret control at") + target(A) + return TRUE + /obj/machinery/porta_turret/syndicate installation = null always_up = 1 diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 06884c59b5..97b8a95277 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -11,7 +11,7 @@ var/equip_ready = 1 //whether the equipment is ready for use. (or deactivated/activated for static stuff) var/energy_drain = 0 var/obj/mecha/chassis = null - var/range = MELEE //bitflags + var/range = MELEE //bitFflags var/salvageable = 1 var/selectable = 1 // Set to 0 for passive equipment such as mining scanner or armor plates var/pacifist_safe = TRUE //Controls if equipment can be used to attack by a pacifist. @@ -94,9 +94,16 @@ chassis.use_power(energy_drain) . = do_after(chassis.occupant, equip_cooldown, target=target) set_ready_state(1) - if(!chassis || chassis.loc != C || src != chassis.selected) + if(!chassis || chassis.loc != C || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir)) return 0 +/obj/item/mecha_parts/mecha_equipment/proc/do_after_mecha(atom/target, delay) + if(!chassis) + return + var/C = chassis.loc + . = do_after(chassis.occupant, delay, target=target) + if(!chassis || chassis.loc != C || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir)) + return 0 /obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M) if(M.equipment.lenYou hear drilling.") if(do_after_cooldown(target)) + set_ready_state(FALSE) + log_message("Started drilling [target]") if(isturf(target)) var/turf/T = target T.drill_act(src) - else - log_message("Drilled through [target]") + set_ready_state(TRUE) + return + while(do_after_mecha(target, drill_delay)) if(isliving(target)) - if(istype(src , /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill)) - drill_mob(target, chassis.occupant, 120) - else - drill_mob(target, chassis.occupant) + drill_mob(target, chassis.occupant) + playsound(src,'sound/weapons/drill.ogg',40,1) + else if(isobj(target)) + var/obj/O = target + O.take_damage(15, BRUTE, 0, FALSE, get_dir(chassis, target)) + playsound(src,'sound/weapons/drill.ogg',40,1) else - target.ex_act(EXPLODE_HEAVY) + set_ready_state(TRUE) + return + set_ready_state(TRUE) /turf/proc/drill_act(obj/item/mecha_parts/mecha_equipment/drill/drill) return +/turf/closed/wall/drill_act(obj/item/mecha_parts/mecha_equipment/drill/drill) + if(drill.do_after_mecha(src, 60 / drill.drill_level)) + drill.log_message("Drilled through [src]") + dismantle_wall(TRUE, FALSE) + /turf/closed/wall/r_wall/drill_act(obj/item/mecha_parts/mecha_equipment/drill/drill) - if(istype(drill, /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill)) - if(drill.do_after_cooldown(src))//To slow down how fast mechs can drill through the station + if(drill.drill_level >= DRILL_HARDENED) + if(drill.do_after_mecha(src, 120 / drill.drill_level)) drill.log_message("Drilled through [src]") - ex_act(EXPLODE_LIGHT) + dismantle_wall(TRUE, FALSE) else drill.occupant_message("[src] is too durable to drill through.") @@ -91,30 +108,40 @@ GET_COMPONENT_FROM(butchering, /datum/component/butchering, src) butchering.butchering_enabled = FALSE -/obj/item/mecha_parts/mecha_equipment/drill/proc/drill_mob(mob/living/target, mob/user, var/drill_damage=80) - target.visible_message("[chassis] drills [target] with [src].", \ - "[chassis] drills [target] with [src].") - add_logs(user, target, "attacked", "[name]", "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") - if(target.stat == DEAD) +/obj/item/mecha_parts/mecha_equipment/drill/proc/drill_mob(mob/living/target, mob/user) + target.visible_message("[chassis] is drilling [target] with [src]!", \ + "[chassis] is drilling you with [src]!") + add_logs(user, target, "drilled", "[name]", "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") + if(target.stat == DEAD && target.getBruteLoss() >= 200) add_logs(user, target, "gibbed", name) - if(target.butcher_results.len || target.guaranteed_butcher_results.len) + if(LAZYLEN(target.butcher_results) || LAZYLEN(target.guaranteed_butcher_results)) GET_COMPONENT_FROM(butchering, /datum/component/butchering, src) butchering.Butcher(chassis, target) else target.gib() else - target.take_bodypart_damage(drill_damage) + //drill makes a hole + var/obj/item/bodypart/target_part = target.get_bodypart(ran_zone("chest")) + target.apply_damage(10, BRUTE, "chest", target.run_armor_check(target_part, "melee")) - if(target) - target.Unconscious(200) - target.updatehealth() + //blood splatters + var/splatter_dir = get_dir(chassis, target) + if(isalien(target)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target.drop_location(), splatter_dir) + else + new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.drop_location(), splatter_dir) + //organs go everywhere + if(target_part && prob(10 * drill_level)) + target_part.dismember(BRUTE) /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill name = "diamond-tipped exosuit drill" desc = "Equipment for engineering and combat exosuits. This is an upgraded version of the drill that'll pierce the heavens!" icon_state = "mecha_diamond_drill" equip_cooldown = 10 + drill_delay = 4 + drill_level = DRILL_HARDENED force = 15 @@ -140,3 +167,6 @@ return scanning_time = world.time + equip_cooldown mineral_scan_pulse(get_turf(src)) + +#undef DRILL_BASIC +#undef DRILL_HARDENED \ No newline at end of file diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index a5e435b063..b278ac25c1 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -138,16 +138,16 @@ return ..() /obj/effect/portal/attack_ghost(mob/dead/observer/O) - if(!teleport(O)) + if(!teleport(O, TRUE)) return ..() -/obj/effect/portal/proc/teleport(atom/movable/M) - if(!istype(M) || istype(M, /obj/effect) || (ismecha(M) && !mech_sized) || (!isobj(M) && !ismob(M))) //Things that shouldn't teleport. +/obj/effect/portal/proc/teleport(atom/movable/M, force = FALSE) + if(!force && (!istype(M) || istype(M, /obj/effect) || (ismecha(M) && !mech_sized) || (!isobj(M) && !ismob(M)))) //Things that shouldn't teleport. return var/turf/real_target = get_link_target_turf() if(!istype(real_target)) return FALSE - if(!ismecha(M) && !istype(M, /obj/item/projectile) && M.anchored && !allow_anchored) + if(!force && (!ismecha(M) && !istype(M, /obj/item/projectile) && M.anchored && !allow_anchored)) return if(ismegafauna(M)) message_admins("[M] has used a portal at [ADMIN_COORDJMP(src)] made by [usr].") diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index a26301693f..82c307d8e5 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -599,8 +599,8 @@ /obj/item/stock_parts/capacitor = 6) /obj/item/circuitboard/machine/chem_dispenser - name = "Portable Chem Dispenser (Machine Board)" - build_path = /obj/machinery/chem_dispenser/constructable + name = "Chem Dispenser (Machine Board)" + build_path = /obj/machinery/chem_dispenser req_components = list( /obj/item/stock_parts/matter_bin = 2, /obj/item/stock_parts/capacitor = 1, diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index ecbb4de4a0..eccd3c657d 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -202,6 +202,6 @@ var/turf/T = get_turf(loc) if(isopenturf(T)) var/turf/open/theturf = T - theturf.MakeSlippery(TURF_WET_WATER, min_wet_time = 10, wet_time_to_add = 5) + theturf.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS) user.visible_message("[user] empties out \the [src] onto the floor using the release valve.", "You quietly empty out \the [src] using its release valve.") diff --git a/code/game/objects/items/grenades/grenade.dm b/code/game/objects/items/grenades/grenade.dm index d3b7a19ba6..93349a6b3b 100644 --- a/code/game/objects/items/grenades/grenade.dm +++ b/code/game/objects/items/grenades/grenade.dm @@ -16,6 +16,7 @@ var/active = 0 var/det_time = 50 var/display_timer = 1 + var/clumsy_check = GRENADE_CLUMSY_FUMBLE /obj/item/grenade/suicide_act(mob/living/carbon/user) user.visible_message("[user] primes [src], then eats it! It looks like [user.p_theyre()] trying to commit suicide!") @@ -32,8 +33,14 @@ qdel(src) /obj/item/grenade/proc/clown_check(mob/living/carbon/human/user) - if(user.has_trait(TRAIT_CLUMSY) && prob(50)) - to_chat(user, "Huh? How does this thing work?") + var/clumsy = user.has_trait(TRAIT_CLUMSY) + if(clumsy && (clumsy_check == GRENADE_CLUMSY_FUMBLE)) + if(prob(50)) + to_chat(user, "Huh? How does this thing work?") + preprime(user, 5, FALSE) + return FALSE + else if(!clumsy && (clumsy_check == GRENADE_NONCLUMSY_FUMBLE)) + to_chat(user, "You pull the pin on [src]. Attached to it is a pink ribbon that says, \"HONK\"") preprime(user, 5, FALSE) return FALSE return TRUE diff --git a/code/game/objects/items/grenades/syndieminibomb.dm b/code/game/objects/items/grenades/syndieminibomb.dm index d63216a2a0..b05da3ab0a 100644 --- a/code/game/objects/items/grenades/syndieminibomb.dm +++ b/code/game/objects/items/grenades/syndieminibomb.dm @@ -43,8 +43,7 @@ for(var/turf/T in view(freeze_range,loc)) if(isfloorturf(T)) var/turf/open/floor/F = T - F.wet = TURF_WET_PERMAFROST - addtimer(CALLBACK(F, /turf/open/floor.proc/MakeDry, TURF_WET_PERMAFROST), rand(3000, 3100)) + F.MakeSlippery(TURF_WET_PERMAFROST, 6 MINUTES) for(var/mob/living/carbon/L in T) L.adjustStaminaLoss(stamina_damage) L.adjust_bodytemperature(-230) diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index 74ac616646..c25a9a2852 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -17,6 +17,8 @@ return ..() /obj/item/melee/transforming/energy/suicide_act(mob/user) + if(!active) + transform_weapon(user, TRUE) user.visible_message("[user] is [pick("slitting [user.p_their()] stomach open with", "falling on")] [src]! It looks like [user.p_theyre()] trying to commit seppuku!") return (BRUTELOSS|FIRELOSS) diff --git a/code/game/objects/items/melee/transforming.dm b/code/game/objects/items/melee/transforming.dm index db8e791700..74eb048d8a 100644 --- a/code/game/objects/items/melee/transforming.dm +++ b/code/game/objects/items/melee/transforming.dm @@ -1,4 +1,5 @@ /obj/item/melee/transforming + sharpness = IS_SHARP var/active = FALSE var/force_on = 30 //force when active var/faction_bonus_force = 0 //Bonus force dealt against certain factions @@ -8,10 +9,10 @@ var/list/attack_verb_on = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") var/list/attack_verb_off = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") w_class = WEIGHT_CLASS_SMALL - sharpness = IS_SHARP var/bonus_active = FALSE //If the faction damage bonus is active var/list/nemesis_factions //Any mob with a faction that exists in this list will take bonus damage/effects var/w_class_on = WEIGHT_CLASS_BULKY + var/clumsy_check = TRUE /obj/item/melee/transforming/Initialize() . = ..() @@ -81,6 +82,6 @@ to_chat(user, "[src] [active ? "is now active":"can now be concealed"].") /obj/item/melee/transforming/proc/clumsy_transform_effect(mob/living/user) - if(user.has_trait(TRAIT_CLUMSY) && prob(50)) + if(clumsy_check && user.has_trait(TRAIT_CLUMSY) && prob(50)) to_chat(user, "You accidentally cut yourself with [src], like a doofus!") user.take_bodypart_damage(5,5) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 628ffe4de0..dd8d8f8a10 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -60,16 +60,24 @@ name = "energy combat shield" desc = "A shield that reflects almost all energy projectiles, but is useless against physical attacks. It can be retracted, expanded, and stored anywhere." icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "eshield0" // eshield1 for expanded lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + attack_verb = list("shoved", "bashed") + throw_range = 5 force = 3 throwforce = 3 throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - attack_verb = list("shoved", "bashed") + var/base_icon_state = "eshield" // [base_icon_state]1 for expanded, [base_icon_state]0 for contracted + var/on_force = 10 + var/on_throwforce = 8 + var/on_throw_speed = 2 var/active = 0 + var/clumsy_check = TRUE + +/obj/item/shield/energy/Initialize() + . = ..() + icon_state = "[base_icon_state]0" /obj/item/shield/energy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) return 0 @@ -78,23 +86,23 @@ return (active) /obj/item/shield/energy/attack_self(mob/living/carbon/human/user) - if(user.has_trait(TRAIT_CLUMSY) && prob(50)) + if(clumsy_check && user.has_trait(TRAIT_CLUMSY) && prob(50)) to_chat(user, "You beat yourself in the head with [src].") user.take_bodypart_damage(5) active = !active - icon_state = "eshield[active]" + icon_state = "[base_icon_state][active]" if(active) - force = 10 - throwforce = 8 - throw_speed = 2 + force = on_force + throwforce = on_throwforce + throw_speed = on_throw_speed w_class = WEIGHT_CLASS_BULKY playsound(user, 'sound/weapons/saberon.ogg', 35, 1) to_chat(user, "[src] is now active.") else - force = 3 - throwforce = 3 - throw_speed = 3 + force = initial(force) + throwforce = initial(throwforce) + throw_speed = initial(throw_speed) w_class = WEIGHT_CLASS_TINY playsound(user, 'sound/weapons/saberoff.ogg', 35, 1) to_chat(user, "[src] can now be concealed.") diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index c4c85bc6a5..5b23ea6715 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -227,6 +227,14 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e tesla_zap(src, 3, power_bounced) addtimer(CALLBACK(src, .proc/reset_shocked), 10) +//The surgeon general warns that being buckled to certain objects recieving powerful shocks is greatly hazardous to your health +//Only tesla coils and grounding rods currently call this because mobs are already targeted over all other objects, but this might be useful for more things later. +/obj/proc/tesla_buckle_check(var/strength) + if(has_buckled_mobs()) + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + buckled_mob.electrocute_act((CLAMP(round(strength/400), 10, 90) + rand(-5, 5)), src, tesla_shock = 1) + /obj/proc/reset_shocked() obj_flags &= ~BEING_SHOCKED diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index dec4709550..b4c14953c5 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -7,11 +7,7 @@ anchored = TRUE icon = 'icons/turf/walls/wall.dmi' icon_state = "wall" - var/mineral = /obj/item/stack/sheet/metal - var/mineral_amount = 2 - var/walltype = /turf/closed/wall - var/girder_type = /obj/structure/girder/displaced - var/opening = FALSE + layer = CLOSED_TURF_LAYER density = TRUE opacity = 1 max_integrity = 100 @@ -28,6 +24,11 @@ smooth = SMOOTH_TRUE can_be_unanchored = FALSE CanAtmosPass = ATMOS_PASS_DENSITY + var/mineral = /obj/item/stack/sheet/metal + var/mineral_amount = 2 + var/walltype = /turf/closed/wall + var/girder_type = /obj/structure/girder/displaced + var/opening = FALSE /obj/structure/falsewall/Initialize() . = ..() diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index fbfc36cd97..3806c2411b 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -534,9 +534,6 @@ new/obj/structure/showcase/machinery/oldpod/used(drop_location()) return ..() - -#define PIRATE_NAMES_FILE "pirates.json" - /obj/effect/mob_spawn/human/pirate name = "space pirate sleeper" desc = "A cryo sleeper smelling faintly of rum." diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 3e6adb1aa8..3f47c7d7df 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -60,7 +60,7 @@ attack_hand(user) /obj/structure/table/attack_hand(mob/living/user) - if(user.pulling && isliving(user.pulling)) + if(Adjacent(user) && user.pulling && isliving(user.pulling)) var/mob/living/pushed_mob = user.pulling if(pushed_mob.buckled) to_chat(user, "[pushed_mob] is buckled to [pushed_mob.buckled]!") @@ -81,6 +81,9 @@ else ..() +/obj/structure/table/attack_tk() + return FALSE + /obj/structure/table/CanPass(atom/movable/mover, turf/target) if(istype(mover) && (mover.pass_flags & PASSTABLE)) return 1 diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 2213e56921..047408c760 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -247,7 +247,7 @@ soundloop.stop() if(isopenturf(loc)) var/turf/open/tile = loc - tile.MakeSlippery(TURF_WET_WATER, min_wet_time = 5, wet_time_to_add = 1) + tile.MakeSlippery(TURF_WET_WATER, min_wet_time = 5 SECONDS, wet_time_to_add = 1 SECONDS) /obj/machinery/shower/attackby(obj/item/I, mob/user, params) diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index e9276d777b..9b19a9d093 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -1,12 +1,16 @@ /turf/open var/slowdown = 0 //negative for faster, positive for slower - var/wet = 0 - var/wet_time = 0 // Time in seconds that this floor will be wet for. var/mutable_appearance/wet_overlay var/postdig_icon_change = FALSE var/postdig_icon var/list/archdrops + var/wet + +/turf/open/Initialize() + . = ..() + if(wet) + AddComponent(/datum/component/wet_floor, wet, INFINITY, 0, INFINITY, TRUE) /turf/open/indestructible name = "floor" @@ -164,11 +168,11 @@ for(var/mob/living/L in contents) if(L.bodytemperature <= 50) L.apply_status_effect(/datum/status_effect/freon) - MakeSlippery(TURF_WET_PERMAFROST, 5) + MakeSlippery(TURF_WET_PERMAFROST, 50) return 1 /turf/open/proc/water_vapor_gas_act() - MakeSlippery(TURF_WET_WATER, min_wet_time = 10, wet_time_to_add = 5) + MakeSlippery(TURF_WET_WATER, min_wet_time = 100, wet_time_to_add = 50) for(var/mob/living/simple_animal/slime/M in src) M.apply_water() @@ -177,7 +181,7 @@ for(var/obj/effect/O in src) if(is_cleanable(O)) qdel(O) - return 1 + return TRUE /turf/open/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube) if(C.movement_type & FLYING) @@ -224,132 +228,23 @@ /turf/open/copyTurf(turf/T) . = ..() - if(. && isopenturf(T) && wet_time) - var/turf/open/O = T - O.MakeSlippery(wet_setting = wet, wet_time_to_add = wet_time) //we're copied, copy how wet we are also + if(. && isopenturf(T)) + GET_COMPONENT(slip, /datum/component/wet_floor) + if(slip) + var/datum/component/wet_floor/WF = T.AddComponent(/datum/component/wet_floor) + WF.InheritComponent(slip) -/turf/open/proc/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0) // 1 = Water, 2 = Lube, 3 = Ice, 4 = Permafrost, 5 = Slide - wet_time = max(wet_time+wet_time_to_add, min_wet_time) - if(wet >= wet_setting) - return - wet = wet_setting - UpdateSlip() - if(wet_setting != TURF_DRY) - if(wet_overlay) - cut_overlay(wet_overlay) - else - wet_overlay = mutable_appearance() - var/turf/open/floor/F = src - if(istype(F)) - if(wet_setting == TURF_WET_PERMAFROST) - wet_overlay.icon = 'icons/effects/water.dmi' - wet_overlay.icon_state = "ice_floor" - else if(wet_setting == TURF_WET_ICE) - wet_overlay.icon = 'icons/turf/overlays.dmi' - wet_overlay.icon_state = "snowfloor" - else - wet_overlay.icon = 'icons/effects/water.dmi' - wet_overlay.icon_state = "wet_floor_static" - else - wet_overlay.icon = 'icons/effects/water.dmi' - wet_overlay.icon_state = "wet_static" - add_overlay(wet_overlay) - HandleWet() +/turf/open/proc/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0, max_wet_time = MAXIMUM_WET_TIME, permanent) + AddComponent(/datum/component/wet_floor, wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent) -/turf/open/proc/UpdateSlip() - var/intensity - var/lube_flags - switch(wet) - if(TURF_WET_WATER) - intensity = 60 - lube_flags = NO_SLIP_WHEN_WALKING - if(TURF_WET_LUBE) - intensity = 80 - lube_flags = SLIDE | GALOSHES_DONT_HELP - if(TURF_WET_ICE) - intensity = 120 - lube_flags = SLIDE | GALOSHES_DONT_HELP - if(TURF_WET_PERMAFROST) - intensity = 120 - lube_flags = SLIDE_ICE | GALOSHES_DONT_HELP - else - qdel(GetComponent(/datum/component/slippery)) - return - var/datum/component/slippery/S = LoadComponent(/datum/component/slippery, NONE, CALLBACK(src, .proc/AfterSlip)) - S.intensity = intensity - S.lube_flags = lube_flags - -/turf/open/proc/AfterSlip(mob/living/L) - if(wet == TURF_WET_LUBE) - L.confused = max(L.confused, 8) - -/turf/open/proc/MakeDry(wet_setting = TURF_WET_WATER) - if(wet > wet_setting || !wet) - return - spawn(rand(0,20)) - if(wet == TURF_WET_PERMAFROST) - wet = TURF_WET_ICE - else if(wet == TURF_WET_ICE) - wet = TURF_WET_WATER - else - wet = TURF_DRY - if(wet_overlay) - cut_overlay(wet_overlay) - UpdateSlip() - -/turf/open/proc/HandleWet() - if(!wet) - //It's possible for this handler to get called after all the wetness is - //cleared, so bail out if that is the case - return - if(!wet_time && wet < TURF_WET_ICE) - MakeDry(TURF_WET_ICE) - if(wet_time > MAXIMUM_WET_TIME) - wet_time = MAXIMUM_WET_TIME - if(wet == TURF_WET_ICE && air.temperature > T0C) - for(var/obj/O in contents) - if(O.flags_2 & FROZEN_2) - O.make_unfrozen() - MakeDry(TURF_WET_ICE) - MakeSlippery(TURF_WET_WATER) - if(wet != TURF_WET_PERMAFROST) - switch(air.temperature) - if(-INFINITY to T0C) - if(wet != TURF_WET_ICE && wet) - MakeDry(TURF_WET_ICE) - MakeSlippery(TURF_WET_ICE) - if(T0C to T20C) - wet_time = max(0, wet_time-1) - if(T20C to T0C + 40) - wet_time = max(0, wet_time-2) - if(T0C + 40 to T0C + 60) - wet_time = max(0, wet_time-3) - if(T0C + 60 to T0C + 80) - wet_time = max(0, wet_time-5) - if(T0C + 80 to T0C + 100) - wet_time = max(0, wet_time-10) - if(T0C + 100 to INFINITY) - wet_time = 0 - else if (GetTemperature() > BODYTEMP_COLD_DAMAGE_LIMIT) //seems like a good place - MakeDry(TURF_WET_PERMAFROST) - else - wet_time = max(0, wet_time-5) - if(wet && wet < TURF_WET_ICE && !wet_time) - MakeDry(TURF_WET_ICE) - if(!wet && wet_time) - wet_time = 0 - if(wet) - addtimer(CALLBACK(src, .proc/HandleWet), 15, TIMER_UNIQUE) +/turf/open/proc/MakeDry(wet_setting = TURF_WET_WATER, immediate = FALSE, amount = INFINITY) + SendSignal(COMSIG_TURF_MAKE_DRY, wet_setting, immediate, amount) /turf/open/get_dumping_location() return src /turf/open/proc/ClearWet()//Nuclear option of immediately removing slipperyness from the tile instead of the natural drying over time - wet = TURF_DRY - UpdateSlip() - if(wet_overlay) - cut_overlay(wet_overlay) - + qdel(GetComponent(/datum/component/wet_floor)) /turf/open/rad_act(pulse_strength) . = ..() diff --git a/code/game/turfs/simulated/chasm.dm b/code/game/turfs/simulated/chasm.dm index 5c05c44fc5..1bb2a2b798 100644 --- a/code/game/turfs/simulated/chasm.dm +++ b/code/game/turfs/simulated/chasm.dm @@ -21,10 +21,10 @@ GET_COMPONENT(chasm_component, /datum/component/chasm) chasm_component.drop(AM) -/turf/open/chasm/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0) +/turf/open/chasm/MakeSlippery() return -/turf/open/chasm/MakeDry(wet_setting = TURF_WET_WATER) +/turf/open/chasm/MakeDry() return /turf/open/chasm/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm index 2b8400abdb..44fc9dc643 100644 --- a/code/game/turfs/simulated/floor/plating/asteroid.dm +++ b/code/game/turfs/simulated/floor/plating/asteroid.dm @@ -33,10 +33,10 @@ /turf/open/floor/plating/asteroid/burn_tile() return -/turf/open/floor/plating/asteroid/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0) +/turf/open/floor/plating/asteroid/MakeSlippery() return -/turf/open/floor/plating/asteroid/MakeDry(wet_setting = TURF_WET_WATER) +/turf/open/floor/plating/asteroid/MakeDry() return /turf/open/floor/plating/asteroid/attackby(obj/item/W, mob/user, params) diff --git a/code/game/turfs/simulated/floor/plating/misc_plating.dm b/code/game/turfs/simulated/floor/plating/misc_plating.dm index 6e37f4ba4a..12edefb680 100644 --- a/code/game/turfs/simulated/floor/plating/misc_plating.dm +++ b/code/game/turfs/simulated/floor/plating/misc_plating.dm @@ -143,22 +143,15 @@ planetary_atmos = TRUE baseturfs = /turf/open/floor/plating/ice slowdown = 1 - wet = TURF_WET_PERMAFROST attachment_holes = FALSE /turf/open/floor/plating/ice/Initialize() . = ..() - UpdateSlip() + MakeSlippery(TURF_WET_PERMAFROST, INFINITY, 0, INFINITY, TRUE) /turf/open/floor/plating/ice/try_replace_tile(obj/item/stack/tile/T, mob/user, params) return -/turf/open/floor/plating/ice/HandleWet() - if(wet == TURF_WET_ICE) - return - ..() - MakeSlippery(TURF_WET_ICE) //rewet after ..() clears out lube/ice etc. - /turf/open/floor/plating/ice/smooth icon_state = "smooth" smooth = SMOOTH_MORE | SMOOTH_BORDER diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index 856537c1d5..2d86854265 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -14,7 +14,7 @@ /turf/open/lava/ex_act(severity, target) contents_explosion(severity, target) -/turf/open/lava/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0) +/turf/open/lava/MakeSlippery() return /turf/open/lava/MakeDry(wet_setting = TURF_WET_WATER) diff --git a/code/game/turfs/simulated/water.dm b/code/game/turfs/simulated/water.dm index 104f8b4b53..765da26136 100644 --- a/code/game/turfs/simulated/water.dm +++ b/code/game/turfs/simulated/water.dm @@ -7,10 +7,7 @@ initial_gas_mix = LAVALAND_DEFAULT_ATMOS planetary_atmos = TRUE slowdown = 1 - wet = TURF_WET_WATER -/turf/open/water/HandleWet() - if(wet == TURF_WET_WATER) - return - ..() - MakeSlippery(TURF_WET_WATER) //rewet after ..() clears out lube/ice etc. \ No newline at end of file +/turf/open/water/Initialize() + . = ..() + MakeSlippery(TURF_WET_WATER, INFINITY, 0, INFINITY, TRUE) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index ed21431fe7..2e4c42ed44 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -84,6 +84,9 @@ /turf/open/space/proc/CanBuildHere() return TRUE +/turf/open/space/handle_slip() + return + /turf/open/space/attackby(obj/item/C, mob/user, params) ..() if(!CanBuildHere()) @@ -162,10 +165,7 @@ A.newtonian_move(A.inertia_dir) -/turf/open/space/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0) - return - -/turf/open/space/handle_slip() +/turf/open/space/MakeSlippery() return /turf/open/space/singularity_act() diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index d6386b48b3..be54715215 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -226,8 +226,8 @@ GLOBAL_PROTECT(protected_ranks) dbfail = 1 else while(query_load_admins.NextRow()) - var/admin_ckey = query_load_admins.item[1] - var/admin_rank = query_load_admins.item[2] + var/admin_ckey = ckey(query_load_admins.item[1]) + var/admin_rank = ckeyEx(query_load_admins.item[2]) var/skip if(rank_names[admin_rank] == null) message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].") @@ -248,7 +248,7 @@ GLOBAL_PROTECT(protected_ranks) for(var/A in GLOB.admin_datums + GLOB.deadmins) if(A == "[J]") //this admin was already loaded from txt override continue - new /datum/admins(rank_names[json["admins"]["[J]"]], "[J]") + new /datum/admins(ckeyEx(rank_names[json["admins"]["[J]"]]), ckey("[J]")) #ifdef TESTING var/msg = "Admins Built:\n" for(var/ckey in GLOB.admin_datums) diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index f74fcbb017..82bbc27abc 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -15,7 +15,7 @@ msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" to_chat(GLOB.admins, msg) else - msg = "ADMIN: [key_name(usr, 1)]: [msg]" + msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" to_chat(GLOB.admins, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 6c723e96ec..0a000c4bf0 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -73,7 +73,7 @@ C.prefs.copy_to(M) M.key = C.key var/datum/mind/app_mind = M.mind - + var/datum/antagonist/wizard/apprentice/app = new() app.master = user app.school = kind @@ -145,6 +145,26 @@ M.mind.add_antag_datum(new_op,creator_op.nuke_team) M.mind.special_role = "Nuclear Operative" +//////CLOWN OP +/obj/item/antag_spawner/nuke_ops/clown + name = "clown operative teleporter" + desc = "A single-use teleporter designed to quickly reinforce clown operatives in the field." + +/obj/item/antag_spawner/nuke_ops/clown/spawn_antag(client/C, turf/T, kind, datum/mind/user) + var/mob/living/carbon/human/M = new/mob/living/carbon/human(T) + C.prefs.copy_to(M) + M.key = C.key + + var/datum/antagonist/nukeop/clownop/new_op = new /datum/antagonist/nukeop/clownop() + new_op.send_to_spawnpoint = FALSE + new_op.nukeop_outfit = /datum/outfit/syndicate/clownop/no_crystals + + var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop/clownop,TRUE) + if(creator_op) + M.mind.add_antag_datum(new_op, creator_op.nuke_team) + M.mind.special_role = "Clown Operative" + + //////SYNDICATE BORG /obj/item/antag_spawner/nuke_ops/borg_tele name = "syndicate cyborg teleporter" @@ -187,7 +207,7 @@ R.real_name = R.name R.key = C.key - + var/datum/antagonist/nukeop/new_borg = new() new_borg.send_to_spawnpoint = FALSE R.mind.add_antag_datum(new_borg,creator_op.nuke_team) diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm index 79b218686b..bf035e1f21 100644 --- a/code/modules/antagonists/disease/disease_mob.dm +++ b/code/modules/antagonists/disease/disease_mob.dm @@ -6,8 +6,8 @@ the new instance inside the host to be updated to the template's stats. */ /mob/camera/disease - name = "" - real_name = "" + name = "Sentient Disease" + real_name = "Sentient Disease" desc = "" icon = 'icons/mob/blob.dmi' icon_state = "marker" @@ -225,7 +225,6 @@ the new instance inside the host to be updated to the template's stats. disease_instances -= V hosts -= V.affected_mob else - points -= 1 to_chat(src, "One of your hosts, [V.affected_mob.real_name], has been purged of your infection.") var/datum/atom_hud/my_hud = GLOB.huds[DATA_HUD_SENTIENT_DISEASE] diff --git a/code/modules/antagonists/nukeop/clownop.dm b/code/modules/antagonists/nukeop/clownop.dm new file mode 100644 index 0000000000..43e62c4638 --- /dev/null +++ b/code/modules/antagonists/nukeop/clownop.dm @@ -0,0 +1,25 @@ + +/datum/antagonist/nukeop/clownop + name = "Clown Operative" + roundend_category = "clown operatives" + antagpanel_category = "ClownOp" + nukeop_outfit = /datum/outfit/syndicate/clownop + +/datum/antagonist/nukeop/leader/clownop + name = "Clown Operative Leader" + roundend_category = "clown operatives" + antagpanel_category = "ClownOp" + nukeop_outfit = /datum/outfit/syndicate/clownop/leader + +/datum/antagonist/nukeop/leader/clownop/give_alias() + title = pick("Head Honker", "Slipmaster", "Clown King", "Honkbearer") + if(nuke_team && nuke_team.syndicate_name) + owner.current.real_name = "[nuke_team.syndicate_name] [title]" + else + owner.current.real_name = "Syndicate [title]" + +/datum/antagonist/nukeop/clownop/admin_add(datum/mind/new_owner,mob/admin) + new_owner.assigned_role = "Clown Operative" + new_owner.add_antag_datum(src) + message_admins("[key_name_admin(admin)] has clown op'ed [new_owner.current].") + log_admin("[key_name(admin)] has clown op'ed [new_owner.current].") diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm index 03a76f19a1..931c38d7b9 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm @@ -15,6 +15,7 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) Such a brazen move will attract the attention of powerful benefactors within the Syndicate, who will supply your team with a massive amount of bonus telecrystals. \ Must be used within five minutes, or your benefactors will lose interest." var/declaring_war = FALSE + var/uplink_type = /obj/item/device/radio/uplink/nuclear /obj/item/device/nuclear_challenge/attack_self(mob/living/user) if(!check_allowed(user)) @@ -59,7 +60,7 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) for(var/obj/machinery/computer/camera_advanced/shuttle_docker/D in GLOB.jam_on_wardec) D.jammed = TRUE - new /obj/item/device/radio/uplink/nuclear(get_turf(user), user.key, CHALLENGE_TELECRYSTALS) + new uplink_type(get_turf(user), user.key, CHALLENGE_TELECRYSTALS) CONFIG_SET(number/shuttle_refuel_delay, max(CONFIG_GET(number/shuttle_refuel_delay), CHALLENGE_SHUTTLE_DELAY)) SSblackbox.record_feedback("amount", "nuclear_challenge_mode", 1) @@ -85,6 +86,9 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) return FALSE return TRUE +/obj/item/device/nuclear_challenge/clownops + uplink_type = /obj/item/device/radio/uplink/clownop + #undef CHALLENGE_TELECRYSTALS #undef CHALLENGE_TIME_LIMIT #undef CHALLENGE_MIN_PLAYERS diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm index e540525405..3a4d200c59 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm @@ -1,16 +1,3 @@ -#define NUKESTATE_INTACT 5 -#define NUKESTATE_UNSCREWED 4 -#define NUKESTATE_PANEL_REMOVED 3 -#define NUKESTATE_WELDED 2 -#define NUKESTATE_CORE_EXPOSED 1 -#define NUKESTATE_CORE_REMOVED 0 - -#define NUKE_OFF_LOCKED 0 -#define NUKE_OFF_UNLOCKED 1 -#define NUKE_ON_TIMING 2 -#define NUKE_ON_EXPLODING 3 - - /obj/machinery/nuclearbomb name = "nuclear fission explosive" desc = "You probably shouldn't stick around to see if this is armed." @@ -59,9 +46,8 @@ set_safety() GLOB.poi_list -= src GLOB.nuke_list -= src - if(countdown) - qdel(countdown) - countdown = null + QDEL_NULL(countdown) + QDEL_NULL(core) . = ..() /obj/machinery/nuclearbomb/examine(mob/user) @@ -459,11 +445,13 @@ //Cinematic SSticker.mode.OnNukeExplosion(off_station) - var/bombz = z - Cinematic(get_cinematic_type(off_station),world,CALLBACK(SSticker,/datum/controller/subsystem/ticker/proc/station_explosion_detonation,src)) - INVOKE_ASYNC(GLOBAL_PROC,.proc/KillEveryoneOnZLevel,bombz) + really_actually_explode(off_station) SSticker.roundend_check_paused = FALSE +/obj/machinery/nuclearbomb/proc/really_actually_explode(off_station) + Cinematic(get_cinematic_type(off_station),world,CALLBACK(SSticker,/datum/controller/subsystem/ticker/proc/station_explosion_detonation,src)) + INVOKE_ASYNC(GLOBAL_PROC,.proc/KillEveryoneOnZLevel, z) + /obj/machinery/nuclearbomb/proc/get_cinematic_type(off_station) if(off_station < 2) return CINEMATIC_SELFDESTRUCT diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm new file mode 100644 index 0000000000..b8f9927bc4 --- /dev/null +++ b/code/modules/antagonists/xeno/xeno.dm @@ -0,0 +1,37 @@ +/datum/team/xeno + name = "Aliens" + +//Simply lists them. +/datum/team/xeno/roundend_report() + var/list/parts = list() + parts += "The [name] were:" + parts += printplayerlist(members) + return "
[parts.Join("
")]
" + +/datum/antagonist/xeno + name = "Xenomorph" + job_rank = ROLE_ALIEN + show_in_antagpanel = FALSE + var/datum/team/xeno/xeno_team + +/datum/antagonist/xeno/create_team(datum/team/xeno/new_team) + if(!new_team) + for(var/datum/antagonist/xeno/X in GLOB.antagonists) + if(!X.owner || !X.xeno_team) + continue + xeno_team = X.xeno_team + return + xeno_team = new + else + if(!istype(new_team)) + CRASH("Wrong xeno team type provided to create_team") + xeno_team = new_team + +/datum/antagonist/xeno/get_team() + return xeno_team + +//XENO +/mob/living/carbon/alien/mind_initialize() + ..() + if(!mind.has_antag_datum(/datum/antagonist/xeno)) + mind.add_antag_datum(/datum/antagonist/xeno) \ No newline at end of file diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index fa19edc832..3ec224e73e 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1274,24 +1274,45 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["taur"] = "None" if("tail_human") + var/list/snowflake_tails_list = list("Normal" = null) + for(var/path in GLOB.tails_list_human) + var/datum/sprite_accessory/tails/human/instance = GLOB.tails_list_human[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) + snowflake_tails_list[S.name] = path var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_human + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in snowflake_tails_list if(new_tail) features["tail_human"] = new_tail if(new_tail != "None") features["taur"] = "None" if("mam_tail") + var/list/snowflake_tails_list = list("Normal" = null) + for(var/path in GLOB.mam_tails_list) + var/datum/sprite_accessory/mam_tails/instance = GLOB.mam_tails_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) + snowflake_tails_list[S.name] = path var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.mam_tails_list + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in snowflake_tails_list if(new_tail) features["mam_tail"] = new_tail if(new_tail != "None") features["taur"] = "None" if("taur") + var/list/snowflake_taur_list = list("Normal" = null) + for(var/path in GLOB.taur_list) + var/datum/sprite_accessory/taur/instance = GLOB.taur_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) + snowflake_taur_list[S.name] = path var/new_taur - new_taur = input(user, "Choose your character's tauric body:", "Character Preference") as null|anything in GLOB.taur_list + new_taur = input(user, "Choose your character's tauric body:", "Character Preference") as null|anything in snowflake_taur_list if(new_taur) features["taur"] = new_taur if(new_taur != "None") @@ -1305,8 +1326,15 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["snout"] = new_snout if("mam_ears") + var/list/snowflake_ears_list = list("Normal" = null) + for(var/path in GLOB.mam_ears_list) + var/datum/sprite_accessory/mam_ears/instance = GLOB.mam_ears_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) + snowflake_ears_list[S.name] = path var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.mam_ears_list + new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in snowflake_ears_list if(new_ears) features["mam_ears"] = new_ears @@ -1317,8 +1345,15 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["horns"] = new_horns if("ears") + var/list/snowflake_ears_list = list("Normal" = null) + for(var/path in GLOB.ears_list) + var/datum/sprite_accessory/ears/instance = GLOB.ears_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) + snowflake_ears_list[S.name] = path var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.ears_list + new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in snowflake_ears_list if(new_ears) features["ears"] = new_ears @@ -1353,8 +1388,15 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["body_markings"] = new_body_markings if("mam_body_markings") + var/list/snowflake_markings_list = list("Normal" = null) + for(var/path in GLOB.mam_body_markings_list) + var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[path] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey))) + snowflake_markings_list[S.name] = path var/new_mam_body_markings - new_mam_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.mam_body_markings_list + new_mam_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in snowflake_markings_list if(new_mam_body_markings) features["mam_body_markings"] = new_mam_body_markings diff --git a/code/modules/clothing/shoes/bananashoes.dm b/code/modules/clothing/shoes/bananashoes.dm index 561b0c15d7..7ffd9c1f40 100644 --- a/code/modules/clothing/shoes/bananashoes.dm +++ b/code/modules/clothing/shoes/bananashoes.dm @@ -4,25 +4,30 @@ name = "mk-honk prototype shoes" desc = "Lost prototype of advanced clown tech. Powered by bananium, these shoes leave a trail of chaos in their wake." icon_state = "clown_prototype_off" - var/on = FALSE actions_types = list(/datum/action/item_action/toggle) + var/on = FALSE + var/always_noslip = FALSE /obj/item/clothing/shoes/clown_shoes/banana_shoes/Initialize() . = ..() AddComponent(/datum/component/material_container, list(MAT_BANANIUM), 200000, TRUE) AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 75) + if(always_noslip) + flags_1 |= NOSLIP_1 /obj/item/clothing/shoes/clown_shoes/banana_shoes/step_action() . = ..() + GET_COMPONENT(bananium, /datum/component/material_container) if(on) - new/obj/item/grown/bananapeel/specialpeel(get_step(src,turn(usr.dir, 180))) //honk - GET_COMPONENT(bananium, /datum/component/material_container) - bananium.use_amount_type(100, MAT_BANANIUM) if(bananium.amount(MAT_BANANIUM) < 100) on = !on - flags_1 &= ~NOSLIP_1 + if(!always_noslip) + flags_1 &= ~NOSLIP_1 update_icon() to_chat(loc, "You ran out of bananium!") + else + new /obj/item/grown/bananapeel/specialpeel(get_step(src,turn(usr.dir, 180))) //honk + bananium.use_amount_type(100, MAT_BANANIUM) /obj/item/clothing/shoes/clown_shoes/banana_shoes/attack_self(mob/user) GET_COMPONENT(bananium, /datum/component/material_container) @@ -42,10 +47,11 @@ on = !on update_icon() to_chat(user, "You [on ? "activate" : "deactivate"] the prototype shoes.") - if(on) - flags_1 |= NOSLIP_1 - else - flags_1 &= ~NOSLIP_1 + if(!always_noslip) + if(on) + flags_1 |= NOSLIP_1 + else + flags_1 &= ~NOSLIP_1 else to_chat(user, "You need bananium to turn the prototype shoes on!") diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index b954dcef75..7ae401f564 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -64,9 +64,7 @@ /obj/item/clothing/shoes/galoshes/dry/step_action() var/turf/open/t_loc = get_turf(src) - if(istype(t_loc) && t_loc.wet) - t_loc.MakeDry(TURF_WET_WATER) - t_loc.wet_time = 0 + t_loc.SendSignal(COMSIG_TURF_MAKE_DRY, TURF_WET_WATER, TRUE, INFINITY) /obj/item/clothing/shoes/clown_shoes desc = "The prankster's standard-issue clowning shoes. Damn, they're huge!" diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index b145dac9f6..5b9a8c8af3 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -1,6 +1,5 @@ #define ION_RANDOM 0 #define ION_ANNOUNCE 1 -#define ION_FILE "ion_laws.json" /datum/round_event_control/ion_storm name = "Ion Storm" typepath = /datum/round_event/ion_storm diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm index 6bc5edf096..01105cf6d3 100644 --- a/code/modules/food_and_drinks/food/snacks_pastry.dm +++ b/code/modules/food_and_drinks/food/snacks_pastry.dm @@ -23,6 +23,21 @@ bonus_reagents = list("sprinkles" = 2, "sugar" = 1) filling_color = "#FF69B4" + +/obj/item/reagent_containers/food/snacks/donut/checkLiked(fraction, mob/M) //Sec officers always love donuts + if(last_check_time + 50 < world.time) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.mind && H.mind.assigned_role == "Security Officer" && !H.has_trait(TRAIT_AGEUSIA)) + to_chat(H,"I love this taste!") + H.adjust_disgust(-5 + -2.5 * fraction) + GET_COMPONENT_FROM(mood, /datum/component/mood, H) + if(mood) + mood.add_event("fav_food", /datum/mood_event/favorite_food) + last_check_time = world.time + return + ..() + /obj/item/reagent_containers/food/snacks/donut/chaos name = "chaos donut" desc = "Like life, it never quite tastes the same." diff --git a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm index f2f288fe7a..1788626b0d 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm @@ -25,6 +25,7 @@ "sugar" = 5, "ice" = 5, "cocoa" = 5, + "vanilla" = 5, "berryjuice" = 5, "singulo" = 5) @@ -40,8 +41,8 @@ return list("flour", "sugar") if(CONE_CHOC) return list("flour", "sugar", "cocoa") - else - return list("milk", "ice") + else //ICECREAM_VANILLA + return list("milk", "ice", "vanilla") /obj/machinery/icecream_vat/proc/get_flavour_name(flavour_type) @@ -56,7 +57,7 @@ return "waffle" if(CONE_CHOC) return "chocolate" - else + else //ICECREAM_VANILLA return "vanilla" diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 7f3870e418..f9c3fdd43d 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -146,6 +146,7 @@ icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "asclepius_dormant" var/activated = FALSE + var/usedHand /obj/item/rod_of_asclepius/attack_self(mob/user) if(activated) @@ -154,6 +155,10 @@ to_chat(user, "The snake carving seems to come alive, if only for a moment, before returning to it's dormant state, almost as if it finds you incapable of holding it's oath.") return var/mob/living/carbon/itemUser = user + usedHand = itemUser.get_held_index_of_item(src) + if(itemUser.has_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH)) + to_chat(user, "You can't possibly handle the responsibility of more than one rod!") + return var/failText = "The snake seems unsatisfied with your incomplete oath and returns to it's previous place on the rod, returning to its dormant, wooden state. You must stand still while completing your oath!" to_chat(itemUser, "The wooden snake that was carved into the rod seems to suddenly come alive and begins to slither down your arm! The compulsion to help others grows abnormally strong...") if(do_after(itemUser, 40, target = itemUser)) @@ -178,7 +183,7 @@ return to_chat(itemUser, "The snake, satisfied with your oath, attaches itself and the rod to your forearm with an inseparable grip. Your thoughts seem to only revolve around the core idea of helping others, and harm is nothing more than a distant, wicked memory...") var/datum/status_effect/hippocraticOath/effect = itemUser.apply_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH) - effect.hand = itemUser.get_held_index_of_item(src) + effect.hand = usedHand activated() /obj/item/rod_of_asclepius/proc/activated() diff --git a/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm b/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm index 9d1bc80094..d70116cc82 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm @@ -5,7 +5,7 @@ var/extra2 = FALSE var/extra2_icon = 'icons/mob/mam_bodyparts.dmi' var/extra2_color_src = MUTCOLORS3 -// var/list/ckeys_allowed = null + var/list/ckeys_allowed /* tbi eventually idk /datum/sprite_accessory/legs/digitigrade_mam @@ -97,6 +97,12 @@ extra = TRUE icon = 'icons/mob/mam_bodyparts.dmi' +//datum/sprite_accessory/ears/elf +// name = "Elf" +// icon_state = "elf" +// icon = 'icons/mob/mam_bodyparts.dmi' +// ckeys_allowed = list("atiefling") + /datum/sprite_accessory/ears/fennec name = "Fennec" icon_state = "fennec" @@ -950,34 +956,44 @@ /datum/sprite_accessory/mam_ears/guilmon name = "Guilmon" icon_state = "guilmon" - icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/snout/guilmon + name = "Guilmon" + icon_state = "guilmon" /datum/sprite_accessory/mam_tails/shark/datashark name = "DataShark" icon_state = "datashark" color_src = 0 -// ckeys_allowed = list("rubyflamewing") + ckeys_allowed = list("rubyflamewing") /datum/sprite_accessory/mam_tails_animated/shark/datashark name = "DataShark" icon_state = "datashark" color_src = 0 -/* -//Till I get my snowflake only ckey lock, these are locked-locked :D +/datum/sprite_accessory/mam_body_markings/shark/datashark + name = "DataShark" + icon_state = "datashark" + color_src = MUTCOLORS2 + ckeys_allowed = list("rubyflamewing") + +//Sabresune /datum/sprite_accessory/mam_ears/sabresune name = "sabresune" icon_state = "sabresune" + hasinner = 1 extra = TRUE extra_color_src = MUTCOLORS3 - locked = TRUE + ckeys_allowed = list("poojawa") /datum/sprite_accessory/mam_tails/sabresune name = "sabresune" icon_state = "sabresune" extra = TRUE - locked = TRUE + ckeys_allowed = list("poojawa") + /datum/sprite_accessory/mam_tails_animated/sabresune name = "sabresune" @@ -990,5 +1006,5 @@ color_src = MUTCOLORS2 extra = FALSE extra2 = FALSE - locked = TRUE -*/ + ckeys_allowed = list("poojawa") + diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 1e7f2210cf..d804b3c499 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -170,8 +170,12 @@ return FALSE return !held_items[hand_index] -/mob/proc/put_in_hand(obj/item/I, hand_index) - if(can_put_in_hand(I, hand_index)) +/mob/proc/put_in_hand(obj/item/I, hand_index, forced = FALSE) + if(forced || can_put_in_hand(I, hand_index)) + if(hand_index == null) + return FALSE + if(get_item_for_held_index(hand_index) != null) + dropItemToGround(get_item_for_held_index(hand_index), force = TRUE) I.forceMove(src) held_items[hand_index] = I I.layer = ABOVE_HUD_LAYER @@ -185,7 +189,6 @@ return hand_index || TRUE return FALSE - //Puts the item into the first available left hand if possible and calls all necessary triggers/updates. returns 1 on success. /mob/proc/put_in_l_hand(obj/item/I) return put_in_hand(I, get_empty_held_index_for_side("l")) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 9b23204d98..31be31160c 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -272,7 +272,7 @@ else if(check_zone(M.zone_selected) == "head") M.visible_message("[M] gives [src] a pat on the head to make [p_them()] feel better!", \ "You give [src] a pat on the head to make [p_them()] feel better!") - if(dna && dna.species && (("tail_lizard" in dna.species.mutant_bodyparts) || (dna.features["tail_human"] != "None") || ("mam_tail" in dna.species.mutant_bodyparts))) + if(dna && dna.species && ((("tail_lizard" || "tail_human" || "mam_tail") in dna.species.mutant_bodyparts && (dna.features["tail_lizard"] || dna.features["tail_human"] || dna.features["mam_tail"])!= "None"))) emote("wag") //lewd else M.visible_message("[M] hugs [src] to make [p_them()] feel better!", \ diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index 662a42eea4..7ee828adb7 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -27,7 +27,6 @@ add_logs(src,, "slipped",, "on [O ? O.name : "floor"]") return loc.handle_slip(src, knockdown_amount, O, lube) - /mob/living/carbon/Process_Spacemove(movement_dir = 0) if(..()) return 1 diff --git a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm index 2208c5a597..da56a69040 100644 --- a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm @@ -54,7 +54,7 @@ liked_food = MEAT disliked_food = TOXIC meat = /obj/item/reagent_containers/food/snacks/carpmeat/aquatic - + /datum/species/aquatic/spec_death(gibbed, mob/living/carbon/human/H) if(H) H.endTailWag() @@ -82,7 +82,6 @@ /datum/species/insect/qualifies_for_rank(rank, list/features) return TRUE -//HERBIVOROUS// //Alien// /datum/species/xeno @@ -159,89 +158,9 @@ description = "A highly corrosive substance, it is capable of burning through most natural or man-made materials in short order." color = "#66CC00" toxpwr = 0 - acidpwr = 12 + acidpwr = 12 */ -/datum/species/yautja - name = "Yautja" - id = "pred" - say_mod = "clicks" - eyes = "predeyes" - mutant_organs = list(/obj/item/organ/tongue/yautja) - specflags = list(EYECOLOR) - lang_spoken = YAUTJA - lang_understood = HUMAN|YAUTJA|ALIEN - no_equip = list(slot_head) - punchdamagelow = 4 - punchdamagehigh = 14 - punchstunthreshold = 13 - blacklisted = 1 - whitelist = 1 - whitelist = list("talkingcactus") - -/datum/outfit/yautja_basic - name = "Yautja, Basic" - uniform = /obj/item/clothing/under/mesh - suit = /obj/item/clothing/suit/armor/yautja_fake - shoes = /obj/item/clothing/shoes/yautja_fake - mask = /obj/item/clothing/mask/gas/yautja_fake - -/datum/species/yautja/before_equip_job(datum/job/J, mob/living/carbon/human/H, visualsOnly = FALSE) - var/datum/outfit/yautja_basic/O = new /datum/outfit/yautja_basic//Just basic gear. Doesn't include anything that gives any meaningful advantage. - H.equipOutfit(O, visualsOnly) - return 0 - -/datum/species/octopus - blacklisted = 1 -/datum/species/carp - blacklisted = 1 -/datum/species/horse - blacklisted = 1*/ - -/////////////////// -//DONATOR SPECIES// -/////////////////// - -//ChronoFlux: Slimecoon -/* -/datum/species/jelly/slime/slimecoon - name = "Slime Raccoon" - id = "slimecoon" - limbs_id = "slime" - whitelisted = 1 - whitelist = list("chronoflux") - blacklisted = 1 - mutant_bodyparts = list("slimecoontail", "slimecoonears", "slimecoonsnout") - default_features = list("slimecoontail" = "Slimecoon Tail", "slimecoonears" = "Slimecoon Ears", "slimecoonsnout" = "Slimecoon Snout")*/ - -// Fat Shark <3 -/* -/datum/species/shark/datashark - name = "DataShark" - id = "datashark" - default_color = "BCAC9B" - species_traits = list(MUTCOLORS_PARTSONLY,EYECOLOR,LIPS,HAIR,SPECIES_ORGANIC) - mutant_bodyparts = list("mam_tail", "mam_body_markings") - default_features = list("mam_tail" = "datashark", "mam_body_markings" = "None") - attack_verb = "bite" - attack_sound = 'sound/weapons/bite.ogg' - miss_sound = 'sound/weapons/slashmiss.ogg' - whitelisted = 1 - whitelist = list("rubyflamewing") - blacklisted = 0 - */ - -/datum/species/guilmon - name = "Guilmon" - id = "guilmon" - default_color = "4B4B4B" - species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,SPECIES_ORGANIC) - mutant_bodyparts = list("mam_tail", "mam_ears", "mam_body_markings") - default_features = list("mcolor" = "FFF", "mcolor2" = "FFF", "mcolor3" = "FFF", "mam_tail" = "guilmon", "mam_ears" = "guilmon", "mam_body_markings" = "guilmon") - attack_verb = "claw" - attack_sound = 'sound/weapons/slash.ogg' - miss_sound = 'sound/weapons/slashmiss.ogg' - //##########SLIMEPEOPLE########## /datum/species/jelly/roundstartslime diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index ff8b3fa0e9..fe0789d960 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -246,7 +246,7 @@ if(prob(75)) var/turf/open/T = loc if(istype(T)) - T.MakeSlippery(TURF_WET_WATER, min_wet_time = 20, wet_time_to_add = 15) + T.MakeSlippery(TURF_WET_WATER, min_wet_time = 20 SECONDS, wet_time_to_add = 15 SECONDS) else visible_message("[src] whirs and bubbles violently, before releasing a plume of froth!") new /obj/effect/particle_effect/foam(loc) diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 38c0d38710..8cfc5784e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -137,8 +137,8 @@ name = "Syndicate Stormtrooper" maxHealth = 200 health = 200 + casingtype = /obj/item/ammo_casing/shotgun/tengauge projectilesound = 'sound/weapons/gunshot.ogg' - casingtype = /obj/item/ammo_casing/shotgun/buckshot loot = list(/obj/effect/gibspawner/human) ///////////////Misc//////////// diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 81206b39da..024f24e182 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -425,5 +425,8 @@ if(..()) return 3 +/mob/living/simple_animal/slime/can_be_implanted() + return TRUE + /mob/living/simple_animal/slime/random/Initialize(mapload, new_colour, new_is_adult) . = ..(mapload, pick(slime_colours), prob(50)) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 0088e09515..cb5769ab8d 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -102,3 +102,5 @@ var/list/progressbars = null //for stacking do_after bars var/list/mousemove_intercept_objects + + var/datum/click_intercept diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index b00aa51d09..1000fa5c8b 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -87,6 +87,7 @@ if(istype(linked_techweb)) linked_techweb.research_points += min(power_produced, 1) addtimer(CALLBACK(src, .proc/reset_shocked), 10) + tesla_buckle_check(power) else ..() @@ -100,6 +101,7 @@ add_load(power) playsound(src.loc, 'sound/magic/lightningshock.ogg', 100, 1, extrarange = 5) tesla_zap(src, 10, power/(coeff/2)) + tesla_buckle_check(power/(coeff/2)) // Tesla R&D researcher /obj/machinery/power/tesla_coil/research @@ -120,6 +122,7 @@ if(istype(linked_techweb)) linked_techweb.research_points += min(power_produced, 3) // 4 coils makes ~720/m bonus for R&D, addtimer(CALLBACK(src, .proc/reset_shocked), 10) + tesla_buckle_check(power) else ..() @@ -136,6 +139,10 @@ return return ..() +/obj/machinery/power/tesla_coil/research/on_construction() + if(anchored) + connect_to_network() + /obj/machinery/power/grounding_rod name = "grounding rod" desc = "Keep an area from being fried from Edison's Bane." @@ -179,5 +186,6 @@ /obj/machinery/power/grounding_rod/tesla_act(var/power) if(anchored && !panel_open) flick("grounding_rodhit", src) + tesla_buckle_check(power) else - ..() + ..() \ No newline at end of file diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm index b700d092d7..de0d03da3b 100644 --- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm +++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm @@ -7,6 +7,11 @@ caliber = "shotgun" projectile_type = /obj/item/projectile/bullet/shotgun_slug materials = list(MAT_METAL=4000) + +/obj/item/ammo_casing/shotgun/tengauge + name = "10g shotgun slug" + desc = "A 10 gauge lead slug." + projectile_type = /obj/item/projectile/bullet/shotgun_slug/tengauge /obj/item/ammo_casing/shotgun/beanbag name = "beanbag slug" diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm index 93a210879e..44b0a70dd5 100644 --- a/code/modules/projectiles/guns/ballistic/toy.dm +++ b/code/modules/projectiles/guns/ballistic/toy.dm @@ -54,7 +54,7 @@ force = 0 throwforce = 0 mag_type = /obj/item/ammo_box/magazine/internal/shot/toy - clumsy_check = 0 + clumsy_check = FALSE item_flags = NONE casing_ejector = FALSE can_suppress = FALSE @@ -87,6 +87,7 @@ item_flags = NONE mag_type = /obj/item/ammo_box/magazine/toy/smgm45/riot casing_ejector = FALSE + clumsy_check = FALSE /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted //Use this for actual toys pin = /obj/item/device/firing_pin @@ -103,6 +104,7 @@ item_flags = NONE mag_type = /obj/item/ammo_box/magazine/toy/m762/riot casing_ejector = FALSE + clumsy_check = FALSE /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted //Use this for actual toys pin = /obj/item/device/firing_pin diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm index ecbe2e96e4..3d616bdd22 100644 --- a/code/modules/projectiles/projectile/bullets/shotgun.dm +++ b/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -1,6 +1,10 @@ /obj/item/projectile/bullet/shotgun_slug name = "12g shotgun slug" damage = 60 + +/obj/item/projectile/bullet/shotgun_slug/tengauge + name = "10g shotgun slug" + damage = 72.5 /obj/item/projectile/bullet/shotgun_beanbag name = "beanbag slug" diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 3b06b4c71e..f280b4f171 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -9,15 +9,17 @@ idle_power_usage = 40 interact_offline = TRUE resistance_flags = FIRE_PROOF | ACID_PROOF + circuit = /obj/item/circuitboard/machine/chem_dispenser var/cell_type = /obj/item/stock_parts/cell/high var/obj/item/stock_parts/cell/cell - var/powerefficiency = 0.01 + var/powerefficiency = 0.1 var/amount = 30 var/recharged = 0 var/recharge_delay = 5 var/mutable_appearance/beaker_overlay var/working_state = "dispenser_working" var/nopower_state = "dispenser_nopower" + var/macrotier = 1 var/obj/item/reagent_containers/beaker = null var/list/dispensable_reagents = list( "hydrogen", @@ -199,7 +201,7 @@ obj/machinery/chem_dispenser/update_icon() var/actual = min(amount, (cell.charge * powerefficiency)*10, free) R.add_reagent(reagent, actual) - cell.use((actual / 10) / powerefficiency) + cell.use(actual / powerefficiency) work_animation() . = TRUE if("remove") @@ -219,16 +221,17 @@ obj/machinery/chem_dispenser/update_icon() if("dispense_recipe") var/recipe_to_use = params["recipe"] var/list/chemicals_to_dispense = process_recipe_list(recipe_to_use) + var/res = get_macro_resolution() for(var/key in chemicals_to_dispense) // i suppose you could edit the list locally before passing it var/list/keysplit = splittext(key," ") var/r_id = keysplit[1] if(beaker && dispensable_reagents.Find(r_id)) // but since we verify we have the reagent, it'll be fine var/datum/reagents/R = beaker.reagents var/free = R.maximum_volume - R.total_volume - var/actual = min(chemicals_to_dispense[key], (cell.charge * powerefficiency)*10, free) + var/actual = min(round(chemicals_to_dispense[key], res), (cell.charge * powerefficiency)*10, free) if(actual) R.add_reagent(r_id, actual) - cell.use((actual / 10) / powerefficiency) + cell.use(actual / powerefficiency) work_animation() if("clear_recipes") var/yesno = alert("Clear all recipes?",, "Yes","No") @@ -241,21 +244,35 @@ obj/machinery/chem_dispenser/update_icon() var/list/first_process = splittext(recipe, ";") if(!LAZYLEN(first_process)) return + var/res = get_macro_resolution() + var/resmismatch = FALSE for(var/reagents in first_process) - var/list/fuck = splittext(reagents, "=") - if(dispensable_reagents.Find(fuck[1])) + var/list/reagent = splittext(reagents, "=") + if(dispensable_reagents.Find(reagent[1])) + if (!resmismatch && !check_macro_part(reagents, res)) + resmismatch = TRUE continue else - var/temp = fuck[1] + var/chemid = reagent[1] visible_message("[src] buzzes.", "You hear a faint buzz.") - to_chat(usr, "[src] cannot find Chemical ID: [temp]!") + to_chat(usr, "[src] cannot find Chemical ID: [chemid]!") playsound(src, "sound/machines/buzz-two.ogg", 50, 1) return + if (resmismatch && alert("[src] is not yet capable of replicating this recipe with the precision it needs, do you want to save it anyway?",, "Yes","No") == "No") + return saved_recipes += list(list("recipe_name" = name, "contents" = recipe)) /obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) if(default_unfasten_wrench(user, I)) return + if(default_deconstruction_screwdriver(user, "dispenser-o", "dispenser", I)) + return + + if(exchange_parts(user, I)) + return + + if(default_deconstruction_crowbar(I)) + return if(istype(I, /obj/item/reagent_containers) && !(I.flags_1 & ABSTRACT_1) && I.is_open_container()) var/obj/item/reagent_containers/B = I . = 1 //no afterattack @@ -294,92 +311,48 @@ obj/machinery/chem_dispenser/update_icon() visible_message("[src] malfunctions, spraying chemicals everywhere!") ..() -/obj/machinery/chem_dispenser/constructable - name = "portable chem dispenser" - icon = 'icons/obj/chemical.dmi' - icon_state = "minidispenser" - powerefficiency = 0.001 - amount = 5 - recharge_delay = 20 - dispensable_reagents = list() - circuit = /obj/item/circuitboard/machine/chem_dispenser - working_state = "minidispenser_working" - nopower_state = "minidispenser_nopower" - var/static/list/dispensable_reagent_tiers = list( - list( - "hydrogen", - "oxygen", - "silicon", - "phosphorus", - "sulfur", - "carbon", - "nitrogen", - "water" - ), - list( - "lithium", - "sugar", - "sacid", - "copper", - "mercury", - "sodium", - "iodine", - "bromine" - ), - list( - "ethanol", - "chlorine", - "potassium", - "aluminium", - "radium", - "fluorine", - "iron", - "welding_fuel", - "silver", - "stable_plasma" - ), - list( - "oil", - "ash", - "acetone", - "saltpetre", - "ammonia", - "diethylamine" - ) - ) -/obj/machinery/chem_dispenser/constructable/RefreshParts() +/obj/machinery/chem_dispenser/RefreshParts() var/time = 0 - var/i + var/newpowereff = 0.0666666 for(var/obj/item/stock_parts/cell/P in component_parts) cell = P for(var/obj/item/stock_parts/matter_bin/M in component_parts) - time += M.rating + newpowereff += 0.0166666666*M.rating for(var/obj/item/stock_parts/capacitor/C in component_parts) time += C.rating recharge_delay = 30/(time/2) //delay between recharges, double the usual time on lowest 50% less than usual on highest for(var/obj/item/stock_parts/manipulator/M in component_parts) - for(i=1, i<=M.rating, i++) - dispensable_reagents |= dispensable_reagent_tiers[i] - dispensable_reagents = sortList(dispensable_reagents) + if (M.rating > macrotier) + macrotier = M.rating + powerefficiency = round(newpowereff, 0.01) -/obj/machinery/chem_dispenser/constructable/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, "minidispenser-o", "minidispenser", I)) - return - if(exchange_parts(user, I)) - return - if(default_deconstruction_crowbar(I)) - return - return ..() - -/obj/machinery/chem_dispenser/constructable/on_deconstruction() +/obj/machinery/chem_dispenser/on_deconstruction() if(beaker) beaker.forceMove(drop_location()) beaker = null return ..() +/obj/machinery/chem_dispenser/proc/get_macro_resolution() + . = 5 + if (macrotier > 1) + . -= macrotier // 5 for tier1, 3 for 2, 2 for 3, 1 for 4. + +/obj/machinery/chem_dispenser/proc/check_macro(var/macro) + var/res = get_macro_resolution() + for (var/reagent in splittext(macro, ";")) + if (!check_macro_part(reagent, res)) + return FALSE + return TRUE + +/obj/machinery/chem_dispenser/proc/check_macro_part(var/part, var/res = get_macro_resolution()) + var/detail = splittext(part, "=") + if (round(text2num(detail[2]), res) != text2num(detail[2])) + return FALSE + return TRUE + /obj/machinery/chem_dispenser/proc/process_recipe_list(var/fucking_hell) var/list/key_list = list() var/list/final_list = list() @@ -389,12 +362,6 @@ obj/machinery/chem_dispenser/update_icon() final_list += list(avoid_assoc_duplicate_keys(fuck[1],key_list) = text2num(fuck[2])) return final_list -/obj/machinery/chem_dispenser/constructable/display_beaker() - var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker") - b_o.pixel_y = -4 - b_o.pixel_x = -4 - return b_o - /obj/machinery/chem_dispenser/drinks/display_beaker() var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker") switch(dir) diff --git a/code/modules/reagents/chemistry/reagents/blob_reagents.dm b/code/modules/reagents/chemistry/reagents/blob_reagents.dm index 3f81e27a83..7ca900488e 100644 --- a/code/modules/reagents/chemistry/reagents/blob_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/blob_reagents.dm @@ -459,7 +459,7 @@ reac_volume = ..() var/turf/open/T = get_turf(M) if(istype(T) && prob(reac_volume)) - T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10, wet_time_to_add = 5) + T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS) M.adjust_fire_stacks(-(reac_volume / 10)) M.ExtinguishMob() M.apply_damage(0.4*reac_volume, BRUTE) @@ -481,7 +481,7 @@ /datum/reagent/blob/pressurized_slime/proc/extinguisharea(obj/structure/blob/B, probchance) for(var/turf/open/T in range(1, B)) if(prob(probchance)) - T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10, wet_time_to_add = 5) + T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS) for(var/obj/O in T) O.extinguish() for(var/mob/living/L in T) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 55d64ea36c..47bbc899a9 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -124,7 +124,7 @@ if(!istype(T)) return if(reac_volume >= 5) - T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10, wet_time_to_add = reac_volume * 1.5) + T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = reac_volume * 1.5 SECONDS) T.name = "deep-fried [initial(T.name)]" T.add_atom_colour(color, TEMPORARY_COLOUR_PRIORITY) @@ -249,7 +249,7 @@ if(reac_volume >= 1) // Make Freezy Foam and anti-fire grenades! if(isopenturf(T)) var/turf/open/OT = T - OT.MakeSlippery(wet_setting=TURF_WET_ICE, min_wet_time=10, wet_time_to_add=reac_volume) // Is less effective in high pressure/high heat capacity environments. More effective in low pressure. + OT.MakeSlippery(wet_setting=TURF_WET_ICE, min_wet_time=100, wet_time_to_add=reac_volume SECONDS) // Is less effective in high pressure/high heat capacity environments. More effective in low pressure. OT.air.temperature -= MOLES_CELLSTANDARD*100*reac_volume/OT.air.heat_capacity() // reduces environment temperature by 5K per unit. /datum/reagent/consumable/condensedcapsaicin @@ -433,7 +433,7 @@ /datum/reagent/consumable/cornoil/reaction_turf(turf/open/T, reac_volume) if (!istype(T)) return - T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10, wet_time_to_add = reac_volume*2) + T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = reac_volume*2 SECONDS) var/obj/effect/hotspot/hotspot = (locate(/obj/effect/hotspot) in T) if(hotspot) var/datum/gas_mixture/lowertemp = T.remove_air(T.air.total_moles()) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 0e6be47893..fca2260e02 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -135,7 +135,7 @@ var/CT = cooling_temperature if(reac_volume >= 5) - T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10, wet_time_to_add = min(reac_volume*1.5, 60)) + T.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = min(reac_volume*1.5 SECONDS, 60 SECONDS)) for(var/mob/living/simple_animal/slime/M in T) M.apply_water() @@ -329,7 +329,7 @@ if (!istype(T)) return if(reac_volume >= 1) - T.MakeSlippery(TURF_WET_LUBE, 15, min(reac_volume * 2, 120)) + T.MakeSlippery(TURF_WET_LUBE, 15 SECONDS, min(reac_volume * 2 SECONDS, 120)) /datum/reagent/spraytan name = "Spray Tan" @@ -1613,9 +1613,8 @@ taste_description = "dryness" /datum/reagent/drying_agent/reaction_turf(turf/open/T, reac_volume) - if(istype(T) && T.wet) - T.wet_time = max(0, T.wet_time-reac_volume*5) // removes 5 seconds of wetness for every unit. - T.HandleWet() + if(istype(T)) + T.MakeDry(ALL, TRUE, reac_volume * 5 SECONDS) //50 deciseconds per unit /datum/reagent/drying_agent/reaction_obj(obj/O, reac_volume) if(O.type == /obj/item/clothing/shoes/galoshes) diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index bf698300e8..23a81a5950 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -513,7 +513,7 @@ required_other = 1 /datum/chemical_reaction/slime/slimeradio/on_reaction(datum/reagents/holder, created_volume) - new /obj/item/slimepotion/slimeradio(get_turf(holder.my_atom)) + new /obj/item/slimepotion/slime/slimeradio(get_turf(holder.my_atom)) ..() //Cerulean diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index 382fe6beae..b4b626dab2 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -49,22 +49,22 @@ /obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/user) ..() - if(slime_up_action) + if(slime_up_action && (upgradetier & XENOBIO_UPGRADE_SLIMEBASIC)) //CIT CHANGE - makes slime-related actions require XENOBIO_UPGRADE_SLIMEBASIC slime_up_action.target = src slime_up_action.Grant(user) actions += slime_up_action - if(slime_place_action) + if(slime_place_action && (upgradetier & XENOBIO_UPGRADE_SLIMEBASIC)) //CIT CHANGE - makes slime-related actions require XENOBIO_UPGRADE_SLIMEBASIC slime_place_action.target = src slime_place_action.Grant(user) actions += slime_place_action - if(feed_slime_action) + if(feed_slime_action && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS feed_slime_action.target = src feed_slime_action.Grant(user) actions += feed_slime_action - if(monkey_recycle_action) + if(monkey_recycle_action && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes remote monkey recycling require XENOBIO_UPGRADE_MONKEYS monkey_recycle_action.target = src monkey_recycle_action.Grant(user) actions += monkey_recycle_action @@ -74,18 +74,18 @@ scan_action.Grant(user) actions += scan_action - if(potion_action) + if(potion_action && (upgradetier & XENOBIO_UPGRADE_SLIMEADV)) // CIT CHANGE - makes giving slimes potions via console require XENOBIO_UPGRADE_SLIMEADV potion_action.target = src potion_action.Grant(user) actions += potion_action /obj/machinery/computer/camera_advanced/xenobio/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/reagent_containers/food/snacks/monkeycube)) + if(istype(O, /obj/item/reagent_containers/food/snacks/monkeycube) && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS monkeys++ to_chat(user, "You feed [O] to [src]. It now has [monkeys] monkey cubes stored.") qdel(O) return - else if(istype(O, /obj/item/storage/bag)) + else if(istype(O, /obj/item/storage/bag) && (upgradetier & XENOBIO_UPGRADE_MONKEYS)) //CIT CHANGE - makes monkey-related actions require XENOBIO_UPGRADE_MONKEYS var/obj/item/storage/P = O var/loaded = 0 for(var/obj/G in P.contents) @@ -96,7 +96,7 @@ if (loaded) to_chat(user, "You fill [src] with the monkey cubes stored in [O]. [src] now has [monkeys] monkey cubes stored.") return - else if(istype(O, /obj/item/slimepotion/slime)) + else if(istype(O, /obj/item/slimepotion/slime) && (upgradetier & XENOBIO_UPGRADE_SLIMEADV)) // CIT CHANGE - makes giving slimes potions via console require XENOBIO_UPGRADE_SLIMEADV var/replaced = FALSE if(user && !user.transferItemToLoc(O, src)) return diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 5b7c5eb804..3340486d2f 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -825,9 +825,11 @@ L.regenerate_icons() qdel(src) -/obj/item/slimepotion/slimeradio +/obj/item/slimepotion/slime/slimeradio name = "bluespace radio potion" desc = "A strange chemical that grants those who ingest it the ability to broadcast and recieve subscape radio waves." + icon = 'icons/obj/chemical.dmi' + icon_state = "potgrey" /obj/item/slimepotion/slime/slimeradio/attack(mob/living/M, mob/user) if(!ismob(M)) diff --git a/code/modules/shuttle/arrivals.dm b/code/modules/shuttle/arrivals.dm index 398d350577..5090800ff4 100644 --- a/code/modules/shuttle/arrivals.dm +++ b/code/modules/shuttle/arrivals.dm @@ -108,10 +108,14 @@ return FALSE /obj/docking_port/mobile/arrivals/proc/PersonCheck() - for(var/M in (GLOB.alive_mob_list & GLOB.player_list)) - var/mob/living/L = M - if((get_area(M) in areas) && L.stat != DEAD) - return TRUE + for(var/V in GLOB.player_list) + var/mob/M = V + if((get_area(M) in areas) && M.stat != DEAD) + if(!iscameramob(M)) + return TRUE + var/mob/camera/C = M + if(C.move_on_shuttle) + return TRUE return FALSE /obj/docking_port/mobile/arrivals/proc/NukeDiskCheck() diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 19162d5259..ee02bd524f 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th else return user.ranged_ability = src - user.client.click_intercept = user.ranged_ability + user.click_intercept = src add_mousepointer(user.client) ranged_ability_user = user if(msg) @@ -87,7 +87,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th if(!ranged_ability_user || !ranged_ability_user.client || (ranged_ability_user.ranged_ability && ranged_ability_user.ranged_ability != src)) //To avoid removing the wrong ability return ranged_ability_user.ranged_ability = null - ranged_ability_user.client.click_intercept = null + ranged_ability_user.click_intercept = null remove_mousepointer(ranged_ability_user.client) if(msg) to_chat(ranged_ability_user, msg) diff --git a/code/modules/uplink/uplink_devices.dm b/code/modules/uplink/uplink_devices.dm index 737f92a9ef..0220c4d179 100644 --- a/code/modules/uplink/uplink_devices.dm +++ b/code/modules/uplink/uplink_devices.dm @@ -12,6 +12,11 @@ GET_COMPONENT(hidden_uplink, /datum/component/uplink) hidden_uplink.set_gamemode(/datum/game_mode/nuclear) +/obj/item/device/radio/uplink/clownop/Initialize() + . = ..() + GET_COMPONENT(hidden_uplink, /datum/component/uplink) + hidden_uplink.set_gamemode(/datum/game_mode/nuclear/clown_ops) + /obj/item/device/multitool/uplink/Initialize(mapload, _owner, _tc_amount = 20) . = ..() AddComponent(/datum/component/uplink, _owner, FALSE, TRUE, null, _tc_amount) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index e3da5aba80..cc1d1d2a47 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -157,6 +157,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) with suppressors." item = /obj/item/gun/ballistic/automatic/pistol cost = 7 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/revolver name = "Syndicate Revolver" @@ -164,6 +165,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/revolver cost = 13 surplus = 50 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + +/datum/uplink_item/dangerous/pie_cannon + name = "Banana Cream Pie Cannon" + desc = "A special pie cannon for a special clown, this gadget can hold up to 20 pies and automatically fabricates one every two seconds!" + cost = 10 + item = /obj/item/pneumatic_cannon/pie/selfcharge + surplus = 0 + include_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/shotgun name = "Bulldog Shotgun" @@ -243,6 +253,16 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) pocketed when inactive. Activating it produces a loud, distinctive noise." item = /obj/item/melee/transforming/energy/sword/saber cost = 8 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + +/datum/uplink_item/dangerous/clownsword + name = "Bananium Energy Sword" + desc = "An energy sword that deals no damage, but will slip anyone it contacts, be it by melee attack, thrown \ + impact, or just stepping on it. Beware friendly fire, as even anti-slip shoes will not protect against it." + item = /obj/item/melee/transforming/energy/sword/bananium + cost = 3 + surplus = 0 + include_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/doublesword name = "Double-Bladed Energy Sword" @@ -251,6 +271,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/twohanded/dualsaber player_minimum = 25 cost = 16 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/doublesword/get_discount() return pick(4;0.8,2;0.65,1;0.5) @@ -277,6 +298,25 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) in addition to dealing high amounts of damage to nearby personnel." item = /obj/item/grenade/syndieminibomb cost = 6 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + +/datum/uplink_item/dangerous/bombanana + name = "Bombanana" + desc = "A banana with an explosive taste! discard the peel quickly, as it will explode with the force of a syndicate minibomb \ + a few seconds after the banana is eaten." + item = /obj/item/reagent_containers/food/snacks/grown/banana/bombanana + cost = 4 //it is a bit cheaper than a minibomb because you have to take off your helmet to eat it, which is how you arm it + surplus = 0 + include_modes = list(/datum/game_mode/nuclear/clown_ops) + +/datum/uplink_item/dangerous/tearstache + name = "Teachstache Grenade" + desc = "A teargas grenade that launches sticky moustaches onto the face of anyone not wearing a clown or mime mask. The moustaches will \ + remain attached to the face of all targets for one minute, preventing the use of breath masks and other such devices." + item = /obj/item/grenade/chem_grenade/teargas/moustache + cost = 3 + surplus = 0 + include_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/foamsmg name = "Toy Submachine Gun" @@ -284,7 +324,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/automatic/c20r/toy cost = 5 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/foammachinegun name = "Toy Machine Gun" @@ -293,7 +333,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/automatic/l6_saw/toy cost = 10 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/viscerators name = "Viscerator Delivery Grenade" @@ -302,7 +342,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/grenade/spawnergrenade/manhacks cost = 5 surplus = 35 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/bioterrorfoam name = "Chemical Foam Grenade" @@ -312,7 +352,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/grenade/chem_grenade/bioterrorfoam cost = 5 surplus = 35 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/bioterror name = "Biohazardous Chemical Sprayer" @@ -322,7 +362,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/reagent_containers/spray/chemsprayer/bioterror cost = 20 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/stealthy_weapons/virus_grenade name = "Fungal Tuberculosis Grenade" @@ -332,7 +372,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/syndie_kit/tuberculosisgrenade cost = 12 surplus = 35 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/dangerous/guardian name = "Holoparasites" @@ -341,7 +381,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/syndie_kit/guardian cost = 18 surplus = 0 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) player_minimum = 25 // Ammunition @@ -355,24 +395,28 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) are dirt cheap but are half as effective as .357 rounds." item = /obj/item/ammo_box/magazine/m10mm cost = 1 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/ammo/pistolap name = "10mm Armour Piercing Magazine" desc = "An additional 8-round 10mm magazine; compatible with the Stechkin Pistol. These rounds are less effective at injuring the target but penetrate protective gear." item = /obj/item/ammo_box/magazine/m10mm/ap cost = 2 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/ammo/pistolfire name = "10mm Incendiary Magazine" desc = "An additional 8-round 10mm magazine; compatible with the Stechkin Pistol. Loaded with incendiary rounds which ignite the target." item = /obj/item/ammo_box/magazine/m10mm/fire cost = 2 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/ammo/pistolhp name = "10mm Hollow Point Magazine" desc = "An additional 8-round 10mm magazine; compatible with the Stechkin Pistol. These rounds are more damaging but ineffective against armour." item = /obj/item/ammo_box/magazine/m10mm/hp cost = 3 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/ammo/pistolaps name = "9mm Handgun Magazine" @@ -394,6 +438,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) For when you really need a lot of things dead." item = /obj/item/ammo_box/a357 cost = 4 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/ammo/shotgun cost = 2 @@ -517,7 +562,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) and broca systems, making it impossible for them to move or speak for some time." item = /obj/item/storage/box/syndie_kit/bioterror cost = 6 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) //Support and Mechs /datum/uplink_item/support @@ -532,6 +577,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/antag_spawner/nuke_ops cost = 25 refundable = TRUE + include_modes = list(/datum/game_mode/nuclear) /datum/uplink_item/support/reinforcement/assault_borg name = "Syndicate Assault Cyborg" @@ -562,6 +608,20 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/mecha/combat/marauder/mauler/loaded cost = 140 +/datum/uplink_item/support/honker + name = "Dark H.O.N.K." + desc = "A clown combat mech equipped with bombanana peel and tearstache grenade launchers, as well as the ubiquitous HoNkER BlAsT 5000." + item = /obj/mecha/combat/honker/dark/loaded + cost = 80 + include_modes = list(/datum/game_mode/nuclear/clown_ops) + +/datum/uplink_item/support/clown_reinforcement + name = "Clown Reinforcements" + desc = "Call in an additional clown to share the fun, equipped with full starting gear, but no telecrystals." + item = /obj/item/antag_spawner/nuke_ops/clown + cost = 20 + include_modes = list(/datum/game_mode/nuclear/clown_ops) + // Stealthy Weapons /datum/uplink_item/stealthy_weapons category = "Stealthy and Inconspicuous Weapons" @@ -573,13 +633,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/sleeping_carp_scroll cost = 17 surplus = 0 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/stealthy_weapons/cqc name = "CQC Manual" desc = "A manual that teaches a single user tactical Close-Quarters Combat before self-destructing." item = /obj/item/cqc_manual - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) cost = 13 surplus = 0 @@ -661,6 +721,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/suppressor cost = 3 surplus = 10 + exclude_modes = list(/datum/game_mode/nuclear/clown_ops) /datum/uplink_item/stealthy_weapons/pizza_bomb name = "Pizza Bomb" @@ -700,7 +761,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) They do not work on heavily lubricated surfaces." item = /obj/item/clothing/shoes/chameleon/noslip cost = 2 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) player_minimum = 20 /datum/uplink_item/stealthy_tools/syndigaloshes/nuke @@ -709,6 +770,16 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) exclude_modes = list() include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/stealthy_tools/combatbananashoes + name = "Combat Banana Shoes" + desc = "While making the wearer immune to most slipping attacks like regular combat clown shoes, these shoes \ + can generate a large number of synthetic banana peels as the wearer walks, slipping up would-be pursuers. They also \ + squeek significantly louder." + item = /obj/item/clothing/shoes/clown_shoes/banana_shoes/combat + cost = 6 + surplus = 0 + include_modes = list(/datum/game_mode/nuclear/clown_ops) + /datum/uplink_item/stealthy_tools/frame name = "F.R.A.M.E. PDA Cartridge" desc = "When inserted into a personal digital assistant, this cartridge gives you five PDA viruses which \ @@ -767,7 +838,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/reagent_containers/syringe/mulligan cost = 4 surplus = 30 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/stealthy_tools/emplight name = "EMP Flashlight" @@ -823,7 +894,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) provides the user with superior armor and mobility compared to the standard syndicate hardsuit." item = /obj/item/clothing/suit/space/hardsuit/syndi/elite cost = 8 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) exclude_modes = list() /datum/uplink_item/suits/hardsuit/shielded @@ -832,7 +903,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) The shields can handle up to three impacts within a short duration and will rapidly recharge while not under fire." item = /obj/item/clothing/suit/space/hardsuit/shielded/syndi cost = 30 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) exclude_modes = list() // Devices and Tools @@ -882,7 +953,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) and other supplies helpful for a field medic." item = /obj/item/storage/firstaid/tactical cost = 4 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/device_tools/syndietome name = "Syndicate Tome" @@ -952,7 +1023,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) 'Advanced Magboots' slow you down in simulated-gravity environments much like the standard issue variety." item = /obj/item/clothing/shoes/magboots/syndie cost = 2 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/device_tools/c4 name = "Composition C-4" @@ -1004,6 +1075,18 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/device/sbeacondrop/bomb cost = 11 +/datum/uplink_item/device_tools/clown_bomb_clownops + name = "Clown Bomb" + desc = "The Clown bomb is a hilarious device capable of massive pranks. It has an adjustable timer, \ + with a minimum of 60 seconds, and can be bolted to the floor with a wrench to prevent \ + movement. The bomb is bulky and cannot be moved; upon ordering this item, a smaller beacon will be \ + transported to you that will teleport the actual bomb to it upon activation. Note that this bomb can \ + be defused, and some crew may attempt to do so." + item = /obj/item/device/sbeacondrop/clownbomb + cost = 15 + surplus = 0 + include_modes = list(/datum/game_mode/nuclear/clown_ops) + /datum/uplink_item/device_tools/syndicate_detonator name = "Syndicate Detonator" desc = "The Syndicate detonator is a companion device to the Syndicate bomb. Simply press the included button \ @@ -1012,7 +1095,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) the blast radius before using the detonator." item = /obj/item/device/syndicatedetonator cost = 3 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/device_tools/rad_laser name = "Radioactive Microlaser" @@ -1029,7 +1112,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/device/assault_pod cost = 30 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/device_tools/shield name = "Energy Shield" @@ -1040,20 +1123,30 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) surplus = 20 include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/device_tools/shield + name = "Bananium Energy Shield" + desc = "A clown's most powerful defensive weapon, this personal shield provides near immunity to ranged energy attacks \ + by bouncing them back at the ones who fired them. It can also be thrown to bounce off of people, slipping them, \ + and returning to you even if you miss. WARNING: DO NOT ATTEMPT TO STAND ON SHIELD WHILE DEPLOYED, EVEN IF WEARING ANTI-SLIP SHOES." + item = /obj/item/shield/energy/bananium + cost = 16 + surplus = 0 + include_modes = list(/datum/game_mode/nuclear/clown_ops) + /datum/uplink_item/device_tools/medgun name = "Medbeam Gun" desc = "A wonder of Syndicate engineering, the Medbeam gun, or Medi-Gun enables a medic to keep his fellow \ operatives in the fight, even while under fire." item = /obj/item/gun/medbeam cost = 15 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/device_tools/potion name = "Syndicate Sentience Potion" item = /obj/item/slimepotion/slime/sentience/nuclear desc = "A potion recovered at great risk by undercover syndicate operatives and then subsequently modified with syndicate technology. Using it will make any animal sentient, and bound to serve you, as well as implanting an internal radio for communication and an internal ID card for opening doors." cost = 4 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) /datum/uplink_item/device_tools/telecrystal name = "Raw Telecrystal" @@ -1189,7 +1282,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) // Role-specific items /datum/uplink_item/role_restricted category = "Role-Restricted" - exclude_modes = list(/datum/game_mode/nuclear) + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) surplus = 0 /datum/uplink_item/role_restricted/reverse_revolver @@ -1346,7 +1439,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/badass/costumes surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) cost = 4 cant_discount = TRUE @@ -1382,7 +1475,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/structure/closet/crate cost = 20 player_minimum = 25 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) cant_discount = TRUE var/starting_crate_value = 50 diff --git a/config/game_options.txt b/config/game_options.txt index d056fb7569..ee080b6014 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -91,6 +91,7 @@ PROBABILITY EXTENDED 0 PROBABILITY SECRET_EXTENDED 0 PROBABILITY DEVIL 0 PROBABILITY DEVIL_AGENTS 0 +PROBABILITY CLOWNOPS 0 ## You probably want to keep sandbox off by default for secret and random. PROBABILITY SANDBOX 0 diff --git a/icons/effects/parallax.dmi b/icons/effects/parallax.dmi index 181b76007d..22f091ff11 100755 Binary files a/icons/effects/parallax.dmi and b/icons/effects/parallax.dmi differ diff --git a/icons/mecha/mecha.dmi b/icons/mecha/mecha.dmi index 5b54c52766..a517f8d7e6 100644 Binary files a/icons/mecha/mecha.dmi and b/icons/mecha/mecha.dmi differ diff --git a/icons/mob/human_parts_greyscale.dmi b/icons/mob/human_parts_greyscale.dmi index df7b5eda9e..529968d7f8 100644 Binary files a/icons/mob/human_parts_greyscale.dmi and b/icons/mob/human_parts_greyscale.dmi differ diff --git a/icons/mob/inhands/equipment/shields_lefthand.dmi b/icons/mob/inhands/equipment/shields_lefthand.dmi index 6b5ca3857d..e9d0dd0d2b 100644 Binary files a/icons/mob/inhands/equipment/shields_lefthand.dmi and b/icons/mob/inhands/equipment/shields_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/shields_righthand.dmi b/icons/mob/inhands/equipment/shields_righthand.dmi index e3de91087a..dda17156fe 100644 Binary files a/icons/mob/inhands/equipment/shields_righthand.dmi and b/icons/mob/inhands/equipment/shields_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/swords_lefthand.dmi b/icons/mob/inhands/weapons/swords_lefthand.dmi index 5e7b04a25a..d6950f4e1e 100644 Binary files a/icons/mob/inhands/weapons/swords_lefthand.dmi and b/icons/mob/inhands/weapons/swords_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/swords_righthand.dmi b/icons/mob/inhands/weapons/swords_righthand.dmi index b330dc8f95..9655829113 100644 Binary files a/icons/mob/inhands/weapons/swords_righthand.dmi and b/icons/mob/inhands/weapons/swords_righthand.dmi differ diff --git a/icons/mob/mam_body_markings.dmi b/icons/mob/mam_body_markings.dmi index be515b3459..d53fa87915 100644 Binary files a/icons/mob/mam_body_markings.dmi and b/icons/mob/mam_body_markings.dmi differ diff --git a/icons/mob/mam_bodyparts.dmi b/icons/mob/mam_bodyparts.dmi index 0511211b8d..66b3b18caf 100644 Binary files a/icons/mob/mam_bodyparts.dmi and b/icons/mob/mam_bodyparts.dmi differ diff --git a/icons/mob/mam_taur.dmi b/icons/mob/mam_taur.dmi index 8cce485c7a..8cd02f862e 100644 Binary files a/icons/mob/mam_taur.dmi and b/icons/mob/mam_taur.dmi differ diff --git a/icons/mob/pets.dmi b/icons/mob/pets.dmi index 3a076f8d7b..12cdbb2817 100644 Binary files a/icons/mob/pets.dmi and b/icons/mob/pets.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index 1022770acd..ef17aa3dc7 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/grenade.dmi b/icons/obj/grenade.dmi index c003cf238e..d6dd4c1b00 100644 Binary files a/icons/obj/grenade.dmi and b/icons/obj/grenade.dmi differ diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi index 8e930e1ee7..03cf226b84 100644 Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ diff --git a/icons/obj/machines/nuke.dmi b/icons/obj/machines/nuke.dmi index ceace5c250..c98e4c1cf6 100644 Binary files a/icons/obj/machines/nuke.dmi and b/icons/obj/machines/nuke.dmi differ diff --git a/modular_citadel/code/datums/mood_events/generic_negative_events.dm b/modular_citadel/code/datums/mood_events/generic_negative_events.dm new file mode 100644 index 0000000000..cc7e714819 --- /dev/null +++ b/modular_citadel/code/datums/mood_events/generic_negative_events.dm @@ -0,0 +1,11 @@ +// Citadel-specific negative moodlets + +/datum/mood_event/plushjack + description = "I have butchered a plush recently.\n" + mood_change = -1 + timeout = 1200 + +/datum/mood_event/plush_nostuffing + description = "A plush I tried to pet had no stuffing...\n" + mood_change = -1 + timeout = 1200 diff --git a/modular_citadel/code/datums/mood_events/generic_positive_events.dm b/modular_citadel/code/datums/mood_events/generic_positive_events.dm new file mode 100644 index 0000000000..40df233fbf --- /dev/null +++ b/modular_citadel/code/datums/mood_events/generic_positive_events.dm @@ -0,0 +1,21 @@ +// Citadel-specific positive moodlets + +/datum/mood_event/headpat + description = "Headpats are nice.\n" + mood_change = 2 + timeout = 1200 + +/datum/mood_event/hugbox + description = "I hugged a box of hugs recently.\n" + mood_change = 1 + timeout = 1200 + +/datum/mood_event/plushpet + description = "I pet a plush recently.\n" + mood_change = 1 + timeout = 3000 + +/datum/mood_event/plushplay + description = "I've played with plushes recently.\n" + mood_change = 3 + timeout = 3000 diff --git a/modular_citadel/code/datums/mood_events/moodular.dm b/modular_citadel/code/datums/mood_events/moodular.dm new file mode 100644 index 0000000000..b764c0027e --- /dev/null +++ b/modular_citadel/code/datums/mood_events/moodular.dm @@ -0,0 +1,42 @@ +// Modular stuff to use with Citadel-specific moods. + +// box of hugs +/obj/item/storage/box/hug/attack_self(mob/user) + . = ..() + GET_COMPONENT_FROM(mood, /datum/component/mood, user) + if(mood) + mood.add_event("hugbox", /datum/mood_event/hugbox) + +// headpats (IMPORTANT) +/mob/living/carbon/help_shake_act(mob/living/carbon/M) + . = ..() + GET_COMPONENT_FROM(mood, /datum/component/mood, src) + if(mood) + mood.add_event("headpat", /datum/mood_event/headpat) + +// plush petting +/obj/item/toy/plush/attack_self(mob/user) + . = ..() + if(stuffed || grenade) + GET_COMPONENT_FROM(mood, /datum/component/mood, user) + if(mood) + mood.add_event("plushpet", /datum/mood_event/plushpet) + else + GET_COMPONENT_FROM(mood, /datum/component/mood, user) + if(mood) + mood.add_event("plush_nostuffing", /datum/mood_event/plush_nostuffing) + +// Jack the Ripper starring plush +/obj/item/toy/plush/attackby(obj/item/I, mob/living/user, params) + . = ..() + if(I.is_sharp()) + if(!grenade) + GET_COMPONENT_FROM(mood, /datum/component/mood, user) + if(mood) + mood.add_event("plushjack", /datum/mood_event/plushjack) + +// plush playing (plush-on-plush action) + if(istype(I, /obj/item/toy/plush)) + GET_COMPONENT_FROM(mood, /datum/component/mood, user) + if(mood) + mood.add_event("plushplay", /datum/mood_event/plushplay) diff --git a/modular_citadel/code/modules/research/designs/xenobio_designs.dm b/modular_citadel/code/modules/research/designs/xenobio_designs.dm new file mode 100644 index 0000000000..45ed8e83a0 --- /dev/null +++ b/modular_citadel/code/modules/research/designs/xenobio_designs.dm @@ -0,0 +1,25 @@ +/datum/design/xenobio_upgrade + name = "owo" + desc = "someone's bussin" + build_type = PROTOLATHE + materials = list(MAT_METAL = 300, MAT_GLASS = 100) + category = list("Electronics") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/xenobio_upgrade/xenobiomonkeys + name = "Xenobiology console monkey upgrade disk" + desc = "This disk will add the ability to remotely recycle monkeys via the Xenobiology console." + id = "xenobio_monkeys" + build_path = /obj/item/disk/xenobio_console_upgrade/monkey + +/datum/design/xenobio_upgrade/xenobioslimebasic + name = "Xenobiology console basic slime upgrade disk" + desc = "This disk will add the ability to remotely manipulate slimes via the Xenobiology console." + id = "xenobio_slimebasic" + build_path = /obj/item/disk/xenobio_console_upgrade/slimebasic + +/datum/design/xenobio_upgrade/xenobioslimeadv + name = "Xenobiology console advanced slime upgrade disk" + desc = "This disk will add the ability to remotely feed slimes potions via the Xenobiology console, and lift the restrictions on the number of slimes that can be stored inside the Xenobiology console. This includes the contents of the basic slime upgrade disk." + id = "xenobio_slimeadv" + build_path = /obj/item/disk/xenobio_console_upgrade/slimeadv diff --git a/modular_citadel/code/modules/research/techweb/all_nodes.dm b/modular_citadel/code/modules/research/techweb/all_nodes.dm index d8babf011b..9f1cd650b5 100644 --- a/modular_citadel/code/modules/research/techweb/all_nodes.dm +++ b/modular_citadel/code/modules/research/techweb/all_nodes.dm @@ -1,3 +1,15 @@ +/datum/techweb_node/bluespace_basic/New() + . = ..() + design_ids += "xenobio_monkeys" + +/datum/techweb_node/practical_bluespace/New() + . = ..() + design_ids += "xenobio_slimebasic" + +/datum/techweb_node/adv_bluespace/New() + . = ..() + design_ids += "xenobio_slimeadv" + /datum/techweb_node/computer_board_gaming id = "computer_board_gaming" display_name = "Games and Toys" diff --git a/modular_citadel/code/modules/research/xenobiology/xenobio_camera.dm b/modular_citadel/code/modules/research/xenobiology/xenobio_camera.dm new file mode 100644 index 0000000000..b700626a1d --- /dev/null +++ b/modular_citadel/code/modules/research/xenobiology/xenobio_camera.dm @@ -0,0 +1,48 @@ +/obj/machinery/computer/camera_advanced/xenobio + max_slimes = 1 + var/upgradetier = 0 + +/obj/machinery/computer/camera_advanced/xenobio/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/disk/xenobio_console_upgrade)) + var/obj/item/disk/xenobio_console_upgrade/diskthing = O + var/successfulupgrade = FALSE + for(var/I in diskthing.upgradetypes) + if(upgradetier & I) + continue + else + upgradetier |= I + successfulupgrade = TRUE + if(I == XENOBIO_UPGRADE_SLIMEADV) + max_slimes = 10 + if(successfulupgrade) + to_chat(user, "You have successfully upgraded [src] with [O].") + else + to_chat(user, "[src] already has the contents of [O] installed!") + return + . = ..() + +/obj/item/disk/xenobio_console_upgrade + name = "Xenobiology console upgrade disk" + desc = "Allan please add detail." + icon_state = "datadisk5" + var/list/upgradetypes = list() + +/obj/item/disk/xenobio_console_upgrade/admin + name = "Xenobio all access thing" + desc = "'the consoles are literally useless!!!!!!!!!!!!!!!'" + upgradetypes = list(XENOBIO_UPGRADE_SLIMEBASIC, XENOBIO_UPGRADE_SLIMEADV, XENOBIO_UPGRADE_MONKEYS) + +/obj/item/disk/xenobio_console_upgrade/monkey + name = "Xenobiology console monkey upgrade disk" + desc = "This disk will add the ability to remotely recycle monkeys via the Xenobiology console." + upgradetypes = list(XENOBIO_UPGRADE_MONKEYS) + +/obj/item/disk/xenobio_console_upgrade/slimebasic + name = "Xenobiology console basic slime upgrade disk" + desc = "This disk will add the ability to remotely manipulate slimes via the Xenobiology console." + upgradetypes = list(XENOBIO_UPGRADE_SLIMEBASIC) + +/obj/item/disk/xenobio_console_upgrade/slimeadv + name = "Xenobiology console advanced slime upgrade disk" + desc = "This disk will add the ability to remotely feed slimes potions via the Xenobiology console, and lift the restrictions on the number of slimes that can be stored inside the Xenobiology console. This includes the contents of the basic slime upgrade disk." + upgradetypes = list(XENOBIO_UPGRADE_SLIMEBASIC, XENOBIO_UPGRADE_SLIMEADV) diff --git a/strings/tips.txt b/strings/tips.txt index 8f68853b42..fa79afd4b2 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -1,16 +1,16 @@ Where the space map levels connect is randomized every round, but are otherwise kept consistent within rounds. Remember that they are not necessarily bidirectional! You can catch thrown items by toggling on your throw mode with an empty hand active. -To crack the safe in the vault, use a stethoscope on it. -You can climb onto a table by dragging yourself onto one. This takes time and drops the items in your hands on the table. +To crack the safe in the vault, use a stethoscope or explosives on it. +You can climb onto a table by dragging yourself onto one. This takes time and drops the items in your hands on the table. Clicking on a table that someone else is climbing onto will knock them down. You can drag other players onto yourself to open the strip menu, letting you remove their equipment or force them to wear something. Note that exosuits or helmets will block your access to the clothing beneath them, and that certain items take longer to strip or put on than others. Clicking on a windoor rather then bumping into it will keep it open, you can click it again to close it. -You can spray a fire extinguisher or fire a gun while floating through space to change your direction. Simply fire opposite to where you want to go. +You can spray a fire extinguisher, throw items or fire a gun while floating through space to change your direction. Simply fire opposite to where you want to go. You can change the control scheme by pressing tab. One is WASD, the other is the arrow keys. Keep in mind that hotkeys are also changed with this. All vending machines can be hacked to obtain some contraband items from them, and some can be fed with coins to gain access to premium items. Firesuits and winter coats offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. Glass shards can be welded to make glass, and metal rods can be welded to make metal. Ores can be welded too, but this takes a lot of fuel. -If you need to drag multiple people either to safety or to space, bring a locker over and stuff them all in before hauling them off. -You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on the grab button in your active hand. An aggressive grab will allow you to place someone on a table by clicking on it, or throw them by toggling on throwing. +If you need to drag multiple people either to safety or to space, bring a locker or crate over and stuff them all in before hauling them off. +You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab will momentarily stun someone, allow you to place Mekhi on a table by clicking on it, or throw them by toggling on throwing. Holding alt and left clicking a tile will allow you to see its contents in the top right window pane, which is much faster than right clicking. The resist button will allow you to resist out of handcuffs, being buckled to a chair or bed, out of locked lockers and more. Whenever you're stuck, try resisting! You can move an item out of the way by dragging it and then clicking on an adjacent tile with an empty hand. @@ -22,47 +22,65 @@ On some maps, you can use a machine in the vault to deposit space cash for cargo As the Captain, you are one of the highest priority targets on the station. Everything from revolutions, to nuclear operatives, to traitors that need to rob you of your unique lasgun or your life are things to worry about. As the Captain, always take the nuclear disk and pinpointer with you every shift. It's a good idea to give one of these to another head you can trust with keeping it safe, such as the Head of Security. As the Captain, you have absolute access and control over the station, but this does not mean that being a horrible person won't result in mutiny and a ban. -As the Captain, you can purchase a new emergency shuttle using a communications console. Some require credits, while others give you credits in exchange. -As the Chief Medical Officer, your hypospray is like an instant injection syringe that can hold 30 units as opposed to the standard 15. +As the Captain, you can purchase a new emergency shuttle using a communications console. Some require credits, while others give you credits in exchange. Keep in mind that purchasing dangerous shuttles will incur the ire of your crew. +As the Chief Medical Officer, your hypospray is like a refillable instant injection syringe that can hold 30 units as opposed to the standard 15. As the Chief Medical Officer, coordinate and communicate with your doctors, chemists, and geneticists during a nuclear emergency, blob infestation, or some other crisis to keep people alive and fighting. As a Medical Doctor, you can attempt to drain blood from a husk with a syringe to determine the cause. If you can extract blood, it was caused by extreme temperatures or lasers, if there is no blood to extract, you have confirmed the presence of changelings. As a Medical Doctor, charcoal will not only heal toxin damage dealt by poisons, but will actively remove them. As a Medical Doctor, you can surgically implant or extract things from people's chests. This can range from putting in a bomb to pulling out an alien larva. -As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. +As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. Using disarm attempt will intentionally fail the surgery step. +As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't revivable or clonable. As a Chemist, there are dozens of chemicals that can heal, and even more that can cause harm. Experiment! As a Chemist, some chemicals can only be synthesized by heating up the contents in the chemical heater. +As a Chemist, you will be expected to supply crew with certain chemicals. For example, clonexadone and mannitol for the cryo tubes, unstable mutagen and saltpetre for botany as well as healing pills and patches for the front desk. +As a Chemist, you can make 100u bottles from plastic sheets. The ChemMaster can produce infinite 30u glass bottles as well. As a Geneticist, you can eject someone from cloning early by disabling power in genetics. Note that they will suffer more genetic damage and may lose vital organs from this. As a Geneticist, becoming a hulk makes you capable of dealing high melee damage, stunlocking people, and punching through walls. However, you can't fire guns, will lose your hulk status if you take too much damage, and are not considered a human by the AI while you are a hulk. As the Virologist, your viruses can range from healing powers so great that you can heal out of critical status, or diseases so dangerous they can kill the entire crew with airborne spontaneous combustion. Experiment! As the Virologist, you only require small amounts of vaccine to heal a sick patient. Work with the Chemist to distribute your cures more efficiently. As the Research Director, you can take AIs out of their cores by loading them into an intelliCard, and then from there into an AI system integrity restorer computer to revive and/or repair them. As the Research Director, you can lock down cyborgs instead of blowing them up. Then you can have their laws reset or if that doesn't work, safely dismantled. -As a Scientist, you can inject yourself with the mutation toxin extracted from green slimes to become one of many races, including a slimeperson, who will never be attacked by slimes, can split and even regenerate limbs! +As a Scientist, you can use the mutation toxin obtained from green slimes to turn yourself into a jelly mutant. Each subspecies has unique features - for example telepathic powers, duplicating bodies or integrating slime extracts! As a Scientist, you can maximize the number of uses you get out of a slime by feeding it slime steroid, created from purple slimes, while alive. You can then apply extract enhancer, created from cerulean slimes, on each extract. -As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signalling device. This will leave behind an anomaly core, which is good for research or the construction of a Phazon mech! -As a Scientist, researchable machine parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions. -As a Scientist, with high enough bluespace research, you can build machines that can teleport objects and people all around the station! Look into setting up a telescience department if you can. +As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signalling device. This will leave behind an anomaly core, which can be used to construct a Phazon mech! +As a Scientist, researchable stock parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions. +As a Scientist, you can generate research points by letting the tachyon-doppler array record increasingly large explosions. +As a Scientist, getting drunk just enough will speed up research. Skol! As a Roboticist, keep an ear out for anomaly announcements. If you get your hands on an anomaly core, you can build a Phazon mech! As a Roboticist, you can repair your cyborgs with a welding tool. If they have taken burn damage from lasers, you can remove their battery, expose the wiring with a screwdriver and replace their wires with a cable coil. +As a Roboticist, you can reset a cyborg's module by cutting and mending the reset wire with a wire cutter. +As a Roboticist, you can greatly help out Shaft Miners by building a Firefighter APLU equipped with a hydraulic clamp and plasma cutter. The mech is ash storm proof and can even walk across lava! +As a Roboticist, you can augment people with cyborg limbs. Augmented limbs can easily be repaired with cables and welders. As the AI, you can click on people's names to look at them. This only works if there are cameras that can see them. As the AI, you can quickly open and close doors by holding shift while clicking them, bolt them when holding ctrl, and even shock them while holding alt. As the AI, you can take pictures with your camera and upload them to newscasters. -As a Cyborg, choose your module carefully, as only a roboticist can let you repick it. If possible, refrain from choosing a module until a situation that requires one occurs. +As a Cyborg, choose your module carefully, as only cutting and mending your reset wire will let you repick it. If possible, refrain from choosing a module until a situation that requires one occurs. As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes can easily stunlock you and you cannot do any precision work as you lack hands. As a Cyborg, you are impervious to fires and heat. If you are rogue, you can release plasma fires everywhere and walk through them without a care in the world! As a Cyborg, you are extremely vulnerable to EMPs as EMPs both stun you and damage you. The ion rifle in the armory or a traitor with an EMP kit can kill you in seconds. +As a Service Cyborg, your spray can knocks people down. However, it is blocked by gas masks. +As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by placing them on the floor and using a screwdriver on them. +As a Medical Cyborg, you can fully perform surgery and even augment people. +As a Janitor Cyborg, you are the bane of all slaughter demons and even Bubblegum himself. Cleaning up blood stains will severely gimp them. As the Chief Engineer, you can rename areas or create entirely new ones using your station blueprints. As the Chief Engineer, your hardsuit is significantly better than everybody else's. It boasts better protection, and is completely heat and fire proof. As the Chief Engineer, you can spy on and even forge PDA communications with the message monitor console! The key is in your office. +As the Chief Engineer, your locker contains a jetpack upgrade that can be attached to hardsuits. As an Engineer, the supermatter shard is an extremely dangerous piece of equipment: touching it will disintegrate you. As an Engineer, you can electrify grilles by placing wire "nodes" beneath them: the big seemingly unconnected bulges from a half completed wiring job. -As an Engineer, return to Engineering once in a while to check on the singularity or energy ball and the SMES cells. It's always a good idea to make sure containment isn't compromised. -As an Engineer, you can power the station solely with the solar arrays. While uninteresting, it is a much safer alternative to the singularity engine. +As an Engineer, return to Engineering once in a while to check on the engine and SMES cells. It's always a good idea to make sure containment isn't compromised. +As an Engineer, you can power the station solely with the solar arrays. While uninteresting, it is a much safer alternative to regular engines. +As an Engineer, you can cool the supermatter crystal by spraying it with a fire extinguisher. Only for the brave! As an Engineer, you can repair windows by using a welding tool on them while on any intent other than harm. -As an Engineer, you can lock emitters using your ID card to prevent others from disabling them. +As an Engineer, you can lock APCs, fire alarms, emitters and radiation collectors using your ID card to prevent others from disabling them. +As an Engineer, you can convert tesla coils into corona analyzers by using a screwdriver on the circuit board. They will produce less power, but generate research points! +As an Engineer, you can use radiation collectors to generate research points. Load them with a 50/50 oxygen/tritium tank and use a multitool to switch them to research mode. As an Atmospheric Technician, you can unwrench a pipe regardless of the pressures of the gases inside, but if they're too high they can burst out and injure you! As an Atmospheric Technician, look into replacing your gas pumps with volumetric gas pumps, as those move air in flat numerical amounts, rather than percentages which leave trace gases. -As an Atmospheric Technician, you are better suited to fighting fires than anyone else. As such, you have access to better firesuits, nanofrost sprays, and a completely heat and fire proof rigsuit. +As an Atmospheric Technician, you are better suited to fighting fires than anyone else. As such, you have access to better firesuits, backpack firefighter tanks, and a completely heat and fire proof rigsuit. +As an Atmospheric Technician, your backpack firefighter tank can launch resin. This resin will extinguish fires and replace any gases with a safe, room-temperature airmix. +As an Atmospheric Technician, your ATMOS holofan projector blocks gases while allowing objects to pass through. With it, you can quickly contain gas spills, fires and hull breaches. Or, use it to seal a plasmaman cloning room. +As an Atmospheric Technician, burning a plasma/oxygen mix inside the incinerator will not only produce power, but also gases such as tritium and water vapor. As the Head of Security, you are expected to coordinate your security force to handle any threat that comes to the station. Sometimes it means making use of the armory to handle a blob, sometimes it means being ruthless during a revolution or cult. As the Head of Security, you can call for executions or forced cyborgization, but may require the Captain's approval. As the Head of Security, don't let the power go to your head. You may have high access, great equipment, and a miniature army at your side, but being a terrible person without a good reason is grounds for banning. @@ -77,6 +95,7 @@ As a Security Officer, mindshield implants can only prevent someone from being t As a Security Officer, examining someone while wearing sechuds or HUDsunglasses will let you set their arrest level, which will cause Beepsky and other security bots to chase after them. As the Detective, people leave fingerprints everywhere and on everything. With the exception of white latex, gloves will hide them. All is not lost, however, as gloves leave fibers specific to their kind such as black or nitrile, pointing to a general department. As the Detective, you can use your forensics scanner from a distance. +As the Detective, your revolver can be loaded with .357 ammunition obtained from a hacked autolathe. Firing it has a decent chance to blow up your revolver. As the Lawyer, try to negotiate with the Warden if sentences seem too high for the crime. As the Lawyer, you can try to convince the captain and Head of Security to hold trials for prisoners in the courtroom. As the Head of Personnel, you are not higher ranking than other heads of staff, even though you are expected to take the Captain's place first should he go missing. If the situation seems too rough for you, consider allowing another head to become temporary Captain. @@ -87,29 +106,34 @@ As the Mime, your oath of silence is your source of power. Breaking it robs you As the Clown, if you lose your banana peel, you can still slip people with your PDA! Honk! As the Clown, eating bananas heals you slightly. Honk! As the Clown, your Holy Grail is the mineral bananium, which can be given to the Roboticist to build you a fun and robust mech beloved by everyone. +As the Clown, you can use your stamp on a sheet of cardboard as the first step of making a honkbot. Fun for the whole crew! As the Chaplain, your null rod has a lot of functions: it can convert water into holy water, which if spread on the ground prevents wizards from jaunting away, can destroy cultist runes by hitting them, and is a very powerful weapon to boot! As the Chaplain, your bible is also a container that can store small items. Depending on your god, your starting bible may come with a surprise! As the Chaplain, you are much more likely to get a response by praying to the gods than most people. To boost your chances, make altars with colorful crayon runes, lit candles, and wire art. As a Botanist, you can hack the MegaSeed Vendor to get access to more exotic seeds. These seeds can alternatively be ordered from cargo. As a Botanist, you can mutate the plants growing in your hydroponics trays with unstable mutagen or, as an alternative, crude radioactives from chemistry to get special variations. As a Botanist, you should look into increasing the potency of your plants. This increases the size, amount of chemicals, points gained from grinding them in the biogenerator, and lets people know you are a proficient botanist. +As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine (blumpkin) + radium and phosphorus (glowshrooms) equals unstable mutagen! As a Cook, you can load your food into snack vending machines. As a Cook, you can rename your custom made food with a pen. As a Cook, any food you make will be much healthier than the junk food found in vendors. Having the crew routinely eating from you will provide minor buffs. +As a Cook, being in the kitchen will make you remember the basics of Close Quarters Cooking. It is highly effective at removing Assistants from your workplace. As the Bartender, the drinks you start with only give you the basics. If you want more advanced mixtures, look into working with chemistry, hydroponics, or even mining for things to grind up and throw in! As the Bartender, you can use a circular saw on your shotgun to make it easier to store. -As a Janitor, if someone steals your janicart, you can instead use your space cleaner spray, grenades, water sprayer or order another from Cargo. +As a Janitor, if someone steals your janicart, you can instead use your space cleaner spray, grenades, water sprayer, exact bloody revenge or order another from Cargo. As a Janitor, mousetraps can be used to create bombs or booby-trap containers. -As the Curator, you are not completely defenseless. Your laser pointer can blind humans and cyborgs, and you can hide items in wirecut books. +As the Curator, you are not completely defenseless. Your whip easily disarms people, your laser pointer can blind humans and cyborgs, and you can hide items in wirecut books. As the Curator, be sure to keep the shelves stocked and the library clean for crew. As a Cargo Technician, you can hack MULEbots to make them faster, run over people in their way, and even let you ride them! As a Cargo Technician, you can order contraband items from the supply shuttle console by de-constructing it and using a multitool on the circuit board, the re-assembling it. As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, liquid containers, plasma sheets, rare seeds from hydroponics, and more! As the Quartermaster, be sure to check the manifests on crates you receive to make sure all the info is correct. If there's a mistake, stamp the manifest DENIED and send it back in a crate with the items untouched for a refund! +As the Quartermaster, you can construct an express supply console that instantly delivers crates by drop pod. The impact will cause a small explosion as well. As a Shaft Miner, the northern side of Lavaland has a lot more rare minerals than on the south. As a Shaft Miner, every monster on Lavaland has a pattern you can exploit to minimize damage from the encounters. -As a Shaft Miner, you can harvest goliath plates from goliaths and upgrade your explorer's suit with them, greatly reducing incoming melee damage. +As a Shaft Miner, you can harvest goliath plates from goliaths and upgrade your explorer's suit, mining hardsuits as well as Firefighter APLUs with them, greatly reducing incoming melee damage. As a Shaft Miner, always have a GPS on you, so a fellow miner or cyborg can come to save you if you die. +As a Shaft Miner, you can craft a variety of equipment from the local fauna. Bone axes, lava boats and ash drake armour are just a few of them! As a Traitor, the cryptographic sequencer (emag) can not only open doors, but also lockers, crates, APCs and more. It can hack cyborgs, and even cause bots to go berserk. Use it on the right machines, and you can even order more traitor gear or contact the Syndicate. Experiment! As a Traitor, subverting the AI to serve you can make it an extremely powerful ally. However, be careful of the wording in the laws you give it, as it may use your poorly written laws against you! As a Traitor, the Captain and the Head of Security are two of the most difficult to kill targets on the station. If either one is your target, plan carefully. @@ -161,7 +185,7 @@ As a Servant, wraith spectacles let you see everything through walls at virtuall As a Servant, declaring war empowers a huge amount of your tools and constructs, and makes you into a spaceproof, armored clockwork automaton. As a Servant, converting or sabotaging Science and Genetics can make defending the Ark much easier. As a Servant, the Clockwork Armaments scripture allows you to summon armor and/or a weapon at will. Use it whenever you unlock it! -You can deconvert Cultists of Nar-Sie and Servants of Ratvar by feeding them large amounts of holy water. +You can deconvert Cultists of Nar-Sie and Servants of Ratvar by feeding them large amounts of holy water. Unlike revolutionaries, implanting them with mindshield implants won't do it! Tiles sprayed with holy water will permanently block Servants of Ratvar from teleporting onto them. The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against both cults and large amounts of it are a great contributor to success against them. The Chaplain's holy weapon will kill clockwork marauders in two hits. @@ -190,3 +214,6 @@ As a Devil, as long as you control at least one other soul, you will automatical At which time a Devil's nameth is spake on the tongue of man, the Devil may appeareth. As a Security Officer, remember that correlation does not equal causation. Someone may have just been at the wrong place at the wrong time! As a Security Officer, remember that you can attach a sec-lite to your taser or your helmet! +You can swap floor tiles by holding a crowbar in one hand and a stack of tiles in the other. +When hacking doors, cutting and mending the "test light wire" will restore power to the door. +When crafting most items, you can either manually combine parts or use the crafting menu. \ No newline at end of file diff --git a/tgstation.dme b/tgstation.dme index b52ba0bc2f..443d286b4d 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -261,6 +261,7 @@ #include "code\controllers\subsystem\processing\processing.dm" #include "code\controllers\subsystem\processing\projectiles.dm" #include "code\controllers\subsystem\processing\traits.dm" +#include "code\controllers\subsystem\processing\wet_floors.dm" #include "code\datums\action.dm" #include "code\datums\ai_laws.dm" #include "code\datums\armor.dm" @@ -336,6 +337,7 @@ #include "code\datums\components\spooky.dm" #include "code\datums\components\squeek.dm" #include "code\datums\components\thermite.dm" +#include "code\datums\components\wet_floor.dm" #include "code\datums\components\decals\blood.dm" #include "code\datums\diseases\_disease.dm" #include "code\datums\diseases\_MobProcs.dm" @@ -480,6 +482,9 @@ #include "code\game\gamemodes\changeling\changeling.dm" #include "code\game\gamemodes\changeling\traitor_chan.dm" #include "code\game\gamemodes\clock_cult\clock_cult.dm" +#include "code\game\gamemodes\clown_ops\bananium_bomb.dm" +#include "code\game\gamemodes\clown_ops\clown_ops.dm" +#include "code\game\gamemodes\clown_ops\clown_weapons.dm" #include "code\game\gamemodes\cult\cult.dm" #include "code\game\gamemodes\devil\devil_game_mode.dm" #include "code\game\gamemodes\devil\game_mode.dm" @@ -1209,6 +1214,7 @@ #include "code\modules\antagonists\morph\morph_antag.dm" #include "code\modules\antagonists\nightmare\nightmare.dm" #include "code\modules\antagonists\ninja\ninja.dm" +#include "code\modules\antagonists\nukeop\clownop.dm" #include "code\modules\antagonists\nukeop\nukeop.dm" #include "code\modules\antagonists\nukeop\equipment\nuclear_challenge.dm" #include "code\modules\antagonists\nukeop\equipment\nuclearbomb.dm" @@ -1238,6 +1244,7 @@ #include "code\modules\antagonists\wizard\equipment\artefact.dm" #include "code\modules\antagonists\wizard\equipment\soulstone.dm" #include "code\modules\antagonists\wizard\equipment\spellbook.dm" +#include "code\modules\antagonists\xeno\xeno.dm" #include "code\modules\assembly\assembly.dm" #include "code\modules\assembly\bomb.dm" #include "code\modules\assembly\doorcontrol.dm" @@ -2600,6 +2607,9 @@ #include "modular_citadel\code\controllers\subsystem\shuttle.dm" #include "modular_citadel\code\datums\uplink_items_cit.dm" #include "modular_citadel\code\datums\components\material_container.dm" +#include "modular_citadel\code\datums\mood_events\generic_negative_events.dm" +#include "modular_citadel\code\datums\mood_events\generic_positive_events.dm" +#include "modular_citadel\code\datums\mood_events\moodular.dm" #include "modular_citadel\code\datums\mutations\hulk.dm" #include "modular_citadel\code\datums\status_effects\debuffs.dm" #include "modular_citadel\code\datums\traits\neutral.dm" @@ -2757,8 +2767,10 @@ #include "modular_citadel\code\modules\recycling\disposal\bin.dm" #include "modular_citadel\code\modules\research\designs\autoylathe_designs.dm" #include "modular_citadel\code\modules\research\designs\machine_designs.dm" +#include "modular_citadel\code\modules\research\designs\xenobio_designs.dm" #include "modular_citadel\code\modules\research\techweb\_techweb.dm" #include "modular_citadel\code\modules\research\techweb\all_nodes.dm" +#include "modular_citadel\code\modules\research\xenobiology\xenobio_camera.dm" #include "modular_citadel\code\modules\uplink\uplink_items.dm" #include "modular_citadel\code\modules\vore\hook-defs_vr.dm" #include "modular_citadel\code\modules\vore\persistence.dm"