diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 6ddd269b4b..9c39446a67 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -64,7 +64,6 @@ #define DEFAULT_BODYPART_ICON 'icons/mob/human_parts.dmi' #define DEFAULT_BODYPART_ICON_ORGANIC 'icons/mob/human_parts_greyscale.dmi' #define DEFAULT_BODYPART_ICON_ROBOTIC 'icons/mob/augmentation/augments.dmi' -#define DEFAULT_BODYPART_ICON_CITADEL 'modular_citadel/icons/mob/mutant_bodyparts.dmi' #define MONKEY_BODYPART "monkey" #define ALIEN_BODYPART "alien" @@ -333,4 +332,4 @@ /// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine() #define EXAMINE_MORE_TIME 1 SECONDS -#define SILENCE_RANGED_MESSAGE (1<<0) +#define SILENCE_RANGED_MESSAGE (1<<0) diff --git a/code/__DEFINES/rockpaperscissors.dm b/code/__DEFINES/rockpaperscissors.dm new file mode 100644 index 0000000000..77ba81938d --- /dev/null +++ b/code/__DEFINES/rockpaperscissors.dm @@ -0,0 +1,7 @@ +#define ROCKPAPERSCISSORS_RANGE 3 +#define ROCKPAPERSCISSORS_TIME_LIMIT 20 SECONDS + +#define ROCKPAPERSCISSORS_LOSE "lose" +#define ROCKPAPERSCISSORS_WIN "win" +#define ROCKPAPERSCISSORS_TIE "tie" +#define ROCKPAPERSCISSORS_NOT_DECIDED "not_decided" \ No newline at end of file diff --git a/code/__DEFINES/storage/volumetrics.dm b/code/__DEFINES/storage/volumetrics.dm index c3f45976ce..e6b732e083 100644 --- a/code/__DEFINES/storage/volumetrics.dm +++ b/code/__DEFINES/storage/volumetrics.dm @@ -24,7 +24,10 @@ GLOBAL_LIST_INIT(default_weight_class_to_volume, list( // Let's keep all of this in one place. given what we put above anyways.. // volume amount for items +/// volume for a data disk #define ITEM_VOLUME_DISK 1 +/// volume for a shotgun stripper clip holding 4 shells +#define ITEM_VOLUME_STRIPPER_CLIP (DEFAULT_VOLUME_NORMAL * 0.5) // #define SAMPLE_VOLUME_AMOUNT 2 diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 8225725ffd..71c2b2e13b 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -278,3 +278,8 @@ GLOBAL_LIST_INIT(all_mutant_parts, list("tail_lizard" = "Tail", "mam_tail" = "Ta GLOBAL_LIST_INIT(unlocked_mutant_parts, list("horns", "insect_fluff")) //parts in either of the above two lists that require a second option that allows them to be coloured GLOBAL_LIST_INIT(colored_mutant_parts, list("insect_wings" = "wings_color", "deco_wings" = "wings_color", "horns" = "horns_color")) + +//species ids that have greyscale sprites +GLOBAL_LIST_INIT(greyscale_limb_types, list("human","moth","lizard","pod","plant","jelly","slime","golem","lum","stargazer","mush","ethereal","snail","c_golem","b_golem","mammal","xeno","ipc","insect","synthliz","avian","aquatic")) + +//species ids that need snowflake coloring applied diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 4de7c88bf7..fee70ee3d5 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -41,3 +41,6 @@ GLOBAL_LIST_EMPTY(ai_status_displays) GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects GLOBAL_LIST_EMPTY(alert_consoles) // Station alert consoles, /obj/machinery/computer/station_alert + +//list of everyone playing rock paper scissors +GLOBAL_LIST_EMPTY(rockpaperscissors_players) diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm index 8ace273dd8..419524c871 100644 --- a/code/_onclick/overmind.dm +++ b/code/_onclick/overmind.dm @@ -32,4 +32,4 @@ /mob/camera/blob/AltClickOn(atom/A) //Remove a blob var/turf/T = get_turf(A) if(T) - remove_blob(T) \ No newline at end of file + remove_blob(T) diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm index a4e0d7703f..ee0fd1ea00 100644 --- a/code/controllers/subsystem/processing/instruments.dm +++ b/code/controllers/subsystem/processing/instruments.dm @@ -4,16 +4,26 @@ PROCESSING_SUBSYSTEM_DEF(instruments) init_order = INIT_ORDER_INSTRUMENTS flags = SS_KEEP_TIMING priority = FIRE_PRIORITY_INSTRUMENTS - var/static/list/datum/instrument/instrument_data = list() //id = datum + /// List of all instrument data, associative id = datum + var/static/list/datum/instrument/instrument_data = list() + /// List of all song datums. var/static/list/datum/song/songs = list() + /// Max lines in songs var/static/musician_maxlines = 600 + /// Max characters per line in songs var/static/musician_maxlinechars = 300 + /// Deciseconds between hearchecks. Too high and instruments seem to lag when people are moving around in terms of who can hear it. Too low and the server lags from this. var/static/musician_hearcheck_mindelay = 5 + /// Maximum instrument channels total instruments are allowed to use. This is so you don't have instruments deadlocking all sound channels. var/static/max_instrument_channels = MAX_INSTRUMENT_CHANNELS + /// Current number of channels allocated for instruments var/static/current_instrument_channels = 0 + /// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer. + var/static/list/synthesizer_instrument_ids /datum/controller/subsystem/processing/instruments/Initialize() initialize_instrument_data() + synthesizer_instrument_ids = get_allowed_instrument_ids() return ..() /datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S) @@ -29,7 +39,10 @@ PROCESSING_SUBSYSTEM_DEF(instruments) continue I = new path I.Initialize() - instrument_data[I.id || "[I.type]"] = I + if(!I.id) + qdel(I) + continue + instrument_data[I.id] = I CHECK_TICK /datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path) diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm index 978802fd80..ad08b8ebdc 100644 --- a/code/datums/mutations/antenna.dm +++ b/code/datums/mutations/antenna.dm @@ -90,6 +90,8 @@ to_chat(user, "You catch some drifting memories of their past conversations...") for(var/spoken_memory in recent_speech) to_chat(user, "[recent_speech[spoken_memory]]") + if(usr in GLOB.rockpaperscissors_players) + to_chat(user, "They're planning on playing [GLOB.rockpaperscissors_players[usr][1]]") if(iscarbon(M)) var/mob/living/carbon/human/H = M to_chat(user, "You find that their intent is to [H.a_intent]...") diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm index 19b0dc76dd..a388d3bc67 100644 --- a/code/game/objects/effects/effect_system/effects_sparks.dm +++ b/code/game/objects/effects/effect_system/effects_sparks.dm @@ -63,3 +63,23 @@ /datum/effect_system/lightning_spread effect_type = /obj/effect/particle_effect/sparks/electricity + +//fake sparks, not subtyped because we don't want light/heat, nor checks inside an often used proc for a rare subcase for saving like 10 lines of code +/obj/effect/particle_effect/fake_sparks + name = "lightning" + icon_state = "electricity" + +/obj/effect/particle_effect/fake_sparks/Initialize() + . = ..() + flick(icon_state, src) // replay the animation + playsound(src, "sparks", 100, TRUE) + QDEL_IN(src, 20) + +/datum/effect_system/fake_spark_spread + effect_type = /obj/effect/particle_effect/fake_sparks + +/proc/do_fake_sparks(n, c, source) + var/datum/effect_system/fake_spark_spread/sparks = new + sparks.set_up(n, c, source) + sparks.autocleanup = TRUE + sparks.start() diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index a6b34c59e3..73246276c2 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -281,6 +281,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons) /obj/item/clothing/suit/armor/vest/infiltrator, /obj/item/clothing/under/syndicate/bloodred, /obj/item/clothing/gloves/color/latex/nitrile/infiltrator, + /obj/item/clothing/gloves/tackler/combat/insulated/infiltrator, /obj/item/clothing/mask/infiltrator, /obj/item/clothing/shoes/combat/sneakboots, /obj/item/gun/ballistic/automatic/pistol, @@ -292,7 +293,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons) new /obj/item/clothing/head/helmet/infiltrator(src) new /obj/item/clothing/suit/armor/vest/infiltrator(src) new /obj/item/clothing/under/syndicate/bloodred(src) - new /obj/item/clothing/gloves/color/latex/nitrile/infiltrator(src) + new /obj/item/clothing/gloves/tackler/combat/insulated/infiltrator(src) new /obj/item/clothing/mask/infiltrator(src) new /obj/item/clothing/shoes/combat/sneakboots(src) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 36e4f825da..7853b9619c 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -86,6 +86,10 @@ /obj/structure/grille/attack_animal(mob/user) . = ..() + if(!user.CheckActionCooldown(CLICK_CD_MELEE)) + return + user.DelayNextAction(flush = TRUE) + user.do_attack_animation(src) if(!shock(user, 70) && !QDELETED(src)) //Last hit still shocks but shouldn't deal damage to the grille) take_damage(rand(5,10), BRUTE, "melee", 1) @@ -114,12 +118,12 @@ /obj/structure/grille/attack_alien(mob/living/user) if(!user.CheckActionCooldown(CLICK_CD_MELEE)) return + user.DelayNextAction(flush = TRUE) user.do_attack_animation(src) user.visible_message("[user] mangles [src].", null, null, COMBAT_MESSAGE_RANGE) if(!shock(user, 70)) take_damage(20, BRUTE, "melee", 1) - /obj/structure/grille/CanPass(atom/movable/mover, turf/target) if(istype(mover) && (mover.pass_flags & PASSGRILLE)) return TRUE diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 380e5c5412..48b86c68dd 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -247,6 +247,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) /// Which of the 5 persistent scar slots we randomly roll to load for this round, if enabled. Actually rolled in [/datum/preferences/proc/load_character(slot)] var/scars_index = 1 + var/chosen_limb_id //body sprite selected to load for the users limbs, null means default, is sanitized when loaded + /datum/preferences/New(client/C) parent = C @@ -526,6 +528,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" mutant_category = 0 + if(length(pref_species.allowed_limb_ids)) + if(!chosen_limb_id || !(chosen_limb_id in pref_species.allowed_limb_ids)) + chosen_limb_id = pref_species.id + dat += "

