diff --git a/README.md b/README.md index 6fccae54e43c1..a28f910037134 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,15 @@ Space Station 13 is a paranoia-laden round-based roleplaying game set against th **[How to compile in VSCode and other build options](tools/build/README.md).** -## Contributors -[Guides for Contributors](.github/CONTRIBUTING.md) +## Getting started -[/tg/station HACKMD account](https://hackmd.io/@tgstation) - Design documentation here +For contribution guidelines refer to the [Guides for Contributors](.github/CONTRIBUTING.md). -[Interested in some starting lore?](https://github.com/tgstation/common_core) +For getting started (dev env, compilation) see the HackMD document [here](https://hackmd.io/@tgstation/HJ8OdjNBc#tgstation-Development-Guide). + +For overall design documentation see [HackMD](https://hackmd.io/@tgstation). + +For lore, [see Common Core](https://github.com/tgstation/common_core). ## LICENSE diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 97a86538e3ad9..ac5d51cdc9921 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -649,7 +649,8 @@ #define GRADIENT_APPLIES_TO_FACIAL_HAIR (1<<1) // Hair masks -#define HAIR_MASK_HIDE_ABOVE_45_DEG_MEDIUM "hide_above_45deg" +#define HAIR_MASK_HIDE_ABOVE_45_DEG_MEDIUM "hide_above_45deg_medium" +#define HAIR_MASK_HIDE_ABOVE_45_DEG_LOW "hide_above_45deg_low" // Height defines // - They are numbers so you can compare height values (x height < y height) diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm index e50794aa6d310..8d05402676846 100644 --- a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm +++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm @@ -261,7 +261,7 @@ candidates -= player else if(is_centcom_level(player.z)) candidates -= player // We don't autotator people in CentCom - else if(player.mind && (player.mind.special_role || player.mind.can_roll_midround())) + else if(player.mind && (player.mind.special_role || !player.mind.can_roll_midround())) candidates -= player // We don't autotator people with roles already /datum/dynamic_ruleset/midround/from_living/autotraitor/execute() @@ -310,7 +310,7 @@ continue if(isnull(player.mind)) continue - if(player.mind.special_role || player.mind.can_roll_midround()) + if(player.mind.special_role || !player.mind.can_roll_midround()) continue candidates += player @@ -479,7 +479,7 @@ candidates -= player continue - if(player.mind && (player.mind.special_role || player.mind.can_roll_midround())) + if(player.mind && (player.mind.special_role || !player.mind.can_roll_midround())) candidates -= player /datum/dynamic_ruleset/midround/from_living/blob_infection/execute() diff --git a/code/datums/components/blob_minion.dm b/code/datums/components/blob_minion.dm index b74f813894db5..78bff449317ce 100644 --- a/code/datums/components/blob_minion.dm +++ b/code/datums/components/blob_minion.dm @@ -139,6 +139,7 @@ /// We only speak telepathically to blobs /datum/component/blob_minion/proc/on_try_speech(mob/living/minion, message, ignore_spam, forced) SIGNAL_HANDLER + minion.log_talk(message, LOG_SAY, tag = "blob hivemind telepathy") var/spanned_message = minion.say_quote(message) var/rendered = span_blob("\[Blob Telepathy\] [minion.real_name] [spanned_message]") relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, minion) diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index b979c9cda0f5c..b51f7097a9fbf 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -23,7 +23,7 @@ // argument handling // if the precision is not specified, default to 0, but apply BoH penalties - if (isnull(precision)) + if(isnull(precision)) precision = 0 switch(channel) @@ -40,7 +40,7 @@ to_chat(MM, span_warning("The bluespace interface on your bag of holding interferes with the teleport!")) // if effects are not specified and not explicitly disabled, sparks - if ((!effectin || !effectout) && !no_effects) + if((!effectin || !effectout) && !no_effects) var/datum/effect_system/spark_spread/sparks = new sparks.set_up(5, 1, teleatom) if (!effectin) @@ -78,10 +78,16 @@ return TRUE tele_play_specials(teleatom, curturf, effectin, asoundin) + var/success = teleatom.forceMove(destturf) - if(success) - log_game("[key_name(teleatom)] has teleported from [loc_name(curturf)] to [loc_name(destturf)]") - tele_play_specials(teleatom, destturf, effectout, asoundout) + if(!success) + return FALSE + + . = TRUE + /* Past this point, the teleport is successful and you can assume that they're already there */ + + log_game("[key_name(teleatom)] has teleported from [loc_name(curturf)] to [loc_name(destturf)]") + tele_play_specials(teleatom, destturf, effectout, asoundout) if(ismob(teleatom)) var/mob/M = teleatom @@ -90,7 +96,22 @@ SEND_SIGNAL(teleatom, COMSIG_MOVABLE_POST_TELEPORT, destination, channel) - return TRUE + //We need to be sure that the buckled mobs can teleport too + if(teleatom.has_buckled_mobs()) + for(var/mob/living/rider in teleatom.buckled_mobs) + //just in case it fails, but the mob gets unbuckled anyways even if it passes + teleatom.unbuckle_mob(rider, TRUE, FALSE) + + var/rider_success = do_teleport(rider, destturf, precision, channel=channel, no_effects=TRUE) + if(!rider_success) + continue + + if(get_turf(rider) != destturf) //precision made them teleport somewhere else + to_chat(rider, span_warning("As you reorient your senses, you realize you aren't riding [teleatom] anymore!")) + continue + + // [mob/living].forceMove() forces mobs to unbuckle, so we need to buckle them again + teleatom.buckle_mob(rider, force=TRUE) /proc/tele_play_specials(atom/movable/teleatom, atom/location, datum/effect_system/effect, sound) if(!location) diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index f06ac5d920916..4104367a73e93 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -215,23 +215,25 @@ if(target == src) return FALSE - // Check if the target to buckle isn't INSIDE OF A WALL - if(!isopenturf(loc) || !isopenturf(target.loc)) - return FALSE - - // Check if the target to buckle isn't A SOLID OBJECT (not including vehicles) var/turf/ground = get_turf(src) - if(ground.is_blocked_turf(exclude_mobs = TRUE, source_atom = src)) - return FALSE + // If we're not already on the same turf as our target... + if(get_turf(target) != ground) + // Check if the target to buckle isn't INSIDE OF A WALL + if(!isopenturf(loc) || !isopenturf(target.loc)) + return FALSE + + // Check if the target to buckle isn't INSIDE A SOLID OBJECT (not including vehicles) + if(ground.is_blocked_turf(exclude_mobs = TRUE, source_atom = src)) + return FALSE + + // If we're checking the loc, make sure the target is on the thing we're bucking them to. + if(check_loc && !target.Adjacent(src)) + return FALSE // Check if this atom can have things buckled to it. if(!can_buckle && !force) return FALSE - // If we're checking the loc, make sure the target is on the thing we're bucking them to. - if(check_loc && !target.Adjacent(src)) - return FALSE - // Make sure the target isn't already buckled to something. if(target.buckled) return FALSE diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 28d52d3260456..cba2a3ee46cf4 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1027,36 +1027,93 @@ ///Called BEFORE the object is ground up - use this to change grind results based on conditions. Return "-1" to prevent the grinding from occurring /obj/item/proc/on_grind() + PROTECTED_PROC(TRUE) + return SEND_SIGNAL(src, COMSIG_ITEM_ON_GRIND) ///Grind item, adding grind_results to item's reagents and transfering to target_holder if specified -/obj/item/proc/grind(datum/reagents/target_holder, mob/user) +/obj/item/proc/grind(datum/reagents/target_holder, mob/user, atom/movable/grinder = loc) + SHOULD_NOT_OVERRIDE(TRUE) + . = FALSE - if(on_grind() == -1) + if(on_grind() == -1 || target_holder.holder_full()) return + . = grind_atom(target_holder, user) + + //reccursive grinding to get all them juices + var/result + for(var/obj/item/ingredient as anything in get_all_contents_type(/obj/item)) + if(ingredient == src) + continue + + result = ingredient.grind(target_holder, user) + if(!.) + . = result + + if(. && istype(grinder)) + return grinder.blended(src, grinded = TRUE) + +///Subtypes override his proc for custom grinding +/obj/item/proc/grind_atom(datum/reagents/target_holder, mob/user) + PROTECTED_PROC(TRUE) + + . = FALSE if(length(grind_results)) target_holder.add_reagent_list(grind_results) . = TRUE - if(reagents?.total_volume) - reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) + if(reagents?.trans_to(target_holder, reagents.total_volume, transferred_by = user)) . = TRUE ///Called BEFORE the object is ground up - use this to change grind results based on conditions. Return "-1" to prevent the grinding from occurring /obj/item/proc/on_juice() + PROTECTED_PROC(TRUE) + if(!juice_typepath) return -1 + return SEND_SIGNAL(src, COMSIG_ITEM_ON_JUICE) ///Juice item, converting nutriments into juice_typepath and transfering to target_holder if specified -/obj/item/proc/juice(datum/reagents/target_holder, mob/user) +/obj/item/proc/juice(datum/reagents/target_holder, mob/user, atom/movable/juicer = loc) + SHOULD_NOT_OVERRIDE(TRUE) + + . = FALSE if(on_juice() == -1 || !reagents?.total_volume) - return FALSE + return + + . = juice_atom(target_holder, user) + + //reccursive juicing to get all them juices + var/result + for(var/obj/item/ingredient as anything in get_all_contents_type(/obj/item)) + if(ingredient == src) + continue + + result = ingredient.juice(target_holder, user) + if(!.) + . = result + + if(. && istype(juicer)) + return juicer.blended(src, grinded = FALSE) + +///Subtypes override his proc for custom juicing +/obj/item/proc/juice_atom(datum/reagents/target_holder, mob/user) + PROTECTED_PROC(TRUE) + + . = FALSE if(ispath(juice_typepath)) reagents.convert_reagent(/datum/reagent/consumable/nutriment, juice_typepath, include_source_subtypes = FALSE) reagents.convert_reagent(/datum/reagent/consumable/nutriment/vitamin, juice_typepath, include_source_subtypes = FALSE) - reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) + . = TRUE + + if(!QDELETED(target_holder)) + reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) + +///What should The atom that blended an object do with it afterwards? Default behaviour is to delete it +/atom/movable/proc/blended(obj/item/blended_item, grinded) + qdel(blended_item) return TRUE diff --git a/code/game/objects/items/cigarettes.dm b/code/game/objects/items/cigarettes.dm index 5eddcf93d4bb2..51b38d032c090 100644 --- a/code/game/objects/items/cigarettes.dm +++ b/code/game/objects/items/cigarettes.dm @@ -185,11 +185,6 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/cigarette/Initialize(mapload) . = ..() - create_reagents(chem_volume, INJECTABLE | NO_REACT) - if(list_reagents) - reagents.add_reagent_list(list_reagents) - if(starts_lit) - light() AddComponent(/datum/component/knockoff, 90, list(BODY_ZONE_PRECISE_MOUTH), slot_flags) //90% to knock off when wearing a mask AddElement(/datum/element/update_icon_updates_onmob) RegisterSignal(src, COMSIG_ATOM_TOUCHED_SPARKS, PROC_REF(sparks_touched)) @@ -201,15 +196,17 @@ CIGARETTE PACKETS ARE IN FANCY.DM initial_reagents = list_reagents,\ food_flags = FOOD_NO_EXAMINE,\ foodtypes = JUNKFOOD,\ - volume = 50,\ + volume = chem_volume,\ eat_time = 0 SECONDS,\ - tastes = list("a never before experienced flavour.", "finally sitting down after standing your entire life"),\ + tastes = list("a never before experienced flavour", "finally sitting down after standing your entire life"),\ eatverbs = list("taste"),\ - bite_consumption = 50,\ + bite_consumption = chem_volume,\ junkiness = 0,\ reagent_purity = null,\ on_consume = CALLBACK(src, PROC_REF(on_consume)),\ ) + if(starts_lit) + light() /obj/item/cigarette/Destroy() STOP_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 18891ebdd9306..fd1529eb3301c 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -135,14 +135,10 @@ return return TRUE -/obj/item/stack/grind(datum/reagents/target_holder, mob/user) +/obj/item/stack/grind_atom(datum/reagents/target_holder, mob/user) var/current_amount = get_amount() if(current_amount <= 0 || QDELETED(src)) //just to get rid of this 0 amount/deleted stack we return success return TRUE - if(on_grind() == -1) - return FALSE - if(isnull(target_holder)) - return TRUE if(reagents) reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) diff --git a/code/game/objects/items/theft_tools.dm b/code/game/objects/items/theft_tools.dm index 6f09f58169521..34176864492da 100644 --- a/code/game/objects/items/theft_tools.dm +++ b/code/game/objects/items/theft_tools.dm @@ -158,6 +158,18 @@ pulseicon = "supermatter_sliver_pulse" layer = ABOVE_MOB_LAYER +/obj/item/nuke_core/supermatter_sliver/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_FISHING_ROD_CAST, PROC_REF(on_hook)) + +/obj/item/nuke_core/supermatter_sliver/proc/on_hook(obj/item/nuke_core/supermatter_sliver/source, obj/item/fishing_rod/rod, mob/user) + SIGNAL_HANDLER + + //hook gets dusted but the rod remains intact + attackby(rod.hook, user) + + return FISHING_ROD_CAST_HANDLED + /obj/item/nuke_core/supermatter_sliver/attack_tk(mob/user) // no TK dusting memes return diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index c3072ac135540..2bbd908c04443 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -39,6 +39,7 @@ drop_sound = 'sound/items/handling/helmet/helmet_drop1.ogg' visor_toggle_up_sound = SFX_VISOR_UP visor_toggle_down_sound = SFX_VISOR_DOWN + hair_mask = HAIR_MASK_HIDE_ABOVE_45_DEG_LOW /obj/item/clothing/head/helmet/sec/Initialize(mapload) . = ..() @@ -68,6 +69,17 @@ return ..() +/obj/item/clothing/head/helmet/sec/attack_self(mob/user) + . = ..() + if(.) + return + balloon_alert(user, "[flags_inv & HIDEHAIR ? "loosening" : "tightening"] straps...") + if(!do_after(user, 3 SECONDS, src)) + return + flags_inv ^= HIDEHAIR + balloon_alert(user, "[flags_inv & HIDEHAIR ? "tightened" : "loosened"] straps") + return TRUE + /obj/item/clothing/head/helmet/sec/click_alt(mob/user) flipped_visor = !flipped_visor balloon_alert(user, "visor flipped") diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 6edbfd382f964..b55277820114d 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -139,10 +139,7 @@ reagents.add_reagent(/datum/reagent/consumable/ethanol/fruit_wine, reagent.volume, data, added_purity = reagent_purity) reagents.del_reagent(reagent.type) -/obj/item/food/grown/grind(datum/reagents/target_holder, mob/user) - if(on_grind() == -1) - return FALSE - +/obj/item/food/grown/grind_atom(datum/reagents/target_holder, mob/user) var/grind_results_num = LAZYLEN(grind_results) if(grind_results_num) var/average_purity = reagents.get_average_purity() @@ -152,9 +149,7 @@ for(var/reagent in grind_results) reagents.add_reagent(reagent, single_reagent_amount, added_purity = average_purity) - if(reagents && target_holder) - reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user) - return TRUE + return reagents?.trans_to(target_holder, reagents.total_volume, transferred_by = user) #undef BITE_SIZE_POTENCY_MULTIPLIER #undef BITE_SIZE_VOLUME_MULTIPLIER diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm index 5a3e7a044e5a0..09d4bc285645b 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm @@ -300,6 +300,7 @@ righthand_file = 'icons/mob/inhands/items/touchspell_righthand.dmi' slot_flags = null item_flags = ABSTRACT | DROPDEL + resistance_flags = FIRE_PROOF|ACID_PROOF w_class = WEIGHT_CLASS_HUGE hitsound = 'sound/items/weapons/sear.ogg' damtype = BURN @@ -399,6 +400,7 @@ w_class = WEIGHT_CLASS_HUGE slot_flags = null item_flags = ABSTRACT + resistance_flags = FIRE_PROOF|ACID_PROOF sharpness = SHARP_EDGED attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices") attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice") @@ -522,6 +524,7 @@ righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' slot_flags = null item_flags = ABSTRACT + resistance_flags = FIRE_PROOF|ACID_PROOF w_class = WEIGHT_CLASS_HUGE sharpness = SHARP_EDGED wound_bonus = -20 @@ -572,7 +575,7 @@ /obj/item/nullrod/bostaff name = "monk's staff" desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts, it is now used to harass the clown." - force = 14 + force = 10 block_chance = 40 block_sound = 'sound/items/weapons/genhit.ogg' slot_flags = ITEM_SLOT_BACK @@ -600,6 +603,10 @@ icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" return ..() +/obj/item/nullrod/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == PROJECTILE_ATTACK) + final_block_chance = 0 //Don't bring a sword to a gunfight + return ..() // Arrhythmic Knife - Lets your walk without rhythm by varying your walk speed. Can't be put away. diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 87c0cdd402918..a642a1f33eac2 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -241,7 +241,7 @@ /// Removes all malfunction-related abilities from the AI /mob/living/silicon/ai/proc/remove_malf_abilities() QDEL_NULL(modules_action) - for(var/datum/ai_module/AM in current_modules) + for(var/datum/ai_module/malf/AM in current_modules) for(var/datum/action/A in actions) if(istype(A, initial(AM.power_type))) qdel(A) diff --git a/code/modules/plumbing/plumbers/grinder_chemical.dm b/code/modules/plumbing/plumbers/grinder_chemical.dm index bd0a69e6d5e86..c631e26def6f5 100644 --- a/code/modules/plumbing/plumbers/grinder_chemical.dm +++ b/code/modules/plumbing/plumbers/grinder_chemical.dm @@ -74,7 +74,7 @@ to_chat(user, span_notice("You dump items from [tool] into the grinder.")) for(var/obj/item/obj_item in tool.contents) - grind(obj_item) + blend(obj_item) return ITEM_INTERACT_SUCCESS else if(!tool.tool_behaviour) var/action = "[grinding ? "grind" : "juice"]" @@ -83,7 +83,7 @@ return ITEM_INTERACT_BLOCKING to_chat(user, span_notice("You attempt to [action] [tool].")) - grind(tool) + blend(tool) return ITEM_INTERACT_SUCCESS /obj/machinery/plumbing/grinder_chemical/CanAllowThrough(atom/movable/mover, border_dir) @@ -97,33 +97,45 @@ /obj/machinery/plumbing/grinder_chemical/proc/on_entered(datum/source, atom/movable/AM) SIGNAL_HANDLER - INVOKE_ASYNC(src, PROC_REF(grind), AM) + if(!isitem(AM)) + return + + INVOKE_ASYNC(src, PROC_REF(blend), AM) + + +/obj/machinery/plumbing/grinder_chemical/blended(obj/item/blended_item, grinded) + //don't delete slime extracts + if(istype(blended_item, /obj/item/slime_extract)) + //so you can't regrind them for extra stuff + blended_item.grind_results = null + + blended_item.forceMove(drop_location()) + + return TRUE + + return ..() /** * Grinds/Juices the atom * Arguments * * [AM][atom] - the atom to grind or juice */ -/obj/machinery/plumbing/grinder_chemical/proc/grind(atom/AM) +/obj/machinery/plumbing/grinder_chemical/proc/blend(obj/item/I) PRIVATE_PROC(TRUE) if(!is_operational || !anchored) return if(reagents.holder_full()) return - if(!isitem(AM)) - return - var/obj/item/I = AM if((I.item_flags & ABSTRACT) || (I.flags_1 & HOLOGRAM_1)) return + if(!I.blend_requirements(src)) + return - var/result if(!grinding) - result = I.juice(reagents, usr) + I.juice(reagents, usr, src) else if(length(I.grind_results) || I.reagents?.total_volume) - result = I.grind(reagents, usr) + I.grind(reagents, usr, src) use_energy(active_power_usage) - if(result) - qdel(I) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 2aac6a457abfd..bcb6cac2f183d 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -454,6 +454,11 @@ //are we printing a valid container var/container_found = FALSE for(var/category in printable_containers) + //container found in previous iteration + if(container_found) + break + + //find for matching typepath for(var/obj/item/reagent_containers/container as anything in printable_containers[category]) if(target == container) container_found = TRUE diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm index 141fb7c4e6fb0..7716784e4b438 100644 --- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm +++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm @@ -434,43 +434,33 @@ playsound(src, 'sound/machines/juicer.ogg', 20, TRUE) var/total_weight - for(var/obj/item/weapon in src) - if((weapon in component_parts) || weapon == beaker) - continue + var/item_weight + for(var/obj/item/ingredient in contents) if(beaker.reagents.holder_full()) break + if((ingredient in component_parts) || ingredient == beaker) + continue + + //record item weight before & after blending + item_weight = ingredient.w_class - //recursively process everything inside this atom - var/item_processed = FALSE - var/item_weight = weapon.w_class - for(var/obj/item/ingredient as anything in weapon.get_all_contents_type(/obj/item)) - if(beaker.reagents.holder_full()) - break - - if(juicing) - if(!ingredient.juice(beaker.reagents, user)) - to_chat(user, span_danger("[src] shorts out as it tries to juice up [ingredient], and transfers it back to storage.")) - continue - item_processed = TRUE - else if(length(ingredient.grind_results) || ingredient.reagents?.total_volume) - if(!ingredient.grind(beaker.reagents, user)) - if(isstack(ingredient)) - to_chat(user, span_notice("[src] attempts to grind as many pieces of [ingredient] as possible.")) - else - to_chat(user, span_danger("[src] shorts out as it tries to grind up [ingredient], and transfers it back to storage.")) - continue - item_processed = TRUE + if(juicing) + if(!ingredient.juice(beaker.reagents, user)) + to_chat(user, span_danger("[src] shorts out as it tries to juice up [ingredient], and transfers it back to storage.")) + continue + else if(!ingredient.grind(beaker.reagents, user)) + if(isstack(ingredient)) + to_chat(user, span_notice("[src] attempts to grind as many pieces of [ingredient] as possible.")) + else + to_chat(user, span_danger("[src] shorts out as it tries to grind up [ingredient], and transfers it back to storage.")) + continue //happens only for stacks where some of the sheets were grinded so we roughly compute the weight grinded - if(item_weight != weapon.w_class) - total_weight += item_weight - weapon.w_class + if(item_weight != ingredient.w_class) + total_weight += item_weight - ingredient.w_class else total_weight += item_weight - //delete only if operation was successfull for atleast 1 item(also delete atoms for whom only some of its contents were processed as they are non functional now) - if(item_processed) - qdel(weapon) - //use power according to the total weight of items grinded use_energy((active_power_usage * (duration / 1 SECONDS)) * (total_weight / maximum_weight)) diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index b4493eb2ae4ab..0c6d638fca18d 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -506,11 +506,17 @@ if(grinded) to_chat(user, span_warning("There is something inside already!")) return - if(I.juice_typepath || I.grind_results) + if(!I.blend_requirements(src)) + to_chat(user, span_warning("Cannot grind this!")) + return + if(length(I.grind_results) || I.reagents?.total_volume) I.forceMove(src) grinded = I - return - to_chat(user, span_warning("You can't grind this!")) + +/obj/item/reagent_containers/cup/mortar/blended(obj/item/blended_item, grinded) + src.grinded = null + + return ..() /obj/item/reagent_containers/cup/mortar/proc/grind_item(obj/item/item, mob/living/carbon/human/user) if(item.flags_1 & HOLOGRAM_1) @@ -520,13 +526,12 @@ if(!item.grind(reagents, user)) if(isstack(item)) - to_chat(usr, span_notice("[src] attempts to grind as many pieces of [item] as possible.")) + to_chat(user, span_notice("[src] attempts to grind as many pieces of [item] as possible.")) else to_chat(user, span_danger("You fail to grind [item].")) return + to_chat(user, span_notice("You grind [item] into a nice powder.")) - grinded = null - QDEL_NULL(item) /obj/item/reagent_containers/cup/mortar/proc/juice_item(obj/item/item, mob/living/carbon/human/user) if(item.flags_1 & HOLOGRAM_1) @@ -537,9 +542,8 @@ if(!item.juice(reagents, user)) to_chat(user, span_notice("You fail to juice [item].")) return + to_chat(user, span_notice("You juice [item] into a fine liquid.")) - grinded = null - QDEL_NULL(item) //Coffeepots: for reference, a standard cup is 30u, to allow 20u for sugar/sweetener/milk/creamer /obj/item/reagent_containers/cup/coffeepot diff --git a/html/changelogs/AutoChangeLog-pr-87489.yml b/html/changelogs/AutoChangeLog-pr-87489.yml deleted file mode 100644 index 8ad3a9a7489c0..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87489.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "grungussuss" -delete-after: True -changes: - - sound: "only insulated,nitrile, enhanced retrieval, latex, boxing, improvised gripper gloves have an equip sound" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87684.yml b/html/changelogs/AutoChangeLog-pr-87684.yml deleted file mode 100644 index e13ae3fcc6cf5..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87684.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "tontyGH" -delete-after: True -changes: - - server: "mob/camera has been renamed to mob/eye, which may break downstreams" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87706.yml b/html/changelogs/AutoChangeLog-pr-87706.yml deleted file mode 100644 index 0265c8918a5c8..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87706.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Ghommie" -delete-after: True -changes: - - bugfix: "Fixed fishing rod duping with poly belts and shapeshift spells." - - spellcheck: "Fixed a small typo when examining fishing rods." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87735.yml b/html/changelogs/AutoChangeLog-pr-87735.yml new file mode 100644 index 0000000000000..b304247196cc0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-87735.yml @@ -0,0 +1,6 @@ +author: "SyncIt21" +delete-after: True +changes: + - bugfix: "mortar pedestal now grinds & juices items that previously could not be processed" + - bugfix: "plumbing grinder won't destroy slime extracts after grinding" + - refactor: "grinding & juicing code has been refactored overall. Please report bugs on github" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87741.yml b/html/changelogs/AutoChangeLog-pr-87741.yml deleted file mode 100644 index e27aee03b3bf4..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87741.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SmArtKar" -delete-after: True -changes: - - bugfix: "Fixed wings and jetpacks sometimes preventing you from opening doors" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87744.yml b/html/changelogs/AutoChangeLog-pr-87744.yml deleted file mode 100644 index c3ac14a47e82b..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87744.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Xander3359" -delete-after: True -changes: - - bugfix: "fix blade ascension not giving you the ring of blades" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87749.yml b/html/changelogs/AutoChangeLog-pr-87749.yml deleted file mode 100644 index bf27ebeb9319e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87749.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "timothymtorres" -delete-after: True -changes: - - admin: "Admins can now add/remove TRAIT_EVIL from mobs." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87756.yml b/html/changelogs/AutoChangeLog-pr-87756.yml deleted file mode 100644 index a5c9f801d186e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87756.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "OrionTheFox" -delete-after: True -changes: - - image: "redid most basic drinking glass sprites, and moved several drinks to use the same color system as beakers. Please bug report any incorrect colored drinks or juices!" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87759.yml b/html/changelogs/AutoChangeLog-pr-87759.yml deleted file mode 100644 index 612020fc7e78c..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87759.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "harryob" -delete-after: True -changes: - - bugfix: "the abductor console now correctly loads images of equipment" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87762.yml b/html/changelogs/AutoChangeLog-pr-87762.yml new file mode 100644 index 0000000000000..6b64fb9da5557 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-87762.yml @@ -0,0 +1,4 @@ +author: "Majkl-J" +delete-after: True +changes: + - bugfix: "Losing malf no longer wipes nonmalf AI abilities" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87775.yml b/html/changelogs/AutoChangeLog-pr-87775.yml new file mode 100644 index 0000000000000..f66a13a25b250 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-87775.yml @@ -0,0 +1,5 @@ +author: "necromanceranne" +delete-after: True +changes: + - bugfix: "Abstract nullrod types can no longer be burned or melted with acid." + - bugfix: "Monk staff now properly does not block projectiles, and uses the correct force before being wielded." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87780.yml b/html/changelogs/AutoChangeLog-pr-87780.yml deleted file mode 100644 index 159c49658736a..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-87780.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SyncIt21" -delete-after: True -changes: - - bugfix: "chem master validates selected container in UI so no more href exploits" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-87784.yml b/html/changelogs/AutoChangeLog-pr-87784.yml new file mode 100644 index 0000000000000..bbd6cd5293395 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-87784.yml @@ -0,0 +1,4 @@ +author: "SyncIt21" +delete-after: True +changes: + - bugfix: "Super matter sliver dusts fishing hooks & cannot be picked up by them" \ No newline at end of file diff --git a/html/changelogs/archive/2024-11.yml b/html/changelogs/archive/2024-11.yml index d5e17a66462b0..3c70b294dc65c 100644 --- a/html/changelogs/archive/2024-11.yml +++ b/html/changelogs/archive/2024-11.yml @@ -311,3 +311,35 @@ unit0016: - code_imp: Mappers can now opt out of automatically linking their up/down station traits. +2024-11-10: + Ghommie: + - bugfix: Fixed fishing rod duping with poly belts and shapeshift spells. + - spellcheck: Fixed a small typo when examining fishing rods. + Majkl-J: + - bugfix: Cigarettes can be injected again and have the right amount of nicotine + OrionTheFox: + - image: redid most basic drinking glass sprites, and moved several drinks to use + the same color system as beakers. Please bug report any incorrect colored drinks + or juices! + Rhials: + - bugfix: Blob Overmind/Minion/Blobbernaut speech is now logged. Beware. + SmArtKar: + - bugfix: Fixed wings and jetpacks sometimes preventing you from opening doors + SyncIt21: + - bugfix: chem master validates selected container in UI so no more href exploits + Xander3359: + - bugfix: fix blade ascension not giving you the ring of blades + grungussuss: + - qol: security helmet straps can be loosened to show hair + - sound: only insulated,nitrile, enhanced retrieval, latex, boxing, improvised gripper + gloves have an equip sound + harryob: + - bugfix: dynamic rulesets can get candidates for their roles + - bugfix: the abductor console now correctly loads images of equipment + timothymtorres: + - admin: Admins can now add/remove TRAIT_EVIL from mobs. + tontyGH: + - server: mob/camera has been renamed to mob/eye, which may break downstreams + - bugfix: Teleporting while buckled to something now works as expected + - bugfix: You can buckle to anything if you share the same tile (cause at that point + it doesn't matter if there's a wall, right?) diff --git a/icons/mob/human/hair_masks.dmi b/icons/mob/human/hair_masks.dmi index 45ecb761d9a54..5dbd4917a87e3 100644 Binary files a/icons/mob/human/hair_masks.dmi and b/icons/mob/human/hair_masks.dmi differ diff --git a/tools/Tgstation.DiscordDiscussions/Program.cs b/tools/Tgstation.DiscordDiscussions/Program.cs index 1989ef6145070..8766418cfec82 100644 --- a/tools/Tgstation.DiscordDiscussions/Program.cs +++ b/tools/Tgstation.DiscordDiscussions/Program.cs @@ -174,7 +174,7 @@ async Task RunAsync(string[] args) var isReopen = Boolean.Parse(args[7]); var joinLink = args.Length > 8 ? args[8] : null; - var prTitle = Environment.GetEnvironmentVariable("GITHUB_PULL_REQUEST_TITLE"); + var prTitle = Environment.GetEnvironmentVariable("GITHUB_PULL_REQUEST_TITLE")!; var gitHubClient = new GitHubClient(new ProductHeaderValue("Tgstation.DiscordDiscussions")) { @@ -214,6 +214,12 @@ async Task RunAsync(string[] args) var prLink = $"https://github.com/{repoOwner}/{repoName}/pull/{prNumber}"; var messageContent = $"#{prNumber} - {prTitle}"; + // thread titles can only be 100 long + if (messageContent.Length > 100) + { + messageContent = $"#{prNumber} - {prTitle[..^(messageContent.Length - 97)]}..."; + } + var channelsClient = serviceProvider.GetRequiredService(); var channelId = new Snowflake(discussionsChannelId);