diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c00e2688f1..e14c5d1624 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -111,6 +111,9 @@ The use of the : operator to override type safety checks is not allowed. You mus ### Type paths must begin with a / eg: `/datum/thing`, not `datum/thing` +### Type paths must be lowercase +eg: `/datum/thing/blue`, not `datum/thing/BLUE` or `datum/thing/Blue` + ### Datum type paths must began with "datum" In DM, this is optional, but omitting it makes finding definitions harder. @@ -240,6 +243,8 @@ This prevents nesting levels from getting deeper then they need to be. * Queries must never specify the database, be it in code, or in text files in the repo. +* Primary keys are inherently immutable and you must never do anything to change the primary key of a row or entity. This includes preserving auto increment numbers of rows when copying data to a table in a conversion script. No amount of bitching about gaps in ids or out of order ids will save you from this policy. + ### Mapping Standards * TGM Format & Map Merge * All new maps submitted to the repo through a pull request must be in TGM format (unless there is a valid reason present to have it in the default BYOND format.) This is done using the [Map Merge](https://github.com/tgstation/tgstation/wiki/Map-Merger) utility included in the repo to convert the file to TGM format. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 949f1c4c44..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,9 +0,0 @@ -[Round ID]: # (If you discovered this issue from playing tgstation hosted servers:) -[Round ID]: # (**INCLUDE THE ROUND ID**) -[Round ID]: # (It can be found in the Status panel or retrieved from https://atlantaned.space/statbus/round.php ! The round id let's us look up valuable information and logs for the round the bug happened.) - -[Testmerges]: # (If you believe the issue to be caused by a test merge [OOC tab -> Show Server Revision], report it in the pull request's comment section instead.) - -[Reproduction]: # (Explain your issue in detail, including the steps to reproduce it. Issues without proper reproduction steps or explanation are open to being ignored/closed by maintainers.) - -[For Admins]: # (Oddities induced by var-edits and other admin tools are not necessarily bugs. Verify that your issues occur under regular circumstances before reporting them.) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..09f99f6c85 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Create a report to help reproduce and fix the issue +--- + +## Round ID: + + + +## Testmerges: + + + +## Reproduction: + + + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..2c9eb90c72 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,7 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +Please be aware that feature discussions most often take place on the Citadel Station Discord and should not be requested here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7855fd5782..f204eb0a72 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,16 @@ -[Changelogs]: # (Your PR should contain a detailed changelog of notable changes, titled and categorized appropriately. This includes, new features, sprites, sounds, balance changes, admin tools, map edits, removals, big refactors, config changes, hosting changes and important fixes. An example changelog has been provided below for you to edit. If you need additional help, read https://github.com/tgstation/tgstation/wiki/Changelogs) + + -:cl: optional name here +## About The Pull Request + + + +## Why It's Good For The Game + + + +## Changelog +:cl: add: Added new things add: Added more things del: Removed old things @@ -19,4 +29,5 @@ admin: messed with admin stuff server: something server ops should know /:cl: -[why]: # (Please add a short description [two lines down] of why you think these changes would benefit the game. If you can't justify it in words, it might not be worth adding.) + + diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm index ba291fc258..fd2e56a53f 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm @@ -272,7 +272,14 @@ /obj/machinery/light, /turf/open/floor/plasteel/freezer, /area/ruin/powered/seedvault) - +"Z" = ( +/obj/item/disk/design_disk/plant_disk, +/obj/machinery/autolathe{ + hacked = TRUE; + desc = "This autolathe seems to have its safety light off." + }, +/turf/open/floor/plasteel/freezer, +/area/ruin/powered/seedvault) (1,1,1) = {" a a @@ -375,7 +382,7 @@ h h u R -u +Z Q a a diff --git a/_maps/RandomRuins/SpaceRuins/oldstation.dmm b/_maps/RandomRuins/SpaceRuins/oldstation.dmm index ef36495c58..e820b06f61 100644 --- a/_maps/RandomRuins/SpaceRuins/oldstation.dmm +++ b/_maps/RandomRuins/SpaceRuins/oldstation.dmm @@ -2183,9 +2183,7 @@ /obj/effect/turf_decal/tile/purple{ dir = 8 }, -/turf/open/floor/plasteel/white{ - icon_state = "whitepurple" - }, +/turf/open/floor/plasteel/white, /area/ruin/space/has_grav/ancientstation/rnd) "fU" = ( /obj/effect/decal/cleanable/dirt, diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index c764adca16..dcd2c47cb2 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -27901,8 +27901,8 @@ pixel_x = 30 }, /obj/machinery/light, -/mob/living/simple_animal/bot/cleanbot{ - name = "C.L.E.A.N." +/obj/machinery/computer/crew{ + dir = 1 }, /turf/open/floor/plasteel/white, /area/medical/medbay/central) @@ -31596,7 +31596,7 @@ /area/medical/sleeper) "bwC" = ( /obj/machinery/computer/med_data{ - dir = 4 + dir = 3 }, /turf/open/floor/plasteel/white, /area/medical/sleeper) @@ -46022,6 +46022,9 @@ }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/effect/turf_decal/delivery, +/obj/machinery/shower{ + dir = 4 + }, /turf/open/floor/plasteel, /area/engine/engineering) "cek" = ( @@ -46030,6 +46033,9 @@ name = "engineering security door" }, /obj/effect/turf_decal/delivery, +/obj/machinery/shower{ + dir = 8 + }, /turf/open/floor/plasteel, /area/engine/engineering) "cel" = ( @@ -56876,6 +56882,12 @@ /obj/item/pen, /turf/open/floor/plasteel/white, /area/science/circuit) +"ium" = ( +/mob/living/simple_animal/bot/cleanbot{ + name = "C.L.E.A.N." + }, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) "izv" = ( /obj/machinery/vending/clothing, /obj/machinery/light/small{ @@ -94176,7 +94188,7 @@ blm bmL boi bpw -bhh +ium bsx btX bvj diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index 4a8eee364c..f9792a736e 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -44325,7 +44325,9 @@ dir = 1; pixel_y = -22 }, -/obj/structure/closet/radiation, +/obj/machinery/shower{ + dir = 4 + }, /turf/open/floor/plasteel, /area/engine/break_room) "bWF" = ( @@ -44352,7 +44354,10 @@ dir = 6 }, /obj/effect/turf_decal/delivery, -/obj/structure/closet/radiation, +/obj/machinery/shower{ + dir = 8; + pixel_y = -4 + }, /turf/open/floor/plasteel, /area/engine/break_room) "bWH" = ( @@ -44932,6 +44937,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, +/obj/structure/closet/radiation, /turf/open/floor/plasteel, /area/engine/engineering) "bYn" = ( @@ -44956,6 +44962,7 @@ /obj/machinery/light{ dir = 4 }, +/obj/structure/closet/radiation, /turf/open/floor/plasteel, /area/engine/engineering) "bYp" = ( @@ -53780,7 +53787,7 @@ "eSB" = ( /obj/machinery/computer/cryopod{ dir = 1; - pixel_y = -26; + pixel_y = -26 }, /turf/open/floor/plasteel/dark, /area/security/prison) diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 4c454aa61a..e35defd485 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -35,3 +35,8 @@ //Overthrow time to update heads obj #define OBJECTIVE_UPDATING_TIME 300 + +//Gangshit +#define NOT_DOMINATING -1 +#define MAX_LEADERS_GANG 3 +#define INITIAL_DOM_ATTEMPTS 3 diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index b5ba44d956..928837979c 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -1,8 +1,5 @@ //LISTMOS //indices of values in gas lists. -#define MOLES 1 -#define ARCHIVE 2 -#define GAS_META 3 #define META_GAS_SPECIFIC_HEAT 1 #define META_GAS_NAME 2 #define META_GAS_MOLES_VISIBLE 3 @@ -242,20 +239,25 @@ //HELPERS #define THERMAL_ENERGY(gas) (gas.temperature * gas.heat_capacity()) - -#define ADD_GAS(gas_id, out_list)\ - var/list/tmp_gaslist = GLOB.gaslist_cache[gas_id]; out_list[gas_id] = tmp_gaslist.Copy(); - -#define ASSERT_GAS(gas_id, gas_mixture) if (!gas_mixture.gases[gas_id]) { ADD_GAS(gas_id, gas_mixture.gases) }; - +#define QUANTIZE(variable) (round(variable,0.0000001))/*I feel the need to document what happens here. Basically this is used to catch most rounding errors, however it's previous value made it so that + once gases got hot enough, most procedures wouldnt occur due to the fact that the mole counts would get rounded away. Thus, we lowered it a few orders of magnititude */ //prefer this to gas_mixture/total_moles in performance critical areas #define TOTAL_MOLES(cached_gases, out_var)\ out_var = 0;\ for(var/total_moles_id in cached_gases){\ - out_var += cached_gases[total_moles_id][MOLES];\ + out_var += cached_gases[total_moles_id];\ } +//Unomos - So for whatever reason, garbage collection actually drastically decreases the cost of atmos later in the round. Turning this into a define yields massively improved performance. +#define GAS_GARBAGE_COLLECT(GASGASGAS)\ + var/list/CACHE_GAS = GASGASGAS;\ + for(var/id in CACHE_GAS){\ + if(QUANTIZE(CACHE_GAS[id]) <= 0)\ + CACHE_GAS -= id;\ + } + +#define ARCHIVE_TEMPERATURE(gas) gas.temperature_archived = gas.temperature GLOBAL_LIST_INIT(pipe_paint_colors, list( "amethyst" = rgb(130,43,255), //supplymain diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm index 9f5f52180c..2abe0db04e 100644 --- a/code/__DEFINES/citadel_defines.dm +++ b/code/__DEFINES/citadel_defines.dm @@ -33,8 +33,8 @@ #define BALLS_VOLUME_MULT 1 #define BALLS_SIZE_MIN 1 -#define BALLS_SIZE_DEF 3 -#define BALLS_SIZE_MAX 7 +#define BALLS_SIZE_DEF 2 +#define BALLS_SIZE_MAX 3 #define BALLS_SACK_SIZE_MIN 1 #define BALLS_SACK_SIZE_DEF 8 diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 3338fc1cda..dbe8cfbb62 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -142,8 +142,9 @@ #define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity)) // /obj signals -#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled) -#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value) +#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled) +#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag) +#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value) // /obj/item signals #define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm index 9f3d38efa1..aa9c5cf34e 100644 --- a/code/__DEFINES/construction.dm +++ b/code/__DEFINES/construction.dm @@ -103,6 +103,7 @@ #define CAT_SOUP "Soups" #define CAT_SPAGHETTI "Spaghettis" #define CAT_SUSHI "Fish" +#define CAT_ICE "Frozen" #define RCD_FLOORWALL 1 #define RCD_AIRLOCK 2 diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 6773d55e53..b316c003a9 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -33,7 +33,8 @@ #define ROLE_DRONE "drone" #define ROLE_DEATHSQUAD "deathsquad" #define ROLE_LAVALAND "lavaland" -#define ROLE_INTERNAL_AFFAIRS "internal affairs agent" +#define ROLE_INTERNAL_AFFAIRS "internal affairs agent" +#define ROLE_GANG "gangster" //Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. //The gamemode specific ones are just so the gamemodes can query whether a player is old enough @@ -58,7 +59,8 @@ GLOBAL_LIST_INIT(special_roles, list( ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockwork_cult, ROLE_OVERTHROW = /datum/game_mode/overthrow, ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs, - ROLE_SENTIENCE + ROLE_SENTIENCE, + ROLE_GANG = /datum/game_mode/gang )) //Job defines for what happens when you fail to qualify for any job during job selection diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 805e72cfcb..5c2deae875 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -71,6 +71,8 @@ #define TRAIT_MUSICIAN "musician" #define TRAIT_CROCRIN_IMMUNE "crocin_immune" #define TRAIT_NYMPHO "nymphomania" +#define TRAIT_MASO "masochism" +#define TRAIT_ASSBLASTUSA "assblastusa" // common trait sources #define TRAIT_GENERIC "generic" diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm index eb2a564d1b..668b151b6e 100644 --- a/code/__HELPERS/_cit_helpers.dm +++ b/code/__HELPERS/_cit_helpers.dm @@ -58,8 +58,11 @@ GLOBAL_LIST_EMPTY(ipc_antennas_list) //Genitals and Arousal Lists GLOBAL_LIST_EMPTY(cock_shapes_list)//global_lists.dm for the list initializations //Now also _DATASTRUCTURES globals.dm GLOBAL_LIST_EMPTY(cock_shapes_icons) //Associated list for names->icon_states for cockshapes. +GLOBAL_LIST_EMPTY(balls_shapes_list) +GLOBAL_LIST_EMPTY(balls_shapes_icons) GLOBAL_LIST_EMPTY(breasts_size_list) GLOBAL_LIST_EMPTY(breasts_shapes_list) +GLOBAL_LIST_EMPTY(breasts_shapes_icons) GLOBAL_LIST_EMPTY(vagina_shapes_list) GLOBAL_LIST_INIT(cum_into_containers_list, list(/obj/item/reagent_containers/food/snacks/pie)) //Yer fuggin snowflake name list jfc GLOBAL_LIST_INIT(dick_nouns, list("dick","cock","member","shaft")) @@ -123,36 +126,36 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE) /mob/living/carbon/proc/has_penis() if(getorganslot("penis"))//slot shared with ovipositor if(istype(getorganslot("penis"), /obj/item/organ/genital/penis)) - return 1 - return 0 + return TRUE + return FALSE /mob/living/carbon/proc/has_balls() if(getorganslot("balls")) if(istype(getorganslot("balls"), /obj/item/organ/genital/testicles)) - return 1 - return 0 + return TRUE + return FALSE /mob/living/carbon/proc/has_vagina() if(getorganslot("vagina")) - return 1 - return 0 + return TRUE + return FALSE /mob/living/carbon/proc/has_breasts() if(getorganslot("breasts")) - return 1 - return 0 + return TRUE + return FALSE /mob/living/carbon/proc/has_ovipositor() if(getorganslot("penis"))//shared slot if(istype(getorganslot("penis"), /obj/item/organ/genital/ovipositor)) - return 1 - return 0 + return TRUE + return FALSE /mob/living/carbon/human/proc/has_eggsack() if(getorganslot("balls")) if(istype(getorganslot("balls"), /obj/item/organ/genital/eggsack)) - return 1 - return 0 + return TRUE + return FALSE /mob/living/carbon/human/proc/is_bodypart_exposed(bodypart) @@ -161,16 +164,16 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE) L = get_equipped_items() for(var/obj/item/I in L) if(I.body_parts_covered & GROIN) - return 0 - return 1 + return FALSE + return TRUE /mob/living/carbon/proc/is_chest_exposed(var/list/L) if(!L) L = get_equipped_items() for(var/obj/item/I in L) if(I.body_parts_covered & CHEST) - return 0 - return 1 + return FALSE + return TRUE //////////////////////// //DANGER | DEBUG PROCS// @@ -191,40 +194,3 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE) H.give_vagina() H.give_womb() H.give_breasts() - -/client/proc/test_mammal_overlays() - set name = "Mass Give Mammalitus" - set category = "Dangerous" - set desc = "Turns every human into a mammal with tails, ears, etc. WARNING: NOT FOR LIVE SERVER USAGE!!" - - log_admin("[src] turned everyone into mammals.") - message_admins("[src] turned everyone into mammals.") - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(!H.dna) - continue - var/datum/dna/hdna = H.dna - H.set_species(/datum/species/mammal) - var/subspec = pick("Fox","Wolf","Fennec") - switch(subspec) - if("Wolf") - hdna.features["mam_tail"] = "Wolf" - hdna.features["mam_ears"] = "Wolf" - hdna.features["mam_snouts"] = "Wolf" - hdna.features["mam_body_markings"] = "Wolf" - hdna.features["mcolor"] = "555" - hdna.features["mcolor2"] = "999" - hdna.features["mcolor3"] = "999" - if("Fox") - hdna.features["mam_tail"] = "Fox" - hdna.features["mam_ears"] = "Fox" - hdna.features["mam_snouts"] = "Fox, Long" - hdna.features["mam_body_markings"] = "Fox" - hdna.features["mcolor"] = "f60" - hdna.features["mcolor2"] = "fff" - hdna.features["mcolor3"] = "fff" - if("Fennec") - hdna.features["mam_tail"] = "Fennec" - hdna.features["mam_ears"] = "Fennec" - hdna.features["mam_snouts"] = "Fox, Short" - hdna.features["mam_body_markings"] = "Fox" - H.regenerate_icons() diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index f6ef08fe0d..ac113b4e08 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -46,7 +46,6 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/xeno_dorsal, GLOB.xeno_dorsal_list) //genitals init_sprite_accessory_subtypes(/datum/sprite_accessory/penis, GLOB.cock_shapes_list) - for(var/K in GLOB.cock_shapes_list) var/datum/sprite_accessory/penis/value = GLOB.cock_shapes_list[K] GLOB.cock_shapes_icons[K] = value.icon_state @@ -54,6 +53,14 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list) GLOB.breasts_size_list = list("a","b","c","d","e") //We need the list to choose from initialized, but it's no longer a sprite_accessory thing. + for(var/K in GLOB.breasts_shapes_list) + var/datum/sprite_accessory/breasts/value = GLOB.breasts_shapes_list[K] + GLOB.breasts_shapes_icons[K] = value.icon_state + + init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list) + for(var/K in GLOB.balls_shapes_list) + var/datum/sprite_accessory/testicles/value = GLOB.balls_shapes_list[K] + GLOB.balls_shapes_icons[K] = value.icon_state //END OF CIT CHANGES //Species diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 74a1fbd785..51c814a5f9 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -77,6 +77,8 @@ //CIT CHANGES - genitals and such if(!GLOB.cock_shapes_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/penis, GLOB.cock_shapes_list) + if(!GLOB.balls_shapes_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list) if(!GLOB.vagina_shapes_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list) if(!GLOB.breasts_shapes_list.len) @@ -168,6 +170,7 @@ "balls_amount" = 2, "balls_sack_size" = BALLS_SACK_SIZE_DEF, "balls_size" = BALLS_SIZE_DEF, + "balls_shape" = "Pair", "balls_cum_rate" = CUM_RATE, "balls_cum_mult" = CUM_RATE_MULT, "balls_efficiency" = CUM_EFFICIENCY, @@ -185,7 +188,7 @@ "has_breasts" = FALSE, "breasts_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), "breasts_size" = pick(GLOB.breasts_size_list), - "breasts_shape" = pick(GLOB.breasts_shapes_list), + "breasts_shape" = "Pair", "breasts_fluid" = "milk", "has_vag" = FALSE, "vag_shape" = pick(GLOB.vagina_shapes_list), @@ -294,7 +297,7 @@ GLOBAL_LIST_EMPTY(species_list) else return "unknown" -/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null) +/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0) if(!user || !target) return 0 var/user_loc = user.loc @@ -327,7 +330,7 @@ GLOBAL_LIST_EMPTY(species_list) drifting = 0 user_loc = user.loc - if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) + if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) . = 0 break if (progress) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 7d6c017b77..7d8eb108f3 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -94,6 +94,8 @@ // Used to get a properly sanitized multiline input, of max_length /proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) var/name = input(user, message, title, default) as message|null + if(isnull(name)) // Return null if canceled. + return null if(no_trim) return copytext(html_encode(name), 1, max_length) else diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index 36049de77b..aa1cdcb40a 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -109,5 +109,7 @@ GLOBAL_LIST_INIT(maintenance_loot, list( /obj/item/reagent_containers/pill/floorpill = 1, /obj/item/storage/daki = 3, //VERY IMPORTANT CIT CHANGE - adds bodypillows to maint /obj/item/storage/pill_bottle/penis_enlargement = 2, + /obj/item/clothing/shoes/wheelys = 1, + /obj/item/clothing/shoes/kindleKicks = 1, "" = 3 )) diff --git a/code/datums/components/storage/concrete/_concrete.dm b/code/datums/components/storage/concrete/_concrete.dm index 4d7e8bddc5..0b78605869 100644 --- a/code/datums/components/storage/concrete/_concrete.dm +++ b/code/datums/components/storage/concrete/_concrete.dm @@ -6,6 +6,8 @@ /datum/component/storage/concrete var/drop_all_on_deconstruct = TRUE var/drop_all_on_destroy = FALSE + var/drop_all_on_break = FALSE + var/unlock_on_break = FALSE var/transfer_contents_on_component_transfer = FALSE var/list/datum/component/storage/slaves = list() @@ -16,6 +18,7 @@ . = ..() RegisterSignal(parent, COMSIG_ATOM_CONTENTS_DEL, .proc/on_contents_del) RegisterSignal(parent, COMSIG_OBJ_DECONSTRUCT, .proc/on_deconstruct) + RegisterSignal(parent, COMSIG_OBJ_BREAK, .proc/on_break) /datum/component/storage/concrete/Destroy() var/atom/real_location = real_location() @@ -100,6 +103,12 @@ if(drop_all_on_deconstruct) do_quick_empty() +/datum/component/storage/concrete/proc/on_break(datum/source, damage_flag) + if(drop_all_on_break) + do_quick_empty() + if(unlock_on_break) + set_locked(source, FALSE) + /datum/component/storage/concrete/can_see_contents() . = ..() for(var/i in slaves) diff --git a/code/datums/components/storage/concrete/emergency.dm b/code/datums/components/storage/concrete/emergency.dm new file mode 100644 index 0000000000..e5f9d83a17 --- /dev/null +++ b/code/datums/components/storage/concrete/emergency.dm @@ -0,0 +1,22 @@ +/datum/component/storage/concrete/emergency + drop_all_on_break = TRUE + unlock_on_break = TRUE + locked = TRUE + +/datum/component/storage/concrete/emergency/Initialize() + . = ..() + RegisterSignal(parent, COMSIG_ATOM_EMAG_ACT, .proc/unlock_me) + +/datum/component/storage/concrete/emergency/signal_insertion_attempt(datum/source, obj/item/I, mob/M, silent = FALSE, force = FALSE) + if(!silent && istype(I, /obj/item/card/emag)) + silent = TRUE // suppresses the message + return ..() + +/datum/component/storage/concrete/check_locked(datum/source, mob/user, message = FALSE) + . = locked && GLOB.security_level < SEC_LEVEL_RED + if(message && . && user) + to_chat(user, "The storage unit will only unlock during a Red or Delta security alert.") + +/datum/component/storage/concrete/emergency/proc/unlock_me(datum/source) + if(locked) + set_locked(source, FALSE) diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index dc25729e37..869fe59ae9 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -156,8 +156,7 @@ next += slave.parent /datum/component/storage/proc/attack_self(datum/source, mob/M) - if(locked) - to_chat(M, "[parent] seems to be locked!") + if(check_locked(source, M, TRUE)) return FALSE if((M.get_active_held_item() == parent) && allow_quick_empty) quick_empty(M) @@ -166,8 +165,7 @@ if(!isitem(O) || !click_gather || SEND_SIGNAL(O, COMSIG_CONTAINS_STORAGE)) return FALSE . = COMPONENT_NO_ATTACK - if(locked) - to_chat(M, "[parent] seems to be locked!") + if(check_locked(source, M, TRUE)) return FALSE var/atom/A = parent var/obj/item/I = O @@ -238,8 +236,7 @@ var/atom/A = parent if((!ishuman(M) && (A.loc != M)) || (M.stat != CONSCIOUS) || M.restrained() || !M.canmove) return - if(locked) - to_chat(M, "[parent] seems to be locked!") + if(check_locked(null, M, TRUE)) return FALSE A.add_fingerprint(M) to_chat(M, "You start dumping out [parent].") @@ -281,7 +278,7 @@ /datum/component/storage/proc/set_locked(datum/source, new_state) locked = new_state - if(locked) + if(check_locked()) close_all() /datum/component/storage/proc/_process_numerical_display() @@ -456,8 +453,7 @@ var/atom/A = parent var/atom/dump_destination = dest_object.get_dumping_location() if(A.Adjacent(M) && dump_destination && M.Adjacent(dump_destination)) - if(locked) - to_chat(M, "[parent] seems to be locked!") + if(check_locked(null, M, TRUE)) return FALSE if(dump_destination.storage_contents_dump_act(src, M)) playsound(A, "rustle", 50, 1, -5) @@ -563,10 +559,9 @@ var/atom/host = parent if(real_location == I.loc) return FALSE //Means the item is already in the storage item - if(locked) + if(check_locked(null, M, !stop_messages)) if(M && !stop_messages) host.add_fingerprint(M) - to_chat(M, "[host] seems to be locked!") return FALSE if(real_location.contents.len >= max_items) if(!stop_messages) @@ -659,8 +654,10 @@ /datum/component/storage/proc/on_check() return TRUE -/datum/component/storage/proc/check_locked() - return locked +/datum/component/storage/proc/check_locked(datum/source, mob/user, message = FALSE) + . = locked + if(message && . && user) + to_chat(user, "[parent] seems to be locked!") /datum/component/storage/proc/signal_take_type(datum/source, type, atom/destination, amount = INFINITY, check_adjacent = FALSE, force = FALSE, mob/user, list/inserted) if(!force) @@ -720,9 +717,7 @@ if(A.loc == user) . = COMPONENT_NO_ATTACK_HAND - if(locked) - to_chat(user, "[parent] seems to be locked!") - else + if(!check_locked(source, user, TRUE)) show_to(user) A.do_jiggle() @@ -747,8 +742,7 @@ /datum/component/storage/proc/on_alt_click(datum/source, mob/user) if(!isliving(user) || !user.CanReach(parent)) return - if(locked) - to_chat(user, "[parent] seems to be locked!") + if(check_locked(source, user, TRUE)) return var/atom/A = parent diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 0ace2ca8fd..0a4d192319 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -364,15 +364,15 @@ /datum/symptom/heal/plasma/CanHeal(datum/disease/advance/A) var/mob/living/M = A.affected_mob var/datum/gas_mixture/environment - var/list/gases + var/plasmamount . = 0 if(M.loc) environment = M.loc.return_air() if(environment) - gases = environment.gases - if(gases["plasma"] && gases["plasma"][MOLES] > gases["plasma"][GAS_META][META_GAS_MOLES_VISIBLE]) //if there's enough plasma in the air to see + plasmamount = environment.gases[/datum/gas/plasma] + if(plasmamount && plasmamount > GLOB.meta_gas_visibility[/datum/gas/plasma]) //if there's enough plasma in the air to see . += power * 0.5 if(M.reagents.has_reagent("plasma")) . += power * 0.75 diff --git a/code/datums/ert.dm b/code/datums/ert.dm index d3c256308d..d61c95c8f2 100644 --- a/code/datums/ert.dm +++ b/code/datums/ert.dm @@ -21,6 +21,8 @@ /datum/ert/amber code = "Amber" + leader_role = /datum/antagonist/ert/commander/red + roles = list(/datum/antagonist/ert/security/red, /datum/antagonist/ert/medic/red, /datum/antagonist/ert/engineer/red) /datum/ert/red leader_role = /datum/antagonist/ert/commander/red diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index d673cdb16d..0623e2f5f9 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -103,11 +103,11 @@ // Can most things breathe? if(trace_gases) continue - if(!(A_gases[/datum/gas/oxygen] && A_gases[/datum/gas/oxygen][MOLES] >= 16)) + if(A_gases[/datum/gas/oxygen] >= 16) continue if(A_gases[/datum/gas/plasma]) continue - if(A_gases[/datum/gas/carbon_dioxide] && A_gases[/datum/gas/carbon_dioxide][MOLES] >= 10) + if(A_gases[/datum/gas/carbon_dioxide] >= 10) continue // Aim for goldilocks temperatures and pressure diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm index 8a5f0f9439..b0a78211b0 100644 --- a/code/datums/martial/krav_maga.dm +++ b/code/datums/martial/krav_maga.dm @@ -191,3 +191,18 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE + +/obj/item/clothing/gloves/krav_maga/combatglovesplus + name = "combat gloves plus" + desc = "These tactical gloves are fireproof and shock resistant, and using nanochip technology it teaches you the powers of krav maga." + icon_state = "black" + item_state = "blackglovesplus" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + strip_delay = 80 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = NONE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm index f299e9904a..f70e3a3c68 100644 --- a/code/datums/traits/neutral.dm +++ b/code/datums/traits/neutral.dm @@ -93,3 +93,12 @@ gain_text = "You feel more prudish." lose_text = "You don't feel as prudish as before." medical_record_text = "Patient exhibits a special gene that makes them immune to Crocin and Hexacrocin." + +/datum/quirk/assblastusa + name = "Buns of Steel" + desc = "You've never skipped ass day. With this trait, you are completely immune to all forms of ass slapping and anyone who tries to slap your rock hard ass usually gets a broken hand." + mob_trait = TRAIT_ASSBLASTUSA + value = 0 + medical_record_text = "Patient never skipped ass day." + gain_text = "Your ass rivals those of golems." + lose_text = "Your butt feels more squishy and slappable." \ No newline at end of file diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm index d6513b5d40..0d5a7bd56c 100644 --- a/code/game/gamemodes/clock_cult/clock_cult.dm +++ b/code/game/gamemodes/clock_cult/clock_cult.dm @@ -131,7 +131,7 @@ Credit where due: config_tag = "clockwork_cult" antag_flag = ROLE_SERVANT_OF_RATVAR false_report_weight = 10 - required_players = 30 + required_players = 35 required_enemies = 3 recommended_enemies = 5 enemy_minimum_age = 7 diff --git a/code/game/gamemodes/clown_ops/clown_ops.dm b/code/game/gamemodes/clown_ops/clown_ops.dm index 12d3106c8d..a666b57afd 100644 --- a/code/game/gamemodes/clown_ops/clown_ops.dm +++ b/code/game/gamemodes/clown_ops/clown_ops.dm @@ -62,5 +62,6 @@ /datum/outfit/syndicate/clownop/leader name = "Clown Operative Leader - Basic" id = /obj/item/card/id/syndicate/nuke_leader + gloves = /obj/item/clothing/gloves/krav_maga/combatglovesplus r_hand = /obj/item/nuclear_challenge/clownops command_radio = TRUE diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index d76552982c..615d55a818 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -137,6 +137,7 @@ /datum/outfit/syndicate/leader name = "Syndicate Leader - Basic" id = /obj/item/card/id/syndicate/nuke_leader + gloves = /obj/item/clothing/gloves/krav_maga/combatglovesplus r_hand = /obj/item/nuclear_challenge command_radio = TRUE diff --git a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm index 6603bb3604..e56495d808 100644 --- a/code/game/gamemodes/objective_items.dm +++ b/code/game/gamemodes/objective_items.dm @@ -124,7 +124,7 @@ /datum/objective_item/steal/plasma/check_special_completion(obj/item/tank/T) var/target_amount = text2num(name) var/found_amount = 0 - found_amount += T.air_contents.gases[/datum/gas/plasma] ? T.air_contents.gases[/datum/gas/plasma][MOLES] : 0 + found_amount += T.air_contents.gases[/datum/gas/plasma] return found_amount>=target_amount diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index 94f5e63afb..cb3cbf2aa6 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -80,9 +80,21 @@ /obj/machinery/computer/atmos_alert/update_icon() ..() + cut_overlays() + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) + var/overlay_state = icon_screen if(stat & (NOPOWER|BROKEN)) + add_overlay("[icon_keyboard]_off") return + add_overlay(icon_keyboard) if(priority_alarms.len) + overlay_state = "alert:2" add_overlay("alert:2") else if(minor_alarms.len) + overlay_state = "alert:1" add_overlay("alert:1") + else + overlay_state = "alert:0" + add_overlay("alert:0") + SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir) + SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128) diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index baa6fa577e..6576f51e46 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -59,8 +59,8 @@ var/total_moles = air_sample.total_moles() if(total_moles) for(var/gas_id in air_sample.gases) - var/gas_name = air_sample.gases[gas_id][GAS_META][META_GAS_NAME] - signal.data["gases"][gas_name] = air_sample.gases[gas_id][MOLES] / total_moles * 100 + var/gas_name = GLOB.meta_gas_names[gas_id] + signal.data["gases"][gas_name] = air_sample.gases[gas_id] / total_moles * 100 radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 09cbb68083..ccc2121929 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -103,7 +103,9 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) if(data_by_z["[z]"] && last_update["[z]"] && world.time <= last_update["[z]"] + SENSORS_UPDATE_PERIOD) return data_by_z["[z]"] - var/list/results = list() + var/list/results_damaged = list() + var/list/results_undamaged = list() + var/obj/item/clothing/under/U var/obj/item/card/id/I var/turf/pos @@ -114,6 +116,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) var/toxdam var/burndam var/brutedam + var/totaldam var/area var/pos_x var/pos_y @@ -157,11 +160,13 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) toxdam = round(H.getToxLoss(),1) burndam = round(H.getFireLoss(),1) brutedam = round(H.getBruteLoss(),1) + totaldam = oxydam + toxdam + burndam + brutedam else oxydam = null toxdam = null burndam = null brutedam = null + totaldam = 0 if (nanite_sensors || U.sensor_mode >= SENSOR_COORDS) if (!pos) @@ -174,16 +179,26 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) pos_x = null pos_y = null - results[++results.len] = list("name" = name, "assignment" = assignment, "ijob" = ijob, "life_status" = life_status, "oxydam" = oxydam, "toxdam" = toxdam, "burndam" = burndam, "brutedam" = brutedam, "area" = area, "pos_x" = pos_x, "pos_y" = pos_y, "can_track" = H.can_track(null)) + var/total_list = list("name" = name, "assignment" = assignment, "ijob" = ijob, "life_status" = life_status, "oxydam" = oxydam, "toxdam" = toxdam, "burndam" = burndam, "brutedam" = brutedam, "totaldam" = totaldam, "area" = area, "pos_x" = pos_x, "pos_y" = pos_y, "can_track" = H.can_track(null)) - data_by_z["[z]"] = sortTim(results,/proc/sensor_compare) + if(totaldam) + results_damaged[++results_damaged.len] = total_list + else + results_undamaged[++results_undamaged.len] = total_list + + var/list/returning = sortTim(results_damaged,/proc/damage_compare) + sortTim(results_undamaged,/proc/ijob_compare) + + data_by_z["[z]"] = returning last_update["[z]"] = world.time - return results + return returning -/proc/sensor_compare(list/a,list/b) +/proc/ijob_compare(list/a,list/b) return a["ijob"] - b["ijob"] +/proc/damage_compare(list/a,list/b) + return b["totaldam"] - a["totaldam"] + /datum/crewmonitor/ui_act(action,params) var/mob/living/silicon/ai/AI = usr if(!istype(AI)) diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 6769244335..36ba4b0c94 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -76,12 +76,23 @@ /obj/machinery/computer/station_alert/update_icon() ..() + cut_overlays() + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) + var/overlay_state = icon_screen if(stat & (NOPOWER|BROKEN)) + add_overlay("[icon_keyboard]_off") return + add_overlay(icon_keyboard) var/active_alarms = FALSE for(var/cat in alarms) var/list/L = alarms[cat] if(L.len) active_alarms = TRUE if(active_alarms) + overlay_state = "alert:2" add_overlay("alert:2") + else + overlay_state = "alert:0" + add_overlay("alert:0") + SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir) + SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128) diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm index 1a3886c3df..80c92c7922 100644 --- a/code/game/mecha/equipment/tools/other_tools.dm +++ b/code/game/mecha/equipment/tools/other_tools.dm @@ -421,14 +421,13 @@ if(!istype(T)) return var/datum/gas_mixture/GM = new - GM.add_gas(/datum/gas/plasma) if(prob(10)) - GM.gases[/datum/gas/plasma][MOLES] += 100 + GM.gases[/datum/gas/plasma] += 100 GM.temperature = 1500+T0C //should be enough to start a fire T.visible_message("[src] suddenly disgorges a cloud of heated plasma.") qdel(src) else - GM.gases[/datum/gas/plasma][MOLES] += 5 + GM.gases[/datum/gas/plasma] += 5 GM.temperature = istype(T) ? T.air.return_temperature() : T20C T.visible_message("[src] suddenly disgorges a cloud of plasma.") T.assume_air(GM) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index d5de4a4898..fcbed0fc4a 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -237,9 +237,8 @@ cabin_air = new cabin_air.temperature = T20C cabin_air.volume = 200 - cabin_air.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen) - cabin_air.gases[/datum/gas/oxygen][MOLES] = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - cabin_air.gases[/datum/gas/nitrogen][MOLES] = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + cabin_air.gases[/datum/gas/oxygen] = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + cabin_air.gases[/datum/gas/nitrogen] = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) return cabin_air /obj/mecha/proc/add_radio() diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index 34263ef9a4..0a0348ae39 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -3,6 +3,7 @@ desc = "Graffiti. Damn kids." icon = 'icons/effects/crayondecal.dmi' icon_state = "rune1" + plane = GAME_PLANE //makes the graffiti visible over a wall. gender = NEUTER mergeable_decal = FALSE var/do_icon_rotate = TRUE diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm index 264715e2ac..10ee73915c 100644 --- a/code/game/objects/effects/effect_system/effects_foam.dm +++ b/code/game/objects/effects/effect_system/effects_foam.dm @@ -1,347 +1,347 @@ -// Foam -// Similar to smoke, but slower and mobs absorb its reagent through their exposed skin. -#define ALUMINUM_FOAM 1 -#define IRON_FOAM 2 -#define RESIN_FOAM 3 - - -/obj/effect/particle_effect/foam - name = "foam" - icon_state = "foam" - opacity = 0 - anchored = TRUE - density = FALSE - layer = EDGED_TURF_LAYER - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - var/amount = 3 - animate_movement = 0 - var/metal = 0 - var/lifetime = 40 - var/reagent_divisor = 7 - var/static/list/blacklisted_turfs = typecacheof(list( - /turf/open/space/transit, - /turf/open/chasm, - /turf/open/lava)) - -/obj/effect/particle_effect/foam/firefighting - name = "firefighting foam" - lifetime = 20 //doesn't last as long as normal foam - amount = 0 //no spread - var/absorbed_plasma = 0 - -/obj/effect/particle_effect/foam/firefighting/MakeSlippery() - return - -/obj/effect/particle_effect/foam/firefighting/process() - ..() - - var/turf/open/T = get_turf(src) - var/obj/effect/hotspot/hotspot = (locate(/obj/effect/hotspot) in T) - if(hotspot && istype(T) && T.air) - qdel(hotspot) - var/datum/gas_mixture/G = T.air - var/plas_amt = min(30,G.gases[/datum/gas/plasma][MOLES]) //Absorb some plasma - G.gases[/datum/gas/plasma][MOLES] -= plas_amt - absorbed_plasma += plas_amt - if(G.temperature > T20C) - G.temperature = max(G.temperature/2,T20C) - G.garbage_collect() - T.air_update_turf() - -/obj/effect/particle_effect/foam/firefighting/kill_foam() - STOP_PROCESSING(SSfastprocess, src) - - if(absorbed_plasma) - var/obj/effect/decal/cleanable/plasma/P = (locate(/obj/effect/decal/cleanable/plasma) in get_turf(src)) - if(!P) - P = new(loc) - P.reagents.add_reagent("stable_plasma", absorbed_plasma) - - flick("[icon_state]-disolve", src) - QDEL_IN(src, 5) - -/obj/effect/particle_effect/foam/firefighting/foam_mob(mob/living/L) - if(!istype(L)) - return - L.adjust_fire_stacks(-2) - L.ExtinguishMob() - -/obj/effect/particle_effect/foam/firefighting/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - return - -/obj/effect/particle_effect/foam/metal - name = "aluminium foam" - metal = ALUMINUM_FOAM - icon_state = "mfoam" - -/obj/effect/particle_effect/foam/metal/MakeSlippery() - return - -/obj/effect/particle_effect/foam/metal/smart - name = "smart foam" - -/obj/effect/particle_effect/foam/metal/iron - name = "iron foam" - metal = IRON_FOAM - -/obj/effect/particle_effect/foam/metal/resin - name = "resin foam" - metal = RESIN_FOAM - -/obj/effect/particle_effect/foam/long_life - lifetime = 150 - -/obj/effect/particle_effect/foam/Initialize() - . = ..() - MakeSlippery() - create_reagents(1000) //limited by the size of the reagent holder anyway. - START_PROCESSING(SSfastprocess, src) - playsound(src, 'sound/effects/bubbles2.ogg', 80, 1, -3) - -/obj/effect/particle_effect/foam/proc/MakeSlippery() - AddComponent(/datum/component/slippery, 100) - -/obj/effect/particle_effect/foam/Destroy() - STOP_PROCESSING(SSfastprocess, src) - return ..() - - -/obj/effect/particle_effect/foam/proc/kill_foam() - STOP_PROCESSING(SSfastprocess, src) - switch(metal) - if(ALUMINUM_FOAM) - new /obj/structure/foamedmetal(get_turf(src)) - if(IRON_FOAM) - new /obj/structure/foamedmetal/iron(get_turf(src)) - if(RESIN_FOAM) - new /obj/structure/foamedmetal/resin(get_turf(src)) - flick("[icon_state]-disolve", src) - QDEL_IN(src, 5) - -/obj/effect/particle_effect/foam/smart/kill_foam() //Smart foam adheres to area borders for walls - STOP_PROCESSING(SSfastprocess, src) - if(metal) - var/turf/T = get_turf(src) - if(isspaceturf(T)) //Block up any exposed space - T.PlaceOnTop(/turf/open/floor/plating/foam) - for(var/direction in GLOB.cardinals) - var/turf/cardinal_turf = get_step(T, direction) - if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf! - new/obj/structure/foamedmetal(T) - break - flick("[icon_state]-disolve", src) - QDEL_IN(src, 5) - -/obj/effect/particle_effect/foam/process() - lifetime-- - if(lifetime < 1) - kill_foam() - return - - var/fraction = 1/initial(reagent_divisor) - for(var/obj/O in range(0,src)) - if(O.type == src.type) - continue - if(isturf(O.loc)) - var/turf/T = O.loc - if(T.intact && O.level == 1) //hidden under the floor - continue - if(lifetime % reagent_divisor) - reagents.reaction(O, VAPOR, fraction) - var/hit = 0 - for(var/mob/living/L in range(0,src)) - hit += foam_mob(L) - if(hit) - lifetime++ //this is so the decrease from mobs hit and the natural decrease don't cumulate. - var/T = get_turf(src) - if(lifetime % reagent_divisor) - reagents.reaction(T, VAPOR, fraction) - - if(--amount < 0) - return - spread_foam() - -/obj/effect/particle_effect/foam/proc/foam_mob(mob/living/L) - if(lifetime<1) - return 0 - if(!istype(L)) - return 0 - var/fraction = 1/initial(reagent_divisor) - if(lifetime % reagent_divisor) - reagents.reaction(L, VAPOR, fraction) - lifetime-- - return 1 - -/obj/effect/particle_effect/foam/proc/spread_foam() - var/turf/t_loc = get_turf(src) - for(var/turf/T in t_loc.GetAtmosAdjacentTurfs()) - var/obj/effect/particle_effect/foam/foundfoam = locate() in T //Don't spread foam where there's already foam! - if(foundfoam) - continue - - if(is_type_in_typecache(T, blacklisted_turfs)) - continue - - for(var/mob/living/L in T) - foam_mob(L) - var/obj/effect/particle_effect/foam/F = new src.type(T) - F.amount = amount - reagents.copy_to(F, (reagents.total_volume)) - F.add_atom_colour(color, FIXED_COLOUR_PRIORITY) - F.metal = metal - - -/obj/effect/particle_effect/foam/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(prob(max(0, exposed_temperature - 475))) //foam dissolves when heated - kill_foam() - - -/obj/effect/particle_effect/foam/metal/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - return - - -/////////////////////////////////////////////// -//FOAM EFFECT DATUM -/datum/effect_system/foam_spread - var/amount = 10 // the size of the foam spread. - var/obj/chemholder - effect_type = /obj/effect/particle_effect/foam - var/metal = 0 - - -/datum/effect_system/foam_spread/metal - effect_type = /obj/effect/particle_effect/foam/metal - - -/datum/effect_system/foam_spread/metal/smart - effect_type = /obj/effect/particle_effect/foam/smart - - -/datum/effect_system/foam_spread/long - effect_type = /obj/effect/particle_effect/foam/long_life - -/datum/effect_system/foam_spread/New() - ..() - chemholder = new /obj() - var/datum/reagents/R = new/datum/reagents(1000) - chemholder.reagents = R - R.my_atom = chemholder - -/datum/effect_system/foam_spread/Destroy() - qdel(chemholder) - chemholder = null - return ..() - -/datum/effect_system/foam_spread/set_up(amt=5, loca, datum/reagents/carry = null) - if(isturf(loca)) - location = loca - else - location = get_turf(loca) - - amount = round(sqrt(amt / 2), 1) - carry.copy_to(chemholder, carry.total_volume) - -/datum/effect_system/foam_spread/metal/set_up(amt=5, loca, datum/reagents/carry = null, metaltype) - ..() - metal = metaltype - -/datum/effect_system/foam_spread/start() - var/obj/effect/particle_effect/foam/F = new effect_type(location) - var/foamcolor = mix_color_from_reagents(chemholder.reagents.reagent_list) - chemholder.reagents.copy_to(F, chemholder.reagents.total_volume/amount) - F.add_atom_colour(foamcolor, FIXED_COLOUR_PRIORITY) - F.amount = amount - F.metal = metal - - -////////////////////////////////////////////////////////// -// FOAM STRUCTURE. Formed by metal foams. Dense and opaque, but easy to break -/obj/structure/foamedmetal - icon = 'icons/effects/effects.dmi' - icon_state = "metalfoam" - density = TRUE - opacity = 1 // changed in New() - anchored = TRUE - layer = EDGED_TURF_LAYER - resistance_flags = FIRE_PROOF | ACID_PROOF - name = "foamed metal" - desc = "A lightweight foamed metal wall." - gender = PLURAL - max_integrity = 20 - CanAtmosPass = ATMOS_PASS_DENSITY - -/obj/structure/foamedmetal/Initialize() - . = ..() - air_update_turf(1) - -/obj/structure/foamedmetal/Move() - var/turf/T = loc - . = ..() - move_update_air(T) - -/obj/structure/foamedmetal/attack_paw(mob/user) - return attack_hand(user) - -/obj/structure/foamedmetal/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - playsound(src.loc, 'sound/weapons/tap.ogg', 100, 1) - -/obj/structure/foamedmetal/attack_hand(mob/user) - . = ..() - if(.) - return - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) - to_chat(user, "You hit [src] but bounce off it!") - playsound(src.loc, 'sound/weapons/tap.ogg', 100, 1) - -/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target) - return !density - -/obj/structure/foamedmetal/iron - max_integrity = 50 - icon_state = "ironfoam" - -//Atmos Backpack Resin, transparent, prevents atmos and filters the air -/obj/structure/foamedmetal/resin - name = "\improper ATMOS Resin" - desc = "A lightweight, transparent resin used to suffocate fires, scrub the air of toxins, and restore the air to a safe temperature." - opacity = FALSE - icon_state = "atmos_resin" - alpha = 120 - max_integrity = 10 - -/obj/structure/foamedmetal/resin/Initialize() - . = ..() - if(isopenturf(loc)) - var/turf/open/O = loc - O.ClearWet() - if(O.air) - var/datum/gas_mixture/G = O.air - G.temperature = 293.15 - for(var/obj/effect/hotspot/H in O) - qdel(H) - var/list/G_gases = G.gases - for(var/I in G_gases) - if(I == /datum/gas/oxygen || I == /datum/gas/nitrogen) - continue - G_gases[I][MOLES] = 0 - G.garbage_collect() - O.air_update_turf() - for(var/obj/machinery/atmospherics/components/unary/U in O) - if(!U.welded) - U.welded = TRUE - U.update_icon() - U.visible_message("[U] sealed shut!") - for(var/mob/living/L in O) - L.ExtinguishMob() - for(var/obj/item/Item in O) - Item.extinguish() - -/obj/structure/foamedmetal/resin/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && (mover.pass_flags & PASSGLASS)) - return TRUE - . = ..() - -#undef ALUMINUM_FOAM -#undef IRON_FOAM -#undef RESIN_FOAM +// Foam +// Similar to smoke, but slower and mobs absorb its reagent through their exposed skin. +#define ALUMINUM_FOAM 1 +#define IRON_FOAM 2 +#define RESIN_FOAM 3 + + +/obj/effect/particle_effect/foam + name = "foam" + icon_state = "foam" + opacity = 0 + anchored = TRUE + density = FALSE + layer = EDGED_TURF_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + var/amount = 3 + animate_movement = 0 + var/metal = 0 + var/lifetime = 40 + var/reagent_divisor = 7 + var/static/list/blacklisted_turfs = typecacheof(list( + /turf/open/space/transit, + /turf/open/chasm, + /turf/open/lava)) + +/obj/effect/particle_effect/foam/firefighting + name = "firefighting foam" + lifetime = 20 //doesn't last as long as normal foam + amount = 0 //no spread + var/absorbed_plasma = 0 + +/obj/effect/particle_effect/foam/firefighting/MakeSlippery() + return + +/obj/effect/particle_effect/foam/firefighting/process() + ..() + + var/turf/open/T = get_turf(src) + var/obj/effect/hotspot/hotspot = (locate(/obj/effect/hotspot) in T) + if(hotspot && istype(T) && T.air) + qdel(hotspot) + var/datum/gas_mixture/G = T.air + var/plas_amt = min(30,G.gases[/datum/gas/plasma]) //Absorb some plasma + G.gases[/datum/gas/plasma] -= plas_amt + absorbed_plasma += plas_amt + if(G.temperature > T20C) + G.temperature = max(G.temperature/2,T20C) + GAS_GARBAGE_COLLECT(G.gases) + T.air_update_turf() + +/obj/effect/particle_effect/foam/firefighting/kill_foam() + STOP_PROCESSING(SSfastprocess, src) + + if(absorbed_plasma) + var/obj/effect/decal/cleanable/plasma/P = (locate(/obj/effect/decal/cleanable/plasma) in get_turf(src)) + if(!P) + P = new(loc) + P.reagents.add_reagent("stable_plasma", absorbed_plasma) + + flick("[icon_state]-disolve", src) + QDEL_IN(src, 5) + +/obj/effect/particle_effect/foam/firefighting/foam_mob(mob/living/L) + if(!istype(L)) + return + L.adjust_fire_stacks(-2) + L.ExtinguishMob() + +/obj/effect/particle_effect/foam/firefighting/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + return + +/obj/effect/particle_effect/foam/metal + name = "aluminium foam" + metal = ALUMINUM_FOAM + icon_state = "mfoam" + +/obj/effect/particle_effect/foam/metal/MakeSlippery() + return + +/obj/effect/particle_effect/foam/metal/smart + name = "smart foam" + +/obj/effect/particle_effect/foam/metal/iron + name = "iron foam" + metal = IRON_FOAM + +/obj/effect/particle_effect/foam/metal/resin + name = "resin foam" + metal = RESIN_FOAM + +/obj/effect/particle_effect/foam/long_life + lifetime = 150 + +/obj/effect/particle_effect/foam/Initialize() + . = ..() + MakeSlippery() + create_reagents(1000) //limited by the size of the reagent holder anyway. + START_PROCESSING(SSfastprocess, src) + playsound(src, 'sound/effects/bubbles2.ogg', 80, 1, -3) + +/obj/effect/particle_effect/foam/proc/MakeSlippery() + AddComponent(/datum/component/slippery, 100) + +/obj/effect/particle_effect/foam/Destroy() + STOP_PROCESSING(SSfastprocess, src) + return ..() + + +/obj/effect/particle_effect/foam/proc/kill_foam() + STOP_PROCESSING(SSfastprocess, src) + switch(metal) + if(ALUMINUM_FOAM) + new /obj/structure/foamedmetal(get_turf(src)) + if(IRON_FOAM) + new /obj/structure/foamedmetal/iron(get_turf(src)) + if(RESIN_FOAM) + new /obj/structure/foamedmetal/resin(get_turf(src)) + flick("[icon_state]-disolve", src) + QDEL_IN(src, 5) + +/obj/effect/particle_effect/foam/smart/kill_foam() //Smart foam adheres to area borders for walls + STOP_PROCESSING(SSfastprocess, src) + if(metal) + var/turf/T = get_turf(src) + if(isspaceturf(T)) //Block up any exposed space + T.PlaceOnTop(/turf/open/floor/plating/foam) + for(var/direction in GLOB.cardinals) + var/turf/cardinal_turf = get_step(T, direction) + if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf! + new/obj/structure/foamedmetal(T) + break + flick("[icon_state]-disolve", src) + QDEL_IN(src, 5) + +/obj/effect/particle_effect/foam/process() + lifetime-- + if(lifetime < 1) + kill_foam() + return + + var/fraction = 1/initial(reagent_divisor) + for(var/obj/O in range(0,src)) + if(O.type == src.type) + continue + if(isturf(O.loc)) + var/turf/T = O.loc + if(T.intact && O.level == 1) //hidden under the floor + continue + if(lifetime % reagent_divisor) + reagents.reaction(O, VAPOR, fraction) + var/hit = 0 + for(var/mob/living/L in range(0,src)) + hit += foam_mob(L) + if(hit) + lifetime++ //this is so the decrease from mobs hit and the natural decrease don't cumulate. + var/T = get_turf(src) + if(lifetime % reagent_divisor) + reagents.reaction(T, VAPOR, fraction) + + if(--amount < 0) + return + spread_foam() + +/obj/effect/particle_effect/foam/proc/foam_mob(mob/living/L) + if(lifetime<1) + return 0 + if(!istype(L)) + return 0 + var/fraction = 1/initial(reagent_divisor) + if(lifetime % reagent_divisor) + reagents.reaction(L, VAPOR, fraction) + lifetime-- + return 1 + +/obj/effect/particle_effect/foam/proc/spread_foam() + var/turf/t_loc = get_turf(src) + for(var/turf/T in t_loc.GetAtmosAdjacentTurfs()) + var/obj/effect/particle_effect/foam/foundfoam = locate() in T //Don't spread foam where there's already foam! + if(foundfoam) + continue + + if(is_type_in_typecache(T, blacklisted_turfs)) + continue + + for(var/mob/living/L in T) + foam_mob(L) + var/obj/effect/particle_effect/foam/F = new src.type(T) + F.amount = amount + reagents.copy_to(F, (reagents.total_volume)) + F.add_atom_colour(color, FIXED_COLOUR_PRIORITY) + F.metal = metal + + +/obj/effect/particle_effect/foam/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(prob(max(0, exposed_temperature - 475))) //foam dissolves when heated + kill_foam() + + +/obj/effect/particle_effect/foam/metal/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + return + + +/////////////////////////////////////////////// +//FOAM EFFECT DATUM +/datum/effect_system/foam_spread + var/amount = 10 // the size of the foam spread. + var/obj/chemholder + effect_type = /obj/effect/particle_effect/foam + var/metal = 0 + + +/datum/effect_system/foam_spread/metal + effect_type = /obj/effect/particle_effect/foam/metal + + +/datum/effect_system/foam_spread/metal/smart + effect_type = /obj/effect/particle_effect/foam/smart + + +/datum/effect_system/foam_spread/long + effect_type = /obj/effect/particle_effect/foam/long_life + +/datum/effect_system/foam_spread/New() + ..() + chemholder = new /obj() + var/datum/reagents/R = new/datum/reagents(1000) + chemholder.reagents = R + R.my_atom = chemholder + +/datum/effect_system/foam_spread/Destroy() + qdel(chemholder) + chemholder = null + return ..() + +/datum/effect_system/foam_spread/set_up(amt=5, loca, datum/reagents/carry = null) + if(isturf(loca)) + location = loca + else + location = get_turf(loca) + + amount = round(sqrt(amt / 2), 1) + carry.copy_to(chemholder, carry.total_volume) + +/datum/effect_system/foam_spread/metal/set_up(amt=5, loca, datum/reagents/carry = null, metaltype) + ..() + metal = metaltype + +/datum/effect_system/foam_spread/start() + var/obj/effect/particle_effect/foam/F = new effect_type(location) + var/foamcolor = mix_color_from_reagents(chemholder.reagents.reagent_list) + chemholder.reagents.copy_to(F, chemholder.reagents.total_volume/amount) + F.add_atom_colour(foamcolor, FIXED_COLOUR_PRIORITY) + F.amount = amount + F.metal = metal + + +////////////////////////////////////////////////////////// +// FOAM STRUCTURE. Formed by metal foams. Dense and opaque, but easy to break +/obj/structure/foamedmetal + icon = 'icons/effects/effects.dmi' + icon_state = "metalfoam" + density = TRUE + opacity = 1 // changed in New() + anchored = TRUE + layer = EDGED_TURF_LAYER + resistance_flags = FIRE_PROOF | ACID_PROOF + name = "foamed metal" + desc = "A lightweight foamed metal wall." + gender = PLURAL + max_integrity = 20 + CanAtmosPass = ATMOS_PASS_DENSITY + +/obj/structure/foamedmetal/Initialize() + . = ..() + air_update_turf(1) + +/obj/structure/foamedmetal/Move() + var/turf/T = loc + . = ..() + move_update_air(T) + +/obj/structure/foamedmetal/attack_paw(mob/user) + return attack_hand(user) + +/obj/structure/foamedmetal/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + playsound(src.loc, 'sound/weapons/tap.ogg', 100, 1) + +/obj/structure/foamedmetal/attack_hand(mob/user) + . = ..() + if(.) + return + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) + to_chat(user, "You hit [src] but bounce off it!") + playsound(src.loc, 'sound/weapons/tap.ogg', 100, 1) + +/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target) + return !density + +/obj/structure/foamedmetal/iron + max_integrity = 50 + icon_state = "ironfoam" + +//Atmos Backpack Resin, transparent, prevents atmos and filters the air +/obj/structure/foamedmetal/resin + name = "\improper ATMOS Resin" + desc = "A lightweight, transparent resin used to suffocate fires, scrub the air of toxins, and restore the air to a safe temperature." + opacity = FALSE + icon_state = "atmos_resin" + alpha = 120 + max_integrity = 10 + +/obj/structure/foamedmetal/resin/Initialize() + . = ..() + if(isopenturf(loc)) + var/turf/open/O = loc + O.ClearWet() + if(O.air) + var/datum/gas_mixture/G = O.air + G.temperature = 293.15 + for(var/obj/effect/hotspot/H in O) + qdel(H) + var/list/G_gases = G.gases + for(var/I in G_gases) + if(I == /datum/gas/oxygen || I == /datum/gas/nitrogen) + continue + G_gases[I] = 0 + GAS_GARBAGE_COLLECT(G.gases) + O.air_update_turf() + for(var/obj/machinery/atmospherics/components/unary/U in O) + if(!U.welded) + U.welded = TRUE + U.update_icon() + U.visible_message("[U] sealed shut!") + for(var/mob/living/L in O) + L.ExtinguishMob() + for(var/obj/item/Item in O) + Item.extinguish() + +/obj/structure/foamedmetal/resin/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && (mover.pass_flags & PASSGLASS)) + return TRUE + . = ..() + +#undef ALUMINUM_FOAM +#undef IRON_FOAM +#undef RESIN_FOAM diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm index e8e833890b..79deac475b 100644 --- a/code/game/objects/effects/effect_system/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_smoke.dm @@ -1,329 +1,328 @@ -///////////////////////////////////////////// -//// SMOKE SYSTEMS -///////////////////////////////////////////// - -/obj/effect/particle_effect/smoke - name = "smoke" - icon = 'icons/effects/96x96.dmi' - icon_state = "smoke" - pixel_x = -32 - pixel_y = -32 - opacity = 0 - layer = FLY_LAYER - anchored = TRUE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - animate_movement = 0 - var/amount = 4 - var/lifetime = 5 - var/opaque = 1 //whether the smoke can block the view when in enough amount - - -/obj/effect/particle_effect/smoke/proc/fade_out(frames = 16) - if(alpha == 0) //Handle already transparent case - return - if(frames == 0) - frames = 1 //We will just assume that by 0 frames, the coder meant "during one frame". - var/step = alpha / frames - for(var/i = 0, i < frames, i++) - alpha -= step - if(alpha < 160) - set_opacity(0) //if we were blocking view, we aren't now because we're fading out - stoplag() - -/obj/effect/particle_effect/smoke/Initialize() - . = ..() - create_reagents(500) - START_PROCESSING(SSobj, src) - - -/obj/effect/particle_effect/smoke/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/effect/particle_effect/smoke/proc/kill_smoke() - STOP_PROCESSING(SSobj, src) - INVOKE_ASYNC(src, .proc/fade_out) - QDEL_IN(src, 10) - -/obj/effect/particle_effect/smoke/process() - lifetime-- - if(lifetime < 1) - kill_smoke() - return 0 - for(var/mob/living/L in range(0,src)) - smoke_mob(L) - return 1 - -/obj/effect/particle_effect/smoke/proc/smoke_mob(mob/living/carbon/C) - if(!istype(C)) - return 0 - if(lifetime<1) - return 0 - if(C.internal != null || C.has_smoke_protection()) - return 0 - if(C.smoke_delay) - return 0 - C.smoke_delay++ - addtimer(CALLBACK(src, .proc/remove_smoke_delay, C), 10) - return 1 - -/obj/effect/particle_effect/smoke/proc/remove_smoke_delay(mob/living/carbon/C) - if(C) - C.smoke_delay = 0 - -/obj/effect/particle_effect/smoke/proc/spread_smoke() - var/turf/t_loc = get_turf(src) - if(!t_loc) - return - var/list/newsmokes = list() - for(var/turf/T in t_loc.GetAtmosAdjacentTurfs()) - var/obj/effect/particle_effect/smoke/foundsmoke = locate() in T //Don't spread smoke where there's already smoke! - if(foundsmoke) - continue - for(var/mob/living/L in T) - smoke_mob(L) - var/obj/effect/particle_effect/smoke/S = new type(T) - reagents.copy_to(S, reagents.total_volume) - S.setDir(pick(GLOB.cardinals)) - S.amount = amount-1 - S.add_atom_colour(color, FIXED_COLOUR_PRIORITY) - S.lifetime = lifetime - if(S.amount>0) - if(opaque) - S.set_opacity(TRUE) - newsmokes.Add(S) - - if(newsmokes.len) - spawn(1) //the smoke spreads rapidly but not instantly - for(var/obj/effect/particle_effect/smoke/SM in newsmokes) - SM.spread_smoke() - - -/datum/effect_system/smoke_spread - var/amount = 10 - effect_type = /obj/effect/particle_effect/smoke - -/datum/effect_system/smoke_spread/set_up(radius = 5, loca) - if(isturf(loca)) - location = loca - else - location = get_turf(loca) - amount = radius - -/datum/effect_system/smoke_spread/start() - if(holder) - location = get_turf(holder) - var/obj/effect/particle_effect/smoke/S = new effect_type(location) - S.amount = amount - if(S.amount) - S.spread_smoke() - - -///////////////////////////////////////////// -// Bad smoke -///////////////////////////////////////////// - -/obj/effect/particle_effect/smoke/bad - lifetime = 8 - -/obj/effect/particle_effect/smoke/bad/smoke_mob(mob/living/carbon/M) - if(..()) - M.drop_all_held_items() - M.adjustOxyLoss(1) - M.emote("cough") - return 1 - -/obj/effect/particle_effect/smoke/bad/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /obj/item/projectile/beam)) - var/obj/item/projectile/beam/B = mover - B.damage = (B.damage/2) - return 1 - - - -/datum/effect_system/smoke_spread/bad - effect_type = /obj/effect/particle_effect/smoke/bad - -///////////////////////////////////////////// -// Nanofrost smoke -///////////////////////////////////////////// - -/obj/effect/particle_effect/smoke/freezing - name = "nanofrost smoke" - color = "#B2FFFF" - opaque = 0 - -/datum/effect_system/smoke_spread/freezing - effect_type = /obj/effect/particle_effect/smoke/freezing - var/blast = 0 - var/temperature = 2 - var/weldvents = TRUE - var/distcheck = TRUE - -/datum/effect_system/smoke_spread/freezing/proc/Chilled(atom/A) - if(isopenturf(A)) - var/turf/open/T = A - if(T.air) - var/datum/gas_mixture/G = T.air - if(!distcheck || get_dist(T, location) < blast) // Otherwise we'll get silliness like people using Nanofrost to kill people through walls with cold air - G.temperature = temperature - T.air_update_turf() - for(var/obj/effect/hotspot/H in T) - qdel(H) - var/list/G_gases = G.gases - if(G_gases[/datum/gas/plasma]) - G.assert_gas(/datum/gas/nitrogen) - G_gases[/datum/gas/nitrogen][MOLES] += (G_gases[/datum/gas/plasma][MOLES]) - G_gases[/datum/gas/plasma][MOLES] = 0 - G.garbage_collect() - if (weldvents) - for(var/obj/machinery/atmospherics/components/unary/U in T) - if(!isnull(U.welded) && !U.welded) //must be an unwelded vent pump or vent scrubber. - U.welded = TRUE - U.update_icon() - U.visible_message("[U] was frozen shut!") - for(var/mob/living/L in T) - L.ExtinguishMob() - for(var/obj/item/Item in T) - Item.extinguish() - -/datum/effect_system/smoke_spread/freezing/set_up(radius = 5, loca, blast_radius = 0) - ..() - blast = blast_radius - -/datum/effect_system/smoke_spread/freezing/start() - if(blast) - for(var/turf/T in RANGE_TURFS(blast, location)) - Chilled(T) - ..() - -/datum/effect_system/smoke_spread/freezing/decon - temperature = 293.15 - distcheck = FALSE - weldvents = FALSE - - -///////////////////////////////////////////// -// Sleep smoke -///////////////////////////////////////////// - -/obj/effect/particle_effect/smoke/sleeping - color = "#9C3636" - lifetime = 10 - -/obj/effect/particle_effect/smoke/sleeping/smoke_mob(mob/living/carbon/M) - if(..()) - M.Sleeping(200) - M.emote("cough") - return 1 - -/datum/effect_system/smoke_spread/sleeping - effect_type = /obj/effect/particle_effect/smoke/sleeping - -///////////////////////////////////////////// -// Chem smoke -///////////////////////////////////////////// - -/obj/effect/particle_effect/smoke/chem - lifetime = 10 - - -/obj/effect/particle_effect/smoke/chem/process() - if(..()) - var/turf/T = get_turf(src) - var/fraction = 1/initial(lifetime) - for(var/atom/movable/AM in T) - if(AM.type == src.type) - continue - if(T.intact && AM.level == 1) //hidden under the floor - continue - reagents.reaction(AM, TOUCH, fraction) - - reagents.reaction(T, TOUCH, fraction) - return 1 - -/obj/effect/particle_effect/smoke/chem/smoke_mob(mob/living/carbon/M) - if(lifetime<1) - return 0 - if(!istype(M)) - return 0 - var/mob/living/carbon/C = M - if(C.internal != null || C.has_smoke_protection()) - return 0 - var/fraction = 1/initial(lifetime) - reagents.copy_to(C, fraction*reagents.total_volume) - reagents.reaction(M, INGEST, fraction) - return 1 - - - -/datum/effect_system/smoke_spread/chem - var/obj/chemholder - effect_type = /obj/effect/particle_effect/smoke/chem - -/datum/effect_system/smoke_spread/chem/New() - ..() - chemholder = new /obj() - var/datum/reagents/R = new/datum/reagents(500) - chemholder.reagents = R - R.my_atom = chemholder - -/datum/effect_system/smoke_spread/chem/Destroy() - qdel(chemholder) - chemholder = null - return ..() - -/datum/effect_system/smoke_spread/chem/set_up(datum/reagents/carry = null, radius = 1, loca, silent = FALSE) - if(isturf(loca)) - location = loca - else - location = get_turf(loca) - amount = radius - carry.copy_to(chemholder, carry.total_volume) - - if(!silent) - var/contained = "" - for(var/reagent in carry.reagent_list) - contained += " [reagent] " - if(contained) - contained = "\[[contained]\]" - - var/where = "[AREACOORD(location)]" - if(carry.my_atom.fingerprintslast) - var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast) - var/more = "" - if(M) - more = "[ADMIN_LOOKUPFLW(M)] " - message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [more ? more : carry.my_atom.fingerprintslast].") - log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].") - else - message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. No associated key.") - log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.") - - -/datum/effect_system/smoke_spread/chem/start() - var/mixcolor = mix_color_from_reagents(chemholder.reagents.reagent_list) - if(holder) - location = get_turf(holder) - var/obj/effect/particle_effect/smoke/chem/S = new effect_type(location) - - if(chemholder.reagents.total_volume > 1) // can't split 1 very well - chemholder.reagents.copy_to(S, chemholder.reagents.total_volume) - - if(mixcolor) - S.add_atom_colour(mixcolor, FIXED_COLOUR_PRIORITY) // give the smoke color, if it has any to begin with - S.amount = amount - if(S.amount) - S.spread_smoke() //calling process right now so the smoke immediately attacks mobs. - - -///////////////////////////////////////////// -// Transparent smoke -///////////////////////////////////////////// - -//Same as the base type, but the smoke produced is not opaque -/datum/effect_system/smoke_spread/transparent - effect_type = /obj/effect/particle_effect/smoke/transparent - -/obj/effect/particle_effect/smoke/transparent - opaque = FALSE +///////////////////////////////////////////// +//// SMOKE SYSTEMS +///////////////////////////////////////////// + +/obj/effect/particle_effect/smoke + name = "smoke" + icon = 'icons/effects/96x96.dmi' + icon_state = "smoke" + pixel_x = -32 + pixel_y = -32 + opacity = 0 + layer = FLY_LAYER + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + animate_movement = 0 + var/amount = 4 + var/lifetime = 5 + var/opaque = 1 //whether the smoke can block the view when in enough amount + + +/obj/effect/particle_effect/smoke/proc/fade_out(frames = 16) + if(alpha == 0) //Handle already transparent case + return + if(frames == 0) + frames = 1 //We will just assume that by 0 frames, the coder meant "during one frame". + var/step = alpha / frames + for(var/i = 0, i < frames, i++) + alpha -= step + if(alpha < 160) + set_opacity(0) //if we were blocking view, we aren't now because we're fading out + stoplag() + +/obj/effect/particle_effect/smoke/Initialize() + . = ..() + create_reagents(500) + START_PROCESSING(SSobj, src) + + +/obj/effect/particle_effect/smoke/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/particle_effect/smoke/proc/kill_smoke() + STOP_PROCESSING(SSobj, src) + INVOKE_ASYNC(src, .proc/fade_out) + QDEL_IN(src, 10) + +/obj/effect/particle_effect/smoke/process() + lifetime-- + if(lifetime < 1) + kill_smoke() + return 0 + for(var/mob/living/L in range(0,src)) + smoke_mob(L) + return 1 + +/obj/effect/particle_effect/smoke/proc/smoke_mob(mob/living/carbon/C) + if(!istype(C)) + return 0 + if(lifetime<1) + return 0 + if(C.internal != null || C.has_smoke_protection()) + return 0 + if(C.smoke_delay) + return 0 + C.smoke_delay++ + addtimer(CALLBACK(src, .proc/remove_smoke_delay, C), 10) + return 1 + +/obj/effect/particle_effect/smoke/proc/remove_smoke_delay(mob/living/carbon/C) + if(C) + C.smoke_delay = 0 + +/obj/effect/particle_effect/smoke/proc/spread_smoke() + var/turf/t_loc = get_turf(src) + if(!t_loc) + return + var/list/newsmokes = list() + for(var/turf/T in t_loc.GetAtmosAdjacentTurfs()) + var/obj/effect/particle_effect/smoke/foundsmoke = locate() in T //Don't spread smoke where there's already smoke! + if(foundsmoke) + continue + for(var/mob/living/L in T) + smoke_mob(L) + var/obj/effect/particle_effect/smoke/S = new type(T) + reagents.copy_to(S, reagents.total_volume) + S.setDir(pick(GLOB.cardinals)) + S.amount = amount-1 + S.add_atom_colour(color, FIXED_COLOUR_PRIORITY) + S.lifetime = lifetime + if(S.amount>0) + if(opaque) + S.set_opacity(TRUE) + newsmokes.Add(S) + + if(newsmokes.len) + spawn(1) //the smoke spreads rapidly but not instantly + for(var/obj/effect/particle_effect/smoke/SM in newsmokes) + SM.spread_smoke() + + +/datum/effect_system/smoke_spread + var/amount = 10 + effect_type = /obj/effect/particle_effect/smoke + +/datum/effect_system/smoke_spread/set_up(radius = 5, loca) + if(isturf(loca)) + location = loca + else + location = get_turf(loca) + amount = radius + +/datum/effect_system/smoke_spread/start() + if(holder) + location = get_turf(holder) + var/obj/effect/particle_effect/smoke/S = new effect_type(location) + S.amount = amount + if(S.amount) + S.spread_smoke() + + +///////////////////////////////////////////// +// Bad smoke +///////////////////////////////////////////// + +/obj/effect/particle_effect/smoke/bad + lifetime = 8 + +/obj/effect/particle_effect/smoke/bad/smoke_mob(mob/living/carbon/M) + if(..()) + M.drop_all_held_items() + M.adjustOxyLoss(1) + M.emote("cough") + return 1 + +/obj/effect/particle_effect/smoke/bad/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /obj/item/projectile/beam)) + var/obj/item/projectile/beam/B = mover + B.damage = (B.damage/2) + return 1 + + + +/datum/effect_system/smoke_spread/bad + effect_type = /obj/effect/particle_effect/smoke/bad + +///////////////////////////////////////////// +// Nanofrost smoke +///////////////////////////////////////////// + +/obj/effect/particle_effect/smoke/freezing + name = "nanofrost smoke" + color = "#B2FFFF" + opaque = 0 + +/datum/effect_system/smoke_spread/freezing + effect_type = /obj/effect/particle_effect/smoke/freezing + var/blast = 0 + var/temperature = 2 + var/weldvents = TRUE + var/distcheck = TRUE + +/datum/effect_system/smoke_spread/freezing/proc/Chilled(atom/A) + if(isopenturf(A)) + var/turf/open/T = A + if(T.air) + var/datum/gas_mixture/G = T.air + if(!distcheck || get_dist(T, location) < blast) // Otherwise we'll get silliness like people using Nanofrost to kill people through walls with cold air + G.temperature = temperature + T.air_update_turf() + for(var/obj/effect/hotspot/H in T) + qdel(H) + var/list/G_gases = G.gases + if(G_gases[/datum/gas/plasma]) + G_gases[/datum/gas/nitrogen] += (G_gases[/datum/gas/plasma]) + G_gases[/datum/gas/plasma] = 0 + GAS_GARBAGE_COLLECT(G.gases) + if (weldvents) + for(var/obj/machinery/atmospherics/components/unary/U in T) + if(!isnull(U.welded) && !U.welded) //must be an unwelded vent pump or vent scrubber. + U.welded = TRUE + U.update_icon() + U.visible_message("[U] was frozen shut!") + for(var/mob/living/L in T) + L.ExtinguishMob() + for(var/obj/item/Item in T) + Item.extinguish() + +/datum/effect_system/smoke_spread/freezing/set_up(radius = 5, loca, blast_radius = 0) + ..() + blast = blast_radius + +/datum/effect_system/smoke_spread/freezing/start() + if(blast) + for(var/turf/T in RANGE_TURFS(blast, location)) + Chilled(T) + ..() + +/datum/effect_system/smoke_spread/freezing/decon + temperature = 293.15 + distcheck = FALSE + weldvents = FALSE + + +///////////////////////////////////////////// +// Sleep smoke +///////////////////////////////////////////// + +/obj/effect/particle_effect/smoke/sleeping + color = "#9C3636" + lifetime = 10 + +/obj/effect/particle_effect/smoke/sleeping/smoke_mob(mob/living/carbon/M) + if(..()) + M.Sleeping(200) + M.emote("cough") + return 1 + +/datum/effect_system/smoke_spread/sleeping + effect_type = /obj/effect/particle_effect/smoke/sleeping + +///////////////////////////////////////////// +// Chem smoke +///////////////////////////////////////////// + +/obj/effect/particle_effect/smoke/chem + lifetime = 10 + + +/obj/effect/particle_effect/smoke/chem/process() + if(..()) + var/turf/T = get_turf(src) + var/fraction = 1/initial(lifetime) + for(var/atom/movable/AM in T) + if(AM.type == src.type) + continue + if(T.intact && AM.level == 1) //hidden under the floor + continue + reagents.reaction(AM, TOUCH, fraction) + + reagents.reaction(T, TOUCH, fraction) + return 1 + +/obj/effect/particle_effect/smoke/chem/smoke_mob(mob/living/carbon/M) + if(lifetime<1) + return 0 + if(!istype(M)) + return 0 + var/mob/living/carbon/C = M + if(C.internal != null || C.has_smoke_protection()) + return 0 + var/fraction = 1/initial(lifetime) + reagents.copy_to(C, fraction*reagents.total_volume) + reagents.reaction(M, INGEST, fraction) + return 1 + + + +/datum/effect_system/smoke_spread/chem + var/obj/chemholder + effect_type = /obj/effect/particle_effect/smoke/chem + +/datum/effect_system/smoke_spread/chem/New() + ..() + chemholder = new /obj() + var/datum/reagents/R = new/datum/reagents(500) + chemholder.reagents = R + R.my_atom = chemholder + +/datum/effect_system/smoke_spread/chem/Destroy() + qdel(chemholder) + chemholder = null + return ..() + +/datum/effect_system/smoke_spread/chem/set_up(datum/reagents/carry = null, radius = 1, loca, silent = FALSE) + if(isturf(loca)) + location = loca + else + location = get_turf(loca) + amount = radius + carry.copy_to(chemholder, carry.total_volume) + + if(!silent) + var/contained = "" + for(var/reagent in carry.reagent_list) + contained += " [reagent] " + if(contained) + contained = "\[[contained]\]" + + var/where = "[AREACOORD(location)]" + if(carry.my_atom.fingerprintslast) + var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast) + var/more = "" + if(M) + more = "[ADMIN_LOOKUPFLW(M)] " + message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [more ? more : carry.my_atom.fingerprintslast].") + log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].") + else + message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. No associated key.") + log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.") + + +/datum/effect_system/smoke_spread/chem/start() + var/mixcolor = mix_color_from_reagents(chemholder.reagents.reagent_list) + if(holder) + location = get_turf(holder) + var/obj/effect/particle_effect/smoke/chem/S = new effect_type(location) + + if(chemholder.reagents.total_volume > 1) // can't split 1 very well + chemholder.reagents.copy_to(S, chemholder.reagents.total_volume) + + if(mixcolor) + S.add_atom_colour(mixcolor, FIXED_COLOUR_PRIORITY) // give the smoke color, if it has any to begin with + S.amount = amount + if(S.amount) + S.spread_smoke() //calling process right now so the smoke immediately attacks mobs. + + +///////////////////////////////////////////// +// Transparent smoke +///////////////////////////////////////////// + +//Same as the base type, but the smoke produced is not opaque +/datum/effect_system/smoke_spread/transparent + effect_type = /obj/effect/particle_effect/smoke/transparent + +/obj/effect/particle_effect/smoke/transparent + opaque = FALSE diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index b1bb3e6b4d..65395d534a 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -1,6 +1,6 @@ #define CELSIUS_TO_KELVIN(T_K) ((T_K) + T0C) -#define OPTIMAL_TEMP_K_PLA_BURN_SCALE(PRESSURE_P,PRESSURE_O,TEMP_O) (((PRESSURE_P) * GLOB.meta_gas_info[/datum/gas/plasma][META_GAS_SPECIFIC_HEAT]) / (((PRESSURE_P) * GLOB.meta_gas_info[/datum/gas/plasma][META_GAS_SPECIFIC_HEAT] + (PRESSURE_O) * GLOB.meta_gas_info[/datum/gas/oxygen][META_GAS_SPECIFIC_HEAT]) / PLASMA_UPPER_TEMPERATURE - (PRESSURE_O) * GLOB.meta_gas_info[/datum/gas/oxygen][META_GAS_SPECIFIC_HEAT] / CELSIUS_TO_KELVIN(TEMP_O))) +#define OPTIMAL_TEMP_K_PLA_BURN_SCALE(PRESSURE_P,PRESSURE_O,TEMP_O) (((PRESSURE_P) * GLOB.meta_gas_specific_heats[/datum/gas/plasma]) / (((PRESSURE_P) * GLOB.meta_gas_specific_heats[/datum/gas/plasma] + (PRESSURE_O) * GLOB.meta_gas_specific_heats[/datum/gas/oxygen]) / PLASMA_UPPER_TEMPERATURE - (PRESSURE_O) * GLOB.meta_gas_specific_heats[/datum/gas/oxygen] / CELSIUS_TO_KELVIN(TEMP_O))) #define OPTIMAL_TEMP_K_PLA_BURN_RATIO(PRESSURE_P,PRESSURE_O,TEMP_O) (CELSIUS_TO_KELVIN(TEMP_O) * PLASMA_OXYGEN_FULLBURN * (PRESSURE_P) / (PRESSURE_O)) /obj/effect/spawner/newbomb @@ -19,12 +19,10 @@ var/obj/item/tank/internals/plasma/PT = new(V) var/obj/item/tank/internals/oxygen/OT = new(V) - PT.air_contents.assert_gas(/datum/gas/plasma) - PT.air_contents.gases[/datum/gas/plasma][MOLES] = pressure_p*PT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_p)) + PT.air_contents.gases[/datum/gas/plasma] = pressure_p*PT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_p)) PT.air_contents.temperature = CELSIUS_TO_KELVIN(temp_p) - OT.air_contents.assert_gas(/datum/gas/oxygen) - OT.air_contents.gases[/datum/gas/oxygen][MOLES] = pressure_o*OT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_o)) + OT.air_contents.gases[/datum/gas/oxygen] = pressure_o*OT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_o)) OT.air_contents.temperature = CELSIUS_TO_KELVIN(temp_o) V.tank_one = PT diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 9ff1b5cf01..9314faab27 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -449,7 +449,11 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) return 0 /obj/item/proc/eyestab(mob/living/carbon/M, mob/living/carbon/user) - + if(user.has_trait(TRAIT_PACIFISM)) + to_chat(user, "You don't want to harm [M]!") + return + if(user.has_trait(TRAIT_CLUMSY) && prob(50)) + M = user var/is_human_victim = 0 var/obj/item/bodypart/affecting = M.get_bodypart(BODY_ZONE_HEAD) if(ishuman(M)) diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 30b1ca3399..96bbe759ca 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -105,7 +105,7 @@ if(isturf(A)) return - if(istype(A,/obj/item/storage/lockbox)) + if(istype(A,/obj/item/storage/lockbox) || istype(A, /obj/item/storage/pod)) A.emag_act(user) uses = max(uses - 1, 0) if(!uses) diff --git a/code/game/objects/items/chrono_eraser.dm b/code/game/objects/items/chrono_eraser.dm index f7c37715aa..344e7c5472 100644 --- a/code/game/objects/items/chrono_eraser.dm +++ b/code/game/objects/items/chrono_eraser.dm @@ -248,9 +248,8 @@ /obj/effect/chrono_field/return_air() //we always have nominal air and temperature var/datum/gas_mixture/GM = new - GM.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen) - GM.gases[/datum/gas/oxygen][MOLES] = MOLES_O2STANDARD - GM.gases[/datum/gas/nitrogen][MOLES] = MOLES_N2STANDARD + GM.gases[/datum/gas/oxygen] = MOLES_O2STANDARD + GM.gases[/datum/gas/nitrogen] = MOLES_N2STANDARD GM.temperature = T20C return GM diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 26f8a73cb3..30089f298f 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -68,6 +68,8 @@ var/pre_noise = FALSE var/post_noise = FALSE + var/datum/team/gang/gang //For marking territory. + var/gang_tag_delay = 30 //this is the delay for gang mode tag applications on anything that gang = true on. /obj/item/toy/crayon/suicide_act(mob/user) user.visible_message("[user] is jamming [src] up [user.p_their()] nose and into [user.p_their()] brain. It looks like [user.p_theyre()] trying to commit suicide!") @@ -288,6 +290,13 @@ else if(drawing in numerals) temp = "number" + // If a gang member is using a gang spraycan, it'll behave differently + var/gang_mode = FALSE + if(gang && user.mind && user.mind.has_antag_datum(/datum/antagonist/gang)) //Heres a check. + gang_mode = TRUE // No more runtimes if a non-gang member sprays a gang can, it just works like normal cans. + // discontinue if the area isn't valid for tagging because gang "honour" + if(gang_mode && (!can_claim_for_gang(user, target))) + return var/graf_rot if(drawing in oriented) @@ -310,20 +319,22 @@ clicky = CLAMP(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2) if(!instant) - to_chat(user, "You start drawing a [temp] on the [target.name]...") + to_chat(user, "You start drawing a [temp] on the [target.name]...") if(pre_noise) audible_message("You hear spraying.") playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) - var/takes_time = !instant + var/takes_time = !instant //For order purposes, since I'm maximum bad. + if(gang_mode) + takes_time = TRUE var/wait_time = 50 if(paint_mode == PAINT_LARGE_HORIZONTAL) wait_time *= 3 - if(takes_time) - if(!do_after(user, 50, target = target)) + if(takes_time) //This is what deteremines the time it takes to spray a tag in gang mode. 50 is Default. + if(!do_after(user, gang_tag_delay, target = target)) //25 is a good number, but we have gang_tag_delay var now. return if(length(text_buffer)) @@ -332,26 +343,34 @@ var/list/turf/affected_turfs = list() + if(actually_paints) - switch(paint_mode) - if(PAINT_NORMAL) - var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot) - C.add_hiddenprint(user) - C.pixel_x = clickx - C.pixel_y = clicky - affected_turfs += target - if(PAINT_LARGE_HORIZONTAL) - var/turf/left = locate(target.x-1,target.y,target.z) - var/turf/right = locate(target.x+1,target.y,target.z) - if(is_type_in_list(left, validSurfaces) && is_type_in_list(right, validSurfaces)) - var/obj/effect/decal/cleanable/crayon/C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON) + if(gang_mode) + // Double check it wasn't tagged in the meanwhile. + if(!can_claim_for_gang(user, target)) + return + tag_for_gang(user, target) + affected_turfs += target + else + switch(paint_mode) + if(PAINT_NORMAL) + var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot) C.add_hiddenprint(user) - affected_turfs += left - affected_turfs += right + C.pixel_x = clickx + C.pixel_y = clicky affected_turfs += target - else - to_chat(user, "There isn't enough space to paint!") - return + if(PAINT_LARGE_HORIZONTAL) + var/turf/left = locate(target.x-1,target.y,target.z) + var/turf/right = locate(target.x+1,target.y,target.z) + if(is_type_in_list(left, validSurfaces) && is_type_in_list(right, validSurfaces)) + var/obj/effect/decal/cleanable/crayon/C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON) + C.add_hiddenprint(user) + affected_turfs += left + affected_turfs += right + affected_turfs += target + else + to_chat(user, "There isn't enough space to paint!") + return if(!instant) to_chat(user, "You finish drawing \the [temp].") @@ -373,6 +392,52 @@ reagents.trans_to(t, ., volume_multiplier) check_empty(user) + +//////////////Gang mode stuff///////////////// +/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target) + // Check area validity. + // Reject space, player-created areas, and non-station z-levels. + var/area/A = get_area(target) + if(!A || (!is_station_level(A.z)) || !A.valid_territory) + to_chat(user, "[A] is unsuitable for tagging.") + return FALSE + + var/spraying_over = FALSE + for(var/G in target) + var/obj/effect/decal/cleanable/crayon/gang/gangtag = G + if(istype(gangtag)) + var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang) + if(gangtag.gang != GA.gang) + spraying_over = TRUE + break + + var/occupying_gang = territory_claimed(A, user) + if(occupying_gang && !spraying_over) + to_chat(user, "[A] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!") + return FALSE + + // If you pass the gauntlet of checks, you're good to proceed + return TRUE + +/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user) + for(var/datum/team/gang/G in GLOB.gangs) + if(territory.type in (G.territories|G.new_territories)) + . = G.name + break + +/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target) + //Delete any old markings on this tile, including other gang tags + for(var/obj/effect/decal/cleanable/crayon/old_marking in target) + qdel(old_marking) + + var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) + var/area/territory = get_area(target) + + new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"graffiti",0,user) // Heres the gang tag. + to_chat(user, "You tagged [territory] for your gang!") +/////////////////Gang end//////////////////// + + /obj/item/toy/crayon/attack(mob/M, mob/user) if(edible && (M == user)) to_chat(user, "You take a bite of the [src.name]. Delicious!") @@ -524,6 +589,7 @@ is_capped = TRUE self_contained = FALSE // Don't disappear when they're empty can_change_colour = TRUE + gang = TRUE //Gang check is true for all things upon the honored hierarchy of spraycans, except those that are FALSE. validSurfaces = list(/turf/open/floor, /turf/closed/wall) reagent_contents = list("welding_fuel" = 1, "ethanol" = 1) @@ -669,6 +735,7 @@ icon_capped = "deathcan2_cap" icon_uncapped = "deathcan2" use_overlays = FALSE + gang = FALSE volume_multiplier = 25 charges = 100 @@ -683,6 +750,7 @@ icon_capped = "clowncan2_cap" icon_uncapped = "clowncan2" use_overlays = FALSE + gang = FALSE reagent_contents = list("lube" = 1, "banana" = 1) volume_multiplier = 5 @@ -695,6 +763,7 @@ icon_capped = "mimecan_cap" icon_uncapped = "mimecan" use_overlays = FALSE + gang = FALSE can_change_colour = FALSE paint_color = "#FFFFFF" //RGB @@ -703,6 +772,26 @@ post_noise = FALSE reagent_contents = list("nothing" = 1, "mutetoxin" = 1) +/obj/item/toy/crayon/spraycan/gang + charges = 20 // Charges back to 20, which is the default value for them. + gang = TRUE + gang_tag_delay = 15 //Its 50% faster than a regular spraycan, for tagging. After-all they did spend points/meet the boss. + + pre_noise = FALSE + post_noise = TRUE // Its even more stealthy just a tad. + +/obj/item/toy/crayon/spraycan/gang/Initialize(loc, datum/team/gang/G) + ..() + if(G) + gang = G + paint_color = G.color + update_icon() + +/obj/item/toy/crayon/spraycan/gang/examine(mob/user) + . = ..() + if(user.mind && user.mind.has_antag_datum(/datum/antagonist/gang) || isobserver(user)) + to_chat(user, "This spraycan has been specially modified with a stage 2 nozzle kit, making it faster.") + #undef RANDOM_GRAFFITI #undef RANDOM_LETTER #undef RANDOM_NUMBER diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 7d3d36c5b2..48b21d58bd 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -358,9 +358,9 @@ GLOBAL_LIST_EMPTY(PDAs) if (total_moles) for(var/id in env_gases) - var/gas_level = env_gases[id][MOLES]/total_moles + var/gas_level = env_gases[id]/total_moles if(gas_level > 0) - dat += "[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_level*100, 0.01)]%
" + dat += "[GLOB.meta_gas_names[id]]: [round(gas_level*100, 0.01)]%
" dat += "Temperature: [round(environment.temperature-T0C)]°C
" dat += "
" diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 79b04c771b..2df0ad0fbb 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -428,39 +428,38 @@ SLIME SCANNER if(total_moles) var/list/env_gases = environment.gases - environment.assert_gases(arglist(GLOB.hardcoded_gases)) - var/o2_concentration = env_gases[/datum/gas/oxygen][MOLES]/total_moles - var/n2_concentration = env_gases[/datum/gas/nitrogen][MOLES]/total_moles - var/co2_concentration = env_gases[/datum/gas/carbon_dioxide][MOLES]/total_moles - var/plasma_concentration = env_gases[/datum/gas/plasma][MOLES]/total_moles + var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles + var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles + var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles + var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles if(abs(n2_concentration - N2STANDARD) < 20) - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen][MOLES], 0.01)] mol)") + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") else - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen][MOLES], 0.01)] mol)") + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") if(abs(o2_concentration - O2STANDARD) < 2) - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen][MOLES], 0.01)] mol)") + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") else - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen][MOLES], 0.01)] mol)") + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") if(co2_concentration > 0.01) - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide][MOLES], 0.01)] mol)") + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") else - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide][MOLES], 0.01)] mol)") + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") if(plasma_concentration > 0.005) - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma][MOLES], 0.01)] mol)") + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") else - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma][MOLES], 0.01)] mol)") + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") - environment.garbage_collect() + GAS_GARBAGE_COLLECT(environment.gases) for(var/id in env_gases) if(id in GLOB.hardcoded_gases) continue - var/gas_concentration = env_gases[id][MOLES]/total_moles - to_chat(user, "[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id][MOLES], 0.01)] mol)") + var/gas_concentration = env_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id], 0.01)] mol)") to_chat(user, "Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)") /obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens @@ -550,8 +549,8 @@ SLIME SCANNER var/list/cached_gases = air_contents.gases for(var/id in cached_gases) - var/gas_concentration = cached_gases[id][MOLES]/total_moles - to_chat(user, "[cached_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id][MOLES], 0.01)] mol)") + var/gas_concentration = cached_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id], 0.01)] mol)") to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)") else diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm index 2635955a15..fd51ecf26b 100644 --- a/code/game/objects/items/flamethrower.dm +++ b/code/game/objects/items/flamethrower.dm @@ -205,7 +205,7 @@ //Transfer 5% of current tank air contents to turf var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(release_amount) if(air_transfer.gases[/datum/gas/plasma]) - air_transfer.gases[/datum/gas/plasma][MOLES] *= 5 + air_transfer.gases[/datum/gas/plasma] *= 5 target.assume_air(air_transfer) //Burn it based on transfered gas target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) diff --git a/code/game/objects/items/implants/implant_mindshield.dm b/code/game/objects/items/implants/implant_mindshield.dm index ed930ee480..1982740cc9 100644 --- a/code/game/objects/items/implants/implant_mindshield.dm +++ b/code/game/objects/items/implants/implant_mindshield.dm @@ -27,7 +27,7 @@ if(target.mind.has_antag_datum(/datum/antagonist/brainwashed)) target.mind.remove_antag_datum(/datum/antagonist/brainwashed) - if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable) + if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable || target.mind.has_antag_datum(/datum/antagonist/gang/boss)) if(!silent) target.visible_message("[target] seems to resist the implant!", "You feel something interfering with your mental conditioning, but you resist it!") var/obj/item/implanter/I = loc @@ -38,9 +38,12 @@ I.update_icon() return FALSE + var/datum/antagonist/gang/gang = target.mind.has_antag_datum(/datum/antagonist/gang) var/datum/antagonist/rev/rev = target.mind.has_antag_datum(/datum/antagonist/rev) if(rev) rev.remove_revolutionary(FALSE, user) + if(gang) + target.mind.remove_antag_datum(/datum/antagonist/gang) if(!silent) if(target.mind in SSticker.mode.cult) to_chat(target, "You feel something interfering with your mental conditioning, but you resist it!") diff --git a/code/game/objects/items/implants/implant_misc.dm b/code/game/objects/items/implants/implant_misc.dm index 6db3699beb..889f702ee7 100644 --- a/code/game/objects/items/implants/implant_misc.dm +++ b/code/game/objects/items/implants/implant_misc.dm @@ -33,24 +33,8 @@ /obj/item/implant/adrenalin/activate() . = ..() uses-- + imp_in.do_adrenaline(150, TRUE, 0, 0, TRUE, list("inaprovaline" = 3, "synaptizine" = 10, "omnizine" = 10, "stimulants" = 10), "You feel a sudden surge of energy!") to_chat(imp_in, "You feel a sudden surge of energy!") - imp_in.SetSleeping(0) - imp_in.SetStun(0) - imp_in.SetKnockdown(0) - imp_in.SetUnconscious(0) - imp_in.adjustStaminaLoss(-150) - imp_in.stuttering = 0 - imp_in.updatehealth() - imp_in.update_stamina() - imp_in.resting = 0 - imp_in.lying = 0 - imp_in.update_canmove() - - imp_in.reagents.add_reagent("inaprovaline", 3) //let's give another chance to dumb fucks who forget to breathe - imp_in.reagents.add_reagent("synaptizine", 10) - imp_in.reagents.add_reagent("omnizine", 10) - imp_in.reagents.add_reagent("stimulants", 10) - if(!uses) qdel(src) diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm index 11d6f264b8..ef2aa825e7 100644 --- a/code/game/objects/items/kitchen.dm +++ b/code/game/objects/items/kitchen.dm @@ -49,8 +49,6 @@ forkload = null else if(user.zone_selected == BODY_ZONE_PRECISE_EYES) - if(user.has_trait(TRAIT_CLUMSY) && prob(50)) - M = user return eyestab(M,user) else return ..() @@ -79,8 +77,6 @@ /obj/item/kitchen/knife/attack(mob/living/carbon/M, mob/living/carbon/user) if(user.zone_selected == BODY_ZONE_PRECISE_EYES) - if(user.has_trait(TRAIT_CLUMSY) && prob(50)) - M = user return eyestab(M,user) else return ..() diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 3a35f4d8ef..8ed0f273e6 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -283,7 +283,7 @@ else var/turf/T = get_turf(src) if(!isspaceturf(T)) - consume_turf(T) + shard.consume_turf(T) /obj/item/melee/supermatter_sword/afterattack(target, mob/user, proximity_flag) . = ..() @@ -330,18 +330,7 @@ else if(!isturf(target)) shard.Bumped(target) else - consume_turf(target) - -/obj/item/melee/supermatter_sword/proc/consume_turf(turf/T) - var/oldtype = T.type - var/turf/newT = T.ScrapeAway() - if(newT.type == oldtype) - return - playsound(T, 'sound/effects/supermatter.ogg', 50, 1) - T.visible_message("[T] smacks into [src] and rapidly flashes to ash.",\ - "You hear a loud crack as you are washed with a wave of heat.") - shard.Consume() - T.CalculateAdjacentTurfs() + shard.consume_turf(target) /obj/item/melee/supermatter_sword/add_blood_DNA(list/blood_dna) return FALSE diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index 703de902b6..5fe6d2d442 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -591,10 +591,22 @@ desc = "An adorable stuffed toy that resembles a slime. It is practically just a hacky sack." icon_state = "plushie_slime" item_state = "plushie_slime" - attack_verb = list("blorbled", "slimed", "absorbed") + attack_verb = list("blorbled", "slimed", "absorbed", "glomped") squeak_override = list('sound/effects/blobattack.ogg' = 1) gender = FEMALE //given all the jokes and drawings, I'm not sure the xenobiologists would make a slimeboy +/obj/item/toy/plush/slimeplushie/annie + desc = "An adorable stuffed toy that resembles a slimey crewmember." + icon_state = "annie" + item_state = "annie" + +/obj/item/toy/plush/slimeplushie/paxton + desc = "An adorable stuffed toy that resembles a slimey crewmember." + icon_state = "paxton" + item_state = "paxton" + attack_verb = list("CQC'd", "jabroni'd", "powergamed", "robusted", "cakehatted") + gender = MALE + /obj/item/toy/plush/awakenedplushie name = "awakened plushie" desc = "An ancient plushie that has grown enlightened to the true nature of reality." @@ -635,6 +647,13 @@ attack_verb = list("lit", "flickered", "flashed") squeak_override = list('sound/weapons/magout.ogg' = 1) +/obj/item/toy/plush/box + name = "cardboard plushie" + desc = "A toy box plushie, it holds cotten. Only a baddie would place a bomb through the postal system..." + icon_state = "box" + item_state = "box" + attack_verb = list("open", "closed", "packed", "hidden", "rigged", "bombed", "sent", "gave") + /obj/item/toy/plush/borgplushie name = "robot plushie" desc = "An adorable stuffed toy of a robot." @@ -659,6 +678,12 @@ icon_state = "neeb" item_state = "neeb" +/obj/item/toy/plush/borgplushie/bhijn + desc = "An adorable stuffed toy of a IPC." + icon_state = "bhijn" + item_state = "bhijn" + attack_verb = list("closed", "reworked", "merged") + /obj/item/toy/plush/bird name = "bird plushie" desc = "An adorable stuffed plushie that resembles an avian." @@ -728,6 +753,10 @@ icon_state = "pavel" item_state = "pavel" +/obj/item/toy/plush/mammal/mason + icon_state = "mason" + item_state = "mason" + /obj/item/toy/plush/mammal/oten icon_state = "oten" item_state = "oten" @@ -736,6 +765,10 @@ icon_state = "ray" item_state = "ray" +/obj/item/toy/plush/mammal/redtail + icon_state = "redtail" + item_state = "redtail" + /obj/item/toy/plush/mammal/dawud icon_state = "dawud" item_state = "dawud" @@ -743,6 +776,7 @@ /obj/item/toy/plush/mammal/edgar icon_state = "edgar" item_state = "edgar" + attack_verb = list("collared", "tricked", "headpatted") /obj/item/toy/plush/mammal/frank icon_state = "frank" @@ -777,6 +811,16 @@ icon_state = "zed" item_state = "zed" +/obj/item/toy/plush/mammal/justin + icon_state = "justin" + item_state = "justin" + attack_verb = list("buttslapped", "fixed") + +/obj/item/toy/plush/mammal/reece + icon_state = "reece" + item_state = "reece" + attack_verb = list("healed", "cured", "demoted") + /obj/item/toy/plush/mammal/dog desc = "An adorable stuffed toy that resembles a canine." icon_state = "katlin" @@ -840,6 +884,12 @@ icon_state = "drew" item_state = "drew" +/obj/item/toy/plush/catgirl/trilby + desc = "A masked stuffed toy that resembles a feline scientist." + icon_state = "trilby" + item_state = "trilby" + attack_verb = list("pred", "coded", "remembered") + /obj/item/toy/plush/catgirl/fermis name = "medcat plushie" desc = "An affectionate stuffed toy that resembles a certain medcat, comes complete with battery operated wagging tail!! You get the impression she's cheering you on to to find happiness and be kind to people." diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 1171bd7bf7..30c7115346 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -153,3 +153,16 @@ slot_flags = null to_chat(user, "[src] can now be concealed.") add_fingerprint(user) + +/obj/item/shield/makeshift + name = "metal shield" + desc = "A large shield made of wired and welded sheets of metal. The handle is made of cloth and leather making it unwieldy." + armor = list("melee" = 25, "bullet" = 25, "laser" = 5, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 80) + lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' + item_state = "makeshift_shield" + materials = list(MAT_METAL = 18000) + slot_flags = null + block_chance = 25 + force = 5 + throwforce = 7 diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 89d939e505..050e4f7b15 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -252,7 +252,8 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \ new/datum/stack_recipe("bedsheet", /obj/item/bedsheet, 3), \ new/datum/stack_recipe("empty sandbag", /obj/item/emptysandbag, 4), \ null, \ - new/datum/stack_recipe("fingerless gloves", /obj/item/clothing/gloves/fingerless, 1), \ + new/datum/stack_recipe("fingerless gloves", /obj/item/clothing/gloves/fingerless, 1),\ + new/datum/stack_recipe("white gloves", /obj/item/clothing/gloves/color/white, 1),\ new/datum/stack_recipe("black gloves", /obj/item/clothing/gloves/color/black, 3), \ null, \ new/datum/stack_recipe("blindfold", /obj/item/clothing/glasses/sunglasses/blindfold, 2), \ @@ -281,16 +282,38 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \ */ GLOBAL_LIST_INIT(cardboard_recipes, list ( \ new/datum/stack_recipe("box", /obj/item/storage/box), \ + new/datum/stack_recipe("sec box", /obj/item/storage/box/seclooking), \ new/datum/stack_recipe("light tubes", /obj/item/storage/box/lights/tubes), \ new/datum/stack_recipe("light bulbs", /obj/item/storage/box/lights/bulbs), \ new/datum/stack_recipe("mouse traps", /obj/item/storage/box/mousetraps), \ + new/datum/stack_recipe("pizza box", /obj/item/pizzabox), \ + new/datum/stack_recipe("power cell", /obj/item/storage/box/cells), \ + new/datum/stack_recipe("02", /obj/item/storage/box/otwo), \ + null, \ + new/datum/stack_recipe("lethal ammo box", /obj/item/storage/box/lethalshot), \ + new/datum/stack_recipe("rubber shot ammo box", /obj/item/storage/box/rubbershot), \ + new/datum/stack_recipe("bean bag ammo box", /obj/item/storage/box/beanbag), \ + new/datum/stack_recipe("12g ammo box", /obj/item/storage/box/lethalslugs), \ + new/datum/stack_recipe("stun slug ammo box", /obj/item/storage/box/stunslug), \ + new/datum/stack_recipe("tech shell ammo box", /obj/item/storage/box/techsslug), \ + new/datum/stack_recipe("incendiary ammo box", /obj/item/storage/box/fireshot), \ + new/datum/stack_recipe("firing pins", /obj/item/storage/box/firingpins), \ + new/datum/stack_recipe("loose ammo", /obj/item/storage/box/ammoshells), \ + null, \ new/datum/stack_recipe("cardborg suit", /obj/item/clothing/suit/cardborg, 3), \ new/datum/stack_recipe("cardborg helmet", /obj/item/clothing/head/cardborg), \ - new/datum/stack_recipe("pizza box", /obj/item/pizzabox), \ new/datum/stack_recipe("folder", /obj/item/folder), \ new/datum/stack_recipe("large box", /obj/structure/closet/cardboard, 4), \ new/datum/stack_recipe("cardboard cutout", /obj/item/cardboard_cutout, 5), \ -)) + null, \ + new/datum/stack_recipe("colored brown", /obj/item/storage/box/brown), \ + new/datum/stack_recipe("colored green", /obj/item/storage/box/green), \ + new/datum/stack_recipe("colored red", /obj/item/storage/box/blue), \ + new/datum/stack_recipe("colored blue", /obj/item/storage/box/red), \ + new/datum/stack_recipe("colored yellow", /obj/item/storage/box/yellow), \ + new/datum/stack_recipe("colored pink", /obj/item/storage/box/pink), \ + new/datum/stack_recipe("colored purple", /obj/item/storage/box/purple), \ + )) /obj/item/stack/sheet/cardboard //BubbleWrap //it's cardboard you fuck name = "cardboard" diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 1f1143e7ce..5fd676ee50 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -28,7 +28,6 @@ STR.max_w_class = WEIGHT_CLASS_NORMAL STR.max_items = 21 - /* * Backpack Types */ @@ -244,6 +243,18 @@ icon_state = "satchel-explorer" item_state = "securitypack" +/obj/item/storage/backpack/satchel/bone + name = "bone satchel" + desc = "A bone satchel fashend with watcher wings and large bones from goliath. Can be worn on the belt." + icon = 'icons/obj/mining.dmi' + icon_state = "goliath_saddle" + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT + +/obj/item/storage/backpack/satchel/bone/ComponentInitialize() + . = ..() + GET_COMPONENT(STR, /datum/component/storage) + STR.max_combined_w_class = 10 + /obj/item/storage/backpack/satchel/cap name = "captain's satchel" desc = "An exclusive satchel for Nanotrasen officers." diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index c8e96d0b28..d94b43c02b 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -14,9 +14,11 @@ * ID and security PDA cart boxes, * Handcuff, mousetrap, and pillbottle boxes, * Snap-pops and matchboxes, - * Replacement light boxes. - * Action Figure Boxes - * Various paper bags. + * Replacement light boxes, + * Ammo types, + * Action Figure Boxes, + * Various paper bags, + * Colored boxes * * For syndicate call-ins see uplink_kits.dm */ @@ -74,7 +76,6 @@ return 0 return ..() - //Disk boxes /obj/item/storage/box/disks name = "diskette box" @@ -84,7 +85,6 @@ for(var/i in 1 to 7) new /obj/item/disk/data(src) - /obj/item/storage/box/disks_plantgene name = "plant data disks box" illustration = "disk_kit" @@ -117,7 +117,6 @@ new /obj/item/crowbar/red(src) new /obj/item/reagent_containers/hypospray/medipen(src) - // Engineer survival box /obj/item/storage/box/engineer/PopulateContents() new /obj/item/clothing/mask/breath(src) @@ -143,6 +142,29 @@ ..() // we want the regular stuff too new /obj/item/radio/off(src) +/obj/item/storage/box/seclooking + icon_state = "secbox" + illustration = null + +/obj/item/storage/box/cells + name = "box of powercells" + desc = "Contains powercells." + illustration = "power_cell" + +/obj/item/storage/box/ammoshells + name = "box of loose ammo" + desc = "Contains loose ammo." + illustration = "loose_ammo" + +/obj/item/storage/box/otwo + name = "box of o2 supplies" + desc = "Contains o2 supplies." + illustration = "02" + +/obj/item/storage/box/otwo/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/tank/internals/emergency_oxygen/engi(src) + /obj/item/storage/box/gloves name = "box of latex gloves" desc = "Contains sterile latex gloves." @@ -261,7 +283,6 @@ new /obj/item/assembly/flash/handheld(src) new /obj/item/screwdriver(src) - /obj/item/storage/box/teargas name = "box of tear gas grenades (WARNING)" desc = "WARNING: These devices are extremely dangerous and can cause blindness and skin irritation." @@ -465,7 +486,7 @@ /obj/item/storage/box/firingpins name = "box of standard firing pins" desc = "A box full of standard firing pins, to allow newly-developed firearms to operate." - illustration = "id" + illustration = "firing_pins" /obj/item/storage/box/firingpins/PopulateContents() for(var/i in 1 to 5) @@ -474,7 +495,7 @@ /obj/item/storage/box/lasertagpins name = "box of laser tag firing pins" desc = "A box full of laser tag firing pins, to allow newly-developed firearms to require wearing brightly coloured plastic armor before being able to be used." - illustration = "id" + illustration = "firing_pins" /obj/item/storage/box/lasertagpins/PopulateContents() for(var/i in 1 to 3) @@ -615,7 +636,6 @@ for(var/i in 1 to 7) new /obj/item/light/bulb(src) - /obj/item/storage/box/deputy name = "box of deputy armbands" desc = "To be issued to those authorized to act as deputy of security." @@ -721,6 +741,46 @@ for(var/i in 1 to 6) new /obj/item/ammo_casing/shotgun/beanbag(src) +/obj/item/storage/box/lethalslugs + name = "box of 12g shotgun slugs" + desc = "A box full of lethal 12g slug, designed for riot shotguns." + icon_state = "12g_box" + illustration = null + +/obj/item/storage/box/lethalslugs/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/projectile/bullet/shotgun_slug(src) + +/obj/item/storage/box/stunslug + name = "box of stun slugs" + desc = "A box full of stun 12g slugs." + icon_state = "stunslug_box" + illustration = null + +/obj/item/storage/box/stunslug/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/projectile/bullet/shotgun_stunslug(src) + +/obj/item/storage/box/techsslug + name = "box of tech shotgun shells" + desc = "A box full of tech shotgun shells." + icon_state = "techslug_box" + illustration = null + +/obj/item/storage/box/techsslug/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/techshell(src) + +/obj/item/storage/box/fireshot + name = "box of incendiary ammo" + desc = "A box full of tech incendiary ammo." + icon_state = "fireshot_box" + illustration = null + +/obj/item/storage/box/techsslug/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/incendiary(src) + /obj/item/storage/box/actionfigure name = "box of action figures" desc = "The latest set of collectable action figures." @@ -966,7 +1026,6 @@ for(var/i in 1 to 7) new /obj/item/reagent_containers/pill/patch/silver_sulf(src) - /obj/item/storage/box/fountainpens name = "box of fountain pens" @@ -1025,3 +1084,32 @@ new /obj/item/stock_parts/matter_bin/bluespace(src) new /obj/item/stock_parts/matter_bin/bluespace(src) new /obj/item/stock_parts/matter_bin/bluespace(src) + +//Colored boxes. +/obj/item/storage/box/green + icon_state = "box_green" + illustration = null + +/obj/item/storage/box/blue + icon_state = "box_blue" + illustration = null + +/obj/item/storage/box/purple + icon_state = "box_purple" + illustration = null + +/obj/item/storage/box/red + icon_state = "box_red" + illustration = null + +/obj/item/storage/box/yellow + icon_state = "box_yellow" + illustration = null + +/obj/item/storage/box/brown + icon_state = "box_brown" + illustration = null + +/obj/item/storage/box/pink + icon_state = "box_pink" + illustration = null diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index c0e81a7949..9bf7ba082c 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -1,3 +1,5 @@ +#define STUNBATON_CHARGE_LENIENCY 0.3 + /obj/item/melee/baton name = "stunbaton" desc = "A stun baton for incapacitating people with." @@ -13,7 +15,7 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) var/stunforce = 140 - var/status = 0 + var/status = FALSE var/obj/item/stock_parts/cell/cell var/hitcost = 1000 var/throw_hit_chance = 35 @@ -44,17 +46,30 @@ /obj/item/melee/baton/loaded //this one starts with a cell pre-installed. preload_cell_type = /obj/item/stock_parts/cell/high -/obj/item/melee/baton/proc/deductcharge(chrgdeductamt) - if(cell) - //Note this value returned is significant, as it will determine - //if a stun is applied or not - . = cell.use(chrgdeductamt) - if(status && cell.charge < hitcost) - //we're below minimum, turn off - status = 0 - update_icon() - playsound(loc, "sparks", 75, 1, -1) +/obj/item/melee/baton/proc/deductcharge(chrgdeductamt, chargecheck = TRUE) + if(!cell) + switch_status(FALSE, TRUE) + return FALSE + //Note this value returned is significant, as it will determine + //if a stun is applied or not + . = cell.use(chrgdeductamt) + if(status && (!. || (chargecheck && cell.charge < hitcost * STUNBATON_CHARGE_LENIENCY))) + //we're below minimum, turn off + switch_status(FALSE) +/obj/item/melee/baton/proc/switch_status(new_status = FALSE, silent = FALSE) + if(status != new_status) + status = new_status + if(!silent) + playsound(loc, "sparks", 75, 1, -1) + if(status) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + update_icon() + +/obj/item/melee/baton/process() + deductcharge(hitcost * 0.004, FALSE) /obj/item/melee/baton/update_icon() if(status) @@ -77,7 +92,7 @@ if(cell) to_chat(user, "[src] already has a cell.") else - if(C.maxcharge < hitcost) + if(C.maxcharge < hitcost * STUNBATON_CHARGE_LENIENCY) to_chat(user, "[src] requires a higher capacity cell.") return if(!user.transferItemToLoc(W, src)) @@ -92,31 +107,25 @@ cell.forceMove(get_turf(src)) cell = null to_chat(user, "You remove the cell from [src].") - status = 0 - update_icon() + switch_status(FALSE, TRUE) else return ..() /obj/item/melee/baton/attack_self(mob/user) - if(cell && cell.charge > hitcost) - status = !status + if(cell && cell.charge > hitcost * STUNBATON_CHARGE_LENIENCY) + switch_status(!status) to_chat(user, "[src] is now [status ? "on" : "off"].") - playsound(loc, "sparks", 75, 1, -1) else - status = 0 + switch_status(FALSE, TRUE) if(!cell) to_chat(user, "[src] does not have a power source!") else to_chat(user, "[src] is out of charge.") - update_icon() add_fingerprint(user) /obj/item/melee/baton/attack(mob/M, mob/living/carbon/human/user) if(status && user.has_trait(TRAIT_CLUMSY) && prob(50)) - user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \ - "You accidentally hit yourself with [src]!") - user.Knockdown(stunforce*3) - deductcharge(hitcost) + clowning_around(user) return if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)//CIT CHANGE - makes it impossible to baton in stamina softcrit @@ -153,17 +162,21 @@ var/mob/living/carbon/human/H = L if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that playsound(L, 'sound/weapons/genhit.ogg', 50, 1) - return 0 + return FALSE + var/stunpwr = stunforce if(iscyborg(loc)) var/mob/living/silicon/robot/R = loc - if(!R || !R.cell || !R.cell.use(hitcost)) - return 0 + if(!istype(R) || !R.cell || !R.cell.use(hitcost)) + return FALSE else - if(!deductcharge(hitcost)) - return 0 + var/stuncharge = cell.charge + if(!deductcharge(hitcost, FALSE)) + stunpwr *= round(stuncharge/hitcost) + if(stunpwr < stunforce * STUNBATON_CHARGE_LENIENCY) + return FALSE - L.Knockdown(stunforce) - L.adjustStaminaLoss(stunforce*0.1, affected_zone = (istype(user) ? user.zone_selected : BODY_ZONE_CHEST))//CIT CHANGE - makes stunbatons deal extra staminaloss. Todo: make this also deal pain when pain gets implemented. + L.Knockdown(stunpwr) + L.adjustStaminaLoss(stunpwr*0.1, affected_zone = (istype(user) ? user.zone_selected : BODY_ZONE_CHEST))//CIT CHANGE - makes stunbatons deal extra staminaloss. Todo: make this also deal pain when pain gets implemented. L.apply_effect(EFFECT_STUTTER, stunforce) SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK) if(user) @@ -180,11 +193,18 @@ H.forcesay(GLOB.hit_appends) - return 1 + return TRUE + +/obj/item/melee/baton/proc/clowning_around(mob/living/user) + user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \ + "You accidentally hit yourself with [src]!") + user.Knockdown(stunforce*3) + deductcharge(hitcost) /obj/item/melee/baton/emp_act(severity) . = ..() if (!(. & EMP_PROTECT_SELF)) + switch_status(FALSE) deductcharge(1000 / severity) //Makeshift stun baton. Replacement for stun gloves. @@ -202,12 +222,15 @@ hitcost = 2000 throw_hit_chance = 10 slot_flags = ITEM_SLOT_BACK - var/obj/item/assembly/igniter/sparkler = 0 + var/obj/item/assembly/igniter/sparkler /obj/item/melee/baton/cattleprod/Initialize() . = ..() sparkler = new (src) + sparkler.activate_cooldown = 5 /obj/item/melee/baton/cattleprod/baton_stun() - if(sparkler.activate()) - ..() + sparkler?.activate() + . = ..() + +#undef STUNBATON_CHARGE_LENIENCY \ No newline at end of file diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm index 673337518d..0b520d9dc1 100644 --- a/code/game/objects/items/tanks/jetpack.dm +++ b/code/game/objects/items/tanks/jetpack.dm @@ -17,8 +17,7 @@ /obj/item/tank/jetpack/New() ..() if(gas_type) - air_contents.assert_gas(gas_type) - air_contents.gases[gas_type][MOLES] = (6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C) + air_contents.gases[gas_type] = (6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C) ion_trail = new ion_trail.set_up(src) @@ -237,3 +236,4 @@ var/obj/item/clothing/suit/space/hardsuit/C = wear_suit J = C.jetpack return J + diff --git a/code/game/objects/items/tanks/tank_types.dm b/code/game/objects/items/tanks/tank_types.dm index f4eb763847..c16762467e 100644 --- a/code/game/objects/items/tanks/tank_types.dm +++ b/code/game/objects/items/tanks/tank_types.dm @@ -21,8 +21,7 @@ /obj/item/tank/internals/oxygen/New() ..() - air_contents.assert_gas(/datum/gas/oxygen) - air_contents.gases[/datum/gas/oxygen][MOLES] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.gases[/datum/gas/oxygen] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return @@ -49,9 +48,8 @@ /obj/item/tank/internals/anesthetic/New() ..() - air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrous_oxide) - air_contents.gases[/datum/gas/oxygen][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD - air_contents.gases[/datum/gas/nitrous_oxide][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD + air_contents.gases[/datum/gas/oxygen] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD + air_contents.gases[/datum/gas/nitrous_oxide] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD return /* @@ -67,9 +65,8 @@ /obj/item/tank/internals/air/New() ..() - air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrogen) - air_contents.gases[/datum/gas/oxygen][MOLES] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD - air_contents.gases[/datum/gas/nitrogen][MOLES] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD + air_contents.gases[/datum/gas/oxygen] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD + air_contents.gases[/datum/gas/nitrogen] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD return @@ -87,8 +84,7 @@ /obj/item/tank/internals/plasma/New() ..() - air_contents.assert_gas(/datum/gas/plasma) - air_contents.gases[/datum/gas/plasma][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.gases[/datum/gas/plasma] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return /obj/item/tank/internals/plasma/attackby(obj/item/W, mob/user, params) @@ -106,7 +102,7 @@ /obj/item/tank/internals/plasma/full/New() ..() // Plasma asserted in parent - air_contents.gases[/datum/gas/plasma][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return @@ -124,13 +120,12 @@ /obj/item/tank/internals/plasmaman/New() ..() - air_contents.assert_gas(/datum/gas/plasma) - air_contents.gases[/datum/gas/plasma][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.gases[/datum/gas/plasma] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return /obj/item/tank/internals/plasmaman/full/New() ..() // Plasma asserted in parent - air_contents.gases[/datum/gas/plasma][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return @@ -144,7 +139,7 @@ /obj/item/tank/internals/plasmaman/belt/full/New() ..() // Plasma asserted in parent - air_contents.gases[/datum/gas/plasma][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return @@ -166,8 +161,7 @@ /obj/item/tank/internals/emergency_oxygen/New() ..() - air_contents.assert_gas(/datum/gas/oxygen) - air_contents.gases[/datum/gas/oxygen][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.gases[/datum/gas/oxygen] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return /obj/item/tank/internals/emergency_oxygen/engi diff --git a/code/game/objects/items/teleprod.dm b/code/game/objects/items/teleprod.dm index fd9972f427..c514e5e926 100644 --- a/code/game/objects/items/teleprod.dm +++ b/code/game/objects/items/teleprod.dm @@ -6,27 +6,23 @@ item_state = "teleprod" slot_flags = null -/obj/item/melee/baton/cattleprod/teleprod/attack(mob/living/carbon/M, mob/living/carbon/user)//handles making things teleport when hit - ..() - if(status && user.has_trait(TRAIT_CLUMSY) && prob(50)) - user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \ - "You accidentally hit yourself with [src]!") - if(do_teleport(user, get_turf(user), 50))//honk honk - SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK) - user.Knockdown(stunforce*3) - deductcharge(hitcost) - else - SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK) - user.Knockdown(stunforce*3) - deductcharge(hitcost/4) +/obj/item/melee/baton/cattleprod/teleprod/baton_stun(mob/living/carbon/M, mob/living/carbon/user)//handles making things teleport when hit + . = ..() + if(!. || !istype(M) || M.anchored) return else - if(status) - if(!istype(M) && M.anchored) - return . - else - SEND_SIGNAL(M, COMSIG_LIVING_MINOR_SHOCK) - do_teleport(M, get_turf(M), 15) + SEND_SIGNAL(M, COMSIG_LIVING_MINOR_SHOCK) + do_teleport(M, get_turf(M), 15) + +/obj/item/melee/baton/cattleprod/teleprod/clowning_around(mob/living/user) + user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \ + "You accidentally hit yourself with [src]!") + SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK) + user.Knockdown(stunforce*3) + if(do_teleport(user, get_turf(user), 50)) + deductcharge(hitcost) + else + deductcharge(hitcost * 0.25) /obj/item/melee/baton/cattleprod/attackby(obj/item/I, mob/user, params)//handles sticking a crystal onto a stunprod to make a teleprod if(istype(I, /obj/item/stack/ore/bluespace_crystal)) diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index 2e8cb569a6..217b53c928 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -75,11 +75,6 @@ return ..() if(user.zone_selected != BODY_ZONE_PRECISE_EYES && user.zone_selected != BODY_ZONE_HEAD) return ..() - if(user.has_trait(TRAIT_PACIFISM)) - to_chat(user, "You don't want to harm [M]!") - return - if(user.has_trait(TRAIT_CLUMSY) && prob(50)) - M = user return eyestab(M,user) /obj/item/screwdriver/brass @@ -103,7 +98,7 @@ /obj/item/screwdriver/abductor/get_belt_overlay() return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "screwdriver_nuke") - + /obj/item/screwdriver/power name = "hand drill" desc = "A simple powered hand drill. It's fitted with a screw bit." diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 6221416db8..2db2cd08d8 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -241,6 +241,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e //what happens when the obj's health is below integrity_failure level. /obj/proc/obj_break(damage_flag) + SEND_SIGNAL(src, COMSIG_OBJ_BREAK, damage_flag) return //what happens when the obj's integrity reaches zero. diff --git a/code/game/objects/structures/transit_tubes/station.dm b/code/game/objects/structures/transit_tubes/station.dm index 6015cf9f4a..c386726f34 100644 --- a/code/game/objects/structures/transit_tubes/station.dm +++ b/code/game/objects/structures/transit_tubes/station.dm @@ -152,8 +152,8 @@ pod_moving = 0 if(!QDELETED(pod)) var/datum/gas_mixture/floor_mixture = loc.return_air() - floor_mixture.archive() - pod.air_contents.archive() + ARCHIVE_TEMPERATURE(floor_mixture) + ARCHIVE_TEMPERATURE(pod.air_contents) pod.air_contents.share(floor_mixture, 1) //mix the pod's gas mixture with the tile it's on air_update_turf() diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm index 03082cd4ba..ee46538be1 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm @@ -11,9 +11,8 @@ /obj/structure/transit_tube_pod/Initialize() . = ..() - air_contents.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen) - air_contents.gases[/datum/gas/oxygen][MOLES] = MOLES_O2STANDARD - air_contents.gases[/datum/gas/nitrogen][MOLES] = MOLES_N2STANDARD + air_contents.gases[/datum/gas/oxygen] = MOLES_O2STANDARD + air_contents.gases[/datum/gas/nitrogen] = MOLES_N2STANDARD air_contents.temperature = T20C diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index f108a536d2..e441ccd6cc 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -297,15 +297,14 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( continue var/list/S_gases = S.air.gases for(var/id in S_gases) - ASSERT_GAS(id, total) - total_gases[id][MOLES] += S_gases[id][MOLES] + total_gases[id] += S_gases[id] total.temperature += S.air.temperature air.copy_from(total) var/list/air_gases = air.gases for(var/id in air_gases) - air_gases[id][MOLES] /= turf_count //Averages contents of the turfs, ignoring walls and the like + air_gases[id] /= turf_count //Averages contents of the turfs, ignoring walls and the like air.temperature /= turf_count SSair.add_to_active(src) diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index fed922c7b4..cff219c63e 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -314,9 +314,8 @@ /turf/open/rad_act(pulse_strength) . = ..() if (air.gases[/datum/gas/carbon_dioxide] && air.gases[/datum/gas/oxygen]) - pulse_strength = min(pulse_strength,air.gases[/datum/gas/carbon_dioxide][MOLES]*1000,air.gases[/datum/gas/oxygen][MOLES]*2000) //Ensures matter is conserved properly - air.gases[/datum/gas/carbon_dioxide][MOLES]=max(air.gases[/datum/gas/carbon_dioxide][MOLES]-(pulse_strength/1000),0) - air.gases[/datum/gas/oxygen][MOLES]=max(air.gases[/datum/gas/oxygen][MOLES]-(pulse_strength/2000),0) - air.assert_gas(/datum/gas/pluoxium) - air.gases[/datum/gas/pluoxium][MOLES]+=(pulse_strength/4000) - air.garbage_collect() + pulse_strength = min(pulse_strength,air.gases[/datum/gas/carbon_dioxide]*1000,air.gases[/datum/gas/oxygen]*2000) //Ensures matter is conserved properly + air.gases[/datum/gas/carbon_dioxide]=max(air.gases[/datum/gas/carbon_dioxide]-(pulse_strength/1000),0) + air.gases[/datum/gas/oxygen]=max(air.gases[/datum/gas/oxygen]-(pulse_strength/2000),0) + air.gases[/datum/gas/pluoxium]+=(pulse_strength/4000) + GAS_GARBAGE_COLLECT(air.gases) diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm index f40f74787a..8efac31f65 100644 --- a/code/game/turfs/simulated/wall/misc_walls.dm +++ b/code/game/turfs/simulated/wall/misc_walls.dm @@ -77,6 +77,19 @@ return ..() +/turf/closed/wall/clockwork/try_destroy(obj/item/I, mob/user, turf/T) + if(!heated) + return ..() + if(!istype(I, /obj/item/pickaxe/drill/jackhammer)) + return FALSE + to_chat(user, "You begin to smash though [src]...") + if(!do_after(user, 70, TRUE, src)) + return FALSE + I.play_tool_sound(src) + visible_message("[user] smashes through [src] with [I]!", "You hear the grinding of metal.") + dismantle_wall() + return TRUE + /turf/closed/wall/clockwork/ReplaceWithLattice() ..() for(var/obj/structure/lattice/L in src) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 226f2a5456..6fa118ab7f 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -903,6 +903,13 @@ else dat += "Alien" + //Gang + if(jobban_isbanned(M, ROLE_GANG) || isbanned_dept) + dat += "Gang" + else + dat += "Gang" + + //Other Roles (black) dat += "" dat += "" @@ -974,7 +981,7 @@ if("ghostroles") joblist += list(ROLE_PAI, ROLE_POSIBRAIN, ROLE_DRONE , ROLE_DEATHSQUAD, ROLE_LAVALAND, ROLE_SENTIENCE) if("teamantags") - joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN) + joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_GANG) if("convertantags") joblist += list(ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ALIEN) if("otherroles") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 67b33afcc6..57dc357859 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -841,8 +841,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) if(Rad.anchored) if(!Rad.loaded_tank) var/obj/item/tank/internals/plasma/Plasma = new/obj/item/tank/internals/plasma(Rad) - Plasma.air_contents.assert_gas(/datum/gas/plasma) - Plasma.air_contents.gases[/datum/gas/plasma][MOLES] = 70 + Plasma.air_contents.gases[/datum/gas/plasma] = 70 Rad.drainratio = 0 Rad.loaded_tank = Plasma Plasma.forceMove(Rad) diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 39aff33953..b19b2a91d8 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -9,10 +9,9 @@ var/list/lines = list("[AREACOORD(target)]: [env.temperature] K ([env.temperature - T0C] C), [env.return_pressure()] kPa[(burning)?(", burning"):(null)]") for(var/id in env_gases) - var/gas = env_gases[id] - var/moles = gas[MOLES] + var/moles = env_gases[id] if (moles >= 0.00001) - lines += "[gas[GAS_META][META_GAS_NAME]]: [moles] mol" + lines += "[GLOB.meta_gas_names[id]]: [moles] mol" to_chat(usr, lines.Join("\n")) /client/proc/air_status(turf/target) diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm index 237d1a6c00..ca491d8cab 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm @@ -108,20 +108,7 @@ to_chat(loc, "Combat injection is still recharging.") return var/mob/living/carbon/human/M = loc - M.SetSleeping(0) - M.SetUnconscious(0) - M.SetStun(0) - M.SetKnockdown(0) - M.reagents.add_reagent("inaprovaline", 3) - M.reagents.add_reagent("synaptizine", 10) - M.reagents.add_reagent("stimulants", 10) - M.adjustStaminaLoss(-150) - M.stuttering = 0 - M.updatehealth() - M.update_stamina() - M.resting = 0 - M.lying = 0 - M.update_canmove() + M.do_adrenaline(150, FALSE, 0, 0, TRUE, list("inaprovaline" = 3, "synaptizine" = 10, "omnizine" = 10), "You feel a sudden surge of energy!") combat_cooldown = 0 START_PROCESSING(SSobj, src) diff --git a/code/modules/antagonists/changeling/powers/adrenaline.dm b/code/modules/antagonists/changeling/powers/adrenaline.dm index 9dbe706ed2..a9d85d0fed 100644 --- a/code/modules/antagonists/changeling/powers/adrenaline.dm +++ b/code/modules/antagonists/changeling/powers/adrenaline.dm @@ -12,21 +12,5 @@ //Recover from stuns. /obj/effect/proc_holder/changeling/adrenaline/sting_action(mob/living/user) - to_chat(user, "Energy rushes through us.[user.lying ? " We arise." : ""]") - user.SetSleeping(0) - user.SetUnconscious(0) - user.SetStun(0) - user.SetKnockdown(0) - user.reagents.add_reagent("changelingadrenaline", 10) - user.reagents.add_reagent("changelinghaste", 2) //For a really quick burst of speed - user.reagents.add_reagent("inaprovaline", 3) //let's give another chance to dumb fucks who forget to breathe - user.adjustStaminaLoss(-150) - user.stuttering = 0 - user.updatehealth() - user.update_stamina() - user.resting = 0 - user.lying = 0 - user.update_canmove() - + user.do_adrenaline(100, FALSE, 70, 0, TRUE, list("epinephrine" = 3, "changelingmeth" = 10, "mannitol" = 10, "omnizine" = 10, "changelingadrenaline" = 5), "Energy rushes through us.") return TRUE - diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index c8d59f54a8..9451012a04 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -13,7 +13,7 @@ var/give_equipment = FALSE var/datum/team/cult/cult_team - + /datum/antagonist/cult/get_team() return cult_team @@ -115,7 +115,7 @@ cult_team.rise(current) if(cult_team.cult_ascendent) cult_team.ascend(current) - + /datum/antagonist/cult/remove_innate_effects(mob/living/mob_override) . = ..() var/mob/living/current = owner.current @@ -220,7 +220,7 @@ throwing.Remove(current) current.update_action_buttons_icon() current.remove_status_effect(/datum/status_effect/cult_master) - + if(ishuman(current)) var/mob/living/carbon/human/H = current H.eye_color = initial(H.eye_color) @@ -241,7 +241,31 @@ var/reckoning_complete = FALSE var/cult_risen = FALSE var/cult_ascendent = FALSE - + +/datum/team/cult/New() + . = ..() + START_PROCESSING(SSprocessing, src) + +/datum/team/cult/Destroy() + STOP_PROCESSING(SSprocessing, src) + return ..() + +/datum/team/cult/process() + if(SSticker.current_state == GAME_STATE_FINISHED) + return + var/datum/objective/sacrifice/sac_objective = locate() in objectives + if(!sac_objective || sac_objective.check_completion()) + return + var/datum/mind/sacrificial = sac_objective.get_target() + var/mob/living/sac_current = sacrificial.current + if(!sacrificial || !sac_current) //target is gone for good but not sacrified. + sort_sacrifice(TRUE) + return + if(QDELETED(sac_objective.target_current) || sac_objective.target_current != sac_current) //target is now a different mob (monkey, simple mob) + sac_objective.sac_image = sac_current.get_sac_image() + sac_objective.target_current = sac_current + sac_objective.update_explanation_text() + /datum/team/cult/proc/check_size() if(cult_ascendent) return @@ -262,7 +286,7 @@ to_chat(B.current, "The veil weakens as your cult grows, your eyes begin to glow...") addtimer(CALLBACK(src, .proc/rise, B.current), 200) cult_risen = TRUE - + if(ratio > CULT_ASCENDENT && !cult_ascendent) for(var/datum/mind/B in members) if(B.current) @@ -270,8 +294,8 @@ to_chat(B.current, "Your cult is ascendent and the red harvest approaches - you cannot hide your true nature for much longer!!") addtimer(CALLBACK(src, .proc/ascend, B.current), 200) cult_ascendent = TRUE - - + + /datum/team/cult/proc/rise(cultist) if(ishuman(cultist)) var/mob/living/carbon/human/H = cultist @@ -279,7 +303,7 @@ H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK) H.add_trait(CULT_EYES) H.update_body() - + /datum/team/cult/proc/ascend(cultist) if(ishuman(cultist)) var/mob/living/carbon/human/H = cultist @@ -289,48 +313,68 @@ /datum/team/cult/proc/setup_objectives() //SAC OBJECTIVE , todo: move this to objective internals + sort_sacrifice() + //SUMMON OBJECTIVE + var/datum/objective/eldergod/summon_objective = new() + summon_objective.team = src + objectives += summon_objective + +/datum/team/cult/proc/sort_sacrifice(replacement = FALSE) + var/list/target_candidates = list() - var/datum/objective/sacrifice/sac_objective = new - sac_objective.team = src + + var/datum/objective/sacrifice/sac_objective = locate() in GLOB.objectives + if(!sac_objective) + sac_objective = new + sac_objective.team = src for(var/mob/living/carbon/human/player in GLOB.player_list) if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && !is_convertable_to_cult(player) && player.stat != DEAD) target_candidates += player.mind - if(target_candidates.len == 0) + if(!length(target_candidates)) message_admins("Cult Sacrifice: Could not find unconvertible target, checking for convertible target.") for(var/mob/living/carbon/human/player in GLOB.player_list) if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && player.stat != DEAD) target_candidates += player.mind + listclearnulls(target_candidates) - if(LAZYLEN(target_candidates)) - sac_objective.target = pick(target_candidates) - sac_objective.update_explanation_text() + if(!LAZYLEN(target_candidates)) + message_admins("Cult Sacrifice: Could not find unconvertible or convertible target. Proceeding to next stage!") + sac_objective.sacced = TRUE + return + var/datum/mind/new_target = pick(target_candidates) + if(replacement) + for(var/datum/mind/H in members) + if(H.current) + to_chat(H.current, "Nar'Sie murmurs, [sac_objective.target] is beyond your reach. Sacrifice [new_target] instead...") + sac_objective.target = new_target + sac_objective.target_current = new_target.current + sac_objective.update_explanation_text() - var/datum/job/sacjob = SSjob.GetJob(sac_objective.target.assigned_role) - var/datum/preferences/sacface = sac_objective.target.current.client.prefs - var/icon/reshape = get_flat_human_icon(null, sacjob, sacface, list(SOUTH)) - reshape.Shift(SOUTH, 4) - reshape.Shift(EAST, 1) - reshape.Crop(7,4,26,31) - reshape.Crop(-5,-3,26,30) - sac_objective.sac_image = reshape + sac_objective.sac_image = sac_objective.target_current.get_sac_image() + objectives += sac_objective - objectives += sac_objective - else - message_admins("Cult Sacrifice: Could not find unconvertible or convertible target. WELP!") +/mob/proc/get_sac_image() + var/icon/reshape = icon(icon, icon_state, SOUTH) + reshape.Shift(SOUTH, 4) + reshape.Shift(EAST, 1) + reshape.Crop(7,4,26,31) + reshape.Crop(-5,-3,26,30) +/mob/living/carbon/human/get_sac_image() + var/datum/job/sacjob = SSjob.GetJob(mind.assigned_role) + var/datum/preferences/sacface = client.prefs + var/icon/reshape = get_flat_human_icon(null, sacjob, sacface, list(SOUTH)) + reshape.Shift(SOUTH, 4) + reshape.Shift(EAST, 1) + reshape.Crop(7,4,26,31) + reshape.Crop(-5,-3,26,30) - //SUMMON OBJECTIVE - - var/datum/objective/eldergod/summon_objective = new() - summon_objective.team = src - objectives += summon_objective - - /datum/objective/sacrifice var/sacced = FALSE var/sac_image + var/mob/living/target_current /datum/objective/sacrifice/check_completion() return sacced || completed diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 8a3f81fc01..3c11351660 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -246,7 +246,7 @@ structure_check() searches for nearby cultist structures required for the invoca return 0 to_chat(convertee, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \ and something evil takes root.") - to_chat(convertee, "Do you wish to embrace the Geometer of Blood? Click here to stop resisting the truth. Or you could choose to continue resisting...") + to_chat(convertee, "Do you wish to embrace the Geometer of Blood? Click here to become a follower of Nar'sie. Or you could choose to continue resisting and suffer a fate worse than death...") currentconversionman = convertee conversiontimeout = world.time + (10 SECONDS) convertee.Stun(100) diff --git a/code/modules/antagonists/traitor/IAA/internal_affairs.dm b/code/modules/antagonists/traitor/IAA/internal_affairs.dm index 5213c679ca..051bf74705 100644 --- a/code/modules/antagonists/traitor/IAA/internal_affairs.dm +++ b/code/modules/antagonists/traitor/IAA/internal_affairs.dm @@ -130,6 +130,7 @@ if(!owner.current||owner.current.stat==DEAD) return to_chat(owner.current, " Target eliminated: [victim.name]") + LAZYINITLIST(targets_stolen) for(var/objective_ in victim.objectives) if(istype(objective_, /datum/objective/assassinate/internal)) var/datum/objective/assassinate/internal/objective = objective_ diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 67527b2e91..1e7a9b7c15 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -27,6 +27,7 @@ var/datum/wires/connected = null var/next_activate = 0 //When we're next allowed to activate - for spam control + var/activate_cooldown = 3 SECONDS /obj/item/assembly/get_part_rating() return 1 @@ -78,7 +79,7 @@ /obj/item/assembly/proc/activate() if(QDELETED(src) || !secured || (next_activate > world.time)) return FALSE - next_activate = world.time + 30 + next_activate = world.time + activate_cooldown return TRUE diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index d40666babb..8ec93f978f 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -1,203 +1,202 @@ -/obj/item/onetankbomb - name = "bomb" - icon = 'icons/obj/tank.dmi' - item_state = "assembly" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 2 - throw_range = 4 - flags_1 = CONDUCT_1 - var/status = FALSE //0 - not readied //1 - bomb finished with welder - var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device - var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank - -/obj/item/onetankbomb/IsSpecialAssembly() - return TRUE - -/obj/item/onetankbomb/examine(mob/user) - bombtank.examine(user) - -/obj/item/onetankbomb/update_icon() - cut_overlays() - if(bombtank) - icon = bombtank.icon - icon_state = bombtank.icon_state - if(bombassembly) - add_overlay(bombassembly.icon_state) - copy_overlays(bombassembly) - add_overlay("bomb_assembly") - -/obj/item/onetankbomb/wrench_act(mob/living/user, obj/item/I) - to_chat(user, "You disassemble [src]!") - if(bombassembly) - bombassembly.forceMove(drop_location()) - bombassembly.master = null - bombassembly = null - if(bombtank) - bombtank.forceMove(drop_location()) - bombtank.master = null - bombtank = null - qdel(src) - return TRUE - -/obj/item/onetankbomb/welder_act(mob/living/user, obj/item/I) - . = FALSE - if(status) - to_chat(user, "[bombtank] already has a pressure hole!") - return - if(!I.tool_start_check(user, amount=0)) - return - if(I.use_tool(src, user, 0, volume=40)) - status = TRUE - GLOB.bombers += "[key_name(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]" - message_admins("[ADMIN_LOOKUPFLW(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]") - to_chat(user, "A pressure hole has been bored to [bombtank] valve. \The [bombtank] can now be ignited.") - add_fingerprint(user) - return TRUE - - -/obj/item/onetankbomb/analyzer_act(mob/living/user, obj/item/I) - bombtank.analyzer_act(user, I) - -/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly - bombassembly.attack_self(user, TRUE) - add_fingerprint(user) - return - -/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. - audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*") - playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) - sleep(10) - if(QDELETED(src)) - return - if(status) - bombtank.ignite() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file - else - bombtank.release() - -//Assembly / attached device memes - -/obj/item/onetankbomb/Crossed(atom/movable/AM as mob|obj) //for mousetraps - . = ..() - if(bombassembly) - bombassembly.Crossed(AM) - -/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps - if(bombassembly) - bombassembly.on_found(finder) - -/obj/item/onetankbomb/attack_hand() //also for mousetraps - . = ..() - if(.) - return - if(bombassembly) - bombassembly.attack_hand() - -/obj/item/onetankbomb/Move() - . = ..() - if(bombassembly) - bombassembly.setDir(dir) - bombassembly.Move() - -/obj/item/onetankbomb/dropped() - . = ..() - if(bombassembly) - bombassembly.dropped() - - - - -// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- - -//Bomb assembly proc. This turns assembly+tank into a bomb -/obj/item/tank/proc/bomb_assemble(obj/item/assembly_holder/assembly, mob/living/user) - //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - if(isigniter(assembly.a_left) == isigniter(assembly.a_right)) - return - - if((src in user.get_equipped_items(TRUE)) && !user.canUnEquip(src)) - to_chat(user, "[src] is stuck to you!") - return - - if(!user.canUnEquip(assembly)) - to_chat(user, "[assembly] is stuck to your hand!") - return - - var/obj/item/onetankbomb/bomb = new - user.transferItemToLoc(src, bomb) - user.transferItemToLoc(assembly, bomb) - - bomb.bombassembly = assembly //Tell the bomb about its assembly part - assembly.master = bomb //Tell the assembly about its new owner - - bomb.bombtank = src //Same for tank - master = bomb - - forceMove(bomb) - bomb.update_icon() - - user.put_in_hands(bomb) //Equips the bomb if possible, or puts it on the floor. - to_chat(user, "You attach [assembly] to [src].") - return - -/obj/item/tank/proc/ignite() //This happens when a bomb is told to explode - air_contents.assert_gases(/datum/gas/plasma, /datum/gas/oxygen) - var/fuel_moles = air_contents.gases[/datum/gas/plasma][MOLES] + air_contents.gases[/datum/gas/oxygen][MOLES]/6 - air_contents.garbage_collect() - var/datum/gas_mixture/bomb_mixture = air_contents.copy() - var/strength = 1 - - var/turf/ground_zero = get_turf(loc) - - if(master) - qdel(master) - qdel(src) - - if(bomb_mixture.temperature > (T0C + 400)) - strength = (fuel_moles/15) - - if(strength >=1) - explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) - else if(strength >=0.5) - explosion(ground_zero, 0, 1, 2, 4) - else if(strength >=0.2) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(bomb_mixture) - ground_zero.hotspot_expose(1000, 125) - - else if(bomb_mixture.temperature > (T0C + 250)) - strength = (fuel_moles/20) - - if(strength >=1) - explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) - else if (strength >=0.5) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(bomb_mixture) - ground_zero.hotspot_expose(1000, 125) - - else if(bomb_mixture.temperature > (T0C + 100)) - strength = (fuel_moles/25) - - if (strength >=1) - explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) - else - ground_zero.assume_air(bomb_mixture) - ground_zero.hotspot_expose(1000, 125) - - else - ground_zero.assume_air(bomb_mixture) - ground_zero.hotspot_expose(1000, 125) - - ground_zero.air_update_turf() - -/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. - var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) - var/turf/T = get_turf(src) - if(!T) - return - T.assume_air(removed) - air_update_turf() +/obj/item/onetankbomb + name = "bomb" + icon = 'icons/obj/tank.dmi' + item_state = "assembly" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 2 + throw_range = 4 + flags_1 = CONDUCT_1 + var/status = FALSE //0 - not readied //1 - bomb finished with welder + var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device + var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank + +/obj/item/onetankbomb/IsSpecialAssembly() + return TRUE + +/obj/item/onetankbomb/examine(mob/user) + bombtank.examine(user) + +/obj/item/onetankbomb/update_icon() + cut_overlays() + if(bombtank) + icon = bombtank.icon + icon_state = bombtank.icon_state + if(bombassembly) + add_overlay(bombassembly.icon_state) + copy_overlays(bombassembly) + add_overlay("bomb_assembly") + +/obj/item/onetankbomb/wrench_act(mob/living/user, obj/item/I) + to_chat(user, "You disassemble [src]!") + if(bombassembly) + bombassembly.forceMove(drop_location()) + bombassembly.master = null + bombassembly = null + if(bombtank) + bombtank.forceMove(drop_location()) + bombtank.master = null + bombtank = null + qdel(src) + return TRUE + +/obj/item/onetankbomb/welder_act(mob/living/user, obj/item/I) + . = FALSE + if(status) + to_chat(user, "[bombtank] already has a pressure hole!") + return + if(!I.tool_start_check(user, amount=0)) + return + if(I.use_tool(src, user, 0, volume=40)) + status = TRUE + GLOB.bombers += "[key_name(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]" + message_admins("[ADMIN_LOOKUPFLW(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]") + to_chat(user, "A pressure hole has been bored to [bombtank] valve. \The [bombtank] can now be ignited.") + add_fingerprint(user) + return TRUE + + +/obj/item/onetankbomb/analyzer_act(mob/living/user, obj/item/I) + bombtank.analyzer_act(user, I) + +/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly + bombassembly.attack_self(user, TRUE) + add_fingerprint(user) + return + +/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. + audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*") + playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) + sleep(10) + if(QDELETED(src)) + return + if(status) + bombtank.ignite() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file + else + bombtank.release() + +//Assembly / attached device memes + +/obj/item/onetankbomb/Crossed(atom/movable/AM as mob|obj) //for mousetraps + . = ..() + if(bombassembly) + bombassembly.Crossed(AM) + +/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps + if(bombassembly) + bombassembly.on_found(finder) + +/obj/item/onetankbomb/attack_hand() //also for mousetraps + . = ..() + if(.) + return + if(bombassembly) + bombassembly.attack_hand() + +/obj/item/onetankbomb/Move() + . = ..() + if(bombassembly) + bombassembly.setDir(dir) + bombassembly.Move() + +/obj/item/onetankbomb/dropped() + . = ..() + if(bombassembly) + bombassembly.dropped() + + + + +// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- + +//Bomb assembly proc. This turns assembly+tank into a bomb +/obj/item/tank/proc/bomb_assemble(obj/item/assembly_holder/assembly, mob/living/user) + //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it + if(isigniter(assembly.a_left) == isigniter(assembly.a_right)) + return + + if((src in user.get_equipped_items(TRUE)) && !user.canUnEquip(src)) + to_chat(user, "[src] is stuck to you!") + return + + if(!user.canUnEquip(assembly)) + to_chat(user, "[assembly] is stuck to your hand!") + return + + var/obj/item/onetankbomb/bomb = new + user.transferItemToLoc(src, bomb) + user.transferItemToLoc(assembly, bomb) + + bomb.bombassembly = assembly //Tell the bomb about its assembly part + assembly.master = bomb //Tell the assembly about its new owner + + bomb.bombtank = src //Same for tank + master = bomb + + forceMove(bomb) + bomb.update_icon() + + user.put_in_hands(bomb) //Equips the bomb if possible, or puts it on the floor. + to_chat(user, "You attach [assembly] to [src].") + return + +/obj/item/tank/proc/ignite() //This happens when a bomb is told to explode + var/fuel_moles = air_contents.gases[/datum/gas/plasma] + air_contents.gases[/datum/gas/oxygen]/6 + GAS_GARBAGE_COLLECT(air_contents.gases) + var/datum/gas_mixture/bomb_mixture = air_contents.copy() + var/strength = 1 + + var/turf/ground_zero = get_turf(loc) + + if(master) + qdel(master) + qdel(src) + + if(bomb_mixture.temperature > (T0C + 400)) + strength = (fuel_moles/15) + + if(strength >=1) + explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) + else if(strength >=0.5) + explosion(ground_zero, 0, 1, 2, 4) + else if(strength >=0.2) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(bomb_mixture) + ground_zero.hotspot_expose(1000, 125) + + else if(bomb_mixture.temperature > (T0C + 250)) + strength = (fuel_moles/20) + + if(strength >=1) + explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) + else if (strength >=0.5) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(bomb_mixture) + ground_zero.hotspot_expose(1000, 125) + + else if(bomb_mixture.temperature > (T0C + 100)) + strength = (fuel_moles/25) + + if (strength >=1) + explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) + else + ground_zero.assume_air(bomb_mixture) + ground_zero.hotspot_expose(1000, 125) + + else + ground_zero.assume_air(bomb_mixture) + ground_zero.hotspot_expose(1000, 125) + + ground_zero.air_update_turf() + +/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. + var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) + var/turf/T = get_turf(src) + if(!T) + return + T.assume_air(removed) + air_update_turf() diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm index bf5d8efb13..d1da05e42d 100644 --- a/code/modules/atmospherics/environmental/LINDA_fire.dm +++ b/code/modules/atmospherics/environmental/LINDA_fire.dm @@ -14,9 +14,9 @@ if(!air_contents) return 0 - var/oxy = air_contents.gases[/datum/gas/oxygen] ? air_contents.gases[/datum/gas/oxygen][MOLES] : 0 - var/tox = air_contents.gases[/datum/gas/plasma] ? air_contents.gases[/datum/gas/plasma][MOLES] : 0 - var/trit = air_contents.gases[/datum/gas/tritium] ? air_contents.gases[/datum/gas/tritium][MOLES] : 0 + var/oxy = air_contents.gases[/datum/gas/oxygen] + var/tox = air_contents.gases[/datum/gas/plasma] + var/trit = air_contents.gases[/datum/gas/tritium] if(active_hotspot) if(soh) if((tox > 0.5 || trit > 0.5) && oxy > 0.5) @@ -162,7 +162,7 @@ color = list(LERP(0.3, 1, 1-greyscale_fire) * heat_r,0.3 * heat_g * greyscale_fire,0.3 * heat_b * greyscale_fire, 0.59 * heat_r * greyscale_fire,LERP(0.59, 1, 1-greyscale_fire) * heat_g,0.59 * heat_b * greyscale_fire, 0.11 * heat_r * greyscale_fire,0.11 * heat_g * greyscale_fire,LERP(0.11, 1, 1-greyscale_fire) * heat_b, 0,0,0) alpha = heat_a -#define INSUFFICIENT(path) (!location.air.gases[path] || location.air.gases[path][MOLES] < 0.5) +#define INSUFFICIENT(path) (location.air.gases[path] < 0.5) /obj/effect/hotspot/process() if(just_spawned) just_spawned = FALSE @@ -184,7 +184,7 @@ return //Not enough to burn - if(((!location.air.gases[/datum/gas/plasma] || location.air.gases[/datum/gas/plasma][MOLES] < 0.5) && (!location.air.gases[/datum/gas/tritium] || location.air.gases[/datum/gas/tritium][MOLES] < 0.5)) || location.air.gases[/datum/gas/oxygen][MOLES] < 0.5) + if((location.air.gases[/datum/gas/plasma] < 0.5 && location.air.gases[/datum/gas/tritium] < 0.5) || location.air.gases[/datum/gas/oxygen] < 0.5) qdel(src) return diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm index 45bb47ab58..ea555c9489 100644 --- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm +++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm @@ -88,7 +88,7 @@ temperature_archived = temperature /turf/open/archive() - air.archive() + ARCHIVE_TEMPERATURE(air) archived_cycle = SSair.times_fired temperature_archived = temperature @@ -121,10 +121,9 @@ if (nonoverlaying_gases[id]) continue var/gas = gases[id] - var/gas_meta = gas[GAS_META] - var/gas_overlay = gas_meta[META_GAS_OVERLAY] - if(gas_overlay && gas[MOLES] > gas_meta[META_GAS_MOLES_VISIBLE]) - . += gas_overlay[min(FACTOR_GAS_VISIBLE_MAX, CEILING(gas[MOLES] / MOLES_GAS_VISIBLE_STEP, 1))] + var/gas_overlay = GLOB.meta_gas_overlays[id] + if(gas_overlay && gas > GLOB.meta_gas_visibility[id]) + . += gas_overlay[min(FACTOR_GAS_VISIBLE_MAX, CEILING(gas / MOLES_GAS_VISIBLE_STEP, 1))] /proc/typecache_of_gases_with_no_overlays() . = list() @@ -215,7 +214,7 @@ if (planet_atmos) //share our air with the "atmosphere" "above" the turf var/datum/gas_mixture/G = new G.copy_from_turf(src) - G.archive() + ARCHIVE_TEMPERATURE(G) if(our_air.compare(G)) if(!our_excited_group) var/datum/excited_group/EG = new @@ -232,6 +231,11 @@ atmos_cooldown = cached_atmos_cooldown +/turf/open/space/process_cell(fire_count) //dumb hack to prevent space pollution + . = ..() + var/datum/gas_mixture/immutable/I = space_gas + I.after_process_cell() + /turf/proc/process_cell_reaction() SSair.remove_from_react_queue(src) @@ -327,7 +331,7 @@ A.merge(T.air) for(var/id in A_gases) - A_gases[id][MOLES] /= turflen + A_gases[id] /= turflen for(var/t in turf_list) var/turf/open/T = t diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index 0304946111..9858db2abb 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -5,82 +5,38 @@ What are the archived variables for? */ #define MINIMUM_HEAT_CAPACITY 0.0003 #define MINIMUM_MOLE_COUNT 0.01 -#define QUANTIZE(variable) (round(variable,0.0000001))/*I feel the need to document what happens here. Basically this is used to catch most rounding errors, however it's previous value made it so that - once gases got hot enough, most procedures wouldnt occur due to the fact that the mole counts would get rounded away. Thus, we lowered it a few orders of magnititude */ -GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm -GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) - -/proc/init_gaslist_cache() - . = list() - for(var/id in GLOB.meta_gas_info) - var/list/cached_gas = new(3) - - .[id] = cached_gas - - cached_gas[MOLES] = 0 - cached_gas[ARCHIVE] = 0 - cached_gas[GAS_META] = GLOB.meta_gas_info[id] +//Unomos - global list inits for all of the meta gas lists. +//This setup allows procs to only look at one list instead of trying to dig around in lists-within-lists +GLOBAL_LIST_INIT(meta_gas_specific_heats, meta_gas_heat_list()) +GLOBAL_LIST_INIT(meta_gas_names, meta_gas_name_list()) +GLOBAL_LIST_INIT(meta_gas_visibility, meta_gas_visibility_list()) +GLOBAL_LIST_INIT(meta_gas_overlays, meta_gas_overlay_list()) +GLOBAL_LIST_INIT(meta_gas_dangers, meta_gas_danger_list()) +GLOBAL_LIST_INIT(meta_gas_ids, meta_gas_id_list()) +GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list()) /datum/gas_mixture - var/list/gases + var/list/gases = list() var/temperature = 0 //kelvins var/tmp/temperature_archived = 0 var/volume = CELL_VOLUME //liters var/last_share = 0 - var/list/reaction_results + var/list/reaction_results = list() var/list/analyzer_results //used for analyzer feedback - not initialized until its used var/gc_share = FALSE // Whether to call garbage_collect() on the sharer during shares, used for immutable mixtures /datum/gas_mixture/New(volume) - gases = new if (!isnull(volume)) src.volume = volume - reaction_results = new - -//listmos procs -//use the macros in performance intensive areas. for their definitions, refer to code/__DEFINES/atmospherics.dm - - //assert_gas(gas_id) - used to guarantee that the gas list for this id exists in gas_mixture.gases. - //Must be used before adding to a gas. May be used before reading from a gas. -/datum/gas_mixture/proc/assert_gas(gas_id) - ASSERT_GAS(gas_id, src) - - //assert_gases(args) - shorthand for calling ASSERT_GAS() once for each gas type. -/datum/gas_mixture/proc/assert_gases() - for(var/id in args) - ASSERT_GAS(id, src) - - //add_gas(gas_id) - similar to assert_gas(), but does not check for an existing - //gas list for this id. This can clobber existing gases. - //Used instead of assert_gas() when you know the gas does not exist. Faster than assert_gas(). -/datum/gas_mixture/proc/add_gas(gas_id) - ADD_GAS(gas_id, gases) - - //add_gases(args) - shorthand for calling add_gas() once for each gas_type. -/datum/gas_mixture/proc/add_gases() - var/cached_gases = gases - for(var/id in args) - ADD_GAS(id, cached_gases) - - //garbage_collect() - removes any gas list which is empty. - //If called with a list as an argument, only removes gas lists with IDs from that list. - //Must be used after subtracting from a gas. Must be used after assert_gas() - //if assert_gas() was called only to read from the gas. - //By removing empty gases, processing speed is increased. -/datum/gas_mixture/proc/garbage_collect(list/tocheck) - var/list/cached_gases = gases - for(var/id in (tocheck || cached_gases)) - if(QUANTIZE(cached_gases[id][MOLES]) <= 0 && QUANTIZE(cached_gases[id][ARCHIVE]) <= 0) - cached_gases -= id //PV = nRT -/datum/gas_mixture/proc/heat_capacity(data = MOLES) //joules per kelvin +/datum/gas_mixture/proc/heat_capacity() //joules per kelvin var/list/cached_gases = gases + var/list/cached_gasheats = GLOB.meta_gas_specific_heats . = 0 for(var/id in cached_gases) - var/gas_data = cached_gases[id] - . += gas_data[data] * gas_data[GAS_META][META_GAS_SPECIFIC_HEAT] + . += cached_gases[id] * cached_gasheats[id] /datum/gas_mixture/turf/heat_capacity() . = ..() @@ -108,10 +64,6 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) /datum/gas_mixture/proc/thermal_energy() //joules return THERMAL_ENERGY(src) //see code/__DEFINES/atmospherics.dm; use the define in performance critical areas -/datum/gas_mixture/proc/archive() - //Update archived versions of variables - //Returns: 1 in all cases - /datum/gas_mixture/proc/merge(datum/gas_mixture/giver) //Merges all air from giver into self. Deletes giver. //Returns: 1 if we are mutable, 0 otherwise @@ -156,14 +108,6 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) //Performs various reactions such as combustion or fusion (LOL) //Returns: 1 if any reaction took place; 0 otherwise -/datum/gas_mixture/archive() - var/list/cached_gases = gases - - temperature_archived = temperature - for(var/id in cached_gases) - cached_gases[id][ARCHIVE] = cached_gases[id][MOLES] - - return 1 /datum/gas_mixture/merge(datum/gas_mixture/giver) if(!giver) @@ -181,8 +125,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) var/list/giver_gases = giver.gases //gas transfer for(var/giver_id in giver_gases) - ASSERT_GAS(giver_id, src) - cached_gases[giver_id][MOLES] += giver_gases[giver_id][MOLES] + cached_gases[giver_id] += giver_gases[giver_id] return 1 @@ -198,10 +141,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) removed.temperature = temperature for(var/id in cached_gases) - ADD_GAS(id, removed.gases) - removed_gases[id][MOLES] = QUANTIZE((cached_gases[id][MOLES] / sum) * amount) - cached_gases[id][MOLES] -= removed_gases[id][MOLES] - garbage_collect() + removed_gases[id] = QUANTIZE((cached_gases[id] / sum) * amount) + cached_gases[id] -= removed_gases[id] + GAS_GARBAGE_COLLECT(gases) return removed @@ -216,11 +158,10 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) removed.temperature = temperature for(var/id in cached_gases) - ADD_GAS(id, removed.gases) - removed_gases[id][MOLES] = QUANTIZE(cached_gases[id][MOLES] * ratio) - cached_gases[id][MOLES] -= removed_gases[id][MOLES] + removed_gases[id] = QUANTIZE(cached_gases[id] * ratio) + cached_gases[id] -= removed_gases[id] - garbage_collect() + GAS_GARBAGE_COLLECT(gases) return removed @@ -231,8 +172,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) copy.temperature = temperature for(var/id in cached_gases) - ADD_GAS(id, copy.gases) - copy_gases[id][MOLES] = cached_gases[id][MOLES] + copy_gases[id] = cached_gases[id] return copy @@ -243,8 +183,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) temperature = sample.temperature for(var/id in sample_gases) - ASSERT_GAS(id,src) - cached_gases[id][MOLES] = sample_gases[id][MOLES] + cached_gases[id] = sample_gases[id] //remove all gases not in the sample cached_gases &= sample_gases @@ -272,8 +211,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) var/path = id if(!ispath(path)) path = gas_id2path(path) //a lot of these strings can't have embedded expressions (especially for mappers), so support for IDs needs to stick around - ADD_GAS(path, gases) - gases[path][MOLES] = text2num(gas[id]) + gases[path] = text2num(gas[id]) return 1 /datum/gas_mixture/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4) @@ -296,26 +234,25 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) var/moved_moles = 0 var/abs_moved_moles = 0 + //we're gonna define these vars outside of this for loop because as it turns out, var declaration is pricy + var/delta + var/gas_heat_capacity + //and also cache this shit rq because that results in sanic speed for reasons byond explanation + var/list/cached_gasheats = GLOB.meta_gas_specific_heats //GAS TRANSFER - for(var/id in sharer_gases - cached_gases) // create gases not in our cache - ADD_GAS(id, gases) - for(var/id in cached_gases) // transfer gases - ASSERT_GAS(id, sharer) + for(var/id in cached_gases | sharer_gases) // transfer gases - var/gas = cached_gases[id] - var/sharergas = sharer_gases[id] - - var/delta = QUANTIZE(gas[ARCHIVE] - sharergas[ARCHIVE])/(atmos_adjacent_turfs+1) //the amount of gas that gets moved between the mixtures + delta = QUANTIZE(cached_gases[id] - sharer_gases[id])/(atmos_adjacent_turfs+1) //the amount of gas that gets moved between the mixtures if(delta && abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/gas_heat_capacity = delta * gas[GAS_META][META_GAS_SPECIFIC_HEAT] + gas_heat_capacity = delta * cached_gasheats[id] if(delta > 0) heat_capacity_self_to_sharer += gas_heat_capacity else heat_capacity_sharer_to_self -= gas_heat_capacity //subtract here instead of adding the absolute value because we know that delta is negative. - gas[MOLES] -= delta - sharergas[MOLES] += delta + cached_gases[id] -= delta + sharer_gases[id] += delta moved_moles += delta abs_moved_moles += abs(delta) @@ -338,11 +275,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.1) // <10% change in sharer heat capacity temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT) - if(length(cached_gases ^ sharer_gases)) //if all gases were present in both mixtures, we know that no gases are 0 - garbage_collect(cached_gases - sharer_gases) //any gases the sharer had, we are guaranteed to have. gases that it didn't have we are not. - sharer.garbage_collect(sharer_gases - cached_gases) //the reverse is equally true if (initial(sharer.gc_share)) - sharer.garbage_collect() + GAS_GARBAGE_COLLECT(sharer.gases) if(temperature_delta > MINIMUM_TEMPERATURE_TO_MOVE || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) var/our_moles TOTAL_MOLES(cached_gases,our_moles) @@ -356,8 +290,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) sharer_temperature = sharer.temperature_archived var/temperature_delta = temperature_archived - sharer_temperature if(abs(temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity(ARCHIVE) - sharer_heat_capacity = sharer_heat_capacity || sharer.heat_capacity(ARCHIVE) + var/self_heat_capacity = heat_capacity() + sharer_heat_capacity = sharer_heat_capacity || sharer.heat_capacity() if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) var/heat = conduction_coefficient*temperature_delta* \ @@ -376,9 +310,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) for(var/id in cached_gases | sample_gases) // compare gases from either mixture var/gas_moles = cached_gases[id] - gas_moles = gas_moles ? gas_moles[MOLES] : 0 var/sample_moles = sample_gases[id] - sample_moles = sample_moles ? sample_moles[MOLES] : 0 var/delta = abs(gas_moles - sample_moles) if(delta > MINIMUM_MOLES_DELTA_TO_MOVE && \ delta > gas_moles * MINIMUM_AIR_RATIO_TO_MOVE) @@ -425,7 +357,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) for(var/id in min_reqs) if (id == "TEMP" || id == "ENER") continue - if(!cached_gases[id] || cached_gases[id][MOLES] < min_reqs[id]) + if(cached_gases[id] < min_reqs[id]) continue reaction_loop //at this point, all minimum requirements for the reaction are satisfied. @@ -449,7 +381,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) if (. & STOP_REACTIONS) break if(.) - garbage_collect() + GAS_GARBAGE_COLLECT(gases) if(temperature < TCMB) //just for safety temperature = TCMB diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm index 9dcf8c3145..d628826b01 100644 --- a/code/modules/atmospherics/gasmixtures/gas_types.dm +++ b/code/modules/atmospherics/gasmixtures/gas_types.dm @@ -1,33 +1,66 @@ GLOBAL_LIST_INIT(hardcoded_gases, list(/datum/gas/oxygen, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, /datum/gas/plasma)) //the main four gases, which were at one time hardcoded GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, /datum/gas/pluoxium, /datum/gas/stimulum, /datum/gas/nitryl))) //unable to react amongst themselves -/proc/meta_gas_list() - . = subtypesof(/datum/gas) - for(var/gas_path in .) - var/list/gas_info = new(7) - var/datum/gas/gas = gas_path - - gas_info[META_GAS_SPECIFIC_HEAT] = initial(gas.specific_heat) - gas_info[META_GAS_NAME] = initial(gas.name) - gas_info[META_GAS_MOLES_VISIBLE] = initial(gas.moles_visible) - if(initial(gas.moles_visible) != null) - gas_info[META_GAS_OVERLAY] = new /list(FACTOR_GAS_VISIBLE_MAX) - for(var/i in 1 to FACTOR_GAS_VISIBLE_MAX) - gas_info[META_GAS_OVERLAY][i] = new /obj/effect/overlay/gas(initial(gas.gas_overlay), i * 255 / FACTOR_GAS_VISIBLE_MAX) - gas_info[META_GAS_FUSION_POWER] = initial(gas.fusion_power) - gas_info[META_GAS_DANGER] = initial(gas.dangerous) - gas_info[META_GAS_ID] = initial(gas.id) - .[gas_path] = gas_info - /proc/gas_id2path(id) - var/list/meta_gas = GLOB.meta_gas_info + var/list/meta_gas = GLOB.meta_gas_ids if(id in meta_gas) return id for(var/path in meta_gas) - if(meta_gas[path][META_GAS_ID] == id) + if(meta_gas[path] == id) return path return "" +//Unomos - oh god oh fuck oh shit oh lord have mercy this is messy as fuck oh god +//my addiction to seeing better performance numbers isn't healthy, kids +//you see this shit, children? +//i am not a good idol. don't take after me. +//this is literally worse than my alcohol addiction +/proc/meta_gas_heat_list() + . = subtypesof(/datum/gas) + for(var/gas_path in .) + var/datum/gas/gas = gas_path + .[gas_path] = initial(gas.specific_heat) + +/proc/meta_gas_name_list() + . = subtypesof(/datum/gas) + for(var/gas_path in .) + var/datum/gas/gas = gas_path + .[gas_path] = initial(gas.name) + +/proc/meta_gas_visibility_list() + . = subtypesof(/datum/gas) + for(var/gas_path in .) + var/datum/gas/gas = gas_path + .[gas_path] = initial(gas.moles_visible) + +/proc/meta_gas_overlay_list() + . = subtypesof(/datum/gas) + for(var/gas_path in .) + var/datum/gas/gas = gas_path + .[gas_path] = 0 //gotta make sure if(GLOB.meta_gas_overlays[gaspath]) doesn't break + if(initial(gas.moles_visible) != null) + .[gas_path] = new /list(FACTOR_GAS_VISIBLE_MAX) + for(var/i in 1 to FACTOR_GAS_VISIBLE_MAX) + .[gas_path][i] = new /obj/effect/overlay/gas(initial(gas.gas_overlay), i * 255 / FACTOR_GAS_VISIBLE_MAX) + +/proc/meta_gas_danger_list() + . = subtypesof(/datum/gas) + for(var/gas_path in .) + var/datum/gas/gas = gas_path + .[gas_path] = initial(gas.dangerous) + +/proc/meta_gas_id_list() + . = subtypesof(/datum/gas) + for(var/gas_path in .) + var/datum/gas/gas = gas_path + .[gas_path] = initial(gas.id) + +/proc/meta_gas_fusion_list() + . = subtypesof(/datum/gas) + for(var/gas_path in .) + var/datum/gas/gas = gas_path + .[gas_path] = initial(gas.fusion_power) + /*||||||||||||||/----------\||||||||||||||*\ ||||||||||||||||[GAS DATUMS]|||||||||||||||| ||||||||||||||||\__________/|||||||||||||||| diff --git a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm index 08f64b5f4a..53f7ede3e6 100644 --- a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm +++ b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm @@ -7,22 +7,18 @@ /datum/gas_mixture/immutable/New() ..() - garbage_collect() - -/datum/gas_mixture/immutable/garbage_collect() temperature = initial_temperature temperature_archived = initial_temperature gases.Cut() -/datum/gas_mixture/immutable/archive() - return 1 //nothing changes, so we do nothing and the archive is successful - /datum/gas_mixture/immutable/merge() return 0 //we're immutable. /datum/gas_mixture/immutable/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4) . = ..(sharer, 0) - garbage_collect() + temperature = initial_temperature + temperature_archived = initial_temperature + gases.Cut() /datum/gas_mixture/immutable/react() return 0 //we're immutable. @@ -43,6 +39,10 @@ . = ..() temperature = initial_temperature +/datum/gas_mixture/immutable/proc/after_process_cell() + temperature = initial_temperature + temperature_archived = initial_temperature + gases.Cut() //used by space tiles /datum/gas_mixture/immutable/space @@ -62,10 +62,13 @@ /datum/gas_mixture/immutable/cloner initial_temperature = T20C -/datum/gas_mixture/immutable/cloner/garbage_collect() +/datum/gas_mixture/immutable/cloner/New() ..() - ADD_GAS(/datum/gas/nitrogen, gases) - gases[/datum/gas/nitrogen][MOLES] = MOLES_O2STANDARD + MOLES_N2STANDARD + gases[/datum/gas/nitrogen] = MOLES_O2STANDARD + MOLES_N2STANDARD + +/datum/gas_mixture/immutable/cloner/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4) + . = ..(sharer, 0) + gases[/datum/gas/nitrogen] = MOLES_O2STANDARD + MOLES_N2STANDARD /datum/gas_mixture/immutable/cloner/heat_capacity() return (MOLES_O2STANDARD + MOLES_N2STANDARD)*20 //specific heat of nitrogen is 20 diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm index 84ad1e9258..46ad4f26b7 100644 --- a/code/modules/atmospherics/gasmixtures/reactions.dm +++ b/code/modules/atmospherics/gasmixtures/reactions.dm @@ -60,7 +60,7 @@ if(location && location.freon_gas_act()) . = REACTING else if(location && location.water_vapor_gas_act()) - air.gases[/datum/gas/water_vapor][MOLES] -= MOLES_GAS_VISIBLE + air.gases[/datum/gas/water_vapor] -= MOLES_GAS_VISIBLE . = REACTING //tritium combustion: combustion of oxygen and tritium (treated as hydrocarbons). creates hotspots. exothermic @@ -86,21 +86,20 @@ var/turf/open/location = isturf(holder) ? holder : null var/burned_fuel = 0 - if(cached_gases[/datum/gas/oxygen][MOLES] < cached_gases[/datum/gas/tritium][MOLES]) - burned_fuel = cached_gases[/datum/gas/oxygen][MOLES]/TRITIUM_BURN_OXY_FACTOR - cached_gases[/datum/gas/tritium][MOLES] -= burned_fuel + if(cached_gases[/datum/gas/oxygen] < cached_gases[/datum/gas/tritium]) + burned_fuel = cached_gases[/datum/gas/oxygen]/TRITIUM_BURN_OXY_FACTOR + cached_gases[/datum/gas/tritium] -= burned_fuel else - burned_fuel = cached_gases[/datum/gas/tritium][MOLES]*TRITIUM_BURN_TRIT_FACTOR - cached_gases[/datum/gas/tritium][MOLES] -= cached_gases[/datum/gas/tritium][MOLES]/TRITIUM_BURN_TRIT_FACTOR - cached_gases[/datum/gas/oxygen][MOLES] -= cached_gases[/datum/gas/tritium][MOLES] + burned_fuel = cached_gases[/datum/gas/tritium]*TRITIUM_BURN_TRIT_FACTOR + cached_gases[/datum/gas/tritium] -= cached_gases[/datum/gas/tritium]/TRITIUM_BURN_TRIT_FACTOR + cached_gases[/datum/gas/oxygen] -= cached_gases[/datum/gas/tritium] if(burned_fuel) energy_released += FIRE_HYDROGEN_ENERGY_RELEASED * burned_fuel if(location && prob(10) && burned_fuel > TRITIUM_MINIMUM_RADIATION_ENERGY) //woah there let's not crash the server radiation_pulse(location, energy_released/TRITIUM_BURN_RADIOACTIVITY_FACTOR) - ASSERT_GAS(/datum/gas/water_vapor, air) //oxygen+more-or-less hydrogen=H2O - cached_gases[/datum/gas/water_vapor][MOLES] += burned_fuel/TRITIUM_BURN_OXY_FACTOR + cached_gases[/datum/gas/water_vapor] += burned_fuel/TRITIUM_BURN_OXY_FACTOR cached_results["fire"] += burned_fuel @@ -157,23 +156,21 @@ temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE) if(temperature_scale > 0) oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - temperature_scale - if(cached_gases[/datum/gas/oxygen][MOLES] / cached_gases[/datum/gas/plasma][MOLES] > SUPER_SATURATION_THRESHOLD) //supersaturation. Form Tritium. + if(cached_gases[/datum/gas/oxygen] / cached_gases[/datum/gas/plasma] > SUPER_SATURATION_THRESHOLD) //supersaturation. Form Tritium. super_saturation = TRUE - if(cached_gases[/datum/gas/oxygen][MOLES] > cached_gases[/datum/gas/plasma][MOLES]*PLASMA_OXYGEN_FULLBURN) - plasma_burn_rate = (cached_gases[/datum/gas/plasma][MOLES]*temperature_scale)/PLASMA_BURN_RATE_DELTA + if(cached_gases[/datum/gas/oxygen] > cached_gases[/datum/gas/plasma]*PLASMA_OXYGEN_FULLBURN) + plasma_burn_rate = (cached_gases[/datum/gas/plasma]*temperature_scale)/PLASMA_BURN_RATE_DELTA else - plasma_burn_rate = (temperature_scale*(cached_gases[/datum/gas/oxygen][MOLES]/PLASMA_OXYGEN_FULLBURN))/PLASMA_BURN_RATE_DELTA + plasma_burn_rate = (temperature_scale*(cached_gases[/datum/gas/oxygen]/PLASMA_OXYGEN_FULLBURN))/PLASMA_BURN_RATE_DELTA if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY) - plasma_burn_rate = min(plasma_burn_rate,cached_gases[/datum/gas/plasma][MOLES],cached_gases[/datum/gas/oxygen][MOLES]/oxygen_burn_rate) //Ensures matter is conserved properly - cached_gases[/datum/gas/plasma][MOLES] = QUANTIZE(cached_gases[/datum/gas/plasma][MOLES] - plasma_burn_rate) - cached_gases[/datum/gas/oxygen][MOLES] = QUANTIZE(cached_gases[/datum/gas/oxygen][MOLES] - (plasma_burn_rate * oxygen_burn_rate)) + plasma_burn_rate = min(plasma_burn_rate,cached_gases[/datum/gas/plasma],cached_gases[/datum/gas/oxygen]/oxygen_burn_rate) //Ensures matter is conserved properly + cached_gases[/datum/gas/plasma] = QUANTIZE(cached_gases[/datum/gas/plasma] - plasma_burn_rate) + cached_gases[/datum/gas/oxygen] = QUANTIZE(cached_gases[/datum/gas/oxygen] - (plasma_burn_rate * oxygen_burn_rate)) if (super_saturation) - ASSERT_GAS(/datum/gas/tritium,air) - cached_gases[/datum/gas/tritium][MOLES] += plasma_burn_rate + cached_gases[/datum/gas/tritium] += plasma_burn_rate else - ASSERT_GAS(/datum/gas/carbon_dioxide,air) - cached_gases[/datum/gas/carbon_dioxide][MOLES] += plasma_burn_rate + cached_gases[/datum/gas/carbon_dioxide] += plasma_burn_rate energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate) @@ -231,21 +228,21 @@ var/old_heat_capacity = air.heat_capacity() var/reaction_energy = 0 - var/mediation = FUSION_MEDIATION_FACTOR*(air.heat_capacity()-(cached_gases[/datum/gas/plasma][MOLES]*cached_gases[/datum/gas/plasma][GAS_META][META_GAS_SPECIFIC_HEAT]))/(air.total_moles()-cached_gases[/datum/gas/plasma][MOLES]) //This is the average specific heat of the mixture,not including plasma. + var/mediation = FUSION_MEDIATION_FACTOR*(air.heat_capacity()-(cached_gases[/datum/gas/plasma]*GLOB.meta_gas_specific_heats[/datum/gas/plasma]))/(air.total_moles()-cached_gases[/datum/gas/plasma]) //This is the average specific heat of the mixture,not including plasma. - var/gases_fused = air.total_moles() - cached_gases[/datum/gas/plasma][MOLES] - var/plasma_differential = (cached_gases[/datum/gas/plasma][MOLES] - gases_fused) / air.total_moles() + var/gases_fused = air.total_moles() - cached_gases[/datum/gas/plasma] + var/plasma_differential = (cached_gases[/datum/gas/plasma] - gases_fused) / air.total_moles() var/reaction_efficiency = FUSION_EFFICIENCY_BASE ** -((plasma_differential ** 2) / FUSION_EFFICIENCY_DIVISOR) //https://www.desmos.com/calculator/6jjx3vdrvx var/gas_power = 0 for (var/gas_id in cached_gases) - gas_power += reaction_efficiency * (cached_gases[gas_id][GAS_META][META_GAS_FUSION_POWER]*cached_gases[gas_id][MOLES]) + gas_power += reaction_efficiency * (GLOB.meta_gas_fusions[gas_id]*cached_gases[gas_id]) var/power_ratio = gas_power/mediation cached_scan_results[id] = power_ratio //used for analyzer feedback for (var/gas_id in cached_gases) //and now we fuse - cached_gases[gas_id][MOLES] = 0 + cached_gases[gas_id] = 0 var/radiation_power = (FUSION_RADIATION_FACTOR * power_ratio) / (power_ratio + FUSION_RADIATION_CONSTANT) //https://www.desmos.com/calculator/4i1f296phl var/zap_power = ((FUSION_ZAP_POWER_ASYMPTOTE * power_ratio) / (power_ratio + FUSION_ZAP_POWER_CONSTANT)) + FUSION_ZAP_POWER_BASE //https://www.desmos.com/calculator/n0zkdpxnrr @@ -255,33 +252,30 @@ if (power_ratio > FUSION_SUPER_TIER_THRESHOLD) //power ratio 50+: SUPER TIER. The gases become so energized that they fuse into a ton of tritium, which is pretty nice! Until you consider the fact that everything just exploded, the canister is probably going to break and you're irradiated. reaction_energy += gases_fused * FUSION_RELEASE_ENERGY_SUPER * (power_ratio / FUSION_ENERGY_DIVISOR_SUPER) - cached_gases[/datum/gas/tritium][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_TRITIUM //60% of the gas is converted to energy, 40% to trit + cached_gases[/datum/gas/tritium] += gases_fused * FUSION_GAS_CREATION_FACTOR_TRITIUM //60% of the gas is converted to energy, 40% to trit fusion_prepare_to_die_edition_rng = 100 //Wait a minute.. do_explosion = TRUE zap_range = FUSION_ZAP_RANGE_SUPER else if (power_ratio > FUSION_HIGH_TIER_THRESHOLD) //power ratio 20-50; High tier. The reaction is so energized that it fuses into a small amount of stimulum, and some pluoxium. Very dangerous, but super cool and super useful. reaction_energy += gases_fused * FUSION_RELEASE_ENERGY_HIGH * (power_ratio / FUSION_ENERGY_DIVISOR_HIGH) - air.assert_gases(/datum/gas/stimulum, /datum/gas/pluoxium) - cached_gases[/datum/gas/stimulum][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_STIM //40% of the gas is converted to energy, 60% to stim and pluox - cached_gases[/datum/gas/pluoxium][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_PLUOX + cached_gases[/datum/gas/stimulum] += gases_fused * FUSION_GAS_CREATION_FACTOR_STIM //40% of the gas is converted to energy, 60% to stim and pluox + cached_gases[/datum/gas/pluoxium] += gases_fused * FUSION_GAS_CREATION_FACTOR_PLUOX fusion_prepare_to_die_edition_rng = power_ratio //Now we're getting into dangerous territory do_explosion = TRUE zap_range = FUSION_ZAP_RANGE_HIGH else if (power_ratio > FUSION_MID_TIER_THRESHOLD) //power_ratio 5 to 20; Mediation is overpowered, fusion reaction starts to break down. reaction_energy += gases_fused * FUSION_RELEASE_ENERGY_MID * (power_ratio / FUSION_ENERGY_DIVISOR_MID) - air.assert_gases(/datum/gas/nitryl,/datum/gas/nitrous_oxide) - cached_gases[/datum/gas/nitryl][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_NITRYL //20% of the gas is converted to energy, 80% to nitryl and N2O - cached_gases[/datum/gas/nitrous_oxide][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_N2O + cached_gases[/datum/gas/nitryl] += gases_fused * FUSION_GAS_CREATION_FACTOR_NITRYL //20% of the gas is converted to energy, 80% to nitryl and N2O + cached_gases[/datum/gas/nitrous_oxide] += gases_fused * FUSION_GAS_CREATION_FACTOR_N2O fusion_prepare_to_die_edition_rng = power_ratio * FUSION_MID_TIER_RAD_PROB_FACTOR //Still unlikely, but don't stand next to the reaction unprotected zap_range = FUSION_ZAP_RANGE_MID else //power ratio 0 to 5; Gas power is overpowered. Fusion isn't nearly as powerful. reaction_energy += gases_fused * FUSION_RELEASE_ENERGY_LOW * (power_ratio / FUSION_ENERGY_DIVISOR_LOW) - air.assert_gases(/datum/gas/bz, /datum/gas/carbon_dioxide) - cached_gases[/datum/gas/bz][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_BZ //10% of the gas is converted to energy, 90% to BZ and CO2 - cached_gases[/datum/gas/carbon_dioxide][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_CO2 + cached_gases[/datum/gas/bz] += gases_fused * FUSION_GAS_CREATION_FACTOR_BZ //10% of the gas is converted to energy, 90% to BZ and CO2 + cached_gases[/datum/gas/carbon_dioxide] += gases_fused * FUSION_GAS_CREATION_FACTOR_CO2 fusion_prepare_to_die_edition_rng = power_ratio * FUSION_LOW_TIER_RAD_PROB_FACTOR //Low, but still something to look out for zap_range = FUSION_ZAP_RANGE_LOW @@ -322,14 +316,13 @@ var/temperature = air.temperature var/old_heat_capacity = air.heat_capacity() - var/heat_efficency = min(temperature/(FIRE_MINIMUM_TEMPERATURE_TO_EXIST*100),cached_gases[/datum/gas/oxygen][MOLES],cached_gases[/datum/gas/nitrogen][MOLES]) + var/heat_efficency = min(temperature/(FIRE_MINIMUM_TEMPERATURE_TO_EXIST*100),cached_gases[/datum/gas/oxygen],cached_gases[/datum/gas/nitrogen]) var/energy_used = heat_efficency*NITRYL_FORMATION_ENERGY - ASSERT_GAS(/datum/gas/nitryl,air) - if ((cached_gases[/datum/gas/oxygen][MOLES] - heat_efficency < 0 )|| (cached_gases[/datum/gas/nitrogen][MOLES] - heat_efficency < 0)) //Shouldn't produce gas from nothing. + if ((cached_gases[/datum/gas/oxygen] - heat_efficency < 0 )|| (cached_gases[/datum/gas/nitrogen] - heat_efficency < 0)) //Shouldn't produce gas from nothing. return NO_REACTION - cached_gases[/datum/gas/oxygen][MOLES] -= heat_efficency - cached_gases[/datum/gas/nitrogen][MOLES] -= heat_efficency - cached_gases[/datum/gas/nitryl][MOLES] += heat_efficency*2 + cached_gases[/datum/gas/oxygen] -= heat_efficency + cached_gases[/datum/gas/nitrogen] -= heat_efficency + cached_gases[/datum/gas/nitryl] += heat_efficency*2 if(energy_used > 0) var/new_heat_capacity = air.heat_capacity() @@ -355,14 +348,13 @@ var/pressure = air.return_pressure() var/old_heat_capacity = air.heat_capacity() - var/reaction_efficency = min(1/((pressure/(0.1*ONE_ATMOSPHERE))*(max(cached_gases[/datum/gas/plasma][MOLES]/cached_gases[/datum/gas/nitrous_oxide][MOLES],1))),cached_gases[/datum/gas/nitrous_oxide][MOLES],cached_gases[/datum/gas/plasma][MOLES]/2) + var/reaction_efficency = min(1/((pressure/(0.1*ONE_ATMOSPHERE))*(max(cached_gases[/datum/gas/plasma]/cached_gases[/datum/gas/nitrous_oxide],1))),cached_gases[/datum/gas/nitrous_oxide],cached_gases[/datum/gas/plasma]/2) var/energy_released = 2*reaction_efficency*FIRE_CARBON_ENERGY_RELEASED - if ((cached_gases[/datum/gas/nitrous_oxide][MOLES] - reaction_efficency < 0 )|| (cached_gases[/datum/gas/plasma][MOLES] - (2*reaction_efficency) < 0)) //Shouldn't produce gas from nothing. + if ((cached_gases[/datum/gas/nitrous_oxide] - reaction_efficency < 0 )|| (cached_gases[/datum/gas/plasma] - (2*reaction_efficency) < 0)) //Shouldn't produce gas from nothing. return NO_REACTION - ASSERT_GAS(/datum/gas/bz,air) - cached_gases[/datum/gas/bz][MOLES] += reaction_efficency - cached_gases[/datum/gas/nitrous_oxide][MOLES] -= reaction_efficency - cached_gases[/datum/gas/plasma][MOLES] -= 2*reaction_efficency + cached_gases[/datum/gas/bz] += reaction_efficency + cached_gases[/datum/gas/nitrous_oxide] -= reaction_efficency + cached_gases[/datum/gas/plasma] -= 2*reaction_efficency if(energy_released > 0) @@ -388,16 +380,15 @@ var/list/cached_gases = air.gases var/old_heat_capacity = air.heat_capacity() - var/heat_scale = min(air.temperature/STIMULUM_HEAT_SCALE,cached_gases[/datum/gas/tritium][MOLES],cached_gases[/datum/gas/plasma][MOLES],cached_gases[/datum/gas/nitryl][MOLES]) + var/heat_scale = min(air.temperature/STIMULUM_HEAT_SCALE,cached_gases[/datum/gas/tritium],cached_gases[/datum/gas/plasma],cached_gases[/datum/gas/nitryl]) var/stim_energy_change = heat_scale + STIMULUM_FIRST_RISE*(heat_scale**2) - STIMULUM_FIRST_DROP*(heat_scale**3) + STIMULUM_SECOND_RISE*(heat_scale**4) - STIMULUM_ABSOLUTE_DROP*(heat_scale**5) - ASSERT_GAS(/datum/gas/stimulum,air) - if ((cached_gases[/datum/gas/tritium][MOLES] - heat_scale < 0 )|| (cached_gases[/datum/gas/plasma][MOLES] - heat_scale < 0) || (cached_gases[/datum/gas/nitryl][MOLES] - heat_scale < 0)) //Shouldn't produce gas from nothing. + if ((cached_gases[/datum/gas/tritium] - heat_scale < 0 )|| (cached_gases[/datum/gas/plasma] - heat_scale < 0) || (cached_gases[/datum/gas/nitryl] - heat_scale < 0)) //Shouldn't produce gas from nothing. return NO_REACTION - cached_gases[/datum/gas/stimulum][MOLES]+= heat_scale/10 - cached_gases[/datum/gas/tritium][MOLES] -= heat_scale - cached_gases[/datum/gas/plasma][MOLES] -= heat_scale - cached_gases[/datum/gas/nitryl][MOLES] -= heat_scale + cached_gases[/datum/gas/stimulum]+= heat_scale/10 + cached_gases[/datum/gas/tritium] -= heat_scale + cached_gases[/datum/gas/plasma] -= heat_scale + cached_gases[/datum/gas/nitryl] -= heat_scale if(stim_energy_change) var/new_heat_capacity = air.heat_capacity() @@ -418,15 +409,14 @@ /datum/gas_reaction/nobliumformation/react(datum/gas_mixture/air) var/list/cached_gases = air.gases - air.assert_gases(/datum/gas/hypernoblium,/datum/gas/bz) var/old_heat_capacity = air.heat_capacity() - var/nob_formed = min((cached_gases[/datum/gas/nitrogen][MOLES]+cached_gases[/datum/gas/tritium][MOLES])/100,cached_gases[/datum/gas/tritium][MOLES]/10,cached_gases[/datum/gas/nitrogen][MOLES]/20) - var/energy_taken = nob_formed*(NOBLIUM_FORMATION_ENERGY/(max(cached_gases[/datum/gas/bz][MOLES],1))) - if ((cached_gases[/datum/gas/tritium][MOLES] - 10*nob_formed < 0) || (cached_gases[/datum/gas/nitrogen][MOLES] - 20*nob_formed < 0)) + var/nob_formed = min((cached_gases[/datum/gas/nitrogen]+cached_gases[/datum/gas/tritium])/100,cached_gases[/datum/gas/tritium]/10,cached_gases[/datum/gas/nitrogen]/20) + var/energy_taken = nob_formed*(NOBLIUM_FORMATION_ENERGY/(max(cached_gases[/datum/gas/bz],1))) + if ((cached_gases[/datum/gas/tritium] - 10*nob_formed < 0) || (cached_gases[/datum/gas/nitrogen] - 20*nob_formed < 0)) return NO_REACTION - cached_gases[/datum/gas/tritium][MOLES] -= 10*nob_formed - cached_gases[/datum/gas/nitrogen][MOLES] -= 20*nob_formed - cached_gases[/datum/gas/hypernoblium][MOLES]+= nob_formed + cached_gases[/datum/gas/tritium] -= 10*nob_formed + cached_gases[/datum/gas/nitrogen] -= 20*nob_formed + cached_gases[/datum/gas/hypernoblium]+= nob_formed if (nob_formed) @@ -449,14 +439,13 @@ /datum/gas_reaction/miaster/react(datum/gas_mixture/air, datum/holder) var/list/cached_gases = air.gases // As the name says it, it needs to be dry - if(cached_gases[/datum/gas/water_vapor] && cached_gases[/datum/gas/water_vapor][MOLES]/air.total_moles() > 0.1) + if(cached_gases[/datum/gas/water_vapor] && cached_gases[/datum/gas/water_vapor]/air.total_moles() > 0.1) return //Replace miasma with oxygen - var/cleaned_air = min(cached_gases[/datum/gas/miasma][MOLES], 20 + (air.temperature - FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 70) / 20) - cached_gases[/datum/gas/miasma][MOLES] -= cleaned_air - ASSERT_GAS(/datum/gas/oxygen,air) - cached_gases[/datum/gas/oxygen][MOLES] += cleaned_air + var/cleaned_air = min(cached_gases[/datum/gas/miasma], 20 + (air.temperature - FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 70) / 20) + cached_gases[/datum/gas/miasma] -= cleaned_air + cached_gases[/datum/gas/oxygen] += cleaned_air //Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable air.temperature += cleaned_air * 0.002 diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 0a7b76cc79..6ea571935b 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -1,880 +1,880 @@ -/datum/tlv - var/min2 - var/min1 - var/max1 - var/max2 - -/datum/tlv/New(min2 as num, min1 as num, max1 as num, max2 as num) - if(min2) src.min2 = min2 - if(min1) src.min1 = min1 - if(max1) src.max1 = max1 - if(max2) src.max2 = max2 - -/datum/tlv/proc/get_danger_level(val as num) - if(max2 != -1 && val >= max2) - return 2 - if(min2 != -1 && val <= min2) - return 2 - if(max1 != -1 && val >= max1) - return 1 - if(min1 != -1 && val <= min1) - return 1 - return 0 - -/datum/tlv/no_checks - min2 = -1 - min1 = -1 - max1 = -1 - max2 = -1 - -/datum/tlv/dangerous - min2 = -1 - min1 = -1 - max1 = 0.2 - max2 = 0.5 - -/obj/item/electronics/airalarm - name = "air alarm electronics" - icon_state = "airalarm_electronics" - -/obj/item/wallframe/airalarm - name = "air alarm frame" - desc = "Used for building Air Alarms." - icon = 'icons/obj/monitors.dmi' - icon_state = "alarm_bitem" - result_path = /obj/machinery/airalarm - -#define AALARM_MODE_SCRUBBING 1 -#define AALARM_MODE_VENTING 2 //makes draught -#define AALARM_MODE_PANIC 3 //like siphon, but stronger (enables widenet) -#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing -#define AALARM_MODE_OFF 5 -#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents -#define AALARM_MODE_SIPHON 7 //Scrubbers suck air -#define AALARM_MODE_CONTAMINATED 8 //Turns on all filtering and widenet scrubbing. -#define AALARM_MODE_REFILL 9 //just like normal, but with triple the air output - -#define AALARM_REPORT_TIMEOUT 100 - -#define AALARM_OVERLAY_OFF "alarm_off" -#define AALARM_OVERLAY_GREEN "alarm_green" -#define AALARM_OVERLAY_WARN "alarm_amber" -#define AALARM_OVERLAY_DANGER "alarm_red" - -/obj/machinery/airalarm - name = "air alarm" - desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous." - icon = 'icons/obj/monitors.dmi' - icon_state = "alarm0" - use_power = IDLE_POWER_USE - idle_power_usage = 4 - active_power_usage = 8 - power_channel = ENVIRON - req_access = list(ACCESS_ATMOSPHERICS) - max_integrity = 250 - integrity_failure = 80 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) - resistance_flags = FIRE_PROOF - - var/danger_level = 0 - var/mode = AALARM_MODE_SCRUBBING - - var/locked = TRUE - var/aidisabled = 0 - var/shorted = 0 - var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone - var/brightness_on = 1 - - var/frequency = FREQ_ATMOS_CONTROL - var/alarm_frequency = FREQ_ATMOS_ALARMS - var/datum/radio_frequency/radio_connection - - var/list/TLV = list( // Breathable air. - "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa - "temperature" = new/datum/tlv(T0C, T0C+10, T0C+40, T0C+66), - /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa - /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000), - /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10), - /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5), - /datum/gas/plasma = new/datum/tlv/dangerous, - /datum/gas/nitrous_oxide = new/datum/tlv/dangerous, - /datum/gas/bz = new/datum/tlv/dangerous, - /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic - /datum/gas/water_vapor = new/datum/tlv/dangerous, - /datum/gas/tritium = new/datum/tlv/dangerous, - /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects - /datum/gas/nitryl = new/datum/tlv/dangerous, - /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires - ) - -/obj/machinery/airalarm/server // No checks here. - TLV = list( - "pressure" = new/datum/tlv/no_checks, - "temperature" = new/datum/tlv/no_checks, - /datum/gas/oxygen = new/datum/tlv/no_checks, - /datum/gas/nitrogen = new/datum/tlv/no_checks, - /datum/gas/carbon_dioxide = new/datum/tlv/no_checks, - /datum/gas/miasma = new/datum/tlv/no_checks, - /datum/gas/plasma = new/datum/tlv/no_checks, - /datum/gas/nitrous_oxide = new/datum/tlv/no_checks, - /datum/gas/bz = new/datum/tlv/no_checks, - /datum/gas/hypernoblium = new/datum/tlv/no_checks, - /datum/gas/water_vapor = new/datum/tlv/no_checks, - /datum/gas/tritium = new/datum/tlv/no_checks, - /datum/gas/stimulum = new/datum/tlv/no_checks, - /datum/gas/nitryl = new/datum/tlv/no_checks, - /datum/gas/pluoxium = new/datum/tlv/no_checks - ) - -/obj/machinery/airalarm/kitchen_cold_room // Copypasta: to check temperatures. - TLV = list( - "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa - "temperature" = new/datum/tlv(T0C-73.15, T0C-63.15, T0C, T0C+10), - /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa - /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000), - /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10), - /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5), - /datum/gas/plasma = new/datum/tlv/dangerous, - /datum/gas/nitrous_oxide = new/datum/tlv/dangerous, - /datum/gas/bz = new/datum/tlv/dangerous, - /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic - /datum/gas/water_vapor = new/datum/tlv/dangerous, - /datum/gas/tritium = new/datum/tlv/dangerous, - /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects - /datum/gas/nitryl = new/datum/tlv/dangerous, - /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires - ) - -/obj/machinery/airalarm/unlocked - locked = FALSE - -/obj/machinery/airalarm/engine - name = "engine air alarm" - locked = FALSE - req_access = null - req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_ENGINE) - -/obj/machinery/airalarm/mixingchamber - name = "chamber air alarm" - locked = FALSE - req_access = null - req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_TOX, ACCESS_TOX_STORAGE) - -/obj/machinery/airalarm/all_access - name = "all-access air alarm" - desc = "This particular atmos control unit appears to have no access restrictions." - locked = FALSE - req_access = null - req_one_access = null - -/obj/machinery/airalarm/syndicate //general syndicate access - req_access = list(ACCESS_SYNDICATE) - -/obj/machinery/airalarm/directional/north //Pixel offsets get overwritten on New() - dir = SOUTH - pixel_y = 24 - -/obj/machinery/airalarm/directional/south - dir = NORTH - pixel_y = -24 - -/obj/machinery/airalarm/directional/east - dir = WEST - pixel_x = 24 - -/obj/machinery/airalarm/directional/west - dir = EAST - pixel_x = -24 - -//all air alarms in area are connected via magic -/area - var/list/air_vent_names = list() - var/list/air_scrub_names = list() - var/list/air_vent_info = list() - var/list/air_scrub_info = list() - -/obj/machinery/airalarm/Initialize(mapload, ndir, nbuild) - . = ..() - wires = new /datum/wires/airalarm(src) - - if(ndir) - setDir(ndir) - - if(nbuild) - buildstage = 0 - panel_open = TRUE - pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) - pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : 0 - - if(name == initial(name)) - name = "[get_area_name(src)] Air Alarm" - - power_change() - set_frequency(frequency) - -/obj/machinery/airalarm/Destroy() - SSradio.remove_object(src, frequency) - qdel(wires) - wires = null - return ..() - -/obj/machinery/airalarm/examine(mob/user) - . = ..() - switch(buildstage) - if(0) - to_chat(user, "It is missing air alarm electronics.") - if(1) - to_chat(user, "It is missing wiring.") - if(2) - to_chat(user, "Alt-click to [locked ? "unlock" : "lock"] the interface.") - -/obj/machinery/airalarm/ui_status(mob/user) - if(user.has_unlimited_silicon_privilege && aidisabled) - to_chat(user, "AI control has been disabled.") - else if(!shorted) - return ..() - return UI_CLOSE - -/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "airalarm", name, 440, 650, master_ui, state) - ui.open() - -/obj/machinery/airalarm/ui_data(mob/user) - var/data = list( - "locked" = locked, - "siliconUser" = user.has_unlimited_silicon_privilege, - "emagged" = (obj_flags & EMAGGED ? 1 : 0), - "danger_level" = danger_level, - ) - - var/area/A = get_area(src) - data["atmos_alarm"] = A.atmosalm - data["fire_alarm"] = A.fire - - var/turf/T = get_turf(src) - var/datum/gas_mixture/environment = T.return_air() - var/datum/tlv/cur_tlv - - data["environment_data"] = list() - var/pressure = environment.return_pressure() - cur_tlv = TLV["pressure"] - data["environment_data"] += list(list( - "name" = "Pressure", - "value" = pressure, - "unit" = "kPa", - "danger_level" = cur_tlv.get_danger_level(pressure) - )) - var/temperature = environment.temperature - cur_tlv = TLV["temperature"] - data["environment_data"] += list(list( - "name" = "Temperature", - "value" = temperature, - "unit" = "K ([round(temperature - T0C, 0.1)]C)", - "danger_level" = cur_tlv.get_danger_level(temperature) - )) - var/total_moles = environment.total_moles() - var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume - for(var/gas_id in environment.gases) - if(!(gas_id in TLV)) // We're not interested in this gas, it seems. - continue - cur_tlv = TLV[gas_id] - data["environment_data"] += list(list( - "name" = environment.gases[gas_id][GAS_META][META_GAS_NAME], - "value" = environment.gases[gas_id][MOLES] / total_moles * 100, - "unit" = "%", - "danger_level" = cur_tlv.get_danger_level(environment.gases[gas_id][MOLES] * partial_pressure) - )) - - if(!locked || user.has_unlimited_silicon_privilege) - data["vents"] = list() - for(var/id_tag in A.air_vent_names) - var/long_name = A.air_vent_names[id_tag] - var/list/info = A.air_vent_info[id_tag] - if(!info || info["frequency"] != frequency) - continue - data["vents"] += list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "checks" = info["checks"], - "excheck" = info["checks"]&1, - "incheck" = info["checks"]&2, - "direction" = info["direction"], - "external" = info["external"], - "internal" = info["internal"], - "extdefault"= (info["external"] == ONE_ATMOSPHERE), - "intdefault"= (info["internal"] == 0) - )) - data["scrubbers"] = list() - for(var/id_tag in A.air_scrub_names) - var/long_name = A.air_scrub_names[id_tag] - var/list/info = A.air_scrub_info[id_tag] - if(!info || info["frequency"] != frequency) - continue - data["scrubbers"] += list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "scrubbing" = info["scrubbing"], - "widenet" = info["widenet"], - "filter_types" = info["filter_types"] - )) - data["mode"] = mode - data["modes"] = list() - data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0)) - data["modes"] += list(list("name" = "Contaminated - Scrubs out ALL contaminants quickly","mode" = AALARM_MODE_CONTAMINATED, "selected" = mode == AALARM_MODE_CONTAMINATED, "danger" = 0)) - data["modes"] += list(list("name" = "Draught - Siphons out air while replacing", "mode" = AALARM_MODE_VENTING, "selected" = mode == AALARM_MODE_VENTING, "danger" = 0)) - data["modes"] += list(list("name" = "Refill - Triple vent output", "mode" = AALARM_MODE_REFILL, "selected" = mode == AALARM_MODE_REFILL, "danger" = 1)) - data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 1)) - data["modes"] += list(list("name" = "Siphon - Siphons air out of the room", "mode" = AALARM_MODE_SIPHON, "selected" = mode == AALARM_MODE_SIPHON, "danger" = 1)) - data["modes"] += list(list("name" = "Panic Siphon - Siphons air out of the room quickly","mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1)) - data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0)) - if(obj_flags & EMAGGED) - data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FLOOD, "selected" = mode == AALARM_MODE_FLOOD, "danger" = 1)) - - var/datum/tlv/selected - var/list/thresholds = list() - - selected = TLV["pressure"] - thresholds += list(list("name" = "Pressure", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) - - selected = TLV["temperature"] - thresholds += list(list("name" = "Temperature", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) - - for(var/gas_id in GLOB.meta_gas_info) - if(!(gas_id in TLV)) // We're not interested in this gas, it seems. - continue - selected = TLV[gas_id] - thresholds += list(list("name" = GLOB.meta_gas_info[gas_id][META_GAS_NAME], "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max2", "selected" = selected.max2)) - - data["thresholds"] = thresholds - return data - -/obj/machinery/airalarm/ui_act(action, params) - if(..() || buildstage != 2) - return - if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled)) - return - var/device_id = params["id_tag"] - switch(action) - if("lock") - if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN)) - locked = !locked - . = TRUE - if("power", "toggle_filter", "widenet", "scrubbing") - send_signal(device_id, list("[action]" = params["val"]), usr) - . = TRUE - if("excheck") - send_signal(device_id, list("checks" = text2num(params["val"])^1), usr) - . = TRUE - if("incheck") - send_signal(device_id, list("checks" = text2num(params["val"])^2), usr) - . = TRUE - if("set_external_pressure", "set_internal_pressure") - var/area/A = get_area(src) - var/target = input("New target pressure:", name, A.air_vent_info[device_id][(action == "set_external_pressure" ? "external" : "internal")]) as num|null - if(!isnull(target) && !..()) - send_signal(device_id, list("[action]" = target), usr) - . = TRUE - if("reset_external_pressure") - send_signal(device_id, list("reset_external_pressure"), usr) - . = TRUE - if("reset_internal_pressure") - send_signal(device_id, list("reset_internal_pressure"), usr) - . = TRUE - if("threshold") - var/env = params["env"] - if(text2path(env)) - env = text2path(env) - - var/name = params["var"] - var/datum/tlv/tlv = TLV[env] - if(isnull(tlv)) - return - var/value = input("New [name] for [env]:", name, tlv.vars[name]) as num|null - if(!isnull(value) && !..()) - if(value < 0) - tlv.vars[name] = -1 - else - tlv.vars[name] = round(value, 0.01) - investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS) - . = TRUE - if("mode") - mode = text2num(params["mode"]) - investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS) - apply_mode() - . = TRUE - if("alarm") - var/area/A = get_area(src) - if(A.atmosalert(2, src)) - post_alert(2) - . = TRUE - if("reset") - var/area/A = get_area(src) - if(A.atmosalert(0, src)) - post_alert(0) - . = TRUE - update_icon() - -/obj/machinery/airalarm/proc/reset(wire) - switch(wire) - if(WIRE_POWER) - if(!wires.is_cut(WIRE_POWER)) - shorted = FALSE - update_icon() - if(WIRE_AI) - if(!wires.is_cut(WIRE_AI)) - aidisabled = FALSE - - -/obj/machinery/airalarm/proc/shock(mob/user, prb) - if((stat & (NOPOWER))) // unpowered, no shock - return 0 - if(!prob(prb)) - return 0 //you lucked out, no shock for you - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() //sparks always. - if (electrocute_mob(user, get_area(src), src, 1, TRUE)) - return 1 - else - return 0 - -/obj/machinery/airalarm/proc/refresh_all() - var/area/A = get_area(src) - for(var/id_tag in A.air_vent_names) - var/list/I = A.air_vent_info[id_tag] - if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) - continue - send_signal(id_tag, list("status")) - for(var/id_tag in A.air_scrub_names) - var/list/I = A.air_scrub_info[id_tag] - if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) - continue - send_signal(id_tag, list("status")) - -/obj/machinery/airalarm/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) - -/obj/machinery/airalarm/proc/send_signal(target, list/command, mob/user)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise - if(!radio_connection) - return 0 - - var/datum/signal/signal = new(command) - signal.data["tag"] = target - signal.data["sigtype"] = "command" - signal.data["user"] = user - radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) - - return 1 - -/obj/machinery/airalarm/proc/get_mode_name(mode_value) - switch(mode_value) - if(AALARM_MODE_SCRUBBING) - return "Filtering" - if(AALARM_MODE_CONTAMINATED) - return "Contaminated" - if(AALARM_MODE_VENTING) - return "Draught" - if(AALARM_MODE_REFILL) - return "Refill" - if(AALARM_MODE_PANIC) - return "Panic Siphon" - if(AALARM_MODE_REPLACEMENT) - return "Cycle" - if(AALARM_MODE_SIPHON) - return "Siphon" - if(AALARM_MODE_OFF) - return "Off" - if(AALARM_MODE_FLOOD) - return "Flood" - -/obj/machinery/airalarm/proc/apply_mode() - var/area/A = get_area(src) - switch(mode) - if(AALARM_MODE_SCRUBBING) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 1, - "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma), - "scrubbing" = 1, - "widenet" = 0, - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 1, - "checks" = 1, - "set_external_pressure" = ONE_ATMOSPHERE - )) - if(AALARM_MODE_CONTAMINATED) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 1, - "set_filters" = list( - /datum/gas/carbon_dioxide, - /datum/gas/miasma, - /datum/gas/plasma, - /datum/gas/water_vapor, - /datum/gas/hypernoblium, - /datum/gas/nitrous_oxide, - /datum/gas/nitryl, - /datum/gas/tritium, - /datum/gas/bz, - /datum/gas/stimulum, - /datum/gas/pluoxium - ), - "scrubbing" = 1, - "widenet" = 1, - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 1, - "checks" = 1, - "set_external_pressure" = ONE_ATMOSPHERE - )) - if(AALARM_MODE_VENTING) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 1, - "widenet" = 0, - "scrubbing" = 0 - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 1, - "checks" = 1, - "set_external_pressure" = ONE_ATMOSPHERE*2 - )) - if(AALARM_MODE_REFILL) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 1, - "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma), - "scrubbing" = 1, - "widenet" = 0, - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 1, - "checks" = 1, - "set_external_pressure" = ONE_ATMOSPHERE * 3 - )) - if(AALARM_MODE_PANIC, - AALARM_MODE_REPLACEMENT) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 1, - "widenet" = 1, - "scrubbing" = 0 - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 0 - )) - if(AALARM_MODE_SIPHON) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 1, - "widenet" = 0, - "scrubbing" = 0 - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 0 - )) - - if(AALARM_MODE_OFF) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 0 - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 0 - )) - if(AALARM_MODE_FLOOD) - for(var/device_id in A.air_scrub_names) - send_signal(device_id, list( - "power" = 0 - )) - for(var/device_id in A.air_vent_names) - send_signal(device_id, list( - "power" = 1, - "checks" = 2, - "set_internal_pressure" = 0 - )) - -/obj/machinery/airalarm/update_icon() - set_light(0) - cut_overlays() - SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) - if(stat & NOPOWER) - icon_state = "alarm0" - return - - if(stat & BROKEN) - icon_state = "alarmx" - return - - if(panel_open) - switch(buildstage) - if(2) - icon_state = "alarmx" - if(1) - icon_state = "alarm_b2" - if(0) - icon_state = "alarm_b1" - return - - icon_state = "alarm1" - var/overlay_state = AALARM_OVERLAY_OFF - var/area/A = get_area(src) - switch(max(danger_level, A.atmosalm)) - if(0) - add_overlay(AALARM_OVERLAY_GREEN) - overlay_state = AALARM_OVERLAY_GREEN - light_color = LIGHT_COLOR_PALEBLUE - set_light(brightness_on) - if(1) - add_overlay(AALARM_OVERLAY_WARN) - overlay_state = AALARM_OVERLAY_WARN - light_color = LIGHT_COLOR_LAVA - set_light(brightness_on) - if(2) - add_overlay(AALARM_OVERLAY_DANGER) - overlay_state = AALARM_OVERLAY_DANGER - light_color = LIGHT_COLOR_RED - set_light(brightness_on) - - SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) - update_light() - -/obj/machinery/airalarm/process() - if((stat & (NOPOWER|BROKEN)) || shorted) - return - - var/turf/location = get_turf(src) - if(!location) - return - - var/datum/tlv/cur_tlv - - var/datum/gas_mixture/environment = location.return_air() - var/list/env_gases = environment.gases - var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume - - cur_tlv = TLV["pressure"] - var/environment_pressure = environment.return_pressure() - var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) - - cur_tlv = TLV["temperature"] - var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) - - var/gas_dangerlevel = 0 - for(var/gas_id in env_gases) - if(!(gas_id in TLV)) // We're not interested in this gas, it seems. - continue - cur_tlv = TLV[gas_id] - gas_dangerlevel = max(gas_dangerlevel, cur_tlv.get_danger_level(env_gases[gas_id][MOLES] * partial_pressure)) - - environment.garbage_collect() - - var/old_danger_level = danger_level - danger_level = max(pressure_dangerlevel, temperature_dangerlevel, gas_dangerlevel) - - if(old_danger_level != danger_level) - apply_danger_level() - if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05) - mode = AALARM_MODE_SCRUBBING - apply_mode() - - return - - -/obj/machinery/airalarm/proc/post_alert(alert_level) - var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) - - if(!frequency) - return - - var/datum/signal/alert_signal = new(list( - "zone" = get_area_name(src), - "type" = "Atmospheric" - )) - if(alert_level==2) - alert_signal.data["alert"] = "severe" - else if (alert_level==1) - alert_signal.data["alert"] = "minor" - else if (alert_level==0) - alert_signal.data["alert"] = "clear" - - frequency.post_signal(src, alert_signal, range = -1) - -/obj/machinery/airalarm/proc/apply_danger_level() - var/area/A = get_area(src) - - var/new_area_danger_level = 0 - for(var/obj/machinery/airalarm/AA in A) - if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted) - new_area_danger_level = max(new_area_danger_level,AA.danger_level) - if(A.atmosalert(new_area_danger_level,src)) //if area was in normal state or if area was in alert state - post_alert(new_area_danger_level) - - update_icon() - -/obj/machinery/airalarm/attackby(obj/item/W, mob/user, params) - switch(buildstage) - if(2) - if(istype(W, /obj/item/wirecutters) && panel_open && wires.is_all_cut()) - W.play_tool_sound(src) - to_chat(user, "You cut the final wires.") - new /obj/item/stack/cable_coil(loc, 5) - buildstage = 1 - update_icon() - return - else if(istype(W, /obj/item/screwdriver)) // Opening that Air Alarm up. - W.play_tool_sound(src) - panel_open = !panel_open - to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"].") - update_icon() - return - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))// trying to unlock the interface with an ID card - togglelock(user) - else if(panel_open && is_wire_tool(W)) - wires.interact(user) - return - if(1) - if(istype(W, /obj/item/crowbar)) - user.visible_message("[user.name] removes the electronics from [src.name].",\ - "You start prying out the circuit...") - W.play_tool_sound(src) - if (W.use_tool(src, user, 20)) - if (buildstage == 1) - to_chat(user, "You remove the air alarm electronics.") - new /obj/item/electronics/airalarm( src.loc ) - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) - buildstage = 0 - update_icon() - return - - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/cable = W - if(cable.get_amount() < 5) - to_chat(user, "You need five lengths of cable to wire the air alarm!") - return - user.visible_message("[user.name] wires the air alarm.", \ - "You start wiring the air alarm...") - if (do_after(user, 20, target = src)) - if (cable.get_amount() >= 5 && buildstage == 1) - cable.use(5) - to_chat(user, "You wire the air alarm.") - wires.repair() - aidisabled = 0 - locked = FALSE - mode = 1 - shorted = 0 - post_alert(0) - buildstage = 2 - update_icon() - return - if(0) - if(istype(W, /obj/item/electronics/airalarm)) - if(user.temporarilyRemoveItemFromInventory(W)) - to_chat(user, "You insert the circuit.") - buildstage = 1 - update_icon() - qdel(W) - return - - if(istype(W, /obj/item/electroadaptive_pseudocircuit)) - var/obj/item/electroadaptive_pseudocircuit/P = W - if(!P.adapt_circuit(user, 25)) - return - user.visible_message("[user] fabricates a circuit and places it into [src].", \ - "You adapt an air alarm circuit and slot it into the assembly.") - buildstage = 1 - update_icon() - return - - if(istype(W, /obj/item/wrench)) - to_chat(user, "You detach \the [src] from the wall.") - W.play_tool_sound(src) - new /obj/item/wallframe/airalarm( user.loc ) - qdel(src) - return - - return ..() - -/obj/machinery/airalarm/AltClick(mob/user) - ..() - if(!user.canUseTopic(src, !issilicon(user)) || !isturf(loc)) - return - else - togglelock(user) - -/obj/machinery/airalarm/proc/togglelock(mob/living/user) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "It does nothing!") - else - if(src.allowed(usr) && !wires.is_cut(WIRE_IDSCAN)) - locked = !locked - updateUsrDialog() - to_chat(user, "You [ locked ? "lock" : "unlock"] the air alarm interface.") - else - to_chat(user, "Access denied.") - return - -/obj/machinery/airalarm/power_change() - ..() - if(stat & NOPOWER) - set_light(0) - update_icon() - -/obj/machinery/airalarm/emag_act(mob/user) - if(obj_flags & EMAGGED) - return - obj_flags |= EMAGGED - visible_message("Sparks fly out of [src]!", "You emag [src], disabling its safeties.") - playsound(src, "sparks", 50, 1) - -/obj/machinery/airalarm/obj_break(damage_flag) - ..() - update_icon() - set_light(0) - -/obj/machinery/airalarm/deconstruct(disassembled = TRUE) - if(!(flags_1 & NODECONSTRUCT_1)) - new /obj/item/stack/sheet/metal(loc, 2) - var/obj/item/I = new /obj/item/electronics/airalarm(loc) - if(!disassembled) - I.obj_integrity = I.max_integrity * 0.5 - new /obj/item/stack/cable_coil(loc, 3) - qdel(src) - -#undef AALARM_MODE_SCRUBBING -#undef AALARM_MODE_VENTING -#undef AALARM_MODE_PANIC -#undef AALARM_MODE_REPLACEMENT -#undef AALARM_MODE_OFF -#undef AALARM_MODE_FLOOD -#undef AALARM_MODE_SIPHON -#undef AALARM_MODE_CONTAMINATED -#undef AALARM_MODE_REFILL -#undef AALARM_REPORT_TIMEOUT +/datum/tlv + var/min2 + var/min1 + var/max1 + var/max2 + +/datum/tlv/New(min2 as num, min1 as num, max1 as num, max2 as num) + if(min2) src.min2 = min2 + if(min1) src.min1 = min1 + if(max1) src.max1 = max1 + if(max2) src.max2 = max2 + +/datum/tlv/proc/get_danger_level(val as num) + if(max2 != -1 && val >= max2) + return 2 + if(min2 != -1 && val <= min2) + return 2 + if(max1 != -1 && val >= max1) + return 1 + if(min1 != -1 && val <= min1) + return 1 + return 0 + +/datum/tlv/no_checks + min2 = -1 + min1 = -1 + max1 = -1 + max2 = -1 + +/datum/tlv/dangerous + min2 = -1 + min1 = -1 + max1 = 0.2 + max2 = 0.5 + +/obj/item/electronics/airalarm + name = "air alarm electronics" + icon_state = "airalarm_electronics" + +/obj/item/wallframe/airalarm + name = "air alarm frame" + desc = "Used for building Air Alarms." + icon = 'icons/obj/monitors.dmi' + icon_state = "alarm_bitem" + result_path = /obj/machinery/airalarm + +#define AALARM_MODE_SCRUBBING 1 +#define AALARM_MODE_VENTING 2 //makes draught +#define AALARM_MODE_PANIC 3 //like siphon, but stronger (enables widenet) +#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing +#define AALARM_MODE_OFF 5 +#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents +#define AALARM_MODE_SIPHON 7 //Scrubbers suck air +#define AALARM_MODE_CONTAMINATED 8 //Turns on all filtering and widenet scrubbing. +#define AALARM_MODE_REFILL 9 //just like normal, but with triple the air output + +#define AALARM_REPORT_TIMEOUT 100 + +#define AALARM_OVERLAY_OFF "alarm_off" +#define AALARM_OVERLAY_GREEN "alarm_green" +#define AALARM_OVERLAY_WARN "alarm_amber" +#define AALARM_OVERLAY_DANGER "alarm_red" + +/obj/machinery/airalarm + name = "air alarm" + desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous." + icon = 'icons/obj/monitors.dmi' + icon_state = "alarm0" + use_power = IDLE_POWER_USE + idle_power_usage = 4 + active_power_usage = 8 + power_channel = ENVIRON + req_access = list(ACCESS_ATMOSPHERICS) + max_integrity = 250 + integrity_failure = 80 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) + resistance_flags = FIRE_PROOF + + var/danger_level = 0 + var/mode = AALARM_MODE_SCRUBBING + + var/locked = TRUE + var/aidisabled = 0 + var/shorted = 0 + var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone + var/brightness_on = 1 + + var/frequency = FREQ_ATMOS_CONTROL + var/alarm_frequency = FREQ_ATMOS_ALARMS + var/datum/radio_frequency/radio_connection + + var/list/TLV = list( // Breathable air. + "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa + "temperature" = new/datum/tlv(T0C, T0C+10, T0C+40, T0C+66), + /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa + /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000), + /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10), + /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5), + /datum/gas/plasma = new/datum/tlv/dangerous, + /datum/gas/nitrous_oxide = new/datum/tlv/dangerous, + /datum/gas/bz = new/datum/tlv/dangerous, + /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic + /datum/gas/water_vapor = new/datum/tlv/dangerous, + /datum/gas/tritium = new/datum/tlv/dangerous, + /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects + /datum/gas/nitryl = new/datum/tlv/dangerous, + /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires + ) + +/obj/machinery/airalarm/server // No checks here. + TLV = list( + "pressure" = new/datum/tlv/no_checks, + "temperature" = new/datum/tlv/no_checks, + /datum/gas/oxygen = new/datum/tlv/no_checks, + /datum/gas/nitrogen = new/datum/tlv/no_checks, + /datum/gas/carbon_dioxide = new/datum/tlv/no_checks, + /datum/gas/miasma = new/datum/tlv/no_checks, + /datum/gas/plasma = new/datum/tlv/no_checks, + /datum/gas/nitrous_oxide = new/datum/tlv/no_checks, + /datum/gas/bz = new/datum/tlv/no_checks, + /datum/gas/hypernoblium = new/datum/tlv/no_checks, + /datum/gas/water_vapor = new/datum/tlv/no_checks, + /datum/gas/tritium = new/datum/tlv/no_checks, + /datum/gas/stimulum = new/datum/tlv/no_checks, + /datum/gas/nitryl = new/datum/tlv/no_checks, + /datum/gas/pluoxium = new/datum/tlv/no_checks + ) + +/obj/machinery/airalarm/kitchen_cold_room // Copypasta: to check temperatures. + TLV = list( + "pressure" = new/datum/tlv(ONE_ATMOSPHERE * 0.8, ONE_ATMOSPHERE* 0.9, ONE_ATMOSPHERE * 1.1, ONE_ATMOSPHERE * 1.2), // kPa + "temperature" = new/datum/tlv(T0C-73.15, T0C-63.15, T0C, T0C+10), + /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa + /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000), + /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10), + /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5), + /datum/gas/plasma = new/datum/tlv/dangerous, + /datum/gas/nitrous_oxide = new/datum/tlv/dangerous, + /datum/gas/bz = new/datum/tlv/dangerous, + /datum/gas/hypernoblium = new/datum/tlv(-1, -1, 1000, 1000), // Hyper-Noblium is inert and nontoxic + /datum/gas/water_vapor = new/datum/tlv/dangerous, + /datum/gas/tritium = new/datum/tlv/dangerous, + /datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects + /datum/gas/nitryl = new/datum/tlv/dangerous, + /datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires + ) + +/obj/machinery/airalarm/unlocked + locked = FALSE + +/obj/machinery/airalarm/engine + name = "engine air alarm" + locked = FALSE + req_access = null + req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_ENGINE) + +/obj/machinery/airalarm/mixingchamber + name = "chamber air alarm" + locked = FALSE + req_access = null + req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_TOX, ACCESS_TOX_STORAGE) + +/obj/machinery/airalarm/all_access + name = "all-access air alarm" + desc = "This particular atmos control unit appears to have no access restrictions." + locked = FALSE + req_access = null + req_one_access = null + +/obj/machinery/airalarm/syndicate //general syndicate access + req_access = list(ACCESS_SYNDICATE) + +/obj/machinery/airalarm/directional/north //Pixel offsets get overwritten on New() + dir = SOUTH + pixel_y = 24 + +/obj/machinery/airalarm/directional/south + dir = NORTH + pixel_y = -24 + +/obj/machinery/airalarm/directional/east + dir = WEST + pixel_x = 24 + +/obj/machinery/airalarm/directional/west + dir = EAST + pixel_x = -24 + +//all air alarms in area are connected via magic +/area + var/list/air_vent_names = list() + var/list/air_scrub_names = list() + var/list/air_vent_info = list() + var/list/air_scrub_info = list() + +/obj/machinery/airalarm/Initialize(mapload, ndir, nbuild) + . = ..() + wires = new /datum/wires/airalarm(src) + + if(ndir) + setDir(ndir) + + if(nbuild) + buildstage = 0 + panel_open = TRUE + pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) + pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : 0 + + if(name == initial(name)) + name = "[get_area_name(src)] Air Alarm" + + power_change() + set_frequency(frequency) + +/obj/machinery/airalarm/Destroy() + SSradio.remove_object(src, frequency) + qdel(wires) + wires = null + return ..() + +/obj/machinery/airalarm/examine(mob/user) + . = ..() + switch(buildstage) + if(0) + to_chat(user, "It is missing air alarm electronics.") + if(1) + to_chat(user, "It is missing wiring.") + if(2) + to_chat(user, "Alt-click to [locked ? "unlock" : "lock"] the interface.") + +/obj/machinery/airalarm/ui_status(mob/user) + if(user.has_unlimited_silicon_privilege && aidisabled) + to_chat(user, "AI control has been disabled.") + else if(!shorted) + return ..() + return UI_CLOSE + +/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "airalarm", name, 440, 650, master_ui, state) + ui.open() + +/obj/machinery/airalarm/ui_data(mob/user) + var/data = list( + "locked" = locked, + "siliconUser" = user.has_unlimited_silicon_privilege, + "emagged" = (obj_flags & EMAGGED ? 1 : 0), + "danger_level" = danger_level, + ) + + var/area/A = get_area(src) + data["atmos_alarm"] = A.atmosalm + data["fire_alarm"] = A.fire + + var/turf/T = get_turf(src) + var/datum/gas_mixture/environment = T.return_air() + var/datum/tlv/cur_tlv + + data["environment_data"] = list() + var/pressure = environment.return_pressure() + cur_tlv = TLV["pressure"] + data["environment_data"] += list(list( + "name" = "Pressure", + "value" = pressure, + "unit" = "kPa", + "danger_level" = cur_tlv.get_danger_level(pressure) + )) + var/temperature = environment.temperature + cur_tlv = TLV["temperature"] + data["environment_data"] += list(list( + "name" = "Temperature", + "value" = temperature, + "unit" = "K ([round(temperature - T0C, 0.1)]C)", + "danger_level" = cur_tlv.get_danger_level(temperature) + )) + var/total_moles = environment.total_moles() + var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume + for(var/gas_id in environment.gases) + if(!(gas_id in TLV)) // We're not interested in this gas, it seems. + continue + cur_tlv = TLV[gas_id] + data["environment_data"] += list(list( + "name" = GLOB.meta_gas_names[gas_id], + "value" = environment.gases[gas_id] / total_moles * 100, + "unit" = "%", + "danger_level" = cur_tlv.get_danger_level(environment.gases[gas_id] * partial_pressure) + )) + + if(!locked || user.has_unlimited_silicon_privilege) + data["vents"] = list() + for(var/id_tag in A.air_vent_names) + var/long_name = A.air_vent_names[id_tag] + var/list/info = A.air_vent_info[id_tag] + if(!info || info["frequency"] != frequency) + continue + data["vents"] += list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "checks" = info["checks"], + "excheck" = info["checks"]&1, + "incheck" = info["checks"]&2, + "direction" = info["direction"], + "external" = info["external"], + "internal" = info["internal"], + "extdefault"= (info["external"] == ONE_ATMOSPHERE), + "intdefault"= (info["internal"] == 0) + )) + data["scrubbers"] = list() + for(var/id_tag in A.air_scrub_names) + var/long_name = A.air_scrub_names[id_tag] + var/list/info = A.air_scrub_info[id_tag] + if(!info || info["frequency"] != frequency) + continue + data["scrubbers"] += list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "scrubbing" = info["scrubbing"], + "widenet" = info["widenet"], + "filter_types" = info["filter_types"] + )) + data["mode"] = mode + data["modes"] = list() + data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0)) + data["modes"] += list(list("name" = "Contaminated - Scrubs out ALL contaminants quickly","mode" = AALARM_MODE_CONTAMINATED, "selected" = mode == AALARM_MODE_CONTAMINATED, "danger" = 0)) + data["modes"] += list(list("name" = "Draught - Siphons out air while replacing", "mode" = AALARM_MODE_VENTING, "selected" = mode == AALARM_MODE_VENTING, "danger" = 0)) + data["modes"] += list(list("name" = "Refill - Triple vent output", "mode" = AALARM_MODE_REFILL, "selected" = mode == AALARM_MODE_REFILL, "danger" = 1)) + data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 1)) + data["modes"] += list(list("name" = "Siphon - Siphons air out of the room", "mode" = AALARM_MODE_SIPHON, "selected" = mode == AALARM_MODE_SIPHON, "danger" = 1)) + data["modes"] += list(list("name" = "Panic Siphon - Siphons air out of the room quickly","mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1)) + data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0)) + if(obj_flags & EMAGGED) + data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FLOOD, "selected" = mode == AALARM_MODE_FLOOD, "danger" = 1)) + + var/datum/tlv/selected + var/list/thresholds = list() + + selected = TLV["pressure"] + thresholds += list(list("name" = "Pressure", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) + + selected = TLV["temperature"] + thresholds += list(list("name" = "Temperature", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) + + for(var/gas_id in GLOB.meta_gas_names) + if(!(gas_id in TLV)) // We're not interested in this gas, it seems. + continue + selected = TLV[gas_id] + thresholds += list(list("name" = GLOB.meta_gas_names[gas_id], "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = gas_id, "val" = "max2", "selected" = selected.max2)) + + data["thresholds"] = thresholds + return data + +/obj/machinery/airalarm/ui_act(action, params) + if(..() || buildstage != 2) + return + if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled)) + return + var/device_id = params["id_tag"] + switch(action) + if("lock") + if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN)) + locked = !locked + . = TRUE + if("power", "toggle_filter", "widenet", "scrubbing") + send_signal(device_id, list("[action]" = params["val"]), usr) + . = TRUE + if("excheck") + send_signal(device_id, list("checks" = text2num(params["val"])^1), usr) + . = TRUE + if("incheck") + send_signal(device_id, list("checks" = text2num(params["val"])^2), usr) + . = TRUE + if("set_external_pressure", "set_internal_pressure") + var/area/A = get_area(src) + var/target = input("New target pressure:", name, A.air_vent_info[device_id][(action == "set_external_pressure" ? "external" : "internal")]) as num|null + if(!isnull(target) && !..()) + send_signal(device_id, list("[action]" = target), usr) + . = TRUE + if("reset_external_pressure") + send_signal(device_id, list("reset_external_pressure"), usr) + . = TRUE + if("reset_internal_pressure") + send_signal(device_id, list("reset_internal_pressure"), usr) + . = TRUE + if("threshold") + var/env = params["env"] + if(text2path(env)) + env = text2path(env) + + var/name = params["var"] + var/datum/tlv/tlv = TLV[env] + if(isnull(tlv)) + return + var/value = input("New [name] for [env]:", name, tlv.vars[name]) as num|null + if(!isnull(value) && !..()) + if(value < 0) + tlv.vars[name] = -1 + else + tlv.vars[name] = round(value, 0.01) + investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS) + . = TRUE + if("mode") + mode = text2num(params["mode"]) + investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS) + apply_mode() + . = TRUE + if("alarm") + var/area/A = get_area(src) + if(A.atmosalert(2, src)) + post_alert(2) + . = TRUE + if("reset") + var/area/A = get_area(src) + if(A.atmosalert(0, src)) + post_alert(0) + . = TRUE + update_icon() + +/obj/machinery/airalarm/proc/reset(wire) + switch(wire) + if(WIRE_POWER) + if(!wires.is_cut(WIRE_POWER)) + shorted = FALSE + update_icon() + if(WIRE_AI) + if(!wires.is_cut(WIRE_AI)) + aidisabled = FALSE + + +/obj/machinery/airalarm/proc/shock(mob/user, prb) + if((stat & (NOPOWER))) // unpowered, no shock + return 0 + if(!prob(prb)) + return 0 //you lucked out, no shock for you + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() //sparks always. + if (electrocute_mob(user, get_area(src), src, 1, TRUE)) + return 1 + else + return 0 + +/obj/machinery/airalarm/proc/refresh_all() + var/area/A = get_area(src) + for(var/id_tag in A.air_vent_names) + var/list/I = A.air_vent_info[id_tag] + if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) + continue + send_signal(id_tag, list("status")) + for(var/id_tag in A.air_scrub_names) + var/list/I = A.air_scrub_info[id_tag] + if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) + continue + send_signal(id_tag, list("status")) + +/obj/machinery/airalarm/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) + +/obj/machinery/airalarm/proc/send_signal(target, list/command, mob/user)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise + if(!radio_connection) + return 0 + + var/datum/signal/signal = new(command) + signal.data["tag"] = target + signal.data["sigtype"] = "command" + signal.data["user"] = user + radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) + + return 1 + +/obj/machinery/airalarm/proc/get_mode_name(mode_value) + switch(mode_value) + if(AALARM_MODE_SCRUBBING) + return "Filtering" + if(AALARM_MODE_CONTAMINATED) + return "Contaminated" + if(AALARM_MODE_VENTING) + return "Draught" + if(AALARM_MODE_REFILL) + return "Refill" + if(AALARM_MODE_PANIC) + return "Panic Siphon" + if(AALARM_MODE_REPLACEMENT) + return "Cycle" + if(AALARM_MODE_SIPHON) + return "Siphon" + if(AALARM_MODE_OFF) + return "Off" + if(AALARM_MODE_FLOOD) + return "Flood" + +/obj/machinery/airalarm/proc/apply_mode() + var/area/A = get_area(src) + switch(mode) + if(AALARM_MODE_SCRUBBING) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 1, + "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma), + "scrubbing" = 1, + "widenet" = 0, + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 1, + "checks" = 1, + "set_external_pressure" = ONE_ATMOSPHERE + )) + if(AALARM_MODE_CONTAMINATED) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 1, + "set_filters" = list( + /datum/gas/carbon_dioxide, + /datum/gas/miasma, + /datum/gas/plasma, + /datum/gas/water_vapor, + /datum/gas/hypernoblium, + /datum/gas/nitrous_oxide, + /datum/gas/nitryl, + /datum/gas/tritium, + /datum/gas/bz, + /datum/gas/stimulum, + /datum/gas/pluoxium + ), + "scrubbing" = 1, + "widenet" = 1, + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 1, + "checks" = 1, + "set_external_pressure" = ONE_ATMOSPHERE + )) + if(AALARM_MODE_VENTING) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 1, + "widenet" = 0, + "scrubbing" = 0 + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 1, + "checks" = 1, + "set_external_pressure" = ONE_ATMOSPHERE*2 + )) + if(AALARM_MODE_REFILL) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 1, + "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma), + "scrubbing" = 1, + "widenet" = 0, + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 1, + "checks" = 1, + "set_external_pressure" = ONE_ATMOSPHERE * 3 + )) + if(AALARM_MODE_PANIC, + AALARM_MODE_REPLACEMENT) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 1, + "widenet" = 1, + "scrubbing" = 0 + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 0 + )) + if(AALARM_MODE_SIPHON) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 1, + "widenet" = 0, + "scrubbing" = 0 + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 0 + )) + + if(AALARM_MODE_OFF) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 0 + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 0 + )) + if(AALARM_MODE_FLOOD) + for(var/device_id in A.air_scrub_names) + send_signal(device_id, list( + "power" = 0 + )) + for(var/device_id in A.air_vent_names) + send_signal(device_id, list( + "power" = 1, + "checks" = 2, + "set_internal_pressure" = 0 + )) + +/obj/machinery/airalarm/update_icon() + set_light(0) + cut_overlays() + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) + if(stat & NOPOWER) + icon_state = "alarm0" + return + + if(stat & BROKEN) + icon_state = "alarmx" + return + + if(panel_open) + switch(buildstage) + if(2) + icon_state = "alarmx" + if(1) + icon_state = "alarm_b2" + if(0) + icon_state = "alarm_b1" + return + + icon_state = "alarm1" + var/overlay_state = AALARM_OVERLAY_OFF + var/area/A = get_area(src) + switch(max(danger_level, A.atmosalm)) + if(0) + add_overlay(AALARM_OVERLAY_GREEN) + overlay_state = AALARM_OVERLAY_GREEN + light_color = LIGHT_COLOR_PALEBLUE + set_light(brightness_on) + if(1) + add_overlay(AALARM_OVERLAY_WARN) + overlay_state = AALARM_OVERLAY_WARN + light_color = LIGHT_COLOR_LAVA + set_light(brightness_on) + if(2) + add_overlay(AALARM_OVERLAY_DANGER) + overlay_state = AALARM_OVERLAY_DANGER + light_color = LIGHT_COLOR_RED + set_light(brightness_on) + + SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + update_light() + +/obj/machinery/airalarm/process() + if((stat & (NOPOWER|BROKEN)) || shorted) + return + + var/turf/location = get_turf(src) + if(!location) + return + + var/datum/tlv/cur_tlv + + var/datum/gas_mixture/environment = location.return_air() + var/list/env_gases = environment.gases + var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume + + cur_tlv = TLV["pressure"] + var/environment_pressure = environment.return_pressure() + var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) + + cur_tlv = TLV["temperature"] + var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) + + var/gas_dangerlevel = 0 + for(var/gas_id in env_gases) + if(!(gas_id in TLV)) // We're not interested in this gas, it seems. + continue + cur_tlv = TLV[gas_id] + gas_dangerlevel = max(gas_dangerlevel, cur_tlv.get_danger_level(env_gases[gas_id] * partial_pressure)) + + GAS_GARBAGE_COLLECT(environment.gases) + + var/old_danger_level = danger_level + danger_level = max(pressure_dangerlevel, temperature_dangerlevel, gas_dangerlevel) + + if(old_danger_level != danger_level) + apply_danger_level() + if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05) + mode = AALARM_MODE_SCRUBBING + apply_mode() + + return + + +/obj/machinery/airalarm/proc/post_alert(alert_level) + var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) + + if(!frequency) + return + + var/datum/signal/alert_signal = new(list( + "zone" = get_area_name(src), + "type" = "Atmospheric" + )) + if(alert_level==2) + alert_signal.data["alert"] = "severe" + else if (alert_level==1) + alert_signal.data["alert"] = "minor" + else if (alert_level==0) + alert_signal.data["alert"] = "clear" + + frequency.post_signal(src, alert_signal, range = -1) + +/obj/machinery/airalarm/proc/apply_danger_level() + var/area/A = get_area(src) + + var/new_area_danger_level = 0 + for(var/obj/machinery/airalarm/AA in A) + if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted) + new_area_danger_level = max(new_area_danger_level,AA.danger_level) + if(A.atmosalert(new_area_danger_level,src)) //if area was in normal state or if area was in alert state + post_alert(new_area_danger_level) + + update_icon() + +/obj/machinery/airalarm/attackby(obj/item/W, mob/user, params) + switch(buildstage) + if(2) + if(istype(W, /obj/item/wirecutters) && panel_open && wires.is_all_cut()) + W.play_tool_sound(src) + to_chat(user, "You cut the final wires.") + new /obj/item/stack/cable_coil(loc, 5) + buildstage = 1 + update_icon() + return + else if(istype(W, /obj/item/screwdriver)) // Opening that Air Alarm up. + W.play_tool_sound(src) + panel_open = !panel_open + to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"].") + update_icon() + return + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))// trying to unlock the interface with an ID card + togglelock(user) + else if(panel_open && is_wire_tool(W)) + wires.interact(user) + return + if(1) + if(istype(W, /obj/item/crowbar)) + user.visible_message("[user.name] removes the electronics from [src.name].",\ + "You start prying out the circuit...") + W.play_tool_sound(src) + if (W.use_tool(src, user, 20)) + if (buildstage == 1) + to_chat(user, "You remove the air alarm electronics.") + new /obj/item/electronics/airalarm( src.loc ) + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + buildstage = 0 + update_icon() + return + + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/cable = W + if(cable.get_amount() < 5) + to_chat(user, "You need five lengths of cable to wire the air alarm!") + return + user.visible_message("[user.name] wires the air alarm.", \ + "You start wiring the air alarm...") + if (do_after(user, 20, target = src)) + if (cable.get_amount() >= 5 && buildstage == 1) + cable.use(5) + to_chat(user, "You wire the air alarm.") + wires.repair() + aidisabled = 0 + locked = FALSE + mode = 1 + shorted = 0 + post_alert(0) + buildstage = 2 + update_icon() + return + if(0) + if(istype(W, /obj/item/electronics/airalarm)) + if(user.temporarilyRemoveItemFromInventory(W)) + to_chat(user, "You insert the circuit.") + buildstage = 1 + update_icon() + qdel(W) + return + + if(istype(W, /obj/item/electroadaptive_pseudocircuit)) + var/obj/item/electroadaptive_pseudocircuit/P = W + if(!P.adapt_circuit(user, 25)) + return + user.visible_message("[user] fabricates a circuit and places it into [src].", \ + "You adapt an air alarm circuit and slot it into the assembly.") + buildstage = 1 + update_icon() + return + + if(istype(W, /obj/item/wrench)) + to_chat(user, "You detach \the [src] from the wall.") + W.play_tool_sound(src) + new /obj/item/wallframe/airalarm( user.loc ) + qdel(src) + return + + return ..() + +/obj/machinery/airalarm/AltClick(mob/user) + ..() + if(!user.canUseTopic(src, !issilicon(user)) || !isturf(loc)) + return + else + togglelock(user) + +/obj/machinery/airalarm/proc/togglelock(mob/living/user) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "It does nothing!") + else + if(src.allowed(usr) && !wires.is_cut(WIRE_IDSCAN)) + locked = !locked + updateUsrDialog() + to_chat(user, "You [ locked ? "lock" : "unlock"] the air alarm interface.") + else + to_chat(user, "Access denied.") + return + +/obj/machinery/airalarm/power_change() + ..() + if(stat & NOPOWER) + set_light(0) + update_icon() + +/obj/machinery/airalarm/emag_act(mob/user) + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED + visible_message("Sparks fly out of [src]!", "You emag [src], disabling its safeties.") + playsound(src, "sparks", 50, 1) + +/obj/machinery/airalarm/obj_break(damage_flag) + ..() + update_icon() + set_light(0) + +/obj/machinery/airalarm/deconstruct(disassembled = TRUE) + if(!(flags_1 & NODECONSTRUCT_1)) + new /obj/item/stack/sheet/metal(loc, 2) + var/obj/item/I = new /obj/item/electronics/airalarm(loc) + if(!disassembled) + I.obj_integrity = I.max_integrity * 0.5 + new /obj/item/stack/cable_coil(loc, 3) + qdel(src) + +#undef AALARM_MODE_SCRUBBING +#undef AALARM_MODE_VENTING +#undef AALARM_MODE_PANIC +#undef AALARM_MODE_REPLACEMENT +#undef AALARM_MODE_OFF +#undef AALARM_MODE_FLOOD +#undef AALARM_MODE_SIPHON +#undef AALARM_MODE_CONTAMINATED +#undef AALARM_MODE_REFILL +#undef AALARM_REPORT_TIMEOUT diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm index e7df188f70..abdc3ada57 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm @@ -27,7 +27,26 @@ Thus, the two variables affect pump operation are set in New(): construction_type = /obj/item/pipe/directional pipe_state = "pump" - + +/obj/machinery/atmospherics/components/binary/pump/CtrlClick(mob/user) + var/area/A = get_area(src) + var/turf/T = get_turf(src) + if(user.canUseTopic(src, BE_CLOSE, FALSE,)) + on = !on + update_icon() + investigate_log("Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + return ..() + +/obj/machinery/atmospherics/components/binary/pump/AltClick(mob/user) + var/area/A = get_area(src) + var/turf/T = get_turf(src) + if(user.canUseTopic(src, BE_CLOSE, FALSE,)) + target_pressure = MAX_OUTPUT_PRESSURE + to_chat(user,"You maximize the pressure on the [src].") + investigate_log("Pump, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + /obj/machinery/atmospherics/components/binary/pump/layer1 piping_layer = PIPING_LAYER_MIN pixel_x = -PIPING_LAYER_P_X diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm index 46b646cabf..2efff16301 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm @@ -28,6 +28,16 @@ Thus, the two variables affect pump operation are set in New(): construction_type = /obj/item/pipe/directional pipe_state = "volumepump" +/obj/machinery/atmospherics/components/binary/volume_pump/CtrlClick(mob/user) + var/area/A = get_area(src) + var/turf/T = get_turf(src) + if(user.canUseTopic(src, BE_CLOSE, FALSE,)) + on = !on + update_icon() + investigate_log("Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + return ..() + /obj/machinery/atmospherics/components/binary/volume_pump/layer1 piping_layer = PIPING_LAYER_MIN pixel_x = -PIPING_LAYER_P_X @@ -192,4 +202,4 @@ Thus, the two variables affect pump operation are set in New(): else investigate_log("Pump, [src.name], was unwrenched by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) message_admins("Pump, [src.name], was unwrenched by [ADMIN_LOOKUPFLW(user)] at [A]") - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm index 4101deb00c..52e8d9eb83 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm @@ -12,6 +12,25 @@ construction_type = /obj/item/pipe/trinary/flippable pipe_state = "filter" +/obj/machinery/atmospherics/components/trinary/filter/CtrlClick(mob/user) + var/area/A = get_area(src) + var/turf/T = get_turf(src) + if(user.canUseTopic(src, BE_CLOSE, FALSE,)) + on = !on + update_icon() + investigate_log("Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + return ..() + +/obj/machinery/atmospherics/components/trinary/filter/AltClick(mob/user) + var/area/A = get_area(src) + var/turf/T = get_turf(src) + if(user.canUseTopic(src, BE_CLOSE, FALSE,)) + target_pressure = MAX_OUTPUT_PRESSURE + to_chat(user,"You maximize the pressure on the [src].") + investigate_log("Pump, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + /obj/machinery/atmospherics/components/trinary/filter/layer1 piping_layer = PIPING_LAYER_MIN pixel_x = -PIPING_LAYER_P_X @@ -159,11 +178,10 @@ var/datum/gas_mixture/filtered_out = new filtered_out.temperature = removed.temperature - filtered_out.add_gas(filter_type) - filtered_out.gases[filter_type][MOLES] = removed.gases[filter_type][MOLES] + filtered_out.gases[filter_type] = removed.gases[filter_type] - removed.gases[filter_type][MOLES] = 0 - removed.garbage_collect() + removed.gases[filter_type] = 0 + GAS_GARBAGE_COLLECT(removed.gases) var/datum/gas_mixture/target = (air2.return_pressure() < target_pressure ? air2 : air1) //if there's no room for the filtered gas; just leave it in air1 target.merge(filtered_out) @@ -191,9 +209,8 @@ data["filter_types"] = list() data["filter_types"] += list(list("name" = "Nothing", "path" = "", "selected" = !filter_type)) - for(var/path in GLOB.meta_gas_info) - var/list/gas = GLOB.meta_gas_info[path] - data["filter_types"] += list(list("name" = gas[META_GAS_NAME], "id" = gas[META_GAS_ID], "selected" = (path == gas_id2path(filter_type)))) + for(var/path in GLOB.meta_gas_ids) + data["filter_types"] += list(list("name" = GLOB.meta_gas_names[path], "id" = GLOB.meta_gas_ids[path], "selected" = (path == gas_id2path(filter_type)))) return data @@ -224,9 +241,9 @@ filter_type = null var/filter_name = "nothing" var/gas = gas_id2path(params["mode"]) - if(gas in GLOB.meta_gas_info) + if(gas in GLOB.meta_gas_names) filter_type = gas - filter_name = GLOB.meta_gas_info[gas][META_GAS_NAME] + filter_name = GLOB.meta_gas_names[gas] investigate_log("was set to filter [filter_name] by [key_name(usr)]", INVESTIGATE_ATMOS) . = TRUE update_icon() diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm index 4dd2972526..ba9fdf31af 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm @@ -14,7 +14,25 @@ pipe_state = "mixer" //node 3 is the outlet, nodes 1 & 2 are intakes - +/obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user) + var/area/A = get_area(src) + var/turf/T = get_turf(src) + if(user.canUseTopic(src, BE_CLOSE, FALSE,)) + on = !on + update_icon() + investigate_log("Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + return ..() + +/obj/machinery/atmospherics/components/trinary/mixer/AltClick(mob/user) + var/area/A = get_area(src) + var/turf/T = get_turf(src) + if(user.canUseTopic(src, BE_CLOSE, FALSE,)) + target_pressure = MAX_OUTPUT_PRESSURE + to_chat(user,"You maximize the pressure on the [src].") + investigate_log("Pump, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") + /obj/machinery/atmospherics/components/trinary/mixer/layer1 piping_layer = PIPING_LAYER_MIN pixel_x = -PIPING_LAYER_P_X diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 9b69b2fa6f..bfe60cd573 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -183,8 +183,8 @@ if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic. beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents. beaker.reagents.reaction(occupant, VAPOR) - air1.gases[/datum/gas/oxygen][MOLES] -= max(0,air1.gases[/datum/gas/oxygen][MOLES] - 2 / efficiency) //Let's use gas for this - air1.garbage_collect() + air1.gases[/datum/gas/oxygen] -= max(0,air1.gases[/datum/gas/oxygen] - 2 / efficiency) //Let's use gas for this + GAS_GARBAGE_COLLECT(air1.gases) if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker). reagent_transfer = 0 @@ -198,7 +198,7 @@ var/datum/gas_mixture/air1 = airs[1] - if(!nodes[1] || !airs[1] || !air1.gases.len || air1.gases[/datum/gas/oxygen][MOLES] < 5) // Turn off if the machine won't work. + if(!nodes[1] || !airs[1] || !air1.gases.len || air1.gases[/datum/gas/oxygen] < 5) // Turn off if the machine won't work. on = FALSE update_icon() return @@ -220,8 +220,8 @@ air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB) mob_occupant.adjust_bodytemperature(heat / heat_capacity, TCMB) - air1.gases[/datum/gas/oxygen][MOLES] = max(0,air1.gases[/datum/gas/oxygen][MOLES] - 0.5 / efficiency) // Magically consume gas? Why not, we run on cryo magic. - air1.garbage_collect() + air1.gases[/datum/gas/oxygen] = max(0,air1.gases[/datum/gas/oxygen] - 0.5 / efficiency) // Magically consume gas? Why not, we run on cryo magic. + GAS_GARBAGE_COLLECT(air1.gases) /obj/machinery/atmospherics/components/unary/cryo_cell/power_change() ..() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm index 1a9d1cff30..4f16406456 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm @@ -17,9 +17,8 @@ air_contents.volume = volume air_contents.temperature = T20C if(gas_type) - air_contents.assert_gas(gas_type) - air_contents.gases[gas_type][MOLES] = AIR_CONTENTS - name = "[name] ([air_contents.gases[gas_type][GAS_META][META_GAS_NAME]])" + air_contents.gases[gas_type] = AIR_CONTENTS + name = "[name] ([GLOB.meta_gas_names[gas_type]])" /obj/machinery/atmospherics/components/unary/tank/carbon_dioxide gas_type = /datum/gas/carbon_dioxide @@ -44,6 +43,5 @@ /obj/machinery/atmospherics/components/unary/tank/air/New() ..() var/datum/gas_mixture/air_contents = airs[1] - air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrogen) - air_contents.gases[/datum/gas/oxygen][MOLES] = AIR_CONTENTS * 0.2 - air_contents.gases[/datum/gas/nitrogen][MOLES] = AIR_CONTENTS * 0.8 + air_contents.gases[/datum/gas/oxygen] = AIR_CONTENTS * 0.2 + air_contents.gases[/datum/gas/nitrogen] = AIR_CONTENTS * 0.8 diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm index ee819d4f74..7f40630a86 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -1,330 +1,328 @@ -#define SIPHONING 0 -#define SCRUBBING 1 - -/obj/machinery/atmospherics/components/unary/vent_scrubber - name = "air scrubber" - desc = "Has a valve and pump attached to it." - icon_state = "scrub_map" - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 60 - can_unwrench = TRUE - welded = FALSE - level = 1 - layer = GAS_SCRUBBER_LAYER - - var/id_tag = null - var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing - - var/filter_types = list(/datum/gas/carbon_dioxide) - var/volume_rate = 200 - var/widenet = 0 //is this scrubber acting on the 3x3 area around it. - var/list/turf/adjacent_turfs = list() - - var/frequency = FREQ_ATMOS_CONTROL - var/datum/radio_frequency/radio_connection - var/radio_filter_out - var/radio_filter_in - - pipe_state = "scrubber" - -/obj/machinery/atmospherics/components/unary/vent_scrubber/layer1 - piping_layer = PIPING_LAYER_MIN - pixel_x = -PIPING_LAYER_P_X - pixel_y = -PIPING_LAYER_P_Y - -/obj/machinery/atmospherics/components/unary/vent_scrubber/layer3 - piping_layer = PIPING_LAYER_MAX - pixel_x = PIPING_LAYER_P_X - pixel_y = PIPING_LAYER_P_Y - -/obj/machinery/atmospherics/components/unary/vent_scrubber/New() - ..() - if(!id_tag) - id_tag = assign_uid_vents() - - for(var/f in filter_types) - if(istext(f)) - filter_types -= f - filter_types += gas_id2path(f) - -/obj/machinery/atmospherics/components/unary/vent_scrubber/on - on = TRUE - icon_state = "scrub_map_on" - -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer1 - piping_layer = PIPING_LAYER_MIN - pixel_x = -PIPING_LAYER_P_X - pixel_y = -PIPING_LAYER_P_Y - -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer3 - piping_layer = PIPING_LAYER_MAX - pixel_x = PIPING_LAYER_P_X - pixel_y = PIPING_LAYER_P_Y - -/obj/machinery/atmospherics/components/unary/vent_scrubber/Destroy() - var/area/A = get_area(src) - if (A) - A.air_scrub_names -= id_tag - A.air_scrub_info -= id_tag - - SSradio.remove_object(src,frequency) - radio_connection = null - adjacent_turfs.Cut() - return ..() - -/obj/machinery/atmospherics/components/unary/vent_scrubber/auto_use_power() - if(!on || welded || !is_operational() || !powered(power_channel)) - return FALSE - - var/amount = idle_power_usage - - if(scrubbing & SCRUBBING) - amount += idle_power_usage * length(filter_types) - else //scrubbing == SIPHONING - amount = active_power_usage - - if(widenet) - amount += amount * (adjacent_turfs.len * (adjacent_turfs.len / 2)) - use_power(amount, power_channel) - return TRUE - -/obj/machinery/atmospherics/components/unary/vent_scrubber/update_icon_nopipes() - cut_overlays() - if(showpipe) - add_overlay(getpipeimage(icon, "scrub_cap", initialize_directions)) - - if(welded) - icon_state = "scrub_welded" - return - - if(!nodes[1] || !on || !is_operational()) - icon_state = "scrub_off" - return - - if(scrubbing & SCRUBBING) - if(widenet) - icon_state = "scrub_wide" - else - icon_state = "scrub_on" - else //scrubbing == SIPHONING - icon_state = "scrub_purge" - -/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, radio_filter_in) - -/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/broadcast_status() - if(!radio_connection) - return FALSE - - var/list/f_types = list() - for(var/path in GLOB.meta_gas_info) - var/list/gas = GLOB.meta_gas_info[path] - f_types += list(list("gas_id" = gas[META_GAS_ID], "gas_name" = gas[META_GAS_NAME], "enabled" = (path in filter_types))) - - var/datum/signal/signal = new(list( - "tag" = id_tag, - "frequency" = frequency, - "device" = "VS", - "timestamp" = world.time, - "power" = on, - "scrubbing" = scrubbing, - "widenet" = widenet, - "filter_types" = f_types, - "sigtype" = "status" - )) - - var/area/A = get_area(src) - if(!A.air_scrub_names[id_tag]) - name = "\improper [A.name] air scrubber #[A.air_scrub_names.len + 1]" - A.air_scrub_names[id_tag] = name - - A.air_scrub_info[id_tag] = signal.data - radio_connection.post_signal(src, signal, radio_filter_out) - - return TRUE - -/obj/machinery/atmospherics/components/unary/vent_scrubber/atmosinit() - radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null - radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null - if(frequency) - set_frequency(frequency) - broadcast_status() - check_turfs() - ..() - -/obj/machinery/atmospherics/components/unary/vent_scrubber/process_atmos() - ..() - if(welded || !is_operational()) - return FALSE - if(!nodes[1] || !on) - on = FALSE - return FALSE - scrub(loc) - if(widenet) - for(var/turf/tile in adjacent_turfs) - scrub(tile) - return TRUE - -/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/scrub(var/turf/tile) - if(!istype(tile)) - return FALSE - var/datum/gas_mixture/environment = tile.return_air() - var/datum/gas_mixture/air_contents = airs[1] - var/list/env_gases = environment.gases - - if(air_contents.return_pressure() >= 50*ONE_ATMOSPHERE) - return FALSE - - if(scrubbing & SCRUBBING) - if(length(env_gases & filter_types)) - var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles() - - //Take a gas sample - var/datum/gas_mixture/removed = tile.remove_air(transfer_moles) - - //Nothing left to remove from the tile - if(isnull(removed)) - return FALSE - - var/list/removed_gases = removed.gases - - //Filter it - var/datum/gas_mixture/filtered_out = new - var/list/filtered_gases = filtered_out.gases - filtered_out.temperature = removed.temperature - - for(var/gas in filter_types & removed_gases) - filtered_out.add_gas(gas) - filtered_gases[gas][MOLES] = removed_gases[gas][MOLES] - removed_gases[gas][MOLES] = 0 - - removed.garbage_collect() - - //Remix the resulting gases - air_contents.merge(filtered_out) - tile.assume_air(removed) - tile.air_update_turf() - - else //Just siphoning all air - - var/transfer_moles = environment.total_moles()*(volume_rate/environment.volume) - - var/datum/gas_mixture/removed = tile.remove_air(transfer_moles) - - air_contents.merge(removed) - tile.air_update_turf() - - update_parents() - - return TRUE - -//There is no easy way for an object to be notified of changes to atmos can pass flags -// So we check every machinery process (2 seconds) -/obj/machinery/atmospherics/components/unary/vent_scrubber/process() - if(widenet) - check_turfs() - -//we populate a list of turfs with nonatmos-blocked cardinal turfs AND -// diagonal turfs that can share atmos with *both* of the cardinal turfs - -/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/check_turfs() - adjacent_turfs.Cut() - var/turf/T = get_turf(src) - if(istype(T)) - adjacent_turfs = T.GetAtmosAdjacentTurfs(alldir = 1) - -/obj/machinery/atmospherics/components/unary/vent_scrubber/receive_signal(datum/signal/signal) - if(!is_operational() || !signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) - return 0 - - var/mob/signal_sender = signal.data["user"] - - if("power" in signal.data) - on = text2num(signal.data["power"]) - if("power_toggle" in signal.data) - on = !on - - if("widenet" in signal.data) - widenet = text2num(signal.data["widenet"]) - if("toggle_widenet" in signal.data) - widenet = !widenet - - var/old_scrubbing = scrubbing - if("scrubbing" in signal.data) - scrubbing = text2num(signal.data["scrubbing"]) - if("toggle_scrubbing" in signal.data) - scrubbing = !scrubbing - if(scrubbing != old_scrubbing) - investigate_log(" was toggled to [scrubbing ? "scrubbing" : "siphon"] mode by [key_name(signal_sender)]",INVESTIGATE_ATMOS) - - if("toggle_filter" in signal.data) - filter_types ^= gas_id2path(signal.data["toggle_filter"]) - - if("set_filters" in signal.data) - filter_types = list() - for(var/gas in signal.data["set_filters"]) - filter_types += gas_id2path(gas) - - if("init" in signal.data) - name = signal.data["init"] - return - - if("status" in signal.data) - broadcast_status() - return //do not update_icon - - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/components/unary/vent_scrubber/power_change() - ..() - update_icon_nopipes() - -/obj/machinery/atmospherics/components/unary/vent_scrubber/welder_act(mob/living/user, obj/item/I) - if(!I.tool_start_check(user, amount=0)) - return TRUE - to_chat(user, "Now welding the scrubber.") - if(I.use_tool(src, user, 20, volume=50)) - if(!welded) - user.visible_message("[user] welds the scrubber shut.","You weld the scrubber shut.", "You hear welding.") - welded = TRUE - else - user.visible_message("[user] unwelds the scrubber.", "You unweld the scrubber.", "You hear welding.") - welded = FALSE - update_icon() - pipe_vision_img = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) - pipe_vision_img.plane = ABOVE_HUD_PLANE - return TRUE - -/obj/machinery/atmospherics/components/unary/vent_scrubber/can_unwrench(mob/user) - . = ..() - if(. && on && is_operational()) - to_chat(user, "You cannot unwrench [src], turn it off first!") - return FALSE - -/obj/machinery/atmospherics/components/unary/vent_scrubber/examine(mob/user) - ..() - if(welded) - to_chat(user, "It seems welded shut.") - -/obj/machinery/atmospherics/components/unary/vent_scrubber/can_crawl_through() - return !welded - -/obj/machinery/atmospherics/components/unary/vent_scrubber/attack_alien(mob/user) - if(!welded || !(do_after(user, 20, target = src))) - return - user.visible_message("[user] furiously claws at [src]!", "You manage to clear away the stuff blocking the scrubber.", "You hear loud scraping noises.") - welded = FALSE - update_icon() - pipe_vision_img = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) - pipe_vision_img.plane = ABOVE_HUD_PLANE - playsound(loc, 'sound/weapons/bladeslice.ogg', 100, 1) - - - -#undef SIPHONING -#undef SCRUBBING +#define SIPHONING 0 +#define SCRUBBING 1 + +/obj/machinery/atmospherics/components/unary/vent_scrubber + name = "air scrubber" + desc = "Has a valve and pump attached to it." + icon_state = "scrub_map" + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 60 + can_unwrench = TRUE + welded = FALSE + level = 1 + layer = GAS_SCRUBBER_LAYER + + var/id_tag = null + var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing + + var/filter_types = list(/datum/gas/carbon_dioxide) + var/volume_rate = 200 + var/widenet = 0 //is this scrubber acting on the 3x3 area around it. + var/list/turf/adjacent_turfs = list() + + var/frequency = FREQ_ATMOS_CONTROL + var/datum/radio_frequency/radio_connection + var/radio_filter_out + var/radio_filter_in + + pipe_state = "scrubber" + +/obj/machinery/atmospherics/components/unary/vent_scrubber/layer1 + piping_layer = PIPING_LAYER_MIN + pixel_x = -PIPING_LAYER_P_X + pixel_y = -PIPING_LAYER_P_Y + +/obj/machinery/atmospherics/components/unary/vent_scrubber/layer3 + piping_layer = PIPING_LAYER_MAX + pixel_x = PIPING_LAYER_P_X + pixel_y = PIPING_LAYER_P_Y + +/obj/machinery/atmospherics/components/unary/vent_scrubber/New() + ..() + if(!id_tag) + id_tag = assign_uid_vents() + + for(var/f in filter_types) + if(istext(f)) + filter_types -= f + filter_types += gas_id2path(f) + +/obj/machinery/atmospherics/components/unary/vent_scrubber/on + on = TRUE + icon_state = "scrub_map_on" + +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer1 + piping_layer = PIPING_LAYER_MIN + pixel_x = -PIPING_LAYER_P_X + pixel_y = -PIPING_LAYER_P_Y + +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer3 + piping_layer = PIPING_LAYER_MAX + pixel_x = PIPING_LAYER_P_X + pixel_y = PIPING_LAYER_P_Y + +/obj/machinery/atmospherics/components/unary/vent_scrubber/Destroy() + var/area/A = get_area(src) + if (A) + A.air_scrub_names -= id_tag + A.air_scrub_info -= id_tag + + SSradio.remove_object(src,frequency) + radio_connection = null + adjacent_turfs.Cut() + return ..() + +/obj/machinery/atmospherics/components/unary/vent_scrubber/auto_use_power() + if(!on || welded || !is_operational() || !powered(power_channel)) + return FALSE + + var/amount = idle_power_usage + + if(scrubbing & SCRUBBING) + amount += idle_power_usage * length(filter_types) + else //scrubbing == SIPHONING + amount = active_power_usage + + if(widenet) + amount += amount * (adjacent_turfs.len * (adjacent_turfs.len / 2)) + use_power(amount, power_channel) + return TRUE + +/obj/machinery/atmospherics/components/unary/vent_scrubber/update_icon_nopipes() + cut_overlays() + if(showpipe) + add_overlay(getpipeimage(icon, "scrub_cap", initialize_directions)) + + if(welded) + icon_state = "scrub_welded" + return + + if(!nodes[1] || !on || !is_operational()) + icon_state = "scrub_off" + return + + if(scrubbing & SCRUBBING) + if(widenet) + icon_state = "scrub_wide" + else + icon_state = "scrub_on" + else //scrubbing == SIPHONING + icon_state = "scrub_purge" + +/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, radio_filter_in) + +/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/broadcast_status() + if(!radio_connection) + return FALSE + + var/list/f_types = list() + for(var/path in GLOB.meta_gas_ids) + f_types += list(list("gas_id" = GLOB.meta_gas_ids[path], "gas_name" = GLOB.meta_gas_names[path], "enabled" = (path in filter_types))) + + var/datum/signal/signal = new(list( + "tag" = id_tag, + "frequency" = frequency, + "device" = "VS", + "timestamp" = world.time, + "power" = on, + "scrubbing" = scrubbing, + "widenet" = widenet, + "filter_types" = f_types, + "sigtype" = "status" + )) + + var/area/A = get_area(src) + if(!A.air_scrub_names[id_tag]) + name = "\improper [A.name] air scrubber #[A.air_scrub_names.len + 1]" + A.air_scrub_names[id_tag] = name + + A.air_scrub_info[id_tag] = signal.data + radio_connection.post_signal(src, signal, radio_filter_out) + + return TRUE + +/obj/machinery/atmospherics/components/unary/vent_scrubber/atmosinit() + radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null + radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null + if(frequency) + set_frequency(frequency) + broadcast_status() + check_turfs() + ..() + +/obj/machinery/atmospherics/components/unary/vent_scrubber/process_atmos() + ..() + if(welded || !is_operational()) + return FALSE + if(!nodes[1] || !on) + on = FALSE + return FALSE + scrub(loc) + if(widenet) + for(var/turf/tile in adjacent_turfs) + scrub(tile) + return TRUE + +/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/scrub(var/turf/tile) + if(!istype(tile)) + return FALSE + var/datum/gas_mixture/environment = tile.return_air() + var/datum/gas_mixture/air_contents = airs[1] + var/list/env_gases = environment.gases + + if(air_contents.return_pressure() >= 50*ONE_ATMOSPHERE) + return FALSE + + if(scrubbing & SCRUBBING) + if(length(env_gases & filter_types)) + var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles() + + //Take a gas sample + var/datum/gas_mixture/removed = tile.remove_air(transfer_moles) + + //Nothing left to remove from the tile + if(isnull(removed)) + return FALSE + + var/list/removed_gases = removed.gases + + //Filter it + var/datum/gas_mixture/filtered_out = new + var/list/filtered_gases = filtered_out.gases + filtered_out.temperature = removed.temperature + + for(var/gas in filter_types & removed_gases) + filtered_gases[gas] = removed_gases[gas] + removed_gases[gas] = 0 + + GAS_GARBAGE_COLLECT(removed.gases) + + //Remix the resulting gases + air_contents.merge(filtered_out) + tile.assume_air(removed) + tile.air_update_turf() + + else //Just siphoning all air + + var/transfer_moles = environment.total_moles()*(volume_rate/environment.volume) + + var/datum/gas_mixture/removed = tile.remove_air(transfer_moles) + + air_contents.merge(removed) + tile.air_update_turf() + + update_parents() + + return TRUE + +//There is no easy way for an object to be notified of changes to atmos can pass flags +// So we check every machinery process (2 seconds) +/obj/machinery/atmospherics/components/unary/vent_scrubber/process() + if(widenet) + check_turfs() + +//we populate a list of turfs with nonatmos-blocked cardinal turfs AND +// diagonal turfs that can share atmos with *both* of the cardinal turfs + +/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/check_turfs() + adjacent_turfs.Cut() + var/turf/T = get_turf(src) + if(istype(T)) + adjacent_turfs = T.GetAtmosAdjacentTurfs(alldir = 1) + +/obj/machinery/atmospherics/components/unary/vent_scrubber/receive_signal(datum/signal/signal) + if(!is_operational() || !signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) + return 0 + + var/mob/signal_sender = signal.data["user"] + + if("power" in signal.data) + on = text2num(signal.data["power"]) + if("power_toggle" in signal.data) + on = !on + + if("widenet" in signal.data) + widenet = text2num(signal.data["widenet"]) + if("toggle_widenet" in signal.data) + widenet = !widenet + + var/old_scrubbing = scrubbing + if("scrubbing" in signal.data) + scrubbing = text2num(signal.data["scrubbing"]) + if("toggle_scrubbing" in signal.data) + scrubbing = !scrubbing + if(scrubbing != old_scrubbing) + investigate_log(" was toggled to [scrubbing ? "scrubbing" : "siphon"] mode by [key_name(signal_sender)]",INVESTIGATE_ATMOS) + + if("toggle_filter" in signal.data) + filter_types ^= gas_id2path(signal.data["toggle_filter"]) + + if("set_filters" in signal.data) + filter_types = list() + for(var/gas in signal.data["set_filters"]) + filter_types += gas_id2path(gas) + + if("init" in signal.data) + name = signal.data["init"] + return + + if("status" in signal.data) + broadcast_status() + return //do not update_icon + + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/components/unary/vent_scrubber/power_change() + ..() + update_icon_nopipes() + +/obj/machinery/atmospherics/components/unary/vent_scrubber/welder_act(mob/living/user, obj/item/I) + if(!I.tool_start_check(user, amount=0)) + return TRUE + to_chat(user, "Now welding the scrubber.") + if(I.use_tool(src, user, 20, volume=50)) + if(!welded) + user.visible_message("[user] welds the scrubber shut.","You weld the scrubber shut.", "You hear welding.") + welded = TRUE + else + user.visible_message("[user] unwelds the scrubber.", "You unweld the scrubber.", "You hear welding.") + welded = FALSE + update_icon() + pipe_vision_img = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) + pipe_vision_img.plane = ABOVE_HUD_PLANE + return TRUE + +/obj/machinery/atmospherics/components/unary/vent_scrubber/can_unwrench(mob/user) + . = ..() + if(. && on && is_operational()) + to_chat(user, "You cannot unwrench [src], turn it off first!") + return FALSE + +/obj/machinery/atmospherics/components/unary/vent_scrubber/examine(mob/user) + ..() + if(welded) + to_chat(user, "It seems welded shut.") + +/obj/machinery/atmospherics/components/unary/vent_scrubber/can_crawl_through() + return !welded + +/obj/machinery/atmospherics/components/unary/vent_scrubber/attack_alien(mob/user) + if(!welded || !(do_after(user, 20, target = src))) + return + user.visible_message("[user] furiously claws at [src]!", "You manage to clear away the stuff blocking the scrubber.", "You hear loud scraping noises.") + welded = FALSE + update_icon() + pipe_vision_img = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) + pipe_vision_img.plane = ABOVE_HUD_PLANE + playsound(loc, 'sound/weapons/bladeslice.ogg', 100, 1) + + + +#undef SIPHONING +#undef SCRUBBING diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm index c4fa387ab0..38178a4339 100644 --- a/code/modules/atmospherics/machinery/datum_pipeline.dm +++ b/code/modules/atmospherics/machinery/datum_pipeline.dm @@ -145,7 +145,7 @@ var/member_gases = member.air_temporary.gases for(var/id in member_gases) - member_gases[id][MOLES] *= member.volume/air.volume + member_gases[id] *= member.volume/air.volume member.air_temporary.temperature = air.temperature @@ -254,4 +254,4 @@ G.copy_from(total_gas_mixture) var/list/G_gases = G.gases for(var/id in G_gases) - G_gases[id][MOLES] *= G.volume/total_gas_mixture.volume + G_gases[id] *= G.volume/total_gas_mixture.volume diff --git a/code/modules/atmospherics/machinery/other/miner.dm b/code/modules/atmospherics/machinery/other/miner.dm index 20251418bc..adb17b4e94 100644 --- a/code/modules/atmospherics/machinery/other/miner.dm +++ b/code/modules/atmospherics/machinery/other/miner.dm @@ -131,8 +131,7 @@ if(!isopenturf(O)) return FALSE var/datum/gas_mixture/merger = new - merger.assert_gas(spawn_id) - merger.gases[spawn_id][MOLES] = (spawn_mol) + merger.gases[spawn_id] = (spawn_mol) merger.temperature = spawn_temp O.assume_air(merger) O.air_update_turf(TRUE) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index d23a5df958..bad2b85bfe 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -204,16 +204,14 @@ /obj/machinery/portable_atmospherics/canister/proc/create_gas() if(gas_type) - air_contents.add_gas(gas_type) if(starter_temp) air_contents.temperature = starter_temp - air_contents.gases[gas_type][MOLES] = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + air_contents.gases[gas_type] = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) if(starter_temp) air_contents.temperature = starter_temp /obj/machinery/portable_atmospherics/canister/air/create_gas() - air_contents.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen) - air_contents.gases[/datum/gas/oxygen][MOLES] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - air_contents.gases[/datum/gas/nitrogen][MOLES] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + air_contents.gases[/datum/gas/oxygen] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + air_contents.gases[/datum/gas/nitrogen] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) #define HOLDING (1<<0) #define CONNECTED (1<<1) @@ -439,10 +437,10 @@ var/list/danger = list() for(var/id in air_contents.gases) var/gas = air_contents.gases[id] - if(!gas[GAS_META][META_GAS_DANGER]) + if(!GLOB.meta_gas_dangers[id]) continue - if(gas[MOLES] > (gas[GAS_META][META_GAS_MOLES_VISIBLE] || MOLES_GAS_VISIBLE)) //if moles_visible is undefined, default to default visibility - danger[gas[GAS_META][META_GAS_NAME]] = gas[MOLES] //ex. "plasma" = 20 + if(gas > (GLOB.meta_gas_visibility[id] || MOLES_GAS_VISIBLE)) //if moles_visible is undefined, default to default visibility + danger[GLOB.meta_gas_names[id]] = gas //ex. "plasma" = 20 if(danger.len) message_admins("[ADMIN_LOOKUPFLW(usr)] opened a canister that contains the following at [ADMIN_VERBOSEJMP(src)]:") diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm index 29a454f59a..28cdb56e3d 100644 --- a/code/modules/atmospherics/machinery/portable/scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/scrubber.dm @@ -1,146 +1,144 @@ -/obj/machinery/portable_atmospherics/scrubber - name = "portable air scrubber" - icon_state = "pscrubber:0" - density = TRUE - - var/on = FALSE - var/volume_rate = 1000 - volume = 1000 - - var/list/scrubbing = list(/datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/bz, /datum/gas/nitryl, /datum/gas/tritium, /datum/gas/hypernoblium, /datum/gas/water_vapor) - -/obj/machinery/portable_atmospherics/scrubber/Destroy() - var/turf/T = get_turf(src) - T.assume_air(air_contents) - air_update_turf() - return ..() - -/obj/machinery/portable_atmospherics/scrubber/update_icon() - icon_state = "pscrubber:[on]" - - cut_overlays() - if(holding) - add_overlay("scrubber-open") - if(connected_port) - add_overlay("scrubber-connector") - -/obj/machinery/portable_atmospherics/scrubber/process_atmos() - ..() - if(!on) - return - - if(holding) - scrub(holding.air_contents) - else - var/turf/T = get_turf(src) - scrub(T.return_air()) - -/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/datum/gas_mixture/mixture) - var/transfer_moles = min(1, volume_rate / mixture.volume) * mixture.total_moles() - - var/datum/gas_mixture/filtering = mixture.remove(transfer_moles) // Remove part of the mixture to filter. - var/datum/gas_mixture/filtered = new - if(!filtering) - return - - filtered.temperature = filtering.temperature - for(var/gas in filtering.gases & scrubbing) - filtered.add_gas(gas) - filtered.gases[gas][MOLES] = filtering.gases[gas][MOLES] // Shuffle the "bad" gasses to the filtered mixture. - filtering.gases[gas][MOLES] = 0 - filtering.garbage_collect() // Now that the gasses are set to 0, clean up the mixture. - - air_contents.merge(filtered) // Store filtered out gasses. - mixture.merge(filtering) // Returned the cleaned gas. - if(!holding) - air_update_turf() - -/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) - . = ..() - if(. & EMP_PROTECT_SELF) - return - if(is_operational()) - if(prob(50 / severity)) - on = !on - update_icon() - -/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "portable_scrubber", name, 420, 435, master_ui, state) - ui.open() - -/obj/machinery/portable_atmospherics/scrubber/ui_data() - var/data = list() - data["on"] = on - data["connected"] = connected_port ? 1 : 0 - data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - - data["id_tag"] = -1 //must be defined in order to reuse code between portable and vent scrubbers - data["filter_types"] = list() - for(var/path in GLOB.meta_gas_info) - var/list/gas = GLOB.meta_gas_info[path] - data["filter_types"] += list(list("gas_id" = gas[META_GAS_ID], "gas_name" = gas[META_GAS_NAME], "enabled" = (path in scrubbing))) - - if(holding) - data["holding"] = list() - data["holding"]["name"] = holding.name - data["holding"]["pressure"] = round(holding.air_contents.return_pressure()) - return data - -/obj/machinery/portable_atmospherics/scrubber/ui_act(action, params) - if(..()) - return - switch(action) - if("power") - on = !on - . = TRUE - if("eject") - if(holding) - holding.forceMove(drop_location()) - holding = null - . = TRUE - if("toggle_filter") - scrubbing ^= gas_id2path(params["val"]) - . = TRUE - update_icon() - -/obj/machinery/portable_atmospherics/scrubber/huge - name = "huge air scrubber" - icon_state = "scrubber:0" - anchored = TRUE - active_power_usage = 500 - idle_power_usage = 10 - - volume_rate = 1500 - volume = 50000 - - var/movable = FALSE - -/obj/machinery/portable_atmospherics/scrubber/huge/movable - movable = TRUE - -/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() - icon_state = "scrubber:[on]" - -/obj/machinery/portable_atmospherics/scrubber/huge/process_atmos() - if((!anchored && !movable) || !is_operational()) - on = FALSE - update_icon() - use_power = on ? ACTIVE_POWER_USE : IDLE_POWER_USE - if(!on) - return - - ..() - if(!holding) - var/turf/T = get_turf(src) - for(var/turf/AT in T.GetAtmosAdjacentTurfs(alldir = TRUE)) - scrub(AT.return_air()) - -/obj/machinery/portable_atmospherics/scrubber/huge/attackby(obj/item/W, mob/user) - if(default_unfasten_wrench(user, W)) - if(!movable) - on = FALSE - else - return ..() +/obj/machinery/portable_atmospherics/scrubber + name = "portable air scrubber" + icon_state = "pscrubber:0" + density = TRUE + + var/on = FALSE + var/volume_rate = 1000 + volume = 1000 + + var/list/scrubbing = list(/datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/bz, /datum/gas/nitryl, /datum/gas/tritium, /datum/gas/hypernoblium, /datum/gas/water_vapor) + +/obj/machinery/portable_atmospherics/scrubber/Destroy() + var/turf/T = get_turf(src) + T.assume_air(air_contents) + air_update_turf() + return ..() + +/obj/machinery/portable_atmospherics/scrubber/update_icon() + icon_state = "pscrubber:[on]" + + cut_overlays() + if(holding) + add_overlay("scrubber-open") + if(connected_port) + add_overlay("scrubber-connector") + +/obj/machinery/portable_atmospherics/scrubber/process_atmos() + ..() + if(!on) + return + + if(holding) + scrub(holding.air_contents) + else + var/turf/T = get_turf(src) + scrub(T.return_air()) + +/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/datum/gas_mixture/mixture) + var/transfer_moles = min(1, volume_rate / mixture.volume) * mixture.total_moles() + + var/datum/gas_mixture/filtering = mixture.remove(transfer_moles) // Remove part of the mixture to filter. + var/datum/gas_mixture/filtered = new + if(!filtering) + return + + filtered.temperature = filtering.temperature + for(var/gas in filtering.gases & scrubbing) + filtered.gases[gas] = filtering.gases[gas] // Shuffle the "bad" gasses to the filtered mixture. + filtering.gases[gas] = 0 + GAS_GARBAGE_COLLECT(filtering.gases) + + air_contents.merge(filtered) // Store filtered out gasses. + mixture.merge(filtering) // Returned the cleaned gas. + if(!holding) + air_update_turf() + +/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + if(is_operational()) + if(prob(50 / severity)) + on = !on + update_icon() + +/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "portable_scrubber", name, 420, 435, master_ui, state) + ui.open() + +/obj/machinery/portable_atmospherics/scrubber/ui_data() + var/data = list() + data["on"] = on + data["connected"] = connected_port ? 1 : 0 + data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + + data["id_tag"] = -1 //must be defined in order to reuse code between portable and vent scrubbers + data["filter_types"] = list() + for(var/path in GLOB.meta_gas_ids) + data["filter_types"] += list(list("gas_id" = GLOB.meta_gas_ids[path], "gas_name" = GLOB.meta_gas_names[path], "enabled" = (path in scrubbing))) + + if(holding) + data["holding"] = list() + data["holding"]["name"] = holding.name + data["holding"]["pressure"] = round(holding.air_contents.return_pressure()) + return data + +/obj/machinery/portable_atmospherics/scrubber/ui_act(action, params) + if(..()) + return + switch(action) + if("power") + on = !on + . = TRUE + if("eject") + if(holding) + holding.forceMove(drop_location()) + holding = null + . = TRUE + if("toggle_filter") + scrubbing ^= gas_id2path(params["val"]) + . = TRUE + update_icon() + +/obj/machinery/portable_atmospherics/scrubber/huge + name = "huge air scrubber" + icon_state = "scrubber:0" + anchored = TRUE + active_power_usage = 500 + idle_power_usage = 10 + + volume_rate = 1500 + volume = 50000 + + var/movable = FALSE + +/obj/machinery/portable_atmospherics/scrubber/huge/movable + movable = TRUE + +/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() + icon_state = "scrubber:[on]" + +/obj/machinery/portable_atmospherics/scrubber/huge/process_atmos() + if((!anchored && !movable) || !is_operational()) + on = FALSE + update_icon() + use_power = on ? ACTIVE_POWER_USE : IDLE_POWER_USE + if(!on) + return + + ..() + if(!holding) + var/turf/T = get_turf(src) + for(var/turf/AT in T.GetAtmosAdjacentTurfs(alldir = TRUE)) + scrub(AT.return_air()) + +/obj/machinery/portable_atmospherics/scrubber/huge/attackby(obj/item/W, mob/user) + if(default_unfasten_wrench(user, W)) + if(!movable) + on = FALSE + else + return ..() diff --git a/code/modules/cargo/bounties/engineering.dm b/code/modules/cargo/bounties/engineering.dm index eb1764d482..e10d48f3bf 100644 --- a/code/modules/cargo/bounties/engineering.dm +++ b/code/modules/cargo/bounties/engineering.dm @@ -12,7 +12,7 @@ var/obj/item/tank/T = O if(!T.air_contents.gases[gas_type]) return FALSE - return T.air_contents.gases[gas_type][MOLES] >= moles_required + return T.air_contents.gases[gas_type] >= moles_required /datum/bounty/item/engineering/gas/nitryl_tank name = "Full Tank of Nitryl" diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index ca8f10869d..2c672339a6 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -433,7 +433,6 @@ /datum/supply_pack/security/russianclothing name = "Russian Surplus Clothing" desc = "An old russian crate full of surplus armor that they used to use! Has two sets of bulletproff armor, a few union suits and some warm hats!" - hidden = TRUE contraband = TRUE cost = 5000 // Its basicly sec suits, good boots/gloves contains = list(/obj/item/clothing/suit/security/officer/russian, @@ -873,8 +872,7 @@ /datum/supply_pack/engineering/shield_sat name = "Shield Generator Satellite" desc = "Protect the very existence of this station with these Anti-Meteor defenses. Contains three Shield Generator Satellites." - cost = 3000 - special = TRUE + cost = 4000 contains = list( /obj/machinery/satellite/meteor_shield, /obj/machinery/satellite/meteor_shield, @@ -882,16 +880,13 @@ ) crate_name= "shield sat crate" - /datum/supply_pack/engineering/shield_sat_control name = "Shield System Control Board" desc = "A control system for the Shield Generator Satellite system." - cost = 5000 - special = TRUE + cost = 4000 contains = list(/obj/item/circuitboard/computer/sat_control) crate_name= "shield control board crate" - ////////////////////////////////////////////////////////////////////////////// //////////////////////// Engine Construction ///////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -961,6 +956,15 @@ crate_name = "grounding rod crate" crate_type = /obj/structure/closet/crate/engineering/electrical +/datum/supply_pack/engine/mason + name = "M.A.S.O.N RIG Crate" + desc = "The rare M.A.S.O.N RIG. Requires CE access to open." + cost = 15000 + access = ACCESS_CE + contains = list(/obj/item/clothing/suit/space/hardsuit/ancient/mason) + crate_name = "M.A.S.O.N Rig" + crate_type = /obj/structure/closet/crate/secure/engineering + /datum/supply_pack/engine/PA name = "Particle Accelerator Crate" desc = "A supermassive black hole or hyper-powered teslaball are the perfect way to spice up any party! This \"My First Apocalypse\" kit contains everything you need to build your own Particle Accelerator! Ages 10 and up." diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 8e4891fb70..91a329c67b 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -117,6 +117,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) "balls_color" = "fff", "balls_amount" = 2, "balls_sack_size" = BALLS_SACK_SIZE_DEF, + "balls_shape" = "Single", "balls_size" = BALLS_SIZE_DEF, "balls_cum_rate" = CUM_RATE, "balls_cum_mult" = CUM_RATE_MULT, @@ -701,6 +702,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) else dat += "Testicles Color:" dat += "    Change
" + dat += "Testicles showing:[features["balls_shape"]]" dat += APPEARANCE_CATEGORY_COLUMN dat += "Has Vagina:" dat += "[features["has_vag"] == TRUE ? "Yes" : "No"]" @@ -1458,7 +1460,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) if("flavor_text") - var/msg = stripped_multiline_input(usr,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(features["flavor_text"]), MAX_MESSAGE_LEN*2, TRUE) as null|message + var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", html_decode(features["flavor_text"]), MAX_MESSAGE_LEN*2, TRUE) if(!isnull(msg)) msg = copytext(msg, 1, MAX_MESSAGE_LEN*2) features["flavor_text"] = msg @@ -1836,7 +1838,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) features["cock_color"] = sanitize_hexcolor(new_cockcolor) else - user << "Invalid color. Your color is not bright enough." + to_chat(user,"Invalid color. Your color is not bright enough.") if("cock_length") var/new_length = input(user, "Penis length in inches:\n([COCK_SIZE_MIN]-[COCK_SIZE_MAX])", "Character Preference") as num|null @@ -1858,7 +1860,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) features["balls_color"] = sanitize_hexcolor(new_ballscolor) else - user << "Invalid color. Your color is not bright enough." + to_chat(user,"Invalid color. Your color is not bright enough.") + + if("balls_shape") + var/new_shape + new_shape = input(user, "Testicle Type:", "Character Preference") as null|anything in GLOB.balls_shapes_list + if(new_shape) + features["balls_shape"] = new_shape if("egg_size") var/new_size @@ -1874,7 +1882,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) features["eggsack_egg_color"] = sanitize_hexcolor(new_egg_color) else - user << "Invalid color. Your color is not bright enough." + to_chat(user,"Invalid color. Your color is not bright enough.") if("breasts_size") var/new_size @@ -1897,7 +1905,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) features["breasts_color"] = sanitize_hexcolor(new_breasts_color) else - user << "Invalid color. Your color is not bright enough." + to_chat(user,"Invalid color. Your color is not bright enough.") if("vag_shape") var/new_shape @@ -1914,7 +1922,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) features["vag_color"] = sanitize_hexcolor(new_vagcolor) else - user << "Invalid color. Your color is not bright enough." + to_chat(user,"Invalid color. Your color is not bright enough.") if("ooccolor") var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference",ooccolor) as color|null diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 39b44f1ab9..c2675ba867 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -330,6 +330,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["feature_has_balls"] >> features["has_balls"] S["feature_balls_color"] >> features["balls_color"] S["feature_balls_size"] >> features["balls_size"] + S["feature_balls_shape"] >> features["balls_shape"] S["feature_balls_sack_size"] >> features["balls_sack_size"] S["feature_balls_fluid"] >> features["balls_fluid"] //breasts features diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index b691074c27..bc36353ac5 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -27,7 +27,7 @@ /obj/item/clothing/gloves/combat name = "combat gloves" desc = "These tactical gloves are fireproof and shock resistant." - icon_state = "black" + icon_state = "combat" item_state = "blackgloves" siemens_coefficient = 0 permeability_coefficient = 0.05 diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm index c6d657b271..b4310a7f42 100644 --- a/code/modules/clothing/outfits/ert.dm +++ b/code/modules/clothing/outfits/ert.dm @@ -46,7 +46,8 @@ /datum/outfit/ert/commander/alert name = "ERT Commander - High Alert" - + + suit = /obj/item/clothing/suit/space/hardsuit/ert/alert glasses = /obj/item/clothing/glasses/thermal/eyepatch backpack_contents = list(/obj/item/storage/box/engineer=1,\ /obj/item/melee/baton/loaded=1,\ @@ -80,7 +81,8 @@ /datum/outfit/ert/security/alert name = "ERT Security - High Alert" - + + suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/sec backpack_contents = list(/obj/item/storage/box/engineer=1,\ /obj/item/storage/box/handcuffs=1,\ /obj/item/clothing/mask/gas/sechailer/swat=1,\ @@ -117,6 +119,7 @@ /datum/outfit/ert/medic/alert name = "ERT Medic - High Alert" + suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/med backpack_contents = list(/obj/item/storage/box/engineer=1,\ /obj/item/melee/baton/loaded=1,\ /obj/item/clothing/mask/gas/sechailer/swat=1,\ @@ -153,6 +156,7 @@ /datum/outfit/ert/engineer/alert name = "ERT Engineer - High Alert" + suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/engi backpack_contents = list(/obj/item/storage/box/engineer=1,\ /obj/item/melee/baton/loaded=1,\ /obj/item/clothing/mask/gas/sechailer/swat=1,\ diff --git a/code/modules/clothing/shoes/taeclowndo.dm b/code/modules/clothing/shoes/taeclowndo.dm new file mode 100644 index 0000000000..20d9fa6b70 --- /dev/null +++ b/code/modules/clothing/shoes/taeclowndo.dm @@ -0,0 +1,36 @@ +/obj/item/clothing/shoes/clown_shoes/taeclowndo + var/list/spelltypes = list ( + /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pie, + /obj/effect/proc_holder/spell/aimed/banana_peel, + /obj/effect/proc_holder/spell/targeted/touch/megahonk, + /obj/effect/proc_holder/spell/targeted/touch/bspie, + ) + var/list/spells = list() + + +/obj/item/clothing/shoes/clown_shoes/taeclowndo/equipped(mob/user, slot) + . = ..() + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(!(H.has_trait(TRAIT_CLUMSY)) && !(H.mind && H.mind.assigned_role == "Clown")) + return + if(slot == SLOT_SHOES) + spells = new + for(var/spell in spelltypes) + var/obj/effect/proc_holder/spell/S = new spell + spells += S + S.charge_counter = 0 + S.start_recharge() + H.mind.AddSpell(S) + +/obj/item/clothing/shoes/clown_shoes/taeclowndo/dropped(mob/user) + . = ..() + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.get_item_by_slot(SLOT_SHOES) == src) + for(var/spell in spells) + var/obj/effect/proc_holder/spell/S = spell + H.mind.spell_list.Remove(S) + qdel(S) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index c061d756b7..d40b5f4e9b 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -435,7 +435,6 @@ . = ..() AddComponent(/datum/component/anti_magic, TRUE, FALSE) - //Medical hardsuit /obj/item/clothing/head/helmet/space/hardsuit/medical name = "medical hardsuit helmet" @@ -445,6 +444,7 @@ item_color = "medical" flash_protect = 0 armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR scan_reagents = 1 /obj/item/clothing/suit/space/hardsuit/medical @@ -499,8 +499,6 @@ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/rd tauric = TRUE //Citadel Add for tauric hardsuits - - //Security hardsuit /obj/item/clothing/head/helmet/space/hardsuit/security name = "security hardsuit helmet" @@ -510,7 +508,6 @@ item_color = "sec" armor = list("melee" = 35, "bullet" = 15, "laser" = 30,"energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - /obj/item/clothing/suit/space/hardsuit/security icon_state = "hardsuit-sec" name = "security hardsuit" @@ -532,7 +529,6 @@ item_color = "hos" armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) - /obj/item/clothing/suit/space/hardsuit/security/hos icon_state = "hardsuit-hos" name = "head of security's hardsuit" @@ -623,6 +619,49 @@ var/footstep = 1 var/datum/component/mobhook +/obj/item/clothing/suit/space/hardsuit/ancient/mason + name = "M.A.S.O.N RIG" + desc = "The Multi-Augmented Severe Operations Networked Resource Integration Gear is an man-portable tank designed for extreme environmental situations. It is excessively bulky, but rated for all but the most atomic of hazards. The specialized armor is surprisingly weak to conventional weaponry. The exo slot can attach most storge bags on to the suit." + icon_state = "hardsuit-ancient" + item_state = "anc_hardsuit" + armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 500, "bomb" = 500, "bio" = 500, "rad" = 500, "fire" = 500, "acid" = 500) + slowdown = 6 //Slow + allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/storage, /obj/item/construction/rcd, /obj/item/pipe_dispenser) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ancient/mason + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/head/helmet/space/hardsuit/ancient/mason + name = "M.A.S.O.N RIG helmet" + desc = "The M.A.S.O.N RIG helmet is complimentary to the rest of the armor. It features a very large, high powered flood lamp and robust flash protection." + icon_state = "hardsuit0-ancient" + item_state = "anc_helm" + armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 500, "bomb" = 500, "bio" = 500, "rad" = 500, "fire" = 500, "acid" = 500) + item_color = "ancient" + brightness_on = 16 + scan_reagents = 1 + flash_protect = 5 //We will not be flash by bombs + tint = 1 + var/obj/machinery/doppler_array/integrated/bomb_radar + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/head/helmet/space/hardsuit/ancient/mason/Initialize() + . = ..() + bomb_radar = new /obj/machinery/doppler_array/integrated(src) + +/obj/item/clothing/head/helmet/space/hardsuit/ancient/mason/equipped(mob/living/carbon/human/user, slot) + ..() + if (slot == SLOT_HEAD) + var/datum/atom_hud/DHUD = GLOB.huds[DATA_HUD_DIAGNOSTIC_BASIC] + DHUD.add_hud_to(user) + +/obj/item/clothing/head/helmet/space/hardsuit/ancient/mason/dropped(mob/living/carbon/human/user) + ..() + if (user.head == src) + var/datum/atom_hud/DHUD = GLOB.huds[DATA_HUD_DIAGNOSTIC_BASIC] + DHUD.remove_hud_from(user) + /obj/item/clothing/suit/space/hardsuit/ancient/proc/on_mob_move() var/mob/living/carbon/human/H = loc if(!istype(H) || H.wear_suit != src) @@ -692,7 +731,6 @@ return 1 return 0 - /obj/item/clothing/suit/space/hardsuit/shielded/Destroy() STOP_PROCESSING(SSobj, src) return ..() @@ -750,8 +788,6 @@ item_state = "ert_command" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/ctf/blue - - /obj/item/clothing/head/helmet/space/hardsuit/shielded/ctf name = "shielded hardsuit helmet" desc = "Standard issue hardsuit helmet for playing capture the flag." @@ -760,7 +796,6 @@ item_color = "ert_medical" armor = list("melee" = 0, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 95, "acid" = 95) - /obj/item/clothing/head/helmet/space/hardsuit/shielded/ctf/red icon_state = "hardsuit0-ert_security" item_state = "hardsuit0-ert_security" @@ -773,10 +808,6 @@ item_state = "hardsuit0-ert_commander" item_color = "ert_commander" - - - - //////Syndicate Version /obj/item/clothing/suit/space/hardsuit/shielded/syndi @@ -791,7 +822,6 @@ slowdown = 0 tauric = TRUE //Citadel Add for tauric hardsuits - /obj/item/clothing/suit/space/hardsuit/shielded/syndi/Initialize() jetpack = new /obj/item/tank/jetpack/suit(src) . = ..() diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index 652c0048c2..6d7c1036be 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -8,7 +8,8 @@ Contains: - NASA Voidsuit - Father Christmas' magical clothes - Pirate's spacesuit - - ERT hardsuit: command, sec, engi, med + - ERT hardsuit: Command, Sec, Engi, Med + - ERT High Alarm - Command, Sec, Engi, Med - EVA spacesuit - Freedom's spacesuit (freedom from vacuum's oppression) - Carp hardsuit @@ -185,6 +186,7 @@ Contains: slowdown = 0 strip_delay = 130 resistance_flags = ACID_PROOF + tauric = TRUE //Citadel Add for tauric hardsuits //ERT Security /obj/item/clothing/head/helmet/space/hardsuit/ert/sec @@ -198,7 +200,6 @@ Contains: icon_state = "ert_security" item_state = "ert_security" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/sec - tauric = TRUE //Citadel Add for tauric hardsuits //ERT Engineering /obj/item/clothing/head/helmet/space/hardsuit/ert/engi @@ -212,7 +213,6 @@ Contains: icon_state = "ert_engineer" item_state = "ert_engineer" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/engi - tauric = TRUE //Citadel Add for tauric hardsuits //ERT Medical /obj/item/clothing/head/helmet/space/hardsuit/ert/med @@ -227,8 +227,73 @@ Contains: item_state = "ert_medical" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/med species_exception = list(/datum/species/angel) + + //Red alert ERT + +/obj/item/clothing/head/helmet/space/hardsuit/ert/alert + name = "emergency response unit helmet" + desc = "Red alert command helmet for the ERT. This one is more armored than its standard version." + icon_state = "hardsuit0-ert_commander-alert" + item_state = "hardsuit0-ert_commander-alert" + item_color = "ert_commander-alert" + armor = list("melee" = 70, "bullet" = 55, "laser" = 50, "energy" = 50, "bomb" = 65, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + strip_delay = 130 + item_flags = NODROP + brightness_on = 8 + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/suit/space/hardsuit/ert/alert + name = "emergency response team suit" + desc = "Red alert command suit for the ERT. This one is more armored than its standard version." + icon_state = "ert_command-alert" + item_state = "ert_command-alert" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/alert + allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals) + armor = list("melee" = 70, "bullet" = 55, "laser" = 50, "energy" = 50, "bomb" = 65, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + strip_delay = 130 + resistance_flags = FIRE_PROOF | ACID_PROOF tauric = TRUE //Citadel Add for tauric hardsuits + //ERT Security +/obj/item/clothing/head/helmet/space/hardsuit/ert/alert/sec + desc = "Red alert security helmet for the ERT. This one is more armored than its standard version." + icon_state = "hardsuit0-ert_security-alert" + item_state = "hardsuit0-ert_security-alert" + item_color = "ert_security-alert" + +/obj/item/clothing/suit/space/hardsuit/ert/alert/sec + desc = "Red alert security suit for the ERT. This one is more armored than its standard version." + icon_state = "ert_security-alert" + item_state = "ert_security-alert" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/alert/sec + + //ERT Engineering +/obj/item/clothing/head/helmet/space/hardsuit/ert/alert/engi + desc = "Red alert engineer helmet for the ERT. This one is more armored than its standard version." + icon_state = "hardsuit0-ert_engineer-alert" + item_state = "hardsuit0-ert_engineer-alert" + item_color = "ert_engineer-alert" + +/obj/item/clothing/suit/space/hardsuit/ert/alert/engi + desc = "Red alert engineer suit for the ERT. This one is more armored than its standard version." + icon_state = "ert_engineer-alert" + item_state = "ert_engineer-alert" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/alert/engi + + //ERT Medical +/obj/item/clothing/head/helmet/space/hardsuit/ert/alert/med + desc = "Red alert medical helmet for the ERT. This one is more armored than its standard version." + icon_state = "hardsuit0-ert_medical-alert" + item_state = "hardsuit0-ert_medical-alert" + item_color = "ert_medical-alert" + +/obj/item/clothing/suit/space/hardsuit/ert/alert/med + desc = "Red alert medical suit for the ERT. This one is more armored than its standard version." + icon_state = "ert_medical-alert" + item_state = "ert_medical-alert" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/alert/med + species_exception = list(/datum/species/angel) + /obj/item/clothing/suit/space/eva name = "EVA suit" icon_state = "space" @@ -280,7 +345,6 @@ Contains: item_flags = NODROP mutantrace_variation = NO_MUTANTRACE_VARIATION - /obj/item/clothing/suit/space/hardsuit/carp name = "carp space suit" desc = "A slimming piece of dubious space carp technology, you suspect it won't stand up to hand-to-hand blows." @@ -291,7 +355,6 @@ Contains: allowed = list(/obj/item/tank/internals, /obj/item/gun/ballistic/automatic/speargun) //I'm giving you a hint here helmettype = /obj/item/clothing/head/helmet/space/hardsuit/carp - /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal name = "paranormal response unit helmet" desc = "A helmet worn by those who deal with paranormal threats for a living." diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index 8a02245550..7346dc9ea9 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -36,7 +36,7 @@ if(tauric == TRUE) center = TRUE dimension_x = 64 - else if(H.dna.features["taur"] in list("Fox","Wolf","Otie","Drake","Lab","Shepherd","Husky","Eevee","Panther","Horse","Cow","Tiger")) + else if(H.dna.features["taur"] in list("Fox","Wolf","Otie","Drake","Lab","Shepherd","Husky","Eevee","Panther","Horse","Cow","Tiger","Deer")) taurmode = PAW_TAURIC if(tauric == TRUE) center = TRUE diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index 481fbade71..5d534e00a7 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -24,11 +24,11 @@ ToggleHood() /obj/item/clothing/suit/hooded/item_action_slot_check(slot, mob/user) - if(slot == SLOT_WEAR_SUIT) + if(slot == SLOT_WEAR_SUIT || slot == SLOT_NECK) return 1 /obj/item/clothing/suit/hooded/equipped(mob/user, slot) - if(slot != SLOT_WEAR_SUIT) + if(slot != SLOT_WEAR_SUIT && slot != SLOT_NECK) RemoveHood() ..() diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm index 29ddb8e800..ba2a27af77 100644 --- a/code/modules/crafting/craft.dm +++ b/code/modules/crafting/craft.dm @@ -21,6 +21,8 @@ CAT_BURGER, CAT_CAKE, CAT_EGG, + CAT_SUSHI, //Called Fish + CAT_ICE, //Called Frozen CAT_MEAT, CAT_MISCFOOD, CAT_PASTRY, @@ -28,7 +30,6 @@ CAT_PIZZA, CAT_SALAD, CAT_SANDWICH, - CAT_SUSHI, CAT_SOUP, CAT_SPAGHETTI), CAT_CLOTHING) //Clothing subcategories diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index c3858da6ad..8d261a797c 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -10,7 +10,6 @@ var/category = CAT_NONE //where it shows up in the crafting UI var/subcategory = CAT_NONE - /datum/crafting_recipe/pin_removal name = "Pin Removal" result = /obj/item/gun @@ -54,6 +53,18 @@ category = CAT_WEAPONRY subcategory = CAT_WEAPON +/datum/crafting_recipe/makeshiftshield + name = "Makeshift Metal Shield" + result = /obj/item/shield/makeshift + reqs = list(/obj/item/stack/cable_coil = 30, + /obj/item/stack/sheet/metal = 10, + /obj/item/stack/sheet/cloth = 2, + /obj/item/stack/sheet/leather = 3) + tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + time = 100 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + /datum/crafting_recipe/molotov name = "Molotov" result = /obj/item/reagent_containers/food/drinks/bottle/molotov @@ -312,6 +323,18 @@ category = CAT_WEAPONRY subcategory = CAT_WEAPON +/datum/crafting_recipe/irifle + name = "Improvised Rifle(7.62mm)" + result = /obj/item/gun/ballistic/shotgun/boltaction/improvised + reqs = list(/obj/item/weaponcrafting/receiver = 1, + /obj/item/pipe = 2, + /obj/item/weaponcrafting/stock = 1, + /obj/item/stack/packageWrap = 5) + tools = list(TOOL_SCREWDRIVER) + time = 100 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + /datum/crafting_recipe/chainsaw name = "Chainsaw" result = /obj/item/twohanded/required/chainsaw @@ -395,7 +418,6 @@ reqs = list(/obj/item/paper = 5) category = CAT_MISC - /datum/crafting_recipe/flashlight_eyes name = "Flashlight Eyes" result = /obj/item/organ/eyes/robotic/flashlight @@ -536,6 +558,14 @@ /obj/item/stack/sheet/animalhide/ashdrake = 5) category = CAT_PRIMAL +/datum/crafting_recipe/bonebag + name = "Bone Satchel" + result = /obj/item/storage/backpack/satchel/bone + time = 30 + reqs = list(/obj/item/stack/sheet/bone = 3, + /obj/item/stack/sheet/sinew = 2) + category = CAT_PRIMAL + /datum/crafting_recipe/gold_horn name = "Golden Bike Horn" result = /obj/item/bikehorn/golden @@ -611,7 +641,6 @@ /obj/item/assembly/igniter = 1) category = CAT_MISC - /datum/crafting_recipe/rcl name = "Makeshift Rapid Cable Layer" result = /obj/item/twohanded/rcl/ghetto @@ -735,7 +764,10 @@ name = "Improvised Jetpack" result = /obj/item/tank/jetpack/improvised time = 30 - reqs = list(/obj/item/tank/internals/oxygen = 2, /obj/item/extinguisher = 1, /obj/item/pipe = 3, /obj/item/stack/cable_coil = 30)//red oxygen tank so it looks right + reqs = list(/obj/item/tank/internals/oxygen = 2, + /obj/item/extinguisher = 1, + /obj/item/pipe = 3, + /obj/item/stack/cable_coil = 30) category = CAT_MISC tools = list(TOOL_WRENCH, TOOL_WELDER, TOOL_WIRECUTTER) diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 966d5cc0d8..d8906de036 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -1,548 +1,548 @@ -/datum/round_event_control/spacevine - name = "Spacevine" - typepath = /datum/round_event/spacevine - weight = 15 - max_occurrences = 3 - min_players = 10 - -/datum/round_event/spacevine - fakeable = FALSE - -/datum/round_event/spacevine/start() - var/list/turfs = list() //list of all the empty floor turfs in the hallway areas - - var/obj/structure/spacevine/SV = new() - - for(var/area/hallway/A in world) - for(var/turf/F in A) - if(F.Enter(SV)) - turfs += F - - qdel(SV) - - if(turfs.len) //Pick a turf to spawn at if we can - var/turf/T = pick(turfs) - new /datum/spacevine_controller(T) //spawn a controller at turf - - -/datum/spacevine_mutation - var/name = "" - var/severity = 1 - var/hue - var/quality - -/datum/spacevine_mutation/proc/add_mutation_to_vinepiece(obj/structure/spacevine/holder) - holder.mutations |= src - holder.add_atom_colour(hue, FIXED_COLOUR_PRIORITY) - -/datum/spacevine_mutation/proc/process_mutation(obj/structure/spacevine/holder) - return - -/datum/spacevine_mutation/proc/process_temperature(obj/structure/spacevine/holder, temp, volume) - return - -/datum/spacevine_mutation/proc/on_birth(obj/structure/spacevine/holder) - return - -/datum/spacevine_mutation/proc/on_grow(obj/structure/spacevine/holder) - return - -/datum/spacevine_mutation/proc/on_death(obj/structure/spacevine/holder) - return - -/datum/spacevine_mutation/proc/on_hit(obj/structure/spacevine/holder, mob/hitter, obj/item/I, expected_damage) - . = expected_damage - -/datum/spacevine_mutation/proc/on_cross(obj/structure/spacevine/holder, mob/crosser) - return - -/datum/spacevine_mutation/proc/on_chem(obj/structure/spacevine/holder, datum/reagent/R) - return - -/datum/spacevine_mutation/proc/on_eat(obj/structure/spacevine/holder, mob/living/eater) - return - -/datum/spacevine_mutation/proc/on_spread(obj/structure/spacevine/holder, turf/target) - return - -/datum/spacevine_mutation/proc/on_buckle(obj/structure/spacevine/holder, mob/living/buckled) - return - -/datum/spacevine_mutation/proc/on_explosion(severity, target, obj/structure/spacevine/holder) - return - - -/datum/spacevine_mutation/light - name = "light" - hue = "#ffff00" - quality = POSITIVE - severity = 4 - -/datum/spacevine_mutation/light/on_grow(obj/structure/spacevine/holder) - if(holder.energy) - holder.set_light(severity, 0.3) - -/datum/spacevine_mutation/toxicity - name = "toxic" - hue = "#ff00ff" - severity = 10 - quality = NEGATIVE - -/datum/spacevine_mutation/toxicity/on_cross(obj/structure/spacevine/holder, mob/living/crosser) - if(issilicon(crosser)) - return - if(prob(severity) && istype(crosser) && !isvineimmune(crosser)) - to_chat(crosser, "You accidentally touch the vine and feel a strange sensation.") - crosser.adjustToxLoss(5) - -/datum/spacevine_mutation/toxicity/on_eat(obj/structure/spacevine/holder, mob/living/eater) - if(!isvineimmune(eater)) - eater.adjustToxLoss(5) - -/datum/spacevine_mutation/explosive //OH SHIT IT CAN CHAINREACT RUN!!! - name = "explosive" - hue = "#ff0000" - quality = NEGATIVE - severity = 2 - -/datum/spacevine_mutation/explosive/on_explosion(explosion_severity, target, obj/structure/spacevine/holder) - if(explosion_severity < 3) - qdel(holder) - else - . = 1 - QDEL_IN(holder, 5) - -/datum/spacevine_mutation/explosive/on_death(obj/structure/spacevine/holder, mob/hitter, obj/item/I) - explosion(holder.loc, 0, 0, severity, 0, 0) - -/datum/spacevine_mutation/fire_proof - name = "fire proof" - hue = "#ff8888" - quality = MINOR_NEGATIVE - -/datum/spacevine_mutation/fire_proof/process_temperature(obj/structure/spacevine/holder, temp, volume) - return 1 - -/datum/spacevine_mutation/fire_proof/on_hit(obj/structure/spacevine/holder, mob/hitter, obj/item/I, expected_damage) - if(I && I.damtype == "fire") - . = 0 - else - . = expected_damage - -/datum/spacevine_mutation/vine_eating - name = "vine eating" - hue = "#ff7700" - quality = MINOR_NEGATIVE - -/datum/spacevine_mutation/vine_eating/on_spread(obj/structure/spacevine/holder, turf/target) - var/obj/structure/spacevine/prey = locate() in target - if(prey && !prey.mutations.Find(src)) //Eat all vines that are not of the same origin - qdel(prey) - -/datum/spacevine_mutation/aggressive_spread //very OP, but im out of other ideas currently - name = "aggressive spreading" - hue = "#333333" - severity = 3 - quality = NEGATIVE - -/datum/spacevine_mutation/aggressive_spread/on_spread(obj/structure/spacevine/holder, turf/target) - target.ex_act(severity, null, src) // vine immunity handled at /mob/ex_act - -/datum/spacevine_mutation/aggressive_spread/on_buckle(obj/structure/spacevine/holder, mob/living/buckled) - buckled.ex_act(severity, null, src) - -/datum/spacevine_mutation/transparency - name = "transparent" - hue = "" - quality = POSITIVE - -/datum/spacevine_mutation/transparency/on_grow(obj/structure/spacevine/holder) - holder.set_opacity(0) - holder.alpha = 125 - -/datum/spacevine_mutation/oxy_eater - name = "oxygen consuming" - hue = "#ffff88" - severity = 3 - quality = NEGATIVE - -/datum/spacevine_mutation/oxy_eater/process_mutation(obj/structure/spacevine/holder) - var/turf/open/floor/T = holder.loc - if(istype(T)) - var/datum/gas_mixture/GM = T.air - if(!GM.gases[/datum/gas/oxygen]) - return - GM.gases[/datum/gas/oxygen][MOLES] = max(GM.gases[/datum/gas/oxygen][MOLES] - severity * holder.energy, 0) - GM.garbage_collect() - -/datum/spacevine_mutation/nitro_eater - name = "nitrogen consuming" - hue = "#8888ff" - severity = 3 - quality = NEGATIVE - -/datum/spacevine_mutation/nitro_eater/process_mutation(obj/structure/spacevine/holder) - var/turf/open/floor/T = holder.loc - if(istype(T)) - var/datum/gas_mixture/GM = T.air - if(!GM.gases[/datum/gas/nitrogen]) - return - GM.gases[/datum/gas/nitrogen][MOLES] = max(GM.gases[/datum/gas/nitrogen][MOLES] - severity * holder.energy, 0) - GM.garbage_collect() - -/datum/spacevine_mutation/carbondioxide_eater - name = "CO2 consuming" - hue = "#00ffff" - severity = 3 - quality = POSITIVE - -/datum/spacevine_mutation/carbondioxide_eater/process_mutation(obj/structure/spacevine/holder) - var/turf/open/floor/T = holder.loc - if(istype(T)) - var/datum/gas_mixture/GM = T.air - if(!GM.gases[/datum/gas/carbon_dioxide]) - return - GM.gases[/datum/gas/carbon_dioxide][MOLES] = max(GM.gases[/datum/gas/carbon_dioxide][MOLES] - severity * holder.energy, 0) - GM.garbage_collect() - -/datum/spacevine_mutation/plasma_eater - name = "toxins consuming" - hue = "#ffbbff" - severity = 3 - quality = POSITIVE - -/datum/spacevine_mutation/plasma_eater/process_mutation(obj/structure/spacevine/holder) - var/turf/open/floor/T = holder.loc - if(istype(T)) - var/datum/gas_mixture/GM = T.air - if(!GM.gases[/datum/gas/plasma]) - return - GM.gases[/datum/gas/plasma][MOLES] = max(GM.gases[/datum/gas/plasma][MOLES] - severity * holder.energy, 0) - GM.garbage_collect() - -/datum/spacevine_mutation/thorns - name = "thorny" - hue = "#666666" - severity = 10 - quality = NEGATIVE - -/datum/spacevine_mutation/thorns/on_cross(obj/structure/spacevine/holder, mob/living/crosser) - if(prob(severity) && istype(crosser) && !isvineimmune(holder)) - var/mob/living/M = crosser - M.adjustBruteLoss(5) - to_chat(M, "You cut yourself on the thorny vines.") - -/datum/spacevine_mutation/thorns/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage) - if(prob(severity) && istype(hitter) && !isvineimmune(holder)) - var/mob/living/M = hitter - M.adjustBruteLoss(5) - to_chat(M, "You cut yourself on the thorny vines.") - . = expected_damage - -/datum/spacevine_mutation/woodening - name = "hardened" - hue = "#997700" - quality = NEGATIVE - -/datum/spacevine_mutation/woodening/on_grow(obj/structure/spacevine/holder) - if(holder.energy) - holder.density = TRUE - holder.max_integrity = 100 - holder.obj_integrity = holder.max_integrity - -/datum/spacevine_mutation/woodening/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage) - if(I.is_sharp()) - . = expected_damage * 0.5 - else - . = expected_damage - -/datum/spacevine_mutation/flowering - name = "flowering" - hue = "#0A480D" - quality = NEGATIVE - severity = 10 - -/datum/spacevine_mutation/flowering/on_grow(obj/structure/spacevine/holder) - if(holder.energy == 2 && prob(severity) && !locate(/obj/structure/alien/resin/flower_bud_enemy) in range(5,holder)) - new/obj/structure/alien/resin/flower_bud_enemy(get_turf(holder)) - -/datum/spacevine_mutation/flowering/on_cross(obj/structure/spacevine/holder, mob/living/crosser) - if(prob(25)) - holder.entangle(crosser) - - -// SPACE VINES (Note that this code is very similar to Biomass code) -/obj/structure/spacevine - name = "space vines" - desc = "An extremely expansionistic species of vine." - icon = 'icons/effects/spacevines.dmi' - icon_state = "Light1" - anchored = TRUE - density = FALSE - layer = SPACEVINE_LAYER - mouse_opacity = MOUSE_OPACITY_OPAQUE //Clicking anywhere on the turf is good enough - pass_flags = PASSTABLE | PASSGRILLE - max_integrity = 50 - var/energy = 0 - var/datum/spacevine_controller/master = null - var/list/mutations = list() - -/obj/structure/spacevine/Initialize() - . = ..() - add_atom_colour("#ffffff", FIXED_COLOUR_PRIORITY) - -/obj/structure/spacevine/examine(mob/user) - ..() - var/text = "This one is a" - if(mutations.len) - for(var/A in mutations) - var/datum/spacevine_mutation/SM = A - text += " [SM.name]" - else - text += " normal" - text += " vine." - to_chat(user, text) - -/obj/structure/spacevine/Destroy() - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_death(src) - if(master) - master.VineDestroyed(src) - mutations = list() - set_opacity(0) - if(has_buckled_mobs()) - unbuckle_all_mobs(force=1) - return ..() - -/obj/structure/spacevine/proc/on_chem_effect(datum/reagent/R) - var/override = 0 - for(var/datum/spacevine_mutation/SM in mutations) - override += SM.on_chem(src, R) - if(!override && istype(R, /datum/reagent/toxin/plantbgone)) - if(prob(50)) - qdel(src) - -/obj/structure/spacevine/proc/eat(mob/eater) - var/override = 0 - for(var/datum/spacevine_mutation/SM in mutations) - override += SM.on_eat(src, eater) - if(!override) - qdel(src) - -/obj/structure/spacevine/attacked_by(obj/item/I, mob/living/user) - var/damage_dealt = I.force - if(I.is_sharp()) - damage_dealt *= 4 - if(I.damtype == BURN) - damage_dealt *= 4 - - for(var/datum/spacevine_mutation/SM in mutations) - damage_dealt = SM.on_hit(src, user, I, damage_dealt) //on_hit now takes override damage as arg and returns new value for other mutations to permutate further - take_damage(damage_dealt, I.damtype, "melee", 1) - -/obj/structure/spacevine/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src, 'sound/weapons/slash.ogg', 50, 1) - else - playsound(src, 'sound/weapons/tap.ogg', 50, 1) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, 1) - -/obj/structure/spacevine/Crossed(mob/crosser) - if(isliving(crosser)) - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_cross(src, crosser) - -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/structure/spacevine/attack_hand(mob/user) - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_hit(src, user) - user_unbuckle_mob(user, user) - . = ..() - -/obj/structure/spacevine/attack_paw(mob/living/user) - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_hit(src, user) - user_unbuckle_mob(user,user) - -/obj/structure/spacevine/attack_alien(mob/living/user) - eat(user) - -/datum/spacevine_controller - var/list/obj/structure/spacevine/vines - var/list/growth_queue - var/spread_multiplier = 5 - var/spread_cap = 30 - var/list/vine_mutations_list - var/mutativeness = 1 - -/datum/spacevine_controller/New(turf/location, list/muts, potency, production) - vines = list() - growth_queue = list() - spawn_spacevine_piece(location, null, muts) - START_PROCESSING(SSobj, src) - vine_mutations_list = list() - init_subtypes(/datum/spacevine_mutation/, vine_mutations_list) - if(potency != null) - mutativeness = potency / 10 - if(production != null) - spread_cap *= production / 5 - spread_multiplier /= production / 5 - -/datum/spacevine_controller/vv_get_dropdown() - . = ..() - . += "---" - .["Delete Vines"] = "?_src_=[REF(src)];[HrefToken()];purge_vines=1" - -/datum/spacevine_controller/Topic(href, href_list) - if(..() || !check_rights(R_ADMIN, FALSE) || !usr.client.holder.CheckAdminHref(href, href_list)) - return - - if(href_list["purge_vines"]) - if(alert(usr, "Are you sure you want to delete this spacevine cluster?", "Delete Vines", "Yes", "No") != "Yes") - return - DeleteVines() - -/datum/spacevine_controller/proc/DeleteVines() //this is kill - QDEL_LIST(vines) //this will also qdel us - -/datum/spacevine_controller/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/datum/spacevine_controller/proc/spawn_spacevine_piece(turf/location, obj/structure/spacevine/parent, list/muts) - var/obj/structure/spacevine/SV = new(location) - growth_queue += SV - vines += SV - SV.master = src - if(muts && muts.len) - for(var/datum/spacevine_mutation/M in muts) - M.add_mutation_to_vinepiece(SV) - return - if(parent) - SV.mutations |= parent.mutations - var/parentcolor = parent.atom_colours[FIXED_COLOUR_PRIORITY] - SV.add_atom_colour(parentcolor, FIXED_COLOUR_PRIORITY) - if(prob(mutativeness)) - var/datum/spacevine_mutation/randmut = pick(vine_mutations_list - SV.mutations) - randmut.add_mutation_to_vinepiece(SV) - - for(var/datum/spacevine_mutation/SM in SV.mutations) - SM.on_birth(SV) - location.Entered(SV) - -/datum/spacevine_controller/proc/VineDestroyed(obj/structure/spacevine/S) - S.master = null - vines -= S - growth_queue -= S - if(!vines.len) - var/obj/item/seeds/kudzu/KZ = new(S.loc) - KZ.mutations |= S.mutations - KZ.set_potency(mutativeness * 10) - KZ.set_production((spread_cap / initial(spread_cap)) * 5) - qdel(src) - -/datum/spacevine_controller/process() - if(!LAZYLEN(vines)) - qdel(src) //space vines exterminated. Remove the controller - return - if(!growth_queue) - qdel(src) //Sanity check - return - - var/length = 0 - - length = min( spread_cap , max( 1 , vines.len / spread_multiplier ) ) - var/i = 0 - var/list/obj/structure/spacevine/queue_end = list() - - for(var/obj/structure/spacevine/SV in growth_queue) - if(QDELETED(SV)) - continue - i++ - queue_end += SV - growth_queue -= SV - for(var/datum/spacevine_mutation/SM in SV.mutations) - SM.process_mutation(SV) - if(SV.energy < 2) //If tile isn't fully grown - if(prob(20)) - SV.grow() - else //If tile is fully grown - SV.entangle_mob() - - SV.spread() - if(i >= length) - break - - growth_queue = growth_queue + queue_end - -/obj/structure/spacevine/proc/grow() - if(!energy) - src.icon_state = pick("Med1", "Med2", "Med3") - energy = 1 - set_opacity(1) - else - src.icon_state = pick("Hvy1", "Hvy2", "Hvy3") - energy = 2 - - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_grow(src) - -/obj/structure/spacevine/proc/entangle_mob() - if(!has_buckled_mobs() && prob(25)) - for(var/mob/living/V in src.loc) - entangle(V) - if(has_buckled_mobs()) - break //only capture one mob at a time - - -/obj/structure/spacevine/proc/entangle(mob/living/V) - if(!V || isvineimmune(V)) - return - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_buckle(src, V) - if((V.stat != DEAD) && (V.buckled != src)) //not dead or captured - to_chat(V, "The vines [pick("wind", "tangle", "tighten")] around you!") - buckle_mob(V, 1) - -/obj/structure/spacevine/proc/spread() - var/direction = pick(GLOB.cardinals) - var/turf/stepturf = get_step(src,direction) - if (!isspaceturf(stepturf) && stepturf.Enter(src)) - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_spread(src, stepturf) - stepturf = get_step(src,direction) //in case turf changes, to make sure no runtimes happen - if(!locate(/obj/structure/spacevine, stepturf)) - if(master) - master.spawn_spacevine_piece(stepturf, src) - -/obj/structure/spacevine/ex_act(severity, target) - if(istype(target, type)) //if its agressive spread vine dont do anything - return - var/i - for(var/datum/spacevine_mutation/SM in mutations) - i += SM.on_explosion(severity, target, src) - if(!i && prob(100/severity)) - qdel(src) - -/obj/structure/spacevine/temperature_expose(null, temp, volume) - var/override = 0 - for(var/datum/spacevine_mutation/SM in mutations) - override += SM.process_temperature(src, temp, volume) - if(!override) - qdel(src) - -/obj/structure/spacevine/CanPass(atom/movable/mover, turf/target) - if(isvineimmune(mover)) - . = TRUE - else - . = ..() - -/proc/isvineimmune(atom/A) - . = FALSE - if(isliving(A)) - var/mob/living/M = A - if(("vines" in M.faction) || ("plants" in M.faction)) - . = TRUE +/datum/round_event_control/spacevine + name = "Spacevine" + typepath = /datum/round_event/spacevine + weight = 15 + max_occurrences = 3 + min_players = 10 + +/datum/round_event/spacevine + fakeable = FALSE + +/datum/round_event/spacevine/start() + var/list/turfs = list() //list of all the empty floor turfs in the hallway areas + + var/obj/structure/spacevine/SV = new() + + for(var/area/hallway/A in world) + for(var/turf/F in A) + if(F.Enter(SV)) + turfs += F + + qdel(SV) + + if(turfs.len) //Pick a turf to spawn at if we can + var/turf/T = pick(turfs) + new /datum/spacevine_controller(T) //spawn a controller at turf + + +/datum/spacevine_mutation + var/name = "" + var/severity = 1 + var/hue + var/quality + +/datum/spacevine_mutation/proc/add_mutation_to_vinepiece(obj/structure/spacevine/holder) + holder.mutations |= src + holder.add_atom_colour(hue, FIXED_COLOUR_PRIORITY) + +/datum/spacevine_mutation/proc/process_mutation(obj/structure/spacevine/holder) + return + +/datum/spacevine_mutation/proc/process_temperature(obj/structure/spacevine/holder, temp, volume) + return + +/datum/spacevine_mutation/proc/on_birth(obj/structure/spacevine/holder) + return + +/datum/spacevine_mutation/proc/on_grow(obj/structure/spacevine/holder) + return + +/datum/spacevine_mutation/proc/on_death(obj/structure/spacevine/holder) + return + +/datum/spacevine_mutation/proc/on_hit(obj/structure/spacevine/holder, mob/hitter, obj/item/I, expected_damage) + . = expected_damage + +/datum/spacevine_mutation/proc/on_cross(obj/structure/spacevine/holder, mob/crosser) + return + +/datum/spacevine_mutation/proc/on_chem(obj/structure/spacevine/holder, datum/reagent/R) + return + +/datum/spacevine_mutation/proc/on_eat(obj/structure/spacevine/holder, mob/living/eater) + return + +/datum/spacevine_mutation/proc/on_spread(obj/structure/spacevine/holder, turf/target) + return + +/datum/spacevine_mutation/proc/on_buckle(obj/structure/spacevine/holder, mob/living/buckled) + return + +/datum/spacevine_mutation/proc/on_explosion(severity, target, obj/structure/spacevine/holder) + return + + +/datum/spacevine_mutation/light + name = "light" + hue = "#ffff00" + quality = POSITIVE + severity = 4 + +/datum/spacevine_mutation/light/on_grow(obj/structure/spacevine/holder) + if(holder.energy) + holder.set_light(severity, 0.3) + +/datum/spacevine_mutation/toxicity + name = "toxic" + hue = "#ff00ff" + severity = 10 + quality = NEGATIVE + +/datum/spacevine_mutation/toxicity/on_cross(obj/structure/spacevine/holder, mob/living/crosser) + if(issilicon(crosser)) + return + if(prob(severity) && istype(crosser) && !isvineimmune(crosser)) + to_chat(crosser, "You accidentally touch the vine and feel a strange sensation.") + crosser.adjustToxLoss(5) + +/datum/spacevine_mutation/toxicity/on_eat(obj/structure/spacevine/holder, mob/living/eater) + if(!isvineimmune(eater)) + eater.adjustToxLoss(5) + +/datum/spacevine_mutation/explosive //OH SHIT IT CAN CHAINREACT RUN!!! + name = "explosive" + hue = "#ff0000" + quality = NEGATIVE + severity = 2 + +/datum/spacevine_mutation/explosive/on_explosion(explosion_severity, target, obj/structure/spacevine/holder) + if(explosion_severity < 3) + qdel(holder) + else + . = 1 + QDEL_IN(holder, 5) + +/datum/spacevine_mutation/explosive/on_death(obj/structure/spacevine/holder, mob/hitter, obj/item/I) + explosion(holder.loc, 0, 0, severity, 0, 0) + +/datum/spacevine_mutation/fire_proof + name = "fire proof" + hue = "#ff8888" + quality = MINOR_NEGATIVE + +/datum/spacevine_mutation/fire_proof/process_temperature(obj/structure/spacevine/holder, temp, volume) + return 1 + +/datum/spacevine_mutation/fire_proof/on_hit(obj/structure/spacevine/holder, mob/hitter, obj/item/I, expected_damage) + if(I && I.damtype == "fire") + . = 0 + else + . = expected_damage + +/datum/spacevine_mutation/vine_eating + name = "vine eating" + hue = "#ff7700" + quality = MINOR_NEGATIVE + +/datum/spacevine_mutation/vine_eating/on_spread(obj/structure/spacevine/holder, turf/target) + var/obj/structure/spacevine/prey = locate() in target + if(prey && !prey.mutations.Find(src)) //Eat all vines that are not of the same origin + qdel(prey) + +/datum/spacevine_mutation/aggressive_spread //very OP, but im out of other ideas currently + name = "aggressive spreading" + hue = "#333333" + severity = 3 + quality = NEGATIVE + +/datum/spacevine_mutation/aggressive_spread/on_spread(obj/structure/spacevine/holder, turf/target) + target.ex_act(severity, null, src) // vine immunity handled at /mob/ex_act + +/datum/spacevine_mutation/aggressive_spread/on_buckle(obj/structure/spacevine/holder, mob/living/buckled) + buckled.ex_act(severity, null, src) + +/datum/spacevine_mutation/transparency + name = "transparent" + hue = "" + quality = POSITIVE + +/datum/spacevine_mutation/transparency/on_grow(obj/structure/spacevine/holder) + holder.set_opacity(0) + holder.alpha = 125 + +/datum/spacevine_mutation/oxy_eater + name = "oxygen consuming" + hue = "#ffff88" + severity = 3 + quality = NEGATIVE + +/datum/spacevine_mutation/oxy_eater/process_mutation(obj/structure/spacevine/holder) + var/turf/open/floor/T = holder.loc + if(istype(T)) + var/datum/gas_mixture/GM = T.air + if(!GM.gases[/datum/gas/oxygen]) + return + GM.gases[/datum/gas/oxygen] = max(GM.gases[/datum/gas/oxygen] - severity * holder.energy, 0) + GAS_GARBAGE_COLLECT(GM.gases) + +/datum/spacevine_mutation/nitro_eater + name = "nitrogen consuming" + hue = "#8888ff" + severity = 3 + quality = NEGATIVE + +/datum/spacevine_mutation/nitro_eater/process_mutation(obj/structure/spacevine/holder) + var/turf/open/floor/T = holder.loc + if(istype(T)) + var/datum/gas_mixture/GM = T.air + if(!GM.gases[/datum/gas/nitrogen]) + return + GM.gases[/datum/gas/nitrogen] = max(GM.gases[/datum/gas/nitrogen] - severity * holder.energy, 0) + GAS_GARBAGE_COLLECT(GM.gases) + +/datum/spacevine_mutation/carbondioxide_eater + name = "CO2 consuming" + hue = "#00ffff" + severity = 3 + quality = POSITIVE + +/datum/spacevine_mutation/carbondioxide_eater/process_mutation(obj/structure/spacevine/holder) + var/turf/open/floor/T = holder.loc + if(istype(T)) + var/datum/gas_mixture/GM = T.air + if(!GM.gases[/datum/gas/carbon_dioxide]) + return + GM.gases[/datum/gas/carbon_dioxide] = max(GM.gases[/datum/gas/carbon_dioxide] - severity * holder.energy, 0) + GAS_GARBAGE_COLLECT(GM.gases) + +/datum/spacevine_mutation/plasma_eater + name = "toxins consuming" + hue = "#ffbbff" + severity = 3 + quality = POSITIVE + +/datum/spacevine_mutation/plasma_eater/process_mutation(obj/structure/spacevine/holder) + var/turf/open/floor/T = holder.loc + if(istype(T)) + var/datum/gas_mixture/GM = T.air + if(!GM.gases[/datum/gas/plasma]) + return + GM.gases[/datum/gas/plasma] = max(GM.gases[/datum/gas/plasma] - severity * holder.energy, 0) + GAS_GARBAGE_COLLECT(GM.gases) + +/datum/spacevine_mutation/thorns + name = "thorny" + hue = "#666666" + severity = 10 + quality = NEGATIVE + +/datum/spacevine_mutation/thorns/on_cross(obj/structure/spacevine/holder, mob/living/crosser) + if(prob(severity) && istype(crosser) && !isvineimmune(holder)) + var/mob/living/M = crosser + M.adjustBruteLoss(5) + to_chat(M, "You cut yourself on the thorny vines.") + +/datum/spacevine_mutation/thorns/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage) + if(prob(severity) && istype(hitter) && !isvineimmune(holder)) + var/mob/living/M = hitter + M.adjustBruteLoss(5) + to_chat(M, "You cut yourself on the thorny vines.") + . = expected_damage + +/datum/spacevine_mutation/woodening + name = "hardened" + hue = "#997700" + quality = NEGATIVE + +/datum/spacevine_mutation/woodening/on_grow(obj/structure/spacevine/holder) + if(holder.energy) + holder.density = TRUE + holder.max_integrity = 100 + holder.obj_integrity = holder.max_integrity + +/datum/spacevine_mutation/woodening/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage) + if(I.is_sharp()) + . = expected_damage * 0.5 + else + . = expected_damage + +/datum/spacevine_mutation/flowering + name = "flowering" + hue = "#0A480D" + quality = NEGATIVE + severity = 10 + +/datum/spacevine_mutation/flowering/on_grow(obj/structure/spacevine/holder) + if(holder.energy == 2 && prob(severity) && !locate(/obj/structure/alien/resin/flower_bud_enemy) in range(5,holder)) + new/obj/structure/alien/resin/flower_bud_enemy(get_turf(holder)) + +/datum/spacevine_mutation/flowering/on_cross(obj/structure/spacevine/holder, mob/living/crosser) + if(prob(25)) + holder.entangle(crosser) + + +// SPACE VINES (Note that this code is very similar to Biomass code) +/obj/structure/spacevine + name = "space vines" + desc = "An extremely expansionistic species of vine." + icon = 'icons/effects/spacevines.dmi' + icon_state = "Light1" + anchored = TRUE + density = FALSE + layer = SPACEVINE_LAYER + mouse_opacity = MOUSE_OPACITY_OPAQUE //Clicking anywhere on the turf is good enough + pass_flags = PASSTABLE | PASSGRILLE + max_integrity = 50 + var/energy = 0 + var/datum/spacevine_controller/master = null + var/list/mutations = list() + +/obj/structure/spacevine/Initialize() + . = ..() + add_atom_colour("#ffffff", FIXED_COLOUR_PRIORITY) + +/obj/structure/spacevine/examine(mob/user) + ..() + var/text = "This one is a" + if(mutations.len) + for(var/A in mutations) + var/datum/spacevine_mutation/SM = A + text += " [SM.name]" + else + text += " normal" + text += " vine." + to_chat(user, text) + +/obj/structure/spacevine/Destroy() + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_death(src) + if(master) + master.VineDestroyed(src) + mutations = list() + set_opacity(0) + if(has_buckled_mobs()) + unbuckle_all_mobs(force=1) + return ..() + +/obj/structure/spacevine/proc/on_chem_effect(datum/reagent/R) + var/override = 0 + for(var/datum/spacevine_mutation/SM in mutations) + override += SM.on_chem(src, R) + if(!override && istype(R, /datum/reagent/toxin/plantbgone)) + if(prob(50)) + qdel(src) + +/obj/structure/spacevine/proc/eat(mob/eater) + var/override = 0 + for(var/datum/spacevine_mutation/SM in mutations) + override += SM.on_eat(src, eater) + if(!override) + qdel(src) + +/obj/structure/spacevine/attacked_by(obj/item/I, mob/living/user) + var/damage_dealt = I.force + if(I.is_sharp()) + damage_dealt *= 4 + if(I.damtype == BURN) + damage_dealt *= 4 + + for(var/datum/spacevine_mutation/SM in mutations) + damage_dealt = SM.on_hit(src, user, I, damage_dealt) //on_hit now takes override damage as arg and returns new value for other mutations to permutate further + take_damage(damage_dealt, I.damtype, "melee", 1) + +/obj/structure/spacevine/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src, 'sound/weapons/slash.ogg', 50, 1) + else + playsound(src, 'sound/weapons/tap.ogg', 50, 1) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, 1) + +/obj/structure/spacevine/Crossed(mob/crosser) + if(isliving(crosser)) + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_cross(src, crosser) + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/structure/spacevine/attack_hand(mob/user) + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_hit(src, user) + user_unbuckle_mob(user, user) + . = ..() + +/obj/structure/spacevine/attack_paw(mob/living/user) + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_hit(src, user) + user_unbuckle_mob(user,user) + +/obj/structure/spacevine/attack_alien(mob/living/user) + eat(user) + +/datum/spacevine_controller + var/list/obj/structure/spacevine/vines + var/list/growth_queue + var/spread_multiplier = 5 + var/spread_cap = 30 + var/list/vine_mutations_list + var/mutativeness = 1 + +/datum/spacevine_controller/New(turf/location, list/muts, potency, production) + vines = list() + growth_queue = list() + spawn_spacevine_piece(location, null, muts) + START_PROCESSING(SSobj, src) + vine_mutations_list = list() + init_subtypes(/datum/spacevine_mutation/, vine_mutations_list) + if(potency != null) + mutativeness = potency / 10 + if(production != null) + spread_cap *= production / 5 + spread_multiplier /= production / 5 + +/datum/spacevine_controller/vv_get_dropdown() + . = ..() + . += "---" + .["Delete Vines"] = "?_src_=[REF(src)];[HrefToken()];purge_vines=1" + +/datum/spacevine_controller/Topic(href, href_list) + if(..() || !check_rights(R_ADMIN, FALSE) || !usr.client.holder.CheckAdminHref(href, href_list)) + return + + if(href_list["purge_vines"]) + if(alert(usr, "Are you sure you want to delete this spacevine cluster?", "Delete Vines", "Yes", "No") != "Yes") + return + DeleteVines() + +/datum/spacevine_controller/proc/DeleteVines() //this is kill + QDEL_LIST(vines) //this will also qdel us + +/datum/spacevine_controller/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/datum/spacevine_controller/proc/spawn_spacevine_piece(turf/location, obj/structure/spacevine/parent, list/muts) + var/obj/structure/spacevine/SV = new(location) + growth_queue += SV + vines += SV + SV.master = src + if(muts && muts.len) + for(var/datum/spacevine_mutation/M in muts) + M.add_mutation_to_vinepiece(SV) + return + if(parent) + SV.mutations |= parent.mutations + var/parentcolor = parent.atom_colours[FIXED_COLOUR_PRIORITY] + SV.add_atom_colour(parentcolor, FIXED_COLOUR_PRIORITY) + if(prob(mutativeness)) + var/datum/spacevine_mutation/randmut = pick(vine_mutations_list - SV.mutations) + randmut.add_mutation_to_vinepiece(SV) + + for(var/datum/spacevine_mutation/SM in SV.mutations) + SM.on_birth(SV) + location.Entered(SV) + +/datum/spacevine_controller/proc/VineDestroyed(obj/structure/spacevine/S) + S.master = null + vines -= S + growth_queue -= S + if(!vines.len) + var/obj/item/seeds/kudzu/KZ = new(S.loc) + KZ.mutations |= S.mutations + KZ.set_potency(mutativeness * 10) + KZ.set_production((spread_cap / initial(spread_cap)) * 5) + qdel(src) + +/datum/spacevine_controller/process() + if(!LAZYLEN(vines)) + qdel(src) //space vines exterminated. Remove the controller + return + if(!growth_queue) + qdel(src) //Sanity check + return + + var/length = 0 + + length = min( spread_cap , max( 1 , vines.len / spread_multiplier ) ) + var/i = 0 + var/list/obj/structure/spacevine/queue_end = list() + + for(var/obj/structure/spacevine/SV in growth_queue) + if(QDELETED(SV)) + continue + i++ + queue_end += SV + growth_queue -= SV + for(var/datum/spacevine_mutation/SM in SV.mutations) + SM.process_mutation(SV) + if(SV.energy < 2) //If tile isn't fully grown + if(prob(20)) + SV.grow() + else //If tile is fully grown + SV.entangle_mob() + + SV.spread() + if(i >= length) + break + + growth_queue = growth_queue + queue_end + +/obj/structure/spacevine/proc/grow() + if(!energy) + src.icon_state = pick("Med1", "Med2", "Med3") + energy = 1 + set_opacity(1) + else + src.icon_state = pick("Hvy1", "Hvy2", "Hvy3") + energy = 2 + + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_grow(src) + +/obj/structure/spacevine/proc/entangle_mob() + if(!has_buckled_mobs() && prob(25)) + for(var/mob/living/V in src.loc) + entangle(V) + if(has_buckled_mobs()) + break //only capture one mob at a time + + +/obj/structure/spacevine/proc/entangle(mob/living/V) + if(!V || isvineimmune(V)) + return + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_buckle(src, V) + if((V.stat != DEAD) && (V.buckled != src)) //not dead or captured + to_chat(V, "The vines [pick("wind", "tangle", "tighten")] around you!") + buckle_mob(V, 1) + +/obj/structure/spacevine/proc/spread() + var/direction = pick(GLOB.cardinals) + var/turf/stepturf = get_step(src,direction) + if (!isspaceturf(stepturf) && stepturf.Enter(src)) + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_spread(src, stepturf) + stepturf = get_step(src,direction) //in case turf changes, to make sure no runtimes happen + if(!locate(/obj/structure/spacevine, stepturf)) + if(master) + master.spawn_spacevine_piece(stepturf, src) + +/obj/structure/spacevine/ex_act(severity, target) + if(istype(target, type)) //if its agressive spread vine dont do anything + return + var/i + for(var/datum/spacevine_mutation/SM in mutations) + i += SM.on_explosion(severity, target, src) + if(!i && prob(100/severity)) + qdel(src) + +/obj/structure/spacevine/temperature_expose(null, temp, volume) + var/override = 0 + for(var/datum/spacevine_mutation/SM in mutations) + override += SM.process_temperature(src, temp, volume) + if(!override) + qdel(src) + +/obj/structure/spacevine/CanPass(atom/movable/mover, turf/target) + if(isvineimmune(mover)) + . = TRUE + else + . = ..() + +/proc/isvineimmune(atom/A) + . = FALSE + if(isliving(A)) + var/mob/living/M = A + if(("vines" in M.faction) || ("plants" in M.faction)) + . = TRUE diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index 46e637e640..b753a6c246 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -73,7 +73,7 @@ var/mob/living/carbon/human/H = target var/headarmor = 0 // Target's head armor - armor_block = H.run_armor_check(affecting, "melee","","",armour_penetration) // For normal attack damage + armor_block = H.run_armor_check(affecting, "melee", null, null,armour_penetration) // For normal attack damage //If they have a hat/helmet and the user is targeting their head. if(istype(H.head, /obj/item/clothing/head) && affecting == BODY_ZONE_HEAD) diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index f2b195c32d..2b37065709 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -108,9 +108,9 @@ All foods are distributed among various categories. Use common sense. else if(fullness > 150 && fullness < 500) user.visible_message("[user] takes a [eatverb] from \the [src].", "You take a [eatverb] from \the [src].") else if(fullness > 500 && fullness < 600) - user.visible_message("[user] unwillingly takes a [eatverb] of a bit of \the [src].", "You unwillingly take a [eatverb] of a bit of \the [src].") + user.visible_message("[user] unwillingly takes a [eatverb] of a bit of \the [src].", "You unwillingly take a [eatverb] of a bit of \the [src].") else if(fullness > (600 * (1 + M.overeatduration / 2000))) // The more you eat - the more you can eat - user.visible_message("[user] cannot force any more of \the [src] to go down [user.p_their()] throat!", "You cannot force any more of \the [src] to go down your throat!") + user.visible_message("[user] cannot force any more of \the [src] to go down [user.p_their()] throat!", "You cannot force any more of \the [src] to go down your throat!") return 0 if(M.has_trait(TRAIT_VORACIOUS)) M.changeNext_move(CLICK_CD_MELEE * 0.5) //nom nom nom diff --git a/code/modules/food_and_drinks/food/snacks_frozen.dm b/code/modules/food_and_drinks/food/snacks_frozen.dm new file mode 100644 index 0000000000..febfa527ca --- /dev/null +++ b/code/modules/food_and_drinks/food/snacks_frozen.dm @@ -0,0 +1,187 @@ +/obj/item/reagent_containers/food/snacks/icecreamsandwich + name = "icecream sandwich" + desc = "Portable Ice-cream in its own packaging." + icon = 'icons/obj/food/food.dmi' + icon_state = "icecreamsandwich" + bonus_reagents = list("nutriment" = 1, "ice" = 2) + list_reagents = list("nutriment" = 2, "ice" = 2) + tastes = list("ice cream" = 1) + foodtype = GRAIN | DAIRY + +/obj/item/reagent_containers/food/snacks/sundae + name = "sundae" + desc = "A classic dessert." + icon_state = "sundae" + bonus_reagents = list("nutriment" = 2, "vitamin" = 1) + list_reagents = list("nutriment" = 6, "banana" = 5, "vitamin" = 2) + filling_color = "#FFFACD" + tastes = list("ice cream" = 1, "banana" = 1) + foodtype = FRUIT | DAIRY | SUGAR + +/obj/item/reagent_containers/food/snacks/honkdae + name = "honkdae" + desc = "The clown's favorite dessert." + icon_state = "honkdae" + bonus_reagents = list("nutriment" = 2, "vitamin" = 2) + list_reagents = list("nutriment" = 6, "banana" = 10, "vitamin" = 4) + filling_color = "#FFFACD" + tastes = list("ice cream" = 1, "banana" = 1, "a bad joke" = 1) + foodtype = FRUIT | DAIRY | SUGAR + +/obj/item/reagent_containers/food/snacks/spacefreezy + name = "space freezy" + desc = "The best icecream in space." + icon_state = "spacefreezy" + bonus_reagents = list("nutriment" = 2, "vitamin" = 2) + list_reagents = list("nutriment" = 6, "bluecherryjelly" = 5, "vitamin" = 4) + filling_color = "#87CEFA" + tastes = list("blue cherries" = 2, "ice cream" = 2) + foodtype = FRUIT | DAIRY + +///////////// +//SNOWCONES// +///////////// + +/obj/item/reagent_containers/food/snacks/snowcones //We use this as a base for all other snowcones + name = "flaverless snowcone" + desc = "Its just harden water slivers. Still fun to chew on." + icon = 'icons/obj/food/snowcones.dmi' + icon_state = "flaverless_sc" + trash = /obj/item/reagent_containers/food/drinks/sillycup //We dont eat paper cups + bonus_reagents = list("water" = 10) //Base line will allways give water + list_reagents = list("water" = 1) // We dont get food for water/juices + filling_color = "#FFFFFF" //Ice is white + tastes = list("ice" = 1, "water" = 1) + foodtype = SUGAR //We use SUGAR as a base line to act in as junkfood, other wise we use fruit + +/obj/item/reagent_containers/food/snacks/snowcones/lime + name = "lime flavored snowcone" + desc = "A lime flavord snowball in a paper cup." + icon_state = "lime_sc" + list_reagents = list("nutriment" = 1, "limejuice" = 5) + tastes = list("ice" = 1, "water" = 1, "limes" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/lemon + name = "lemon flavored snowcone" + desc = "A lemon flavord snowball in a paper cup." + icon_state = "lemon_sc" + list_reagents = list("nutriment" = 1, "lemonjuice" = 5) + tastes = list("ice" = 1, "water" = 1, "lemons" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/apple + name = "apple flavored snowcone" + desc = "A apple flavord snowball in a paper cup." + icon_state = "blue_sc" + list_reagents = list("nutriment" = 1, "applejuice" = 5) + tastes = list("ice" = 1, "water" = 1, "apples" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/grape + name = "grape flavored snowcone" + desc = "A grape flavord snowball in a paper cup." + icon_state = "grape_sc" + list_reagents = list("nutriment" = 1, "berryjuice" = 5) + tastes = list("ice" = 1, "water" = 1, "grape" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/orange + name = "orange flavored snowcone" + desc = "A mix of different flavors dizzled on a snowball in a paper cup." + icon_state = "orange_sc" + list_reagents = list("nutriment" = 1, "orangejuice" = 10) + tastes = list("ice" = 1, "water" = 1, "berries" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/blue + name = "bluecherry flavored snowcone" + desc = "A bluecharry flavord snowball in a paper cup, how rare!" + icon_state = "red_sc" + list_reagents = list("nutriment" = 1, "bluecherryjelly" = 5) + tastes = list("ice" = 1, "water" = 1, "blue" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/red + name = "cherry flavored snowcone" + desc = "A cherry flavord snowball in a paper cup." + icon_state = "blue_sc" + list_reagents = list("nutriment" = 1, "cherryjelly" = 5) + tastes = list("ice" = 1, "water" = 1, "red" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/kiwi + name = "kiwi flavored snowcone" + desc = "A kiwi flavord snowball in a paper cup." + icon_state = "kiwi_sc" + list_reagents = list("nutriment" = 3, "vitamin" = 6) + tastes = list("ice" = 1, "space" = 3, "kiwi" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/mix + name = "mixed berry flavored snowcone" + desc = "A mix of different flavors dizzled on a snowball in a paper cup." + icon_state = "berry_sc" + list_reagents = list("nutriment" = 1, "berryjuice" = 10) + tastes = list("ice" = 1, "water" = 1, "berries" = 5) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/fruitsalad + name = "mixed fruit flavored snowcone" + desc = "A mix of different flavors dizzled on a snowball in a paper cup." + icon_state = "fruitsalad_sc" + list_reagents = list("nutriment" = 1, "lemonjuice" = 5, "limejuice" = 5, "lemonjuice" = 5, "orangejuice" = 5) + tastes = list("ice" = 1, "water" = 1, "fruits" = 25) + foodtype = FRUIT + +/obj/item/reagent_containers/food/snacks/snowcones/pineapple + name = "pineapple flavored snowcone" + desc = "A pineapple flavord snowball in a paper cup." + icon_state = "pineapple_sc" + list_reagents = list("nutriment" = 1, "water" = 1) + tastes = list("ice" = 1, "water" = 1, "pineapples" = 5) + foodtype = PINEAPPLE //Pineapple to allow all that like pineapple to enjoy + +/obj/item/reagent_containers/food/snacks/snowcones/mime + name = "mime snowcone" + desc = "..." + icon_state = "mime_sc" + list_reagents = list("nutriment" = 1, "nothing" = 5) + tastes = list("nothing" = 5) + +/obj/item/reagent_containers/food/snacks/snowcones/clown + name = "joke flavored snowcone" + desc = "A waterd down jokeful flavord snowball in a paper cup." + icon_state = "clown_sc" + list_reagents = list("nutriment" = 1, "laughter" = 5) + tastes = list("jokes" = 5, "brainfreeze" = 5, "joy" = 5) + +/obj/item/reagent_containers/food/snacks/snowcones/soda + name = "sodawater flavored snowcone" + desc = "A waterd down sodawater flavored snowcone snowball in a paper cup." + icon_state = "soda_sc" + list_reagents = list("nutriment" = 1, "sodawater" = 5) + tastes = list("surgar" = 1, "water" = 5, "soda" = 5) + foodtype = JUNKFOOD | SUGAR + +/obj/item/reagent_containers/food/snacks/snowcones/pwgrmer + name = "pwergamer flavored snowcone" + desc = "A waterd down pwergamer soda flavord snowball in a paper cup." + icon_state = "pwergamer_sc" + list_reagents = list("nutriment" = 1, "laughter" = 1) + tastes = list("vaild" = 5, "salt" = 5, "wats" = 5) + foodtype = JUNKFOOD | SUGAR + +/obj/item/reagent_containers/food/snacks/snowcones/honey + name = "honey flavored snowcone" + desc = "A honey flavord snowball in a paper cup." + icon_state = "honey_sc" + list_reagents = list("nutriment" = 1, "honey" = 5) + tastes = list("pollen" = 5, "sweetness" = 5, "wax" = 1) + +/obj/item/reagent_containers/food/snacks/snowcones/rainbow + name = "rainbow color snowcone" + desc = "A rainbow color snowball in a paper cup." + icon_state = "rainbow_sc" + list_reagents = list("nutriment" = 5, "laughter" = 25) + tastes = list("sunlight" = 5, "light" = 5, "slime" = 5, "paint" = 3, "clouds" = 3) diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm index 5213cc96e1..c1abec86c1 100644 --- a/code/modules/food_and_drinks/food/snacks_other.dm +++ b/code/modules/food_and_drinks/food/snacks_other.dm @@ -340,36 +340,6 @@ tastes = list("melon" = 1) foodtype = FRUIT -/obj/item/reagent_containers/food/snacks/spacefreezy - name = "space freezy" - desc = "The best icecream in space." - icon_state = "spacefreezy" - bonus_reagents = list("nutriment" = 2, "vitamin" = 2) - list_reagents = list("nutriment" = 6, "bluecherryjelly" = 5, "vitamin" = 4) - filling_color = "#87CEFA" - tastes = list("blue cherries" = 2, "ice cream" = 2) - foodtype = FRUIT | DAIRY - -/obj/item/reagent_containers/food/snacks/sundae - name = "sundae" - desc = "A classic dessert." - icon_state = "sundae" - bonus_reagents = list("nutriment" = 2, "vitamin" = 1) - list_reagents = list("nutriment" = 6, "banana" = 5, "vitamin" = 2) - filling_color = "#FFFACD" - tastes = list("ice cream" = 1, "banana" = 1) - foodtype = FRUIT | DAIRY | SUGAR - -/obj/item/reagent_containers/food/snacks/honkdae - name = "honkdae" - desc = "The clown's favorite dessert." - icon_state = "honkdae" - bonus_reagents = list("nutriment" = 2, "vitamin" = 2) - list_reagents = list("nutriment" = 6, "banana" = 10, "vitamin" = 4) - filling_color = "#FFFACD" - tastes = list("ice cream" = 1, "banana" = 1, "a bad joke" = 1) - foodtype = FRUIT | DAIRY | SUGAR - /obj/item/reagent_containers/food/snacks/nachos name = "nachos" desc = "Chips from Space Mexico." diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm index 435b4a371a..fe05d8a5bf 100644 --- a/code/modules/food_and_drinks/food/snacks_pastry.dm +++ b/code/modules/food_and_drinks/food/snacks_pastry.dm @@ -203,7 +203,7 @@ list_reagents = list("nutriment" = 1) filling_color = "#F0E68C" tastes = list("cookie" = 1) - foodtype = GRAIN | SUGAR + foodtype = SUGAR /obj/item/reagent_containers/food/snacks/donkpocket name = "\improper Donk-pocket" diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm index 04177783e6..8264b4153e 100644 --- a/code/modules/food_and_drinks/food/snacks_pie.dm +++ b/code/modules/food_and_drinks/food/snacks_pie.dm @@ -62,6 +62,28 @@ /obj/item/reagent_containers/food/snacks/pie/cream/nostun stunning = FALSE +/obj/item/reagent_containers/food/snacks/pie/cream/body + +/obj/item/reagent_containers/food/snacks/pie/cream/body/Destroy() + var/turf/T = get_turf(src) + for(var/atom/movable/A in contents) + A.forceMove(T) + A.throw_at(T, 1, 1) + . = ..() + +/obj/item/reagent_containers/food/snacks/pie/cream/body/On_Consume(mob/living/carbon/M) + if(!reagents.total_volume) //so that it happens on the last bite + if(iscarbon(M) && contents.len) + var/turf/T = get_turf(src) + for(var/atom/movable/A in contents) + A.forceMove(T) + A.throw_at(T, 1, 1) + M.visible_message("[src] bursts out of [M]!
") + M.emote("scream") + M.Knockdown(40) + M.adjustBruteLoss(60) + return ..() + /obj/item/reagent_containers/food/snacks/pie/berryclafoutis name = "berry clafoutis" desc = "No black birds, this is a good sign." diff --git a/code/modules/food_and_drinks/food/snacks_sandwichtoast.dm b/code/modules/food_and_drinks/food/snacks_sandwichtoast.dm index 88f8848315..1b602b6759 100644 --- a/code/modules/food_and_drinks/food/snacks_sandwichtoast.dm +++ b/code/modules/food_and_drinks/food/snacks_sandwichtoast.dm @@ -52,16 +52,6 @@ list_reagents = list("nutriment" = 2, "cherryjelly" = 5, "vitamin" = 2) foodtype = GRAIN | FRUIT -/obj/item/reagent_containers/food/snacks/icecreamsandwich - name = "icecream sandwich" - desc = "Portable Ice-cream in its own packaging." - icon = 'icons/obj/food/food.dmi' - icon_state = "icecreamsandwich" - bonus_reagents = list("nutriment" = 1, "ice" = 2) - list_reagents = list("nutriment" = 2, "ice" = 2) - tastes = list("ice cream" = 1) - foodtype = GRAIN | DAIRY - /obj/item/reagent_containers/food/snacks/notasandwich name = "not-a-sandwich" desc = "Something seems to be wrong with this, you can't quite figure what. Maybe it's his moustache." diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm new file mode 100644 index 0000000000..08d5716779 --- /dev/null +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm @@ -0,0 +1,244 @@ + +/datum/crafting_recipe/food/icecreamsandwich + name = "Icecream sandwich" + reqs = list( + /datum/reagent/consumable/cream = 5, + /datum/reagent/consumable/ice = 5, + /obj/item/reagent_containers/food/snacks/icecream = 1 + ) + result = /obj/item/reagent_containers/food/snacks/icecreamsandwich + subcategory = CAT_ICE + +/datum/crafting_recipe/food/spacefreezy + name ="Space freezy" + reqs = list( + /datum/reagent/consumable/bluecherryjelly = 5, + /datum/reagent/consumable/spacemountainwind = 15, + /obj/item/reagent_containers/food/snacks/icecream = 1 + ) + result = /obj/item/reagent_containers/food/snacks/spacefreezy + subcategory = CAT_ICE + +/datum/crafting_recipe/food/sundae + name ="Sundae" + reqs = list( + /datum/reagent/consumable/cream = 5, + /obj/item/reagent_containers/food/snacks/grown/cherries = 1, + /obj/item/reagent_containers/food/snacks/grown/banana = 1, + /obj/item/reagent_containers/food/snacks/icecream = 1 + ) + result = /obj/item/reagent_containers/food/snacks/sundae + subcategory = CAT_ICE + +/datum/crafting_recipe/food/honkdae + name ="Honkdae" + reqs = list( + /datum/reagent/consumable/cream = 5, + /obj/item/clothing/mask/gas/clown_hat = 1, + /obj/item/reagent_containers/food/snacks/grown/cherries = 1, + /obj/item/reagent_containers/food/snacks/grown/banana = 2, + /obj/item/reagent_containers/food/snacks/icecream = 1 + ) + result = /obj/item/reagent_containers/food/snacks/honkdae + subcategory = CAT_ICE + +//////////////////////////SNOW CONES/////////////////////// + +/datum/crafting_recipe/food/flaverless_sc + name = "Flaverless snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones + subcategory = CAT_ICE + +/datum/crafting_recipe/food/pineapple_sc + name = "Pineapple snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /obj/item/reagent_containers/food/snacks/pineappleslice = 2 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/pineapple + subcategory = CAT_ICE + +/datum/crafting_recipe/food/lime_sc + name = "Lime snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/limejuice = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/lime + subcategory = CAT_ICE + +/datum/crafting_recipe/food/lemon_sc + name = "Lemon snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/lemonjuice = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/lemon + subcategory = CAT_ICE + +/datum/crafting_recipe/food/apple_sc + name = "Apple snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/applejuice = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/apple + subcategory = CAT_ICE + +/datum/crafting_recipe/food/grape_sc + name = "Grape snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/berryjuice = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/grape + subcategory = CAT_ICE + +/datum/crafting_recipe/food/orange_sc + name = "Orange snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/orangejuice = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/orange + subcategory = CAT_ICE + +/datum/crafting_recipe/food/blue_sc + name = "Bluecherry snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/bluecherryjelly= 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/blue + subcategory = CAT_ICE + +/datum/crafting_recipe/food/red_sc + name = "Cherry snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/cherryjelly= 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/red + subcategory = CAT_ICE + +/datum/crafting_recipe/food/mix_sc + name = "Mixed berrie snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/berryjuice = 15 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/mix + subcategory = CAT_ICE + +/datum/crafting_recipe/food/fruitsalad_sc + name = "Fruit Salad snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/orangejuice = 5, + /datum/reagent/consumable/limejuice = 5, + /datum/reagent/consumable/lemonjuice = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/fruitsalad + subcategory = CAT_ICE + +/datum/crafting_recipe/food/mime_sc + name = "Mime snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/nothing = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/mime + subcategory = CAT_ICE + +/datum/crafting_recipe/food/clown_sc + name = "Clown snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/clownstears = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/clown + subcategory = CAT_ICE + +/datum/crafting_recipe/food/soda_sc + name = "Soda water snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/sodawater = 15 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/soda + subcategory = CAT_ICE + +/datum/crafting_recipe/food/pwgrmer_sc + name = "Pwergamer snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/pwr_game = 15 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/pwgrmer + subcategory = CAT_ICE + +/datum/crafting_recipe/food/kiwi_sc + name = "Soda water snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /obj/item/reagent_containers/food/snacks/egg/kiwiEgg = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/kiwi + subcategory = CAT_ICE + +/datum/crafting_recipe/food/honey_sc + name = "Honey snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/consumable/honey = 5 + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/honey + subcategory = CAT_ICE + +/datum/crafting_recipe/food/honey_sc + name = "Rainbow snowcone" + reqs = list( + /obj/item/reagent_containers/food/drinks/sillycup = 1, + /datum/reagent/water = 5, + /datum/reagent/consumable/ice = 15, + /datum/reagent/colorful_reagent = 1 //Hard to make + ) + result = /obj/item/reagent_containers/food/snacks/snowcones/rainbow + subcategory = CAT_ICE diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm index 3dd27ddd01..0c4e2c2e30 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm @@ -186,39 +186,6 @@ result = /obj/item/reagent_containers/food/snacks/melonfruitbowl subcategory = CAT_MISCFOOD -/datum/crafting_recipe/food/spacefreezy - name ="Space freezy" - reqs = list( - /datum/reagent/consumable/bluecherryjelly = 5, - /datum/reagent/consumable/spacemountainwind = 15, - /obj/item/reagent_containers/food/snacks/icecream = 1 - ) - result = /obj/item/reagent_containers/food/snacks/spacefreezy - subcategory = CAT_MISCFOOD - -/datum/crafting_recipe/food/sundae - name ="Sundae" - reqs = list( - /datum/reagent/consumable/cream = 5, - /obj/item/reagent_containers/food/snacks/grown/cherries = 1, - /obj/item/reagent_containers/food/snacks/grown/banana = 1, - /obj/item/reagent_containers/food/snacks/icecream = 1 - ) - result = /obj/item/reagent_containers/food/snacks/sundae - subcategory = CAT_MISCFOOD - -/datum/crafting_recipe/food/honkdae - name ="Honkdae" - reqs = list( - /datum/reagent/consumable/cream = 5, - /obj/item/clothing/mask/gas/clown_hat = 1, - /obj/item/reagent_containers/food/snacks/grown/cherries = 1, - /obj/item/reagent_containers/food/snacks/grown/banana = 2, - /obj/item/reagent_containers/food/snacks/icecream = 1 - ) - result = /obj/item/reagent_containers/food/snacks/honkdae - subcategory = CAT_MISCFOOD - /datum/crafting_recipe/food/nachos name ="Nachos" reqs = list( diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm index a2e83a09b4..0f1c40da3f 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm @@ -43,16 +43,6 @@ result = /obj/item/reagent_containers/food/snacks/jellysandwich/cherry subcategory = CAT_SANDWICH -/datum/crafting_recipe/food/icecreamsandwich - name = "Icecream sandwich" - reqs = list( - /datum/reagent/consumable/cream = 5, - /datum/reagent/consumable/ice = 5, - /obj/item/reagent_containers/food/snacks/icecream = 1 - ) - result = /obj/item/reagent_containers/food/snacks/icecreamsandwich - subcategory = CAT_SANDWICH - /datum/crafting_recipe/food/notasandwich name = "Not a sandwich" reqs = list( diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm index 3fe5a2dfca..23f178edc9 100644 --- a/code/modules/hydroponics/grown/towercap.dm +++ b/code/modules/hydroponics/grown/towercap.dm @@ -187,7 +187,7 @@ var/turf/open/O = loc if(O.air) var/loc_gases = O.air.gases - if(loc_gases[/datum/gas/oxygen][MOLES] > 13) + if(loc_gases[/datum/gas/oxygen] > 13) return TRUE return FALSE diff --git a/code/modules/integrated_electronics/core/saved_circuits.dm b/code/modules/integrated_electronics/core/saved_circuits.dm index eef4fe38dd..2fe6984808 100644 --- a/code/modules/integrated_electronics/core/saved_circuits.dm +++ b/code/modules/integrated_electronics/core/saved_circuits.dm @@ -124,7 +124,7 @@ // Save modified name if(initial(name) != name) assembly_params["name"] = name - + // Save modified description if(initial(desc) != desc) assembly_params["desc"] = desc @@ -144,7 +144,7 @@ return "Bad assembly name." if(assembly_params["desc"] && !reject_bad_text(assembly_params["desc"])) return "Bad assembly description." - if(assembly_params["detail_color"] && !(assembly_params["detail_color"] in color_whitelist)) + if(assembly_params["detail_color"] && !reject_bad_text(assembly_params["detail_color"], 7)) return "Bad assembly color." // Loads assembly parameters from a list @@ -153,7 +153,7 @@ // Load modified name, if any. if(assembly_params["name"]) name = assembly_params["name"] - + // Load modified description, if any. if(assembly_params["desc"]) desc = assembly_params["desc"] diff --git a/code/modules/integrated_electronics/subtypes/atmospherics.dm b/code/modules/integrated_electronics/subtypes/atmospherics.dm index 1715223fa2..230d1ce154 100644 --- a/code/modules/integrated_electronics/subtypes/atmospherics.dm +++ b/code/modules/integrated_electronics/subtypes/atmospherics.dm @@ -1,762 +1,761 @@ -#define SOURCE_TO_TARGET 0 -#define TARGET_TO_SOURCE 1 -#define PUMP_EFFICIENCY 0.6 -#define TANK_FAILURE_PRESSURE (ONE_ATMOSPHERE*25) -#define PUMP_MAX_PRESSURE (ONE_ATMOSPHERE*24) -#define PUMP_MAX_VOLUME 100 - - -/obj/item/integrated_circuit/atmospherics - category_text = "Atmospherics" - cooldown_per_use = 2 SECONDS - complexity = 10 - size = 7 - outputs = list( - "self reference" = IC_PINTYPE_SELFREF, - "pressure" = IC_PINTYPE_NUMBER - ) - var/datum/gas_mixture/air_contents - var/volume = 2 //Pretty small, I know - -/obj/item/integrated_circuit/atmospherics/Initialize() - air_contents = new(volume) - ..() - -/obj/item/integrated_circuit/atmospherics/return_air() - return air_contents - -//Check if the gas container is adjacent and of the right type -/obj/item/integrated_circuit/atmospherics/proc/check_gassource(atom/gasholder) - if(!gasholder) - return FALSE - if(!gasholder.Adjacent(get_object())) - return FALSE - if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) - return FALSE - return TRUE - -//Needed in circuits where source and target types differ -/obj/item/integrated_circuit/atmospherics/proc/check_gastarget(atom/gasholder) - return check_gassource(gasholder) - - -// - gas pump - // **works** -/obj/item/integrated_circuit/atmospherics/pump - name = "gas pump" - desc = "Somehow moves gases between two tanks, canisters, and other gas containers." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "source" = IC_PINTYPE_REF, - "target" = IC_PINTYPE_REF, - "target pressure" = IC_PINTYPE_NUMBER - ) - activators = list( - "transfer" = IC_PINTYPE_PULSE_IN, - "on transfer" = IC_PINTYPE_PULSE_OUT - ) - var/direction = SOURCE_TO_TARGET - var/target_pressure = PUMP_MAX_PRESSURE - power_draw_per_use = 20 - -/obj/item/integrated_circuit/atmospherics/pump/Initialize() - air_contents = new(volume) - extended_desc += " Use negative pressure to move air from target to source. \ - Note that only part of the gas is moved on each transfer, \ - so multiple activations will be necessary to achieve target pressure. \ - The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." - . = ..() - -// This proc gets the direction of the gas flow depending on its value, by calling update target -/obj/item/integrated_circuit/atmospherics/pump/on_data_written() - var/amt = get_pin_data(IC_INPUT, 3) - update_target(amt) - -/obj/item/integrated_circuit/atmospherics/pump/proc/update_target(new_amount) - if(!isnum(new_amount)) - new_amount = 0 - // See in which direction the gas moves - if(new_amount < 0) - direction = TARGET_TO_SOURCE - else - direction = SOURCE_TO_TARGET - target_pressure = min(round(PUMP_MAX_PRESSURE),abs(new_amount)) - -/obj/item/integrated_circuit/atmospherics/pump/do_work() - var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) - var/obj/target = get_pin_data_as_type(IC_INPUT, 2, /obj) - perform_magic(source, target) - activate_pin(2) - -/obj/item/integrated_circuit/atmospherics/pump/proc/perform_magic(atom/source, atom/target) - //Check if both atoms are of the right type: atmos circuits/gas tanks/canisters. If one is the same, use the circuit var - if(!check_gassource(source)) - source = src - - if(!check_gastarget(target)) - target = src - - // If both are the same, this whole proc would do nothing and just waste performance - if(source == target) - return - - var/datum/gas_mixture/source_air = source.return_air() - var/datum/gas_mixture/target_air = target.return_air() - - if(!source_air || !target_air) - return - - // Swapping both source and target - if(direction == TARGET_TO_SOURCE) - var/temp = source_air - source_air = target_air - target_air = temp - - // If what you are pumping is empty, use the circuit's storage - if(source_air.total_moles() <= 0) - source_air = air_contents - - // Move gas from one place to another - move_gas(source_air, target_air) - air_update_turf() - -/obj/item/integrated_circuit/atmospherics/pump/proc/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air) - - // No moles = nothing to pump - if(source_air.total_moles() <= 0 || target_air.return_pressure() >= PUMP_MAX_PRESSURE) - return - - // Negative Kelvin temperatures should never happen and if they do, normalize them - if(source_air.temperature < TCMB) - source_air.temperature = TCMB - - var/pressure_delta = target_pressure - target_air.return_pressure() - if(pressure_delta > 0.1) - var/transfer_moles = (pressure_delta*target_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY - var/datum/gas_mixture/removed = source_air.remove(transfer_moles) - target_air.merge(removed) - - -// - volume pump - // **Works** -/obj/item/integrated_circuit/atmospherics/pump/volume - name = "volume pump" - desc = "Moves gases between two tanks, canisters, and other gas containers by using their volume, up to 200 L/s." - extended_desc = " Use negative volume to move air from target to source. Note that only part of the gas is moved on each transfer. Its maximum pumping volume is capped at 1000kPa." - - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "source" = IC_PINTYPE_REF, - "target" = IC_PINTYPE_REF, - "transfer volume" = IC_PINTYPE_NUMBER - ) - activators = list( - "transfer" = IC_PINTYPE_PULSE_IN, - "on transfer" = IC_PINTYPE_PULSE_OUT - ) - direction = SOURCE_TO_TARGET - var/transfer_rate = PUMP_MAX_VOLUME - power_draw_per_use = 20 - -/obj/item/integrated_circuit/atmospherics/pump/volume/update_target(new_amount) - if(!isnum(new_amount)) - new_amount = 0 - // See in which direction the gas moves - if(new_amount < 0) - direction = TARGET_TO_SOURCE - else - direction = SOURCE_TO_TARGET - target_pressure = min(PUMP_MAX_VOLUME,abs(new_amount)) - -/obj/item/integrated_circuit/atmospherics/pump/volume/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air) - // No moles = nothing to pump - if(source_air.total_moles() <= 0) - return - - // Negative Kelvin temperatures should never happen and if they do, normalize them - if(source_air.temperature < TCMB) - source_air.temperature = TCMB - - if((source_air.return_pressure() < 0.01) || (target_air.return_pressure() >= PUMP_MAX_PRESSURE)) - return - - //The second part of the min caps the pressure built by the volume pumps to the max pump pressure - var/transfer_ratio = min(transfer_rate,target_air.volume*PUMP_MAX_PRESSURE/source_air.return_pressure())/source_air.volume - - var/datum/gas_mixture/removed = source_air.remove_ratio(transfer_ratio * PUMP_EFFICIENCY) - - target_air.merge(removed) - - -// - gas vent - // **works** -/obj/item/integrated_circuit/atmospherics/pump/vent - name = "gas vent" - extended_desc = "Use negative volume to move air from target to environment. Note that only part of the gas is moved on each transfer. Unlike the gas pump, this one keeps pumping even further to pressures of 9000 pKa and it is not advised to use it on tank circuits." - desc = "Moves gases between the environment and adjacent gas containers." - inputs = list( - "container" = IC_PINTYPE_REF, - "target pressure" = IC_PINTYPE_NUMBER - ) - -/obj/item/integrated_circuit/atmospherics/pump/vent/on_data_written() - var/amt = get_pin_data(IC_INPUT, 2) - update_target(amt) - -/obj/item/integrated_circuit/atmospherics/pump/vent/do_work() - var/turf/source = get_turf(src) - var/obj/target = get_pin_data_as_type(IC_INPUT, 1, /obj) - perform_magic(source, target) - activate_pin(2) - -/obj/item/integrated_circuit/atmospherics/pump/vent/check_gastarget(atom/gasholder) - if(!gasholder) - return FALSE - if(!gasholder.Adjacent(get_object())) - return FALSE - if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) - return FALSE - return TRUE - - -/obj/item/integrated_circuit/atmospherics/pump/vent/check_gassource(atom/target) - if(!target) - return FALSE - if(!istype(target, /turf)) - return FALSE - return TRUE - - -// - integrated connector - // Can connect and disconnect properly -/obj/item/integrated_circuit/atmospherics/connector - name = "integrated connector" - desc = "Creates an airtight seal with standard connectors found on the floor, \ - allowing the assembly to exchange gases with a pipe network." - extended_desc = "This circuit will automatically attempt to locate and connect to ports on the floor beneath it when activated. \ - You must set a target before connecting." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "target" = IC_PINTYPE_REF - ) - activators = list( - "toggle connection" = IC_PINTYPE_PULSE_IN, - "on connected" = IC_PINTYPE_PULSE_OUT, - "on connection failed" = IC_PINTYPE_PULSE_OUT, - "on disconnected" = IC_PINTYPE_PULSE_OUT - ) - - var/obj/machinery/atmospherics/components/unary/portables_connector/connector - -/obj/item/integrated_circuit/atmospherics/connector/Initialize() - air_contents = new(volume) - START_PROCESSING(SSobj, src) - . = ..() - -//Sucks up the gas from the connector -/obj/item/integrated_circuit/atmospherics/connector/process() - set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) - -/obj/item/integrated_circuit/atmospherics/connector/check_gassource(atom/gasholder) - if(!gasholder) - return FALSE - if(!istype(gasholder,/obj/machinery/atmospherics/components/unary/portables_connector)) - return FALSE - return TRUE - -//If the assembly containing this is moved from the tile the connector pipe is in, the connection breaks -/obj/item/integrated_circuit/atmospherics/connector/ext_moved() - if(connector) - if(get_dist(get_object(), connector) > 0) - // The assembly is set as connected device and the connector handles the rest - connector.connected_device = null - connector = null - activate_pin(4) - -/obj/item/integrated_circuit/atmospherics/connector/do_work() - // If there is a connection, disconnect - if(connector) - connector.connected_device = null - connector = null - activate_pin(4) - return - - var/obj/machinery/atmospherics/components/unary/portables_connector/PC = locate() in get_turf(src) - // If no connector can't connect - if(!PC) - activate_pin(3) - return - connector = PC - connector.connected_device = src - activate_pin(2) - -// Required for making the connector port script work -obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir() - return air_contents - - -// - gas filter - // **works** -/obj/item/integrated_circuit/atmospherics/pump/filter - name = "gas filter" - desc = "Filters one gas out of a mixture." - complexity = 20 - size = 8 - spawn_flags = IC_SPAWN_RESEARCH - inputs = list( - "source" = IC_PINTYPE_REF, - "filtered output" = IC_PINTYPE_REF, - "contaminants output" = IC_PINTYPE_REF, - "wanted gases" = IC_PINTYPE_LIST, - "target pressure" = IC_PINTYPE_NUMBER - ) - power_draw_per_use = 30 - -/obj/item/integrated_circuit/atmospherics/pump/filter/on_data_written() - var/amt = get_pin_data(IC_INPUT, 5) - target_pressure = CLAMP(amt, 0, PUMP_MAX_PRESSURE) - -/obj/item/integrated_circuit/atmospherics/pump/filter/do_work() - activate_pin(2) - var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) - var/obj/filtered = get_pin_data_as_type(IC_INPUT, 2, /obj) - var/obj/contaminants = get_pin_data_as_type(IC_INPUT, 3, /obj) - - var/wanted = get_pin_data(IC_INPUT, 4) - - // If there is no filtered output, this whole thing makes no sense - if(!check_gassource(filtered)) - return - - var/datum/gas_mixture/filtered_air = filtered.return_air() - if(!filtered_air) - return - - // If no source is set, the source is possibly this circuit's content - if(!check_gassource(source)) - source = src - var/datum/gas_mixture/source_air = source.return_air() - - //No source air: source is this circuit - if(!source_air) - source_air = air_contents - - // If no filtering tank is set, filter through itself - if(!check_gassource(contaminants)) - contaminants = src - var/datum/gas_mixture/contaminated_air = contaminants.return_air() - - //If there is no gas mixture datum for unfiltered, pump the contaminants back into the circuit - if(!contaminated_air) - contaminated_air = air_contents - - if(contaminated_air.return_pressure() >= PUMP_MAX_PRESSURE || filtered_air.return_pressure() >= PUMP_MAX_PRESSURE) - return - - var/pressure_delta = target_pressure - contaminated_air.return_pressure() - var/transfer_moles - - //Negative Kelvins are an anomaly and should be normalized if encountered - if(source_air.temperature < TCMB) - source_air.temperature = TCMB - - transfer_moles = (pressure_delta*contaminated_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY - - //If there is nothing to transfer, just return - if(transfer_moles <= 0) - return - - //This is the var that holds the currently filtered part of the gas - var/datum/gas_mixture/removed = source_air.remove(transfer_moles) - if(!removed) - return - - //This is the gas that will be moved from source to filtered - var/datum/gas_mixture/filtered_out = new - - for(var/filtered_gas in removed.gases) - //Get the name of the gas and see if it is in the list - if(removed.gases[filtered_gas][GAS_META][META_GAS_NAME] in wanted) - //The gas that is put in all the filtered out gases - filtered_out.temperature = removed.temperature - filtered_out.add_gas(filtered_gas) - filtered_out.gases[filtered_gas][MOLES] = removed.gases[filtered_gas][MOLES] - - //The filtered out gas is entirely removed from the currently filtered gases - removed.gases[filtered_gas][MOLES] = 0 - removed.garbage_collect() - - //Check if the pressure is high enough to put stuff in filtered, or else just put it back in the source - var/datum/gas_mixture/target = (filtered_air.return_pressure() < target_pressure ? filtered_air : source_air) - target.merge(filtered_out) - contaminated_air.merge(removed) - - -/obj/item/integrated_circuit/atmospherics/pump/filter/Initialize() - air_contents = new(volume) - . = ..() - extended_desc = "Remember to properly spell and capitalize the filtered gas name. \ - Note that only part of the gas is moved on each transfer, \ - so multiple activations will be necessary to achieve target pressure. \ - The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." - - -// - gas mixer - // **works** -/obj/item/integrated_circuit/atmospherics/pump/mixer - name = "gas mixer" - desc = "Mixes 2 different types of gases." - complexity = 20 - size = 8 - spawn_flags = IC_SPAWN_RESEARCH - inputs = list( - "first source" = IC_PINTYPE_REF, - "second source" = IC_PINTYPE_REF, - "output" = IC_PINTYPE_REF, - "first source percentage" = IC_PINTYPE_NUMBER, - "target pressure" = IC_PINTYPE_NUMBER - ) - power_draw_per_use = 30 - -/obj/item/integrated_circuit/atmospherics/pump/mixer/do_work() - activate_pin(2) - var/obj/source_1 = get_pin_data(IC_INPUT, 1) - var/obj/source_2 = get_pin_data(IC_INPUT, 2) - var/obj/gas_output = get_pin_data(IC_INPUT, 3) - if(!check_gassource(source_1)) - source_1 = src - - if(!check_gassource(source_2)) - source_2 = src - - if(!check_gassource(gas_output)) - gas_output = src - - if(source_1 == gas_output || source_2 == gas_output) - return - - var/datum/gas_mixture/source_1_gases = source_1.return_air() - var/datum/gas_mixture/source_2_gases = source_2.return_air() - var/datum/gas_mixture/output_gases = gas_output.return_air() - - if(!source_1_gases || !source_2_gases || !output_gases) - return - - if(output_gases.return_pressure() >= PUMP_MAX_PRESSURE) - return - - if(source_1_gases.return_pressure() <= 0 || source_2_gases.return_pressure() <= 0) - return - - //This calculates how much should be sent - var/gas_percentage = round(max(min(get_pin_data(IC_INPUT, 4),100),0) / 100) - - //Basically: number of moles = percentage of pressure filled up * efficiency coefficient * (pressure from both gases * volume of output) / (R * Temperature) - var/transfer_moles = (get_pin_data(IC_INPUT, 5) / max(1,output_gases.return_pressure())) * PUMP_EFFICIENCY * (source_1_gases.return_pressure() * gas_percentage + source_2_gases.return_pressure() * (1 - gas_percentage)) * output_gases.volume/ (R_IDEAL_GAS_EQUATION * max(output_gases.temperature,TCMB)) - - - if(transfer_moles <= 0) - return - - var/datum/gas_mixture/mix = source_1_gases.remove(transfer_moles * gas_percentage) - output_gases.merge(mix) - mix = source_2_gases.remove(transfer_moles * (1-gas_percentage)) - output_gases.merge(mix) - - -// - integrated tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank - name = "integrated tank" - desc = "A small tank for the storage of gases." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - size = 4 - activators = list( - "push ref" = IC_PINTYPE_PULSE_IN - ) - volume = 3 //emergency tank sized - var/broken = FALSE - -/obj/item/integrated_circuit/atmospherics/tank/Initialize() - air_contents = new(volume) - START_PROCESSING(SSobj, src) - extended_desc = "Take care not to pressurize it above [round(TANK_FAILURE_PRESSURE)] kPa, or else it will break." - . = ..() - -/obj/item/integrated_circuit/atmospherics/tank/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/atmospherics/tank/do_work() - set_pin_data(IC_OUTPUT, 1, WEAKREF(src)) - push_data() - -/obj/item/integrated_circuit/atmospherics/tank/process() - var/tank_pressure = air_contents.return_pressure() - set_pin_data(IC_OUTPUT, 2, tank_pressure) - push_data() - - //Check if tank broken - if(!broken && tank_pressure > TANK_FAILURE_PRESSURE) - broken = TRUE - to_chat(view(2),"The [name] ruptures, releasing its gases!") - if(broken) - release() - -/obj/item/integrated_circuit/atmospherics/tank/proc/release() - if(air_contents.total_moles() > 0) - playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) - var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) - var/turf/current_turf = get_turf(src) - var/datum/gas_mixture/exterior_gas - if(!current_turf) - return - - exterior_gas = current_turf.return_air() - exterior_gas.merge(expelled_gas) - - -// - large integrated tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank/large - name = "large integrated tank" - desc = "A less small tank for the storage of gases." - volume = 9 - size = 12 - spawn_flags = IC_SPAWN_RESEARCH - - -// - freezer tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank/freezer - name = "freezer tank" - desc = "Cools the gas it contains to a preset temperature." - volume = 6 - size = 8 - inputs = list( - "target temperature" = IC_PINTYPE_NUMBER, - "on" = IC_PINTYPE_BOOLEAN - ) - inputs_default = list("1" = 300) - spawn_flags = IC_SPAWN_RESEARCH - var/temperature = 293.15 - var/heater_coefficient = 0.1 - -/obj/item/integrated_circuit/atmospherics/tank/freezer/on_data_written() - temperature = max(73.15,min(293.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/tank/freezer/process() - var/tank_pressure = air_contents.return_pressure() - set_pin_data(IC_OUTPUT, 2, tank_pressure) - push_data() - - //Cool the tank if the power is on and the temp is above - if(!power_draw_idle || air_contents.temperature < temperature) - return - - air_contents.temperature = max(73.15,air_contents.temperature - (air_contents.temperature - temperature) * heater_coefficient) - - -// - heater tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank/freezer/heater - name = "heater tank" - desc = "Heats the gas it contains to a preset temperature." - volume = 6 - inputs = list( - "target temperature" = IC_PINTYPE_NUMBER, - "on" = IC_PINTYPE_BOOLEAN - ) - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/on_data_written() - temperature = max(293.15,min(573.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/process() - var/tank_pressure = air_contents.return_pressure() - set_pin_data(IC_OUTPUT, 2, tank_pressure) - push_data() - - //Heat the tank if the power is on or its temperature is below what is set - if(!power_draw_idle || air_contents.temperature > temperature) - return - - air_contents.temperature = min(573.15,air_contents.temperature + (temperature - air_contents.temperature) * heater_coefficient) - - -// - atmospheric cooler - // **works** -/obj/item/integrated_circuit/atmospherics/cooler - name = "atmospheric cooler circuit" - desc = "Cools the air around it." - volume = 6 - size = 13 - spawn_flags = IC_SPAWN_RESEARCH - inputs = list( - "target temperature" = IC_PINTYPE_NUMBER, - "on" = IC_PINTYPE_BOOLEAN - ) - var/temperature = 293.15 - var/heater_coefficient = 0.1 - -/obj/item/integrated_circuit/atmospherics/cooler/Initialize() - air_contents = new(volume) - START_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/atmospherics/cooler/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/atmospherics/cooler/on_data_written() - temperature = max(243.15,min(293.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/cooler/process() - set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) - push_data() - - - //Get the turf you're on and its gas mixture - var/turf/current_turf = get_turf(src) - if(!current_turf) - return - - var/datum/gas_mixture/turf_air = current_turf.return_air() - if(!power_draw_idle || turf_air.temperature < temperature) - return - - //Cool the gas - turf_air.temperature = max(243.15,turf_air.temperature - (turf_air.temperature - temperature) * heater_coefficient) - - -// - atmospheric heater - // **works** -/obj/item/integrated_circuit/atmospherics/cooler/heater - name = "atmospheric heater circuit" - desc = "Heats the air around it." - -/obj/item/integrated_circuit/atmospherics/cooler/heater/on_data_written() - temperature = max(293.15,min(323.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/cooler/heater/process() - set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) - push_data() - - //Get the turf and its air mixture - var/turf/current_turf = get_turf(src) - if(!current_turf) - return - - var/datum/gas_mixture/turf_air = current_turf.return_air() - if(!power_draw_idle || turf_air.temperature > temperature) - return - - //Heat the gas - turf_air.temperature = min(323.15,turf_air.temperature + (temperature - turf_air.temperature) * heater_coefficient) - - -// - tank slot - // **works** -/obj/item/integrated_circuit/input/tank_slot - category_text = "Atmospherics" - cooldown_per_use = 1 - name = "tank slot" - desc = "Lets you add a tank to your assembly and remove it even when the assembly is closed." - extended_desc = "It can help you extract gases easier." - complexity = 25 - size = 30 - inputs = list() - outputs = list( - "pressure used" = IC_PINTYPE_NUMBER, - "current tank" = IC_PINTYPE_REF - ) - activators = list( - "push ref" = IC_PINTYPE_PULSE_IN, - "on insert" = IC_PINTYPE_PULSE_OUT, - "on remove" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - - can_be_asked_input = TRUE - demands_object_input = TRUE - can_input_object_when_closed = TRUE - - var/obj/item/tank/internals/current_tank - -/obj/item/integrated_circuit/input/tank_slot/Initialize() - START_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/input/tank_slot/process() - push_pressure() - -/obj/item/integrated_circuit/input/tank_slot/attackby(var/obj/item/tank/internals/I, var/mob/living/user) - //Check if it truly is a tank - if(!istype(I,/obj/item/tank/internals)) - to_chat(user,"The [I.name] doesn't seem to fit in here.") - return - - //Check if there is no other tank already inside - if(current_tank) - to_chat(user,"There is already a gas tank inside.") - return - - //The current tank is the one we just attached, its location is inside the circuit - current_tank = I - user.transferItemToLoc(I,src) - to_chat(user,"You put the [I.name] inside the tank slot.") - - //Set the pin to a weak reference of the current tank - push_pressure() - set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) - push_data() - do_work(1) - - -/obj/item/integrated_circuit/input/tank_slot/ask_for_input(mob/user) - attack_self(user) - -/obj/item/integrated_circuit/input/tank_slot/attack_self(mob/user) - //Check if no tank attached - if(!current_tank) - to_chat(user, "There is currently no tank attached.") - return - - //Remove tank and put in user's hands/location - to_chat(user, "You take [current_tank] out of the tank slot.") - user.put_in_hands(current_tank) - current_tank = null - - //Remove tank reference - push_pressure() - set_pin_data(IC_OUTPUT, 2, null) - push_data() - do_work(2) - -/obj/item/integrated_circuit/input/tank_slot/do_work() - set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) - push_data() - -/obj/item/integrated_circuit/input/tank_slot/proc/push_pressure() - if(!current_tank) - set_pin_data(IC_OUTPUT, 1, 0) - return - - var/datum/gas_mixture/tank_air = current_tank.return_air() - if(!tank_air) - set_pin_data(IC_OUTPUT, 1, 0) - return - - set_pin_data(IC_OUTPUT, 1, tank_air.return_pressure()) - push_data() - - -#undef SOURCE_TO_TARGET -#undef TARGET_TO_SOURCE -#undef PUMP_EFFICIENCY -#undef TANK_FAILURE_PRESSURE -#undef PUMP_MAX_PRESSURE -#undef PUMP_MAX_VOLUME +#define SOURCE_TO_TARGET 0 +#define TARGET_TO_SOURCE 1 +#define PUMP_EFFICIENCY 0.6 +#define TANK_FAILURE_PRESSURE (ONE_ATMOSPHERE*25) +#define PUMP_MAX_PRESSURE (ONE_ATMOSPHERE*24) +#define PUMP_MAX_VOLUME 100 + + +/obj/item/integrated_circuit/atmospherics + category_text = "Atmospherics" + cooldown_per_use = 2 SECONDS + complexity = 10 + size = 7 + outputs = list( + "self reference" = IC_PINTYPE_SELFREF, + "pressure" = IC_PINTYPE_NUMBER + ) + var/datum/gas_mixture/air_contents + var/volume = 2 //Pretty small, I know + +/obj/item/integrated_circuit/atmospherics/Initialize() + air_contents = new(volume) + ..() + +/obj/item/integrated_circuit/atmospherics/return_air() + return air_contents + +//Check if the gas container is adjacent and of the right type +/obj/item/integrated_circuit/atmospherics/proc/check_gassource(atom/gasholder) + if(!gasholder) + return FALSE + if(!gasholder.Adjacent(get_object())) + return FALSE + if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) + return FALSE + return TRUE + +//Needed in circuits where source and target types differ +/obj/item/integrated_circuit/atmospherics/proc/check_gastarget(atom/gasholder) + return check_gassource(gasholder) + + +// - gas pump - // **works** +/obj/item/integrated_circuit/atmospherics/pump + name = "gas pump" + desc = "Somehow moves gases between two tanks, canisters, and other gas containers." + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "source" = IC_PINTYPE_REF, + "target" = IC_PINTYPE_REF, + "target pressure" = IC_PINTYPE_NUMBER + ) + activators = list( + "transfer" = IC_PINTYPE_PULSE_IN, + "on transfer" = IC_PINTYPE_PULSE_OUT + ) + var/direction = SOURCE_TO_TARGET + var/target_pressure = PUMP_MAX_PRESSURE + power_draw_per_use = 20 + +/obj/item/integrated_circuit/atmospherics/pump/Initialize() + air_contents = new(volume) + extended_desc += " Use negative pressure to move air from target to source. \ + Note that only part of the gas is moved on each transfer, \ + so multiple activations will be necessary to achieve target pressure. \ + The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." + . = ..() + +// This proc gets the direction of the gas flow depending on its value, by calling update target +/obj/item/integrated_circuit/atmospherics/pump/on_data_written() + var/amt = get_pin_data(IC_INPUT, 3) + update_target(amt) + +/obj/item/integrated_circuit/atmospherics/pump/proc/update_target(new_amount) + if(!isnum(new_amount)) + new_amount = 0 + // See in which direction the gas moves + if(new_amount < 0) + direction = TARGET_TO_SOURCE + else + direction = SOURCE_TO_TARGET + target_pressure = min(round(PUMP_MAX_PRESSURE),abs(new_amount)) + +/obj/item/integrated_circuit/atmospherics/pump/do_work() + var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) + var/obj/target = get_pin_data_as_type(IC_INPUT, 2, /obj) + perform_magic(source, target) + activate_pin(2) + +/obj/item/integrated_circuit/atmospherics/pump/proc/perform_magic(atom/source, atom/target) + //Check if both atoms are of the right type: atmos circuits/gas tanks/canisters. If one is the same, use the circuit var + if(!check_gassource(source)) + source = src + + if(!check_gastarget(target)) + target = src + + // If both are the same, this whole proc would do nothing and just waste performance + if(source == target) + return + + var/datum/gas_mixture/source_air = source.return_air() + var/datum/gas_mixture/target_air = target.return_air() + + if(!source_air || !target_air) + return + + // Swapping both source and target + if(direction == TARGET_TO_SOURCE) + var/temp = source_air + source_air = target_air + target_air = temp + + // If what you are pumping is empty, use the circuit's storage + if(source_air.total_moles() <= 0) + source_air = air_contents + + // Move gas from one place to another + move_gas(source_air, target_air) + air_update_turf() + +/obj/item/integrated_circuit/atmospherics/pump/proc/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air) + + // No moles = nothing to pump + if(source_air.total_moles() <= 0 || target_air.return_pressure() >= PUMP_MAX_PRESSURE) + return + + // Negative Kelvin temperatures should never happen and if they do, normalize them + if(source_air.temperature < TCMB) + source_air.temperature = TCMB + + var/pressure_delta = target_pressure - target_air.return_pressure() + if(pressure_delta > 0.1) + var/transfer_moles = (pressure_delta*target_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY + var/datum/gas_mixture/removed = source_air.remove(transfer_moles) + target_air.merge(removed) + + +// - volume pump - // **Works** +/obj/item/integrated_circuit/atmospherics/pump/volume + name = "volume pump" + desc = "Moves gases between two tanks, canisters, and other gas containers by using their volume, up to 200 L/s." + extended_desc = " Use negative volume to move air from target to source. Note that only part of the gas is moved on each transfer. Its maximum pumping volume is capped at 1000kPa." + + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "source" = IC_PINTYPE_REF, + "target" = IC_PINTYPE_REF, + "transfer volume" = IC_PINTYPE_NUMBER + ) + activators = list( + "transfer" = IC_PINTYPE_PULSE_IN, + "on transfer" = IC_PINTYPE_PULSE_OUT + ) + direction = SOURCE_TO_TARGET + var/transfer_rate = PUMP_MAX_VOLUME + power_draw_per_use = 20 + +/obj/item/integrated_circuit/atmospherics/pump/volume/update_target(new_amount) + if(!isnum(new_amount)) + new_amount = 0 + // See in which direction the gas moves + if(new_amount < 0) + direction = TARGET_TO_SOURCE + else + direction = SOURCE_TO_TARGET + target_pressure = min(PUMP_MAX_VOLUME,abs(new_amount)) + +/obj/item/integrated_circuit/atmospherics/pump/volume/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air) + // No moles = nothing to pump + if(source_air.total_moles() <= 0) + return + + // Negative Kelvin temperatures should never happen and if they do, normalize them + if(source_air.temperature < TCMB) + source_air.temperature = TCMB + + if((source_air.return_pressure() < 0.01) || (target_air.return_pressure() >= PUMP_MAX_PRESSURE)) + return + + //The second part of the min caps the pressure built by the volume pumps to the max pump pressure + var/transfer_ratio = min(transfer_rate,target_air.volume*PUMP_MAX_PRESSURE/source_air.return_pressure())/source_air.volume + + var/datum/gas_mixture/removed = source_air.remove_ratio(transfer_ratio * PUMP_EFFICIENCY) + + target_air.merge(removed) + + +// - gas vent - // **works** +/obj/item/integrated_circuit/atmospherics/pump/vent + name = "gas vent" + extended_desc = "Use negative volume to move air from target to environment. Note that only part of the gas is moved on each transfer. Unlike the gas pump, this one keeps pumping even further to pressures of 9000 pKa and it is not advised to use it on tank circuits." + desc = "Moves gases between the environment and adjacent gas containers." + inputs = list( + "container" = IC_PINTYPE_REF, + "target pressure" = IC_PINTYPE_NUMBER + ) + +/obj/item/integrated_circuit/atmospherics/pump/vent/on_data_written() + var/amt = get_pin_data(IC_INPUT, 2) + update_target(amt) + +/obj/item/integrated_circuit/atmospherics/pump/vent/do_work() + var/turf/source = get_turf(src) + var/obj/target = get_pin_data_as_type(IC_INPUT, 1, /obj) + perform_magic(source, target) + activate_pin(2) + +/obj/item/integrated_circuit/atmospherics/pump/vent/check_gastarget(atom/gasholder) + if(!gasholder) + return FALSE + if(!gasholder.Adjacent(get_object())) + return FALSE + if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) + return FALSE + return TRUE + + +/obj/item/integrated_circuit/atmospherics/pump/vent/check_gassource(atom/target) + if(!target) + return FALSE + if(!istype(target, /turf)) + return FALSE + return TRUE + + +// - integrated connector - // Can connect and disconnect properly +/obj/item/integrated_circuit/atmospherics/connector + name = "integrated connector" + desc = "Creates an airtight seal with standard connectors found on the floor, \ + allowing the assembly to exchange gases with a pipe network." + extended_desc = "This circuit will automatically attempt to locate and connect to ports on the floor beneath it when activated. \ + You must set a target before connecting." + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "target" = IC_PINTYPE_REF + ) + activators = list( + "toggle connection" = IC_PINTYPE_PULSE_IN, + "on connected" = IC_PINTYPE_PULSE_OUT, + "on connection failed" = IC_PINTYPE_PULSE_OUT, + "on disconnected" = IC_PINTYPE_PULSE_OUT + ) + + var/obj/machinery/atmospherics/components/unary/portables_connector/connector + +/obj/item/integrated_circuit/atmospherics/connector/Initialize() + air_contents = new(volume) + START_PROCESSING(SSobj, src) + . = ..() + +//Sucks up the gas from the connector +/obj/item/integrated_circuit/atmospherics/connector/process() + set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) + +/obj/item/integrated_circuit/atmospherics/connector/check_gassource(atom/gasholder) + if(!gasholder) + return FALSE + if(!istype(gasholder,/obj/machinery/atmospherics/components/unary/portables_connector)) + return FALSE + return TRUE + +//If the assembly containing this is moved from the tile the connector pipe is in, the connection breaks +/obj/item/integrated_circuit/atmospherics/connector/ext_moved() + if(connector) + if(get_dist(get_object(), connector) > 0) + // The assembly is set as connected device and the connector handles the rest + connector.connected_device = null + connector = null + activate_pin(4) + +/obj/item/integrated_circuit/atmospherics/connector/do_work() + // If there is a connection, disconnect + if(connector) + connector.connected_device = null + connector = null + activate_pin(4) + return + + var/obj/machinery/atmospherics/components/unary/portables_connector/PC = locate() in get_turf(src) + // If no connector can't connect + if(!PC) + activate_pin(3) + return + connector = PC + connector.connected_device = src + activate_pin(2) + +// Required for making the connector port script work +obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir() + return air_contents + + +// - gas filter - // **works** +/obj/item/integrated_circuit/atmospherics/pump/filter + name = "gas filter" + desc = "Filters one gas out of a mixture." + complexity = 20 + size = 8 + spawn_flags = IC_SPAWN_RESEARCH + inputs = list( + "source" = IC_PINTYPE_REF, + "filtered output" = IC_PINTYPE_REF, + "contaminants output" = IC_PINTYPE_REF, + "wanted gases" = IC_PINTYPE_LIST, + "target pressure" = IC_PINTYPE_NUMBER + ) + power_draw_per_use = 30 + +/obj/item/integrated_circuit/atmospherics/pump/filter/on_data_written() + var/amt = get_pin_data(IC_INPUT, 5) + target_pressure = CLAMP(amt, 0, PUMP_MAX_PRESSURE) + +/obj/item/integrated_circuit/atmospherics/pump/filter/do_work() + activate_pin(2) + var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) + var/obj/filtered = get_pin_data_as_type(IC_INPUT, 2, /obj) + var/obj/contaminants = get_pin_data_as_type(IC_INPUT, 3, /obj) + + var/wanted = get_pin_data(IC_INPUT, 4) + + // If there is no filtered output, this whole thing makes no sense + if(!check_gassource(filtered)) + return + + var/datum/gas_mixture/filtered_air = filtered.return_air() + if(!filtered_air) + return + + // If no source is set, the source is possibly this circuit's content + if(!check_gassource(source)) + source = src + var/datum/gas_mixture/source_air = source.return_air() + + //No source air: source is this circuit + if(!source_air) + source_air = air_contents + + // If no filtering tank is set, filter through itself + if(!check_gassource(contaminants)) + contaminants = src + var/datum/gas_mixture/contaminated_air = contaminants.return_air() + + //If there is no gas mixture datum for unfiltered, pump the contaminants back into the circuit + if(!contaminated_air) + contaminated_air = air_contents + + if(contaminated_air.return_pressure() >= PUMP_MAX_PRESSURE || filtered_air.return_pressure() >= PUMP_MAX_PRESSURE) + return + + var/pressure_delta = target_pressure - contaminated_air.return_pressure() + var/transfer_moles + + //Negative Kelvins are an anomaly and should be normalized if encountered + if(source_air.temperature < TCMB) + source_air.temperature = TCMB + + transfer_moles = (pressure_delta*contaminated_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY + + //If there is nothing to transfer, just return + if(transfer_moles <= 0) + return + + //This is the var that holds the currently filtered part of the gas + var/datum/gas_mixture/removed = source_air.remove(transfer_moles) + if(!removed) + return + + //This is the gas that will be moved from source to filtered + var/datum/gas_mixture/filtered_out = new + + for(var/filtered_gas in removed.gases) + //Get the name of the gas and see if it is in the list + if(GLOB.meta_gas_names[filtered_gas] in wanted) + //The gas that is put in all the filtered out gases + filtered_out.temperature = removed.temperature + filtered_out.gases[filtered_gas] = removed.gases[filtered_gas] + + //The filtered out gas is entirely removed from the currently filtered gases + removed.gases[filtered_gas] = 0 + GAS_GARBAGE_COLLECT(removed.gases) + + //Check if the pressure is high enough to put stuff in filtered, or else just put it back in the source + var/datum/gas_mixture/target = (filtered_air.return_pressure() < target_pressure ? filtered_air : source_air) + target.merge(filtered_out) + contaminated_air.merge(removed) + + +/obj/item/integrated_circuit/atmospherics/pump/filter/Initialize() + air_contents = new(volume) + . = ..() + extended_desc = "Remember to properly spell and capitalize the filtered gas name. \ + Note that only part of the gas is moved on each transfer, \ + so multiple activations will be necessary to achieve target pressure. \ + The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." + + +// - gas mixer - // **works** +/obj/item/integrated_circuit/atmospherics/pump/mixer + name = "gas mixer" + desc = "Mixes 2 different types of gases." + complexity = 20 + size = 8 + spawn_flags = IC_SPAWN_RESEARCH + inputs = list( + "first source" = IC_PINTYPE_REF, + "second source" = IC_PINTYPE_REF, + "output" = IC_PINTYPE_REF, + "first source percentage" = IC_PINTYPE_NUMBER, + "target pressure" = IC_PINTYPE_NUMBER + ) + power_draw_per_use = 30 + +/obj/item/integrated_circuit/atmospherics/pump/mixer/do_work() + activate_pin(2) + var/obj/source_1 = get_pin_data(IC_INPUT, 1) + var/obj/source_2 = get_pin_data(IC_INPUT, 2) + var/obj/gas_output = get_pin_data(IC_INPUT, 3) + if(!check_gassource(source_1)) + source_1 = src + + if(!check_gassource(source_2)) + source_2 = src + + if(!check_gassource(gas_output)) + gas_output = src + + if(source_1 == gas_output || source_2 == gas_output) + return + + var/datum/gas_mixture/source_1_gases = source_1.return_air() + var/datum/gas_mixture/source_2_gases = source_2.return_air() + var/datum/gas_mixture/output_gases = gas_output.return_air() + + if(!source_1_gases || !source_2_gases || !output_gases) + return + + if(output_gases.return_pressure() >= PUMP_MAX_PRESSURE) + return + + if(source_1_gases.return_pressure() <= 0 || source_2_gases.return_pressure() <= 0) + return + + //This calculates how much should be sent + var/gas_percentage = round(max(min(get_pin_data(IC_INPUT, 4),100),0) / 100) + + //Basically: number of moles = percentage of pressure filled up * efficiency coefficient * (pressure from both gases * volume of output) / (R * Temperature) + var/transfer_moles = (get_pin_data(IC_INPUT, 5) / max(1,output_gases.return_pressure())) * PUMP_EFFICIENCY * (source_1_gases.return_pressure() * gas_percentage + source_2_gases.return_pressure() * (1 - gas_percentage)) * output_gases.volume/ (R_IDEAL_GAS_EQUATION * max(output_gases.temperature,TCMB)) + + + if(transfer_moles <= 0) + return + + var/datum/gas_mixture/mix = source_1_gases.remove(transfer_moles * gas_percentage) + output_gases.merge(mix) + mix = source_2_gases.remove(transfer_moles * (1-gas_percentage)) + output_gases.merge(mix) + + +// - integrated tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank + name = "integrated tank" + desc = "A small tank for the storage of gases." + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + size = 4 + activators = list( + "push ref" = IC_PINTYPE_PULSE_IN + ) + volume = 3 //emergency tank sized + var/broken = FALSE + +/obj/item/integrated_circuit/atmospherics/tank/Initialize() + air_contents = new(volume) + START_PROCESSING(SSobj, src) + extended_desc = "Take care not to pressurize it above [round(TANK_FAILURE_PRESSURE)] kPa, or else it will break." + . = ..() + +/obj/item/integrated_circuit/atmospherics/tank/Destroy() + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/atmospherics/tank/do_work() + set_pin_data(IC_OUTPUT, 1, WEAKREF(src)) + push_data() + +/obj/item/integrated_circuit/atmospherics/tank/process() + var/tank_pressure = air_contents.return_pressure() + set_pin_data(IC_OUTPUT, 2, tank_pressure) + push_data() + + //Check if tank broken + if(!broken && tank_pressure > TANK_FAILURE_PRESSURE) + broken = TRUE + to_chat(view(2),"The [name] ruptures, releasing its gases!") + if(broken) + release() + +/obj/item/integrated_circuit/atmospherics/tank/proc/release() + if(air_contents.total_moles() > 0) + playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) + var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) + var/turf/current_turf = get_turf(src) + var/datum/gas_mixture/exterior_gas + if(!current_turf) + return + + exterior_gas = current_turf.return_air() + exterior_gas.merge(expelled_gas) + + +// - large integrated tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank/large + name = "large integrated tank" + desc = "A less small tank for the storage of gases." + volume = 9 + size = 12 + spawn_flags = IC_SPAWN_RESEARCH + + +// - freezer tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank/freezer + name = "freezer tank" + desc = "Cools the gas it contains to a preset temperature." + volume = 6 + size = 8 + inputs = list( + "target temperature" = IC_PINTYPE_NUMBER, + "on" = IC_PINTYPE_BOOLEAN + ) + inputs_default = list("1" = 300) + spawn_flags = IC_SPAWN_RESEARCH + var/temperature = 293.15 + var/heater_coefficient = 0.1 + +/obj/item/integrated_circuit/atmospherics/tank/freezer/on_data_written() + temperature = max(73.15,min(293.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/tank/freezer/process() + var/tank_pressure = air_contents.return_pressure() + set_pin_data(IC_OUTPUT, 2, tank_pressure) + push_data() + + //Cool the tank if the power is on and the temp is above + if(!power_draw_idle || air_contents.temperature < temperature) + return + + air_contents.temperature = max(73.15,air_contents.temperature - (air_contents.temperature - temperature) * heater_coefficient) + + +// - heater tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank/freezer/heater + name = "heater tank" + desc = "Heats the gas it contains to a preset temperature." + volume = 6 + inputs = list( + "target temperature" = IC_PINTYPE_NUMBER, + "on" = IC_PINTYPE_BOOLEAN + ) + spawn_flags = IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/on_data_written() + temperature = max(293.15,min(573.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/process() + var/tank_pressure = air_contents.return_pressure() + set_pin_data(IC_OUTPUT, 2, tank_pressure) + push_data() + + //Heat the tank if the power is on or its temperature is below what is set + if(!power_draw_idle || air_contents.temperature > temperature) + return + + air_contents.temperature = min(573.15,air_contents.temperature + (temperature - air_contents.temperature) * heater_coefficient) + + +// - atmospheric cooler - // **works** +/obj/item/integrated_circuit/atmospherics/cooler + name = "atmospheric cooler circuit" + desc = "Cools the air around it." + volume = 6 + size = 13 + spawn_flags = IC_SPAWN_RESEARCH + inputs = list( + "target temperature" = IC_PINTYPE_NUMBER, + "on" = IC_PINTYPE_BOOLEAN + ) + var/temperature = 293.15 + var/heater_coefficient = 0.1 + +/obj/item/integrated_circuit/atmospherics/cooler/Initialize() + air_contents = new(volume) + START_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/atmospherics/cooler/Destroy() + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/atmospherics/cooler/on_data_written() + temperature = max(243.15,min(293.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/cooler/process() + set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) + push_data() + + + //Get the turf you're on and its gas mixture + var/turf/current_turf = get_turf(src) + if(!current_turf) + return + + var/datum/gas_mixture/turf_air = current_turf.return_air() + if(!power_draw_idle || turf_air.temperature < temperature) + return + + //Cool the gas + turf_air.temperature = max(243.15,turf_air.temperature - (turf_air.temperature - temperature) * heater_coefficient) + + +// - atmospheric heater - // **works** +/obj/item/integrated_circuit/atmospherics/cooler/heater + name = "atmospheric heater circuit" + desc = "Heats the air around it." + +/obj/item/integrated_circuit/atmospherics/cooler/heater/on_data_written() + temperature = max(293.15,min(323.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/cooler/heater/process() + set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) + push_data() + + //Get the turf and its air mixture + var/turf/current_turf = get_turf(src) + if(!current_turf) + return + + var/datum/gas_mixture/turf_air = current_turf.return_air() + if(!power_draw_idle || turf_air.temperature > temperature) + return + + //Heat the gas + turf_air.temperature = min(323.15,turf_air.temperature + (temperature - turf_air.temperature) * heater_coefficient) + + +// - tank slot - // **works** +/obj/item/integrated_circuit/input/tank_slot + category_text = "Atmospherics" + cooldown_per_use = 1 + name = "tank slot" + desc = "Lets you add a tank to your assembly and remove it even when the assembly is closed." + extended_desc = "It can help you extract gases easier." + complexity = 25 + size = 30 + inputs = list() + outputs = list( + "pressure used" = IC_PINTYPE_NUMBER, + "current tank" = IC_PINTYPE_REF + ) + activators = list( + "push ref" = IC_PINTYPE_PULSE_IN, + "on insert" = IC_PINTYPE_PULSE_OUT, + "on remove" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + + can_be_asked_input = TRUE + demands_object_input = TRUE + can_input_object_when_closed = TRUE + + var/obj/item/tank/internals/current_tank + +/obj/item/integrated_circuit/input/tank_slot/Initialize() + START_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/input/tank_slot/process() + push_pressure() + +/obj/item/integrated_circuit/input/tank_slot/attackby(var/obj/item/tank/internals/I, var/mob/living/user) + //Check if it truly is a tank + if(!istype(I,/obj/item/tank/internals)) + to_chat(user,"The [I.name] doesn't seem to fit in here.") + return + + //Check if there is no other tank already inside + if(current_tank) + to_chat(user,"There is already a gas tank inside.") + return + + //The current tank is the one we just attached, its location is inside the circuit + current_tank = I + user.transferItemToLoc(I,src) + to_chat(user,"You put the [I.name] inside the tank slot.") + + //Set the pin to a weak reference of the current tank + push_pressure() + set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) + push_data() + do_work(1) + + +/obj/item/integrated_circuit/input/tank_slot/ask_for_input(mob/user) + attack_self(user) + +/obj/item/integrated_circuit/input/tank_slot/attack_self(mob/user) + //Check if no tank attached + if(!current_tank) + to_chat(user, "There is currently no tank attached.") + return + + //Remove tank and put in user's hands/location + to_chat(user, "You take [current_tank] out of the tank slot.") + user.put_in_hands(current_tank) + current_tank = null + + //Remove tank reference + push_pressure() + set_pin_data(IC_OUTPUT, 2, null) + push_data() + do_work(2) + +/obj/item/integrated_circuit/input/tank_slot/do_work() + set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) + push_data() + +/obj/item/integrated_circuit/input/tank_slot/proc/push_pressure() + if(!current_tank) + set_pin_data(IC_OUTPUT, 1, 0) + return + + var/datum/gas_mixture/tank_air = current_tank.return_air() + if(!tank_air) + set_pin_data(IC_OUTPUT, 1, 0) + return + + set_pin_data(IC_OUTPUT, 1, tank_air.return_pressure()) + push_data() + + +#undef SOURCE_TO_TARGET +#undef TARGET_TO_SOURCE +#undef PUMP_EFFICIENCY +#undef TANK_FAILURE_PRESSURE +#undef PUMP_MAX_PRESSURE +#undef PUMP_MAX_VOLUME diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index a7c58e8669..b258a15972 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -1165,8 +1165,8 @@ var/list/gas_names = list() var/list/gas_amounts = list() for(var/id in gases) - var/name = gases[id][GAS_META][META_GAS_NAME] - var/amt = round(gases[id][MOLES], 0.001) + var/name = GLOB.meta_gas_names[id] + var/amt = round(gases[id], 0.001) gas_names.Add(name) gas_amounts.Add(amt) diff --git a/code/modules/integrated_electronics/subtypes/weaponized.dm b/code/modules/integrated_electronics/subtypes/weaponized.dm index f9259359a3..7bccbfafcd 100644 --- a/code/modules/integrated_electronics/subtypes/weaponized.dm +++ b/code/modules/integrated_electronics/subtypes/weaponized.dm @@ -215,7 +215,7 @@ The 'fire' activator will cause the mechanism to attempt to launch objects at the coordinates, if possible. Note that the \ projectile needs to be inside the machine, or on an adjacent tile, and must be medium sized or smaller. The assembly \ must also be a gun if you wish to launch something while the assembly is in hand." - complexity = 75 + complexity = 50 w_class = WEIGHT_CLASS_SMALL size = 4 cooldown_per_use = 30 @@ -306,7 +306,7 @@ desc = "Used to stun a target holding the device via electricity." icon_state = "power_relay" extended_desc = "Attempts to stun the holder of this device, with the strength input being the strength of the stun, from 1 to 70." - complexity = 60 + complexity = 30 size = 4 inputs = list("strength" = IC_PINTYPE_NUMBER) activators = list("stun" = IC_PINTYPE_PULSE_IN, "on success" = IC_PINTYPE_PULSE_OUT, "on fail" = IC_PINTYPE_PULSE_OUT) @@ -340,4 +340,4 @@ var/mob/living/carbon/human/H = L H.forcesay(GLOB.hit_appends) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/language/slime.dm b/code/modules/language/slime.dm index 7171c07b39..cca56ca933 100644 --- a/code/modules/language/slime.dm +++ b/code/modules/language/slime.dm @@ -5,6 +5,7 @@ ask_verb = "warbles" exclaim_verb = "warbles" key = "k" + flags = TONGUELESS_SPEECH syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","*","!") default_priority = 70 diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index ca313182b6..0d6c337444 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -31,7 +31,7 @@ force = 10 throwforce = 7 slot_flags = ITEM_SLOT_BELT - w_class = WEIGHT_CLASS_NORMAL + w_class = WEIGHT_CLASS_SMALL materials = list(MAT_METAL=1000) /obj/item/pickaxe/silver @@ -41,6 +41,7 @@ toolspeed = 0.5 //mines faster than a normal pickaxe, bought from mining vendor desc = "A silver-plated pickaxe that mines slightly faster than standard-issue." force = 17 + materials = list(MAT_SILVER=4000) /obj/item/pickaxe/diamond name = "diamond-tipped pickaxe" @@ -49,6 +50,7 @@ toolspeed = 0.3 desc = "A pickaxe with a diamond pick head. Extremely robust at cracking rock walls and digging up dirt." force = 19 + materials = list(MAT_DIAMOND=4000) /obj/item/pickaxe/drill name = "mining drill" @@ -75,12 +77,13 @@ /obj/item/pickaxe/drill/cyborg/diamond //This is the BORG version! name = "diamond-tipped cyborg mining drill" //To inherit the NODROP_1 flag, and easier to change borg specific drill mechanics. icon_state = "diamonddrill" - toolspeed = 0.2 + toolspeed = 0.1 /obj/item/pickaxe/drill/jackhammer name = "sonic jackhammer" icon_state = "jackhammer" item_state = "jackhammer" + w_class = WEIGHT_CLASS_HUGE toolspeed = 0.1 //the epitome of powertools. extremely fast mining, laughs at puny walls usesound = 'sound/weapons/sonic_jackhammer.ogg' hitsound = 'sound/weapons/sonic_jackhammer.ogg' @@ -97,12 +100,12 @@ slot_flags = ITEM_SLOT_BELT force = 8 tool_behaviour = TOOL_SHOVEL - toolspeed = 1 + toolspeed = 0.1 //Can only dig ash and thats about it, out classed by the picks and drills no more! usesound = 'sound/effects/shovel_dig.ogg' throwforce = 4 item_state = "shovel" w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=50) + materials = list(MAT_METAL=350) attack_verb = list("bashed", "bludgeoned", "thrashed", "whacked") sharpness = IS_SHARP @@ -124,6 +127,8 @@ item_state = "spade" lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' + toolspeed = 0.5 force = 5 throwforce = 7 + materials = list(MAT_METAL=50) w_class = WEIGHT_CLASS_SMALL diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm index e23c3deb58..f63b459f10 100644 --- a/code/modules/mining/equipment/resonator.dm +++ b/code/modules/mining/equipment/resonator.dm @@ -8,10 +8,10 @@ righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi' desc = "A handheld device that creates small fields of energy that resonate until they detonate, crushing rock. It does increased damage in low pressure." w_class = WEIGHT_CLASS_NORMAL - force = 15 + force = 18 throwforce = 10 var/burst_time = 30 - var/fieldlimit = 4 + var/fieldlimit = 6 var/list/fields = list() var/quick_burst_mod = 0.8 @@ -20,7 +20,8 @@ desc = "An upgraded version of the resonator that can produce more fields at once, as well as having no damage penalty for bursting a resonance field early." icon_state = "resonator_u" item_state = "resonator_u" - fieldlimit = 6 + force = 20 + fieldlimit = 8 quick_burst_mod = 1 /obj/item/resonator/attack_self(mob/user) diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm index 66c79ea0f0..f8d8d26328 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm @@ -439,6 +439,10 @@ name = "Tress Shoulder" icon_state = "hair_tressshoulder" +/datum/sprite_accessory/hair/longtwintails + name = "Twintails (Long)" + icon_state = "hair_longstraighttwintails" + /datum/sprite_accessory/hair/updo name = "Updo" icon_state = "hair_updo" diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm index 543fda9fd0..b8edd34ee9 100644 --- a/code/modules/mob/living/carbon/alien/life.dm +++ b/code/modules/mob/living/carbon/alien/life.dm @@ -15,25 +15,23 @@ var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME var/list/breath_gases = breath.gases - breath.assert_gases(/datum/gas/plasma, /datum/gas/oxygen) - //Partial pressure of the toxins in our breath - var/Toxins_pp = (breath_gases[/datum/gas/plasma][MOLES]/breath.total_moles())*breath_pressure + var/Toxins_pp = (breath_gases[/datum/gas/plasma]/breath.total_moles())*breath_pressure if(Toxins_pp > tox_detect_threshold) // Detect toxins in air - adjustPlasma(breath_gases[/datum/gas/plasma][MOLES]*250) + adjustPlasma(breath_gases[/datum/gas/plasma]*250) throw_alert("alien_tox", /obj/screen/alert/alien_tox) - toxins_used = breath_gases[/datum/gas/plasma][MOLES] + toxins_used = breath_gases[/datum/gas/plasma] else clear_alert("alien_tox") //Breathe in toxins and out oxygen - breath_gases[/datum/gas/plasma][MOLES] -= toxins_used - breath_gases[/datum/gas/oxygen][MOLES] += toxins_used + breath_gases[/datum/gas/plasma] -= toxins_used + breath_gases[/datum/gas/oxygen] += toxins_used - breath.garbage_collect() + GAS_GARBAGE_COLLECT(breath.gases) //BREATH TEMPERATURE handle_breath_temperature(breath) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 49d2722323..13da7b913c 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -260,7 +260,7 @@ else return - if(do_mob(usr, src, POCKET_STRIP_DELAY/delay_denominator)) //placing an item into the pocket is 4 times faster + if(do_mob(usr, src, POCKET_STRIP_DELAY/delay_denominator, ignorehelditem = TRUE)) //placing an item into the pocket is 4 times faster if(pocket_item) if(pocket_item == (pocket_id == SLOT_R_STORE ? r_store : l_store)) //item still in the pocket we search dropItemToGround(pocket_item) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index b66ebdb001..d280c1d311 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -256,7 +256,7 @@ var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) if(!affecting) affecting = get_bodypart(BODY_ZONE_CHEST) - var/armor_block = run_armor_check(affecting, "melee","","",10) + var/armor_block = run_armor_check(affecting, "melee", null, null,10) playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) visible_message("[M] has slashed at [src]!", \ diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index d68c936909..526304032b 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1475,9 +1475,9 @@ GLOBAL_LIST_EMPTY(roundstart_races) else if(aim_for_mouth && ( target_on_help || target_restrained || target_aiming_for_mouth)) playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1) - user.visible_message( - "[user] slaps [target] in the face!", - "You slap [user == target ? "yourself" : target] in the face! ",\ + user.visible_message(\ + "\The [user] slaps \the [target] in the face!",\ + "You slap [user == target ? "yourself" : "\the [target]"] in the face! ",\ "You hear a slap." ) if (!target.has_trait(TRAIT_NYMPHO)) @@ -1486,20 +1486,33 @@ GLOBAL_LIST_EMPTY(roundstart_races) user.adjustStaminaLossBuffered(3) return FALSE else if(aim_for_groin && (target == user || target.lying || same_dir) && (target_on_help || target_restrained || target_aiming_for_groin)) + user.do_attack_animation(target, ATTACK_EFFECT_ASS_SLAP) + user.adjustStaminaLossBuffered(3) + if(target.has_trait(TRAIT_ASSBLASTUSA)) + var/hit_zone = (user.held_index_to_dir(user.active_hand_index) == "l" ? "l_":"r_") + "arm" + user.adjustStaminaLoss(20, affected_zone = hit_zone) + user.visible_message(\ + "\The [user] slaps \the [target]'s ass, but their hand bounces off like they hit metal!",\ + "You slap [user == target ? "your" : "\the [target]'s"] ass, but feel an intense amount of pain as you realise their buns are harder than steel!",\ + "You hear a slap." + ) + playsound(target.loc, 'sound/weapons/tap.ogg', 50, 1, -1) + user.emote("scream") + return FALSE + playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1) - user.visible_message( - "[user] slaps [target]'s ass!", - "You slap [user == target ? "your" : target + "'s"] ass! ",\ + user.visible_message(\ + "\The [user] slaps \the [target]'s ass!",\ + "You slap [user == target ? "your" : "\the [target]'s"] ass!",\ "You hear a slap." ) if (target.canbearoused) target.adjustArousalLoss(5) - if (target.getArousalLoss() >= 100 && ishuman(target) && target.has_trait(TRAIT_NYMPHO) && target.has_dna()) + if (target.getArousalLoss() >= 100 && ishuman(target) && target.has_trait(TRAIT_MASO) && target.has_dna()) target.mob_climax(forced_climax=TRUE) if (!target.has_trait(TRAIT_NYMPHO)) stop_wagging_tail(target) - user.do_attack_animation(target, ATTACK_EFFECT_ASS_SLAP) - user.adjustStaminaLossBuffered(3) + return FALSE else if(attacker_style && attacker_style.disarm_act(user,target)) return 1 @@ -1666,8 +1679,11 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(H.stat == CONSCIOUS && H != user && prob(I.force + ((100 - H.health) * 0.5))) // rev deconversion through blunt trauma. var/datum/antagonist/rev/rev = H.mind.has_antag_datum(/datum/antagonist/rev) + var/datum/antagonist/gang/gang = H.mind.has_antag_datum(/datum/antagonist/gang/) if(rev) rev.remove_revolutionary(FALSE, user) + if(gang) + H.mind.remove_antag_datum(/datum/antagonist/gang) if(bloody) //Apply blood if(H.wear_mask) @@ -1727,6 +1743,11 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(BP) if(damage > 0 ? BP.receive_damage(damage * hit_percent * brutemod * H.physiology.brute_mod, 0) : BP.heal_damage(abs(damage * hit_percent * brutemod * H.physiology.brute_mod), 0)) H.update_damage_overlays() + if(H.has_trait(TRAIT_MASO)) + H.adjustArousalLoss(damage * brutemod * H.physiology.brute_mod) + if (H.getArousalLoss() >= 100 && ishuman(H) && H.has_dna()) + H.mob_climax(forced_climax=TRUE) + else//no bodypart, we deal damage with a more general method. H.adjustBruteLoss(damage * hit_percent * brutemod * H.physiology.brute_mod) if(BURN) diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index c5a64ebd13..30bf705547 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -97,6 +97,7 @@ brutemod = 0.9 /datum/species/lizard/ashwalker/on_species_gain(mob/living/carbon/human/C, datum/species/old_species) - if((C.dna.features["spines"] != "None" ) && (C.dna.features["tail"] == "None")) //tbh, it's kinda ugly for them not to have a tail yet have floating spines - C.dna.features["tail"] = "Smooth" + if((C.dna.features["spines"] != "None" ) && (C.dna.features["tail_lizard"] == "None")) //tbh, it's kinda ugly for them not to have a tail yet have floating spines + C.dna.features["tail_lizard"] = "Smooth" + C.update_body() return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index 3095ca48ae..afd6e4e7f5 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -33,7 +33,7 @@ if((!istype(H.w_uniform, /obj/item/clothing/under/plasmaman) || !istype(H.head, /obj/item/clothing/head/helmet/space/plasmaman)) && !atmos_sealed) if(environment) if(environment.total_moles()) - if(environment.gases[/datum/gas/oxygen] && (environment.gases[/datum/gas/oxygen][MOLES]) >= 1) //Same threshhold that extinguishes fire + if(environment.gases[/datum/gas/oxygen] && (environment.gases[/datum/gas/oxygen]) >= 1) //Same threshhold that extinguishes fire H.adjust_fire_stacks(0.5) if(!H.on_fire && H.fire_stacks > 0) H.visible_message("[H]'s body reacts with the atmosphere and bursts into flames!","Your body reacts with the atmosphere and bursts into flame!") diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 9b5375035e..ef5b9045ca 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -660,6 +660,8 @@ generate/load female uniform sprites matching all previously decided variables . += "-[BP.dmg_overlay_type]" if(BP.body_markings) . += "-[BP.body_markings]" + else + . += "-no_marking" if(has_trait(TRAIT_HUSK)) . += "-husk" diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index e757e6dcf4..9c0eca4e8a 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -150,10 +150,9 @@ var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME var/list/breath_gases = breath.gases - breath.assert_gases(/datum/gas/oxygen, /datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/bz) - var/O2_partialpressure = (breath_gases[/datum/gas/oxygen][MOLES]/breath.total_moles())*breath_pressure - var/Toxins_partialpressure = (breath_gases[/datum/gas/plasma][MOLES]/breath.total_moles())*breath_pressure - var/CO2_partialpressure = (breath_gases[/datum/gas/carbon_dioxide][MOLES]/breath.total_moles())*breath_pressure + var/O2_partialpressure = (breath_gases[/datum/gas/oxygen]/breath.total_moles())*breath_pressure + var/Toxins_partialpressure = (breath_gases[/datum/gas/plasma]/breath.total_moles())*breath_pressure + var/CO2_partialpressure = (breath_gases[/datum/gas/carbon_dioxide]/breath.total_moles())*breath_pressure //OXYGEN @@ -177,7 +176,7 @@ var/ratio = 1 - O2_partialpressure/safe_oxy_min adjustOxyLoss(min(5*ratio, 3)) failed_last_breath = 1 - oxygen_used = breath_gases[/datum/gas/oxygen][MOLES]*ratio + oxygen_used = breath_gases[/datum/gas/oxygen]*ratio else adjustOxyLoss(3) failed_last_breath = 1 @@ -189,12 +188,12 @@ o2overloadtime = 0 //reset our counter for this too if(health >= crit_threshold) adjustOxyLoss(-5) - oxygen_used = breath_gases[/datum/gas/oxygen][MOLES] + oxygen_used = breath_gases[/datum/gas/oxygen] clear_alert("not_enough_oxy") SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation") - breath_gases[/datum/gas/oxygen][MOLES] -= oxygen_used - breath_gases[/datum/gas/carbon_dioxide][MOLES] += oxygen_used + breath_gases[/datum/gas/oxygen] -= oxygen_used + breath_gases[/datum/gas/carbon_dioxide] += oxygen_used //CARBON DIOXIDE if(CO2_partialpressure > safe_co2_max) @@ -213,7 +212,7 @@ //TOXINS/PLASMA if(Toxins_partialpressure > safe_tox_max) - var/ratio = (breath_gases[/datum/gas/plasma][MOLES]/safe_tox_max) * 10 + var/ratio = (breath_gases[/datum/gas/plasma]/safe_tox_max) * 10 adjustToxLoss(CLAMP(ratio, MIN_TOXIC_GAS_DAMAGE, MAX_TOXIC_GAS_DAMAGE)) throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) else @@ -221,7 +220,7 @@ //NITROUS OXIDE if(breath_gases[/datum/gas/nitrous_oxide]) - var/SA_partialpressure = (breath_gases[/datum/gas/nitrous_oxide][MOLES]/breath.total_moles())*breath_pressure + var/SA_partialpressure = (breath_gases[/datum/gas/nitrous_oxide]/breath.total_moles())*breath_pressure if(SA_partialpressure > SA_para_min) Unconscious(60) if(SA_partialpressure > SA_sleep_min) @@ -232,7 +231,7 @@ //BZ (Facepunch port of their Agent B) if(breath_gases[/datum/gas/bz]) - var/bz_partialpressure = (breath_gases[/datum/gas/bz][MOLES]/breath.total_moles())*breath_pressure + var/bz_partialpressure = (breath_gases[/datum/gas/bz]/breath.total_moles())*breath_pressure if(bz_partialpressure > 1) hallucination += 10 else if(bz_partialpressure > 0.01) @@ -240,17 +239,17 @@ //TRITIUM if(breath_gases[/datum/gas/tritium]) - var/tritium_partialpressure = (breath_gases[/datum/gas/tritium][MOLES]/breath.total_moles())*breath_pressure + var/tritium_partialpressure = (breath_gases[/datum/gas/tritium]/breath.total_moles())*breath_pressure radiation += tritium_partialpressure/10 //NITRYL if(breath_gases[/datum/gas/nitryl]) - var/nitryl_partialpressure = (breath_gases[/datum/gas/nitryl][MOLES]/breath.total_moles())*breath_pressure + var/nitryl_partialpressure = (breath_gases[/datum/gas/nitryl]/breath.total_moles())*breath_pressure adjustFireLoss(nitryl_partialpressure/4) //MIASMA if(breath_gases[/datum/gas/miasma]) - var/miasma_partialpressure = (breath_gases[/datum/gas/miasma][MOLES]/breath.total_moles())*breath_pressure + var/miasma_partialpressure = (breath_gases[/datum/gas/miasma]/breath.total_moles())*breath_pressure if(prob(1 * miasma_partialpressure)) var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3) @@ -292,7 +291,7 @@ - breath.garbage_collect() + GAS_GARBAGE_COLLECT(breath.gases) //BREATH TEMPERATURE handle_breath_temperature(breath) @@ -345,8 +344,7 @@ var/list/cached_gases = miasma_turf.air.gases - ASSERT_GAS(/datum/gas/miasma, miasma_turf.air) - cached_gases[/datum/gas/miasma][MOLES] += 0.02 + cached_gases[/datum/gas/miasma] += 0.02 /mob/living/carbon/proc/handle_blood() return diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 9ef4c1567a..cb7b9a469c 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -109,7 +109,7 @@ ExtinguishMob() return var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment - if(!G.gases[/datum/gas/oxygen] || G.gases[/datum/gas/oxygen][MOLES] < 1) + if(G.gases[/datum/gas/oxygen] < 1) ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire return var/turf/location = get_turf(src) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index b9b6424092..8ae1bd3ff0 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -103,21 +103,23 @@ //CIT CHANGES START HERE - makes it so resting stops you from moving through standing folks without a short delay if(resting && !L.resting) - if(attemptingcrawl) - return TRUE - if(getStaminaLoss() >= STAMINA_SOFTCRIT) - to_chat(src, "You're too exhausted to crawl under [L].") - return TRUE - attemptingcrawl = TRUE var/origtargetloc = L.loc - visible_message("[src] is attempting to crawl under [L].", "You are now attempting to crawl under [L].") - if(do_after(src, CRAWLUNDER_DELAY, target = src)) - if(resting) - var/src_passmob = (pass_flags & PASSMOB) - pass_flags |= PASSMOB - Move(origtargetloc) - if(!src_passmob) - pass_flags &= ~PASSMOB + if(!pulledby) + if(attemptingcrawl) + return TRUE + if(getStaminaLoss() >= STAMINA_SOFTCRIT) + to_chat(src, "You're too exhausted to crawl under [L].") + return TRUE + attemptingcrawl = TRUE + visible_message("[src] is attempting to crawl under [L].", "You are now attempting to crawl under [L].") + if(!do_after(src, CRAWLUNDER_DELAY, target = src) || !resting) + attemptingcrawl = FALSE + return TRUE + var/src_passmob = (pass_flags & PASSMOB) + pass_flags |= PASSMOB + Move(origtargetloc) + if(!src_passmob) + pass_flags &= ~PASSMOB attemptingcrawl = FALSE return TRUE //END OF CIT CHANGES @@ -698,7 +700,7 @@ who.visible_message("[src] tries to remove [who]'s [what.name].", \ "[src] tries to remove [who]'s [what.name].") what.add_fingerprint(src) - if(do_mob(src, who, what.strip_delay)) + if(do_mob(src, who, what.strip_delay, ignorehelditem = TRUE)) if(what && Adjacent(who)) if(islist(where)) var/list/L = where @@ -1180,3 +1182,32 @@ update_transform() if("lighting_alpha") sync_lighting_plane_alpha() + +/mob/living/proc/do_adrenaline( + stamina_boost = 150, + put_on_feet = TRUE, + clamp_unconscious_to = 0, + clamp_immobility_to = 0, + reset_misc = TRUE, + healing_chems = list("inaprovaline" = 3, "synaptizine" = 10, "omnizine" = 10, "stimulants" = 10), + message = "You feel a surge of energy!" + ) + if(AmountSleeping() > clamp_unconscious_to) + SetSleeping(clamp_unconscious_to) + if(AmountUnconscious() > clamp_unconscious_to) + SetUnconscious(clamp_unconscious_to) + if(AmountStun() > clamp_immobility_to) + SetStun(clamp_immobility_to) + if(AmountKnockdown() > clamp_immobility_to) + SetKnockdown(clamp_immobility_to) + adjustStaminaLoss(max(0, -stamina_boost)) + if(put_on_feet) + resting = FALSE + lying = FALSE + if(reset_misc) + stuttering = 0 + updatehealth() + update_stamina() + update_canmove() + for(var/chem in healing_chems) + reagents.add_reagent(chem, healing_chems[chem]) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 643c91b95a..0d74094d81 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -1,24 +1,18 @@ -/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = null, soften_text = null, armour_penetration, penetrated_text) +/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = "Your armor absorbs the blow!", soften_text = "Your armor softens the blow!", armour_penetration, penetrated_text = "Your armor was penetrated!") var/armor = getarmor(def_zone, attack_flag) //the if "armor" check is because this is used for everything on /living, including humans if(armor && armour_penetration) armor = max(0, armor - armour_penetration) if(penetrated_text) - to_chat(src, "[penetrated_text]") - else - to_chat(src, "Your armor was penetrated!") + to_chat(src, "[penetrated_text]") else if(armor >= 100) if(absorb_text) - to_chat(src, "[absorb_text]") - else - to_chat(src, "Your armor absorbs the blow!") + to_chat(src, "[absorb_text]") else if(armor > 0) if(soften_text) - to_chat(src, "[soften_text]") - else - to_chat(src, "Your armor softens the blow!") + to_chat(src, "[soften_text]") return armor @@ -43,7 +37,7 @@ return /mob/living/bullet_act(obj/item/projectile/P, def_zone) - var/armor = run_armor_check(def_zone, P.flag, "","",P.armour_penetration) + var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null) if(!P.nodamage) apply_damage(P.damage, P.damage_type, def_zone, armor) if(P.dismemberment) diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 90070f1d5d..0e8d37a807 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -544,9 +544,9 @@ if (total_moles) for(var/id in env_gases) - var/gas_level = env_gases[id][MOLES]/total_moles + var/gas_level = env_gases[id]/total_moles if(gas_level > 0.01) - dat += "[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_level*100)]%
" + dat += "[GLOB.meta_gas_names[id]]: [round(gas_level*100)]%
" dat += "Temperature: [round(environment.temperature-T0C)]°C
" dat += "Refresh Reading
" dat += "
" diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 293e3e21fd..6577553a6a 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -30,7 +30,7 @@ Cross Blasts and the AoE burst gain additional range as Hierophant loses health, When Hierophant dies, it stops trying to murder you and shrinks into a small form, which, while much weaker, is still quite effective. - The smaller club can place a teleport beacon, allowing the user to teleport themself and their allies to the beacon. -Difficulty: Hard +Difficulty: Normal */ @@ -47,11 +47,11 @@ Difficulty: Hard icon = 'icons/mob/lavaland/hierophant_new.dmi' faction = list("boss") //asteroid mobs? get that shit out of my beautiful square house speak_emote = list("preaches") - armour_penetration = 50 + armour_penetration = 75 melee_damage_lower = 15 - melee_damage_upper = 15 + melee_damage_upper = 20 speed = 1 - move_to_delay = 10 + move_to_delay = 11 ranged = 1 ranged_cooldown_time = 40 aggro_vision_range = 21 //so it can see to one side of the arena to the other diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm index 3d10bfd121..fc51b9afe4 100644 --- a/code/modules/mob/living/simple_animal/hostile/tree.dm +++ b/code/modules/mob/living/simple_animal/hostile/tree.dm @@ -1,71 +1,71 @@ -/mob/living/simple_animal/hostile/tree - name = "pine tree" - desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." - icon = 'icons/obj/flora/pinetrees.dmi' - icon_state = "pine_1" - icon_living = "pine_1" - icon_dead = "pine_1" - icon_gib = "pine_1" - gender = NEUTER - speak_chance = 0 - turns_per_move = 5 - response_help = "brushes" - response_disarm = "pushes" - response_harm = "hits" - speed = 1 - maxHealth = 250 - health = 250 - mob_size = MOB_SIZE_LARGE - - pixel_x = -16 - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - speak_emote = list("pines") - emote_taunt = list("growls") - taunt_chance = 20 - - atmos_requirements = list("min_oxy" = 2, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 5 - minbodytemp = 0 - maxbodytemp = 1200 - - faction = list("hostile") - deathmessage = "is hacked into pieces!" - loot = list(/obj/item/stack/sheet/mineral/wood) - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = 1 - -/mob/living/simple_animal/hostile/tree/Life() - ..() - if(isopenturf(loc)) - var/turf/open/T = src.loc - if(T.air && T.air.gases[/datum/gas/carbon_dioxide]) - var/co2 = T.air.gases[/datum/gas/carbon_dioxide][MOLES] - if(co2 > 0) - if(prob(25)) - var/amt = min(co2, 9) - T.air.gases[/datum/gas/carbon_dioxide][MOLES] -= amt - T.atmos_spawn_air("o2=[amt]") - -/mob/living/simple_animal/hostile/tree/AttackingTarget() - . = ..() - if(iscarbon(target)) - var/mob/living/carbon/C = target - if(prob(15)) - C.Knockdown(60) - C.visible_message("\The [src] knocks down \the [C]!", \ - "\The [src] knocks you down!") - -/mob/living/simple_animal/hostile/tree/festivus - name = "festivus pole" - desc = "Serenity now... SERENITY NOW!" - icon_state = "festivus_pole" - icon_living = "festivus_pole" - icon_dead = "festivus_pole" - icon_gib = "festivus_pole" - loot = list(/obj/item/stack/rods) - speak_emote = list("polls") +/mob/living/simple_animal/hostile/tree + name = "pine tree" + desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." + icon = 'icons/obj/flora/pinetrees.dmi' + icon_state = "pine_1" + icon_living = "pine_1" + icon_dead = "pine_1" + icon_gib = "pine_1" + gender = NEUTER + speak_chance = 0 + turns_per_move = 5 + response_help = "brushes" + response_disarm = "pushes" + response_harm = "hits" + speed = 1 + maxHealth = 250 + health = 250 + mob_size = MOB_SIZE_LARGE + + pixel_x = -16 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("pines") + emote_taunt = list("growls") + taunt_chance = 20 + + atmos_requirements = list("min_oxy" = 2, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 5 + minbodytemp = 0 + maxbodytemp = 1200 + + faction = list("hostile") + deathmessage = "is hacked into pieces!" + loot = list(/obj/item/stack/sheet/mineral/wood) + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = 1 + +/mob/living/simple_animal/hostile/tree/Life() + ..() + if(isopenturf(loc)) + var/turf/open/T = src.loc + if(T.air && T.air.gases[/datum/gas/carbon_dioxide]) + var/co2 = T.air.gases[/datum/gas/carbon_dioxide] + if(co2 > 0) + if(prob(25)) + var/amt = min(co2, 9) + T.air.gases[/datum/gas/carbon_dioxide] -= amt + T.atmos_spawn_air("o2=[amt]") + +/mob/living/simple_animal/hostile/tree/AttackingTarget() + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/C = target + if(prob(15)) + C.Knockdown(60) + C.visible_message("\The [src] knocks down \the [C]!", \ + "\The [src] knocks you down!") + +/mob/living/simple_animal/hostile/tree/festivus + name = "festivus pole" + desc = "Serenity now... SERENITY NOW!" + icon_state = "festivus_pole" + icon_living = "festivus_pole" + icon_dead = "festivus_pole" + icon_gib = "festivus_pole" + loot = list(/obj/item/stack/rods) + speak_emote = list("polls") diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index fcb201d0ad..2517d2438d 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -1,589 +1,588 @@ -/mob/living/simple_animal - name = "animal" - icon = 'icons/mob/animal.dmi' - health = 20 - maxHealth = 20 - gender = PLURAL //placeholder - - status_flags = CANPUSH - - var/icon_living = "" - var/icon_dead = "" //icon when the animal is dead. Don't use animated icons for this. - var/icon_gib = null //We only try to show a gibbing animation if this exists. - - var/list/speak = list() - var/list/speak_emote = list()// Emotes while speaking IE: Ian [emote], [text] -- Ian barks, "WOOF!". Spoken text is generated from the speak variable. - var/speak_chance = 0 - var/list/emote_hear = list() //Hearable emotes - var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps - - var/turns_per_move = 1 - var/turns_since_move = 0 - var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. - var/wander = 1 // Does the mob wander around when idle? - var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. - - //Interaction - var/response_help = "pokes" - var/response_disarm = "shoves" - var/response_harm = "hits" - var/harm_intent_damage = 3 - var/force_threshold = 0 //Minimum force required to deal any damage - - //Temperature effect - var/minbodytemp = 250 - var/maxbodytemp = 350 - - //Healable by medical stacks? Defaults to yes. - var/healable = 1 - - //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage - var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum - var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above - - //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly - var/melee_damage_lower = 0 - var/melee_damage_upper = 0 - var/obj_damage = 0 //how much damage this simple animal does to objects, if any - var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value - var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. - var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source - var/attacktext = "attacks" - var/attack_sound = null - var/friendly = "nuzzles" //If the mob does no damage with it's attack - var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls - - var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster - - //Hot simple_animal baby making vars - var/list/childtype = null - var/next_scan_time = 0 - var/animal_species //Sorry, no spider+corgi buttbabies. - - //simple_animal access - var/obj/item/card/id/access_card = null //innate access uses an internal ID card - var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against - var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood - - var/mob/living/simple_animal/hostile/spawner/nest - - var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions - - var/list/loot = list() //list of things spawned at mob's loc when it dies - var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses - var/deathmessage = "" - var/death_sound = null //The sound played on death - - var/allow_movement_on_non_turfs = FALSE - - var/attacked_sound = "punch" //Played when someone punches the creature - - var/dextrous = FALSE //If the creature has, and can use, hands - var/dextrous_hud_type = /datum/hud/dextrous - var/datum/personal_crafting/handcrafting - - var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever), AI_Z_OFF (Temporarily off due to nonpresence of players) - - var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. - - //domestication - var/tame = 0 - - var/my_z // I don't want to confuse this with client registered_z - - var/do_footstep = FALSE - -/mob/living/simple_animal/Initialize() - . = ..() - GLOB.simple_animals[AIStatus] += src - handcrafting = new() - if(gender == PLURAL) - gender = pick(MALE,FEMALE) - if(!real_name) - real_name = name - if(!loc) - stack_trace("Simple animal being instantiated in nullspace") - update_simplemob_varspeed() - -/mob/living/simple_animal/Destroy() - GLOB.simple_animals[AIStatus] -= src - if (SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) - SSnpcpool.currentrun -= src - - if(nest) - nest.spawned_mobs -= src - nest = null - - var/turf/T = get_turf(src) - if (T && AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - - return ..() - -/mob/living/simple_animal/initialize_footstep() - if(do_footstep) - ..() - -/mob/living/simple_animal/updatehealth() - ..() - health = CLAMP(health, 0, maxHealth) - -/mob/living/simple_animal/update_stat() - if(status_flags & GODMODE) - return - if(stat != DEAD) - if(health <= 0) - death() - else - stat = CONSCIOUS - med_hud_set_status() - - -/mob/living/simple_animal/handle_status_effects() - ..() - if(stuttering) - stuttering = 0 - -/mob/living/simple_animal/proc/handle_automated_action() - set waitfor = FALSE - return - -/mob/living/simple_animal/proc/handle_automated_movement() - set waitfor = FALSE - if(!stop_automated_movement && wander) - if((isturf(src.loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - if(!(stop_automated_movement_when_pulled && pulledby)) //Some animals don't move when pulled - var/anydir = pick(GLOB.cardinals) - if(Process_Spacemove(anydir)) - Move(get_step(src, anydir), anydir) - turns_since_move = 0 - return 1 - -/mob/living/simple_animal/proc/handle_automated_speech(var/override) - set waitfor = FALSE - if(speak_chance) - if(prob(speak_chance) || override) - if(speak && speak.len) - if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) - var/length = speak.len - if(emote_hear && emote_hear.len) - length += emote_hear.len - if(emote_see && emote_see.len) - length += emote_see.len - var/randomValue = rand(1,length) - if(randomValue <= speak.len) - say(pick(speak), forced = "poly") - else - randomValue -= speak.len - if(emote_see && randomValue <= emote_see.len) - emote("me [pick(emote_see)]", 1) - else - emote("me [pick(emote_hear)]", 2) - else - say(pick(speak), forced = "poly") - else - if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - emote("me", 1, pick(emote_see)) - if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) - emote("me", 2, pick(emote_hear)) - if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - var/length = emote_hear.len + emote_see.len - var/pick = rand(1,length) - if(pick <= emote_see.len) - emote("me", 1, pick(emote_see)) - else - emote("me", 2, pick(emote_hear)) - - -/mob/living/simple_animal/proc/environment_is_safe(datum/gas_mixture/environment, check_temp = FALSE) - . = TRUE - - if(pulledby && pulledby.grab_state >= GRAB_KILL && atmos_requirements["min_oxy"]) - . = FALSE //getting choked - - if(isturf(src.loc) && isopenturf(src.loc)) - var/turf/open/ST = src.loc - if(ST.air) - var/ST_gases = ST.air.gases - ST.air.assert_gases(arglist(GLOB.hardcoded_gases)) - - var/tox = ST_gases[/datum/gas/plasma][MOLES] - var/oxy = ST_gases[/datum/gas/oxygen][MOLES] - var/n2 = ST_gases[/datum/gas/nitrogen][MOLES] - var/co2 = ST_gases[/datum/gas/carbon_dioxide][MOLES] - - ST.air.garbage_collect() - - if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) - . = FALSE - else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) - . = FALSE - else if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) - . = FALSE - else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) - . = FALSE - else if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) - . = FALSE - else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) - . = FALSE - else if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) - . = FALSE - else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) - . = FALSE - else - if(atmos_requirements["min_oxy"] || atmos_requirements["min_tox"] || atmos_requirements["min_n2"] || atmos_requirements["min_co2"]) - . = FALSE - - if(check_temp) - var/areatemp = get_temperature(environment) - if((areatemp < minbodytemp) || (areatemp > maxbodytemp)) - . = FALSE - - -/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) - var/atom/A = src.loc - if(isturf(A)) - var/areatemp = get_temperature(environment) - if( abs(areatemp - bodytemperature) > 5) - var/diff = areatemp - bodytemperature - diff = diff / 5 - adjust_bodytemperature(diff) - - if(!environment_is_safe(environment)) - adjustHealth(unsuitable_atmos_damage) - - handle_temperature_damage() - -/mob/living/simple_animal/proc/handle_temperature_damage() - if((bodytemperature < minbodytemp) || (bodytemperature > maxbodytemp)) - adjustHealth(unsuitable_atmos_damage) - -/mob/living/simple_animal/gib() - if(butcher_results) - var/atom/Tsec = drop_location() - for(var/path in butcher_results) - for(var/i in 1 to butcher_results[path]) - new path(Tsec) - ..() - -/mob/living/simple_animal/gib_animation() - if(icon_gib) - new /obj/effect/temp_visual/gib_animation/animal(loc, icon_gib) - -/mob/living/simple_animal/say_mod(input, message_mode) - if(speak_emote && speak_emote.len) - verb_say = pick(speak_emote) - . = ..() - -/mob/living/simple_animal/emote(act, m_type=1, message = null, intentional = FALSE) - if(stat) - return - if(act == "scream") - message = "makes a loud and pained whimper." //ugly hack to stop animals screaming when crushed :P - act = "me" - ..(act, m_type, message) - -/mob/living/simple_animal/proc/set_varspeed(var_value) - speed = var_value - update_simplemob_varspeed() - -/mob/living/simple_animal/proc/update_simplemob_varspeed() - if(speed == 0) - remove_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE) - add_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE, 100, multiplicative_slowdown = speed, override = TRUE) - -/mob/living/simple_animal/Stat() - ..() - if(statpanel("Status")) - stat(null, "Health: [round((health / maxHealth) * 100)]%") - return 1 - -/mob/living/simple_animal/proc/drop_loot() - if(loot.len) - for(var/i in loot) - new i(loc) - -/mob/living/simple_animal/death(gibbed) - movement_type &= ~FLYING - if(nest) - nest.spawned_mobs -= src - nest = null - drop_loot() - if(dextrous) - drop_all_held_items() - if(!gibbed) - if(death_sound) - playsound(get_turf(src),death_sound, 200, 1) - if(deathmessage || !del_on_death) - emote("deathgasp") - if(del_on_death) - ..() - //Prevent infinite loops if the mob Destroy() is overridden in such - //a manner as to cause a call to death() again - del_on_death = FALSE - qdel(src) - else - health = 0 - icon_state = icon_dead - density = FALSE - lying = 1 - ..() - -/mob/living/simple_animal/proc/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility) - return FALSE - if(ismob(the_target)) - var/mob/M = the_target - if(M.status_flags & GODMODE) - return FALSE - if (isliving(the_target)) - var/mob/living/L = the_target - if(L.stat != CONSCIOUS) - return FALSE - if (ismecha(the_target)) - var/obj/mecha/M = the_target - if (M.occupant) - return FALSE - return TRUE - -/mob/living/simple_animal/handle_fire() - return - -/mob/living/simple_animal/IgniteMob() - return FALSE - -/mob/living/simple_animal/ExtinguishMob() - return - -/mob/living/simple_animal/revive(full_heal = 0, admin_revive = 0) - if(..()) //successfully ressuscitated from death - icon = initial(icon) - icon_state = icon_living - density = initial(density) - lying = 0 - . = 1 - movement_type = initial(movement_type) - -/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 - if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) - return - next_scan_time = world.time + 400 - var/alone = 1 - var/mob/living/simple_animal/partner - var/children = 0 - for(var/mob/M in view(7, src)) - if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. - continue - else if(istype(M, childtype)) //Check for children SECOND. - children++ - else if(istype(M, animal_species)) - if(M.ckey) - continue - else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; - partner = M - - else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. - return //we never mate when not alone, so just abort early - - if(alone && partner && children < 3) - var/childspawn = pickweight(childtype) - var/turf/target = get_turf(loc) - if(target) - return new childspawn(target) - -/mob/living/simple_animal/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE) - if(incapacitated()) - to_chat(src, "You can't do that right now!") - return FALSE - if(be_close && !in_range(M, src)) - to_chat(src, "You are too far away!") - return FALSE - if(!(no_dextery || dextrous)) - to_chat(src, "You don't have the dexterity to do this!") - return FALSE - return TRUE - -/mob/living/simple_animal/stripPanelUnequip(obj/item/what, mob/who, where) - if(!canUseTopic(who, BE_CLOSE)) - return - else - ..() - -/mob/living/simple_animal/stripPanelEquip(obj/item/what, mob/who, where) - if(!canUseTopic(who, BE_CLOSE)) - return - else - ..() - -/mob/living/simple_animal/update_canmove(value_otherwise = TRUE) - if(IsUnconscious() || IsStun() || IsKnockdown() || stat || resting) - drop_all_held_items() - canmove = FALSE - else if(buckled) - canmove = FALSE - else - canmove = value_otherwise - update_transform() - update_action_buttons_icon() - return canmove - -/mob/living/simple_animal/update_transform() - var/matrix/ntransform = matrix(transform) //aka transform.Copy() - var/changed = 0 - - if(resize != RESIZE_DEFAULT_SIZE) - changed++ - ntransform.Scale(resize) - resize = RESIZE_DEFAULT_SIZE - - if(changed) - animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) - -/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion - toggle_ai(AI_OFF) // To prevent any weirdness. - -/mob/living/simple_animal/update_sight() - if(!client) - return - if(stat == DEAD) - sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_OBSERVER - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - sync_lighting_plane_alpha() - -/mob/living/simple_animal/get_idcard() - return access_card - -/mob/living/simple_animal/OpenCraftingMenu() - if(dextrous) - handcrafting.ui_interact(src) - -/mob/living/simple_animal/can_hold_items() - return dextrous - -/mob/living/simple_animal/IsAdvancedToolUser() - return dextrous - -/mob/living/simple_animal/activate_hand(selhand) - if(!dextrous) - return ..() - if(!selhand) - selhand = (active_hand_index % held_items.len)+1 - if(istext(selhand)) - selhand = lowertext(selhand) - if(selhand == "right" || selhand == "r") - selhand = 2 - if(selhand == "left" || selhand == "l") - selhand = 1 - if(selhand != active_hand_index) - swap_hand(selhand) - else - mode() - -/mob/living/simple_animal/swap_hand(hand_index) - if(!dextrous) - return ..() - if(!hand_index) - hand_index = (active_hand_index % held_items.len)+1 - var/obj/item/held_item = get_active_held_item() - if(held_item) - if(istype(held_item, /obj/item/twohanded)) - var/obj/item/twohanded/T = held_item - if(T.wielded == 1) - to_chat(usr, "Your other hand is too busy holding the [T.name].") - return - var/oindex = active_hand_index - active_hand_index = hand_index - if(hud_used) - var/obj/screen/inventory/hand/H - H = hud_used.hand_slots["[hand_index]"] - if(H) - H.update_icon() - H = hud_used.hand_slots["[oindex]"] - if(H) - H.update_icon() - -/mob/living/simple_animal/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE) - . = ..(I, del_on_fail, merge_stacks) - update_inv_hands() - -/mob/living/simple_animal/update_inv_hands() - if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) - var/obj/item/l_hand = get_item_for_held_index(1) - var/obj/item/r_hand = get_item_for_held_index(2) - if(r_hand) - r_hand.layer = ABOVE_HUD_LAYER - r_hand.plane = ABOVE_HUD_PLANE - r_hand.screen_loc = ui_hand_position(get_held_index_of_item(r_hand)) - client.screen |= r_hand - if(l_hand) - l_hand.layer = ABOVE_HUD_LAYER - l_hand.plane = ABOVE_HUD_PLANE - l_hand.screen_loc = ui_hand_position(get_held_index_of_item(l_hand)) - client.screen |= l_hand - -//ANIMAL RIDING - -/mob/living/simple_animal/user_buckle_mob(mob/living/M, mob/user) - GET_COMPONENT(riding_datum, /datum/component/riding) - if(riding_datum) - if(user.incapacitated()) - return - for(var/atom/movable/A in get_turf(src)) - if(A != src && A != M && A.density) - return - M.forceMove(get_turf(src)) - return ..() - -/mob/living/simple_animal/relaymove(mob/user, direction) - GET_COMPONENT(riding_datum, /datum/component/riding) - if(tame && riding_datum) - riding_datum.handle_ride(user, direction) - -/mob/living/simple_animal/buckle_mob(mob/living/buckled_mob, force = 0, check_loc = 1) - . = ..() - LoadComponent(/datum/component/riding) - -/mob/living/simple_animal/proc/toggle_ai(togglestatus) - if (AIStatus != togglestatus) - if (togglestatus > 0 && togglestatus < 5) - if (togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) - var/turf/T = get_turf(src) - if (AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - else - SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src - GLOB.simple_animals[AIStatus] -= src - GLOB.simple_animals[togglestatus] += src - AIStatus = togglestatus - else - stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") - -/mob/living/simple_animal/proc/consider_wakeup() - if (pulledby || shouldwakeup) - toggle_ai(AI_ON) - -/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(!ckey && !stat)//Not unconscious - if(AIStatus == AI_IDLE) - toggle_ai(AI_ON) - - -/mob/living/simple_animal/onTransitZ(old_z, new_z) - ..() - if (AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src - toggle_ai(initial(AIStatus)) +/mob/living/simple_animal + name = "animal" + icon = 'icons/mob/animal.dmi' + health = 20 + maxHealth = 20 + gender = PLURAL //placeholder + + status_flags = CANPUSH + + var/icon_living = "" + var/icon_dead = "" //icon when the animal is dead. Don't use animated icons for this. + var/icon_gib = null //We only try to show a gibbing animation if this exists. + + var/list/speak = list() + var/list/speak_emote = list()// Emotes while speaking IE: Ian [emote], [text] -- Ian barks, "WOOF!". Spoken text is generated from the speak variable. + var/speak_chance = 0 + var/list/emote_hear = list() //Hearable emotes + var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + + var/turns_per_move = 1 + var/turns_since_move = 0 + var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. + var/wander = 1 // Does the mob wander around when idle? + var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. + + //Interaction + var/response_help = "pokes" + var/response_disarm = "shoves" + var/response_harm = "hits" + var/harm_intent_damage = 3 + var/force_threshold = 0 //Minimum force required to deal any damage + + //Temperature effect + var/minbodytemp = 250 + var/maxbodytemp = 350 + + //Healable by medical stacks? Defaults to yes. + var/healable = 1 + + //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage + var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum + var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above + + //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly + var/melee_damage_lower = 0 + var/melee_damage_upper = 0 + var/obj_damage = 0 //how much damage this simple animal does to objects, if any + var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value + var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. + var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source + var/attacktext = "attacks" + var/attack_sound = null + var/friendly = "nuzzles" //If the mob does no damage with it's attack + var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls + + var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster + + //Hot simple_animal baby making vars + var/list/childtype = null + var/next_scan_time = 0 + var/animal_species //Sorry, no spider+corgi buttbabies. + + //simple_animal access + var/obj/item/card/id/access_card = null //innate access uses an internal ID card + var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against + var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood + + var/mob/living/simple_animal/hostile/spawner/nest + + var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions + + var/list/loot = list() //list of things spawned at mob's loc when it dies + var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses + var/deathmessage = "" + var/death_sound = null //The sound played on death + + var/allow_movement_on_non_turfs = FALSE + + var/attacked_sound = "punch" //Played when someone punches the creature + + var/dextrous = FALSE //If the creature has, and can use, hands + var/dextrous_hud_type = /datum/hud/dextrous + var/datum/personal_crafting/handcrafting + + var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever), AI_Z_OFF (Temporarily off due to nonpresence of players) + + var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. + + //domestication + var/tame = 0 + + var/my_z // I don't want to confuse this with client registered_z + + var/do_footstep = FALSE + +/mob/living/simple_animal/Initialize() + . = ..() + GLOB.simple_animals[AIStatus] += src + handcrafting = new() + if(gender == PLURAL) + gender = pick(MALE,FEMALE) + if(!real_name) + real_name = name + if(!loc) + stack_trace("Simple animal being instantiated in nullspace") + update_simplemob_varspeed() + +/mob/living/simple_animal/Destroy() + GLOB.simple_animals[AIStatus] -= src + if (SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) + SSnpcpool.currentrun -= src + + if(nest) + nest.spawned_mobs -= src + nest = null + + var/turf/T = get_turf(src) + if (T && AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + + return ..() + +/mob/living/simple_animal/initialize_footstep() + if(do_footstep) + ..() + +/mob/living/simple_animal/updatehealth() + ..() + health = CLAMP(health, 0, maxHealth) + +/mob/living/simple_animal/update_stat() + if(status_flags & GODMODE) + return + if(stat != DEAD) + if(health <= 0) + death() + else + stat = CONSCIOUS + med_hud_set_status() + + +/mob/living/simple_animal/handle_status_effects() + ..() + if(stuttering) + stuttering = 0 + +/mob/living/simple_animal/proc/handle_automated_action() + set waitfor = FALSE + return + +/mob/living/simple_animal/proc/handle_automated_movement() + set waitfor = FALSE + if(!stop_automated_movement && wander) + if((isturf(src.loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + if(!(stop_automated_movement_when_pulled && pulledby)) //Some animals don't move when pulled + var/anydir = pick(GLOB.cardinals) + if(Process_Spacemove(anydir)) + Move(get_step(src, anydir), anydir) + turns_since_move = 0 + return 1 + +/mob/living/simple_animal/proc/handle_automated_speech(var/override) + set waitfor = FALSE + if(speak_chance) + if(prob(speak_chance) || override) + if(speak && speak.len) + if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) + var/length = speak.len + if(emote_hear && emote_hear.len) + length += emote_hear.len + if(emote_see && emote_see.len) + length += emote_see.len + var/randomValue = rand(1,length) + if(randomValue <= speak.len) + say(pick(speak), forced = "poly") + else + randomValue -= speak.len + if(emote_see && randomValue <= emote_see.len) + emote("me [pick(emote_see)]", 1) + else + emote("me [pick(emote_hear)]", 2) + else + say(pick(speak), forced = "poly") + else + if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + emote("me", 1, pick(emote_see)) + if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) + emote("me", 2, pick(emote_hear)) + if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + var/length = emote_hear.len + emote_see.len + var/pick = rand(1,length) + if(pick <= emote_see.len) + emote("me", 1, pick(emote_see)) + else + emote("me", 2, pick(emote_hear)) + + +/mob/living/simple_animal/proc/environment_is_safe(datum/gas_mixture/environment, check_temp = FALSE) + . = TRUE + + if(pulledby && pulledby.grab_state >= GRAB_KILL && atmos_requirements["min_oxy"]) + . = FALSE //getting choked + + if(isturf(src.loc) && isopenturf(src.loc)) + var/turf/open/ST = src.loc + if(ST.air) + var/ST_gases = ST.air.gases + + var/tox = ST_gases[/datum/gas/plasma] + var/oxy = ST_gases[/datum/gas/oxygen] + var/n2 = ST_gases[/datum/gas/nitrogen] + var/co2 = ST_gases[/datum/gas/carbon_dioxide] + + GAS_GARBAGE_COLLECT(ST.air.gases) + + if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) + . = FALSE + else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) + . = FALSE + else if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) + . = FALSE + else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) + . = FALSE + else if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) + . = FALSE + else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) + . = FALSE + else if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) + . = FALSE + else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) + . = FALSE + else + if(atmos_requirements["min_oxy"] || atmos_requirements["min_tox"] || atmos_requirements["min_n2"] || atmos_requirements["min_co2"]) + . = FALSE + + if(check_temp) + var/areatemp = get_temperature(environment) + if((areatemp < minbodytemp) || (areatemp > maxbodytemp)) + . = FALSE + + +/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) + var/atom/A = src.loc + if(isturf(A)) + var/areatemp = get_temperature(environment) + if( abs(areatemp - bodytemperature) > 5) + var/diff = areatemp - bodytemperature + diff = diff / 5 + adjust_bodytemperature(diff) + + if(!environment_is_safe(environment)) + adjustHealth(unsuitable_atmos_damage) + + handle_temperature_damage() + +/mob/living/simple_animal/proc/handle_temperature_damage() + if((bodytemperature < minbodytemp) || (bodytemperature > maxbodytemp)) + adjustHealth(unsuitable_atmos_damage) + +/mob/living/simple_animal/gib() + if(butcher_results) + var/atom/Tsec = drop_location() + for(var/path in butcher_results) + for(var/i in 1 to butcher_results[path]) + new path(Tsec) + ..() + +/mob/living/simple_animal/gib_animation() + if(icon_gib) + new /obj/effect/temp_visual/gib_animation/animal(loc, icon_gib) + +/mob/living/simple_animal/say_mod(input, message_mode) + if(speak_emote && speak_emote.len) + verb_say = pick(speak_emote) + . = ..() + +/mob/living/simple_animal/emote(act, m_type=1, message = null, intentional = FALSE) + if(stat) + return + if(act == "scream") + message = "makes a loud and pained whimper." //ugly hack to stop animals screaming when crushed :P + act = "me" + ..(act, m_type, message) + +/mob/living/simple_animal/proc/set_varspeed(var_value) + speed = var_value + update_simplemob_varspeed() + +/mob/living/simple_animal/proc/update_simplemob_varspeed() + if(speed == 0) + remove_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE) + add_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE, 100, multiplicative_slowdown = speed, override = TRUE) + +/mob/living/simple_animal/Stat() + ..() + if(statpanel("Status")) + stat(null, "Health: [round((health / maxHealth) * 100)]%") + return 1 + +/mob/living/simple_animal/proc/drop_loot() + if(loot.len) + for(var/i in loot) + new i(loc) + +/mob/living/simple_animal/death(gibbed) + movement_type &= ~FLYING + if(nest) + nest.spawned_mobs -= src + nest = null + drop_loot() + if(dextrous) + drop_all_held_items() + if(!gibbed) + if(death_sound) + playsound(get_turf(src),death_sound, 200, 1) + if(deathmessage || !del_on_death) + emote("deathgasp") + if(del_on_death) + ..() + //Prevent infinite loops if the mob Destroy() is overridden in such + //a manner as to cause a call to death() again + del_on_death = FALSE + qdel(src) + else + health = 0 + icon_state = icon_dead + density = FALSE + lying = 1 + ..() + +/mob/living/simple_animal/proc/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility) + return FALSE + if(ismob(the_target)) + var/mob/M = the_target + if(M.status_flags & GODMODE) + return FALSE + if (isliving(the_target)) + var/mob/living/L = the_target + if(L.stat != CONSCIOUS) + return FALSE + if (ismecha(the_target)) + var/obj/mecha/M = the_target + if (M.occupant) + return FALSE + return TRUE + +/mob/living/simple_animal/handle_fire() + return + +/mob/living/simple_animal/IgniteMob() + return FALSE + +/mob/living/simple_animal/ExtinguishMob() + return + +/mob/living/simple_animal/revive(full_heal = 0, admin_revive = 0) + if(..()) //successfully ressuscitated from death + icon = initial(icon) + icon_state = icon_living + density = initial(density) + lying = 0 + . = 1 + movement_type = initial(movement_type) + +/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 + if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) + return + next_scan_time = world.time + 400 + var/alone = 1 + var/mob/living/simple_animal/partner + var/children = 0 + for(var/mob/M in view(7, src)) + if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. + continue + else if(istype(M, childtype)) //Check for children SECOND. + children++ + else if(istype(M, animal_species)) + if(M.ckey) + continue + else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; + partner = M + + else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. + return //we never mate when not alone, so just abort early + + if(alone && partner && children < 3) + var/childspawn = pickweight(childtype) + var/turf/target = get_turf(loc) + if(target) + return new childspawn(target) + +/mob/living/simple_animal/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE) + if(incapacitated()) + to_chat(src, "You can't do that right now!") + return FALSE + if(be_close && !in_range(M, src)) + to_chat(src, "You are too far away!") + return FALSE + if(!(no_dextery || dextrous)) + to_chat(src, "You don't have the dexterity to do this!") + return FALSE + return TRUE + +/mob/living/simple_animal/stripPanelUnequip(obj/item/what, mob/who, where) + if(!canUseTopic(who, BE_CLOSE)) + return + else + ..() + +/mob/living/simple_animal/stripPanelEquip(obj/item/what, mob/who, where) + if(!canUseTopic(who, BE_CLOSE)) + return + else + ..() + +/mob/living/simple_animal/update_canmove(value_otherwise = TRUE) + if(IsUnconscious() || IsStun() || IsKnockdown() || stat || resting) + drop_all_held_items() + canmove = FALSE + else if(buckled) + canmove = FALSE + else + canmove = value_otherwise + update_transform() + update_action_buttons_icon() + return canmove + +/mob/living/simple_animal/update_transform() + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + var/changed = 0 + + if(resize != RESIZE_DEFAULT_SIZE) + changed++ + ntransform.Scale(resize) + resize = RESIZE_DEFAULT_SIZE + + if(changed) + animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) + +/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion + toggle_ai(AI_OFF) // To prevent any weirdness. + +/mob/living/simple_animal/update_sight() + if(!client) + return + if(stat == DEAD) + sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_OBSERVER + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + sync_lighting_plane_alpha() + +/mob/living/simple_animal/get_idcard() + return access_card + +/mob/living/simple_animal/OpenCraftingMenu() + if(dextrous) + handcrafting.ui_interact(src) + +/mob/living/simple_animal/can_hold_items() + return dextrous + +/mob/living/simple_animal/IsAdvancedToolUser() + return dextrous + +/mob/living/simple_animal/activate_hand(selhand) + if(!dextrous) + return ..() + if(!selhand) + selhand = (active_hand_index % held_items.len)+1 + if(istext(selhand)) + selhand = lowertext(selhand) + if(selhand == "right" || selhand == "r") + selhand = 2 + if(selhand == "left" || selhand == "l") + selhand = 1 + if(selhand != active_hand_index) + swap_hand(selhand) + else + mode() + +/mob/living/simple_animal/swap_hand(hand_index) + if(!dextrous) + return ..() + if(!hand_index) + hand_index = (active_hand_index % held_items.len)+1 + var/obj/item/held_item = get_active_held_item() + if(held_item) + if(istype(held_item, /obj/item/twohanded)) + var/obj/item/twohanded/T = held_item + if(T.wielded == 1) + to_chat(usr, "Your other hand is too busy holding the [T.name].") + return + var/oindex = active_hand_index + active_hand_index = hand_index + if(hud_used) + var/obj/screen/inventory/hand/H + H = hud_used.hand_slots["[hand_index]"] + if(H) + H.update_icon() + H = hud_used.hand_slots["[oindex]"] + if(H) + H.update_icon() + +/mob/living/simple_animal/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE) + . = ..(I, del_on_fail, merge_stacks) + update_inv_hands() + +/mob/living/simple_animal/update_inv_hands() + if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) + var/obj/item/l_hand = get_item_for_held_index(1) + var/obj/item/r_hand = get_item_for_held_index(2) + if(r_hand) + r_hand.layer = ABOVE_HUD_LAYER + r_hand.plane = ABOVE_HUD_PLANE + r_hand.screen_loc = ui_hand_position(get_held_index_of_item(r_hand)) + client.screen |= r_hand + if(l_hand) + l_hand.layer = ABOVE_HUD_LAYER + l_hand.plane = ABOVE_HUD_PLANE + l_hand.screen_loc = ui_hand_position(get_held_index_of_item(l_hand)) + client.screen |= l_hand + +//ANIMAL RIDING + +/mob/living/simple_animal/user_buckle_mob(mob/living/M, mob/user) + GET_COMPONENT(riding_datum, /datum/component/riding) + if(riding_datum) + if(user.incapacitated()) + return + for(var/atom/movable/A in get_turf(src)) + if(A != src && A != M && A.density) + return + M.forceMove(get_turf(src)) + return ..() + +/mob/living/simple_animal/relaymove(mob/user, direction) + GET_COMPONENT(riding_datum, /datum/component/riding) + if(tame && riding_datum) + riding_datum.handle_ride(user, direction) + +/mob/living/simple_animal/buckle_mob(mob/living/buckled_mob, force = 0, check_loc = 1) + . = ..() + LoadComponent(/datum/component/riding) + +/mob/living/simple_animal/proc/toggle_ai(togglestatus) + if (AIStatus != togglestatus) + if (togglestatus > 0 && togglestatus < 5) + if (togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) + var/turf/T = get_turf(src) + if (AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + else + SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src + GLOB.simple_animals[AIStatus] -= src + GLOB.simple_animals[togglestatus] += src + AIStatus = togglestatus + else + stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") + +/mob/living/simple_animal/proc/consider_wakeup() + if (pulledby || shouldwakeup) + toggle_ai(AI_ON) + +/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE, forced = FALSE) + . = ..() + if(!ckey && !stat)//Not unconscious + if(AIStatus == AI_IDLE) + toggle_ai(AI_ON) + + +/mob/living/simple_animal/onTransitZ(old_z, new_z) + ..() + if (AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src + toggle_ai(initial(AIStatus)) diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index 161362c187..ebb34fe77a 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -132,7 +132,7 @@ if(stat != DEAD) var/bz_percentage =0 if(environment.gases[/datum/gas/bz]) - bz_percentage = environment.gases[/datum/gas/bz][MOLES] / environment.total_moles() + bz_percentage = environment.gases[/datum/gas/bz] / environment.total_moles() var/stasis = (bz_percentage >= 0.05 && bodytemperature < (T0C + 100)) || force_stasis if(stat == CONSCIOUS && stasis) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9d662b1673..7ff8fbc0b1 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -67,8 +67,8 @@ t += "Temperature: [environment.temperature] \n" for(var/id in environment.gases) var/gas = environment.gases[id] - if(gas[MOLES]) - t+="[gas[GAS_META][META_GAS_NAME]]: [gas[MOLES]] \n" + if(gas) + t+="[GLOB.meta_gas_names[id]]: [gas] \n" to_chat(usr, t) diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm index 39a0bba701..850566f625 100644 --- a/code/modules/mob/say_vr.dm +++ b/code/modules/mob/say_vr.dm @@ -8,9 +8,9 @@ set src in usr if(usr != src) usr << "No." - var/msg = input(usr,"Set the flavor text in your 'examine' verb. Can also be used for OOC notes about your character.","Flavor Text",html_decode(flavor_text)) as message|null - - if(msg != null) + var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", html_decode(flavor_text), MAX_MESSAGE_LEN*2, TRUE) + + if(!isnull(msg)) msg = copytext(msg, 1, MAX_MESSAGE_LEN) msg = html_encode(msg) @@ -23,11 +23,12 @@ /mob/proc/print_flavor_text() if(flavor_text && flavor_text != "") - var/msg = replacetext(flavor_text, "\n", " ") + // We are decoding and then encoding to not only get correct amount of characters, but also to prevent partial escaping characters being shown. + var/msg = html_decode(replacetext(flavor_text, "\n", " ")) if(lentext(msg) <= 40) - return "[msg]" + return "[html_encode(msg)]" else - return "[copytext(msg, 1, 37)]... More..." + return "[html_encode(copytext(msg, 1, 37))]... More..." /mob/proc/get_top_level_mob() if(istype(src.loc,/mob)&&src.loc!=src) @@ -116,6 +117,73 @@ proc/get_top_level_mob(var/mob/S) message = null emote_type = EMOTE_VISIBLE +///////////////// SUBTLE 2: NO GHOST BOOGALOO + +/datum/emote/living/subtler + key = "subtler" + key_third_person = "subtler" + message = null + mob_type_blacklist_typecache = list(/mob/living/brain) + + +/datum/emote/living/subtler/proc/check_invalid(mob/user, input) + . = TRUE + if(copytext(input,1,5) == "says") + to_chat(user, "Invalid emote.") + else if(copytext(input,1,9) == "exclaims") + to_chat(user, "Invalid emote.") + else if(copytext(input,1,6) == "yells") + to_chat(user, "Invalid emote.") + else if(copytext(input,1,5) == "asks") + to_chat(user, "Invalid emote.") + else + . = FALSE + +/datum/emote/living/subtler/run_emote(mob/user, params, type_override = null) + if(jobban_isbanned(user, "emote")) + to_chat(user, "You cannot send subtle emotes (banned).") + return FALSE + else if(user.client && user.client.prefs.muted & MUTE_IC) + to_chat(user, "You cannot send IC messages (muted).") + return FALSE + else if(!params) + var/subtle_emote = copytext(sanitize(input("Choose an emote to display.") as message|null), 1, MAX_MESSAGE_LEN) + if(subtle_emote && !check_invalid(user, subtle_emote)) + var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") + switch(type) + if("Visible") + emote_type = EMOTE_VISIBLE + if("Hearable") + emote_type = EMOTE_AUDIBLE + else + alert("Unable to use this emote, must be either hearable or visible.") + return + message = subtle_emote + else + return FALSE + else + message = params + if(type_override) + emote_type = type_override + . = TRUE + if(!can_run_emote(user)) + return FALSE + + user.log_message(message, INDIVIDUAL_EMOTE_LOG) + message = "[user] " + "[message]" + + for(var/mob/M) + if(M in list(/mob/living)) + M.show_message(message) + + if(emote_type == EMOTE_AUDIBLE) + user.audible_message(message=message,hearing_distance=1) + else + user.visible_message(message=message,self_message=message,vision_distance=1) + log_emote("[key_name(user)] : [message]") + + message = null + ///////////////// VERB CODE /mob/living/verb/subtle() set name = "Subtle" @@ -124,3 +192,12 @@ proc/get_top_level_mob(var/mob/S) to_chat(usr, "Speech is currently admin-disabled.") return usr.emote("subtle") + +///////////////// VERB CODE 2 +/mob/living/verb/subtler() + set name = "Subtler" + set category = "IC" + if(GLOB.say_disabled) //This is here to try to identify lag problems + to_chat(usr, "Speech is currently admin-disabled.") + return + usr.emote("subtler") diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm index 138e8bb9dd..e7dd42a797 100644 --- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm @@ -81,13 +81,13 @@ if(air.total_moles()) for(var/gasid in air.gases) gasdata.Add(list(list( - "name"= air.gases[gasid][GAS_META][META_GAS_NAME], - "amount" = round(100*air.gases[gasid][MOLES]/air.total_moles(),0.01)))) + "name"= GLOB.meta_gas_names[gasid], + "amount" = round(100*air.gases[gasid]/air.total_moles(),0.01)))) else for(var/gasid in air.gases) gasdata.Add(list(list( - "name"= air.gases[gasid][GAS_META][META_GAS_NAME], + "name"= GLOB.meta_gas_names[gasid], "amount" = 0))) data["gases"] = gasdata diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm index 5c19a67284..816ae58749 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm @@ -4,22 +4,7 @@ if(!ninjacost(0,N_ADRENALINE)) var/mob/living/carbon/human/H = affecting - H.SetSleeping(0) - H.SetStun(0) - H.SetKnockdown(0) - H.SetUnconscious(0) - H.adjustStaminaLoss(-150) - H.stuttering = 0 - H.updatehealth() - H.update_stamina() - H.resting = 0 - H.lying = 0 - H.update_canmove() - - H.reagents.add_reagent("inaprovaline", 3) //let's give another chance to dumb fucks who forget to breathe - H.reagents.add_reagent("synaptizine", 10) - H.reagents.add_reagent("omnizine", 10) - H.reagents.add_reagent("stimulants", 10) + H.do_adrenaline(150, TRUE, 0, 0, TRUE, list("inaprovaline" = 3, "synaptizine" = 10, "omnizine" = 10), "You feel a sudden surge of energy!") H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!"), forced = "ninjaboost") diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 3b9c3a549b..4159d9898a 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -43,11 +43,10 @@ playsound(src, 'sound/machines/ding.ogg', 50, 1) eject() else - var/gasdrained = min(powerproduction_drain*drainratio,loaded_tank.air_contents.gases[/datum/gas/plasma][MOLES]) - loaded_tank.air_contents.gases[/datum/gas/plasma][MOLES] -= gasdrained - loaded_tank.air_contents.assert_gas(/datum/gas/tritium) - loaded_tank.air_contents.gases[/datum/gas/tritium][MOLES] += gasdrained - loaded_tank.air_contents.garbage_collect() + var/gasdrained = min(powerproduction_drain*drainratio,loaded_tank.air_contents.gases[/datum/gas/plasma]) + loaded_tank.air_contents.gases[/datum/gas/plasma] -= gasdrained + loaded_tank.air_contents.gases[/datum/gas/tritium] += gasdrained + GAS_GARBAGE_COLLECT(loaded_tank.air_contents.gases) var/power_produced = RAD_COLLECTOR_OUTPUT add_avail(power_produced) @@ -58,11 +57,10 @@ eject() else var/gasdrained = bitcoinproduction_drain*drainratio - loaded_tank.air_contents.gases[/datum/gas/tritium][MOLES] -= gasdrained - loaded_tank.air_contents.gases[/datum/gas/oxygen][MOLES] -= gasdrained - loaded_tank.air_contents.assert_gas(/datum/gas/carbon_dioxide) - loaded_tank.air_contents.gases[/datum/gas/carbon_dioxide][MOLES] += gasdrained*2 - loaded_tank.air_contents.garbage_collect() + loaded_tank.air_contents.gases[/datum/gas/tritium] -= gasdrained + loaded_tank.air_contents.gases[/datum/gas/oxygen] -= gasdrained + loaded_tank.air_contents.gases[/datum/gas/carbon_dioxide] += gasdrained*2 + GAS_GARBAGE_COLLECT(loaded_tank.air_contents.gases) var/bitcoins_mined = RAD_COLLECTOR_OUTPUT SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, bitcoins_mined*RAD_COLLECTOR_MINING_CONVERSION_RATE) stored_power-=bitcoins_mined @@ -76,7 +74,6 @@ var/fuel if(loaded_tank) fuel = loaded_tank.air_contents.gases[/datum/gas/plasma] - fuel = fuel ? fuel[MOLES] : 0 investigate_log("turned [active?"on":"off"] by [key_name(user)]. [loaded_tank?"Fuel: [round(fuel/0.29)]%":"It is empty"].", INVESTIGATE_SINGULO) return else diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index ad94d577ea..d87f9821ef 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -295,6 +295,16 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) E.energy = power qdel(src) +/obj/machinery/power/supermatter_crystal/proc/consume_turf(turf/T) + var/oldtype = T.type + var/turf/newT = T.ScrapeAway() + if(newT.type == oldtype) + return + playsound(T, 'sound/effects/supermatter.ogg', 50, 1) + T.visible_message("[T] smacks into [src] and rapidly flashes to ash.",\ + "You hear a loud crack as you are washed with a wave of heat.") + T.CalculateAdjacentTurfs() + /obj/machinery/power/supermatter_crystal/process_atmos() var/turf/T = loc @@ -303,6 +313,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) if(!istype(T)) //We are in a crate or somewhere that isn't turf, if we return to turf resume processing but for now. return //Yeah just stop. + if(istype(T, /turf/closed)) + consume_turf(T) if(power) soundloop.volume = min(40, (round(power/100)/50)+1) // 5 +1 volume per 20 power. 2500 power is max @@ -339,16 +351,15 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) if(damage > damage_archived && prob(10)) playsound(get_turf(src), 'sound/effects/empulse.ogg', 50, 1) - removed.assert_gases(/datum/gas/oxygen, /datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/nitrogen) //calculating gas related values combined_gas = max(removed.total_moles(), 0) - plasmacomp = max(removed.gases[/datum/gas/plasma][MOLES]/combined_gas, 0) - o2comp = max(removed.gases[/datum/gas/oxygen][MOLES]/combined_gas, 0) - co2comp = max(removed.gases[/datum/gas/carbon_dioxide][MOLES]/combined_gas, 0) + plasmacomp = max(removed.gases[/datum/gas/plasma]/combined_gas, 0) + o2comp = max(removed.gases[/datum/gas/oxygen]/combined_gas, 0) + co2comp = max(removed.gases[/datum/gas/carbon_dioxide]/combined_gas, 0) - n2ocomp = max(removed.gases[/datum/gas/nitrous_oxide][MOLES]/combined_gas, 0) - n2comp = max(removed.gases[/datum/gas/nitrogen][MOLES]/combined_gas, 0) + n2ocomp = max(removed.gases[/datum/gas/nitrous_oxide]/combined_gas, 0) + n2comp = max(removed.gases[/datum/gas/nitrogen]/combined_gas, 0) gasmix_power_ratio = min(max(plasmacomp + o2comp + co2comp - n2comp, 0), 1) @@ -400,9 +411,9 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) removed.temperature = max(0, min(removed.temperature, 2500 * dynamic_heat_modifier)) //Calculate how much gas to release - removed.gases[/datum/gas/plasma][MOLES] += max((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER, 0) + removed.gases[/datum/gas/plasma] += max((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER, 0) - removed.gases[/datum/gas/oxygen][MOLES] += max(((device_energy + removed.temperature * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0) + removed.gases[/datum/gas/oxygen] += max(((device_energy + removed.temperature * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0) if(produces_gas) env.merge(removed) diff --git a/code/modules/projectiles/ammunition/energy/stun.dm b/code/modules/projectiles/ammunition/energy/stun.dm index 9b24571d12..3f033ac904 100644 --- a/code/modules/projectiles/ammunition/energy/stun.dm +++ b/code/modules/projectiles/ammunition/energy/stun.dm @@ -21,6 +21,7 @@ /obj/item/ammo_casing/energy/disabler projectile_type = /obj/item/projectile/beam/disabler select_name = "disable" - e_cost = 50 + e_cost = 40 fire_sound = 'sound/weapons/taser2.ogg' harmful = FALSE + click_cooldown_override = 3 diff --git a/code/modules/projectiles/boxes_magazines/internal/rifle.dm b/code/modules/projectiles/boxes_magazines/internal/rifle.dm index ef83e96b1c..ae49a8cadd 100644 --- a/code/modules/projectiles/boxes_magazines/internal/rifle.dm +++ b/code/modules/projectiles/boxes_magazines/internal/rifle.dm @@ -6,10 +6,12 @@ max_ammo = 5 multiload = 1 +/obj/item/ammo_box/magazine/internal/boltaction/improvised + max_ammo = 1 + /obj/item/ammo_box/magazine/internal/boltaction/enchanted max_ammo = 1 ammo_type = /obj/item/ammo_casing/a762/enchanted /obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage ammo_type = /obj/item/ammo_casing/magic/arcane_barrage - diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 8aa8d53726..4bd65a7b20 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -268,8 +268,6 @@ empty_alarm() return - - // L6 SAW // /obj/item/gun/ballistic/automatic/l6_saw @@ -292,13 +290,11 @@ /obj/item/gun/ballistic/automatic/l6_saw/unrestricted pin = /obj/item/firing_pin - /obj/item/gun/ballistic/automatic/l6_saw/examine(mob/user) ..() if(cover_open && magazine) to_chat(user, "It seems like you could use an empty hand to remove the magazine.") - /obj/item/gun/ballistic/automatic/l6_saw/attack_self(mob/user) cover_open = !cover_open to_chat(user, "You [cover_open ? "open" : "close"] [src]'s cover.") @@ -308,12 +304,10 @@ playsound(user, 'sound/weapons/sawclose.ogg', 60, 1) update_icon() - /obj/item/gun/ballistic/automatic/l6_saw/update_icon() icon_state = "l6[cover_open ? "open" : "closed"][magazine ? CEILING(get_ammo(0)/12.5, 1)*25 : "-empty"][suppressed ? "-suppressed" : ""]" item_state = "l6[cover_open ? "openmag" : "closedmag"]" - /obj/item/gun/ballistic/automatic/l6_saw/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) //what I tried to do here is just add a check to see if the cover is open or not and add an icon_state change because I can't figure out how c-20rs do it with overlays if(cover_open) to_chat(user, "[src]'s cover is open! Close it before firing!") @@ -344,8 +338,6 @@ return ..() - - // SNIPER // /obj/item/gun/ballistic/automatic/sniper_rifle @@ -367,14 +359,12 @@ slot_flags = ITEM_SLOT_BACK actions_types = list() - /obj/item/gun/ballistic/automatic/sniper_rifle/update_icon() if(magazine) icon_state = "sniper-mag" else icon_state = "sniper" - /obj/item/gun/ballistic/automatic/sniper_rifle/syndicate name = "syndicate sniper rifle" desc = "An illegally modified .50 cal sniper rifle with suppression compatibility. Quickscoping still doesn't work." @@ -403,7 +393,6 @@ else icon_state = "surplus-e" - // Laser rifle (rechargeable magazine) // /obj/item/gun/ballistic/automatic/laser diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 914f9bc016..bb6a144c93 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -71,7 +71,6 @@ var/obj/item/ammo_casing/AC = magazine.get_round() //load next casing. chambered = AC - /obj/item/gun/ballistic/shotgun/examine(mob/user) ..() if (chambered) @@ -117,6 +116,14 @@ knife_x_offset = 27 knife_y_offset = 13 +/obj/item/gun/ballistic/shotgun/boltaction/improvised + name = "Makeshift 7.62mm Rifle" + icon_state = "ishotgun" + item_state = "shotgun" + desc = "A large zip gun more or less that takes a single 7.62mm bullet" + mag_type = /obj/item/ammo_box/magazine/internal/boltaction/improvised + can_bayonet = FALSE + /obj/item/gun/ballistic/shotgun/boltaction/pump(mob/M) playsound(M, 'sound/weapons/shotgunpump.ogg', 60, 1) if(bolt_open) @@ -137,7 +144,6 @@ ..() to_chat(user, "The bolt is [bolt_open ? "open" : "closed"].") - /obj/item/gun/ballistic/shotgun/boltaction/enchanted name = "enchanted bolt action rifle" desc = "Careful not to lose your head." @@ -153,10 +159,8 @@ icon_state = "arcane_barrage" item_state = "arcane_barrage" can_bayonet = FALSE - item_flags = NEEDS_PERMIT | DROPDEL flags_1 = NONE - mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage /obj/item/gun/ballistic/shotgun/boltaction/enchanted/Initialize() @@ -207,7 +211,6 @@ "Slick" = "cshotgun_slick" ) - /obj/item/gun/ballistic/shotgun/automatic/combat/compact name = "compact combat shotgun" desc = "A compact version of the semi automatic combat shotgun. For close encounters." @@ -218,7 +221,6 @@ "Slick" = "cshotgunc_slick" ) - //Dual Feed Shotgun /obj/item/gun/ballistic/shotgun/automatic/dual_tube @@ -261,5 +263,4 @@ return pump() - // DOUBLE BARRELED SHOTGUN and IMPROVISED SHOTGUN are in revolver.dm diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index fba355d738..a4ec979a06 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -424,7 +424,7 @@ M.gets_drilled(K.firer) if(modifier) for(var/mob/living/L in range(1, target_turf) - K.firer - target) - var/armor = L.run_armor_check(K.def_zone, K.flag, "", "", K.armour_penetration) + var/armor = L.run_armor_check(K.def_zone, K.flag, null, null, K.armour_penetration) L.apply_damage(K.damage*modifier, K.damage_type, K.def_zone, armor) to_chat(L, "You're struck by a [K.name]!") @@ -530,7 +530,7 @@ var/kill_modifier = 1 if(K.pressure_decrease_active) kill_modifier *= K.pressure_decrease - var/armor = L.run_armor_check(K.def_zone, K.flag, "", "", K.armour_penetration) + var/armor = L.run_armor_check(K.def_zone, K.flag, null, null, K.armour_penetration) L.apply_damage(bounties_reaped[L.type]*kill_modifier, K.damage_type, K.def_zone, armor) /obj/item/borg/upgrade/modkit/bounty/proc/get_kill(mob/living/L) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index f4ca82b4e6..cbd0348743 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -74,6 +74,7 @@ flag = "energy" hitsound = 'sound/weapons/tap.ogg' eyeblur = 0 + speed = 0.7 impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser light_color = LIGHT_COLOR_BLUE tracer_type = /obj/effect/projectile/tracer/disabler diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm index c8c4a73b3b..f9aa47c6a3 100644 --- a/code/modules/projectiles/projectile/bullets/shotgun.dm +++ b/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -18,6 +18,7 @@ /obj/item/projectile/bullet/shotgun_stunslug name = "stunslug" damage = 5 + stamina = 20 knockdown = 100 stutter = 5 jitter = 20 diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 442ef191a9..7c24a79359 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -555,7 +555,7 @@ if(!D) WARNING("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])") return FALSE - + update_total() var/cached_total = total_volume if(cached_total + amount > maximum_volume) @@ -599,9 +599,9 @@ if(data) R.data = data R.on_new(data) - + if(isliving(my_atom)) - R.on_mob_add(my_atom) //Must occur befor it could posibly run on_mob_delete + R.on_mob_add(my_atom) //Must occur befor it could posibly run on_mob_delete update_total() if(my_atom) my_atom.on_reagent_change(ADD_REAGENT) diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index d77756a649..88f3e92613 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -164,6 +164,9 @@ overdose_threshold = 20 addiction_threshold = 10 metabolization_rate = 0.75 * REAGENTS_METABOLISM + var/brain_damage = TRUE + var/jitter = TRUE + var/confusion = TRUE /datum/reagent/drug/methamphetamine/on_mob_add(mob/living/L) ..() @@ -181,10 +184,10 @@ M.AdjustKnockdown(-40, 0) M.AdjustUnconscious(-40, 0) M.adjustStaminaLoss(-7.5 * REM, 0) - M.Jitter(2) - M.adjustBrainLoss(rand(1,4)) - if(prob(30)) - M.confused = max(1, M.confused) + if(jitter) + M.Jitter(2) + if(brain_damage) + M.adjustBrainLoss(rand(1,4)) M.heal_overall_damage(2, 2) if(prob(5)) M.emote(pick("twitch", "shiver")) @@ -240,6 +243,14 @@ ..() . = 1 +/datum/reagent/drug/methamphetamine/changeling + id = "changelingmeth" + name = "Changeling Adrenaline" + addiction_threshold = 35 + overdose_threshold = 35 + jitter = FALSE + brain_damage = FALSE + /datum/reagent/drug/bath_salts name = "Bath Salts" id = "bath_salts" diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 5071150e24..a65a1f4adb 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -319,7 +319,7 @@ victim.blind_eyes(2) victim.confused = max(M.confused, 3) victim.damageoverlaytemp = 60 - victim.Knockdown(60) + victim.Knockdown(60, override_stamdmg = min(reac_volume * 3, 15)) return else if ( eyes_covered ) // Eye cover is better than mouth cover victim.blur_eyes(3) @@ -332,7 +332,7 @@ victim.blind_eyes(3) victim.confused = max(M.confused, 6) victim.damageoverlaytemp = 75 - victim.Knockdown(100) + victim.Knockdown(100, override_stamdmg = min(reac_volume * 5, 25)) victim.update_damage_hud() /datum/reagent/consumable/condensedcapsaicin/on_mob_life(mob/living/carbon/M) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 3970e8157b..b5a56c944d 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -1171,6 +1171,7 @@ M.AdjustUnconscious(-20, 0) M.AdjustStun(-20, 0) M.AdjustKnockdown(-20, 0) + M.AdjustSleeping(-20, 0) M.adjustStaminaLoss(-30, 0) ..() return TRUE diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index a51134f84d..afb985120e 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -18,6 +18,7 @@ var/spray_range = 3 //the range of tiles the sprayer will reach when in spray mode. var/stream_range = 1 //the range of tiles the sprayer will reach when in stream mode. var/stream_amount = 10 //the amount of reagents transfered when in stream mode. + var/spray_delay = 3 //The amount of sleep() delay between each chempuff step. var/can_fill_from_container = TRUE amount_per_transfer_from_this = 5 volume = 250 @@ -64,7 +65,7 @@ /obj/item/reagent_containers/spray/proc/spray(atom/A) - var/range = max(min(current_range, get_dist(src, A)), 1) + var/range = CLAMP(get_dist(src, A), 1, current_range) var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) D.create_reagents(amount_per_transfer_from_this) var/puff_reagent_left = range //how many turf, mob or dense objet we can react with before we consider the chem puff consumed @@ -74,7 +75,7 @@ else reagents.trans_to(D, amount_per_transfer_from_this, 1/range) D.color = mix_color_from_reagents(D.reagents.reagent_list) - var/wait_step = max(round(2+3/range), 2) + var/wait_step = max(round(2+ spray_delay * INVERSE(range)), 2) do_spray(A, wait_step, D, range, puff_reagent_left) /obj/item/reagent_containers/spray/proc/do_spray(atom/A, wait_step, obj/effect/decal/chempuff/D, range, puff_reagent_left) @@ -166,7 +167,7 @@ user.visible_message("[user] decided life was worth living.") return -//Drying Agent +//Drying Agent /obj/item/reagent_containers/spray/drying_agent name = "drying agent spray" desc = "A spray bottle for drying agent." @@ -194,6 +195,7 @@ righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' volume = 40 stream_range = 4 + spray_delay = 1 amount_per_transfer_from_this = 5 list_reagents = list("condensedcapsaicin" = 40) diff --git a/code/modules/research/designs/biogenerator_designs.dm b/code/modules/research/designs/biogenerator_designs.dm index 3d48dd51ec..05e4c667f6 100644 --- a/code/modules/research/designs/biogenerator_designs.dm +++ b/code/modules/research/designs/biogenerator_designs.dm @@ -59,6 +59,14 @@ build_path = /obj/item/reagent_containers/food/snacks/monkeycube category = list("initial", "Food") +/datum/design/smeat + name = "Biomass Meat Slab" + id = "smeat" + build_type = BIOGENERATOR + materials = list(MAT_BIOMASS = 175) + build_path = /obj/item/reagent_containers/food/snacks/meat/slab/synthmeat + category = list("initial", "Food") + /datum/design/ez_nut name = "E-Z Nutrient" id = "ez_nut" diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm deleted file mode 100644 index 68aee66d62..0000000000 --- a/code/modules/research/designs/comp_board_designs.dm +++ /dev/null @@ -1,305 +0,0 @@ -///////////////////Computer Boards/////////////////////////////////// - -/datum/design/board - name = "Computer Design ( NULL ENTRY )" - desc = "I promise this doesn't give you syndicate goodies!" - build_type = IMPRINTER - materials = list(MAT_GLASS = 1000) - -/datum/design/board/arcade_battle - name = "Computer Design (Battle Arcade Machine)" - desc = "Allows for the construction of circuit boards used to build a new arcade machine." - id = "arcade_battle" - build_path = /obj/item/circuitboard/computer/arcade/battle - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/orion_trail - name = "Computer Design (Orion Trail Arcade Machine)" - desc = "Allows for the construction of circuit boards used to build a new Orion Trail machine." - id = "arcade_orion" - build_path = /obj/item/circuitboard/computer/arcade/orion_trail - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/seccamera - name = "Computer Design (Security Camera)" - desc = "Allows for the construction of circuit boards used to build security camera computers." - id = "seccamera" - build_path = /obj/item/circuitboard/computer/security - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SECURITY - -/datum/design/board/rdcamera - name = "Computer Design (Research Monitor)" - desc = "Allows for the construction of circuit boards used to build research camera computers." - id = "rdcamera" - build_path = /obj/item/circuitboard/computer/research - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/xenobiocamera - name = "Computer Design (Xenobiology Console)" - desc = "Allows for the construction of circuit boards used to build xenobiology camera computers." - id = "xenobioconsole" - build_path = /obj/item/circuitboard/computer/xenobiology - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/aiupload - name = "Computer Design (AI Upload)" - desc = "Allows for the construction of circuit boards used to build an AI Upload Console." - id = "aiupload" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) - build_path = /obj/item/circuitboard/computer/aiupload - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/borgupload - name = "Computer Design (Cyborg Upload)" - desc = "Allows for the construction of circuit boards used to build a Cyborg Upload Console." - id = "borgupload" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) - build_path = /obj/item/circuitboard/computer/borgupload - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/med_data - name = "Computer Design (Medical Records)" - desc = "Allows for the construction of circuit boards used to build a medical records console." - id = "med_data" - build_path = /obj/item/circuitboard/computer/med_data - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/board/operating - name = "Computer Design (Operating Computer)" - desc = "Allows for the construction of circuit boards used to build an operating computer console." - id = "operating" - build_path = /obj/item/circuitboard/computer/operating - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/pandemic - name = "Computer Design (PanD.E.M.I.C. 2200)" - desc = "Allows for the construction of circuit boards used to build a PanD.E.M.I.C. 2200 console." - id = "pandemic" - build_path = /obj/item/circuitboard/computer/pandemic - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/board/scan_console - name = "Computer Design (DNA Machine)" - desc = "Allows for the construction of circuit boards used to build a new DNA scanning console." - id = "scan_console" - build_path = /obj/item/circuitboard/computer/scan_consolenew - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/comconsole - name = "Computer Design (Communications)" - desc = "Allows for the construction of circuit boards used to build a communications console." - id = "comconsole" - build_path = /obj/item/circuitboard/computer/communications - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SECURITY //Honestly should have a bridge techfab for this sometime. - -/datum/design/board/idcardconsole - name = "Computer Design (ID Console)" - desc = "Allows for the construction of circuit boards used to build an ID computer." - id = "idcardconsole" - build_path = /obj/item/circuitboard/computer/card - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SECURITY //Honestly should have a bridge techfab for this sometime. - -/datum/design/board/crewconsole - name = "Computer Design (Crew monitoring computer)" - desc = "Allows for the construction of circuit boards used to build a Crew monitoring computer." - id = "crewconsole" - build_path = /obj/item/circuitboard/computer/crew - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/board/secdata - name = "Computer Design (Security Records Console)" - desc = "Allows for the construction of circuit boards used to build a security records console." - id = "secdata" - build_path = /obj/item/circuitboard/computer/secure_data - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SECURITY - -/datum/design/board/atmosalerts - name = "Computer Design (Atmosphere Alert)" - desc = "Allows for the construction of circuit boards used to build an atmosphere alert console." - id = "atmosalerts" - build_path = /obj/item/circuitboard/computer/atmos_alert - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/atmos_control - name = "Computer Design (Atmospheric Monitor)" - desc = "Allows for the construction of circuit boards used to build an Atmospheric Monitor." - id = "atmos_control" - build_path = /obj/item/circuitboard/computer/atmos_control - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/robocontrol - name = "Computer Design (Robotics Control Console)" - desc = "Allows for the construction of circuit boards used to build a Robotics Control console." - id = "robocontrol" - build_path = /obj/item/circuitboard/computer/robotics - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/slot_machine - name = "Computer Design (Slot Machine)" - desc = "Allows for the construction of circuit boards used to build a new slot machine." - id = "slotmachine" - build_path = /obj/item/circuitboard/computer/slot_machine - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/powermonitor - name = "Computer Design (Power Monitor)" - desc = "Allows for the construction of circuit boards used to build a new power monitor." - id = "powermonitor" - build_path = /obj/item/circuitboard/computer/powermonitor - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/solarcontrol - name = "Computer Design (Solar Control)" - desc = "Allows for the construction of circuit boards used to build a solar control console." - id = "solarcontrol" - build_path = /obj/item/circuitboard/computer/solar_control - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/prisonmanage - name = "Computer Design (Prisoner Management Console)" - desc = "Allows for the construction of circuit boards used to build a prisoner management console." - id = "prisonmanage" - build_path = /obj/item/circuitboard/computer/prisoner - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SECURITY - -/datum/design/board/mechacontrol - name = "Computer Design (Exosuit Control Console)" - desc = "Allows for the construction of circuit boards used to build an exosuit control console." - id = "mechacontrol" - build_path = /obj/item/circuitboard/computer/mecha_control - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/mechapower - name = "Computer Design (Mech Bay Power Control Console)" - desc = "Allows for the construction of circuit boards used to build a mech bay power control console." - id = "mechapower" - build_path = /obj/item/circuitboard/computer/mech_bay_power_console - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/rdconsole - name = "Computer Design (R&D Console)" - desc = "Allows for the construction of circuit boards used to build a new R&D console." - id = "rdconsole" - build_path = /obj/item/circuitboard/computer/rdconsole - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/cargo - name = "Computer Design (Supply Console)" - desc = "Allows for the construction of circuit boards used to build a Supply Console." - id = "cargo" - build_path = /obj/item/circuitboard/computer/cargo - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_CARGO - -/datum/design/board/cargorequest - name = "Computer Design (Supply Request Console)" - desc = "Allows for the construction of circuit boards used to build a Supply Request Console." - id = "cargorequest" - build_path = /obj/item/circuitboard/computer/cargo/request - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_CARGO - -/datum/design/board/bounty - name = "Computer Design (Bounty Console)" - desc = "Allows for the construction of circuit boards used to build a Bounty Console." - id = "bounty" - build_path = /obj/item/circuitboard/computer/bounty - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_CARGO - -/datum/design/board/mining - name = "Computer Design (Outpost Status Display)" - desc = "Allows for the construction of circuit boards used to build an outpost status display console." - id = "mining" - build_path = /obj/item/circuitboard/computer/mining - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SECURITY - -/datum/design/board/comm_monitor - name = "Computer Design (Telecommunications Monitoring Console)" - desc = "Allows for the construction of circuit boards used to build a telecommunications monitor." - id = "comm_monitor" - build_path = /obj/item/circuitboard/computer/comm_monitor - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/comm_server - name = "Computer Design (Telecommunications Server Monitoring Console)" - desc = "Allows for the construction of circuit boards used to build a telecommunication server browser and monitor." - id = "comm_server" - build_path = /obj/item/circuitboard/computer/comm_server - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/message_monitor - name = "Computer Design (Messaging Monitor Console)" - desc = "Allows for the construction of circuit boards used to build a messaging monitor console." - id = "message_monitor" - build_path = /obj/item/circuitboard/computer/message_monitor - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/aifixer - name = "Computer Design (AI Integrity Restorer)" - desc = "Allows for the construction of circuit boards used to build an AI Integrity Restorer." - id = "aifixer" - build_path = /obj/item/circuitboard/computer/aifixer - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/libraryconsole - name = "Computer Design (Library Console)" - desc = "Allows for the construction of circuit boards used to build a new library console." - id = "libraryconsole" - build_path = /obj/item/circuitboard/computer/libraryconsole - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/apc_control - name = "Computer Design (APC Control)" - desc = "Allows for the construction of circuit boards used to build a new APC control console." - id = "apc_control" - build_path = /obj/item/circuitboard/computer/apc_control - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/nanite_chamber_control - name = "Computer Design (Nanite Chamber Control)" - desc = "Allows for the construction of circuit boards used to build a new nanite chamber control console." - id = "nanite_chamber_control" - build_path = /obj/item/circuitboard/computer/nanite_chamber_control - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/nanite_cloud_control - name = "Computer Design (Nanite Cloud Control)" - desc = "Allows for the construction of circuit boards used to build a new nanite cloud control console." - id = "nanite_cloud_control" - build_path = /obj/item/circuitboard/computer/nanite_cloud_controller - category = list("Computer Boards") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE diff --git a/code/modules/research/designs/comp_board_designs/comp_board_designs_all_misc.dm b/code/modules/research/designs/comp_board_designs/comp_board_designs_all_misc.dm new file mode 100644 index 0000000000..780a1bc97d --- /dev/null +++ b/code/modules/research/designs/comp_board_designs/comp_board_designs_all_misc.dm @@ -0,0 +1,38 @@ +///////////////////Computer Boards/////////////////////////////////// +/datum/design/board + name = "Computer Design ( NULL ENTRY )" + desc = "A blank compurter board!" + build_type = IMPRINTER + materials = list(MAT_GLASS = 1000) + +/datum/design/board/arcade_battle + name = "Computer Design (Battle Arcade Machine)" + desc = "Allows for the construction of circuit boards used to build a new arcade machine." + id = "arcade_battle" + build_path = /obj/item/circuitboard/computer/arcade/battle + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/orion_trail + name = "Computer Design (Orion Trail Arcade Machine)" + desc = "Allows for the construction of circuit boards used to build a new Orion Trail machine." + id = "arcade_orion" + build_path = /obj/item/circuitboard/computer/arcade/orion_trail + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/slot_machine + name = "Computer Design (Slot Machine)" + desc = "Allows for the construction of circuit boards used to build a new slot machine." + id = "slotmachine" + build_path = /obj/item/circuitboard/computer/slot_machine + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/libraryconsole + name = "Computer Design (Library Console)" + desc = "Allows for the construction of circuit boards used to build a new library console." + id = "libraryconsole" + build_path = /obj/item/circuitboard/computer/libraryconsole + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ALL \ No newline at end of file diff --git a/code/modules/research/designs/comp_board_designs/comp_board_designs_cargo .dm b/code/modules/research/designs/comp_board_designs/comp_board_designs_cargo .dm new file mode 100644 index 0000000000..fd548adc76 --- /dev/null +++ b/code/modules/research/designs/comp_board_designs/comp_board_designs_cargo .dm @@ -0,0 +1,35 @@ +/////////////////// +///CARGO Boards//// +/////////////////// + +/datum/design/board/cargo + name = "Computer Design (Supply Console)" + desc = "Allows for the construction of circuit boards used to build a Supply Console." + id = "cargo" + build_path = /obj/item/circuitboard/computer/cargo + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/board/cargorequest + name = "Computer Design (Supply Request Console)" + desc = "Allows for the construction of circuit boards used to build a Supply Request Console." + id = "cargorequest" + build_path = /obj/item/circuitboard/computer/cargo/request + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/board/bounty + name = "Computer Design (Bounty Console)" + desc = "Allows for the construction of circuit boards used to build a Bounty Console." + id = "bounty" + build_path = /obj/item/circuitboard/computer/bounty + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/board/mining + name = "Computer Design (Outpost Status Display)" + desc = "Allows for the construction of circuit boards used to build an outpost status display console." + id = "mining" + build_path = /obj/item/circuitboard/computer/mining + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SECURITY \ No newline at end of file diff --git a/code/modules/research/designs/comp_board_designs/comp_board_designs_engi.dm b/code/modules/research/designs/comp_board_designs/comp_board_designs_engi.dm new file mode 100644 index 0000000000..5767588178 --- /dev/null +++ b/code/modules/research/designs/comp_board_designs/comp_board_designs_engi.dm @@ -0,0 +1,75 @@ +/////////////////// +///ENGINE Boards/// +/////////////////// + +/datum/design/board/comm_monitor + name = "Computer Design (Telecommunications Monitoring Console)" + desc = "Allows for the construction of circuit boards used to build a telecommunications monitor." + id = "comm_monitor" + build_path = /obj/item/circuitboard/computer/comm_monitor + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/comm_server + name = "Computer Design (Telecommunications Server Monitoring Console)" + desc = "Allows for the construction of circuit boards used to build a telecommunication server browser and monitor." + id = "comm_server" + build_path = /obj/item/circuitboard/computer/comm_server + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/message_monitor + name = "Computer Design (Messaging Monitor Console)" + desc = "Allows for the construction of circuit boards used to build a messaging monitor console." + id = "message_monitor" + build_path = /obj/item/circuitboard/computer/message_monitor + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/apc_control + name = "Computer Design (APC Control)" + desc = "Allows for the construction of circuit boards used to build a new APC control console." + id = "apc_control" + build_path = /obj/item/circuitboard/computer/apc_control + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/meteor_console + name = "Computer Design (Meteor Satellite Console)" + desc = "Allows for the construction of circuit boards used to build a new Meteor Satellite monitor console." + id = "meteor_console" + build_path = /obj/item/circuitboard/computer/sat_control + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/atmosalerts + name = "Computer Design (Atmosphere Alert)" + desc = "Allows for the construction of circuit boards used to build an atmosphere alert console." + id = "atmosalerts" + build_path = /obj/item/circuitboard/computer/atmos_alert + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/atmos_control + name = "Computer Design (Atmospheric Monitor)" + desc = "Allows for the construction of circuit boards used to build an Atmospheric Monitor." + id = "atmos_control" + build_path = /obj/item/circuitboard/computer/atmos_control + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/powermonitor + name = "Computer Design (Power Monitor)" + desc = "Allows for the construction of circuit boards used to build a new power monitor." + id = "powermonitor" + build_path = /obj/item/circuitboard/computer/powermonitor + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/solarcontrol + name = "Computer Design (Solar Control)" + desc = "Allows for the construction of circuit boards used to build a solar control console." + id = "solarcontrol" + build_path = /obj/item/circuitboard/computer/solar_control + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING \ No newline at end of file diff --git a/code/modules/research/designs/comp_board_designs/comp_board_designs_medical.dm b/code/modules/research/designs/comp_board_designs/comp_board_designs_medical.dm new file mode 100644 index 0000000000..05632fad69 --- /dev/null +++ b/code/modules/research/designs/comp_board_designs/comp_board_designs_medical.dm @@ -0,0 +1,43 @@ +/////////////////// +///MEDICAL Boards// +/////////////////// + +/datum/design/board/pandemic + name = "Computer Design (PanD.E.M.I.C. 2200)" + desc = "Allows for the construction of circuit boards used to build a PanD.E.M.I.C. 2200 console." + id = "pandemic" + build_path = /obj/item/circuitboard/computer/pandemic + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/board/operating + name = "Computer Design (Operating Computer)" + desc = "Allows for the construction of circuit boards used to build an operating computer console." + id = "operating" + build_path = /obj/item/circuitboard/computer/operating + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/scan_console + name = "Computer Design (DNA Machine)" + desc = "Allows for the construction of circuit boards used to build a new DNA scanning console." + id = "scan_console" + build_path = /obj/item/circuitboard/computer/scan_consolenew + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/crewconsole + name = "Computer Design (Crew monitoring computer)" + desc = "Allows for the construction of circuit boards used to build a Crew monitoring computer." + id = "crewconsole" + build_path = /obj/item/circuitboard/computer/crew + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SECURITY + +/datum/design/board/med_data + name = "Computer Design (Medical Records)" + desc = "Allows for the construction of circuit boards used to build a medical records console." + id = "med_data" + build_path = /obj/item/circuitboard/computer/med_data + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SECURITY diff --git a/code/modules/research/designs/comp_board_designs/comp_board_designs_sci.dm b/code/modules/research/designs/comp_board_designs/comp_board_designs_sci.dm new file mode 100644 index 0000000000..e0b0a22be6 --- /dev/null +++ b/code/modules/research/designs/comp_board_designs/comp_board_designs_sci.dm @@ -0,0 +1,93 @@ +/////////////////// +///SCI Boards////// +/////////////////// + +/datum/design/board/rdcamera + name = "Computer Design (Research Monitor)" + desc = "Allows for the construction of circuit boards used to build research camera computers." + id = "rdcamera" + build_path = /obj/item/circuitboard/computer/research + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/xenobiocamera + name = "Computer Design (Xenobiology Console)" + desc = "Allows for the construction of circuit boards used to build xenobiology camera computers." + id = "xenobioconsole" + build_path = /obj/item/circuitboard/computer/xenobiology + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/aiupload + name = "Computer Design (AI Upload)" + desc = "Allows for the construction of circuit boards used to build an AI Upload Console." + id = "aiupload" + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) + build_path = /obj/item/circuitboard/computer/aiupload + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/borgupload + name = "Computer Design (Cyborg Upload)" + desc = "Allows for the construction of circuit boards used to build a Cyborg Upload Console." + id = "borgupload" + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) + build_path = /obj/item/circuitboard/computer/borgupload + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/robocontrol + name = "Computer Design (Robotics Control Console)" + desc = "Allows for the construction of circuit boards used to build a Robotics Control console." + id = "robocontrol" + build_path = /obj/item/circuitboard/computer/robotics + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/mechacontrol + name = "Computer Design (Exosuit Control Console)" + desc = "Allows for the construction of circuit boards used to build an exosuit control console." + id = "mechacontrol" + build_path = /obj/item/circuitboard/computer/mecha_control + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/mechapower + name = "Computer Design (Mech Bay Power Control Console)" + desc = "Allows for the construction of circuit boards used to build a mech bay power control console." + id = "mechapower" + build_path = /obj/item/circuitboard/computer/mech_bay_power_console + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/rdconsole + name = "Computer Design (R&D Console)" + desc = "Allows for the construction of circuit boards used to build a new R&D console." + id = "rdconsole" + build_path = /obj/item/circuitboard/computer/rdconsole + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/nanite_chamber_control + name = "Computer Design (Nanite Chamber Control)" + desc = "Allows for the construction of circuit boards used to build a new nanite chamber control console." + id = "nanite_chamber_control" + build_path = /obj/item/circuitboard/computer/nanite_chamber_control + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/nanite_cloud_control + name = "Computer Design (Nanite Cloud Control)" + desc = "Allows for the construction of circuit boards used to build a new nanite cloud control console." + id = "nanite_cloud_control" + build_path = /obj/item/circuitboard/computer/nanite_cloud_controller + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/aifixer + name = "Computer Design (AI Integrity Restorer)" + desc = "Allows for the construction of circuit boards used to build an AI Integrity Restorer." + id = "aifixer" + build_path = /obj/item/circuitboard/computer/aifixer + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE \ No newline at end of file diff --git a/code/modules/research/designs/comp_board_designs/comp_board_designs_sec.dm b/code/modules/research/designs/comp_board_designs/comp_board_designs_sec.dm new file mode 100644 index 0000000000..0e93481f66 --- /dev/null +++ b/code/modules/research/designs/comp_board_designs/comp_board_designs_sec.dm @@ -0,0 +1,43 @@ +/////////////////// +///SECURITY Boards/ +/////////////////// + +/datum/design/board/seccamera + name = "Computer Design (Security Camera)" + desc = "Allows for the construction of circuit boards used to build security camera computers." + id = "seccamera" + build_path = /obj/item/circuitboard/computer/security + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/board/secdata + name = "Computer Design (Security Records Console)" + desc = "Allows for the construction of circuit boards used to build a security records console." + id = "secdata" + build_path = /obj/item/circuitboard/computer/secure_data + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/board/prisonmanage + name = "Computer Design (Prisoner Management Console)" + desc = "Allows for the construction of circuit boards used to build a prisoner management console." + id = "prisonmanage" + build_path = /obj/item/circuitboard/computer/prisoner + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + +/datum/design/board/comconsole + name = "Computer Design (Communications)" + desc = "Allows for the construction of circuit boards used to build a communications console." + id = "comconsole" + build_path = /obj/item/circuitboard/computer/communications + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_ENGINEERING //Honestly should have a bridge techfab for this sometime. + +/datum/design/board/idcardconsole + name = "Computer Design (ID Console)" + desc = "Allows for the construction of circuit boards used to build an ID computer." + id = "idcardconsole" + build_path = /obj/item/circuitboard/computer/card + category = list("Computer Boards") + departmental_flags = DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_ENGINEERING //Honestly should have a bridge techfab for this sometime. diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm deleted file mode 100644 index 93dfd3d2bb..0000000000 --- a/code/modules/research/designs/machine_designs.dm +++ /dev/null @@ -1,572 +0,0 @@ -//////////////////////////////////////// -//////////////MISC Boards/////////////// -//////////////////////////////////////// - -/datum/design/board/smes - name = "Machine Design (SMES Board)" - desc = "The circuit board for a SMES." - id = "smes" - build_path = /obj/item/circuitboard/machine/smes - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/circulator - name = "Machine Design (Circulator Board)" - desc = "The circuit board for a circulator." - id = "circulator" - build_path = /obj/item/circuitboard/machine/circulator - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/teg - name = "Machine Design (TEG Board)" - desc = "The circuit board for a TEG." - id = "teg" - build_path = /obj/item/circuitboard/machine/generator - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/announcement_system - name = "Machine Design (Automated Announcement System Board)" - desc = "The circuit board for an automated announcement system." - id = "automated_announcement" - build_path = /obj/item/circuitboard/machine/announcement_system - category = list("Subspace Telecomms") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/turbine_computer - name = "Computer Design (Power Turbine Console Board)" - desc = "The circuit board for a power turbine console." - id = "power_turbine_console" - build_path = /obj/item/circuitboard/computer/turbine_computer - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/emitter - name = "Machine Design (Emitter Board)" - desc = "The circuit board for an emitter." - id = "emitter" - build_path = /obj/item/circuitboard/machine/emitter - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/power_compressor - name = "Machine Design (Power Compressor Board)" - desc = "The circuit board for a power compressor." - id = "power_compressor" - build_path = /obj/item/circuitboard/machine/power_compressor - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/power_turbine - name = "Machine Design (Power Turbine Board)" - desc = "The circuit board for a power turbine." - id = "power_turbine" - build_path = /obj/item/circuitboard/machine/power_turbine - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/thermomachine - name = "Machine Design (Freezer/Heater Board)" - desc = "The circuit board for a freezer/heater." - id = "thermomachine" - build_path = /obj/item/circuitboard/machine/thermomachine - category = list ("Engineering Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/space_heater - name = "Machine Design (Space Heater Board)" - desc = "The circuit board for a space heater." - id = "space_heater" - build_path = /obj/item/circuitboard/machine/space_heater - category = list ("Engineering Machinery") - departmental_flags = ALL - -/datum/design/board/teleport_station - name = "Machine Design (Teleportation Station Board)" - desc = "The circuit board for a teleportation station." - id = "tele_station" - build_path = /obj/item/circuitboard/machine/teleporter_station - category = list ("Teleportation Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/teleport_hub - name = "Machine Design (Teleportation Hub Board)" - desc = "The circuit board for a teleportation hub." - id = "tele_hub" - build_path = /obj/item/circuitboard/machine/teleporter_hub - category = list ("Teleportation Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/quantumpad - name = "Machine Design (Quantum Pad Board)" - desc = "The circuit board for a quantum telepad." - id = "quantumpad" - build_path = /obj/item/circuitboard/machine/quantumpad - category = list ("Teleportation Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/launchpad - name = "Machine Design (Bluespace Launchpad Board)" - desc = "The circuit board for a bluespace Launchpad." - id = "launchpad" - build_path = /obj/item/circuitboard/machine/launchpad - category = list ("Teleportation Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/launchpad_console - name = "Machine Design (Bluespace Launchpad Console Board)" - desc = "The circuit board for a bluespace launchpad Console." - id = "launchpad_console" - build_path = /obj/item/circuitboard/computer/launchpad_console - category = list ("Teleportation Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/teleconsole - name = "Computer Design (Teleporter Console)" - desc = "Allows for the construction of circuit boards used to build a teleporter control console." - id = "teleconsole" - build_path = /obj/item/circuitboard/computer/teleporter - category = list("Teleportation Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/sleeper - name = "Machine Design (Sleeper Board)" - desc = "The circuit board for a sleeper." - id = "sleeper" - build_path = /obj/item/circuitboard/machine/sleeper - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_MEDICAL - category = list ("Medical Machinery") - -/datum/design/board/vr_sleeper - name = "Machine Design (VR Sleeper Board)" - desc = "The circuit board for a VR sleeper." - id = "vr_sleeper" - build_path = /obj/item/circuitboard/machine/vr_sleeper - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE - category = list ("Medical Machinery") - -/datum/design/board/cryotube - name = "Machine Design (Cryotube Board)" - desc = "The circuit board for a cryotube." - id = "cryotube" - build_path = /obj/item/circuitboard/machine/cryo_tube - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_MEDICAL - category = list ("Medical Machinery") - -/datum/design/board/chem_dispenser - name = "Machine Design (Portable Chem Dispenser Board)" - desc = "The circuit board for a portable chem dispenser." - id = "chem_dispenser" - build_path = /obj/item/circuitboard/machine/chem_dispenser - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_MEDICAL - category = list ("Medical Machinery") - -/datum/design/board/chem_master - name = "Machine Design (Chem Master Board)" - desc = "The circuit board for a Chem Master 3000." - id = "chem_master" - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_MEDICAL - build_path = /obj/item/circuitboard/machine/chem_master - category = list ("Medical Machinery") - -/datum/design/board/chem_heater - name = "Machine Design (Chemical Heater Board)" - desc = "The circuit board for a chemical heater." - id = "chem_heater" - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_MEDICAL - build_path = /obj/item/circuitboard/machine/chem_heater - category = list ("Medical Machinery") - -/datum/design/board/smoke_machine - name = "Machine Design (Smoke Machine)" - desc = "The circuit board for a smoke machine." - id = "smoke_machine" - build_path = /obj/item/circuitboard/machine/smoke_machine - category = list ("Medical Machinery") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/board/reagentgrinder - name = "Machine Design (All-In-One Grinder)" - desc = "The circuit board for an All-In-One Grinder." - id = "reagentgrinder" - build_path = /obj/item/circuitboard/machine/reagentgrinder - category = list ("Medical Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/clonecontrol - name = "Computer Design (Cloning Machine Console)" - desc = "Allows for the construction of circuit boards used to build a new Cloning Machine console." - id = "clonecontrol" - build_path = /obj/item/circuitboard/computer/cloning - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - category = list("Medical Machinery") - -/datum/design/board/clonepod - name = "Machine Design (Clone Pod)" - desc = "Allows for the construction of circuit boards used to build a Cloning Pod." - id = "clonepod" - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - build_path = /obj/item/circuitboard/machine/clonepod - category = list("Medical Machinery") - -/datum/design/board/clonescanner - name = "Machine Design (Cloning Scanner)" - desc = "Allows for the construction of circuit boards used to build a Cloning Scanner." - id = "clonescanner" - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - build_path = /obj/item/circuitboard/machine/clonescanner - category = list("Medical Machinery") - -/datum/design/board/biogenerator - name = "Machine Design (Biogenerator Board)" - desc = "The circuit board for a biogenerator." - id = "biogenerator" - build_path = /obj/item/circuitboard/machine/biogenerator - category = list ("Hydroponics Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/hydroponics - name = "Machine Design (Hydroponics Tray Board)" - desc = "The circuit board for a hydroponics tray." - id = "hydro_tray" - build_path = /obj/item/circuitboard/machine/hydroponics - category = list ("Hydroponics Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/destructive_analyzer - name = "Machine Design (Destructive Analyzer Board)" - desc = "The circuit board for a destructive analyzer." - id = "destructive_analyzer" - build_path = /obj/item/circuitboard/machine/destructive_analyzer - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/experimentor - name = "Machine Design (E.X.P.E.R.I-MENTOR Board)" - desc = "The circuit board for an E.X.P.E.R.I-MENTOR." - id = "experimentor" - build_path = /obj/item/circuitboard/machine/experimentor - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/protolathe - name = "Machine Design (Protolathe Board)" - desc = "The circuit board for a protolathe." - id = "protolathe" - build_path = /obj/item/circuitboard/machine/protolathe - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/circuit_imprinter - name = "Machine Design (Circuit Imprinter Board)" - desc = "The circuit board for a circuit imprinter." - id = "circuit_imprinter" - build_path = /obj/item/circuitboard/machine/circuit_imprinter - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/rdservercontrol - name = "Computer Design (R&D Server Control Console Board)" - desc = "The circuit board for an R&D Server Control Console." - id = "rdservercontrol" - build_path = /obj/item/circuitboard/computer/rdservercontrol - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/rdserver - name = "Machine Design (R&D Server Board)" - desc = "The circuit board for an R&D Server." - id = "rdserver" - build_path = /obj/item/circuitboard/machine/rdserver - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/mechfab - name = "Machine Design (Exosuit Fabricator Board)" - desc = "The circuit board for an Exosuit Fabricator." - id = "mechfab" - build_path = /obj/item/circuitboard/machine/mechfab - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/cyborgrecharger - name = "Machine Design (Cyborg Recharger Board)" - desc = "The circuit board for a Cyborg Recharger." - id = "cyborgrecharger" - build_path = /obj/item/circuitboard/machine/cyborgrecharger - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/mech_recharger - name = "Machine Design (Mechbay Recharger Board)" - desc = "The circuit board for a Mechbay Recharger." - id = "mech_recharger" - build_path = /obj/item/circuitboard/machine/mech_recharger - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/nanite_chamber - name = "Machine Design (Nanite Chamber Board)" - desc = "The circuit board for a Nanite Chamber." - id = "nanite_chamber" - build_path = /obj/item/circuitboard/machine/nanite_chamber - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/public_nanite_chamber - name = "Machine Design (Public Nanite Chamber Board)" - desc = "The circuit board for a Public Nanite Chamber." - id = "public_nanite_chamber" - build_path = /obj/item/circuitboard/machine/public_nanite_chamber - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/nanite_programmer - name = "Machine Design (Nanite Programmer Board)" - desc = "The circuit board for a Nanite Programmer." - id = "nanite_programmer" - build_path = /obj/item/circuitboard/machine/nanite_programmer - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/nanite_program_hub - name = "Machine Design (Nanite Program Hub Board)" - desc = "The circuit board for a Nanite Program Hub." - id = "nanite_program_hub" - build_path = /obj/item/circuitboard/machine/nanite_program_hub - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/microwave - name = "Machine Design (Microwave Board)" - desc = "The circuit board for a microwave." - id = "microwave" - build_path = /obj/item/circuitboard/machine/microwave - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/gibber - name = "Machine Design (Gibber Board)" - desc = "The circuit board for a gibber." - id = "gibber" - build_path = /obj/item/circuitboard/machine/gibber - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/smartfridge - name = "Machine Design (Smartfridge Board)" - desc = "The circuit board for a smartfridge." - id = "smartfridge" - build_path = /obj/item/circuitboard/machine/smartfridge - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/monkey_recycler - name = "Machine Design (Monkey Recycler Board)" - desc = "The circuit board for a monkey recycler." - id = "monkey_recycler" - build_path = /obj/item/circuitboard/machine/monkey_recycler - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/seed_extractor - name = "Machine Design (Seed Extractor Board)" - desc = "The circuit board for a seed extractor." - id = "seed_extractor" - build_path = /obj/item/circuitboard/machine/seed_extractor - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/processor - name = "Machine Design (Food/Slime Processor Board)" - desc = "The circuit board for a processing unit. Screwdriver the circuit to switch between food (default) or slime processing." - id = "processor" - build_path = /obj/item/circuitboard/machine/processor - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/soda_dispenser - name = "Machine Design (Portable Soda Dispenser Board)" - desc = "The circuit board for a portable soda dispenser." - id = "soda_dispenser" - build_path = /obj/item/circuitboard/machine/chem_dispenser/drinks - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - category = list ("Misc. Machinery") - -/datum/design/board/beer_dispenser - name = "Machine Design (Portable Booze Dispenser Board)" - desc = "The circuit board for a portable booze dispenser." - id = "beer_dispenser" - build_path = /obj/item/circuitboard/machine/chem_dispenser/drinks/beer - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - category = list ("Misc. Machinery") - -/datum/design/board/recycler - name = "Machine Design (Recycler Board)" - desc = "The circuit board for a recycler." - id = "recycler" - build_path = /obj/item/circuitboard/machine/recycler - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/holopad - name = "Machine Design (AI Holopad Board)" - desc = "The circuit board for a holopad." - id = "holopad" - build_path = /obj/item/circuitboard/machine/holopad - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/autolathe - name = "Machine Design (Autolathe Board)" - desc = "The circuit board for an autolathe." - id = "autolathe" - build_path = /obj/item/circuitboard/machine/autolathe - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL //Lets be honest here half the maps have public ones. - -/datum/design/board/recharger - name = "Machine Design (Weapon Recharger Board)" - desc = "The circuit board for a Weapon Recharger." - id = "recharger" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) - build_path = /obj/item/circuitboard/machine/recharger - category = list("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/vendor - name = "Machine Design (Vendor Board)" - desc = "The circuit board for a Vendor." - id = "vendor" - build_path = /obj/item/circuitboard/machine/vendor - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/ore_redemption - name = "Machine Design (Ore Redemption Board)" - desc = "The circuit board for an Ore Redemption machine." - id = "ore_redemption" - build_path = /obj/item/circuitboard/machine/ore_redemption - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/mining_equipment_vendor - name = "Machine Design (Mining Rewards Vender Board)" - desc = "The circuit board for a Mining Rewards Vender." - id = "mining_equipment_vendor" - build_path = /obj/item/circuitboard/machine/mining_equipment_vendor - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO - -/datum/design/board/tesla_coil - name = "Machine Design (Tesla Coil Board)" - desc = "The circuit board for a tesla coil." - id = "tesla_coil" - build_path = /obj/item/circuitboard/machine/tesla_coil - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/rad_collector - name = "Machine Design (Radiation Collector Board)" - desc = "The circuit board for a radiation collector array." - id = "rad_collector" - build_path = /obj/item/circuitboard/machine/rad_collector - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/grounding_rod - name = "Machine Design (Grounding Rod Board)" - desc = "The circuit board for a grounding rod." - id = "grounding_rod" - build_path = /obj/item/circuitboard/machine/grounding_rod - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/plantgenes - name = "Machine Design (Plant DNA Manipulator Board)" - desc = "The circuit board for a plant DNA manipulator." - id = "plantgenes" - build_path = /obj/item/circuitboard/machine/plantgenes - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/ntnet_relay - name = "Machine Design (NTNet Relay Board)" - desc = "The circuit board for a wireless network relay." - id = "ntnet_relay" - build_path = /obj/item/circuitboard/machine/ntnet_relay - category = list("Subspace Telecomms") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/limbgrower - name = "Machine Design (Limb Grower Board)" - desc = "The circuit board for a limb grower." - id = "limbgrower" - build_path = /obj/item/circuitboard/machine/limbgrower - category = list("Medical Machinery") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/board/harvester - name = "Machine Design (Organ Harvester Board)" - desc = "The circuit board for an organ harvester." - id = "harvester" - build_path = /obj/item/circuitboard/machine/harvester - category = list("Medical Machinery") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/board/deepfryer - name = "Machine Design (Deep Fryer)" - desc = "The circuit board for a Deep Fryer." - id = "deepfryer" - build_path = /obj/item/circuitboard/machine/deep_fryer - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/donksofttoyvendor - name = "Machine Design (Donksoft Toy Vendor Board)" - desc = "The circuit board for a Donksoft Toy Vendor." - id = "donksofttoyvendor" - build_path = /obj/item/circuitboard/machine/vending/donksofttoyvendor - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL //Toys!! - -/datum/design/board/cell_charger - name = "Machine Design (Cell Charger Board)" - desc = "The circuit board for a cell charger." - id = "cell_charger" - build_path = /obj/item/circuitboard/machine/cell_charger - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_ALL - -/datum/design/board/dish_drive - name = "Machine Design (Dish Drive)" - desc = "The circuit board for a dish drive." - id = "dish_drive" - build_path = /obj/item/circuitboard/machine/dish_drive - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/board/stacking_unit_console - name = "Machine Design (Stacking Machine Console)" - desc = "The circuit board for a Stacking Machine Console." - id = "stack_console" - build_path = /obj/item/circuitboard/machine/stacking_unit_console - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/stacking_machine - name = "Machine Design (Stacking Machine)" - desc = "The circuit board for a Stacking Machine." - id = "stack_machine" - build_path = /obj/item/circuitboard/machine/stacking_machine - category = list ("Misc. Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/board/ore_silo - name = "Machine Design (Ore Silo)" - desc = "The circuit board for an ore silo." - id = "ore_silo" - build_path = /obj/item/circuitboard/machine/ore_silo - category = list ("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_CARGO diff --git a/code/modules/research/designs/machine_desings/machine_designs_all_misc.dm b/code/modules/research/designs/machine_desings/machine_designs_all_misc.dm new file mode 100644 index 0000000000..31723cde07 --- /dev/null +++ b/code/modules/research/designs/machine_desings/machine_designs_all_misc.dm @@ -0,0 +1,100 @@ +//////////////////////////////////////// +//////////////MISC Boards/////////////// +//////////////////////////////////////// + +/datum/design/board/recycler + name = "Machine Design (Recycler Board)" + desc = "The circuit board for a recycler." + id = "recycler" + build_path = /obj/item/circuitboard/machine/recycler + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/holopad + name = "Machine Design (AI Holopad Board)" + desc = "The circuit board for a holopad." + id = "holopad" + build_path = /obj/item/circuitboard/machine/holopad + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/autolathe + name = "Machine Design (Autolathe Board)" + desc = "The circuit board for an autolathe." + id = "autolathe" + build_path = /obj/item/circuitboard/machine/autolathe + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/recharger + name = "Machine Design (Weapon Recharger Board)" + desc = "The circuit board for a Weapon Recharger." + id = "recharger" + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) + build_path = /obj/item/circuitboard/machine/recharger + category = list("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/vendor + name = "Machine Design (Vendor Board)" + desc = "The circuit board for a Vendor." + id = "vendor" + build_path = /obj/item/circuitboard/machine/vendor + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/donksofttoyvendor + name = "Machine Design (Donksoft Toy Vendor Board)" + desc = "The circuit board for a Donksoft Toy Vendor." + id = "donksofttoyvendor" + build_path = /obj/item/circuitboard/machine/vending/donksofttoyvendor + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/space_heater + name = "Machine Design (Space Heater Board)" + desc = "The circuit board for a space heater." + id = "space_heater" + build_path = /obj/item/circuitboard/machine/space_heater + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/reagentgrinder + name = "Machine Design (All-In-One Grinder)" + desc = "The circuit board for an All-In-One Grinder." + id = "reagentgrinder" + build_path = /obj/item/circuitboard/machine/reagentgrinder + category = list ("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/microwave + name = "Machine Design (Microwave Board)" + desc = "The circuit board for a microwave." + id = "microwave" + build_path = /obj/item/circuitboard/machine/microwave + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/smartfridge + name = "Machine Design (Smartfridge Board)" + desc = "The circuit board for a smartfridge." + id = "smartfridge" + build_path = /obj/item/circuitboard/machine/smartfridge + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/cell_charger + name = "Machine Design (Cell Charger Board)" + desc = "The circuit board for a cell charger." + id = "cell_charger" + build_path = /obj/item/circuitboard/machine/cell_charger + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ALL + +/datum/design/board/vr_sleeper + name = "Machine Design (VR Sleeper Board)" + desc = "The circuit board for a VR sleeper." + id = "vr_sleeper" + build_path = /obj/item/circuitboard/machine/vr_sleeper + departmental_flags = DEPARTMENTAL_FLAG_ALL + category = list ("Medical Machinery") diff --git a/code/modules/research/designs/machine_desings/machine_designs_cargo.dm b/code/modules/research/designs/machine_desings/machine_designs_cargo.dm new file mode 100644 index 0000000000..a6cc2271c8 --- /dev/null +++ b/code/modules/research/designs/machine_desings/machine_designs_cargo.dm @@ -0,0 +1,42 @@ +/////////////////// +///CARGO Boards// +/////////////////// +/datum/design/board/ore_silo + name = "Machine Design (Ore Silo)" + desc = "The circuit board for an ore silo." + id = "ore_silo" + build_path = /obj/item/circuitboard/machine/ore_silo + category = list ("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/board/mining_equipment_vendor + name = "Machine Design (Mining Rewards Vender Board)" + desc = "The circuit board for a Mining Rewards Vender." + id = "mining_equipment_vendor" + build_path = /obj/item/circuitboard/machine/mining_equipment_vendor + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/stacking_unit_console + name = "Machine Design (Stacking Machine Console)" + desc = "The circuit board for a Stacking Machine Console." + id = "stack_console" + build_path = /obj/item/circuitboard/machine/stacking_unit_console + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/stacking_machine + name = "Machine Design (Stacking Machine)" + desc = "The circuit board for a Stacking Machine." + id = "stack_machine" + build_path = /obj/item/circuitboard/machine/stacking_machine + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/ore_redemption + name = "Machine Design (Ore Redemption Board)" + desc = "The circuit board for an Ore Redemption machine." + id = "ore_redemption" + build_path = /obj/item/circuitboard/machine/ore_redemption + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING \ No newline at end of file diff --git a/code/modules/research/designs/machine_desings/machine_designs_engi.dm b/code/modules/research/designs/machine_desings/machine_designs_engi.dm new file mode 100644 index 0000000000..eb9e00be05 --- /dev/null +++ b/code/modules/research/designs/machine_desings/machine_designs_engi.dm @@ -0,0 +1,106 @@ +/////////////////// +///ENGINE Boards/// +/////////////////// +/datum/design/board/smes + name = "Machine Design (SMES Board)" + desc = "The circuit board for a SMES." + id = "smes" + build_path = /obj/item/circuitboard/machine/smes + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/circulator + name = "Machine Design (Circulator Board)" + desc = "The circuit board for a circulator." + id = "circulator" + build_path = /obj/item/circuitboard/machine/circulator + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/teg + name = "Machine Design (TEG Board)" + desc = "The circuit board for a TEG." + id = "teg" + build_path = /obj/item/circuitboard/machine/generator + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/announcement_system + name = "Machine Design (Automated Announcement System Board)" + desc = "The circuit board for an automated announcement system." + id = "automated_announcement" + build_path = /obj/item/circuitboard/machine/announcement_system + category = list("Subspace Telecomms") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/turbine_computer + name = "Computer Design (Power Turbine Console Board)" + desc = "The circuit board for a power turbine console." + id = "power_turbine_console" + build_path = /obj/item/circuitboard/computer/turbine_computer + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/emitter + name = "Machine Design (Emitter Board)" + desc = "The circuit board for an emitter." + id = "emitter" + build_path = /obj/item/circuitboard/machine/emitter + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/power_compressor + name = "Machine Design (Power Compressor Board)" + desc = "The circuit board for a power compressor." + id = "power_compressor" + build_path = /obj/item/circuitboard/machine/power_compressor + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/power_turbine + name = "Machine Design (Power Turbine Board)" + desc = "The circuit board for a power turbine." + id = "power_turbine" + build_path = /obj/item/circuitboard/machine/power_turbine + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/tesla_coil + name = "Machine Design (Tesla Coil Board)" + desc = "The circuit board for a tesla coil." + id = "tesla_coil" + build_path = /obj/item/circuitboard/machine/tesla_coil + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/rad_collector + name = "Machine Design (Radiation Collector Board)" + desc = "The circuit board for a radiation collector array." + id = "rad_collector" + build_path = /obj/item/circuitboard/machine/rad_collector + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/grounding_rod + name = "Machine Design (Grounding Rod Board)" + desc = "The circuit board for a grounding rod." + id = "grounding_rod" + build_path = /obj/item/circuitboard/machine/grounding_rod + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/ntnet_relay + name = "Machine Design (NTNet Relay Board)" + desc = "The circuit board for a wireless network relay." + id = "ntnet_relay" + build_path = /obj/item/circuitboard/machine/ntnet_relay + category = list("Subspace Telecomms") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/thermomachine + name = "Machine Design (Freezer/Heater Board)" + desc = "The circuit board for a freezer/heater." + id = "thermomachine" + build_path = /obj/item/circuitboard/machine/thermomachine + category = list ("Engineering Machinery") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE \ No newline at end of file diff --git a/code/modules/research/designs/machine_desings/machine_designs_medical.dm b/code/modules/research/designs/machine_desings/machine_designs_medical.dm new file mode 100644 index 0000000000..65b3d74f71 --- /dev/null +++ b/code/modules/research/designs/machine_desings/machine_designs_medical.dm @@ -0,0 +1,91 @@ +/////////////////// +///MEDICAL Boards// +/////////////////// + +/datum/design/board/limbgrower + name = "Machine Design (Limb Grower Board)" + desc = "The circuit board for a limb grower." + id = "limbgrower" + build_path = /obj/item/circuitboard/machine/limbgrower + category = list("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/board/harvester + name = "Machine Design (Organ Harvester Board)" + desc = "The circuit board for an organ harvester." + id = "harvester" + build_path = /obj/item/circuitboard/machine/harvester + category = list("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/board/sleeper + name = "Machine Design (Sleeper Board)" + desc = "The circuit board for a sleeper." + id = "sleeper" + build_path = /obj/item/circuitboard/machine/sleeper + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + category = list ("Medical Machinery") + +/datum/design/board/cryotube + name = "Machine Design (Cryotube Board)" + desc = "The circuit board for a cryotube." + id = "cryotube" + build_path = /obj/item/circuitboard/machine/cryo_tube + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + category = list ("Medical Machinery") + +/datum/design/board/chem_dispenser + name = "Machine Design (Portable Chem Dispenser Board)" + desc = "The circuit board for a portable chem dispenser." + id = "chem_dispenser" + build_path = /obj/item/circuitboard/machine/chem_dispenser + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + category = list ("Medical Machinery") + +/datum/design/board/chem_master + name = "Machine Design (Chem Master Board)" + desc = "The circuit board for a Chem Master 3000." + id = "chem_master" + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + build_path = /obj/item/circuitboard/machine/chem_master + category = list ("Medical Machinery") + +/datum/design/board/chem_heater + name = "Machine Design (Chemical Heater Board)" + desc = "The circuit board for a chemical heater." + id = "chem_heater" + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + build_path = /obj/item/circuitboard/machine/chem_heater + category = list ("Medical Machinery") + +/datum/design/board/smoke_machine + name = "Machine Design (Smoke Machine)" + desc = "The circuit board for a smoke machine." + id = "smoke_machine" + build_path = /obj/item/circuitboard/machine/smoke_machine + category = list ("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/clonecontrol + name = "Computer Design (Cloning Machine Console)" + desc = "Allows for the construction of circuit boards used to build a new Cloning Machine console." + id = "clonecontrol" + build_path = /obj/item/circuitboard/computer/cloning + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + category = list("Medical Machinery") + +/datum/design/board/clonepod + name = "Machine Design (Clone Pod)" + desc = "Allows for the construction of circuit boards used to build a Cloning Pod." + id = "clonepod" + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + build_path = /obj/item/circuitboard/machine/clonepod + category = list("Medical Machinery") + +/datum/design/board/clonescanner + name = "Machine Design (Cloning Scanner)" + desc = "Allows for the construction of circuit boards used to build a Cloning Scanner." + id = "clonescanner" + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + build_path = /obj/item/circuitboard/machine/clonescanner + category = list("Medical Machinery") diff --git a/code/modules/research/designs/machine_desings/machine_designs_sci.dm b/code/modules/research/designs/machine_desings/machine_designs_sci.dm new file mode 100644 index 0000000000..183977b455 --- /dev/null +++ b/code/modules/research/designs/machine_desings/machine_designs_sci.dm @@ -0,0 +1,155 @@ +/////////////////// +///SCI Boards////// +/////////////////// + +/datum/design/board/teleport_station + name = "Machine Design (Teleportation Station Board)" + desc = "The circuit board for a teleportation station." + id = "tele_station" + build_path = /obj/item/circuitboard/machine/teleporter_station + category = list ("Teleportation Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/teleport_hub + name = "Machine Design (Teleportation Hub Board)" + desc = "The circuit board for a teleportation hub." + id = "tele_hub" + build_path = /obj/item/circuitboard/machine/teleporter_hub + category = list ("Teleportation Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/quantumpad + name = "Machine Design (Quantum Pad Board)" + desc = "The circuit board for a quantum telepad." + id = "quantumpad" + build_path = /obj/item/circuitboard/machine/quantumpad + category = list ("Teleportation Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/launchpad + name = "Machine Design (Bluespace Launchpad Board)" + desc = "The circuit board for a bluespace Launchpad." + id = "launchpad" + build_path = /obj/item/circuitboard/machine/launchpad + category = list ("Teleportation Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/launchpad_console + name = "Machine Design (Bluespace Launchpad Console Board)" + desc = "The circuit board for a bluespace launchpad Console." + id = "launchpad_console" + build_path = /obj/item/circuitboard/computer/launchpad_console + category = list ("Teleportation Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/teleconsole + name = "Computer Design (Teleporter Console)" + desc = "Allows for the construction of circuit boards used to build a teleporter control console." + id = "teleconsole" + build_path = /obj/item/circuitboard/computer/teleporter + category = list("Teleportation Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/destructive_analyzer + name = "Machine Design (Destructive Analyzer Board)" + desc = "The circuit board for a destructive analyzer." + id = "destructive_analyzer" + build_path = /obj/item/circuitboard/machine/destructive_analyzer + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/experimentor + name = "Machine Design (E.X.P.E.R.I-MENTOR Board)" + desc = "The circuit board for an E.X.P.E.R.I-MENTOR." + id = "experimentor" + build_path = /obj/item/circuitboard/machine/experimentor + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/protolathe + name = "Machine Design (Protolathe Board)" + desc = "The circuit board for a protolathe." + id = "protolathe" + build_path = /obj/item/circuitboard/machine/protolathe + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/rdservercontrol + name = "Computer Design (R&D Server Control Console Board)" + desc = "The circuit board for an R&D Server Control Console." + id = "rdservercontrol" + build_path = /obj/item/circuitboard/computer/rdservercontrol + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/rdserver + name = "Machine Design (R&D Server Board)" + desc = "The circuit board for an R&D Server." + id = "rdserver" + build_path = /obj/item/circuitboard/machine/rdserver + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/mechfab + name = "Machine Design (Exosuit Fabricator Board)" + desc = "The circuit board for an Exosuit Fabricator." + id = "mechfab" + build_path = /obj/item/circuitboard/machine/mechfab + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/cyborgrecharger + name = "Machine Design (Cyborg Recharger Board)" + desc = "The circuit board for a Cyborg Recharger." + id = "cyborgrecharger" + build_path = /obj/item/circuitboard/machine/cyborgrecharger + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/mech_recharger + name = "Machine Design (Mechbay Recharger Board)" + desc = "The circuit board for a Mechbay Recharger." + id = "mech_recharger" + build_path = /obj/item/circuitboard/machine/mech_recharger + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/nanite_chamber + name = "Machine Design (Nanite Chamber Board)" + desc = "The circuit board for a Nanite Chamber." + id = "nanite_chamber" + build_path = /obj/item/circuitboard/machine/nanite_chamber + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/public_nanite_chamber + name = "Machine Design (Public Nanite Chamber Board)" + desc = "The circuit board for a Public Nanite Chamber." + id = "public_nanite_chamber" + build_path = /obj/item/circuitboard/machine/public_nanite_chamber + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/nanite_programmer + name = "Machine Design (Nanite Programmer Board)" + desc = "The circuit board for a Nanite Programmer." + id = "nanite_programmer" + build_path = /obj/item/circuitboard/machine/nanite_programmer + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/nanite_program_hub + name = "Machine Design (Nanite Program Hub Board)" + desc = "The circuit board for a Nanite Program Hub." + id = "nanite_program_hub" + build_path = /obj/item/circuitboard/machine/nanite_program_hub + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/circuit_imprinter + name = "Machine Design (Circuit Imprinter Board)" + desc = "The circuit board for a circuit imprinter." + id = "circuit_imprinter" + build_path = /obj/item/circuitboard/machine/circuit_imprinter + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING diff --git a/code/modules/research/designs/machine_desings/machine_designs_service.dm b/code/modules/research/designs/machine_desings/machine_designs_service.dm new file mode 100644 index 0000000000..895ad032ba --- /dev/null +++ b/code/modules/research/designs/machine_desings/machine_designs_service.dm @@ -0,0 +1,90 @@ +/////////////////// +///CIV Boards/// +/////////////////// +/datum/design/board/gibber + name = "Machine Design (Gibber Board)" + desc = "The circuit board for a gibber." + id = "gibber" + build_path = /obj/item/circuitboard/machine/gibber + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/board/seed_extractor + name = "Machine Design (Seed Extractor Board)" + desc = "The circuit board for a seed extractor." + id = "seed_extractor" + build_path = /obj/item/circuitboard/machine/seed_extractor + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/board/soda_dispenser + name = "Machine Design (Portable Soda Dispenser Board)" + desc = "The circuit board for a portable soda dispenser." + id = "soda_dispenser" + build_path = /obj/item/circuitboard/machine/chem_dispenser/drinks + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + category = list ("Misc. Machinery") + +/datum/design/board/beer_dispenser + name = "Machine Design (Portable Booze Dispenser Board)" + desc = "The circuit board for a portable booze dispenser." + id = "beer_dispenser" + build_path = /obj/item/circuitboard/machine/chem_dispenser/drinks/beer + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + category = list ("Misc. Machinery") + +/datum/design/board/plantgenes + name = "Machine Design (Plant DNA Manipulator Board)" + desc = "The circuit board for a plant DNA manipulator." + id = "plantgenes" + build_path = /obj/item/circuitboard/machine/plantgenes + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/board/deepfryer + name = "Machine Design (Deep Fryer)" + desc = "The circuit board for a Deep Fryer." + id = "deepfryer" + build_path = /obj/item/circuitboard/machine/deep_fryer + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/board/dish_drive + name = "Machine Design (Dish Drive)" + desc = "The circuit board for a dish drive." + id = "dish_drive" + build_path = /obj/item/circuitboard/machine/dish_drive + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/board/biogenerator + name = "Machine Design (Biogenerator Board)" + desc = "The circuit board for a biogenerator." + id = "biogenerator" + build_path = /obj/item/circuitboard/machine/biogenerator + category = list ("Hydroponics Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/board/hydroponics + name = "Machine Design (Hydroponics Tray Board)" + desc = "The circuit board for a hydroponics tray." + id = "hydro_tray" + build_path = /obj/item/circuitboard/machine/hydroponics + category = list ("Hydroponics Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/board/monkey_recycler + name = "Machine Design (Monkey Recycler Board)" + desc = "The circuit board for a monkey recycler." + id = "monkey_recycler" + build_path = /obj/item/circuitboard/machine/monkey_recycler + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/processor + name = "Machine Design (Food/Slime Processor Board)" + desc = "The circuit board for a processing unit. Screwdriver the circuit to switch between food (default) or slime processing." + id = "processor" + build_path = /obj/item/circuitboard/machine/processor + category = list ("Misc. Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE | DEPARTMENTAL_FLAG_SCIENCE diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 9005b1cd30..7224d0e4e8 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -44,16 +44,6 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL -/datum/design/medicalkit - name = "Empty Medkit" - desc = "A plastic medical kit for storging medical items." - id = "medicalkit" - build_type = PROTOLATHE - materials = list(MAT_PLASTIC = 5000) - build_path = /obj/item/storage/firstaid //So we dont spawn medical items in it - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE - /datum/design/xlarge_beaker name = "X-large Beaker" id = "xlarge_beaker" @@ -82,16 +72,6 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE -/datum/design/cloning_disk - name = "Cloning Data Disk" - desc = "Produce additional disks for storing genetic data." - id = "cloning_disk" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100, MAT_SILVER=50) - build_path = /obj/item/disk/data - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE - /datum/design/noreactsyringe name = "Cryo Syringe" desc = "An advanced syringe that stops reagents inside from reacting. It can hold up to 20 units." @@ -162,6 +142,16 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/medicalkit + name = "Empty Medkit" + desc = "A plastic medical kit for storging medical items." + id = "medicalkit" + build_type = PROTOLATHE + materials = list(MAT_PLASTIC = 5000) + build_path = /obj/item/storage/firstaid //So we dont spawn medical items in it + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + /datum/design/blood_bag name = "Empty Blood Bag" desc = "A small sterilized plastic bag for blood." @@ -172,6 +162,16 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/cloning_disk + name = "Cloning Data Disk" + desc = "Produce additional disks for storing genetic data." + id = "cloning_disk" + build_type = PROTOLATHE + materials = list(MAT_METAL = 300, MAT_GLASS = 100, MAT_SILVER=50) + build_path = /obj/item/disk/data + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + //////////////////////////////////////// //////////Defibrillator Tech//////////// //////////////////////////////////////// @@ -239,72 +239,6 @@ category = list("Misc") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -///////////////////////////////////////// -//////////Alien Surgery Tools//////////// -///////////////////////////////////////// - -/datum/design/alienscalpel - name = "Alien Scalpel" - desc = "An advanced scalpel obtained through Abductor technology." - id = "alien_scalpel" - build_path = /obj/item/scalpel/alien - build_type = PROTOLATHE - materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/alienhemostat - name = "Alien Hemostat" - desc = "An advanced hemostat obtained through Abductor technology." - id = "alien_hemostat" - build_path = /obj/item/hemostat/alien - build_type = PROTOLATHE - materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/alienretractor - name = "Alien Retractor" - desc = "An advanced retractor obtained through Abductor technology." - id = "alien_retractor" - build_path = /obj/item/retractor/alien - build_type = PROTOLATHE - materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/aliensaw - name = "Alien Circular Saw" - desc = "An advanced surgical saw obtained through Abductor technology." - id = "alien_saw" - build_path = /obj/item/circular_saw/alien - build_type = PROTOLATHE - materials = list(MAT_METAL = 10000, MAT_SILVER = 2500, MAT_PLASMA = 1000, MAT_TITANIUM = 1500) - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/aliendrill - name = "Alien Drill" - desc = "An advanced drill obtained through Abductor technology." - id = "alien_drill" - build_path = /obj/item/surgicaldrill/alien - build_type = PROTOLATHE - materials = list(MAT_METAL = 10000, MAT_SILVER = 2500, MAT_PLASMA = 1000, MAT_TITANIUM = 1500) - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - -/datum/design/aliencautery - name = "Alien Cautery" - desc = "An advanced cautery obtained through Abductor technology." - id = "alien_cautery" - build_path = /obj/item/cautery/alien - build_type = PROTOLATHE - materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - - ///////////////////////////////////////// //////////Cybernetic Implants//////////// ///////////////////////////////////////// @@ -654,9 +588,74 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE +///////////////////////////////////////// +//////////Alien Surgery Tools//////////// +///////////////////////////////////////// + +/datum/design/alienscalpel + name = "Alien Scalpel" + desc = "An advanced scalpel obtained through Abductor technology." + id = "alien_scalpel" + build_path = /obj/item/scalpel/alien + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/alienhemostat + name = "Alien Hemostat" + desc = "An advanced hemostat obtained through Abductor technology." + id = "alien_hemostat" + build_path = /obj/item/hemostat/alien + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/alienretractor + name = "Alien Retractor" + desc = "An advanced retractor obtained through Abductor technology." + id = "alien_retractor" + build_path = /obj/item/retractor/alien + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/aliensaw + name = "Alien Circular Saw" + desc = "An advanced surgical saw obtained through Abductor technology." + id = "alien_saw" + build_path = /obj/item/circular_saw/alien + build_type = PROTOLATHE + materials = list(MAT_METAL = 10000, MAT_SILVER = 2500, MAT_PLASMA = 1000, MAT_TITANIUM = 1500) + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/aliendrill + name = "Alien Drill" + desc = "An advanced drill obtained through Abductor technology." + id = "alien_drill" + build_path = /obj/item/surgicaldrill/alien + build_type = PROTOLATHE + materials = list(MAT_METAL = 10000, MAT_SILVER = 2500, MAT_PLASMA = 1000, MAT_TITANIUM = 1500) + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + +/datum/design/aliencautery + name = "Alien Cautery" + desc = "An advanced cautery obtained through Abductor technology." + id = "alien_cautery" + build_path = /obj/item/cautery/alien + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + ///////////////////// ///Surgery Designs/// ///////////////////// + /datum/design/surgery name = "Surgery Design" desc = "what" diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index d567334998..0d699a1d67 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -94,39 +94,15 @@ category = list("Equipment") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE -///////////////////////////////////////// -//////////////////Misc/////////////////// -///////////////////////////////////////// - -/datum/design/welding_mask - name = "Welding Gas Mask" - desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." - id = "weldingmask" +/datum/design/sci_goggles + name = "Science Goggles" + desc = "Goggles fitted with a portable analyzer capable of determining the research worth of an item or components of a machine." + id = "scigoggles" build_type = PROTOLATHE - materials = list(MAT_METAL = 3000, MAT_GLASS = 1000) - build_path = /obj/item/clothing/mask/gas/welding + materials = list(MAT_METAL = 500, MAT_GLASS = 500) + build_path = /obj/item/clothing/glasses/science category = list("Equipment") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/portaseeder - name = "Portable Seed Extractor" - desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant." - id = "portaseeder" - build_type = PROTOLATHE - materials = list(MAT_METAL = 1000, MAT_GLASS = 400) - build_path = /obj/item/storage/bag/plants/portaseeder - category = list("Equipment") - departmental_flags = DEPARTMENTAL_FLAG_SERVICE - -/datum/design/air_horn - name = "Air Horn" - desc = "Damn son, where'd you find this?" - id = "air_horn" - build_type = PROTOLATHE - materials = list(MAT_METAL = 4000, MAT_BANANIUM = 1000) - build_path = /obj/item/bikehorn/airhorn - category = list("Equipment") - departmental_flags = DEPARTMENTAL_FLAG_ALL //HONK! + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE /datum/design/mesons name = "Optical Meson Scanners" @@ -218,6 +194,40 @@ category = list("Equipment") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING +///////////////////////////////////////// +//////////////////Misc/////////////////// +///////////////////////////////////////// + +/datum/design/welding_mask + name = "Welding Gas Mask" + desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." + id = "weldingmask" + build_type = PROTOLATHE + materials = list(MAT_METAL = 3000, MAT_GLASS = 1000) + build_path = /obj/item/clothing/mask/gas/welding + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/portaseeder + name = "Portable Seed Extractor" + desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant." + id = "portaseeder" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 400) + build_path = /obj/item/storage/bag/plants/portaseeder + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_SERVICE + +/datum/design/air_horn + name = "Air Horn" + desc = "Damn son, where'd you find this?" + id = "air_horn" + build_type = PROTOLATHE + materials = list(MAT_METAL = 4000, MAT_BANANIUM = 1000) + build_path = /obj/item/bikehorn/airhorn + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_ALL //HONK! + /datum/design/magboots name = "Magnetic Boots" desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle." @@ -228,26 +238,6 @@ category = list("Equipment") departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING -/datum/design/forcefield_projector - name = "Forcefield Projector" - desc = "A device which can project temporary forcefields to seal off an area." - id = "forcefield_projector" - build_type = PROTOLATHE - materials = list(MAT_METAL = 2500, MAT_GLASS = 1000) - build_path = /obj/item/forcefield_projector - category = list("Equipment") - departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING - -/datum/design/sci_goggles - name = "Science Goggles" - desc = "Goggles fitted with a portable analyzer capable of determining the research worth of an item or components of a machine." - id = "scigoggles" - build_type = PROTOLATHE - materials = list(MAT_METAL = 500, MAT_GLASS = 500) - build_path = /obj/item/clothing/glasses/science - category = list("Equipment") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - /datum/design/diskplantgene name = "Plant Data Disk" desc = "A disk for storing plant genetic data." @@ -312,6 +302,10 @@ category = list("Equipment") departmental_flags = DEPARTMENTAL_FLAG_SERVICE +///////////////////////////////////////// +////////////Holosign Designs///////////// +///////////////////////////////////////// + /datum/design/holosign name = "Holographic Sign Projector" desc = "A holograpic projector used to project various warning signs." @@ -352,9 +346,19 @@ category = list("Equipment") departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING -///////////////////////////////////////// +/datum/design/forcefield_projector + name = "Forcefield Projector" + desc = "A device which can project temporary forcefields to seal off an area." + id = "forcefield_projector" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2500, MAT_GLASS = 1000) + build_path = /obj/item/forcefield_projector + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + +/////////////////////////////// ////////////Tools////////////// -///////////////////////////////////////// +/////////////////////////////// /datum/design/exwelder name = "Experimental Welding Tool" @@ -457,7 +461,7 @@ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING ///////////////////////////////////////// -////////////Armour////////////// +////////////Armour/////////////////////// ///////////////////////////////////////// /datum/design/reactive_armour diff --git a/code/modules/research/designs/smelting_designs.dm b/code/modules/research/designs/smelting_designs.dm index f2f21396d2..8ddf975c54 100644 --- a/code/modules/research/designs/smelting_designs.dm +++ b/code/modules/research/designs/smelting_designs.dm @@ -10,7 +10,6 @@ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING maxstack = 50 - /datum/design/plastitanium_alloy name = "Plasma + Titanium alloy" id = "plastitanium" diff --git a/code/modules/research/xenobiology/crossbreeding/chilling.dm b/code/modules/research/xenobiology/crossbreeding/chilling.dm index 25dbaa461f..cfbbc8e4a1 100644 --- a/code/modules/research/xenobiology/crossbreeding/chilling.dm +++ b/code/modules/research/xenobiology/crossbreeding/chilling.dm @@ -101,10 +101,9 @@ Chilling extracts: for(var/turf/open/T in A) var/datum/gas_mixture/G = T.air if(istype(G)) - G.assert_gas(/datum/gas/plasma) - G.gases[/datum/gas/plasma][MOLES] = 0 + G.gases[/datum/gas/plasma] = 0 filtered = TRUE - G.garbage_collect() + GAS_GARBAGE_COLLECT(G.gases) T.air_update_turf() if(filtered) user.visible_message("Cracks spread throughout [src], and some air is sucked in!") diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index b8d19a7018..28ea849782 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -16,6 +16,17 @@ /obj/item/seeds/sunflower/moonflower = 8 ) +/obj/item/disk/design_disk/plant_disk + name = "Plant Disk Blueprints" + desc = "A disk to be uploaded into the autolathen for more plant disks." + icon_state = "datadisk1" + max_blueprints = 1 + +/obj/item/disk/design_disk/golem_shell/Initialize() + . = ..() + var/datum/design/diskplantgene/P = new + blueprints[1] = P + //Free Golems /obj/item/disk/design_disk/golem_shell diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 9af4194049..d41fd6a79b 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -516,10 +516,10 @@ name = "emergency space suits" desc = "A wall mounted safe containing space suits. Will only open in emergencies." anchored = TRUE - density = FALSE icon = 'icons/obj/storage.dmi' icon_state = "safe" - var/unlocked = FALSE + integrity_failure = 100 + component_type = /datum/component/storage/concrete/emergency /obj/item/storage/pod/PopulateContents() new /obj/item/clothing/head/helmet/space/orange(src) @@ -535,26 +535,6 @@ new /obj/item/survivalcapsule(src) new /obj/item/storage/toolbox/emergency(src) -/obj/item/storage/pod/attackby(obj/item/W, mob/user, params) - if (can_interact(user)) - return ..() - -/obj/item/storage/pod/attack_hand(mob/user) - if (can_interact(user)) - SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SHOW, user) - return TRUE - -/obj/item/storage/pod/MouseDrop(over_object, src_location, over_location) - if(can_interact(usr)) - return ..() - -/obj/item/storage/pod/can_interact(mob/user) - if(!..()) - return FALSE - if(GLOB.security_level == SEC_LEVEL_RED || GLOB.security_level == SEC_LEVEL_DELTA || unlocked) - return TRUE - to_chat(user, "The storage unit will only unlock during a Red or Delta security alert.") - /obj/docking_port/mobile/emergency/backup name = "backup shuttle" id = "backup" diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 9942446868..a6904c28cc 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -290,7 +290,8 @@ All ShuttleMove procs go here // ignores the movement of the shuttle from the staging area on CentCom to // the station as it is loaded in. if (oldT && !is_centcom_level(oldT.z)) - unlocked = TRUE + GET_COMPONENT(STR, /datum/component/storage/concrete/emergency) + STR?.unlock_me() /************************************Mob move procs************************************/ diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm index 35f9a8f50a..73a428af71 100644 --- a/code/modules/spells/spell_types/aimed.dm +++ b/code/modules/spells/spell_types/aimed.dm @@ -74,6 +74,8 @@ /obj/effect/proc_holder/spell/aimed/proc/fire_projectile(mob/living/user, atom/target) current_amount-- + if(!projectile_type) + return for(var/i in 1 to projectiles_per_fire) var/obj/item/projectile/P = new projectile_type(user.loc) P.firer = user diff --git a/code/modules/spells/spell_types/godhand.dm b/code/modules/spells/spell_types/godhand.dm index 919d87b4c7..06d1fc3dc5 100644 --- a/code/modules/spells/spell_types/godhand.dm +++ b/code/modules/spells/spell_types/godhand.dm @@ -94,3 +94,87 @@ M.Stun(40) M.petrify() return ..() + + +/obj/item/melee/touch_attack/megahonk + name = "\improper honkmother's blessing" + desc = "You've got a feeling they won't be laughing after this one. Honk honk." + catchphrase = "HONKDOOOOUKEN!" + on_use_sound = 'sound/items/airhorn.ogg' + icon = 'icons/mecha/mecha_equipment.dmi' + icon_state = "mecha_honker" + +/obj/item/melee/touch_attack/megahonk/afterattack(atom/target, mob/living/carbon/user, proximity) + if(!proximity || !iscarbon(target) || !iscarbon(user) || user.handcuffed) + return + user.say(catchphrase, forced = "spell") + playsound(get_turf(target), on_use_sound,100,1) + for(var/mob/living/carbon/M in (hearers(1, target) - user)) //3x3 around the target, not affecting the user + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(istype(H.ears, /obj/item/clothing/ears/earmuffs)) + continue + var/mul = (M==target ? 1 : 0.5) + to_chat(M, "HONK") + M.SetSleeping(0) + M.stuttering += 20*mul + M.adjustEarDamage(0, 30*mul) + M.Knockdown(60*mul) + if(prob(40)) + M.Knockdown(200*mul) + else + M.Jitter(500*mul) + + charges-- + if(charges <= 0) + qdel(src) + +/obj/item/melee/touch_attack/megahonk/attack_self(mob/user) + . = ..() + to_chat(user, "\The [src] disappears, to honk another day.") + qdel(src) + +/obj/item/melee/touch_attack/bspie + name = "\improper bluespace pie" + desc = "A thing you can barely comprehend as you hold it in your hand. You're fairly sure you could fit an entire body inside." + on_use_sound = 'sound/magic/demon_consume.ogg' + icon = 'icons/obj/food/piecake.dmi' + icon_state = "frostypie" + color = "#000077" + +/obj/item/melee/touch_attack/bspie/attack_self(mob/user) + . = ..() + to_chat(user, "You smear \the [src] on your chest! ") + qdel(src) + +/obj/item/melee/touch_attack/bspie/afterattack(atom/target, mob/living/carbon/user, proximity) + if(!proximity || !iscarbon(target) || !iscarbon(user) || user.handcuffed) + return + if(target == user) + to_chat(user, "You smear \the [src] on your chest!") + qdel(src) + return + var/mob/living/carbon/M = target + + user.visible_message("[user] is trying to stuff [M]\s body into \the [src]!") + if(do_mob(user, M, 250)) + var/name = M.real_name + var/obj/item/reagent_containers/food/snacks/pie/cream/body/pie = new(get_turf(M)) + pie.name = "\improper [name] [pie.name]" + + playsound(get_turf(target), on_use_sound, 50, 1) + + /* + var/obj/item/bodypart/head = M.get_bodypart("head") + if(head) + head.drop_limb() + head.throw_at(get_turf(head), 1, 1) + qdel(M) + */ + M.forceMove(pie) + + + charges-- + + if(charges <= 0) + qdel(src) diff --git a/code/modules/spells/spell_types/taeclowndo.dm b/code/modules/spells/spell_types/taeclowndo.dm new file mode 100644 index 0000000000..d2b0782b07 --- /dev/null +++ b/code/modules/spells/spell_types/taeclowndo.dm @@ -0,0 +1,86 @@ +/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pie + name = "Summon Creampie" + desc = "A clown's weapon of choice. Use this to summon a fresh pie, just waiting to acquaintain itself with someone's face." + invocation_type = "none" + include_user = 1 + range = -1 + clothes_req = 0 + item_type = /obj/item/reagent_containers/food/snacks/pie/cream + + charge_max = 30 + cooldown_min = 30 + action_icon = 'icons/obj/food/piecake.dmi' + action_icon_state = "pie" + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/obj/effect/proc_holder/spell/aimed/banana_peel + name = "Conjure Banana Peel" + desc = "Make a banana peel appear out of thin air right under someone's feet!" + charge_type = "recharge" + charge_max = 100 + cooldown_min = 100 + clothes_req = 0 + invocation_type = "none" + range = 7 + selection_type = "view" + projectile_type = null + + active_msg = "You focus, your mind reaching to the clown dimension, ready to make a peel matrialize wherever you want!" + deactive_msg = "You relax, the peel remaining right in the \"thin air\" it would appear out of." + action_icon = 'icons/obj/hydroponics/harvest.dmi' + base_icon_state = "banana_peel" + action_icon_state = "banana" + + +/obj/effect/proc_holder/spell/aimed/banana_peel/cast(list/targets, mob/user = usr) + var/target = get_turf(targets[1]) + + if(get_dist(user,target)>range) + to_chat(user, "\The [target] is too far away!") + return + + . = ..() + new /obj/item/grown/bananapeel(target) + +/obj/effect/proc_holder/spell/aimed/banana_peel/update_icon() + if(!action) + return + if(active) + action.button_icon_state = base_icon_state + else + action.button_icon_state = action_icon_state + + action.UpdateButtonIcon() + return +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/obj/effect/proc_holder/spell/targeted/touch/megahonk + name = "Mega HoNk" + desc = "This spell channels your inner clown powers, concentrating them into one massive HONK." + hand_path = /obj/item/melee/touch_attack/megahonk + + charge_max = 100 + clothes_req = 0 + cooldown_min = 100 + + action_icon = 'icons/mecha/mecha_equipment.dmi' + action_icon_state = "mecha_honker" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/obj/effect/proc_holder/spell/targeted/touch/bspie + name = "Bluespace Banana Pie" + desc = "An entire body would fit in there!" + hand_path = /obj/item/melee/touch_attack/bspie + + charge_max = 450 + clothes_req = 0 + cooldown_min = 450 + + action_icon = 'icons/obj/food/piecake.dmi' + action_icon_state = "frostypie" + + + + diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 71b659b73f..230347fae5 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -48,7 +48,7 @@ var/body_markings = "" //for bodypart markings var/body_markings_icon = 'modular_citadel/icons/mob/mam_markings.dmi' var/list/markings_color = list() - var/auxmarking + var/auxmarking = "" var/list/auxmarking_color = list() var/animal_origin = null //for nonhuman bodypart (e.g. monkey) @@ -261,7 +261,6 @@ icon = DEFAULT_BODYPART_ICON_ORGANIC else if(status == BODYPART_ROBOTIC) icon = DEFAULT_BODYPART_ICON_ROBOTIC - body_markings = null if(owner) owner.updatehealth() @@ -292,6 +291,7 @@ should_draw_greyscale = FALSE no_update = TRUE body_markings = "husk" // reeee + auxmarking = "husk" if(no_update) return @@ -336,17 +336,18 @@ if("mam_body_markings" in S.default_features) var/datum/sprite_accessory/Smark Smark = GLOB.mam_body_markings_list[H.dna.features["mam_body_markings"]] + body_markings_icon = Smark.icon if(H.dna.features.["mam_body_markings"] != "None") - body_markings_icon = Smark.icon body_markings = lowertext(H.dna.features.["mam_body_markings"]) - if(MATRIXED) - markings_color = list(colorlist) + auxmarking = lowertext(H.dna.features.["mam_body_markings"]) else body_markings = "plain" - markings_color = (H.dna.features.["mcolor"]) + auxmarking = "plain" + markings_color = list(colorlist) + else body_markings = null - markings_color = "" + auxmarking = null if(!dropping_limb && H.dna.check_mutation(HULK)) mutation_color = "00aa00" @@ -361,6 +362,7 @@ if(status == BODYPART_ROBOTIC) dmg_overlay_type = "robotic" body_markings = null + auxmarking = null if(dropping_limb) no_update = TRUE //when attached, the limb won't be affected by the appearance changes of its mob owner. @@ -394,7 +396,8 @@ . += image('icons/mob/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER, image_dir) if(burnstate) . += image('icons/mob/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_0[burnstate]", -DAMAGE_LAYER, image_dir) - if(body_markings && status != BODYPART_ROBOTIC) + + if(!isnull(body_markings) && status == BODYPART_ORGANIC) if(!use_digitigrade) if(BODY_ZONE_CHEST) . += image(body_markings_icon, "[body_markings]_[body_zone]_[icon_gender]", -MARKING_LAYER, image_dir) @@ -450,7 +453,7 @@ limb.icon_state = "[species_id]_[body_zone]" // Body markings - if(body_markings) + if(!isnull(body_markings)) if(species_id == "husk") marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[body_zone]", -MARKING_LAYER, image_dir) else if(species_id == "husk" && use_digitigrade) @@ -470,7 +473,7 @@ if(aux_zone) aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir) . += aux - if(body_markings) + if(!isnull(auxmarking)) if(species_id == "husk") auxmarking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[aux_zone]", -aux_layer, image_dir) else @@ -483,17 +486,18 @@ limb.icon_state = "[body_zone]_[icon_gender]" else limb.icon_state = "[body_zone]" + if(aux_zone) - aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir) + aux = image(limb.icon, "[aux_zone]", -aux_layer, image_dir) . += aux - if(body_markings) + if(!isnull(auxmarking)) if(species_id == "husk") auxmarking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[aux_zone]", -aux_layer, image_dir) else auxmarking = image(body_markings_icon, "[body_markings]_[aux_zone]", -aux_layer, image_dir) . += auxmarking - if(body_markings) + if(!isnull(body_markings)) if(species_id == "husk") marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[body_zone]", -MARKING_LAYER, image_dir) else if(species_id == "husk" && use_digitigrade) @@ -509,17 +513,16 @@ . += marking return - if(should_draw_greyscale) var/draw_color = mutation_color || species_color || (skin_tone && skintone2hex(skin_tone)) if(draw_color) limb.color = "#[draw_color]" if(aux_zone) aux.color = "#[draw_color]" - if(body_markings) + if(!isnull(auxmarking)) auxmarking.color = list(markings_color) - if(body_markings) + if(!isnull(body_markings)) if(species_id == "husk") marking.color = "#141414" else diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 1e22796b1b..9a010e881c 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -84,13 +84,11 @@ var/list/breath_gases = breath.gases - breath.assert_gases(/datum/gas/oxygen, /datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/bz, /datum/gas/nitrogen, /datum/gas/tritium, /datum/gas/nitryl, /datum/gas/pluoxium, /datum/gas/stimulum) - //Partial pressures in our breath - var/O2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen][MOLES])+(8*breath.get_breath_partial_pressure(breath_gases[/datum/gas/pluoxium][MOLES])) - var/N2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrogen][MOLES]) - var/Toxins_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma][MOLES]) - var/CO2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide][MOLES]) + var/O2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen])+(8*breath.get_breath_partial_pressure(breath_gases[/datum/gas/pluoxium])) + var/N2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrogen]) + var/Toxins_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma]) + var/CO2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide]) //-- OXY --// @@ -98,7 +96,7 @@ //Too much oxygen! //Yes, some species may not like it. if(safe_oxygen_max) if((O2_pp > safe_oxygen_max) && safe_oxygen_max == 0) //I guess plasma men technically need to have a check. - var/ratio = (breath_gases[/datum/gas/oxygen][MOLES]/safe_oxygen_max) * 10 + var/ratio = (breath_gases[/datum/gas/oxygen]/safe_oxygen_max) * 10 H.apply_damage_type(CLAMP(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type) H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy) @@ -121,18 +119,18 @@ //Too little oxygen! if(safe_oxygen_min) if(O2_pp < safe_oxygen_min) - gas_breathed = handle_too_little_breath(H, O2_pp, safe_oxygen_min, breath_gases[/datum/gas/oxygen][MOLES]) + gas_breathed = handle_too_little_breath(H, O2_pp, safe_oxygen_min, breath_gases[/datum/gas/oxygen]) H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) else H.failed_last_breath = FALSE if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) - gas_breathed = breath_gases[/datum/gas/oxygen][MOLES] + gas_breathed = breath_gases[/datum/gas/oxygen] H.clear_alert("not_enough_oxy") //Exhale - breath_gases[/datum/gas/oxygen][MOLES] -= gas_breathed - breath_gases[/datum/gas/carbon_dioxide][MOLES] += gas_breathed + breath_gases[/datum/gas/oxygen] -= gas_breathed + breath_gases[/datum/gas/carbon_dioxide] += gas_breathed gas_breathed = 0 //-- Nitrogen --// @@ -140,7 +138,7 @@ //Too much nitrogen! if(safe_nitro_max) if(N2_pp > safe_nitro_max) - var/ratio = (breath_gases[/datum/gas/nitrogen][MOLES]/safe_nitro_max) * 10 + var/ratio = (breath_gases[/datum/gas/nitrogen]/safe_nitro_max) * 10 H.apply_damage_type(CLAMP(ratio, nitro_breath_dam_min, nitro_breath_dam_max), nitro_damage_type) H.throw_alert("too_much_nitro", /obj/screen/alert/too_much_nitro) H.losebreath += 2 @@ -150,18 +148,18 @@ //Too little nitrogen! if(safe_nitro_min) if(N2_pp < safe_nitro_min) - gas_breathed = handle_too_little_breath(H, N2_pp, safe_nitro_min, breath_gases[/datum/gas/nitrogen][MOLES]) + gas_breathed = handle_too_little_breath(H, N2_pp, safe_nitro_min, breath_gases[/datum/gas/nitrogen]) H.throw_alert("nitro", /obj/screen/alert/not_enough_nitro) else H.failed_last_breath = FALSE if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) - gas_breathed = breath_gases[/datum/gas/nitrogen][MOLES] + gas_breathed = breath_gases[/datum/gas/nitrogen] H.clear_alert("nitro") //Exhale - breath_gases[/datum/gas/nitrogen][MOLES] -= gas_breathed - breath_gases[/datum/gas/carbon_dioxide][MOLES] += gas_breathed + breath_gases[/datum/gas/nitrogen] -= gas_breathed + breath_gases[/datum/gas/carbon_dioxide] += gas_breathed gas_breathed = 0 //-- CO2 --// @@ -187,18 +185,18 @@ //Too little CO2! if(safe_co2_min) if(CO2_pp < safe_co2_min) - gas_breathed = handle_too_little_breath(H, CO2_pp, safe_co2_min, breath_gases[/datum/gas/carbon_dioxide][MOLES]) + gas_breathed = handle_too_little_breath(H, CO2_pp, safe_co2_min, breath_gases[/datum/gas/carbon_dioxide]) H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2) else H.failed_last_breath = FALSE if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) - gas_breathed = breath_gases[/datum/gas/carbon_dioxide][MOLES] + gas_breathed = breath_gases[/datum/gas/carbon_dioxide] H.clear_alert("not_enough_co2") //Exhale - breath_gases[/datum/gas/carbon_dioxide][MOLES] -= gas_breathed - breath_gases[/datum/gas/oxygen][MOLES] += gas_breathed + breath_gases[/datum/gas/carbon_dioxide] -= gas_breathed + breath_gases[/datum/gas/oxygen] += gas_breathed gas_breathed = 0 @@ -207,7 +205,7 @@ //Too much toxins! if(safe_toxins_max) if(Toxins_pp > safe_toxins_max) - var/ratio = (breath_gases[/datum/gas/plasma][MOLES]/safe_toxins_max) * 10 + var/ratio = (breath_gases[/datum/gas/plasma]/safe_toxins_max) * 10 H.apply_damage_type(CLAMP(ratio, tox_breath_dam_min, tox_breath_dam_max), tox_damage_type) H.throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) else @@ -217,18 +215,18 @@ //Too little toxins! if(safe_toxins_min) if(Toxins_pp < safe_toxins_min) - gas_breathed = handle_too_little_breath(H, Toxins_pp, safe_toxins_min, breath_gases[/datum/gas/plasma][MOLES]) + gas_breathed = handle_too_little_breath(H, Toxins_pp, safe_toxins_min, breath_gases[/datum/gas/plasma]) H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) else H.failed_last_breath = FALSE if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) - gas_breathed = breath_gases[/datum/gas/plasma][MOLES] + gas_breathed = breath_gases[/datum/gas/plasma] H.clear_alert("not_enough_tox") //Exhale - breath_gases[/datum/gas/plasma][MOLES] -= gas_breathed - breath_gases[/datum/gas/carbon_dioxide][MOLES] += gas_breathed + breath_gases[/datum/gas/plasma] -= gas_breathed + breath_gases[/datum/gas/carbon_dioxide] += gas_breathed gas_breathed = 0 @@ -238,7 +236,7 @@ // N2O - var/SA_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrous_oxide][MOLES]) + var/SA_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrous_oxide]) if(SA_pp > SA_para_min) // Enough to make us stunned for a bit H.Unconscious(60) // 60 gives them one second to wake up and run away a bit! if(SA_pp > SA_sleep_min) // Enough to make us sleep as well @@ -249,7 +247,7 @@ // BZ - var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES]) + var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz]) if(bz_pp > BZ_trip_balls_min) H.hallucination += 10 H.reagents.add_reagent("bz_metabolites",5) @@ -262,14 +260,14 @@ // Tritium - var/trit_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/tritium][MOLES]) + var/trit_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/tritium]) if (trit_pp > 50) H.radiation += trit_pp/2 //If you're breathing in half an atmosphere of radioactive gas, you fucked up. else H.radiation += trit_pp/10 // Nitryl - var/nitryl_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitryl][MOLES]) + var/nitryl_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitryl]) if (prob(nitryl_pp)) to_chat(H, "Your mouth feels like it's burning!") if (nitryl_pp >40) @@ -280,22 +278,22 @@ H.silent = max(H.silent, 3) else H.adjustFireLoss(nitryl_pp/4) - gas_breathed = breath_gases[/datum/gas/nitryl][MOLES] + gas_breathed = breath_gases[/datum/gas/nitryl] if (gas_breathed > gas_stimulation_min) H.reagents.add_reagent("no2",1) - breath_gases[/datum/gas/nitryl][MOLES]-=gas_breathed + breath_gases[/datum/gas/nitryl]-=gas_breathed // Stimulum - gas_breathed = breath_gases[/datum/gas/stimulum][MOLES] + gas_breathed = breath_gases[/datum/gas/stimulum] if (gas_breathed > gas_stimulation_min) var/existing = H.reagents.get_reagent_amount("stimulum") H.reagents.add_reagent("stimulum",max(0, 1 - existing)) - breath_gases[/datum/gas/stimulum][MOLES]-=gas_breathed + breath_gases[/datum/gas/stimulum]-=gas_breathed // Miasma if (breath_gases[/datum/gas/miasma]) - var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES]) + var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma]) //Miasma sickness if(prob(0.5 * miasma_pp)) @@ -334,14 +332,14 @@ // Then again, this is a purely hypothetical scenario and hardly reachable owner.adjust_disgust(0.1 * miasma_pp) - breath_gases[/datum/gas/miasma][MOLES]-=gas_breathed + breath_gases[/datum/gas/miasma]-=gas_breathed // Clear out moods when no miasma at all else SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell") handle_breath_temperature(breath, H) - breath.garbage_collect() + GAS_GARBAGE_COLLECT(breath.gases) return TRUE diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index d5b2d16e67..0e672de225 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -17,6 +17,7 @@ /datum/language/beachbum, /datum/language/ratvar, /datum/language/aphasia, + /datum/language/slime, )) /obj/item/organ/tongue/Initialize(mapload) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 798b495cfd..f2f919a717 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -303,6 +303,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/melee/powerfist cost = 8 +/datum/uplink_item/badass/combatglovesplus + name = "Combat Gloves Plus" + desc = "A pair of gloves that are fireproof and shock resistant, however unlike the regular Combat Gloves this one uses nanotechnology \ + to learn the abilities of krav maga to the wearer." + item = /obj/item/clothing/gloves/krav_maga/combatglovesplus + cost = 5 + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + /datum/uplink_item/dangerous/emp name = "EMP Grenades and Implanter Kit" desc = "A box that contains two EMP grenades and an EMP implant. Useful to disrupt communication, \ @@ -388,7 +396,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) autoinjectors for rapid application on up to two targets each, a syringe, and a bottle containing \ the BVAK solution." item = /obj/item/storage/box/syndie_kit/tuberculosisgrenade - cost = 12 + cost = 8 surplus = 35 include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) restricted = TRUE @@ -510,7 +518,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) name = "12g Scatter Laser shot Slugs" desc = "An alternative 8-round Scatter Laser Shot magazine for use in the Bulldog shotgun." item = /obj/item/ammo_box/magazine/m12g/scatter - cost = 5 // most armor has less laser protection then bullet + cost = 4 // most armor has less laser protection then bullet /datum/uplink_item/ammo/shotgun/bag name = "12g Ammo Duffel Bag" @@ -768,7 +776,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) The concussive effect from the explosion will knock the recipient out for a short period, and deafen \ them for longer. Beware, it has a chance to detonate your PDA." item = /obj/item/cartridge/virus/syndicate - cost = 6 + cost = 5 restricted = TRUE /datum/uplink_item/stealthy_weapons/suppressor @@ -776,7 +784,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "Fitted for use on any small caliber weapon with a threaded barrel, this suppressor will silence the \ shots of the weapon for increased stealth and superior ambushing capability." item = /obj/item/suppressor - cost = 3 + cost = 1 surplus = 10 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) @@ -837,6 +845,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) surplus = 0 include_modes = list(/datum/game_mode/nuclear/clown_ops) +datum/uplink_item/stealthy_tools/taeclowndo_shoes + name = "Tae-clown-do Shoes" + desc = "A pair of shoes for the most elite agents of the honkmotherland. They grant the mastery of taeclowndo with some honk-fu moves as long as they're worn." + cost = 12 + item = /obj/item/clothing/shoes/clown_shoes/taeclowndo + 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 \ @@ -844,7 +859,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) You will receive the unlock code upon activating the virus, and the new uplink may be charged with \ telecrystals normally." item = /obj/item/cartridge/virus/frame - cost = 4 + cost = 2 restricted = TRUE /datum/uplink_item/stealthy_tools/agent_card @@ -894,7 +909,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "Screwed up and have security on your tail? This handy syringe will give you a completely new identity \ and appearance." item = /obj/item/reagent_containers/syringe/mulligan - cost = 4 + cost = 3 surplus = 30 exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) @@ -995,11 +1010,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 1 /datum/uplink_item/device_tools/surgerybag_adv - name = "Syndicate Surgery Duffel Bag" + name = "Advanced Syndicate Surgery Duffel Bag" desc = "The Syndicate surgery duffel bag is a toolkit containing all newest surgery tools, surgical drapes, \ a Syndicate brand MMI, a straitjacket, a muzzle, and a full Syndicate Combat Medic Kit." item = /obj/item/storage/backpack/duffelbag/syndie/surgery_adv - cost = 15 //Mite be to cheap + cost = 10 /datum/uplink_item/device_tools/military_belt name = "Chest Rig" @@ -1041,7 +1056,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) such as AI units and cyborgs, over their private binary channel. Caution should \ be taken while doing this, as unless they are allied with you, they are programmed to report such intrusions." item = /obj/item/encryptionkey/binary - cost = 5 + cost = 2 surplus = 75 restricted = TRUE @@ -1208,7 +1223,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) 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 + cost = 2 include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) restricted = TRUE @@ -1382,6 +1397,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/dnainjector/clumsymut restricted_roles = list("Clown") +/datum/uplink_item/role_restricted/taeclowndo_shoes + name = "Tae-clown-do Shoes" + desc = "A pair of shoes for the most elite agents of the honkmotherland. They grant the mastery of taeclowndo with some honk-fu moves as long as they're worn." + cost = 14 + item = /obj/item/clothing/shoes/clown_shoes/taeclowndo + restricted_roles = list("Clown") + /datum/uplink_item/role_restricted/mimery name = "Guide to Advanced Mimery Series" desc = "The classical two part series on how to further hone your mime skills. Upon studying the series, the user should be able to make 3x1 invisible walls, and shoot bullets out of their fingers. Obviously only works for Mimes." @@ -1448,7 +1470,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) name = "Gold Toolbox" desc = "A gold planted plastitanium toolbox loaded with tools. Comes with a set of AI detection multi-tool and a pare of combat gloves." item = /obj/item/storage/toolbox/gold_real - cost = 5 // Has synda tools + gloves + a robust weapon + cost = 3 // Has synda tools + gloves + a robust weapon restricted_roles = list("Assistant", "Curator") //Curator do to being made of gold - It fits the theme /datum/uplink_item/role_restricted/brainwash_disk @@ -1457,7 +1479,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Insert into an Operating Console to enable the procedure." item = /obj/item/disk/surgery/brainwashing restricted_roles = list("Medical Doctor") - cost = 5 + cost = 3 /datum/uplink_item/role_restricted/haunted_magic_eightball name = "Haunted Magic Eightball" @@ -1517,12 +1539,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 15 restricted_roles = list("Clown") -/datum/uplink_item/device_tools/honkpins //Idealy so they can place it into their own guns without needing cargo +/datum/uplink_item/device_tools/ultrahonkpins //Idealy so they can place it into their own guns without needing cargo name = "Hilarious firing pin" desc = "A single firing pin made for Clown agents, this firing pin makes any gun honk when fired if not a true clown! \ This firing pin also helps you fire the gun correctly. May the HonkMother HONK you agent." - item = /obj/item/firing_pin/clown - cost = 1 + item = /obj/item/firing_pin/clown/ultra + cost = 2 restricted_roles = list("Clown") /* diff --git a/html/changelogs/AutoChangeLog-pr-8236.yml b/html/changelogs/AutoChangeLog-pr-8236.yml new file mode 100644 index 0000000000..3e266fb6fc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8236.yml @@ -0,0 +1,4 @@ +author: "deathride58" +delete-after: True +changes: + - refactor: "Added unomos, which is basically listmos except gas mixtures only use one single list for handling their gasses. This is a significant performance improvement that also offers a mild memory improvement under normal circumstances." diff --git a/html/changelogs/AutoChangeLog-pr-8295.yml b/html/changelogs/AutoChangeLog-pr-8295.yml new file mode 100644 index 0000000000..833474e9af --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8295.yml @@ -0,0 +1,5 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscdel: "Many engi flags on non-engi things" + - code_imp: "changed some code to be organized at a glance" diff --git a/html/changelogs/AutoChangeLog-pr-8387.yml b/html/changelogs/AutoChangeLog-pr-8387.yml new file mode 100644 index 0000000000..4770fe08fc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8387.yml @@ -0,0 +1,4 @@ +author: "jtgsz" +delete-after: True +changes: + - rscadd: "ported gang mode" diff --git a/html/changelogs/AutoChangeLog-pr-8391.yml b/html/changelogs/AutoChangeLog-pr-8391.yml new file mode 100644 index 0000000000..32e837a0a6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8391.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "replaces a sink with a autolathen" diff --git a/html/changelogs/AutoChangeLog-pr-8404.yml b/html/changelogs/AutoChangeLog-pr-8404.yml new file mode 100644 index 0000000000..c230977a44 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8404.yml @@ -0,0 +1,5 @@ +author: "WhiteHusky" +delete-after: True +changes: + - bugfix: "Flavor text with special characters will not get partially unescaped." + - bugfix: "Canceling when setting flavor text does not clear it anymore." diff --git a/html/changelogs/AutoChangeLog-pr-8408.yml b/html/changelogs/AutoChangeLog-pr-8408.yml new file mode 100644 index 0000000000..548e4947be --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8408.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixes cult potentially stalling if the target is erased from existence without being sacced." diff --git a/html/changelogs/AutoChangeLog-pr-8414.yml b/html/changelogs/AutoChangeLog-pr-8414.yml new file mode 100644 index 0000000000..eaec60b3a9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8414.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "MASON SUIT!" diff --git a/html/changelogs/AutoChangeLog-pr-8419.yml b/html/changelogs/AutoChangeLog-pr-8419.yml new file mode 100644 index 0000000000..037dba41ad --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8419.yml @@ -0,0 +1,4 @@ +author: "UntoldTactics" +delete-after: True +changes: + - tweak: "Rewords the click here prompt given when an attempted conversion is done on an offer rune (for blood cult)" diff --git a/html/changelogs/AutoChangeLog-pr-8424.yml b/html/changelogs/AutoChangeLog-pr-8424.yml new file mode 100644 index 0000000000..12295e8a82 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8424.yml @@ -0,0 +1,4 @@ +author: "Arturlang" +delete-after: True +changes: + - rscadd: "You can now use CTRL and ALT click on pumps and filters to toggle them on and off and max their output respectively" diff --git a/html/changelogs/AutoChangeLog-pr-8430.yml b/html/changelogs/AutoChangeLog-pr-8430.yml new file mode 100644 index 0000000000..e59f61821a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8430.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "Bad Idea" diff --git a/html/changelogs/AutoChangeLog-pr-8432.yml b/html/changelogs/AutoChangeLog-pr-8432.yml new file mode 100644 index 0000000000..e65bfd68d3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8432.yml @@ -0,0 +1,5 @@ +author: "Ghommie" +delete-after: True +changes: + - balance: "Nukes the stunprod's 3 seconds delay." + - bugfix: "Fixes teleprods." diff --git a/html/changelogs/AutoChangeLog-pr-8434.yml b/html/changelogs/AutoChangeLog-pr-8434.yml new file mode 100644 index 0000000000..d01858d2b4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8434.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Stops pulls of resting mobs breaking off whenever you swap turfs with someone else because of crawling delays." diff --git a/html/changelogs/AutoChangeLog-pr-8435.yml b/html/changelogs/AutoChangeLog-pr-8435.yml new file mode 100644 index 0000000000..3c2caeb994 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8435.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "fixes IAA." diff --git a/html/changelogs/AutoChangeLog-pr-8436.yml b/html/changelogs/AutoChangeLog-pr-8436.yml new file mode 100644 index 0000000000..6d626bf357 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8436.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "bio mass meat" diff --git a/html/changelogs/AutoChangeLog-pr-8437.yml b/html/changelogs/AutoChangeLog-pr-8437.yml new file mode 100644 index 0000000000..4257a801c9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8437.yml @@ -0,0 +1,5 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "more box options" + - bugfix: "a lack of replaceable boxes" diff --git a/html/changelogs/AutoChangeLog-pr-8439.yml b/html/changelogs/AutoChangeLog-pr-8439.yml new file mode 100644 index 0000000000..1e4bd2efae --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8439.yml @@ -0,0 +1,6 @@ +author: "Ghommie" +delete-after: True +changes: + - balance: "EMPs now flick off stunbatons, they can be turned back on immediately by the user anyway." + - balance: "Stunbatons now very slowly consume charge whilst kept on, at a rate of 4/1000th of a standard batoning charge cost per tick." + - balance: "Softened up the charge cost checks to stop the above update from practically reducing the maximum uses of a stun baton by one. Now, should the remaining charge be lower than the hit cost, the resulting stun will be be proportional to the remaining charge divided by the hitcost, within a limit under which the stun batoning just won't happen." diff --git a/html/changelogs/AutoChangeLog-pr-8442.yml b/html/changelogs/AutoChangeLog-pr-8442.yml new file mode 100644 index 0000000000..e73c67c6dc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8442.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - bugfix: "clothing needing a emag" diff --git a/html/changelogs/AutoChangeLog-pr-8443.yml b/html/changelogs/AutoChangeLog-pr-8443.yml new file mode 100644 index 0000000000..1b1f82977b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8443.yml @@ -0,0 +1,4 @@ +author: "deathride58" +delete-after: True +changes: + - tweak: "The timer for stripping an item off of a spaceman is no longer interrupted by your active held item changing. This means you no longer have to worry about filling both of your hands when you're stripping items off of someone." diff --git a/html/changelogs/AutoChangeLog-pr-8448.yml b/html/changelogs/AutoChangeLog-pr-8448.yml new file mode 100644 index 0000000000..e670ffc0f4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8448.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "4 - > 5" diff --git a/html/changelogs/AutoChangeLog-pr-8449.yml b/html/changelogs/AutoChangeLog-pr-8449.yml new file mode 100644 index 0000000000..c2c397cdd0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8449.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "5->6" diff --git a/html/changelogs/AutoChangeLog-pr-8450.yml b/html/changelogs/AutoChangeLog-pr-8450.yml new file mode 100644 index 0000000000..8afa74c68c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8450.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "4tc - > 2tc" diff --git a/html/changelogs/AutoChangeLog-pr-8452.yml b/html/changelogs/AutoChangeLog-pr-8452.yml new file mode 100644 index 0000000000..2b4e430757 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8452.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "mulligan costs 4 - > 3" diff --git a/html/changelogs/AutoChangeLog-pr-8453.yml b/html/changelogs/AutoChangeLog-pr-8453.yml new file mode 100644 index 0000000000..cda1843779 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8453.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "Tuberculosis 12tc - > 8tc" diff --git a/html/changelogs/AutoChangeLog-pr-8454.yml b/html/changelogs/AutoChangeLog-pr-8454.yml new file mode 100644 index 0000000000..4cc6d98df1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8454.yml @@ -0,0 +1,5 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "15 - >10" + - bugfix: "both bags have the same name" diff --git a/html/changelogs/AutoChangeLog-pr-8455.yml b/html/changelogs/AutoChangeLog-pr-8455.yml new file mode 100644 index 0000000000..e7944f5869 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8455.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "5 - > 2" diff --git a/html/changelogs/AutoChangeLog-pr-8456.yml b/html/changelogs/AutoChangeLog-pr-8456.yml new file mode 100644 index 0000000000..3ff35b1c09 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8456.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "3 -> 1" diff --git a/html/changelogs/AutoChangeLog-pr-8459.yml b/html/changelogs/AutoChangeLog-pr-8459.yml new file mode 100644 index 0000000000..ebce2f0db1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8459.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "2 < - 4" diff --git a/html/changelogs/AutoChangeLog-pr-8460.yml b/html/changelogs/AutoChangeLog-pr-8460.yml new file mode 100644 index 0000000000..39af97ef12 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8460.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "1->2 and ultra" diff --git a/html/changelogs/AutoChangeLog-pr-8463.yml b/html/changelogs/AutoChangeLog-pr-8463.yml new file mode 100644 index 0000000000..a6c8d05522 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8463.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "shield + crafting" diff --git a/html/changelogs/AutoChangeLog-pr-8465.yml b/html/changelogs/AutoChangeLog-pr-8465.yml new file mode 100644 index 0000000000..91556b4afc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8465.yml @@ -0,0 +1,4 @@ +author: "Useroth" +delete-after: True +changes: + - bugfix: "After the PR which raised the ammo capacity of said magazines, due to the code, they ended up with an invalid icon state. Fixed through changing the icon state name in the icon file." diff --git a/html/changelogs/AutoChangeLog-pr-8466.yml b/html/changelogs/AutoChangeLog-pr-8466.yml new file mode 100644 index 0000000000..a3cffc8f59 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8466.yml @@ -0,0 +1,4 @@ +author: "Poojawa" +delete-after: True +changes: + - bugfix: "Atmos and Station alerts should be more alerting." diff --git a/html/changelogs/AutoChangeLog-pr-8470.yml b/html/changelogs/AutoChangeLog-pr-8470.yml new file mode 100644 index 0000000000..39399c0724 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8470.yml @@ -0,0 +1,5 @@ +author: "Ghommie" +delete-after: True +changes: + - balance: "Buffs condensed capsaicin, a yet another feature previously dunked by stam combat." + - balance: "speeds up pepper spray puffs." diff --git a/html/changelogs/AutoChangeLog-pr-8473.yml b/html/changelogs/AutoChangeLog-pr-8473.yml new file mode 100644 index 0000000000..a4743c9a39 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8473.yml @@ -0,0 +1,10 @@ +author: "Useroth" +delete-after: True +changes: + - rscadd: "Taeclowndo shoes which grant the four following abilities: +- Conjuring a cream pie right into their hand. Every three seconds. +- Making a banana peel appear out of thin air at the tile of the clown's choice. Every ten seconds. +- Mega HoNk. A touch-ranged, very small AOE ability, with effect equal to being honked by a Honkerblast on a clown mech, with the effects halved for anyone who isn't its direct target. Every ten seconds. +- Bluespace Banana Pie. You don't throw this one... not right away at least. This baby can fit an entire body inside. Good for disposal of evidence. 25 second-long action, 45 second cooldown. Also produces a \"[victim's name] cream pie\". The body drops out of the pie if you splat it somewhere or destroy the pie. If you eat it, it will chestburst out of you a'la monkey cube. + +It's a 14 TC item for traitor clowns and a 12 TC item for clown-ops." diff --git a/html/changelogs/AutoChangeLog-pr-8476.yml b/html/changelogs/AutoChangeLog-pr-8476.yml new file mode 100644 index 0000000000..551a443f94 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8476.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "hierophant movment and melee attack" diff --git a/html/changelogs/AutoChangeLog-pr-8477.yml b/html/changelogs/AutoChangeLog-pr-8477.yml new file mode 100644 index 0000000000..f5478ed178 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8477.yml @@ -0,0 +1,4 @@ +author: "deathride58" +delete-after: True +changes: + - rscadd: "Ported the zulie cloak and blackredgold coat donor items from RP." diff --git a/html/changelogs/AutoChangeLog-pr-8482.yml b/html/changelogs/AutoChangeLog-pr-8482.yml new file mode 100644 index 0000000000..095f93a7a3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8482.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "fixes eyestabbing people with cutlery while being a pacifist." diff --git a/html/changelogs/AutoChangeLog-pr-8483.yml b/html/changelogs/AutoChangeLog-pr-8483.yml new file mode 100644 index 0000000000..2e3b35ca92 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8483.yml @@ -0,0 +1,4 @@ +author: "Ghommie (original PR by mrhugo13 on tgstation13)" +delete-after: True +changes: + - rscadd: "The Syndicate has decided to equip their Syndicate leaders operative (Aswell as their clown counterparts) with the new Combat Glove Plus! The new Combat Glove Plus does everything the old boring Combat Gloves does but with the added extra of learning Krav Maga upon wearing them, any other Syndicate operative who wants to get in on the action will have to pay 5tc." diff --git a/html/changelogs/AutoChangeLog-pr-8485.yml b/html/changelogs/AutoChangeLog-pr-8485.yml new file mode 100644 index 0000000000..abc79f1f30 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8485.yml @@ -0,0 +1,5 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "More plushies" + - tweak: "attack verbs and descs" diff --git a/html/changelogs/AutoChangeLog-pr-8489.yml b/html/changelogs/AutoChangeLog-pr-8489.yml new file mode 100644 index 0000000000..5e90c56d67 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8489.yml @@ -0,0 +1,5 @@ +author: "Coolgat3" +delete-after: True +changes: + - rscadd: "Added combat gloves sprite" + - imageadd: "Added said sprite" diff --git a/html/changelogs/AutoChangeLog-pr-8491.yml b/html/changelogs/AutoChangeLog-pr-8491.yml new file mode 100644 index 0000000000..0e82e809f7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8491.yml @@ -0,0 +1,5 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Reduces goonchat lag from being blasted by pellets and bullets repeatedly whilst wearing armor by properly removing the armor protection texts this times." + - spellcheck: "also reduced the size of armor protection messages in general. they clog up the chat box." diff --git a/html/changelogs/AutoChangeLog-pr-8494.yml b/html/changelogs/AutoChangeLog-pr-8494.yml new file mode 100644 index 0000000000..2e4d7ac65e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8494.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "bone satchles" diff --git a/html/changelogs/AutoChangeLog-pr-8495.yml b/html/changelogs/AutoChangeLog-pr-8495.yml new file mode 100644 index 0000000000..92777ef8b0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8495.yml @@ -0,0 +1,4 @@ +author: "BurgerBB" +delete-after: True +changes: + - tweak: "\"Unwillingly\" eating food now sends a warning message instead of a notice. Unable to stuff food down your throat sends a danger message instead of a warning message." diff --git a/html/changelogs/AutoChangeLog-pr-8500.yml b/html/changelogs/AutoChangeLog-pr-8500.yml new file mode 100644 index 0000000000..09bfbd9748 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8500.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - balance: "rebalanced stunslugs" diff --git a/html/changelogs/AutoChangeLog-pr-8501.yml b/html/changelogs/AutoChangeLog-pr-8501.yml new file mode 100644 index 0000000000..fba27c4d52 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8501.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "colored boxes, and more types" diff --git a/html/changelogs/AutoChangeLog-pr-8503.yml b/html/changelogs/AutoChangeLog-pr-8503.yml new file mode 100644 index 0000000000..f6c4a7dc75 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8503.yml @@ -0,0 +1,5 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "harm and such" + - balance: "item classes" diff --git a/html/changelogs/AutoChangeLog-pr-8504.yml b/html/changelogs/AutoChangeLog-pr-8504.yml new file mode 100644 index 0000000000..4e80d02845 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8504.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - bugfix: "resonators being so shitty" diff --git a/html/changelogs/AutoChangeLog-pr-8506.yml b/html/changelogs/AutoChangeLog-pr-8506.yml new file mode 100644 index 0000000000..6711217fb5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8506.yml @@ -0,0 +1,5 @@ +author: "CalamaBanana" +delete-after: True +changes: + - rscadd: "Added Deer taur" + - rscadd: "Added Elf ears to mammals" diff --git a/html/changelogs/AutoChangeLog-pr-8515.yml b/html/changelogs/AutoChangeLog-pr-8515.yml new file mode 100644 index 0000000000..99e414f5e1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8515.yml @@ -0,0 +1,5 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixes stunbatons icon not properly updating on cell removal and insertion." + - tweak: "Allows lower charge cells to be used with stun batons, and thus single use crapshots batons." diff --git a/html/changelogs/AutoChangeLog-pr-8516.yml b/html/changelogs/AutoChangeLog-pr-8516.yml new file mode 100644 index 0000000000..f7419f1add --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8516.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - balance: "Adds in a 7 seconds delay to the jackhammer dismantling a superheated clockwork wall." diff --git a/html/changelogs/AutoChangeLog-pr-8517.yml b/html/changelogs/AutoChangeLog-pr-8517.yml new file mode 100644 index 0000000000..5e14c1c696 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8517.yml @@ -0,0 +1,6 @@ +author: "Poojawa" +delete-after: True +changes: + - rscadd: "Added visible and hidden testicles" + - rscadd: "Added multi-boob support. Now you can have two or three pairs of breasts." + - bugfix: "fixed missing vagina and breast sprites" diff --git a/html/changelogs/AutoChangeLog-pr-8518.yml b/html/changelogs/AutoChangeLog-pr-8518.yml new file mode 100644 index 0000000000..cbc34893b6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8518.yml @@ -0,0 +1,5 @@ +author: "Ghommie" +delete-after: True +changes: + - tweak: "escape pods emergency suits storage can now be busted open by emags or excessive damage." + - bugfix: "Fixes alt click bypassing the escape pods' suits storage lock." diff --git a/html/changelogs/AutoChangeLog-pr-8524.yml b/html/changelogs/AutoChangeLog-pr-8524.yml new file mode 100644 index 0000000000..66a7b6d456 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8524.yml @@ -0,0 +1,4 @@ +author: "BurgerBB" +delete-after: True +changes: + - rscadd: "Adds a new trait \"Buns of Steel\" that makes you immune to the effects of ass slapping, and temporarily makes the user's arm useless like a stun baton hit. It costs 0 points." diff --git a/html/changelogs/AutoChangeLog-pr-8525.yml b/html/changelogs/AutoChangeLog-pr-8525.yml new file mode 100644 index 0000000000..ef26cf1a4b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8525.yml @@ -0,0 +1,5 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - bugfix: "Game braking bug +critical: bug fix" diff --git a/html/changelogs/AutoChangeLog-pr-8528.yml b/html/changelogs/AutoChangeLog-pr-8528.yml new file mode 100644 index 0000000000..0dece1e63d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8528.yml @@ -0,0 +1,7 @@ +author: "Poojawa" +delete-after: True +changes: + - bugfix: "fixed prosthetic hands being invisible" + - bugfix: "male foxes exist again" + - bugfix: "female chest markings improved from being too dark in comparison to their other colors, blending better" + - bugfix: "Markings behave better on non-organic limbs." diff --git a/html/changelogs/AutoChangeLog-pr-8534.yml b/html/changelogs/AutoChangeLog-pr-8534.yml new file mode 100644 index 0000000000..5c76fbdf36 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8534.yml @@ -0,0 +1,4 @@ +author: "dtfe3" +delete-after: True +changes: + - rscadd: "Pink Panties" diff --git a/html/changelogs/AutoChangeLog-pr-8538.yml b/html/changelogs/AutoChangeLog-pr-8538.yml new file mode 100644 index 0000000000..7349a31683 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8538.yml @@ -0,0 +1,4 @@ +author: "dtfe3" +delete-after: True +changes: + - rscadd: "Twintails" diff --git a/html/changelogs/AutoChangeLog-pr-8540.yml b/html/changelogs/AutoChangeLog-pr-8540.yml new file mode 100644 index 0000000000..895b80bfa6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8540.yml @@ -0,0 +1,4 @@ +author: "BurgerBB" +delete-after: True +changes: + - balance: "Ass slapping blowback from the Buns of Steel perk now deals 20 stamina damage instead of 50, and no brute damage." diff --git a/html/changelogs/AutoChangeLog-pr-8543.yml b/html/changelogs/AutoChangeLog-pr-8543.yml new file mode 100644 index 0000000000..1e1ce8922b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8543.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - rscadd: "SNOW CONES" diff --git a/html/changelogs/AutoChangeLog-pr-8569.yml b/html/changelogs/AutoChangeLog-pr-8569.yml new file mode 100644 index 0000000000..9a51ab1099 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8569.yml @@ -0,0 +1,4 @@ +author: "dtfe3" +delete-after: True +changes: + - tweak: "Now the fox ears are located in front of hair meaning they now behave much like cat ears, that being they are on-top of the hair layer." diff --git a/icons/effects/crayondecal.dmi b/icons/effects/crayondecal.dmi index bb48832025..fcd27698e7 100644 Binary files a/icons/effects/crayondecal.dmi and b/icons/effects/crayondecal.dmi differ diff --git a/icons/mob/custom_w.dmi b/icons/mob/custom_w.dmi index 0c872625dd..dcb36e7b47 100644 Binary files a/icons/mob/custom_w.dmi and b/icons/mob/custom_w.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 3da5fc08e2..09d6fe5374 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index 047f080946..10474f94da 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi index b479fa0764..9540599364 100644 Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index b518157bb5..0f3438dfc4 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi index a90efcdfea..619db9290d 100644 Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index d905979c9c..8256c1fc07 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 8e0359e6b3..2cb473fb00 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/custom.dmi b/icons/obj/custom.dmi index 9a7d460e14..9faaf8f81a 100644 Binary files a/icons/obj/custom.dmi and b/icons/obj/custom.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index becb58568f..55c33e5e83 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/food/snowcones.dmi b/icons/obj/food/snowcones.dmi new file mode 100644 index 0000000000..bdaa89fdf7 Binary files /dev/null and b/icons/obj/food/snowcones.dmi differ diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi index 543d57ff56..655d37d4a6 100644 Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ diff --git a/icons/obj/plushes.dmi b/icons/obj/plushes.dmi index 7ca7b068b0..8c0e11ef16 100644 Binary files a/icons/obj/plushes.dmi and b/icons/obj/plushes.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index d6ffc12ae7..9467e8f818 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/modular_citadel/code/datums/traits/neutral.dm b/modular_citadel/code/datums/traits/neutral.dm index 264dbfef0a..05aeb27361 100644 --- a/modular_citadel/code/datums/traits/neutral.dm +++ b/modular_citadel/code/datums/traits/neutral.dm @@ -4,6 +4,7 @@ name = "Nymphomania" desc = "You're always feeling a bit in heat. Also, you get aroused faster than usual." value = 0 + mob_trait = TRAIT_NYMPHO gain_text = "You are feeling extra wild." lose_text = "You don't feel that burning sensation anymore." @@ -22,3 +23,11 @@ if(M.canbearoused == FALSE) to_chat(quirk_holder, "Having high libido is useless when you can't feel arousal at all!") qdel(src) + +/datum/quirk/maso + name = "Masochism" + desc = "You are aroused by pain." + value = 0 + mob_trait = TRAIT_MASO + gain_text = "You desire to be hurt." + lose_text = "Pain has become less exciting for you." diff --git a/modular_citadel/code/game/gamemodes/gangs/dominator.dm b/modular_citadel/code/game/gamemodes/gangs/dominator.dm new file mode 100644 index 0000000000..0d89c78010 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/dominator.dm @@ -0,0 +1,243 @@ +#define DOM_BLOCKED_SPAM_CAP 6 +//32 instead of 40 for safety reasons. How many turfs aren't walls around dominator for it to work +#define DOM_REQUIRED_TURFS 32 +#define DOM_HULK_HITS_REQUIRED 10 + +/obj/machinery/dominator + name = "dominator" + desc = "A visibly sinister device. Looks like you can break it if you hit it enough." + icon = 'icons/obj/machines/dominator.dmi' + icon_state = "dominator" + density = TRUE + anchored = TRUE + layer = HIGH_OBJ_LAYER + max_integrity = 300 + integrity_failure = 100 + armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 10, "acid" = 70) + var/datum/team/gang/gang + var/operating = FALSE //false=standby or broken, true=takeover + var/warned = FALSE //if this device has set off the warning at <3 minutes yet + var/spam_prevention = DOM_BLOCKED_SPAM_CAP //first message is immediate + var/datum/effect_system/spark_spread/spark_system + var/obj/effect/countdown/dominator/countdown + +/obj/machinery/dominator/Initialize() + . = ..() + set_light(l_range = 2, l_power = 0.75) + GLOB.poi_list |= src + spark_system = new + spark_system.set_up(5, TRUE, src) + countdown = new(src) + update_icon() + +/obj/machinery/dominator/Destroy() + if(!(stat & BROKEN)) + set_broken() + GLOB.poi_list.Remove(src) + gang = null + QDEL_NULL(spark_system) + QDEL_NULL(countdown) + STOP_PROCESSING(SSmachines, src) + return ..() + +/obj/machinery/dominator/emp_act(severity) + take_damage(100, BURN, "energy", 0) + ..() + +/obj/machinery/dominator/hulk_damage() + return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED + +/obj/machinery/dominator/tesla_act() + qdel(src) + +/obj/machinery/dominator/update_icon() + cut_overlays() + if(!(stat & BROKEN)) + icon_state = "dominator-active" + if(operating) + var/mutable_appearance/dominator_overlay = mutable_appearance('icons/obj/machines/dominator.dmi', "dominator-overlay") + if(gang) + dominator_overlay.color = gang.color + add_overlay(dominator_overlay) + else + icon_state = "dominator" + if(obj_integrity/max_integrity < 0.66) + add_overlay("damage") + else + icon_state = "dominator-broken" + +/obj/machinery/dominator/examine(mob/user) + ..() + if(stat & BROKEN) + return + + if(gang && gang.domination_time != NOT_DOMINATING) + if(gang.domination_time > world.time) + to_chat(user, "Hostile Takeover in progress. Estimated [gang.domination_time_remaining()] seconds remain.") + else + to_chat(user, "Hostile Takeover of [station_name()] successful. Have a great day.") + else + to_chat(user, "System on standby.") + to_chat(user, "System Integrity: [round((obj_integrity/max_integrity)*100,1)]%") + +/obj/machinery/dominator/process() + ..() + if(gang && gang.domination_time != NOT_DOMINATING) + var/time_remaining = gang.domination_time_remaining() + if(time_remaining > 0) + if(excessive_walls_check()) + gang.domination_time += 20 + if(spam_prevention < DOM_BLOCKED_SPAM_CAP) + spam_prevention++ + else + playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) // Play sound buzz-two.ogg, not before cause its annoying. + gang.message_gangtools("Warning: There are too many walls around your gang's dominator, its signal is being blocked!") + say("Error: Takeover signal is currently blocked! There are too many walls within 3 standard units of this device.") + spam_prevention = 0 + return + . = TRUE + playsound(loc, 'sound/items/timer.ogg', 10, 0) + if(!warned && (time_remaining < 180)) + warned = TRUE + var/area/domloc = get_area(loc) + gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!") + for(var/G in GLOB.gangs) + var/datum/team/gang/tempgang = G + if(tempgang != gang) + tempgang.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1) + else + Cinematic(CINEMATIC_MALF,world) //Here is the gang victory trigger on the dominator ending. + gang.winner = TRUE + SSticker.force_ending = TRUE + + if(!.) + STOP_PROCESSING(SSmachines, src) + +/obj/machinery/dominator/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src, 'sound/effects/bang.ogg', 50, 1) + else + playsound(loc, 'sound/weapons/tap.ogg', 50, 1) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, 1) + +/obj/machinery/dominator/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) + . = ..() + if(.) + if(obj_integrity/max_integrity > 0.66) + if(prob(damage_amount*2)) + spark_system.start() + else if(!(stat & BROKEN)) + spark_system.start() + update_icon() + + +/obj/machinery/dominator/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1)) + set_broken() + +/obj/machinery/dominator/deconstruct(disassembled = TRUE) + if(!(flags_1 & NODECONSTRUCT_1)) + if(!(stat & BROKEN)) + set_broken() + new /obj/item/stack/sheet/plasteel(src.loc) + qdel(src) + +/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user) + add_fingerprint(user) + ..() + +/obj/machinery/dominator/attack_hand(mob/user) + if(operating || (stat & BROKEN)) + examine(user) + return + + var/datum/team/gang/tempgang + + var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang) + if(GA) + tempgang = GA.gang + if(!tempgang) + examine(user) + return + + if(tempgang.domination_time != NOT_DOMINATING) + to_chat(user, "Error: Hostile Takeover is already in progress.") + return + + if(!tempgang.dom_attempts) + to_chat(user, "Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.") + return + + var/time = round(tempgang.determine_domination_time()/60,0.1) + if(alert(user,"A takeover will require [time] minutes.\nYour gang will be unable to gain influence while it is active.\nThe entire station will likely be alerted to it once it starts.\nYou have [tempgang.dom_attempts] attempt(s) remaining. Are you ready?","Confirm","Ready","Later") == "Ready") + if((tempgang.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc)) + return 0 + + var/area/A = get_area(loc) + var/locname = A.map_name + + gang = tempgang + gang.dom_attempts -- + priority_announce("Network breach detected in [locname]. The [gang.name] Gang is attempting to seize control of the station!","Network Alert") + gang.domination() + SSshuttle.registerHostileEnvironment(src) + name = "[gang.name] Gang [name]" + operating = TRUE + update_icon() + + countdown.start() + countdown.color = gang.color + + set_light(l_range = 3, l_power = 0.9) + light_color = gang.color + START_PROCESSING(SSmachines, src) + + gang.message_gangtools("Hostile takeover in progress: Estimated [time] minutes until victory.[gang.dom_attempts ? "" : " This is your final attempt."]") + for(var/G in GLOB.gangs) + var/datum/team/gang/vagos = G + if(vagos != gang) + vagos.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1) + +/obj/machinery/dominator/proc/excessive_walls_check() // why the fuck was this even a global proc... + var/open = 0 + for(var/turf/T in view(3, src)) + if(!iswallturf(T)) //Check for /closed/wall, isclosedturf() moves it back to just checking for /closed/ which makes it very finicky. + open++ + //to_chat(world, "THE DOMINATOR SEES [open] OPEN TURFS") uncomment to see what this shitty fucking wallcheck sees + if(open < DOM_REQUIRED_TURFS) + return TRUE + else + return FALSE +/obj/machinery/dominator/proc/set_broken() + if(gang) + gang.domination_time = NOT_DOMINATING + + var/takeover_in_progress = FALSE + for(var/G in GLOB.gangs) + var/datum/team/gang/ballas = G + if(ballas.domination_time != NOT_DOMINATING) + takeover_in_progress = TRUE + break + if(!takeover_in_progress) + var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED + if(!was_stranded) + priority_announce("All hostile activity within station systems has ceased.","Network Alert") + + if(get_security_level() == "delta") + set_security_level("red") + + SSshuttle.clearHostileEnvironment(src) + gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1) + + set_light(0) + operating = FALSE + stat |= BROKEN + update_icon() + STOP_PROCESSING(SSmachines, src) + +#undef DOM_BLOCKED_SPAM_CAP +#undef DOM_REQUIRED_TURFS +#undef DOM_HULK_HITS_REQUIRED \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/dominator_countdown.dm b/modular_citadel/code/game/gamemodes/gangs/dominator_countdown.dm new file mode 100644 index 0000000000..c6ae610e37 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/dominator_countdown.dm @@ -0,0 +1,13 @@ +/obj/effect/countdown/dominator + name = "dominator countdown" + text_size = 1 + color = "#e5e5e5" // Overwritten when the dominator starts + +/obj/effect/countdown/dominator/get_value() + var/obj/machinery/dominator/D = attached_to + if(!istype(D)) + return + else if(D.gang && D.gang.domination_time != NOT_DOMINATING) + return D.gang.domination_time_remaining() + else + return "OFFLINE" \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/gang.dm b/modular_citadel/code/game/gamemodes/gangs/gang.dm new file mode 100644 index 0000000000..00ce3f81a9 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gang.dm @@ -0,0 +1,476 @@ +/datum/antagonist/gang + name = "Gangster" + roundend_category = "gangsters" + can_coexist_with_others = FALSE + job_rank = ROLE_GANG + antagpanel_category = "Gang" + var/hud_type = "gangster" + var/message_name = "Gangster" + var/datum/team/gang/gang + +/datum/antagonist/gang/can_be_owned(datum/mind/new_owner) + . = ..() + if(.) + if(new_owner.unconvertable) + return FALSE + +/datum/antagonist/gang/apply_innate_effects(mob/living/mob_override) + var/mob/living/M = mob_override || owner.current + update_gang_icons_added(M) + +/datum/antagonist/gang/remove_innate_effects(mob/living/mob_override) + var/mob/living/M = mob_override || owner.current + update_gang_icons_removed(M) + +/datum/antagonist/gang/get_team() + return gang + +/datum/antagonist/gang/greet() + gang.greet_gangster(owner) + +/datum/antagonist/gang/farewell() + if(ishuman(owner.current)) + owner.current.visible_message("[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!", null, null, null, owner.current) + to_chat(owner, "You are no longer a gangster!") + +/datum/antagonist/gang/on_gain() + if(!gang) + create_team() + ..() + var/mob/living/carbon/human/H = owner.current + if(istype(H)) + if(owner.assigned_role == "Clown") + to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + H.dna.remove_mutation(CLOWNMUT) + add_to_gang() + +/datum/antagonist/gang/on_removal() + remove_from_gang() + ..() + +/datum/antagonist/gang/create_team(team) + if(!gang) // add_antag_datum calls create_team, so we need to avoid generating two gangs in that case + if(team) + gang = team + return + var/datum/team/gang/gangteam = pick_n_take(GLOB.possible_gangs) + if(gangteam) + gang = new gangteam + +/datum/antagonist/gang/proc/equip_gang() // Bosses get equipped with their tools + return + +/datum/antagonist/gang/proc/update_gang_icons_added(mob/living/M) + var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num] + if(!ganghud) + ganghud = new/datum/atom_hud/antag/gang() + gang.hud_entry_num = GLOB.huds.len+1 // this is the index the gang hud will be added at + GLOB.huds += ganghud + ganghud.color = gang.color + ganghud.join_hud(M) + set_antag_hud(M,hud_type) + +/datum/antagonist/gang/proc/update_gang_icons_removed(mob/living/M) + var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num] + if(ganghud) + ganghud.leave_hud(M) + set_antag_hud(M, null) + +/datum/antagonist/gang/proc/can_be_converted(mob/living/candidate) + if(!candidate.mind) + return FALSE + if(!can_be_owned(candidate.mind)) + return FALSE + var/mob/living/carbon/human/H = candidate + if(!istype(H)) //Can't nonhumans + return FALSE + return TRUE + +/datum/antagonist/gang/proc/promote() // Bump up to boss + var/datum/team/gang/old_gang = gang + var/datum/mind/old_owner = owner + owner.remove_antag_datum(/datum/antagonist/gang) + var/datum/antagonist/gang/boss/lieutenant/new_boss = new + new_boss.silent = TRUE + old_owner.add_antag_datum(new_boss,old_gang) + new_boss.silent = FALSE + log_game("[key_name(old_owner)] has been promoted to Lieutenant in the [old_gang.name] Gang") + to_chat(old_owner, "You have been promoted to Lieutenant!") + + +// Admin commands +/datum/antagonist/gang/get_admin_commands() + . = ..() + .["Promote"] = CALLBACK(src,.proc/admin_promote) + .["Set Influence"] = CALLBACK(src, .proc/admin_adjust_influence) + if(gang.domination_time != NOT_DOMINATING) + .["Set domination time left"] = CALLBACK(src, .proc/set_dom_time_left) + +/datum/antagonist/gang/admin_add(datum/mind/new_owner,mob/admin) + var/new_or_existing = input(admin, "Which gang do you want to be assigned to the user?", "Gangs") as null|anything in list("New","Existing") + if(isnull(new_or_existing)) + return + else if(new_or_existing == "New") + var/newgang = input(admin, "Select a gang, or select random to pick a random one.", "New gang") as null|anything in GLOB.possible_gangs + "Random" + if(isnull(newgang)) + return + else if(newgang == "Random") + var/datum/team/gang/G = pick_n_take(GLOB.possible_gangs) + gang = new G + else + GLOB.possible_gangs -= newgang + gang = new newgang + else + if(!GLOB.gangs.len) // no gangs exist + to_chat(admin, "No gangs exist, please create a new one instead.") + return + var/existinggang = input(admin, "Select a gang, or select random to pick a random one.", "Existing gang") as null|anything in GLOB.gangs + "Random" + if(isnull(existinggang)) + return + else if(existinggang == "Random") + gang = pick(GLOB.gangs) + else + gang = existinggang + ..() + return TRUE + +/datum/antagonist/gang/proc/admin_promote(mob/admin) + message_admins("[key_name_admin(admin)] has promoted [owner] to gang boss.") + log_admin("[key_name(admin)] has promoted [owner] to boss.") + promote() + +/datum/antagonist/gang/proc/admin_adjust_influence() + var/inf = input("Influence for [gang.name]","Gang influence", gang.influence) as null | num + if(!isnull(inf)) + gang.influence = inf + message_admins("[key_name_admin(usr)] changed [gang.name]'s influence to [inf].") + log_admin("[key_name(usr)] changed [gang.name]'s influence to [inf].") + +/datum/antagonist/gang/proc/add_to_gang() + gang.add_member(owner) + owner.current.log_message("Has been converted to the [gang.name] gang!", INDIVIDUAL_ATTACK_LOG) + +/datum/antagonist/gang/proc/remove_from_gang() + gang.remove_member(owner) + owner.current.log_message("Has been deconverted from the [gang.name] gang!", INDIVIDUAL_ATTACK_LOG) + +/datum/antagonist/gang/proc/set_dom_time_left(mob/admin) + if(gang.domination_time == NOT_DOMINATING) + return // an admin shouldn't need this + var/seconds = input(admin, "Set the time left for the gang to win, in seconds", "Domination time left") as null|num + if(seconds && seconds > 0) + gang.domination_time = world.time + seconds*10 + gang.message_gangtools("Takeover shortened to [gang.domination_time_remaining()] seconds by your Syndicate benefactors.") + +// Boss type. Those can use gang tools to buy items for their gang, in particular the Dominator, used to win the gamemode, along with more gang tools to promote fellow gangsters to boss status. +/datum/antagonist/gang/boss + name = "Gang boss" + hud_type = "gang_boss" + message_name = "Leader" + +/datum/antagonist/gang/boss/on_gain() + ..() + if(gang) + gang.leaders += owner + +/datum/antagonist/gang/boss/on_removal() + if(gang) + gang.leaders -= owner + ..() + +/datum/antagonist/gang/boss/antag_listing_name() + return ..() + "(Boss)" + +/datum/antagonist/gang/boss/equip_gang(gangtool = TRUE, pen = TRUE, spraycan = TRUE, hud = TRUE) // usually has to be called separately + var/mob/living/carbon/human/H = owner.current + if(!istype(H)) + return + + var/list/slots = list ( + "backpack" = SLOT_IN_BACKPACK, + "left pocket" = SLOT_L_STORE, + "right pocket" = SLOT_R_STORE, + "hands" = SLOT_HANDS + ) + + if(gangtool)//Here is where all of the text occurs when a gang boss first spawns in. + var/obj/item/device/gangtool/G = new() + var/where = H.equip_in_one_of_slots(G, slots) + if (!where) + to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a Gangtool.") + else + G.register_device(H) + to_chat(H, "The Gangtool in your [where] will allow you to purchase weapons and equipment, send messages to your gang, and recall the emergency shuttle from anywhere on the station.") + to_chat(H, "As the gang boss, you can also promote your gang members to lieutenant. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use gangtools too.") + + if(pen) + var/obj/item/pen/gang/T = new() + var/where2 = H.equip_in_one_of_slots(T, slots) + if (!where2) + to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start.") + else + to_chat(H, "The recruitment pen in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them. All gangsters can use these, distribute them to see your gang grow.") + + if(spraycan) + var/obj/item/toy/crayon/spraycan/gang/SC = new(null,gang) + var/where3 = H.equip_in_one_of_slots(SC, slots) + if (!where3) + to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start.") + else + to_chat(H, "The territory spraycan in your [where3] can be used to claim areas of the station for your gang. The more territory your gang controls, the more influence you get. All gangsters can use these, so distribute them to grow your influence faster.") + + if(hud) + var/obj/item/clothing/glasses/hud/security/chameleon/C = new(null,gang) + var/where4 = H.equip_in_one_of_slots(C, slots) + if (!where4) + to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD.") + else + to_chat(H, "The chameleon security HUD in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited.") + +// Admin commands for bosses +/datum/antagonist/gang/boss/admin_add(datum/mind/new_owner,mob/admin) + if(!new_owner.has_antag_datum(parent_type)) + ..() + to_chat(new_owner.current, "You are a member of the [gang.name] Gang leadership now!") + return + promote() + message_admins("[key_name_admin(admin)] has made [new_owner.current] a boss of the [gang.name] gang.") + log_admin("[key_name(admin)] has made [new_owner.current] a boss of the [gang.name] gang.") + to_chat(new_owner.current, "You are a member of the [gang.name] Gang leadership now!") + +/datum/antagonist/gang/boss/get_admin_commands() + . = ..() + . -= "Promote" + .["Take gangtool"] = CALLBACK(src,.proc/admin_take_gangtool) + .["Give gangtool"] = CALLBACK(src,.proc/admin_give_gangtool) + .["Demote"] = CALLBACK(src,.proc/admin_demote) + +/datum/antagonist/gang/boss/proc/demote() + var/old_gang = gang + var/datum/mind/old_owner = owner + silent = TRUE + owner.remove_antag_datum(/datum/antagonist/gang/boss) + var/datum/antagonist/gang/new_gangster = new /datum/antagonist/gang() + new_gangster.silent = TRUE + old_owner.add_antag_datum(new_gangster,old_gang) + new_gangster.silent = FALSE + log_game("[key_name(old_owner)] has been demoted to Gangster in the [gang.name] Gang") + to_chat(old_owner, "The gang has been disappointed of your leader traits! You are a regular gangster now!") + +/datum/antagonist/gang/boss/proc/admin_take_gangtool(mob/admin) + var/list/L = owner.current.get_contents() + var/obj/item/device/gangtool/gangtool = locate() in L + if (!gangtool) + to_chat(admin, "Deleting gangtool failed!") + return + qdel(gangtool) + +/datum/antagonist/gang/boss/proc/admin_give_gangtool(mob/admin) + equip_gang(TRUE, FALSE, FALSE, FALSE) + +/datum/antagonist/gang/boss/proc/admin_demote(datum/mind/target,mob/user) + message_admins("[key_name_admin(user)] has demoted [owner.current] from gang boss.") + log_admin("[key_name(user)] has demoted [owner.current] from gang boss.") + admin_take_gangtool(user) + demote() + +/datum/antagonist/gang/boss/lieutenant + name = "Gang Lieutenant" + message_name = "Lieutenant" + hud_type = "gang_lt" + +#define MAXIMUM_RECALLS 3 +#define INFLUENCE_INTERVAL 1200 //This handles the interval between each count of influence. +// Gang team datum. This handles the gang itself. +/datum/team/gang + name = "Gang" + member_name = "gangster" + var/hud_entry_num // because if you put something other than a number in GLOB.huds, god have mercy on your fucking soul friend + var/list/leaders = list() // bosses + var/max_leaders = MAX_LEADERS_GANG + var/list/territories = list() // territories owned by the gang. + var/list/lost_territories = list() // territories lost by the gang. + var/list/new_territories = list() // territories captured by the gang. + var/list/gangtools = list() + var/domination_time = NOT_DOMINATING + var/dom_attempts = INITIAL_DOM_ATTEMPTS + var/color + var/influence = 0 // influence of the gang, based on how many territories they own. Can be used to buy weapons and tools from a gang uplink. + var/winner // Once the gang wins with a dominator, this becomes true. For roundend credits purposes. + var/list/inner_outfits = list() + var/list/outer_outfits = list() + var/next_point_time + var/recalls = MAXIMUM_RECALLS // Once this reaches 0, this gang cannot force recall the shuttle with their gangtool anymore + +/datum/team/gang/New(starting_members) + . = ..() + GLOB.gangs += src + if(starting_members) + if(islist(starting_members)) + for(var/datum/mind/groveboss in starting_members) + leaders += groveboss + var/datum/antagonist/gang/boss/gb = new + groveboss.add_antag_datum(gb, src) + gb.equip_gang() + + else + var/datum/mind/CJ = starting_members + if(istype(CJ)) + leaders += CJ + var/datum/antagonist/gang/boss/bossdatum = new + CJ.add_antag_datum(bossdatum, src) + bossdatum.equip_gang() + next_point_time = world.time + INFLUENCE_INTERVAL + addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL) + +/datum/team/gang/Destroy() + GLOB.gangs -= src + ..() + +/datum/team/gang/roundend_report() //roundend report. + var/list/report = list() + report += "[name]:" + if(winner) + report += "The [name] gang successfully activated the mind dominator!" + else + report += "The [name] gang has failed!" + + report += "The [name] gang bosses were:" + report += printplayerlist(leaders) + report += "The [name] [member_name]s were:" + report += printplayerlist(members-leaders) + + return "
[report.Join("
")]
" + +/datum/team/gang/proc/greet_gangster(datum/mind/gangster) //The text a person receives when recruited. + to_chat(gangster, "You are now a member of the [name] Gang!") + to_chat(gangster, "Help your bosses take over the station by claiming territory with spraycans. Simply spray on any unclaimed area of the station.") + to_chat(gangster, "You can also use recruitment pens to recruit more to your cause, If your boss provides you one.") + to_chat(gangster, "Their ultimate objective is to take over the station with a Dominator machine.") + to_chat(gangster, "You can identify your mates by their large, \[G\] icon.") + gangster.store_memory("You are a member of the [name] Gang!") + +/datum/team/gang/proc/handle_territories() + next_point_time = world.time + INFLUENCE_INTERVAL + if(!leaders.len) + return + var/added_names = "" + var/lost_names = "" + + //Re-add territories that were reclaimed, so if they got tagged over, they can still earn income if they tag it back before the next status report + var/list/reclaimed_territories = new_territories & lost_territories + territories |= reclaimed_territories + new_territories -= reclaimed_territories + lost_territories -= reclaimed_territories + + //Process lost territories + for(var/area in lost_territories) + if(lost_names != "") + lost_names += ", " + lost_names += "[lost_territories[area]]" + territories -= area + + //Calculate and report influence growth + + //Process new territories + for(var/area in new_territories) + if(added_names != "") + added_names += ", " + added_names += "[new_territories[area]]" + territories += area + + //Report territory changes + var/message = "[src] Gang Status Report:.
*---------*
" + message += "[new_territories.len] new territories:
[added_names]
" + message += "[lost_territories.len] territories lost:
[lost_names]
" + //Clear the lists + new_territories = list() + lost_territories = list() + var/total_territories = total_claimable_territories() + var/control = round((territories.len/total_territories)*100, 1) + var/uniformed = check_clothing() + message += "Your gang now has [control]% control of the station.
*---------*
" + if(domination_time != NOT_DOMINATING) + var/new_time = max(world.time, domination_time - (uniformed * 4) - (territories.len * 2)) + if(new_time < domination_time) + message += "Takeover shortened by [(domination_time - new_time)*0.1] seconds for defending [territories.len] territories.
" + domination_time = new_time + message += "[domination_time_remaining()] seconds remain in hostile takeover.
" + else + var/new_influence = check_territory_income() + if(new_influence != influence) + message += "Gang influence has increased by [new_influence - influence] for defending [territories.len] territories and [uniformed] uniformed gangsters.
" + influence = new_influence + message += "Your gang now has [influence] influence.
" + message_gangtools(message) + addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL) + +/datum/team/gang/proc/total_claimable_territories() + var/list/valid_territories = list() + for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) //First, collect all area types on the station zlevel + for(var/ar in SSmapping.areas_in_z["[z]"]) + var/area/A = ar + if(!(A.type in valid_territories) && A.valid_territory) + valid_territories |= A.type + return valid_territories.len + +/datum/team/gang/proc/check_territory_income() + var/new_influence = min(999,influence + 15 + (check_clothing() * 2) + territories.len) + return new_influence + +/datum/team/gang/proc/check_clothing() + //Count uniformed gangsters + var/uniformed = 0 + for(var/datum/mind/gangmind in members) + if(ishuman(gangmind.current)) + var/mob/living/carbon/human/gangster = gangmind.current + //Gangster must be alive and should return 0 not continue if conditions are met. + if(!istype(gangster) || gangster.stat == DEAD) + return 0 + + var/obj/item/clothing/outfit + var/obj/item/clothing/gang_outfit + if(gangster.w_uniform) + outfit = gangster.w_uniform + if(outfit.type in inner_outfits) + gang_outfit = outfit + if(gangster.wear_suit) + outfit = gangster.wear_suit + if(outfit.type in outer_outfits) + gang_outfit = outfit + + if(gang_outfit) + uniformed++ + return uniformed + +/datum/team/gang/proc/adjust_influence(value) + influence = max(0, influence + value) + +/datum/team/gang/proc/message_gangtools(message) + if(!gangtools.len || !message) + return + for(var/i in gangtools) + var/obj/item/device/gangtool/tool = i + var/mob/living/mob = get(tool.loc, /mob/living) + if(mob && mob.mind && mob.stat == CONSCIOUS) + var/datum/antagonist/gang/gangster = mob.mind.has_antag_datum(/datum/antagonist/gang) + if(gangster.gang == src) + to_chat(mob, "[icon2html(tool, mob)] [message]") + playsound(mob.loc, 'sound/machines/twobeep.ogg', 50, 1) + return + +/datum/team/gang/proc/domination() + domination_time = world.time + determine_domination_time()*10 + set_security_level("delta") + +/datum/team/gang/proc/determine_domination_time() // calculates the value in seconds (this is the initial domination time!) + var/total_territories = total_claimable_territories() + return max(180,480 - (round((territories.len/total_territories)*100, 1) * 9)) + +/datum/team/gang/proc/domination_time_remaining() // retrieves the value from world.time based deciseconds to seconds + var/diff = domination_time - world.time + return round(diff * 0.1) + + +#undef MAXIMUM_RECALLS +#undef INFLUENCE_INTERVAL \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/gang_datums.dm b/modular_citadel/code/game/gamemodes/gangs/gang_datums.dm new file mode 100644 index 0000000000..334d6bf423 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gang_datums.dm @@ -0,0 +1,139 @@ +// Gang datums go here. If you want to create a new gang, you must be sure to edit: +// name +// color (must be a hex, "blue" isn't acceptable due to how spraycans are handled) +// inner_outfits (must be a list() with typepaths of the clothes in it. One is fine, but there is support for multiple: one will be picked at random when bought) +// outer_outfits (same as above) +// You also need to make a gang graffiti, that will go in crayondecal.dmi inside our icons, with the same name of the gang it's assigned to. Nothing else,just the icon. +// Those are all required. If one is missed, stuff could break. + +/datum/team/gang/clandestine + name = "Clandestine" + color = "#FF0000" + inner_outfits = list(/obj/item/clothing/under/syndicate/combat) + outer_outfits = list(/obj/item/clothing/suit/jacket) + +/datum/team/gang/prima + name = "Prima" + color = "#FFFF00" + inner_outfits = list(/obj/item/clothing/under/color/yellow) + outer_outfits = list(/obj/item/clothing/suit/hastur) + +/datum/team/gang/zerog + name = "Zero-G" + color = "#C0C0C0" + inner_outfits = list(/obj/item/clothing/under/suit_jacket/white) + outer_outfits = list(/obj/item/clothing/suit/hooded/wintercoat) + +/datum/team/gang/max + name = "Max" + color = "#800000" + inner_outfits = list(/obj/item/clothing/under/color/maroon) + outer_outfits = list(/obj/item/clothing/suit/poncho/red) + +/datum/team/gang/blasto + name = "Blasto" + color = "#000080" + inner_outfits = list(/obj/item/clothing/under/suit_jacket/navy) + outer_outfits = list(/obj/item/clothing/suit/jacket/miljacket) + +/datum/team/gang/waffle + name = "Waffle" + color = "#808000" //shared color with cyber, but they can keep brown cause waffles. + inner_outfits = list(/obj/item/clothing/under/suit_jacket/green) + outer_outfits = list(/obj/item/clothing/suit/poncho) + +/datum/team/gang/north + name = "North" + color = "#00FF00" + inner_outfits = list(/obj/item/clothing/under/color/green) + outer_outfits = list(/obj/item/clothing/suit/poncho/green) + +/datum/team/gang/omni + name = "Omni" + color = "#008080" + inner_outfits = list(/obj/item/clothing/under/color/teal) + outer_outfits = list(/obj/item/clothing/suit/studentuni) + +/datum/team/gang/newton + name = "Newton" + color = "#A52A2A" + inner_outfits = list(/obj/item/clothing/under/color/brown) + outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings) + +/datum/team/gang/cyber + name = "Cyber" + color = "#00f904" //Cyber and waffle shared colors, I made these guys green and made weed darker green. + inner_outfits = list(/obj/item/clothing/under/color/lightbrown) + outer_outfits = list(/obj/item/clothing/suit/nemes) + +/datum/team/gang/donk + name = "Donk" + color = "#0000FF" + inner_outfits = list(/obj/item/clothing/under/color/darkblue) + outer_outfits = list(/obj/item/clothing/suit/apron/overalls) + +/datum/team/gang/gene + name = "Gene" + color = "#00FFFF" + inner_outfits = list(/obj/item/clothing/under/color/blue) + outer_outfits = list(/obj/item/clothing/suit/apron) + +/datum/team/gang/gib + name = "Gib" + color = "#636060" //Applying black to grayscale... Zero-G is already grey too. oh well. + inner_outfits = list(/obj/item/clothing/under/color/black) + outer_outfits = list(/obj/item/clothing/suit/jacket/leather/overcoat) + +/datum/team/gang/tunnel + name = "Tunnel" + color = "#FF00FF" //Gave the leather jacket to the tunnel gang over diablo. + inner_outfits = list(/obj/item/clothing/under/villain) + outer_outfits = list(/obj/item/clothing/suit/jacket/leather) + +/datum/team/gang/diablo + name = "Diablo" + color = "#FF0000" //literal early 90s skinhead regalia. + inner_outfits = list(/obj/item/clothing/under/pants/classicjeans) + outer_outfits = list(/obj/item/clothing/suit/suspenders) + +/datum/team/gang/psyke + name = "Psyke" + color = "#808080" + inner_outfits = list(/obj/item/clothing/under/color/grey) + outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings/griffinwings) + +/datum/team/gang/osiron + name = "Osiron" + color = "#FFFFFF" + inner_outfits = list(/obj/item/clothing/under/color/white) + outer_outfits = list(/obj/item/clothing/suit/toggle/labcoat) + +/datum/team/gang/sirius + name = "Sirius" + color = "#FFC0CB" + inner_outfits = list(/obj/item/clothing/under/color/pink) + outer_outfits = list(/obj/item/clothing/suit/jacket/puffer/vest) + +/datum/team/gang/sleepingcarp + name = "Sleeping Carp" + color = "#800080" + inner_outfits = list(/obj/item/clothing/under/color/lightpurple) + outer_outfits = list(/obj/item/clothing/suit/hooded/carp_costume) + +/datum/team/gang/h + name = "H" + color = "#993333" + inner_outfits = list(/obj/item/clothing/under/jabroni) //Why not? + outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings) + +/datum/team/gang/rigatonifamily + name = "Rigatoni family" + color = "#cc9900" // p a s t a colored + inner_outfits = list(/obj/item/clothing/under/rank/chef) + outer_outfits = list(/obj/item/clothing/suit/apron/chef) + +/datum/team/gang/weed + name = "Weed" + color = "#6cd648" + inner_outfits = list(/obj/item/clothing/under/color/darkgreen) + outer_outfits = list(/obj/item/clothing/suit/vapeshirt) \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/gang_decals.dm b/modular_citadel/code/game/gamemodes/gangs/gang_decals.dm new file mode 100644 index 0000000000..6e5cb58891 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gang_decals.dm @@ -0,0 +1,38 @@ +/obj/effect/decal/cleanable/crayon/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null) + . = ..() + if(type == "poseur tag") + var/datum/team/gang/gang = pick(subtypesof(/datum/team/gang)) + var/gangname = initial(gang.name) + icon = 'icons/effects/crayondecal.dmi' + icon_state = "[gangname]" + type = null + +/obj/effect/decal/cleanable/crayon/gang + icon = 'icons/effects/crayondecal.dmi' + layer = ABOVE_NORMAL_TURF_LAYER //Harder to hide + plane = GAME_PLANE + do_icon_rotate = FALSE //These are designed to always face south, so no rotation please. + var/datum/team/gang/gang + +/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, datum/team/gang/G, e_name = "gang tag", rotation = 0, mob/user) + if(!G) + return INITIALIZE_HINT_QDEL + gang = G + var/newcolor = G.color + var/area/territory = get_area(src) + icon_state = G.name + G.new_territories |= list(territory.type = territory.name) + //If this isn't tagged by a specific gangster there's no bonus income. + .=..(mapload, newcolor, icon_state, e_name, rotation) + +/obj/effect/decal/cleanable/crayon/gang/Destroy() + if(gang) + var/area/territory = get_area(src) + gang.territories -= territory.type + gang.new_territories -= territory.type + gang.lost_territories |= list(territory.type = territory.name) + gang = null + return ..() + +/obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T) + return isspaceturf(T) || islava(T) || istype(T, /turf/open/water) || ischasm(T) \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/gang_hud.dm b/modular_citadel/code/game/gamemodes/gangs/gang_hud.dm new file mode 100644 index 0000000000..3fde6d4123 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gang_hud.dm @@ -0,0 +1,34 @@ +/datum/atom_hud/antag/gang + var/color = null + +/datum/atom_hud/antag/gang/add_to_hud(atom/A) + if(!A) + return + var/image/holder = A.hud_list[ANTAG_HUD] + if(holder) + holder.color = color + ..() + +/datum/atom_hud/antag/gang/remove_from_hud(atom/A) + if(!A) + return + var/image/holder = A.hud_list[ANTAG_HUD] + if(holder) + holder.color = null + ..() + +/datum/atom_hud/antag/gang/join_hud(mob/M) + if(!istype(M)) + CRASH("join_hud(): [M] ([M.type]) is not a mob!") + var/image/holder = M.hud_list[ANTAG_HUD] + if(holder) + holder.color = color + ..() + +/datum/atom_hud/antag/gang/leave_hud(mob/M) + if(!istype(M)) + CRASH("leave_hud(): [M] ([M.type]) is not a mob!") + var/image/holder = M.hud_list[ANTAG_HUD] + if(holder) + holder.color = null + ..() \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/gang_items.dm b/modular_citadel/code/game/gamemodes/gangs/gang_items.dm new file mode 100644 index 0000000000..0f16b6462b --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gang_items.dm @@ -0,0 +1,416 @@ +/datum/gang_item + var/name + var/item_path + var/cost + var/spawn_msg + var/category + var/list/gang_whitelist = list() + var/list/gang_blacklist = list() + var/id + +/datum/gang_item/proc/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool, check_canbuy = TRUE) + if(check_canbuy && !can_buy(user, gang, gangtool)) + return FALSE + var/real_cost = get_cost(user, gang, gangtool) + if(!spawn_item(user, gang, gangtool)) + gang.adjust_influence(-real_cost) + to_chat(user, "You bought \the [name].") + return TRUE + +/datum/gang_item/proc/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) // If this returns anything other than null, something fucked up and influence won't lower. + if(item_path) + var/obj/item/O = new item_path(user.loc) + user.put_in_hands(O) + else + return TRUE + if(spawn_msg) + to_chat(user, "[spawn_msg]") + +/datum/gang_item/proc/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + return gang && (gang.influence >= get_cost(user, gang, gangtool)) && can_see(user, gang, gangtool) + +/datum/gang_item/proc/can_see(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + return TRUE + +/datum/gang_item/proc/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + return cost + +/datum/gang_item/proc/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + return "([get_cost(user, gang, gangtool)] Influence)" + +/datum/gang_item/proc/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + return name + +/datum/gang_item/proc/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + return + +/////////////////// +//CLOTHING +/////////////////// + +/datum/gang_item/clothing + category = "Purchase Gang Clothes (Only the jumpsuit and suit give you added influence):" + +/datum/gang_item/clothing/under + name = "Gang Uniform" + id = "under" + cost = 1 + +/datum/gang_item/clothing/under/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(gang.inner_outfits.len) + var/outfit = pick(gang.inner_outfits) + if(outfit) + var/obj/item/O = new outfit(user.loc) + user.put_in_hands(O) + to_chat(user, " This is your gang's official uniform, wearing it will increase your influence") + return + return TRUE + +/datum/gang_item/clothing/suit + name = "Gang Armored Outerwear" + id = "suit" + cost = 1 + +/datum/gang_item/clothing/suit/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(gang.outer_outfits.len) + var/outfit = pick(gang.outer_outfits) + if(outfit) + var/obj/item/O = new outfit(user.loc) + O.armor = O.armor.setRating(melee = 25, bullet = 35, laser = 15, energy = 10, bomb = 30, bio = 0, rad = 0, fire = 30, acid = 30) + O.desc += " Tailored for the [gang.name] Gang to offer the wearer moderate protection against ballistics and physical trauma." + user.put_in_hands(O) + to_chat(user, " This is your gang's official outerwear, wearing it will increase your influence") + return + return TRUE + + +/datum/gang_item/clothing/hat + name = "Pimp Hat" + id = "hat" + cost = 16 + item_path = /obj/item/clothing/head/collectable/petehat/gang + + +/obj/item/clothing/head/collectable/petehat/gang + name = "pimpin' hat" + desc = "The undisputed king of style." + +/datum/gang_item/clothing/mask + name = "Golden Death Mask" + id = "mask" + cost = 18 + item_path = /obj/item/clothing/mask/gskull + +/obj/item/clothing/mask/gskull + name = "golden death mask" + icon_state = "gskull" + desc = "Strike terror, and envy, into the hearts of your enemies." + +/datum/gang_item/clothing/shoes + name = "Bling Boots" + id = "boots" + cost = 22 + item_path = /obj/item/clothing/shoes/gang + +/obj/item/clothing/shoes/gang + name = "blinged-out boots" + desc = "Stand aside peasants." + icon_state = "bling" + +/datum/gang_item/clothing/neck + name = "Gold Necklace" + id = "necklace" + cost = 9 + item_path = /obj/item/clothing/neck/necklace/dope + +/datum/gang_item/clothing/hands + name = "Decorative Brass Knuckles" + id = "hand" + cost = 11 + item_path = /obj/item/clothing/gloves/gang + +/obj/item/clothing/gloves/gang + name = "braggadocio's brass knuckles" + desc = "Purely decorative, don't find out the hard way." + icon_state = "knuckles" + w_class = 3 + +datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang member anyways? + name = "Cool Sunglasses" + id = "glasses" + cost = 5 + item_path = /obj/item/clothing/glasses/sunglasses + +/datum/gang_item/clothing/belt + name = "Badass Belt" + id = "belt" + cost = 13 + item_path = /obj/item/storage/belt/military/gang + +/obj/item/storage/belt/military/gang + name = "badass belt" + icon_state = "gangbelt" + item_state = "gang" + desc = "The belt buckle simply reads 'BAMF'." + +/////////////////// +//WEAPONS +/////////////////// + +/datum/gang_item/weapon + category = "Purchase Weapons:" + +/datum/gang_item/weapon/ammo + +/datum/gang_item/weapon/shuriken + name = "Shuriken" + id = "shuriken" + cost = 2 + item_path = /obj/item/throwing_star + +/datum/gang_item/weapon/switchblade + name = "Switchblade" + id = "switchblade" + cost = 5 + item_path = /obj/item/switchblade + +/datum/gang_item/weapon/surplus //For when a gang boss is extra broke or cheap. + name = "Surplus Rifle" + id = "surplus" + cost = 6 + item_path = /obj/item/gun/ballistic/automatic/surplus + +/datum/gang_item/weapon/ammo/surplus_ammo + name = "Surplus Rifle Ammo" + id = "surplus_ammo" + cost = 3 + item_path = /obj/item/ammo_box/magazine/m10mm/rifle + +/datum/gang_item/weapon/improvised + name = "Sawn-Off Improvised Shotgun" + id = "sawn" + cost = 5 + item_path = /obj/item/gun/ballistic/revolver/doublebarrel/improvised/sawn + +/datum/gang_item/weapon/ammo/improvised_ammo + name = "Box of Buckshot" + id = "buckshot" + cost = 5 + item_path = /obj/item/storage/box/lethalshot + +/datum/gang_item/weapon/pistol + name = "10mm Pistol" + id = "pistol" + cost = 25 + item_path = /obj/item/gun/ballistic/automatic/pistol + +/datum/gang_item/weapon/ammo/pistol_ammo + name = "10mm Ammo" + id = "pistol_ammo" + cost = 10 + item_path = /obj/item/ammo_box/magazine/m10mm + +/datum/gang_item/weapon/sniper + name = "Black Market .50cal Sniper Rifle" + id = "sniper" + cost = 35 + item_path = /obj/item/gun/ballistic/automatic/sniper_rifle + +/datum/gang_item/weapon/ammo/sniper_ammo + name = "Smuggled .50cal Sniper Rounds" + id = "sniper_ammo" + cost = 15 + item_path = /obj/item/ammo_box/magazine/sniper_rounds + +/datum/gang_item/weapon/ammo/sleeper_ammo + name = "Illicit Soporific Cartridges" + id = "sniper_ammo" + cost = 15 + item_path = /obj/item/ammo_box/magazine/sniper_rounds/soporific + +/datum/gang_item/weapon/machinegun + name = "Mounted Machine Gun" + id = "MG" + cost = 45 + item_path = /obj/machinery/manned_turret + spawn_msg = "The mounted machine gun features enhanced responsiveness. Hold down on the trigger while firing to control where you're shooting." + +/datum/gang_item/weapon/machinegun/spawn_item(mob/living/carbon/user, obj/item/device/gangtool/gangtool) + new item_path(user.loc) + to_chat(user, spawn_msg) + +/datum/gang_item/weapon/uzi + name = "Uzi SMG" + id = "uzi" + cost = 50 + item_path = /obj/item/gun/ballistic/automatic/mini_uzi + +/datum/gang_item/weapon/ammo/uzi_ammo + name = "Uzi Ammo" + id = "uzi_ammo" + cost = 20 + item_path = /obj/item/ammo_box/magazine/uzim9mm + +/////////////////// +//EQUIPMENT +/////////////////// + +/datum/gang_item/equipment + category = "Purchase Equipment:" + + +/datum/gang_item/equipment/spraycan + name = "Territory Spraycan" + id = "spraycan" + cost = 1 + item_path = /obj/item/toy/crayon/spraycan/gang + +/datum/gang_item/equipment/spraycan/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + var/obj/item/O = new item_path(user.loc, gang) + user.put_in_hands(O) + +/datum/gang_item/equipment/sharpener + name = "Sharpener" + id = "whetstone" + cost = 3 + item_path = /obj/item/sharpener + + +/datum/gang_item/equipment/emp + name = "EMP Grenade" + id = "EMP" + cost = 7 + item_path = /obj/item/grenade/empgrenade + +/datum/gang_item/equipment/c4 + name = "C4 Explosive" + id = "c4" + cost = 7 + item_path = /obj/item/grenade/plastic/c4 + +/datum/gang_item/equipment/frag + name = "Fragmentation Grenade" + id = "frag nade" + cost = 5 + item_path = /obj/item/grenade/syndieminibomb/concussion/frag + +/datum/gang_item/equipment/stimpack + name = "Black Market Stimulants" + id = "stimpack" + cost = 12 + item_path = /obj/item/reagent_containers/syringe/stimulants + +/datum/gang_item/equipment/implant_breaker + name = "Implant Breaker" + id = "implant_breaker" + cost = 10 + item_path = /obj/item/implanter/gang + +/datum/gang_item/equipment/implant_breaker/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + var/obj/item/O = new item_path(user.loc, gang) + user.put_in_hands(O) + to_chat(user, "The implant breaker is a single-use device that destroys all implants within the target before trying to recruit them to your gang. Also works on enemy gangsters.") + +/datum/gang_item/equipment/wetwork_boots + name = "Wetwork boots" + id = "wetwork" + cost = 8 + item_path = /obj/item/clothing/shoes/combat/gang + +/obj/item/clothing/shoes/combat/gang + name = "Wetwork boots" + desc = "A gang's best hitmen are prepared for anything." + permeability_coefficient = 0.01 + clothing_flags = NOSLIP + +datum/gang_item/equipment/shield + name = "Riot Shield" + id = "riot_shield" + cost = 25 + item_path = /obj/item/shield/riot + +/datum/gang_item/equipment/pen + name = "Recruitment Pen" + id = "pen" + cost = 20 + item_path = /obj/item/pen/gang + spawn_msg = "More recruitment pens will allow you to recruit gangsters faster. Only gang leaders can recruit with pens." + +/datum/gang_item/equipment/pen/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(..()) + gangtool.free_pen = FALSE + return TRUE + return FALSE + +/datum/gang_item/equipment/pen/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(gangtool && gangtool.free_pen) + return 0 + return ..() + +/datum/gang_item/equipment/pen/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(gangtool && gangtool.free_pen) + return "(GET ONE FREE)" + return ..() + + +/datum/gang_item/equipment/gangtool + id = "gangtool" + cost = 5 + +/datum/gang_item/equipment/gangtool/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + var/item_type + if(gang) + item_type = /obj/item/device/gangtool/spare/lt + if(gang.leaders.len < MAX_LEADERS_GANG) + to_chat(user, "Gangtools allow you to promote a gangster to be your Lieutenant, enabling them to recruit and purchase items like you. Simply have them register the gangtool. You may promote up to [MAX_LEADERS_GANG-gang.leaders.len] more Lieutenants") + else + item_type = /obj/item/device/gangtool/spare + var/obj/item/device/gangtool/spare/tool = new item_type(user.loc) + user.put_in_hands(tool) + +/datum/gang_item/equipment/gangtool/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(gang && (gang.leaders.len < gang.max_leaders)) + return "Promote a Gangster" + return "Spare Gangtool" + +/datum/gang_item/equipment/dominator + name = "Station Dominator" + id = "dominator" + cost = 30 + item_path = /obj/machinery/dominator + spawn_msg = "The dominator will secure your gang's dominance over the station. Turn it on when you are ready to defend it." + +/datum/gang_item/equipment/dominator/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(!gang || !gang.dom_attempts) + return FALSE + return ..() + +/datum/gang_item/equipment/dominator/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(!gang || !gang.dom_attempts) + return ..() + return "[..()]" + +/datum/gang_item/equipment/dominator/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(!gang || !gang.dom_attempts) + return "(Out of stock)" + return ..() + +/datum/gang_item/equipment/dominator/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + if(gang) + return "This device requires a 5x5 area clear of walls to FUNCTION. (Estimated Takeover Time: [round(gang.determine_domination_time()/60,0.1)] minutes)" + +/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + var/area/userarea = get_area(user) + if(!(userarea.type in gang.territories|gang.new_territories)) + to_chat(user,"The dominator can be spawned only on territory controlled by your gang!") + return FALSE + for(var/obj/obj in get_turf(user)) + if(obj.density) + to_chat(user, "There's not enough room here!") + return FALSE + + return ..() + +/datum/gang_item/equipment/dominator/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) + new item_path(user.loc) + to_chat(user, spawn_msg) \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/gang_pen.dm b/modular_citadel/code/game/gamemodes/gangs/gang_pen.dm new file mode 100644 index 0000000000..3dfdf4dd9b --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gang_pen.dm @@ -0,0 +1,59 @@ +/* + * Gang Boss Pens + */ +/obj/item/pen/gang + var/cooldown + var/last_used + +/obj/item/pen/gang/Initialize() + . = ..() + last_used = world.time + +/obj/item/pen/gang/attack(mob/living/M, mob/user, stealth = TRUE) //ha + if(!istype(M)) + return + if(!ishuman(M) || !ishuman(user) || M.stat == DEAD) + return ..() + //var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss) //Pen works with bosses only. + var/datum/antagonist/gang/L = user.mind.has_antag_datum(/datum/antagonist/gang) //Pen works with anyone in gang. + if(!L) + return ..() + if(!..()) + return + if(cooldown) + to_chat(user, "[src] needs more time to recharge before it can be used.") + return + if(!M.client || !M.mind) + to_chat(user, "A braindead gangster is an useless gangster!") + return + var/datum/team/gang/gang = L.gang + if(!add_gangster(user, gang, M.mind)) + return + cooldown = TRUE + icon_state = "pen_blink" + var/cooldown_time = 600/gang.leaders.len + addtimer(CALLBACK(src, .proc/cooldown), cooldown_time) + +/obj/item/pen/gang/proc/cooldown() + cooldown = FALSE + icon_state = "pen" + var/mob/M = loc + if(istype(M)) + to_chat(M, "[icon2html(src, M)] [src][(loc == M)?(""):(" in your [loc]")] vibrates softly. It is ready to be used again.") + +/obj/item/pen/gang/proc/add_gangster(mob/user, datum/team/gang/gang, datum/mind/gangster_mind, check = TRUE) // Basically a wrapper to add_antag_datum. + var/datum/antagonist/dudegang = gangster_mind.has_antag_datum(/datum/antagonist/gang) + if(dudegang) + if(dudegang == gang) + to_chat(user, "This mind is already controlled by your gang!") + return + to_chat(user, "This mind is already controlled by someone else!") + return + if(check && gangster_mind.current.has_trait(TRAIT_MINDSHIELD)) //Check to see if the potential gangster is implanted + to_chat(user, "This mind is too strong to control!") + return + var/mob/living/carbon/human/H = gangster_mind.current // we are sure the dude's human cause it's checked in attack() + H.silent = max(H.silent, 5) + H.Knockdown(100) + gangster_mind.add_antag_datum(/datum/antagonist/gang, gang) + return TRUE \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/gangs.dm b/modular_citadel/code/game/gamemodes/gangs/gangs.dm new file mode 100644 index 0000000000..9151107d6f --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gangs.dm @@ -0,0 +1,65 @@ +//gang.dm +//Gang War Game Mode +GLOBAL_LIST_INIT(possible_gangs, subtypesof(/datum/team/gang)) +GLOBAL_LIST_EMPTY(gangs) +/datum/game_mode/gang + name = "gang war" + config_tag = "gang" + antag_flag = ROLE_GANG + restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security") + required_players = 15 + required_enemies = 0 + recommended_enemies = 2 + enemy_minimum_age = 14 + + announce_span = "danger" + announce_text = "A violent turf war has erupted on the station!\n\ + Gangsters: Take over the station with a dominator.\n\ + Crew: Prevent the gangs from expanding and initiating takeover." + + var/list/datum/mind/gangboss_candidates = list() + +/datum/game_mode/gang/generate_report() + return "Cybersun Industries representatives claimed that they, in joint research with the Tiger Cooperative, have made a major breakthrough in brainwashing technology, and have \ + made the nanobots that apply the \"conversion\" very small and capable of fitting into usually innocent objects - namely, pens. While they refused to outsource this technology for \ + months to come due to its flaws, they reported some as missing but passed it off to carelessness. At Central Command, we don't like mysteries, and we have reason to believe that this \ + technology was stolen for anti-Nanotrasen use. Be on the lookout for territory claims and unusually violent crew behavior, applying mindshield implants as necessary." + +/datum/game_mode/gang/pre_setup() + if(CONFIG_GET(flag/protect_roles_from_antagonist)) + restricted_jobs += protected_jobs + + if(CONFIG_GET(flag/protect_assistant_from_antagonist)) + restricted_jobs += "Assistant" + + //Spawn more bosses depending on server population + var/gangs_to_create = 2 + if(prob(num_players()) && num_players() > 1.5*required_players) + gangs_to_create++ + if(prob(num_players()) && num_players() > 2*required_players) + gangs_to_create++ + gangs_to_create = min(gangs_to_create, GLOB.possible_gangs.len) + + for(var/i in 1 to gangs_to_create) + if(!antag_candidates.len) + break + + //Now assign a boss for the gang + var/datum/mind/boss = pick_n_take(antag_candidates) + antag_candidates -= boss + gangboss_candidates += boss + boss.restricted_roles = restricted_jobs + + if(gangboss_candidates.len < 1) //Need at least one gangs + return + + return TRUE + +/datum/game_mode/gang/post_setup() + set waitfor = FALSE + ..() + for(var/i in gangboss_candidates) + var/datum/mind/M = i + var/datum/antagonist/gang/boss/B = new() + M.add_antag_datum(B) + B.equip_gang() diff --git a/modular_citadel/code/game/gamemodes/gangs/gangtool.dm b/modular_citadel/code/game/gamemodes/gangs/gangtool.dm new file mode 100644 index 0000000000..9e828a7042 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/gangtool.dm @@ -0,0 +1,259 @@ +//gangtool device +/obj/item/device/gangtool + name = "suspicious device" + desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it." + icon = 'icons/obj/device.dmi' + icon_state = "gangtool" + item_state = "radio" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + flags_1 = CONDUCT_1 + var/datum/team/gang/gang //Which gang uses this? + var/recalling = 0 + var/outfits = 2 + var/free_pen = 0 + var/promotable = FALSE + var/static/list/buyable_items = list() + var/list/tags = list() + +/obj/item/device/gangtool/Initialize() + . = ..() + update_icon() + for(var/i in subtypesof(/datum/gang_item)) + var/datum/gang_item/G = i + var/id = initial(G.id) + var/cat = initial(G.category) + if(id) + if(!islist(buyable_items[cat])) + buyable_items[cat] = list() + buyable_items[cat][id] = new G +/obj/item/device/gangtool/Destroy() + if(gang) + gang.gangtools -= src + return ..() + +/obj/item/device/gangtool/attack_self(mob/user) + ..() + if (!can_use(user)) + return + var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss) + var/dat + if(!gang) + dat += "This device is not registered.

" + if(L) + if(promotable && L.gang.leaders.len < L.gang.max_leaders) + dat += "Give this device to another member of your organization to use to promote them to Lieutenant.

" + dat += "If this is meant as a spare device for yourself:
" + dat += "Register Device as Spare
" + else if(promotable) + var/datum/antagonist/gang/sweet = user.mind.has_antag_datum(/datum/antagonist/gang) + if(sweet.gang.leaders.len < sweet.gang.max_leaders) + dat += "You have been selected for a promotion!
" + dat += "Accept Promotion
" + else + dat += "No promotions available: All positions filled.
" + else + dat += "This device is not authorized to promote.
" + else + if(gang.domination_time != NOT_DOMINATING) + dat += "
Takeover In Progress:
[DisplayTimeText(gang.domination_time_remaining() * 10)] remain
" + + dat += "Registration: [gang.name] Gang Boss
" + dat += "Organization Size: [gang.members.len] | Station Control: [gang.territories.len] territories under control. | Influence: [gang.influence]
" + dat += "Time until Influence grows: [time2text(gang.next_point_time - world.time, "mm:ss")]
" + dat += "Send message to Gang
" + dat += "Recall shuttle
" + dat += "
" + for(var/cat in buyable_items) + dat += "[cat]
" + for(var/id in buyable_items[cat]) + var/datum/gang_item/G = buyable_items[cat][id] + if(!G.can_see(user, gang, src)) + continue + + var/cost = G.get_cost_display(user, gang, src) + if(cost) + dat += cost + " " + + var/toAdd = G.get_name_display(user, gang, src) + if(G.can_buy(user, gang, src)) + toAdd = "[toAdd]" + dat += toAdd + var/extra = G.get_extra_info(user, gang, src) + if(extra) + dat += "
[extra]" + dat += "
" + dat += "
" + + dat += "Refresh
" + + var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v4.0", 340, 625) + popup.set_content(dat) + popup.open() + +/obj/item/device/gangtool/Topic(href, href_list) + if(!can_use(usr)) + return + + add_fingerprint(usr) + + if(href_list["register"]) + register_device(usr) + + else if(!gang) //Gangtool must be registered before you can use the functions below + return + + if(href_list["purchase"]) + if(islist(buyable_items[href_list["cat"]])) + var/list/L = buyable_items[href_list["cat"]] + var/datum/gang_item/G = L[href_list["id"]] + if(G && G.can_buy(usr, gang, src)) + G.purchase(usr, gang, src, FALSE) + + if(href_list["commute"]) + ping_gang(usr) + if(href_list["recall"]) + recall(usr) + attack_self(usr) + +/obj/item/device/gangtool/update_icon() + overlays.Cut() + var/image/I = new(icon, "[icon_state]-overlay") + if(gang) + I.color = gang.color + overlays.Add(I) + +/obj/item/device/gangtool/proc/ping_gang(mob/user) + if(!can_use(user)) + return + var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message") as null|text + if(!message || !can_use(user)) + return + if(!is_station_level(user.z)) + to_chat(user, "[icon2html(src, user)]Error: Station out of range.") + return + if(gang.members.len) + var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) + if(!G) + return + var/ping = "[gang.name] [G.message_name] [user.real_name]: [message]" + for(var/datum/mind/ganger in gang.members) + if(ganger.current && is_station_level(ganger.current.z) && (ganger.current.stat == CONSCIOUS)) + to_chat(ganger.current, ping) + for(var/mob/M in GLOB.dead_mob_list) + var/link = FOLLOW_LINK(M, user) + to_chat(M, "[link] [ping]") + user.log_talk(message,LOG_SAY, tag="[gang.name] gangster") + +/obj/item/device/gangtool/proc/register_device(mob/user) + if(gang) //It's already been registered! + return + var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) + if(G) + gang = G.gang + gang.gangtools += src + update_icon() + if(!(user.mind in gang.leaders) && promotable) + G.promote() + free_pen = TRUE + gang.message_gangtools("[user] has been promoted to Lieutenant.") + to_chat(user, "The Gangtool you registered will allow you to purchase weapons and equipment, and send messages to your gang.") + to_chat(user, "Unlike regular gangsters, you may use recruitment pens to add recruits to your gang. Use them on unsuspecting crew members to recruit them. Don't forget to get your one free pen from the gangtool.") + else + to_chat(user, "ACCESS DENIED: Unauthorized user.") + +/obj/item/device/gangtool/proc/recall(mob/user) + if(!recallchecks(user)) + return + if(recalling) + to_chat(user, "Error: Recall already in progress.") + return + gang.message_gangtools("[user] is attempting to recall the emergency shuttle.") + recalling = TRUE + to_chat(user, "[icon2html(src, loc)]Generating shuttle recall order with codes retrieved from last call signal...") + addtimer(CALLBACK(src, .proc/recall2, user), rand(100,300)) + +/obj/item/device/gangtool/proc/recall2(mob/user) + if(!recallchecks(user)) + return + to_chat(user, "[icon2html(src, loc)]Shuttle recall order generated. Accessing station long-range communication arrays...") + addtimer(CALLBACK(src, .proc/recall3, user), rand(100,300)) + +/obj/item/device/gangtool/proc/recall3(mob/user) + if(!recallchecks(user)) + return + var/list/living_crew = list()//shamelessly copied from mulligan code, there should be a helper for this + for(var/mob/Player in GLOB.mob_list) + if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) && !isbrain(Player) && Player.client) + living_crew += Player + var/malc = CONFIG_GET(number/midround_antag_life_check) + if(living_crew.len / GLOB.joined_player_list.len <= malc) //Shuttle cannot be recalled if too many people died + to_chat(user, "[icon2html(src, user)]Error: Station communication systems compromised. Unable to establish connection.") + recalling = FALSE + return + to_chat(user, "[icon2html(src, loc)]Comm arrays accessed. Broadcasting recall signal...") + addtimer(CALLBACK(src, .proc/recallfinal, user), rand(100,300)) + +/obj/item/device/gangtool/proc/recallfinal(mob/user) + if(!recallchecks(user)) + return + recalling = FALSE + log_game("[key_name(user)] has tried to recall the shuttle with a gangtool.") + message_admins("[key_name_admin(user)] has tried to recall the shuttle with a gangtool.", 1) + if(SSshuttle.cancelEvac(user)) + gang.recalls-- + return TRUE + + to_chat(user, "[icon2html(src, loc)]No response recieved. Emergency shuttle cannot be recalled at this time.") + return + +/obj/item/device/gangtool/proc/recallchecks(mob/user) + if(!can_use(user)) + return + if(SSshuttle.emergencyNoRecall) + return + if(!gang.recalls) + to_chat(user, "Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.") + return + if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station + to_chat(user, "[icon2html(src, user)]Emergency shuttle cannot be recalled at this time.") + recalling = FALSE + return + if(!gang.dom_attempts) + to_chat(user, "[icon2html(src, user)]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.") + recalling = FALSE + return + if(!is_station_level(user.z)) //Shuttle can only be recalled while on station + to_chat(user, "[icon2html(src, user)]Error: Device out of range of station communication arrays.") + recalling = FALSE + return + return TRUE + +/obj/item/device/gangtool/proc/can_use(mob/living/carbon/human/user) + if(!istype(user)) + return + if(user.incapacitated()) + return + if(!(src in user.contents)) + return + if(!user.mind) + return + var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) + if(!G) + to_chat(user, "Huh, what's this?") + return + if(!isnull(gang) && G.gang != gang) + to_chat(user, "You cannot use gang tools owned by enemy gangs!") + return + return TRUE + + +/obj/item/device/gangtool/spare + outfits = TRUE + +/obj/item/device/gangtool/spare/lt + promotable = TRUE \ No newline at end of file diff --git a/modular_citadel/code/game/gamemodes/gangs/implant_gang.dm b/modular_citadel/code/game/gamemodes/gangs/implant_gang.dm new file mode 100644 index 0000000000..ee91928845 --- /dev/null +++ b/modular_citadel/code/game/gamemodes/gangs/implant_gang.dm @@ -0,0 +1,61 @@ +/obj/item/implant/gang + name = "gang implant" + desc = "Makes you a gangster or such." + activated = 0 + var/datum/team/gang/gang + +/obj/item/implant/gang/Initialize(loc, setgang) + .=..() + gang = setgang + +/obj/item/implant/gang/Destroy() + gang = null + return ..() + +/obj/item/implant/gang/get_data() + var/dat = {"Implant Specifications:
+ Name: Criminal brainwash implant
+ Life: A few seconds after injection.
+ Important Notes: Illegal
+
+ Implant Details:
+ Function: Contains a small pod of nanobots that change the host's brain to be loyal to a certain organization.
+ Special Features: This device will also emit a small EMP pulse, destroying any other implants within the host's brain.
+ Integrity: Implant's EMP function will destroy itself in the process."} + return dat + +/obj/item/implant/gang/implant(mob/living/target, mob/user, silent = 0) + if(!target || !target.mind || target.stat == DEAD) + return 0 + var/datum/antagonist/gang/G = target.mind.has_antag_datum(/datum/antagonist/gang) + if(G && G.gang == G) + return 0 // it's pointless + if(..()) + for(var/obj/item/implant/I in target.implants) + if(I != src) + qdel(I) + + if(ishuman(target)) + var/success + if(G) + if(!istype(G, /datum/antagonist/gang/boss)) + success = TRUE //Was not a gang boss, convert as usual + target.mind.remove_antag_datum(/datum/antagonist/gang) + else + success = TRUE + if(!success) + target.visible_message("[target] seems to resist the implant!", "You feel the influence of your enemies try to invade your mind!") + return FALSE + target.mind.add_antag_datum(/datum/antagonist/gang, gang) + qdel(src) + return TRUE + +/obj/item/implanter/gang + name = "implanter (gang)" + +/obj/item/implanter/gang/Initialize(loc, gang) + if(!gang) + qdel(src) + return + imp = new /obj/item/implant/gang(src,gang) + .=..() \ No newline at end of file diff --git a/modular_citadel/code/game/machinery/cryopod.dm b/modular_citadel/code/game/machinery/cryopod.dm index 1ff628bfbd..15e7d34c65 100644 --- a/modular_citadel/code/game/machinery/cryopod.dm +++ b/modular_citadel/code/game/machinery/cryopod.dm @@ -245,43 +245,6 @@ // This function can not be undone; do not call this unless you are sure /obj/machinery/cryopod/proc/despawn_occupant() var/mob/living/mob_occupant = occupant - var/list/target_candidates = list() - - if(istype(SSticker.mode, /datum/antagonist/cult))//thank - if("sacrifice" in SSticker.mode.cult) - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && !is_convertable_to_cult(player) && player.stat != DEAD) - target_candidates += player.mind - - target_candidates -= mob_occupant.mind - - if(target_candidates.len == 0) - message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.") - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && player.stat != DEAD) - target_candidates += player.mind - - listclearnulls(target_candidates) - if(LAZYLEN(target_candidates)) - var/datum/objective/sacrifice/sac_objective = locate() in GLOB.objectives - sac_objective.target = pick(target_candidates) - sac_objective.update_explanation_text() - - var/datum/job/sacjob = SSjob.GetJob(sac_objective.target.assigned_role) - var/datum/preferences/sacface = sac_objective.target.current.client.prefs - var/icon/reshape = get_flat_human_icon(null, sacjob, sacface) - reshape.Shift(SOUTH, 4) - reshape.Shift(EAST, 1) - reshape.Crop(7,4,26,31) - reshape.Crop(-5,-3,26,30) - sac_objective.sac_image = reshape - - for(var/datum/mind/H in SSticker.mode.cult) - if(H.current) - to_chat(H.current, "Nar'Sie murmurs, [occupant] is beyond your reach. Sacrifice [sac_objective.target.current] instead...
") - - else - message_admins("Cult Sacrifice: Could not find unconvertable or convertable target after cryopod. WELP!") //Update any existing objectives involving this mob. for(var/datum/objective/O in GLOB.objectives) diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index ef9201af60..846a8cff62 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -40,7 +40,7 @@ adjustArousalLoss(arousal_rate * S.arousal_gain_rate) if(dna.features["exhibitionist"] && client) var/amt_nude = 0 - if(is_chest_exposed() && (gender == FEMALE || getorganslot("breasts"))) + if(is_chest_exposed() && (getorganslot("breasts"))) amt_nude++ if(is_groin_exposed()) if(getorganslot("penis")) @@ -64,14 +64,14 @@ /mob/living/proc/adjustArousalLoss(amount, updating_arousal=1) if(status_flags & GODMODE || !canbearoused) - return 0 + return FALSE arousalloss = CLAMP(arousalloss + amount, min_arousal, max_arousal) if(updating_arousal) updatearousal() /mob/living/proc/setArousalLoss(amount, updating_arousal=1) if(status_flags & GODMODE || !canbearoused) - return 0 + return FALSE arousalloss = CLAMP(amount, min_arousal, max_arousal) if(updating_arousal) updatearousal() @@ -99,6 +99,8 @@ switch(G.type) if(/obj/item/organ/genital/penis) S = GLOB.cock_shapes_list[G.shape] + if(/obj/item/organ/genital/testicles) + S = GLOB.balls_shapes_list[G.shape] if(/obj/item/organ/genital/vagina) S = GLOB.vagina_shapes_list[G.shape] if(/obj/item/organ/genital/breasts) @@ -112,54 +114,54 @@ G.update_appearance() /mob/living/proc/update_arousal_hud() - return 0 + return FALSE /datum/species/proc/update_arousal_hud(mob/living/carbon/human/H) - return 0 + return FALSE /mob/living/carbon/human/update_arousal_hud() if(!client || !hud_used) - return 0 + return FALSE if(dna.species.update_arousal_hud()) - return 0 + return FALSE if(!canbearoused) hud_used.arousal.icon_state = "" - return 0 + return FALSE else if(hud_used.arousal) if(stat == DEAD) hud_used.arousal.icon_state = "arousal0" - return 1 + return TRUE if(getArousalLoss() == max_arousal) hud_used.arousal.icon_state = "arousal100" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 90)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal90" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 80)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal80" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 70)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal70" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 60)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal60" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 50)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal50" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 40)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal40" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 30)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal30" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 20)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal10" - return 1 + return TRUE if(getArousalLoss() >= (max_arousal / 100) * 10)//M O D U L A R , W O W hud_used.arousal.icon_state = "arousal10" - return 1 + return TRUE else hud_used.arousal.icon_state = "arousal0" @@ -171,11 +173,11 @@ /obj/screen/arousal/Click() if(!isliving(usr)) - return 0 + return FALSE var/mob/living/M = usr if(M.canbearoused) M.mob_climax() - return 1 + return TRUE else to_chat(M, "Arousal is disabled. Feature is unavailable.") @@ -196,13 +198,6 @@ "You have relieved yourself.") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) setArousalLoss(min_arousal) - /* - switch(gender) - if(MALE) - PoolOrNew(/obj/effect/decal/cleanable/semen, loc) - if(FEMALE) - PoolOrNew(/obj/effect/decal/cleanable/femcum, loc) - */ else to_chat(src, "You aren't aroused enough for that.") diff --git a/modular_citadel/code/modules/arousal/organs/breasts.dm b/modular_citadel/code/modules/arousal/organs/breasts.dm index 1239a515cd..1223f0b616 100644 --- a/modular_citadel/code/modules/arousal/organs/breasts.dm +++ b/modular_citadel/code/modules/arousal/organs/breasts.dm @@ -5,7 +5,6 @@ icon = 'modular_citadel/icons/obj/genitals/breasts.dmi' zone = "chest" slot = "breasts" - w_class = 3 size = BREASTS_SIZE_DEF fluid_id = "milk" var/amount = 2 @@ -14,7 +13,7 @@ can_masturbate_with = TRUE masturbation_verb = "massage" can_climax = TRUE - fluid_transfer_factor =0.5 + fluid_transfer_factor = 0.5 /obj/item/organ/genital/breasts/Initialize() . = ..() @@ -40,6 +39,10 @@ switch(lowershape) if("pair") desc = "You see a pair of breasts." + if("quad") + desc = "You see two pairs of breast, one just under the other." + if("sextuple") + desc = "You see three sets of breasts, running from their chest to their belly." else desc = "You see some breasts, they seem to be quite exotic." if (size) @@ -54,13 +57,11 @@ if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow... var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need. color = "#[skintone2hex(H.skin_tone)]" - string = "breasts_[lowertext(shape)]_[size]-s" + string = "breasts_[GLOB.breasts_shapes_icons[shape]]_[size]-s" else color = "#[owner.dna.features["breasts_color"]]" - string = "breasts_[lowertext(shape)]_[size]" + string = "breasts_[GLOB.breasts_shapes_icons[shape]]_[size]" if(ishuman(owner)) var/mob/living/carbon/human/H = owner + icon_state = sanitize_text(string) H.update_genitals() - - icon_state = sanitize_text(string) - diff --git a/modular_citadel/code/modules/arousal/organs/eggsack.dm b/modular_citadel/code/modules/arousal/organs/eggsack.dm index 27104cd36a..402d246e40 100644 --- a/modular_citadel/code/modules/arousal/organs/eggsack.dm +++ b/modular_citadel/code/modules/arousal/organs/eggsack.dm @@ -6,7 +6,6 @@ zone = "groin" slot = "testicles" color = null //don't use the /genital color since it already is colored - w_class = 3 internal = TRUE var/egg_girth = EGG_GIRTH_DEF var/cum_mult = CUM_RATE_MULT diff --git a/modular_citadel/code/modules/arousal/organs/genitals.dm b/modular_citadel/code/modules/arousal/organs/genitals.dm index 591bb6f1de..cbf14e420a 100644 --- a/modular_citadel/code/modules/arousal/organs/genitals.dm +++ b/modular_citadel/code/modules/arousal/organs/genitals.dm @@ -1,25 +1,26 @@ /obj/item/organ/genital color = "#fcccb3" - var/shape = "human" - var/sensitivity = 1 - var/list/genital_flags = list() - var/can_masturbate_with = FALSE - var/masturbation_verb = "masturbate" - var/can_climax = FALSE - var/fluid_transfer_factor = 0.0 //How much would a partner get in them if they climax using this? - var/size = 2 //can vary between num or text, just used in icon_state strings - var/fluid_id = null - var/fluid_max_volume = 50 - var/fluid_efficiency = 1 - var/fluid_rate = 1 - var/fluid_mult = 1 - var/producing = FALSE - var/aroused_state = FALSE //Boolean used in icon_state strings - var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons. + w_class = WEIGHT_CLASS_NORMAL + var/shape = "human" + var/sensitivity = AROUSAL_START_VALUE + var/list/genital_flags = list() + var/can_masturbate_with = FALSE + var/masturbation_verb = "masturbate" + var/can_climax = FALSE + var/fluid_transfer_factor = 0.0 //How much would a partner get in them if they climax using this? + var/size = 2 //can vary between num or text, just used in icon_state strings + var/fluid_id = null + var/fluid_max_volume = 50 + var/fluid_efficiency = 1 + var/fluid_rate = 1 + var/fluid_mult = 1 + var/producing = FALSE + var/aroused_state = FALSE //Boolean used in icon_state strings + var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons. var/obj/item/organ/genital/linked_organ - var/through_clothes = FALSE - var/internal = FALSE - var/hidden = FALSE + var/through_clothes = FALSE + var/internal = FALSE + var/hidden = FALSE /obj/item/organ/genital/Initialize() . = ..() @@ -140,14 +141,14 @@ if (NOGENITALS in dna.species.species_traits) return //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in. - if(dna.features["has_breasts"]) - give_breasts() if(dna.features["has_vag"]) give_vagina() if(dna.features["has_womb"]) give_womb() if(dna.features["has_balls"]) give_balls() + if(dna.features["has_breasts"]) // since we have multi-boobs as a thing, we'll want to at least draw over these. but not over the pingas. + give_breasts() if(dna.features["has_cock"]) give_penis() if(dna.features["has_ovi"]) @@ -165,7 +166,7 @@ P.Insert(src) if(P) if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) - P.color = skintone2hex(skin_tone) + P.color = "#[skintone2hex(skin_tone)]" else P.color = "#[dna.features["cock_color"]]" P.length = dna.features["cock_length"] @@ -181,13 +182,18 @@ if(!getorganslot("testicles")) var/obj/item/organ/genital/testicles/T = new T.Insert(src) -// if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) -// T.color = skintone2hex(skin_tone) -// else -// T.color = "#[dna.features["balls_color"]]" if(T) + if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) + T.color = "#[skintone2hex(skin_tone)]" + else + T.color = "#[dna.features["balls_color"]]" T.size = dna.features["balls_size"] T.sack_size = dna.features["balls_sack_size"] + T.shape = dna.features["balls_shape"] + if(dna.features["balls_shape"] == "Hidden") + T.internal = TRUE + else + T.internal = FALSE T.fluid_id = dna.features["balls_fluid"] T.fluid_rate = dna.features["balls_cum_rate"] T.fluid_mult = dna.features["balls_cum_mult"] @@ -204,7 +210,7 @@ B.Insert(src) if(B) if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) - B.color = skintone2hex(skin_tone) + B.color = "#[skintone2hex(skin_tone)]" else B.color = "#[dna.features["breasts_color"]]" B.size = dna.features["breasts_size"] @@ -228,7 +234,7 @@ V.Insert(src) if(V) if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) - V.color = skintone2hex(skin_tone) + V.color = "#[skintone2hex(skin_tone)]" else V.color = "[dna.features["vag_color"]]" V.shape = "[dna.features["vag_shape"]]" @@ -311,6 +317,8 @@ switch(G.type) if(/obj/item/organ/genital/penis) S = GLOB.cock_shapes_list[G.shape] + if(/obj/item/organ/genital/testicles) + S = GLOB.balls_shapes_list[G.shape] if(/obj/item/organ/genital/vagina) S = GLOB.vagina_shapes_list[G.shape] if(/obj/item/organ/genital/breasts) @@ -318,6 +326,7 @@ if(!S || S.icon_state == "none") continue + var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer) genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]" @@ -331,12 +340,15 @@ switch(S.color_src) if("cock_color") genital_overlay.color = "#[H.dna.features["cock_color"]]" + if("balls_color") + genital_overlay.color = "#[H.dna.features["balls_color"]]" if("breasts_color") genital_overlay.color = "#[H.dna.features["breasts_color"]]" if("vag_color") genital_overlay.color = "#[H.dna.features["vag_color"]]" standing += genital_overlay + if(LAZYLEN(standing)) H.overlays_standing[layer] = standing.Copy() standing = list() diff --git a/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm b/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm index e857c0d7ed..b913a90fb6 100644 --- a/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm +++ b/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm @@ -55,8 +55,24 @@ center = TRUE //Center the image 'cause 2-tile wide. dimension_x = 64 +//Testicles +//These ones aren't inert +/datum/sprite_accessory/testicles + icon = 'modular_citadel/icons/obj/genitals/testicles_onmob.dmi' + icon_state = "testicle" + name = "testicle" //the preview name of the accessory + color_src = "balls_color" + locked = 0 +/datum/sprite_accessory/testicles/hidden + icon_state = "hidden" + name = "Hidden" + alt_aroused = TRUE +/datum/sprite_accessory/testicles/single + icon_state = "single" + name = "Single" + alt_aroused = TRUE //Vaginas /datum/sprite_accessory/vagina @@ -109,6 +125,15 @@ name = "Pair" alt_aroused = TRUE +/datum/sprite_accessory/breasts/quad + icon_state = "quad" + name = "Quad" + alt_aroused = TRUE + +/datum/sprite_accessory/breasts/sextuple + icon_state = "sextuple" + name = "Sextuple" + alt_aroused = TRUE //OVIPOSITORS BE HERE /datum/sprite_accessory/ovipositor diff --git a/modular_citadel/code/modules/arousal/organs/penis.dm b/modular_citadel/code/modules/arousal/organs/penis.dm index ac812e286d..b6cb8fa4b2 100644 --- a/modular_citadel/code/modules/arousal/organs/penis.dm +++ b/modular_citadel/code/modules/arousal/organs/penis.dm @@ -5,14 +5,13 @@ icon = 'modular_citadel/icons/obj/genitals/penis.dmi' zone = "groin" slot = ORGAN_SLOT_PENIS - w_class = 3 can_masturbate_with = TRUE masturbation_verb = "stroke" can_climax = TRUE - fluid_transfer_factor = 0.5 + fluid_transfer_factor = 0.5 size = 2 //arbitrary value derived from length and girth for sprites. var/length = 6 //inches - var/cached_length //used to detect a change in length + var/cached_length //used to detect a change in length var/girth = 0 var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF @@ -52,15 +51,15 @@ string = "penis_[GLOB.cock_shapes_icons[shape]]_[size]" if(ishuman(owner)) var/mob/living/carbon/human/H = owner + icon_state = sanitize_text(string) H.update_genitals() - icon_state = sanitize_text(string) - /obj/item/organ/genital/penis/update_link() if(owner) linked_organ = (owner.getorganslot("testicles")) if(linked_organ) linked_organ.linked_organ = src + linked_organ.size = size else if(linked_organ) linked_organ.linked_organ = null diff --git a/modular_citadel/code/modules/arousal/organs/testicles.dm b/modular_citadel/code/modules/arousal/organs/testicles.dm index 815d8034e7..0cf698392c 100644 --- a/modular_citadel/code/modules/arousal/organs/testicles.dm +++ b/modular_citadel/code/modules/arousal/organs/testicles.dm @@ -1,17 +1,20 @@ /obj/item/organ/genital/testicles - name = "testicles" - desc = "A male reproductive organ." - icon_state = "testicles" - icon = 'modular_citadel/icons/obj/genitals/penis.dmi' - zone = "groin" - slot = "testicles" - w_class = 3 - internal = TRUE - size = BALLS_SIZE_DEF - var/sack_size = BALLS_SACK_SIZE_DEF - fluid_id = "semen" - producing = TRUE - var/sent_full_message = 1 //defaults to 1 since they're full to start + name = "testicles" + desc = "A male reproductive organ." + icon_state = "testicles" + icon = 'modular_citadel/icons/obj/genitals/testicles.dmi' + zone = "groin" + slot = "testicles" + size = BALLS_SIZE_MIN + var/size_name = "average" + shape = "single" + var/sack_size = BALLS_SACK_SIZE_DEF + fluid_id = "semen" + producing = TRUE + can_masturbate_with = TRUE + masturbation_verb = "massage" + can_climax = TRUE + var/sent_full_message = TRUE //defaults to 1 since they're full to start /obj/item/organ/genital/testicles/Initialize() . = ..() @@ -28,9 +31,9 @@ if(reagents.total_volume >= reagents.maximum_volume) if(!sent_full_message) send_full_message() - sent_full_message = 1 + sent_full_message = TRUE return FALSE - sent_full_message = 0 + sent_full_message = FALSE update_link() if(!linked_organ) return FALSE @@ -42,6 +45,7 @@ linked_organ = (owner.getorganslot("penis")) if(linked_organ) linked_organ.linked_organ = src + else if(linked_organ) linked_organ.linked_organ = null @@ -49,6 +53,34 @@ /obj/item/organ/genital/testicles/proc/send_full_message(msg = "Your balls finally feel full, again.") if(owner && istext(msg)) - owner << msg + to_chat(owner, msg) return TRUE +/obj/item/organ/genital/testicles/update_appearance() + if(owner) + if(size == 0) + size_name = "nonexistant" + if(size == 1) + size_name = "average" + if(size == 2) + size_name = "enlarged" + if(size >= 3) + size_name = "engorged" + + if(!internal) + desc = "You see an [size_name] pair of testicles dangling." + else + desc = "They don't have any testicles you can see." + var/string + if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"]) + if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow... + var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need. + color = "#[skintone2hex(H.skin_tone)]" + string = "testicles_[GLOB.balls_shapes_icons[shape]]_[size]-s" + else + color = "#[owner.dna.features["balls_color"]]" + string = "testicles_[GLOB.balls_shapes_icons[shape]]_[size]" + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + icon_state = sanitize_text(string) + H.update_genitals() diff --git a/modular_citadel/code/modules/arousal/organs/vagina.dm b/modular_citadel/code/modules/arousal/organs/vagina.dm index b8ef7029b6..8c15aa5437 100644 --- a/modular_citadel/code/modules/arousal/organs/vagina.dm +++ b/modular_citadel/code/modules/arousal/organs/vagina.dm @@ -60,10 +60,9 @@ string = "vagina" if(ishuman(owner)) var/mob/living/carbon/human/H = owner + icon_state = sanitize_text(string) H.update_genitals() - icon_state = sanitize_text(string) - /obj/item/organ/genital/vagina/update_link() if(owner) linked_organ = (owner.getorganslot("womb")) diff --git a/modular_citadel/code/modules/arousal/organs/womb.dm b/modular_citadel/code/modules/arousal/organs/womb.dm index c59d74e629..686d9059a0 100644 --- a/modular_citadel/code/modules/arousal/organs/womb.dm +++ b/modular_citadel/code/modules/arousal/organs/womb.dm @@ -5,12 +5,10 @@ icon_state = "womb" zone = "groin" slot = "womb" - w_class = 3 internal = TRUE fluid_id = "femcum" producing = TRUE - /obj/item/organ/genital/womb/Initialize() . = ..() reagents.add_reagent(fluid_id, fluid_max_volume) diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm index c0a4df2243..70f58b3159 100644 --- a/modular_citadel/code/modules/client/loadout/__donator.dm +++ b/modular_citadel/code/modules/client/loadout/__donator.dm @@ -373,4 +373,16 @@ datum/gear/darksabresheath name = "French Beret" category = SLOT_HEAD path = /obj/item/clothing/head/frenchberet - ckeywhitelist = list("notazoltan") \ No newline at end of file + ckeywhitelist = list("notazoltan") + +/datum/gear/zuliecloak + name = "Project: Zul-E" + category = SLOT_WEAR_SUIT + path = /obj/item/clothing/suit/hooded/cloak/zuliecloak + ckeywhitelist = list("asky") + +/datum/gear/blackredgold + name = "Black, Red, and Gold Coat" + category = SLOT_WEAR_SUIT + path = /obj/item/clothing/suit/blackredgold + ckeywhitelist = list("ttbnc") \ No newline at end of file diff --git a/modular_citadel/code/modules/client/preferences_savefile.dm b/modular_citadel/code/modules/client/preferences_savefile.dm index 209d46db61..2921f70684 100644 --- a/modular_citadel/code/modules/client/preferences_savefile.dm +++ b/modular_citadel/code/modules/client/preferences_savefile.dm @@ -56,6 +56,7 @@ WRITE_FILE(S["feature_has_balls"], features["has_balls"]) WRITE_FILE(S["feature_balls_color"], features["balls_color"]) WRITE_FILE(S["feature_balls_size"], features["balls_size"]) + WRITE_FILE(S["feature_balls_shape"], features["balls_shape"]) WRITE_FILE(S["feature_balls_sack_size"], features["balls_sack_size"]) WRITE_FILE(S["feature_balls_fluid"], features["balls_fluid"]) //breasts features diff --git a/modular_citadel/code/modules/custom_loadout/custom_items.dm b/modular_citadel/code/modules/custom_loadout/custom_items.dm index b7f7aea0e8..e1b1110cf8 100644 --- a/modular_citadel/code/modules/custom_loadout/custom_items.dm +++ b/modular_citadel/code/modules/custom_loadout/custom_items.dm @@ -454,3 +454,34 @@ icon_state = "moveralls" mutantrace_variation = NO_MUTANTRACE_VARIATION +/obj/item/clothing/suit/hooded/cloak/zuliecloak + name = "Project: Zul-E" + desc = "A standard version of a prototype cloak given out by Nanotrasen higher ups. It's surprisingly thick and heavy for a cloak despite having most of it's tech stripped. It also comes with a bluespace trinket which calls it's accompanying hat onto the user. A worn inscription on the inside of the cloak reads 'Fleuret' ...the rest is faded away." + icon_state = "zuliecloak" + item_state = "zuliecloak" + icon = 'icons/obj/custom.dmi' + alternate_worn_icon = 'icons/mob/custom_w.dmi' + hoodtype = /obj/item/clothing/head/hooded/cloakhood/zuliecloak + body_parts_covered = CHEST|GROIN|ARMS + slot_flags = SLOT_WEAR_SUIT | ITEM_SLOT_NECK //it's a cloak. it's cosmetic. so why the hell not? what could possibly go wrong? + mutantrace_variation = NO_MUTANTRACE_VARIATION + +/obj/item/clothing/head/hooded/cloakhood/zuliecloak + name = "NT Special Issue" + desc = "This hat is unquestionably the best one, bluespaced to and from CentComm. It smells of Fish and Tea with a hint of antagonism" + icon_state = "zuliecap" + item_state = "zuliecap" + icon = 'icons/obj/custom.dmi' + alternate_worn_icon = 'icons/mob/custom_w.dmi' + flags_inv = HIDEEARS|HIDEHAIR + mutantrace_variation = NO_MUTANTRACE_VARIATION + +/obj/item/clothing/suit/blackredgold + name = "Multicolor Coat" + desc = "An oddly special looking coat with black, red, and gold" + icon = 'icons/obj/custom.dmi' + alternate_worn_icon = 'icons/mob/custom_w.dmi' + icon_state = "redgoldjacket" + item_state = "redgoldjacket" + body_parts_covered = CHEST|GROIN|LEGS|ARMS + mutantrace_variation = NO_MUTANTRACE_VARIATION \ No newline at end of file diff --git a/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm b/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm index a4532d0820..e552c2cbdd 100644 --- a/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -959,6 +959,13 @@ name = "Eevee" icon_state = "eevee" + +/datum/sprite_accessory/mam_ears/elf + name = "Elf" + icon_state = "elf" + color_src = MUTCOLORS3 + + /datum/sprite_accessory/mam_ears/elephant name = "Elephant" icon_state = "elephant" @@ -1497,6 +1504,12 @@ datum/sprite_accessory/mam_tails/insect icon_state = "cow" taur_mode = HOOF_TAURIC +/datum/sprite_accessory/taur/deer + name = "Deer" + icon_state = "deer" + taur_mode = HOOF_TAURIC + color_src = MUTCOLORS + /datum/sprite_accessory/taur/drake name = "Drake" icon_state = "drake" diff --git a/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm b/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm index f0e5c35a8b..5e10e71433 100644 --- a/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm +++ b/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm @@ -1,390 +1,389 @@ -/* -DOG BORG EQUIPMENT HERE -SLEEPER CODE IS IN game/objects/items/devices/dogborg_sleeper.dm ! -*/ - -/obj/item/dogborg/jaws - name = "Dogborg jaws" - desc = "The jaws of the debug errors oh god." - icon = 'icons/mob/dogborg.dmi' - flags_1 = CONDUCT_1 - force = 1 - throwforce = 0 - w_class = 3 - hitsound = 'sound/weapons/bite.ogg' - sharpness = IS_SHARP - -/obj/item/dogborg/jaws/big - name = "combat jaws" - desc = "The jaws of the law. Very sharp." - icon_state = "jaws" - force = 12 - attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced") - -/obj/item/dogborg/jaws/small - name = "puppy jaws" - desc = "Rubberized teeth designed to protect accidental harm. Sharp enough for specialized tasks however." - icon_state = "smalljaws" - force = 6 - attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed") - var/status = 0 - -/obj/item/dogborg/jaws/attack(atom/A, mob/living/silicon/robot/user) - ..() - user.do_attack_animation(A, ATTACK_EFFECT_BITE) - log_combat(user, A, "bit") - -/obj/item/dogborg/jaws/small/attack_self(mob/user) - var/mob/living/silicon/robot.R = user - if(R.cell && R.cell.charge > 100) - if(R.emagged && status == 0) - name = "combat jaws" - icon_state = "jaws" - desc = "The jaws of the law." - force = 12 - attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced") - status = 1 - to_chat(user, "Your jaws are now [status ? "Combat" : "Pup'd"].") - else - name = "puppy jaws" - icon_state = "smalljaws" - desc = "The jaws of a small dog." - force = 5 - attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed") - status = 0 - if(R.emagged) - to_chat(user, "Your jaws are now [status ? "Combat" : "Pup'd"].") - update_icon() - -//Boop - -/obj/item/analyzer/nose - name = "boop module" - icon = 'icons/mob/dogborg.dmi' - icon_state = "nose" - desc = "The BOOP module" - flags_1 = CONDUCT_1 - force = 0 - throwforce = 0 - attack_verb = list("nuzzles", "pushes", "boops") - w_class = 1 - -/obj/item/analyzer/nose/attack_self(mob/user) - user.visible_message("[user] sniffs around the air.", "You sniff the air for gas traces.") - - var/turf/location = user.loc - if(!istype(location)) - return - - var/datum/gas_mixture/environment = location.return_air() - - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles() - - to_chat(user, "Results:") - if(abs(pressure - ONE_ATMOSPHERE) < 10) - to_chat(user, "Pressure: [round(pressure,0.1)] kPa") - else - to_chat(user, "Pressure: [round(pressure,0.1)] kPa") - if(total_moles) - var/list/env_gases = environment.gases - - environment.assert_gases(arglist(GLOB.hardcoded_gases)) - var/o2_concentration = env_gases[/datum/gas/oxygen][MOLES]/total_moles - var/n2_concentration = env_gases[/datum/gas/nitrogen][MOLES]/total_moles - var/co2_concentration = env_gases[/datum/gas/carbon_dioxide][MOLES]/total_moles - var/plasma_concentration = env_gases[/datum/gas/plasma][MOLES]/total_moles - environment.garbage_collect() - - if(abs(n2_concentration - N2STANDARD) < 20) - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] %") - else - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] %") - - if(abs(o2_concentration - O2STANDARD) < 2) - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] %") - else - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] %") - - if(co2_concentration > 0.01) - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] %") - else - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] %") - - if(plasma_concentration > 0.005) - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] %") - else - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] %") - - - for(var/id in env_gases) - if(id in GLOB.hardcoded_gases) - continue - var/gas_concentration = env_gases[id][MOLES]/total_moles - to_chat(user, "[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_concentration*100, 0.01)] %") - to_chat(user, "Temperature: [round(environment.temperature-T0C)] °C") - -/obj/item/analyzer/nose/AltClick(mob/user) //Barometer output for measuring when the next storm happens - . = ..() - -/obj/item/analyzer/nose/afterattack(atom/target, mob/user, proximity) - . = ..() - if(!proximity) - return - do_attack_animation(target, null, src) - user.visible_message("[user] [pick(attack_verb)] \the [target.name] with their nose!") - -//Delivery -/obj/item/storage/bag/borgdelivery - name = "fetching storage" - desc = "Fetch the thing!" - icon = 'icons/mob/dogborg.dmi' - icon_state = "dbag" - w_class = WEIGHT_CLASS_BULKY - -/obj/item/storage/bag/borgdelivery/ComponentInitialize() - . = ..() - GET_COMPONENT(STR, /datum/component/storage) - STR.max_w_class = WEIGHT_CLASS_BULKY - STR.max_combined_w_class = 5 - STR.max_items = 1 - STR.cant_hold = typecacheof(list(/obj/item/disk/nuclear, /obj/item/radio/intercom)) - -//Tongue stuff -/obj/item/soap/tongue - name = "synthetic tongue" - desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face." - icon = 'icons/mob/dogborg.dmi' - icon_state = "synthtongue" - hitsound = 'sound/effects/attackblob.ogg' - cleanspeed = 80 - var/status = 0 - -/obj/item/soap/tongue/scrubpup - cleanspeed = 25 //slightly faster than a mop. - -/obj/item/soap/tongue/New() - ..() - item_flags |= NOBLUDGEON //No more attack messages - -/obj/item/soap/tongue/attack_self(mob/user) - var/mob/living/silicon/robot.R = user - if(R.cell && R.cell.charge > 100) - if(R.emagged && status == 0) - status = !status - name = "energized tongue" - desc = "Your tongue is energized for dangerously maximum efficency." - icon_state = "syndietongue" - to_chat(user, "Your tongue is now [status ? "Energized" : "Normal"].") - cleanspeed = 10 //(nerf'd)tator soap stat - else - status = 0 - name = "synthetic tongue" - desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face." - icon_state = "synthtongue" - cleanspeed = initial(cleanspeed) - if(R.emagged) - to_chat(user, "Your tongue is now [status ? "Energized" : "Normal"].") - update_icon() - -/obj/item/soap/tongue/afterattack(atom/target, mob/user, proximity) - var/mob/living/silicon/robot.R = user - if(!proximity || !check_allowed_items(target)) - return - if(R.client && (target in R.client.screen)) - to_chat(R, "You need to take that [target.name] off before cleaning it!") - else if(is_cleanable(target)) - R.visible_message("[R] begins to lick off \the [target.name].", "You begin to lick off \the [target.name]...") - if(do_after(R, src.cleanspeed, target = target)) - if(!in_range(src, target)) //Proximity is probably old news by now, do a new check. - return //If they moved away, you can't eat them. - to_chat(R, "You finish licking off \the [target.name].") - qdel(target) - R.cell.give(50) - else if(isobj(target)) //hoo boy. danger zone man - if(istype(target,/obj/item/trash)) - R.visible_message("[R] nibbles away at \the [target.name].", "You begin to nibble away at \the [target.name]...") - if(!do_after(R, src.cleanspeed, target = target)) - return //If they moved away, you can't eat them. - to_chat(R, "You finish off \the [target.name].") - qdel(target) - R.cell.give(250) - return - if(istype(target,/obj/item/stock_parts/cell)) - R.visible_message("[R] begins cramming \the [target.name] down its throat.", "You begin cramming \the [target.name] down your throat...") - if(!do_after(R, 50, target = target)) - return //If they moved away, you can't eat them. - to_chat(R, "You finish off \the [target.name].") - var/obj/item/stock_parts/cell.C = target - R.cell.charge = R.cell.charge + (C.charge / 3) //Instant full cell upgrades op idgaf - qdel(target) - return - var/obj/item/I = target //HAHA FUCK IT, NOT LIKE WE ALREADY HAVE A SHITTON OF WAYS TO REMOVE SHIT - if(!I.anchored && R.emagged) - R.visible_message("[R] begins chewing up \the [target.name]. Looks like it's trying to loophole around its diet restriction!", "You begin chewing up \the [target.name]...") - if(!do_after(R, 100, target = I)) //Nerf dat time yo - return //If they moved away, you can't eat them. - visible_message("[R] chews up \the [target.name] and cleans off the debris!") - to_chat(R, "You finish off \the [target.name].") - qdel(I) - R.cell.give(500) - return - R.visible_message("[R] begins to lick \the [target.name] clean...", "You begin to lick \the [target.name] clean...") - else if(ishuman(target)) - var/mob/living/L = target - if(status == 0 && check_zone(R.zone_selected) == "head") - R.visible_message("\the [R] affectionally licks \the [L]'s face!", "You affectionally lick \the [L]'s face!") - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) - if(istype(L) && L.fire_stacks > 0) - L.adjust_fire_stacks(-10) - return - else if(status == 0) - R.visible_message("\the [R] affectionally licks \the [L]!", "You affectionally lick \the [L]!") - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) - if(istype(L) && L.fire_stacks > 0) - L.adjust_fire_stacks(-10) - return - else - if(R.cell.charge <= 800) - to_chat(R, "Insufficent Power!") - return - L.Stun(4) // normal stunbaton is force 7 gimme a break good sir! - L.Knockdown(80) - L.apply_effect(EFFECT_STUTTER, 4) - L.visible_message("[R] has shocked [L] with its tongue!", \ - "[R] has shocked you with its tongue!") - playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - R.cell.use(666) - log_combat(R, L, "tongue stunned") - - else if(istype(target, /obj/structure/window)) - R.visible_message("[R] begins to lick \the [target.name] clean...", "You begin to lick \the [target.name] clean...") - if(do_after(user, src.cleanspeed, target = target)) - to_chat(user, "You clean \the [target.name].") - target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) - target.set_opacity(initial(target.opacity)) - else - R.visible_message("[R] begins to lick \the [target.name] clean...", "You begin to lick \the [target.name] clean...") - if(do_after(user, src.cleanspeed, target = target)) - to_chat(user, "You clean \the [target.name].") - var/obj/effect/decal/cleanable/C = locate() in target - qdel(C) - target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) - SEND_SIGNAL(target, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM) - target.wash_cream() - return - -//Dogfood - -/obj/item/trash/rkibble - name = "robo kibble" - desc = "A novelty bowl of assorted mech fabricator byproducts. Mockingly feed this to the sec-dog to help it recharge." - icon = 'icons/mob/dogborg.dmi' - icon_state= "kibble" - -//Defibs - -/obj/item/twohanded/shockpaddles/cyborg/hound - name = "Paws of Life" - desc = "MediHound specific shock paws." - icon = 'icons/mob/dogborg.dmi' - icon_state = "defibpaddles0" - item_state = "defibpaddles0" - -// Pounce stuff for K-9 - -/obj/item/dogborg/pounce - name = "pounce" - icon = 'icons/mob/dogborg.dmi' - icon_state = "pounce" - desc = "Leap at your target to momentarily stun them." - force = 0 - throwforce = 0 - -/obj/item/dogborg/pounce/New() - ..() - item_flags |= NOBLUDGEON - -/mob/living/silicon/robot - var/leaping = 0 - var/pounce_cooldown = 0 - var/pounce_cooldown_time = 50 //Nearly doubled, u happy? - var/pounce_spoolup = 3 - var/leap_at - var/disabler - var/laser - var/sleeper_g - var/sleeper_r - var/sleeper_nv - -#define MAX_K9_LEAP_DIST 4 //because something's definitely borked the pounce functioning from a distance. - -/obj/item/dogborg/pounce/afterattack(atom/A, mob/user) - var/mob/living/silicon/robot/R = user - if(R && !R.pounce_cooldown) - R.pounce_cooldown = !R.pounce_cooldown - to_chat(R, "Your targeting systems lock on to [A]...") - addtimer(CALLBACK(R, /mob/living/silicon/robot.proc/leap_at, A), R.pounce_spoolup) - spawn(R.pounce_cooldown_time) - R.pounce_cooldown = !R.pounce_cooldown - else if(R && R.pounce_cooldown) - to_chat(R, "Your leg actuators are still recharging!") - -/mob/living/silicon/robot/proc/leap_at(atom/A) - if(leaping || stat || buckled || lying) - return - - if(!has_gravity(src) || !has_gravity(A)) - to_chat(src,"It is unsafe to leap without gravity!") - //It's also extremely buggy visually, so it's balance+bugfix - return - - if(cell.charge <= 500) - to_chat(src,"Insufficent reserves for jump actuators!") - return - - else - leaping = 1 - weather_immunities += "lava" - pixel_y = 10 - update_icons() - throw_at(A, MAX_K9_LEAP_DIST, 1, spin=0, diagonals_first = 1) - cell.use(500) //Doubled the energy consumption - weather_immunities -= "lava" - -/mob/living/silicon/robot/throw_impact(atom/A) - - if(!leaping) - return ..() - - if(A) - if(isliving(A)) - var/mob/living/L = A - var/blocked = 0 - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.check_shields(0, "the [name]", src, attack_type = LEAP_ATTACK)) - blocked = 1 - if(!blocked) - L.visible_message("[src] pounces on [L]!", "[src] pounces on you!") - L.Knockdown(iscarbon(L) ? 450 : 45) // Temporary. If someone could rework how dogborg pounces work to accomodate for combat changes, that'd be nice. - playsound(src, 'sound/weapons/Egloves.ogg', 50, 1) - sleep(2)//Runtime prevention (infinite bump() calls on hulks) - step_towards(src,L) - log_combat(src, L, "borg pounced") - else - Knockdown(45, 1, 1) - - pounce_cooldown = !pounce_cooldown - spawn(pounce_cooldown_time) //3s by default - pounce_cooldown = !pounce_cooldown - else if(A.density && !A.CanPass(src)) - visible_message("[src] smashes into [A]!", "You smash into [A]!") - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - Knockdown(45, 1, 1) - - if(leaping) - leaping = 0 - pixel_y = initial(pixel_y) - update_icons() - update_canmove() +/* +DOG BORG EQUIPMENT HERE +SLEEPER CODE IS IN game/objects/items/devices/dogborg_sleeper.dm ! +*/ + +/obj/item/dogborg/jaws + name = "Dogborg jaws" + desc = "The jaws of the debug errors oh god." + icon = 'icons/mob/dogborg.dmi' + flags_1 = CONDUCT_1 + force = 1 + throwforce = 0 + w_class = 3 + hitsound = 'sound/weapons/bite.ogg' + sharpness = IS_SHARP + +/obj/item/dogborg/jaws/big + name = "combat jaws" + desc = "The jaws of the law. Very sharp." + icon_state = "jaws" + force = 12 + attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced") + +/obj/item/dogborg/jaws/small + name = "puppy jaws" + desc = "Rubberized teeth designed to protect accidental harm. Sharp enough for specialized tasks however." + icon_state = "smalljaws" + force = 6 + attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed") + var/status = 0 + +/obj/item/dogborg/jaws/attack(atom/A, mob/living/silicon/robot/user) + ..() + user.do_attack_animation(A, ATTACK_EFFECT_BITE) + log_combat(user, A, "bit") + +/obj/item/dogborg/jaws/small/attack_self(mob/user) + var/mob/living/silicon/robot.R = user + if(R.cell && R.cell.charge > 100) + if(R.emagged && status == 0) + name = "combat jaws" + icon_state = "jaws" + desc = "The jaws of the law." + force = 12 + attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced") + status = 1 + to_chat(user, "Your jaws are now [status ? "Combat" : "Pup'd"].") + else + name = "puppy jaws" + icon_state = "smalljaws" + desc = "The jaws of a small dog." + force = 5 + attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed") + status = 0 + if(R.emagged) + to_chat(user, "Your jaws are now [status ? "Combat" : "Pup'd"].") + update_icon() + +//Boop + +/obj/item/analyzer/nose + name = "boop module" + icon = 'icons/mob/dogborg.dmi' + icon_state = "nose" + desc = "The BOOP module" + flags_1 = CONDUCT_1 + force = 0 + throwforce = 0 + attack_verb = list("nuzzles", "pushes", "boops") + w_class = 1 + +/obj/item/analyzer/nose/attack_self(mob/user) + user.visible_message("[user] sniffs around the air.", "You sniff the air for gas traces.") + + var/turf/location = user.loc + if(!istype(location)) + return + + var/datum/gas_mixture/environment = location.return_air() + + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + + to_chat(user, "Results:") + if(abs(pressure - ONE_ATMOSPHERE) < 10) + to_chat(user, "Pressure: [round(pressure,0.1)] kPa") + else + to_chat(user, "Pressure: [round(pressure,0.1)] kPa") + if(total_moles) + var/list/env_gases = environment.gases + + var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles + var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles + var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles + var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles + GAS_GARBAGE_COLLECT(environment.gases) + + if(abs(n2_concentration - N2STANDARD) < 20) + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] %") + else + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] %") + + if(abs(o2_concentration - O2STANDARD) < 2) + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] %") + else + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] %") + + if(co2_concentration > 0.01) + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] %") + else + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] %") + + if(plasma_concentration > 0.005) + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] %") + else + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] %") + + + for(var/id in env_gases) + if(id in GLOB.hardcoded_gases) + continue + var/gas_concentration = env_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] %") + to_chat(user, "Temperature: [round(environment.temperature-T0C)] °C") + +/obj/item/analyzer/nose/AltClick(mob/user) //Barometer output for measuring when the next storm happens + . = ..() + +/obj/item/analyzer/nose/afterattack(atom/target, mob/user, proximity) + . = ..() + if(!proximity) + return + do_attack_animation(target, null, src) + user.visible_message("[user] [pick(attack_verb)] \the [target.name] with their nose!") + +//Delivery +/obj/item/storage/bag/borgdelivery + name = "fetching storage" + desc = "Fetch the thing!" + icon = 'icons/mob/dogborg.dmi' + icon_state = "dbag" + w_class = WEIGHT_CLASS_BULKY + +/obj/item/storage/bag/borgdelivery/ComponentInitialize() + . = ..() + GET_COMPONENT(STR, /datum/component/storage) + STR.max_w_class = WEIGHT_CLASS_BULKY + STR.max_combined_w_class = 5 + STR.max_items = 1 + STR.cant_hold = typecacheof(list(/obj/item/disk/nuclear, /obj/item/radio/intercom)) + +//Tongue stuff +/obj/item/soap/tongue + name = "synthetic tongue" + desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face." + icon = 'icons/mob/dogborg.dmi' + icon_state = "synthtongue" + hitsound = 'sound/effects/attackblob.ogg' + cleanspeed = 80 + var/status = 0 + +/obj/item/soap/tongue/scrubpup + cleanspeed = 25 //slightly faster than a mop. + +/obj/item/soap/tongue/New() + ..() + item_flags |= NOBLUDGEON //No more attack messages + +/obj/item/soap/tongue/attack_self(mob/user) + var/mob/living/silicon/robot.R = user + if(R.cell && R.cell.charge > 100) + if(R.emagged && status == 0) + status = !status + name = "energized tongue" + desc = "Your tongue is energized for dangerously maximum efficency." + icon_state = "syndietongue" + to_chat(user, "Your tongue is now [status ? "Energized" : "Normal"].") + cleanspeed = 10 //(nerf'd)tator soap stat + else + status = 0 + name = "synthetic tongue" + desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face." + icon_state = "synthtongue" + cleanspeed = initial(cleanspeed) + if(R.emagged) + to_chat(user, "Your tongue is now [status ? "Energized" : "Normal"].") + update_icon() + +/obj/item/soap/tongue/afterattack(atom/target, mob/user, proximity) + var/mob/living/silicon/robot.R = user + if(!proximity || !check_allowed_items(target)) + return + if(R.client && (target in R.client.screen)) + to_chat(R, "You need to take that [target.name] off before cleaning it!") + else if(is_cleanable(target)) + R.visible_message("[R] begins to lick off \the [target.name].", "You begin to lick off \the [target.name]...") + if(do_after(R, src.cleanspeed, target = target)) + if(!in_range(src, target)) //Proximity is probably old news by now, do a new check. + return //If they moved away, you can't eat them. + to_chat(R, "You finish licking off \the [target.name].") + qdel(target) + R.cell.give(50) + else if(isobj(target)) //hoo boy. danger zone man + if(istype(target,/obj/item/trash)) + R.visible_message("[R] nibbles away at \the [target.name].", "You begin to nibble away at \the [target.name]...") + if(!do_after(R, src.cleanspeed, target = target)) + return //If they moved away, you can't eat them. + to_chat(R, "You finish off \the [target.name].") + qdel(target) + R.cell.give(250) + return + if(istype(target,/obj/item/stock_parts/cell)) + R.visible_message("[R] begins cramming \the [target.name] down its throat.", "You begin cramming \the [target.name] down your throat...") + if(!do_after(R, 50, target = target)) + return //If they moved away, you can't eat them. + to_chat(R, "You finish off \the [target.name].") + var/obj/item/stock_parts/cell.C = target + R.cell.charge = R.cell.charge + (C.charge / 3) //Instant full cell upgrades op idgaf + qdel(target) + return + var/obj/item/I = target //HAHA FUCK IT, NOT LIKE WE ALREADY HAVE A SHITTON OF WAYS TO REMOVE SHIT + if(!I.anchored && R.emagged) + R.visible_message("[R] begins chewing up \the [target.name]. Looks like it's trying to loophole around its diet restriction!", "You begin chewing up \the [target.name]...") + if(!do_after(R, 100, target = I)) //Nerf dat time yo + return //If they moved away, you can't eat them. + visible_message("[R] chews up \the [target.name] and cleans off the debris!") + to_chat(R, "You finish off \the [target.name].") + qdel(I) + R.cell.give(500) + return + R.visible_message("[R] begins to lick \the [target.name] clean...", "You begin to lick \the [target.name] clean...") + else if(ishuman(target)) + var/mob/living/L = target + if(status == 0 && check_zone(R.zone_selected) == "head") + R.visible_message("\the [R] affectionally licks \the [L]'s face!", "You affectionally lick \the [L]'s face!") + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) + if(istype(L) && L.fire_stacks > 0) + L.adjust_fire_stacks(-10) + return + else if(status == 0) + R.visible_message("\the [R] affectionally licks \the [L]!", "You affectionally lick \the [L]!") + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) + if(istype(L) && L.fire_stacks > 0) + L.adjust_fire_stacks(-10) + return + else + if(R.cell.charge <= 800) + to_chat(R, "Insufficent Power!") + return + L.Stun(4) // normal stunbaton is force 7 gimme a break good sir! + L.Knockdown(80) + L.apply_effect(EFFECT_STUTTER, 4) + L.visible_message("[R] has shocked [L] with its tongue!", \ + "[R] has shocked you with its tongue!") + playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) + R.cell.use(666) + log_combat(R, L, "tongue stunned") + + else if(istype(target, /obj/structure/window)) + R.visible_message("[R] begins to lick \the [target.name] clean...", "You begin to lick \the [target.name] clean...") + if(do_after(user, src.cleanspeed, target = target)) + to_chat(user, "You clean \the [target.name].") + target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + target.set_opacity(initial(target.opacity)) + else + R.visible_message("[R] begins to lick \the [target.name] clean...", "You begin to lick \the [target.name] clean...") + if(do_after(user, src.cleanspeed, target = target)) + to_chat(user, "You clean \the [target.name].") + var/obj/effect/decal/cleanable/C = locate() in target + qdel(C) + target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + SEND_SIGNAL(target, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM) + target.wash_cream() + return + +//Dogfood + +/obj/item/trash/rkibble + name = "robo kibble" + desc = "A novelty bowl of assorted mech fabricator byproducts. Mockingly feed this to the sec-dog to help it recharge." + icon = 'icons/mob/dogborg.dmi' + icon_state= "kibble" + +//Defibs + +/obj/item/twohanded/shockpaddles/cyborg/hound + name = "Paws of Life" + desc = "MediHound specific shock paws." + icon = 'icons/mob/dogborg.dmi' + icon_state = "defibpaddles0" + item_state = "defibpaddles0" + +// Pounce stuff for K-9 + +/obj/item/dogborg/pounce + name = "pounce" + icon = 'icons/mob/dogborg.dmi' + icon_state = "pounce" + desc = "Leap at your target to momentarily stun them." + force = 0 + throwforce = 0 + +/obj/item/dogborg/pounce/New() + ..() + item_flags |= NOBLUDGEON + +/mob/living/silicon/robot + var/leaping = 0 + var/pounce_cooldown = 0 + var/pounce_cooldown_time = 50 //Nearly doubled, u happy? + var/pounce_spoolup = 3 + var/leap_at + var/disabler + var/laser + var/sleeper_g + var/sleeper_r + var/sleeper_nv + +#define MAX_K9_LEAP_DIST 4 //because something's definitely borked the pounce functioning from a distance. + +/obj/item/dogborg/pounce/afterattack(atom/A, mob/user) + var/mob/living/silicon/robot/R = user + if(R && !R.pounce_cooldown) + R.pounce_cooldown = !R.pounce_cooldown + to_chat(R, "Your targeting systems lock on to [A]...") + addtimer(CALLBACK(R, /mob/living/silicon/robot.proc/leap_at, A), R.pounce_spoolup) + spawn(R.pounce_cooldown_time) + R.pounce_cooldown = !R.pounce_cooldown + else if(R && R.pounce_cooldown) + to_chat(R, "Your leg actuators are still recharging!") + +/mob/living/silicon/robot/proc/leap_at(atom/A) + if(leaping || stat || buckled || lying) + return + + if(!has_gravity(src) || !has_gravity(A)) + to_chat(src,"It is unsafe to leap without gravity!") + //It's also extremely buggy visually, so it's balance+bugfix + return + + if(cell.charge <= 500) + to_chat(src,"Insufficent reserves for jump actuators!") + return + + else + leaping = 1 + weather_immunities += "lava" + pixel_y = 10 + update_icons() + throw_at(A, MAX_K9_LEAP_DIST, 1, spin=0, diagonals_first = 1) + cell.use(500) //Doubled the energy consumption + weather_immunities -= "lava" + +/mob/living/silicon/robot/throw_impact(atom/A) + + if(!leaping) + return ..() + + if(A) + if(isliving(A)) + var/mob/living/L = A + var/blocked = 0 + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(H.check_shields(0, "the [name]", src, attack_type = LEAP_ATTACK)) + blocked = 1 + if(!blocked) + L.visible_message("[src] pounces on [L]!", "[src] pounces on you!") + L.Knockdown(iscarbon(L) ? 450 : 45) // Temporary. If someone could rework how dogborg pounces work to accomodate for combat changes, that'd be nice. + playsound(src, 'sound/weapons/Egloves.ogg', 50, 1) + sleep(2)//Runtime prevention (infinite bump() calls on hulks) + step_towards(src,L) + log_combat(src, L, "borg pounced") + else + Knockdown(45, 1, 1) + + pounce_cooldown = !pounce_cooldown + spawn(pounce_cooldown_time) //3s by default + pounce_cooldown = !pounce_cooldown + else if(A.density && !A.CanPass(src)) + visible_message("[src] smashes into [A]!", "You smash into [A]!") + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + Knockdown(45, 1, 1) + + if(leaping) + leaping = 0 + pixel_y = initial(pixel_y) + update_icons() + update_canmove() diff --git a/modular_citadel/icons/mob/mam_ears.dmi b/modular_citadel/icons/mob/mam_ears.dmi index f2a788d900..1746dd04dd 100644 Binary files a/modular_citadel/icons/mob/mam_ears.dmi and b/modular_citadel/icons/mob/mam_ears.dmi differ diff --git a/modular_citadel/icons/mob/mam_markings.dmi b/modular_citadel/icons/mob/mam_markings.dmi index 3575f38e1c..8327be6eaf 100644 Binary files a/modular_citadel/icons/mob/mam_markings.dmi and b/modular_citadel/icons/mob/mam_markings.dmi differ diff --git a/modular_citadel/icons/mob/mam_taur.dmi b/modular_citadel/icons/mob/mam_taur.dmi index 3d0d4a0e84..84367ebf0a 100644 Binary files a/modular_citadel/icons/mob/mam_taur.dmi and b/modular_citadel/icons/mob/mam_taur.dmi differ diff --git a/modular_citadel/icons/mob/muzzled_helmet.dmi b/modular_citadel/icons/mob/muzzled_helmet.dmi index e1284679b3..a96f18c3ce 100644 Binary files a/modular_citadel/icons/mob/muzzled_helmet.dmi and b/modular_citadel/icons/mob/muzzled_helmet.dmi differ diff --git a/modular_citadel/icons/mob/suit_digi.dmi b/modular_citadel/icons/mob/suit_digi.dmi index c19fb01161..1f1f506d9f 100644 Binary files a/modular_citadel/icons/mob/suit_digi.dmi and b/modular_citadel/icons/mob/suit_digi.dmi differ diff --git a/modular_citadel/icons/mob/taur_naga.dmi b/modular_citadel/icons/mob/taur_naga.dmi index f4d6a6139f..1ca4509db7 100644 Binary files a/modular_citadel/icons/mob/taur_naga.dmi and b/modular_citadel/icons/mob/taur_naga.dmi differ diff --git a/modular_citadel/icons/obj/genitals/breasts.dmi b/modular_citadel/icons/obj/genitals/breasts.dmi index e864d09a70..8c76891396 100644 Binary files a/modular_citadel/icons/obj/genitals/breasts.dmi and b/modular_citadel/icons/obj/genitals/breasts.dmi differ diff --git a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi index b0dc1d7aef..69a531bd11 100644 Binary files a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi and b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi differ diff --git a/modular_citadel/icons/obj/genitals/penis.dmi b/modular_citadel/icons/obj/genitals/penis.dmi index 5ea37a722d..397fa335e5 100644 Binary files a/modular_citadel/icons/obj/genitals/penis.dmi and b/modular_citadel/icons/obj/genitals/penis.dmi differ diff --git a/modular_citadel/icons/obj/genitals/testicles.dmi b/modular_citadel/icons/obj/genitals/testicles.dmi new file mode 100644 index 0000000000..3d7a5f4f48 Binary files /dev/null and b/modular_citadel/icons/obj/genitals/testicles.dmi differ diff --git a/modular_citadel/icons/obj/genitals/testicles_onmob.dmi b/modular_citadel/icons/obj/genitals/testicles_onmob.dmi new file mode 100644 index 0000000000..581bcb0583 Binary files /dev/null and b/modular_citadel/icons/obj/genitals/testicles_onmob.dmi differ diff --git a/modular_citadel/icons/obj/genitals/vagina.dmi b/modular_citadel/icons/obj/genitals/vagina.dmi index 00891888a4..39bdd48e89 100644 Binary files a/modular_citadel/icons/obj/genitals/vagina.dmi and b/modular_citadel/icons/obj/genitals/vagina.dmi differ diff --git a/modular_citadel/icons/obj/genitals/vagina_onmob.dmi b/modular_citadel/icons/obj/genitals/vagina_onmob.dmi index c22c12ded3..f5daa3fea1 100644 Binary files a/modular_citadel/icons/obj/genitals/vagina_onmob.dmi and b/modular_citadel/icons/obj/genitals/vagina_onmob.dmi differ diff --git a/modular_citadel/icons/obj/guns/cit_guns.dmi b/modular_citadel/icons/obj/guns/cit_guns.dmi index e02025f436..8154a753e0 100644 Binary files a/modular_citadel/icons/obj/guns/cit_guns.dmi and b/modular_citadel/icons/obj/guns/cit_guns.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 4d74c8a95f..b5d72fff2b 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -380,6 +380,7 @@ #include "code\datums\components\storage\concrete\_concrete.dm" #include "code\datums\components\storage\concrete\bag_of_holding.dm" #include "code\datums\components\storage\concrete\bluespace.dm" +#include "code\datums\components\storage\concrete\emergency.dm" #include "code\datums\components\storage\concrete\implant.dm" #include "code\datums\components\storage\concrete\pockets.dm" #include "code\datums\components\storage\concrete\rped.dm" @@ -1495,6 +1496,7 @@ #include "code\modules\clothing\shoes\colour.dm" #include "code\modules\clothing\shoes\magboots.dm" #include "code\modules\clothing\shoes\miscellaneous.dm" +#include "code\modules\clothing\shoes\taeclowndo.dm" #include "code\modules\clothing\shoes\vg_shoes.dm" #include "code\modules\clothing\spacesuits\_spacesuits.dm" #include "code\modules\clothing\spacesuits\chronosuit.dm" @@ -1622,6 +1624,7 @@ #include "code\modules\food_and_drinks\food\snacks_burgers.dm" #include "code\modules\food_and_drinks\food\snacks_cake.dm" #include "code\modules\food_and_drinks\food\snacks_egg.dm" +#include "code\modules\food_and_drinks\food\snacks_frozen.dm" #include "code\modules\food_and_drinks\food\snacks_meat.dm" #include "code\modules\food_and_drinks\food\snacks_other.dm" #include "code\modules\food_and_drinks\food\snacks_pastry.dm" @@ -1650,6 +1653,7 @@ #include "code\modules\food_and_drinks\recipes\tablecraft\recipes_burger.dm" #include "code\modules\food_and_drinks\recipes\tablecraft\recipes_cake.dm" #include "code\modules\food_and_drinks\recipes\tablecraft\recipes_egg.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_frozen.dm" #include "code\modules\food_and_drinks\recipes\tablecraft\recipes_meat.dm" #include "code\modules\food_and_drinks\recipes\tablecraft\recipes_misc.dm" #include "code\modules\food_and_drinks\recipes\tablecraft\recipes_pastry.dm" @@ -2502,20 +2506,12 @@ #include "code\modules\research\server.dm" #include "code\modules\research\stock_parts.dm" #include "code\modules\research\designs\AI_module_designs.dm" -#include "code\modules\research\designs\autolathe_desings\autolathe_designs_construction.dm" -#include "code\modules\research\designs\autolathe_desings\autolathe_designs_electronics.dm" -#include "code\modules\research\designs\autolathe_desings\autolathe_designs_medical_and_dinnerware.dm" -#include "code\modules\research\designs\autolathe_desings\autolathe_designs_sec_and_hacked.dm" -#include "code\modules\research\designs\autolathe_desings\autolathe_designs_tcomms_and_misc.dm" -#include "code\modules\research\designs\autolathe_desings\autolathe_designs_tools.dm" #include "code\modules\research\designs\biogenerator_designs.dm" #include "code\modules\research\designs\bluespace_designs.dm" -#include "code\modules\research\designs\comp_board_designs.dm" #include "code\modules\research\designs\computer_part_designs.dm" #include "code\modules\research\designs\electronics_designs.dm" #include "code\modules\research\designs\equipment_designs.dm" #include "code\modules\research\designs\limbgrower_designs.dm" -#include "code\modules\research\designs\machine_designs.dm" #include "code\modules\research\designs\mecha_designs.dm" #include "code\modules\research\designs\mechfabricator_designs.dm" #include "code\modules\research\designs\medical_designs.dm" @@ -2527,6 +2523,24 @@ #include "code\modules\research\designs\stock_parts_designs.dm" #include "code\modules\research\designs\telecomms_designs.dm" #include "code\modules\research\designs\weapon_designs.dm" +#include "code\modules\research\designs\autolathe_desings\autolathe_designs_construction.dm" +#include "code\modules\research\designs\autolathe_desings\autolathe_designs_electronics.dm" +#include "code\modules\research\designs\autolathe_desings\autolathe_designs_medical_and_dinnerware.dm" +#include "code\modules\research\designs\autolathe_desings\autolathe_designs_sec_and_hacked.dm" +#include "code\modules\research\designs\autolathe_desings\autolathe_designs_tcomms_and_misc.dm" +#include "code\modules\research\designs\autolathe_desings\autolathe_designs_tools.dm" +#include "code\modules\research\designs\comp_board_designs\comp_board_designs_all_misc.dm" +#include "code\modules\research\designs\comp_board_designs\comp_board_designs_cargo .dm" +#include "code\modules\research\designs\comp_board_designs\comp_board_designs_engi.dm" +#include "code\modules\research\designs\comp_board_designs\comp_board_designs_medical.dm" +#include "code\modules\research\designs\comp_board_designs\comp_board_designs_sci.dm" +#include "code\modules\research\designs\comp_board_designs\comp_board_designs_sec.dm" +#include "code\modules\research\designs\machine_desings\machine_designs_all_misc.dm" +#include "code\modules\research\designs\machine_desings\machine_designs_cargo.dm" +#include "code\modules\research\designs\machine_desings\machine_designs_engi.dm" +#include "code\modules\research\designs\machine_desings\machine_designs_medical.dm" +#include "code\modules\research\designs\machine_desings\machine_designs_sci.dm" +#include "code\modules\research\designs\machine_desings\machine_designs_service.dm" #include "code\modules\research\machinery\_production.dm" #include "code\modules\research\machinery\circuit_imprinter.dm" #include "code\modules\research\machinery\departmental_circuit_imprinter.dm" @@ -2652,6 +2666,7 @@ #include "code\modules\spells\spell_types\shapeshift.dm" #include "code\modules\spells\spell_types\spacetime_distortion.dm" #include "code\modules\spells\spell_types\summonitem.dm" +#include "code\modules\spells\spell_types\taeclowndo.dm" #include "code\modules\spells\spell_types\the_traps.dm" #include "code\modules\spells\spell_types\touch_attacks.dm" #include "code\modules\spells\spell_types\trigger.dm" @@ -2818,6 +2833,17 @@ #include "modular_citadel\code\datums\wires\airlock.dm" #include "modular_citadel\code\datums\wires\autoylathe.dm" #include "modular_citadel\code\game\area\cit_areas.dm" +#include "modular_citadel\code\game\gamemodes\gangs\dominator.dm" +#include "modular_citadel\code\game\gamemodes\gangs\dominator_countdown.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gang.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gang_datums.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gang_decals.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gang_hud.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gang_items.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gang_pen.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gangs.dm" +#include "modular_citadel\code\game\gamemodes\gangs\gangtool.dm" +#include "modular_citadel\code\game\gamemodes\gangs\implant_gang.dm" #include "modular_citadel\code\game\gamemodes\miniantags\bot_swarm\swarmer_event.dm" #include "modular_citadel\code\game\gamemodes\revolution\revolution.dm" #include "modular_citadel\code\game\machinery\cryopod.dm"
Other Roles