Body sprite

" + dat += "[chosen_limb_id]" + if(mutant_category) dat += "" mutant_category = 0 @@ -2120,8 +2128,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) else features["body_model"] = chosengender gender = chosengender - facial_hair_style = random_facial_hair_style(gender) - hair_style = random_hair_style(gender) if("body_size") var/min = CONFIG_GET(number/body_size_min) @@ -2147,6 +2153,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/selected_custom_speech_verb = input(user, "Choose your desired speech verb (none means your species speech verb)", "Character Preference") as null|anything in GLOB.speech_verbs if(selected_custom_speech_verb) custom_speech_verb = selected_custom_speech_verb + + if("bodysprite") + var/selected_body_sprite = input(user, "Choose your desired body sprite", "Character Preference") as null|anything in pref_species.allowed_limb_ids + if(selected_body_sprite) + chosen_limb_id = selected_body_sprite //this gets sanitized before loading else switch(href_list["preference"]) //CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj @@ -2538,6 +2549,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.dna.features = features.Copy() character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE) + if(chosen_limb_id && (chosen_limb_id in character.dna.species.allowed_limb_ids)) + character.dna.species.mutant_bodyparts["limbs_id"] = chosen_limb_id character.dna.real_name = character.real_name character.dna.nameless = character.nameless character.dna.custom_species = character.custom_species diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index f253592765..c0a578862a 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -515,7 +515,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["scars4"] >> scars_list["4"] S["scars5"] >> scars_list["5"] json_decode(S["modified_limbs"]) >> modified_limbs - + S["chosen_limb_id"] >> chosen_limb_id //Custom names for(var/custom_name_id in GLOB.preferences_custom_names) @@ -872,6 +872,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["scars4"] , scars_list["4"]) WRITE_FILE(S["scars5"] , scars_list["5"]) WRITE_FILE(S["modified_limbs"] , json_encode(modified_limbs)) + WRITE_FILE(S["chosen_limb_id"], chosen_limb_id) //gear loadout if(chosen_gear.len) diff --git a/code/modules/clothing/gloves/tacklers.dm b/code/modules/clothing/gloves/tacklers.dm index 11b2afa968..f4b4140a1a 100644 --- a/code/modules/clothing/gloves/tacklers.dm +++ b/code/modules/clothing/gloves/tacklers.dm @@ -72,6 +72,25 @@ siemens_coefficient = 0 permeability_coefficient = 0.05 +/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator + name = "insidious guerrilla gloves" + desc = "Specialized combat gloves for carrying people around. Transfers tactical kidnapping and tackling knowledge to the user via the use of nanochips." + icon_state = "infiltrator" + item_state = "infiltrator" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + resistance_flags = FIRE_PROOF | ACID_PROOF + var/carrytrait = TRAIT_QUICKER_CARRY + +/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/equipped(mob/user, slot) + . = ..() + if(slot == SLOT_GLOVES) + ADD_TRAIT(user, carrytrait, GLOVE_TRAIT) + +/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/dropped(mob/user) + . = ..() + REMOVE_TRAIT(user, carrytrait, GLOVE_TRAIT) + /obj/item/clothing/gloves/tackler/rocket name = "rocket gloves" desc = "The ultimate in high risk, high reward, perfect for when you need to stop a criminal from fifty feet away or die trying. Banned in most Spinward gridiron football and rugby leagues." diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 6364a9a1c1..d6853f52ca 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -625,7 +625,7 @@ desc = "An arctic white winter coat with a small blue caduceus instead of a plastic zipper tab. Snazzy." icon_state = "coatmedical" item_state = "coatmedical" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45) hoodtype = /obj/item/clothing/head/hooded/winterhood/medical @@ -638,7 +638,7 @@ desc = "An arctic white winter coat with a small blue caduceus instead of a plastic zipper tab. The normal liner is replaced with an exceptionally thick, soft layer of fur." icon_state = "coatcmo" item_state = "coatcmo" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 0) hoodtype = /obj/item/clothing/head/hooded/winterhood/cmo @@ -651,7 +651,7 @@ desc = "A lab-grade winter coat made with acid resistant polymers. For the enterprising chemist who was exiled to a frozen wasteland on the go." icon_state = "coatchemistry" item_state = "coatchemistry" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 30, "acid" = 45) hoodtype = /obj/item/clothing/head/hooded/winterhood/chemistry @@ -664,7 +664,7 @@ desc = "A white winter coat with green markings. Warm, but wont fight off the common cold or any other disease. Might make people stand far away from you in the hallway. The zipper tab looks like an oversized bacteriophage." icon_state = "coatviro" item_state = "coatviro" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 0, "acid" = 0) hoodtype = /obj/item/clothing/head/hooded/winterhood/viro @@ -677,7 +677,7 @@ desc = "A winter coat with blue markings. Warm, but probably won't protect from biological agents. For the cozy doctor on the go." icon_state = "coatparamed" item_state = "coatparamed" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) + allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45) hoodtype = /obj/item/clothing/head/hooded/winterhood/paramedic diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index 895699434a..4d5f4e2dd0 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -97,9 +97,12 @@ All foods are distributed among various categories. Use common sense. return -/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, def_zone) +/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(user.a_intent == INTENT_HARM) return ..() + INVOKE_ASYNC(src, .proc/attempt_forcefeed, M, user) + +/obj/item/reagent_containers/food/snacks/proc/attempt_forcefeed(mob/living/M, mob/living/user) if(!eatverb) eatverb = pick("bite","chew","nibble","gnaw","gobble","chomp") if(!reagents.total_volume) //Shouldn't be needed but it checks to see if it has anything left in it. diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index abf6632939..f6be9db9a2 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -103,6 +103,7 @@ throwforce = 5 throw_speed = 2 throw_range = 3 + attack_speed = CLICK_CD_MELEE w_class = WEIGHT_CLASS_BULKY flags_1 = CONDUCT_1 armour_penetration = 20 @@ -125,9 +126,12 @@ playsound(src,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1) return (BRUTELOSS) -/obj/item/scythe/pre_attack(atom/A, mob/living/user, params) +/obj/item/scythe/pre_attack(atom/A, mob/living/user, params, attackchain_flags, damage_multiplier) + . = ..() + if(. & STOP_ATTACK_PROC_CHAIN) + return if(swiping || !istype(A, /obj/structure/spacevine) || get_turf(A) == get_turf(user)) - return ..() + return else var/turf/user_turf = get_turf(user) var/dir_to_target = get_dir(user_turf, get_turf(A)) @@ -138,11 +142,12 @@ var/turf/T = get_step(user_turf, turn(dir_to_target, i)) for(var/obj/structure/spacevine/V in T) if(user.Adjacent(V)) - melee_attack_chain(user, V) + melee_attack_chain(user, V, attackchain_flags = ATTACK_IGNORE_CLICKDELAY) stam_gain += 5 //should be hitcost swiping = FALSE stam_gain += 2 //Initial hitcost user.adjustStaminaLoss(-stam_gain) + user.DelayNextAction() // ************************************* // Nutrient defines for hydroponics @@ -192,4 +197,4 @@ /obj/item/reagent_containers/glass/bottle/killer/pestkiller name = "bottle of pest spray" desc = "Contains a pesticide." - list_reagents = list(/datum/reagent/toxin/pestkiller = 50) \ No newline at end of file + list_reagents = list(/datum/reagent/toxin/pestkiller = 50) diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index d842dbc003..a0d96658e6 100644 --- a/code/modules/instruments/songs/_song.dm +++ b/code/modules/instruments/songs/_song.dm @@ -2,6 +2,12 @@ #define MUSIC_MAXLINES 1000 #define MUSIC_MAXLINECHARS 300 +/** + * # Song datum + * + * These are the actual backend behind instruments. + * They attach to an atom and provide the editor + playback functionality. + */ /datum/song /// Name of the song var/name = "Untitled" @@ -15,6 +21,9 @@ /// delay between notes in deciseconds var/tempo = 5 + /// How far we can be heard + var/instrument_range = 15 + /// Are we currently playing? var/playing = FALSE @@ -53,17 +62,24 @@ /////////////////// Playing variables //////////////// /** - * Only used in synthesized playback - The chords we compiled. Non assoc list of lists: - * list(list(key1, key2, key3..., tempo_divisor), list(key1, key2..., tempo_divisor), ...) - * tempo_divisor always exists - * if key1 (and so if there's no keys) doesn't exist it's a rest + * Build by compile_chords() + * Must be rebuilt on instrument switch. * Compilation happens when we start playing and is cleared after we finish playing. + * Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv) */ var/list/compiled_chords + /// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag. + var/elapsed_delay + /// Amount of delay to wait before playing the next chord + var/delay_by + /// Current chord we're on. + var/current_chord /// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1. var/list/channels_playing = list() /// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments. var/list/channels_idle = list() + /// Person playing us + var/mob/user_playing ////////////////////////////////////////////////////// /// Last world.time we checked for who can hear us @@ -72,8 +88,6 @@ var/list/hearing_mobs /// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop) var/debug_mode = FALSE - /// Last time we processed decay - var/last_process_decay /// Max sound channels to occupy var/max_sound_channels = CHANNELS_PER_INSTRUMENT /// Current channels, so we can save a length() call. @@ -113,7 +127,7 @@ var/cached_exponential_dropoff = 1.045 ///////////////////////////////////////////////////////////////////////// -/datum/song/New(atom/parent, list/instrument_ids) +/datum/song/New(atom/parent, list/instrument_ids, new_range) SSinstruments.on_song_new(src) lines = list() tempo = sanitize_tempo(tempo) @@ -125,6 +139,8 @@ hearing_mobs = list() volume = clamp(volume, min_volume, max_volume) update_sustain() + if(new_range) + instrument_range = new_range /datum/song/Destroy() stop_playing() @@ -135,12 +151,15 @@ parent = null return ..() +/** + * Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range. + */ /datum/song/proc/do_hearcheck() last_hearcheck = world.time var/list/old = hearing_mobs.Copy() hearing_mobs.len = 0 var/turf/source = get_turf(parent) - for(var/mob/M in get_hearers_in_view(15, source)) + for(var/mob/M in get_hearers_in_view(instrument_range, source)) if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) continue hearing_mobs[M] = get_dist(M, source) @@ -148,10 +167,15 @@ for(var/i in exited) terminate_sound_mob(i) -/// I can either be a datum, id, or path (if the instrument has no id). +/** + * Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum. + */ /datum/song/proc/set_instrument(datum/instrument/I) + terminate_all_sounds() + var/old_legacy if(using_instrument) using_instrument.songs_using -= src + old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY) using_instrument = null cached_samples = null cached_legacy_ext = null @@ -162,7 +186,7 @@ if(istype(I)) using_instrument = I I.songs_using += src - var/instrument_legacy = CHECK_BITFIELD(I.instrument_flags, INSTRUMENT_LEGACY) + var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY) if(instrument_legacy) cached_legacy_ext = I.legacy_instrument_ext cached_legacy_dir = I.legacy_instrument_path @@ -170,23 +194,37 @@ else cached_samples = I.samples legacy = FALSE + if(isnull(old_legacy) || (old_legacy != instrument_legacy)) + if(playing) + compile_chords() -/// THIS IS A BLOCKING CALL. +/** + * Attempts to start playing our song. + */ /datum/song/proc/start_playing(mob/user) if(playing) return if(!using_instrument?.ready()) to_chat(user, "An error has occured with [src]. Please reset the instrument.") return + compile_chords() + if(!length(compiled_chords)) + to_chat(user, "Song is empty.") + return playing = TRUE - updateDialog() + updateDialog(user_playing) //we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak. //wrap the rest of the stuff to ensure stop_playing() is called. - last_process_decay = world.time + do_hearcheck() + elapsed_delay = 0 + delay_by = 0 + current_chord = 1 + user_playing = user START_PROCESSING(SSinstruments, src) - . = do_play_lines(user) - stop_playing() +/** + * Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs. + */ /datum/song/proc/stop_playing() if(!playing) return @@ -196,42 +234,93 @@ STOP_PROCESSING(SSinstruments, src) terminate_all_sounds(TRUE) hearing_mobs.len = 0 - updateDialog() + user_playing = null -/// THIS IS A BLOCKING CALL. -/datum/song/proc/do_play_lines(user) - if(!playing) +/** + * Processes our song. + */ +/datum/song/proc/process_song(wait) + if(!length(compiled_chords) || should_stop_playing(user_playing)) + stop_playing() return - do_hearcheck() - if(legacy) - do_play_lines_legacy(user) - else - do_play_lines_synthesized(user) + var/list/chord = compiled_chords[current_chord] + if(++elapsed_delay >= delay_by) + play_chord(chord) + elapsed_delay = 0 + delay_by = tempodiv_to_delay(chord[length(chord)]) + current_chord++ + if(current_chord > length(compiled_chords)) + if(repeat) + repeat-- + current_chord = 1 + return + else + stop_playing() + return +/** + * Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo. + */ +/datum/song/proc/tempodiv_to_delay(tempodiv) + if(!tempodiv) + tempodiv = 1 // no division by 0. some song converters tend to use 0 for when it wants to have no div, for whatever reason. + return max(1, round((tempo/tempodiv) / world.tick_lag, 1)) + +/** + * Compiles chords. + */ +/datum/song/proc/compile_chords() + legacy? compile_legacy() : compile_synthesized() + +/** + * Plays a chord. + */ +/datum/song/proc/play_chord(list/chord) + // last value is timing information + for(var/i in 1 to (length(chord) - 1)) + legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing) + +/** + * Checks if we should halt playback. + */ /datum/song/proc/should_stop_playing(mob/user) return QDELETED(parent) || !using_instrument || !playing +/** + * Sanitizes tempo to a value that makes sense and fits the current world.tick_lag. + */ /datum/song/proc/sanitize_tempo(new_tempo) new_tempo = abs(new_tempo) return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS) +/** + * Gets our beats per minute based on our tempo. + */ /datum/song/proc/get_bpm() return 600 / tempo +/** + * Sets our tempo from a beats-per-minute, sanitizing it to a valid number first. + */ /datum/song/proc/set_bpm(bpm) tempo = sanitize_tempo(600 / bpm) -/// Updates the window for our user. Override in subtypes. -/datum/song/proc/updateDialog(mob/user = usr) +/** + * Updates the window for our users. Override down the line. + */ +/datum/song/proc/updateDialog(mob/user) ui_interact(user) /datum/song/process(wait) if(!playing) return PROCESS_KILL - var/delay = world.time - last_process_decay - process_decay(delay) - last_process_decay = world.time + // it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up. + process_song(world.tick_lag) + process_decay(world.tick_lag) +/** + * Updates our cached linear/exponential falloff stuff, saving calculations down the line. + */ /datum/song/proc/update_sustain() // Exponential is easy cached_exponential_dropoff = sustain_exponential_dropoff @@ -241,21 +330,33 @@ var/volume_decrease_per_decisecond = volume_diff / target_duration cached_linear_dropoff = volume_decrease_per_decisecond +/** + * Setter for setting output volume. + */ /datum/song/proc/set_volume(volume) src.volume = clamp(volume, max(0, min_volume), min(100, max_volume)) update_sustain() updateDialog() +/** + * Setter for setting how low the volume has to get before a note is considered "dead" and dropped + */ /datum/song/proc/set_dropoff_volume(volume) sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100) update_sustain() updateDialog() +/** + * Setter for setting exponential falloff factor. + */ /datum/song/proc/set_exponential_drop_rate(drop) sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX) update_sustain() updateDialog() +/** + * Setter for setting linear falloff duration. + */ /datum/song/proc/set_linear_falloff_duration(duration) sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN) update_sustain() @@ -277,10 +378,8 @@ // subtype for handheld instruments, like violin /datum/song/handheld -/datum/song/handheld/updateDialog(mob/user = usr) - if(user.machine != src) - return - parent.ui_interact(user) +/datum/song/handheld/updateDialog(mob/user) + parent.ui_interact(user || usr) /datum/song/handheld/should_stop_playing(mob/user) . = ..() @@ -292,10 +391,8 @@ // subtype for stationary structures, like pianos /datum/song/stationary -/datum/song/stationary/updateDialog(mob/user = usr) - if(user.machine != src) - return - parent.ui_interact(user) +/datum/song/stationary/updateDialog(mob/user) + parent.ui_interact(user || usr) /datum/song/stationary/should_stop_playing(mob/user) . = ..() diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm index fa64656ebc..eee9be3cc7 100644 --- a/code/modules/instruments/songs/play_legacy.dm +++ b/code/modules/instruments/songs/play_legacy.dm @@ -1,48 +1,52 @@ -/// Playing legacy instruments - None of the "advanced" like sound reservations and decay are invoked. -/datum/song/proc/do_play_lines_legacy(mob/user) - while(repeat >= 0) - var/cur_oct[7] - var/cur_acc[7] - for(var/i = 1 to 7) - cur_oct[i] = 3 - cur_acc[i] = "n" +/** + * Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag. + */ +/datum/song/proc/compile_legacy() + if(!length(src.lines)) + return + var/list/lines = src.lines //cache for hyepr speed! + compiled_chords = list() + var/list/octaves = list(3, 3, 3, 3, 3, 3, 3) + var/list/accents = list("n", "n", "n", "n", "n", "n", "n") + for(var/line in lines) + var/list/chords = splittext(lowertext(line), ",") + for(var/chord in chords) + var/list/compiled_chord = list() + var/tempodiv = 1 + var/list/notes_tempodiv = splittext(chord, "/") + var/len = length(notes_tempodiv) + if(len >= 2) + tempodiv = text2num(notes_tempodiv[2]) + if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that. + var/list/notes = splittext(notes_tempodiv[1], "-") + for(var/note in notes) + if(length(note) == 0) + continue + // 1-7, A-G + var/key = text2ascii(note) - 96 + if((key < 1) || (key > 7)) + continue + for(var/i in 2 to length(note)) + var/oct_acc = copytext(note, i, i + 1) + var/num = text2num(oct_acc) + if(!num) //it's an accidental + accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks. + else //octave + octaves[key] = clamp(num, octave_min, octave_max) + compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key]) + compiled_chord += tempodiv //this goes last + if(length(compiled_chord)) + compiled_chords[++compiled_chords.len] = compiled_chord - for(var/line in lines) - for(var/beat in splittext(lowertext(line), ",")) - if(should_stop_playing(user)) - return - var/list/notes = splittext(beat, "/") - if(length(notes)) //because some jack-butts are going to do ,,,, to symbolize 3 rests instead of something reasonable like ,/1. - for(var/note in splittext(notes[1], "-")) - if(length(note) == 0) - continue - var/cur_note = text2ascii(note) - 96 - if(cur_note < 1 || cur_note > 7) - continue - for(var/i=2 to length(note)) - var/ni = copytext(note,i,i+1) - if(!text2num(ni)) - if(ni == "#" || ni == "b" || ni == "n") - cur_acc[cur_note] = ni - else if(ni == "s") - cur_acc[cur_note] = "#" // so shift is never required - else - cur_oct[cur_note] = text2num(ni) - playnote_legacy(cur_note, cur_acc[cur_note], cur_oct[cur_note]) - if(notes.len >= 2 && text2num(notes[2])) - sleep(sanitize_tempo(tempo / text2num(notes[2]))) - else - sleep(tempo) - if(should_stop_playing(user)) - return - repeat-- - updateDialog() - repeat = 0 - -// note is a number from 1-7 for A-G -// acc is either "b", "n", or "#" -// oct is 1-8 (or 9 for C) -/datum/song/proc/playnote_legacy(note, acc as text, oct) +/** + * Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management. + * + * Arguments: + * * note is a number from 1-7 for A-G + * * acc is either "b", "n", or "#" + * * oct is 1-8 (or 9 for C) + */ +/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user) // handle accidental -> B<>C of E<>F if(acc == "b" && (note == 3 || note == 6)) // C or F if(note == 3) diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm index 5e7c5652a0..4df54f5e6b 100644 --- a/code/modules/instruments/songs/play_synthesized.dm +++ b/code/modules/instruments/songs/play_synthesized.dm @@ -1,27 +1,7 @@ -/datum/song/proc/do_play_lines_synthesized(mob/user) - compile_lines() - while(repeat >= 0) - if(should_stop_playing(user)) - return - var/warned = FALSE - for(var/_chord in compiled_chords) - if(should_stop_playing(user)) - return - var/list/chord = _chord - var/tempodiv = chord[chord.len] - for(var/i in 1 to chord.len - 1) - var/key = chord[i] - if(!playkey_synth(key)) - if(!warned) - warned = TRUE - to_chat(user, "Your instrument has ran out of channels. You might be playing your song too fast or be setting sustain to too high of a value. This warning will be suppressed for the rest of this cycle.") - sleep(sanitize_tempo(tempo / (tempodiv || 1))) - repeat-- - updateDialog() - repeat = 0 - -/// C-Db2-A-A4/2,A-B#4-C/3,/4,A,A-B-C as an example -/datum/song/proc/compile_lines() +/** + * Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag. + */ +/datum/song/proc/compile_synthesized() if(!length(src.lines)) return var/list/lines = src.lines //cache for hyepr speed! @@ -57,10 +37,12 @@ compiled_chord += tempodiv //this goes last if(length(compiled_chord)) compiled_chords[++compiled_chords.len] = compiled_chord - CHECK_TICK - return compiled_chords -/datum/song/proc/playkey_synth(key) +/** + * Plays a specific numerical key from our instrument to anyone who can hear us. + * Does a hearing check if enough time has passed. + */ +/datum/song/proc/playkey_synth(key, mob/user) if(can_noteshift) key = clamp(key + note_shift, key_min, key_max) if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) @@ -83,6 +65,9 @@ M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF) // Could do environment and echo later but not for now +/** + * Stops all sounds we are "responsible" for. Only works in synthesized mode. + */ /datum/song/proc/terminate_all_sounds(clear_channels = TRUE) for(var/i in hearing_mobs) terminate_sound_mob(i) @@ -93,10 +78,16 @@ using_sound_channels = 0 SSsounds.free_datum_channels(src) +/** + * Stops all sounds we are responsible for in a given person. Only works in synthesized mode. + */ /datum/song/proc/terminate_sound_mob(mob/M) for(var/channel in channels_playing) M.stop_sound_channel(text2num(channel)) +/** + * Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster. + */ /datum/song/proc/pop_channel() if(length(channels_idle)) //just pop one off of here if we have one available . = text2num(channels_idle[1]) @@ -108,6 +99,12 @@ if(!isnull(.)) using_sound_channels++ +/** + * Decays our channels and updates their volumes to mobs who can hear us. + * + * Arguments: + * * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation. + */ /datum/song/proc/process_decay(wait_ds) var/linear_dropoff = cached_linear_dropoff * wait_ds var/exponential_dropoff = cached_exponential_dropoff ** wait_ds diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index edb681e60f..b7d67e49a6 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -87,7 +87,7 @@ if(user != src && (user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM)) for(var/datum/surgery/S in surgeries) if(S.next_step(user,user.a_intent)) - return 1 + return STOP_ATTACK_PROC_CHAIN if(!all_wounds || !(user.a_intent == INTENT_HELP || user == src)) return ..() @@ -95,7 +95,7 @@ for(var/i in shuffle(all_wounds)) var/datum/wound/W = i if(W.try_treating(I, user)) - return 1 + return STOP_ATTACK_PROC_CHAIN return ..() diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index cb0dbef332..04747ffcb4 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -7,6 +7,11 @@ message = "cries." emote_type = EMOTE_AUDIBLE +/datum/emote/living/carbon/human/cry/run_emote(mob/user, params) + . = ..() + if(. && isipcperson(user)) + do_fake_sparks(5,FALSE,user) + /datum/emote/living/carbon/human/dap key = "dap" key_third_person = "daps" @@ -187,3 +192,71 @@ key_third_person = "chimes" message = "chimes." sound = 'sound/machines/chime.ogg' + +//rock paper scissors emote handling +/mob/living/carbon/human/proc/beginRockPaperScissors(var/chosen_move) + GLOB.rockpaperscissors_players[src] = list(chosen_move, ROCKPAPERSCISSORS_NOT_DECIDED) + do_after_advanced(src, ROCKPAPERSCISSORS_TIME_LIMIT, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT|DO_AFTER_NO_PROGRESSBAR|DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER, CALLBACK(src, .proc/rockpaperscissors_tick)) + var/new_entry = GLOB.rockpaperscissors_players[src] + if(new_entry[2] == ROCKPAPERSCISSORS_NOT_DECIDED) + to_chat(src, "You put your hand back down.") + GLOB.rockpaperscissors_players -= src + +/mob/living/carbon/human/proc/rockpaperscissors_tick() //called every cycle of the progress bar for rock paper scissors while waiting for an opponent + var/mob/living/carbon/human/opponent + for(var/mob/living/carbon/human/potential_opponent in (GLOB.rockpaperscissors_players - src)) //dont play against yourself + if(get_dist(src, potential_opponent) <= ROCKPAPERSCISSORS_RANGE) + opponent = potential_opponent + break + if(opponent) + //we found an opponent before they found us + var/move_to_number = list("rock" = 0, "paper" = 1, "scissors" = 2) + var/our_move = move_to_number[GLOB.rockpaperscissors_players[src][1]] + var/their_move = move_to_number[GLOB.rockpaperscissors_players[opponent][1]] + var/result_us = ROCKPAPERSCISSORS_WIN + var/result_them = ROCKPAPERSCISSORS_LOSE + if(our_move == their_move) + result_us = ROCKPAPERSCISSORS_TIE + result_them = ROCKPAPERSCISSORS_TIE + else + if(((our_move + 1) % 3) == their_move) + result_us = ROCKPAPERSCISSORS_LOSE + result_them = ROCKPAPERSCISSORS_WIN + //we decided our results so set them in the list + GLOB.rockpaperscissors_players[src][2] = result_us + GLOB.rockpaperscissors_players[opponent][2] = result_them + + //show what happened + src.visible_message("[src] makes [GLOB.rockpaperscissors_players[src][1]] with their hand!") + opponent.visible_message("[opponent] makes [GLOB.rockpaperscissors_players[opponent][1]] with their hands!") + switch(result_us) + if(ROCKPAPERSCISSORS_TIE) + src.visible_message("It was a tie!") + if(ROCKPAPERSCISSORS_WIN) + src.visible_message("[src] wins!") + if(ROCKPAPERSCISSORS_LOSE) + src.visible_message("[opponent] wins!") + + //make the progress bar end so that each player can handle the result + return DO_AFTER_STOP + + //no opponent was found, so keep searching + return DO_AFTER_PROCEED + +//the actual emotes +/datum/emote/living/carbon/human/rockpaperscissors + message = "is attempting to play rock paper scissors!" + +/datum/emote/living/carbon/human/rockpaperscissors/rock + key = "rock" + +/datum/emote/living/carbon/human/rockpaperscissors/paper + key = "paper" + +/datum/emote/living/carbon/human/rockpaperscissors/scissors + key = "scissors" + +/datum/emote/living/carbon/human/rockpaperscissors/run_emote(mob/living/carbon/human/user, params) + if(!(user in GLOB.rockpaperscissors_players)) //no using the emote again while already playing! + . = ..() + user.beginRockPaperScissors(key) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 230a634e0e..ed509f900a 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -114,6 +114,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) /// Our default override for typing indicator state var/typing_indicator_state + //the ids you can use for your species, if empty, it means default only and not changeable + var/list/allowed_limb_ids + /////////// // PROCS // /////////// @@ -121,7 +124,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) /datum/species/New() if(!limbs_id) //if we havent set a limbs id to use, just use our own id - limbs_id = id + mutant_bodyparts["limbs_id"] = id //done this way to be non-intrusive to the existing system + else + mutant_bodyparts["limbs_id"] = limbs_id ..() //update our mutant bodyparts to include unlocked ones diff --git a/code/modules/mob/living/carbon/human/species_types/bugmen.dm b/code/modules/mob/living/carbon/human/species_types/bugmen.dm index 2a955e28a4..16b371c772 100644 --- a/code/modules/mob/living/carbon/human/species_types/bugmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/bugmen.dm @@ -13,10 +13,11 @@ meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/insect liked_food = MEAT | FRUIT disliked_food = TOXIC - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL exotic_bloodtype = "BUG" exotic_blood_color = BLOOD_COLOR_BUG tail_type = "mam_tail" wagging_type = "mam_waggingtail" - species_type = "insect" \ No newline at end of file + species_type = "insect" + + allowed_limb_ids = list("insect","apid","moth","moth_not_greyscale") diff --git a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm index 7706d4a9d5..534536d6e7 100644 --- a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm @@ -2,7 +2,6 @@ name = "Anthromorph" id = "mammal" default_color = "4B4B4B" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE) inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BEAST mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "deco_wings" = "None", @@ -17,3 +16,5 @@ tail_type = "mam_tail" wagging_type = "mam_waggingtail" species_type = "furry" + + allowed_limb_ids = list("mammal","aquatic","avian") \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm index 3aef14c256..806782a8b2 100644 --- a/code/modules/mob/living/carbon/human/species_types/ipc.dm +++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm @@ -3,7 +3,6 @@ id = "ipc" say_mod = "beeps" default_color = "00FF00" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL blacklisted = 0 sexes = 0 species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE) diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index f5a2f807e4..0f62953e6c 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -21,6 +21,8 @@ species_type = "plant" + allowed_limb_ids = list("pod","mush") + /datum/species/pod/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() C.faction |= "plants" diff --git a/code/modules/mob/living/carbon/human/species_types/synthliz.dm b/code/modules/mob/living/carbon/human/species_types/synthliz.dm index 173411eaa1..70afa2f476 100644 --- a/code/modules/mob/living/carbon/human/species_types/synthliz.dm +++ b/code/modules/mob/living/carbon/human/species_types/synthliz.dm @@ -1,7 +1,6 @@ /datum/species/synthliz name = "Synthetic Lizardperson" id = "synthliz" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL say_mod = "beeps" default_color = "00FF00" species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,HAS_FLESH,HAS_BONE) diff --git a/code/modules/mob/living/carbon/human/species_types/synths.dm b/code/modules/mob/living/carbon/human/species_types/synths.dm index c10521dfd9..3d55ce1027 100644 --- a/code/modules/mob/living/carbon/human/species_types/synths.dm +++ b/code/modules/mob/living/carbon/human/species_types/synths.dm @@ -61,7 +61,7 @@ mutant_organs = S.mutant_organs.Copy() nojumpsuit = S.nojumpsuit no_equip = S.no_equip.Copy() - limbs_id = S.limbs_id + limbs_id = S.mutant_bodyparts["limbs_id"] use_skintones = S.use_skintones fixed_mut_color = S.fixed_mut_color hair_color = S.hair_color diff --git a/code/modules/mob/living/carbon/human/species_types/xeno.dm b/code/modules/mob/living/carbon/human/species_types/xeno.dm index db34d1ae45..ddd1c86f0a 100644 --- a/code/modules/mob/living/carbon/human/species_types/xeno.dm +++ b/code/modules/mob/living/carbon/human/species_types/xeno.dm @@ -4,7 +4,6 @@ id = "xeno" say_mod = "hisses" default_color = "00FF00" - icon_limbs = DEFAULT_BODYPART_ICON_CITADEL species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR) mutant_bodyparts = list("xenotail"="Xenomorph Tail","xenohead"="Standard","xenodorsal"="Standard", "mam_body_markings" = "Xeno","mcolor" = "0F0","mcolor2" = "0F0","mcolor3" = "0F0","taur" = "None", "legs" = "Digitigrade") attack_verb = "slash" diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index e399ddf872..9b39438563 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -660,7 +660,7 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if //produces a key based on the human's limbs /mob/living/carbon/human/generate_icon_render_key() - . = "[dna.species.limbs_id]" + . = "[dna.species.mutant_bodyparts["limbs_id"]]" if(dna.check_mutation(HULK)) . += "-coloured-hulk" diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index ad1a3bc9b9..fe5a78de21 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -8,8 +8,6 @@ spill_organs(no_brain, no_organs, no_bodyparts) - release_vore_contents(silent = TRUE) // return of the bomb safe internals. - if(!no_bodyparts) spread_bodyparts(no_brain, no_organs) @@ -46,7 +44,6 @@ buckled.unbuckle_mob(src, force = TRUE) dust_animation() - release_vore_contents(silent = TRUE) //technically grief protection, I guess? if they're SM'd it doesn't matter seconds after anyway. spawn_dust(just_ash) QDEL_IN(src,5) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes. @@ -103,5 +100,5 @@ for(var/s in sharedSoullinks) var/datum/soullink/S = s S.sharerDies(gibbed) - + release_vore_contents(silent = TRUE) return TRUE diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 97de8bfd6c..a735baceae 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -9,6 +9,11 @@ key_third_person = "blushes" message = "blushes." +/datum/emote/living/blush/run_emote(mob/user, params) + . = ..() + if(. && isipcperson(user)) + do_fake_sparks(5,FALSE,user) + /datum/emote/living/bow key = "bow" key_third_person = "bows" diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 9018c49b2c..d8299d42ad 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -289,50 +289,57 @@ return FALSE return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range) -/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/weldingtool) && (user.a_intent != INTENT_HARM || user == src)) +/mob/living/silicon/robot/proc/attempt_welder_repair(obj/item/weldingtool/W, mob/user) + if (!getBruteLoss()) + to_chat(user, "[src] is already in good condition!") + return + if (!W.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away. + return + user.DelayNextAction(CLICK_CD_MELEE) + if(src == user) + to_chat(user, "You start fixing yourself...") + if(!W.use_tool(src, user, 50)) + return + adjustBruteLoss(-10) + else + to_chat(user, "You start fixing [src]...") + if(!do_after(user, 30, target = src)) + return + adjustBruteLoss(-30) + updatehealth() + add_fingerprint(user) + visible_message("[user] has fixed some of the dents on [src].") + +/mob/living/silicon/robot/proc/attempt_cable_repair(obj/item/stack/cable_coil/W, mob/user) + if (getFireLoss() > 0 || getToxLoss() > 0) user.DelayNextAction(CLICK_CD_MELEE) - if (!getBruteLoss()) - to_chat(user, "[src] is already in good condition!") - return - if (!W.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away. - return if(src == user) to_chat(user, "You start fixing yourself...") - if(!W.use_tool(src, user, 50)) + if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT)) + to_chat(user, "You need more cable to repair [src]!") return - adjustBruteLoss(-10) + adjustFireLoss(-10) + adjustToxLoss(-10) else to_chat(user, "You start fixing [src]...") - if(!do_after(user, 30, target = src)) + if(!W.use_tool(src, user, 30, 1)) + to_chat(user, "You need more cable to repair [src]!") return - adjustBruteLoss(-30) - updatehealth() - add_fingerprint(user) - visible_message("[user] has fixed some of the dents on [src].") + adjustFireLoss(-30) + adjustToxLoss(-30) + updatehealth() + user.visible_message("[user] has fixed some of the burnt wires on [src].", "You fix some of the burnt wires on [src].") + else + to_chat(user, "The wires seem fine, there's no need to fix them.") + +/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/weldingtool) && (user.a_intent != INTENT_HARM || user == src)) + INVOKE_ASYNC(src, .proc/attempt_welder_repair, W, user) return else if(istype(W, /obj/item/stack/cable_coil) && wiresexposed) - user.DelayNextAction(CLICK_CD_MELEE) - if (getFireLoss() > 0 || getToxLoss() > 0) - if(src == user) - to_chat(user, "You start fixing yourself...") - if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT)) - to_chat(user, "You need more cable to repair [src]!") - return - adjustFireLoss(-10) - adjustToxLoss(-10) - else - to_chat(user, "You start fixing [src]...") - if(!W.use_tool(src, user, 30, 1)) - to_chat(user, "You need more cable to repair [src]!") - return - adjustFireLoss(-30) - adjustToxLoss(-30) - updatehealth() - user.visible_message("[user] has fixed some of the burnt wires on [src].", "You fix some of the burnt wires on [src].") - else - to_chat(user, "The wires seem fine, there's no need to fix them.") + INVOKE_ASYNC(src, .proc/attempt_cable_repair, W, user) + return else if(istype(W, /obj/item/crowbar)) // crowbar means open or close the cover if(opened) 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 525388fa61..bd3a6e8232 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -661,7 +661,7 @@ Difficulty: Normal continue to_chat(M.occupant, "Your [M.name] is struck by a [name]!") playsound(M,'sound/weapons/sear.ogg', 50, 1, -4) - M.take_damage(damage, BURN, 0, 0) + M.take_damage(damage, BURN, 0, 0, null, 50) /obj/effect/hierophant name = "hierophant beacon" diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm index 12f0b41d4e..fa67fd8e3b 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm @@ -200,6 +200,8 @@ L.Stun(75) L.adjustBruteLoss(rand(15,20)) // Less stun more harm latched = TRUE + for(var/obj/mecha/M in loc) + M.take_damage(20, BRUTE, null, null, null, 25) if(!latched) retract() else diff --git a/code/modules/mob/living/ventcrawling.dm b/code/modules/mob/living/ventcrawling.dm index 36a596f42e..6661d0ccea 100644 --- a/code/modules/mob/living/ventcrawling.dm +++ b/code/modules/mob/living/ventcrawling.dm @@ -19,8 +19,11 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, typecacheof(list( to_chat(src, "You can't vent crawl while you're restrained!") return if(has_buckled_mobs()) - to_chat(src, "You can't vent crawl with other creatures on you!") - return + // attempt once + unbuckle_all_mobs() + if(has_buckled_mobs()) + to_chat(src, "You can't vent crawl with other creatures on you!") + return if(buckled) to_chat(src, "You can't vent crawl while buckled!") return diff --git a/code/modules/newscaster/newscaster_machine.dm b/code/modules/newscaster/newscaster_machine.dm index cb2d49fc64..470b34e82c 100644 --- a/code/modules/newscaster/newscaster_machine.dm +++ b/code/modules/newscaster/newscaster_machine.dm @@ -95,6 +95,10 @@ GLOBAL_LIST_EMPTY(allCasters) . = ..() update_icon() +/obj/machinery/newscaster/attack_ghost(mob/dead/observer/user) + if(istype(user)) + user.read_news() + /obj/machinery/newscaster/ui_interact(mob/user) . = ..() if(ishuman(user) || issilicon(user)) diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 78a20e5b62..54b1362518 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -66,7 +66,7 @@ var/energy_transfer = delta_temperature*hot_air_heat_capacity*cold_air_heat_capacity/(hot_air_heat_capacity+cold_air_heat_capacity) var/heat = energy_transfer*(1-efficiency) - lastgen += energy_transfer*efficiency + lastgen += LOGISTIC_FUNCTION(1000000,0.0034,delta_temperature,2000) hot_air.set_temperature(hot_air.return_temperature() - energy_transfer/hot_air_heat_capacity) cold_air.set_temperature(cold_air.return_temperature() + heat/cold_air_heat_capacity) diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index 0b00c89c02..8cd49bdf16 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -149,6 +149,9 @@ icon = 'icons/obj/ammo.dmi' icon_state = "shotgunclip" caliber = "shotgun" // slapped in to allow shell mix n match + slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKET + w_class = WEIGHT_CLASS_NORMAL + w_volume = ITEM_VOLUME_STRIPPER_CLIP ammo_type = /obj/item/ammo_casing/shotgun max_ammo = 4 var/pixeloffsetx = 4 diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 9b25a80680..926ed27854 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -19,6 +19,10 @@ 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. + /// Last world.time of spray + var/last_spray = 0 + /// Spray cooldown + var/spray_cooldown = CLICK_CD_MELEE var/can_fill_from_container = TRUE amount_per_transfer_from_this = 5 volume = 250 @@ -27,8 +31,6 @@ /obj/item/reagent_containers/spray/afterattack(atom/A, mob/user) . = ..() - if(!user.CheckActionCooldown(CLICK_CD_MELEE)) - return if(istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics)) return @@ -49,7 +51,8 @@ to_chat(user, "[src] is empty!") return - spray(A) + if(!spray(A)) + return playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6) user.last_action = world.time @@ -64,10 +67,10 @@ if(reagents.has_reagent(/datum/reagent/lube)) message_admins("[ADMIN_LOOKUPFLW(user)] fired Space lube from \a [src] at [ADMIN_VERBOSEJMP(T)].") log_game("[key_name(user)] fired Space lube from \a [src] at [AREACOORD(T)].") - return - /obj/item/reagent_containers/spray/proc/spray(atom/A) + if((last_spray + spray_cooldown) > world.time) + return 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, NONE, NO_REAGENTS_VALUE) @@ -79,10 +82,11 @@ 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+ spray_delay * INVERSE(range)), 2) - do_spray(A, wait_step, D, range, puff_reagent_left) + last_spray = world.time + INVOKE_ASYNC(src, .proc/do_spray, A, wait_step, D, range, puff_reagent_left) + return TRUE /obj/item/reagent_containers/spray/proc/do_spray(atom/A, wait_step, obj/effect/decal/chempuff/D, range, puff_reagent_left) - set waitfor = FALSE var/range_left = range for(var/i=0, i 125) + to_chat(user, "A color that saturated? Surely not!") + return var/range = input(user, "Enter range (0 - [max_light_beam_distance])", "Range Select", 0) as null|num if(!isnum(range)) return @@ -396,4 +400,4 @@ #undef BLURRY_VISION_ONE #undef BLURRY_VISION_TWO -#undef BLIND_VISION_THREE \ No newline at end of file +#undef BLIND_VISION_THREE diff --git a/html/changelog.html b/html/changelog.html index fd590f4673..870578cae5 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,71 @@ -->
+

27 August 2020

+

silicons updated:

+ +

timothyteakettle updated:

+ + +

26 August 2020

+

ancientpower updated:

+ +

silicons updated:

+ + +

25 August 2020

+

Hatterhat updated:

+ +

Literallynotpickles updated:

+ +

Putnam3145 updated:

+ +

raspy-on-osu updated:

+ +

timothyteakettle updated:

+ + +

24 August 2020

+

MrJWhit updated:

+ +

silicons updated:

+ +

timothyteakettle updated:

+ +

zeroisthebiggay updated:

+ +

23 August 2020

DeltaFire15 updated:

GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index d4243a01ec..fd9523fb15 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -27060,3 +27060,43 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - rscadd: suiciding with the temporal katana omae wa mou shinderius you into the shadow realm - soundadd: twilight isnt earrape +2020-08-24: + MrJWhit: + - bugfix: Fixes areas on expanded airlocks + silicons: + - bugfix: wormhole jaunters work + - tweak: wormhole jaunters no longer get interference from bags of holding + - bugfix: airlocks now only shock on pulse/wirecutters instead of on tgui panel + open. + timothyteakettle: + - rscadd: three new items are in the loadout for all donators + zeroisthebiggay: + - rscadd: contraband black evening gloves in kinkvend +2020-08-25: + Hatterhat: + - rscadd: Insidious combat gloves have been replaced by insidious guerilla gloves. + They're generally the same, except now you can tackle with them. + Literallynotpickles: + - tweak: You can now equip handheld crew monitors on all medical-related winter + coats. + Putnam3145: + - tweak: vore now ejects occupants on death + raspy-on-osu: + - tweak: Thermoelectric Generator power output + timothyteakettle: + - tweak: I.P.Cs now short their circuits when expressing emotion, causing sparks + to appear around them. +2020-08-26: + ancientpower: + - tweak: Ghosts can read newscasters by clicking on them. + silicons: + - balance: hierophant vortex blasts now have 50% armor penetration vs mecha + - balance: ventcrawling now kicks off every attached/buckled mob, even for non humans. +2020-08-27: + silicons: + - tweak: eyebeam lighting can only have 128 maximum HSV saturation now. + - balance: no more shotgun stripper clips in boxes. + - balance: goliath tentacles now do 20 damage to mechs at 25% ap + timothyteakettle: + - tweak: changing your character's gender won't randomize its hairstyle and facial + hairstyle now diff --git a/html/changelogs/AutoChangeLog-pr-13182.yml b/html/changelogs/AutoChangeLog-pr-13182.yml deleted file mode 100644 index 9beb8f28c5..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13182.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "silicons" -delete-after: True -changes: - - bugfix: "airlocks now only shock on pulse/wirecutters instead of on tgui panel open." diff --git a/html/changelogs/AutoChangeLog-pr-13204.yml b/html/changelogs/AutoChangeLog-pr-13204.yml deleted file mode 100644 index e9033c299d..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13204.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "timothyteakettle" -delete-after: True -changes: - - rscadd: "three new items are in the loadout for all donators" diff --git a/html/changelogs/AutoChangeLog-pr-13209.yml b/html/changelogs/AutoChangeLog-pr-13209.yml deleted file mode 100644 index f885a4dc93..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13209.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "silicons" -delete-after: True -changes: - - bugfix: "wormhole jaunters work" - - tweak: "wormhole jaunters no longer get interference from bags of holding" diff --git a/html/changelogs/AutoChangeLog-pr-13232.yml b/html/changelogs/AutoChangeLog-pr-13232.yml new file mode 100644 index 0000000000..9571dbf2b9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13232.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - rscadd: "you can now choose a body sprite as an anthromorph or anthromorphic insect, and can choose from aquatic/avian and apid respectively (and obviously back to the defaults too)" diff --git a/html/changelogs/AutoChangeLog-pr-13239.yml b/html/changelogs/AutoChangeLog-pr-13239.yml deleted file mode 100644 index 0f01f680c6..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13239.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "zeroisthebiggay" -delete-after: True -changes: - - rscadd: "contraband black evening gloves in kinkvend" diff --git a/html/changelogs/AutoChangeLog-pr-13243.yml b/html/changelogs/AutoChangeLog-pr-13243.yml deleted file mode 100644 index a09ae5d607..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13243.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "MrJWhit" -delete-after: True -changes: - - bugfix: "Fixes areas on expanded airlocks" diff --git a/html/changelogs/AutoChangeLog-pr-13265.yml b/html/changelogs/AutoChangeLog-pr-13265.yml new file mode 100644 index 0000000000..917e5786b0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13265.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - rscadd: "an ancient game over a thousand years old has re-emerged among crewmembers - rock paper scissors" diff --git a/icons/mob/human_parts.dmi b/icons/mob/human_parts.dmi index 916686e63f..60978d55a2 100644 Binary files a/icons/mob/human_parts.dmi and b/icons/mob/human_parts.dmi differ diff --git a/icons/mob/human_parts_greyscale.dmi b/icons/mob/human_parts_greyscale.dmi index 989ec8049c..794074bfe4 100644 Binary files a/icons/mob/human_parts_greyscale.dmi and b/icons/mob/human_parts_greyscale.dmi differ diff --git a/icons/mob/wings.dmi b/icons/mob/wings.dmi index ace37b1f17..4523403344 100644 Binary files a/icons/mob/wings.dmi and b/icons/mob/wings.dmi differ diff --git a/modular_citadel/icons/mob/citadel_refs/furry_parts_greyscale.dmi b/modular_citadel/icons/mob/citadel_refs/furry_parts_greyscale.dmi deleted file mode 100644 index c8d5ceb0a6..0000000000 Binary files a/modular_citadel/icons/mob/citadel_refs/furry_parts_greyscale.dmi and /dev/null differ diff --git a/modular_citadel/icons/mob/mutant_bodyparts.dmi b/modular_citadel/icons/mob/mutant_bodyparts.dmi deleted file mode 100644 index 6098dd3567..0000000000 Binary files a/modular_citadel/icons/mob/mutant_bodyparts.dmi and /dev/null differ diff --git a/tgstation.dme b/tgstation.dme index 79c4274722..de6ee1a6bf 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -96,6 +96,7 @@ #include "code\__DEFINES\reagents_specific_heat.dm" #include "code\__DEFINES\research.dm" #include "code\__DEFINES\robots.dm" +#include "code\__DEFINES\rockpaperscissors.dm" #include "code\__DEFINES\role_preferences.dm" #include "code\__DEFINES\rust_g.dm" #include "code\__DEFINES\say.dm"