diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index d2c2168827f..14246f7403b 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -39846,7 +39846,6 @@ "mNO" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/cloth_pile, /obj/effect/turf_decal/tile/blue/opposingcorners{ dir = 1 }, diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 9c7a9f09e4b..c6634a15b47 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -101,6 +101,8 @@ #define NOAUGMENTS 17 ///will be assigned a universal vampire themed last name shared by their department. this is preferenced! #define BLOOD_CLANS 18 +/// Similar to NOEYESPRITES but only excludes the eyeless overlay +#define NOEYEHOLES 19 //organ slots #define ORGAN_SLOT_ADAMANTINE_RESONATOR "adamantine_resonator" @@ -198,31 +200,6 @@ GLOBAL_LIST_INIT(organ_process_order, list( //Defines for Golem Species IDs #define SPECIES_GOLEM "golem" -#define SPECIES_GOLEM_ADAMANTINE "a_golem" -#define SPECIES_GOLEM_PLASMA "p_golem" -#define SPECIES_GOLEM_DIAMOND "diamond_golem" -#define SPECIES_GOLEM_GOLD "gold_golem" -#define SPECIES_GOLEM_SILVER "silver_golem" -#define SPECIES_GOLEM_PLASTEEL "plasteel_golem" -#define SPECIES_GOLEM_TITANIUM "titanium_golem" -#define SPECIES_GOLEM_PLASTITANIUM "plastitanium_golem" -#define SPECIES_GOLEM_ALIEN "alloy_golem" -#define SPECIES_GOLEM_WOOD "wood_golem" -#define SPECIES_GOLEM_URANIUM "uranium_golem" -#define SPECIES_GOLEM_SAND "sand_golem" -#define SPECIES_GOLEM_GLASS "glass_golem" -#define SPECIES_GOLEM_BLUESPACE "bluespace_golem" -#define SPECIES_GOLEM_BANANIUM "ba_golem" -#define SPECIES_GOLEM_CULT "cultgolem" -#define SPECIES_GOLEM_CLOTH "clothgolem" -#define SPECIES_GOLEM_PLASTIC "plastic_golem" -#define SPECIES_GOLEM_BRONZE "bronze_golem" -#define SPECIES_GOLEM_CARDBOARD "c_golem" -#define SPECIES_GOLEM_LEATHER "leather_golem" -#define SPECIES_GOLEM_DURATHREAD "d_golem" -#define SPECIES_GOLEM_BONE "b_golem" -#define SPECIES_GOLEM_SNOW "sn_golem" -#define SPECIES_GOLEM_HYDROGEN "metallic_hydrogen_golem" // Defines for used in creating "perks" for the species preference pages. /// A key that designates UI icon displayed on the perk. @@ -239,3 +216,63 @@ GLOBAL_LIST_INIT(organ_process_order, list( #define SPECIES_POSITIVE_PERK "positive" #define SPECIES_NEGATIVE_PERK "negative" #define SPECIES_NEUTRAL_PERK "neutral" + +/// Golem food defines +#define GOLEM_FOOD_IRON "golem_food_iron" +#define GOLEM_FOOD_GLASS "golem_food_glass" +#define GOLEM_FOOD_URANIUM "golem_food_uranium" +#define GOLEM_FOOD_SILVER "golem_food_silver" +#define GOLEM_FOOD_PLASMA "golem_food_plasma" +#define GOLEM_FOOD_GOLD "golem_food_gold" +#define GOLEM_FOOD_DIAMOND "golem_food_diamond" +#define GOLEM_FOOD_TITANIUM "golem_food_titanium" +#define GOLEM_FOOD_PLASTEEL "golem_food_plasteel" +#define GOLEM_FOOD_BANANIUM "golem_food_bananium" +#define GOLEM_FOOD_BLUESPACE "golem_food_bluespace" +#define GOLEM_FOOD_GIBTONITE "golem_food_gibtonite" +#define GOLEM_FOOD_LIGHTBULB "golem_food_lightbulb" + +/// Golem food datum singletons +GLOBAL_LIST_INIT(golem_stack_food_types, list( + GOLEM_FOOD_IRON = new /datum/golem_food_buff/iron(), + GOLEM_FOOD_GLASS = new /datum/golem_food_buff/glass(), + GOLEM_FOOD_URANIUM = new /datum/golem_food_buff/uranium(), + GOLEM_FOOD_SILVER = new /datum/golem_food_buff/silver(), + GOLEM_FOOD_PLASMA = new /datum/golem_food_buff/plasma(), + GOLEM_FOOD_GOLD = new /datum/golem_food_buff/gold(), + GOLEM_FOOD_DIAMOND = new /datum/golem_food_buff/diamond(), + GOLEM_FOOD_TITANIUM = new /datum/golem_food_buff/titanium(), + GOLEM_FOOD_PLASTEEL = new /datum/golem_food_buff/plasteel(), + GOLEM_FOOD_BANANIUM = new /datum/golem_food_buff/bananium(), + GOLEM_FOOD_BLUESPACE = new /datum/golem_food_buff/bluespace(), + GOLEM_FOOD_GIBTONITE = new /datum/golem_food_buff/gibtonite(), + GOLEM_FOOD_LIGHTBULB = new /datum/golem_food_buff/lightbulb(), +)) + +/// Associated list of stack types to a golem food +GLOBAL_LIST_INIT(golem_stack_food_directory, list( + /obj/item/gibtonite = GLOB.golem_stack_food_types[GOLEM_FOOD_GIBTONITE], + /obj/item/light = GLOB.golem_stack_food_types[GOLEM_FOOD_LIGHTBULB], + /obj/item/stack/sheet/iron = GLOB.golem_stack_food_types[GOLEM_FOOD_IRON], + /obj/item/stack/ore/iron = GLOB.golem_stack_food_types[GOLEM_FOOD_IRON], + /obj/item/stack/sheet/glass = GLOB.golem_stack_food_types[GOLEM_FOOD_GLASS], + /obj/item/stack/sheet/mineral/uranium = GLOB.golem_stack_food_types[GOLEM_FOOD_URANIUM], + /obj/item/stack/ore/uranium = GLOB.golem_stack_food_types[GOLEM_FOOD_URANIUM], + /obj/item/stack/sheet/mineral/silver = GLOB.golem_stack_food_types[GOLEM_FOOD_SILVER], + /obj/item/stack/ore/silver = GLOB.golem_stack_food_types[GOLEM_FOOD_SILVER], + /obj/item/stack/sheet/mineral/plasma = GLOB.golem_stack_food_types[GOLEM_FOOD_PLASMA], + /obj/item/stack/ore/plasma = GLOB.golem_stack_food_types[GOLEM_FOOD_PLASMA], + /obj/item/stack/sheet/mineral/gold = GLOB.golem_stack_food_types[GOLEM_FOOD_GOLD], + /obj/item/stack/ore/gold = GLOB.golem_stack_food_types[GOLEM_FOOD_GOLD], + /obj/item/stack/sheet/mineral/diamond = GLOB.golem_stack_food_types[GOLEM_FOOD_DIAMOND], + /obj/item/stack/ore/diamond = GLOB.golem_stack_food_types[GOLEM_FOOD_DIAMOND], + /obj/item/stack/sheet/mineral/titanium = GLOB.golem_stack_food_types[GOLEM_FOOD_TITANIUM], + /obj/item/stack/ore/titanium = GLOB.golem_stack_food_types[GOLEM_FOOD_TITANIUM], + /obj/item/stack/sheet/plasteel = GLOB.golem_stack_food_types[GOLEM_FOOD_PLASTEEL], + /obj/item/stack/ore/bananium = GLOB.golem_stack_food_types[GOLEM_FOOD_BANANIUM], + /obj/item/stack/sheet/mineral/bananium = GLOB.golem_stack_food_types[GOLEM_FOOD_BANANIUM], + /obj/item/stack/ore/bluespace_crystal = GLOB.golem_stack_food_types[GOLEM_FOOD_BLUESPACE], + /obj/item/stack/ore/bluespace_crystal/refined = GLOB.golem_stack_food_types[GOLEM_FOOD_BLUESPACE], + /obj/item/stack/ore/bluespace_crystal/artificial = GLOB.golem_stack_food_types[GOLEM_FOOD_BLUESPACE], + /obj/item/stack/sheet/bluespace_crystal = GLOB.golem_stack_food_types[GOLEM_FOOD_BLUESPACE], +)) diff --git a/code/__DEFINES/atmospherics/atmos_mob_interaction.dm b/code/__DEFINES/atmospherics/atmos_mob_interaction.dm index 4144be832e2..0472bc96bfd 100644 --- a/code/__DEFINES/atmospherics/atmos_mob_interaction.dm +++ b/code/__DEFINES/atmospherics/atmos_mob_interaction.dm @@ -41,6 +41,11 @@ /// This is used in handle_temperature_damage() for humans, and in reagents that affect body temperature. Temperature damage is multiplied by this amount. #define TEMPERATURE_DAMAGE_COEFFICIENT 1.5 +/// The maximum temperature of Lavaland +#define LAVALAND_MAX_TEMPERATURE 350 +/// The minimum temperature of Icebox +#define ICEBOX_MIN_TEMPERATURE 180 + /// The natural temperature for a body #define BODYTEMP_NORMAL 310.15 /// This is the divisor which handles how much of the temperature difference between the current body temperature and 310.15K (optimal temperature) humans auto-regenerate each tick. The higher the number, the slower the recovery. This is applied each tick, so long as the mob is alive. @@ -59,10 +64,14 @@ /// This also affects how fast the body normalises it's temperature when hot. /// 340k is about 66c, and rather high for a human. #define BODYTEMP_HEAT_DAMAGE_LIMIT (BODYTEMP_NORMAL + 30) +/// A temperature limit which is above the maximum lavaland temperature +#define BODYTEMP_HEAT_LAVALAND_SAFE (LAVALAND_MAX_TEMPERATURE + 5) /// The body temperature limit the human body can take before it starts taking damage from cold. /// This also affects how fast the body normalises it's temperature when cold. /// 270k is about -3c, that is below freezing and would hurt over time. #define BODYTEMP_COLD_DAMAGE_LIMIT (BODYTEMP_NORMAL - 40) +/// A temperature limit which is above the minimum icebox temperature +#define BODYTEMP_COLD_ICEBOX_SAFE (ICEBOX_MIN_TEMPERATURE - 5) /// The body temperature limit the human body can take before it will take wound damage. #define BODYTEMP_HEAT_WOUND_LIMIT (BODYTEMP_NORMAL + 90) // 400.5 k /// The modifier on cold damage limit hulks get ontop of their regular limit diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 1c40654a177..ae5896f2a8b 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -121,6 +121,7 @@ #define COLOR_PALE_RED_GRAY "#D59998" #define COLOR_PALE_PURPLE_GRAY "#CBB1CA" #define COLOR_PURPLE_GRAY "#AE8CA8" +#define COLOR_GOLEM_GRAY "#8E8C81" //Color defines used by the assembly detailer. #define COLOR_ASSEMBLY_BLACK "#545454" @@ -176,6 +177,8 @@ * * Important note: colors can end up significantly different from the basic html picture, especially when saturated */ +/// Bright light used by default in tubes and bulbs +#define LIGHT_COLOR_DEFAULT "#f3fffa" /// Bright but quickly dissipating neon green. rgb(100, 200, 100) #define LIGHT_COLOR_GREEN "#64C864" /// Vivid, slightly blue green. rgb(60, 240, 70) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm index 4ba6ef8f3a8..d5c6a188852 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm @@ -42,6 +42,8 @@ #define COMPONENT_ATOM_BLOCK_DIR_CHANGE (1<<0) ///from base of atom/setDir(): (old_dir, new_dir). Called before the direction changes. #define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" +///from base of atom/setDir(): (old_dir, new_dir). Called after the direction changes. +#define COMSIG_ATOM_POST_DIR_CHANGE "atom_dir_change" /// from /datum/component/singularity/proc/can_move(), as well as /obj/energy_ball/proc/can_move() /// if a callback returns `SINGULARITY_TRY_MOVE_BLOCK`, then the singularity will not move to that turf #define COMSIG_ATOM_SINGULARITY_TRY_MOVE "atom_singularity_try_move" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index b3237d22ebc..2abec797fc6 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -32,11 +32,16 @@ #define COMPONENT_NO_ATTACH (1<<0) ///from base of /obj/item/bodypart/proc/try_attach_limb(): (new_limb, special) #define COMSIG_CARBON_ATTACH_LIMB "carbon_attach_limb" +/// Called from bodypart being attached /obj/item/bodypart/proc/try_attach_limb(mob/living/carbon/new_owner, special) +#define COMSIG_BODYPART_ATTACHED "bodypart_removed" ///from base of /obj/item/bodypart/proc/try_attach_limb(): (new_limb, special) #define COMSIG_CARBON_POST_ATTACH_LIMB "carbon_post_attach_limb" #define COMSIG_BODYPART_GAUZED "bodypart_gauzed" // from /obj/item/bodypart/proc/apply_gauze(/obj/item/stack/gauze) #define COMSIG_BODYPART_GAUZE_DESTROYED "bodypart_degauzed" // from [/obj/item/bodypart/proc/seep_gauze] when it runs out of absorption +/// Called from bodypart changing owner, which could be on attach or detachment. Either argument can be null. (mob/living/carbon/new_owner, mob/living/carbon/old_owner) +#define COMSIG_BODYPART_CHANGED_OWNER "bodypart_changed_owner" + /// Called from update_health_hud, whenever a bodypart is being updated on the health doll #define COMSIG_BODYPART_UPDATING_HEALTH_HUD "bodypart_updating_health_hud" /// Return to override that bodypart's health hud with your own icon diff --git a/code/__DEFINES/food.dm b/code/__DEFINES/food.dm index b6ba97ef312..2448b34c935 100644 --- a/code/__DEFINES/food.dm +++ b/code/__DEFINES/food.dm @@ -18,7 +18,8 @@ #define ORANGES (1<<17) #define BUGS (1<<18) #define GORE (1<<19) -#define BLOODY (1<<20) // SKYRAT EDIT - Hemophage Food +#define STONE (1<<20) +#define BLOODY (1<<21) // SKYRAT EDIT - Hemophage Food DEFINE_BITFIELD(foodtypes, list( "MEAT" = MEAT, @@ -41,6 +42,7 @@ DEFINE_BITFIELD(foodtypes, list( "ORANGES" = ORANGES, "BUGS" = BUGS, "GORE" = GORE, + "STONE" = STONE, "BLOODY" = BLOODY, // SKYRAT EDIT - Hemophage Food )) @@ -66,6 +68,7 @@ DEFINE_BITFIELD(foodtypes, list( "ORANGES", \ "BUGS", \ "GORE", \ + "STONE", \ "BLOODY", /* SKYRAT EDIT - Hemophage Food */ \ ) @@ -91,6 +94,7 @@ DEFINE_BITFIELD(foodtypes, list( "Oranges", \ "Bugs", \ "Gore", \ + "Rocks", \ "Bloody", /* SKYRAT EDIT - Hemophage Food */ \ ) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index b72feb82ed5..cc0b51ff327 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -88,17 +88,22 @@ #define BODYTYPE_LARVA_PLACEHOLDER (1<<6) ///The limb is from a xenomorph. #define BODYTYPE_ALIEN (1<<7) +///The limb is from a golem +#define BODYTYPE_GOLEM (1<<8) // SKYRAT EDIT ADDITION ///The limb fits a modular custom shape -#define BODYTYPE_CUSTOM (1<<8) +#define BODYTYPE_CUSTOM (1<<9) ///The limb fits a taur body -#define BODYTYPE_TAUR (1<<9) +#define BODYTYPE_TAUR (1<<10) ///The limb causes shoes to no longer be displayed, useful for taurs. -#define BODYTYPE_HIDE_SHOES (1<<10) +#define BODYTYPE_HIDE_SHOES (1<<11) ///The limb causes glasses and hats to be drawn on layers 5 and 4 respectively. Currently used for snouts with the (Top) suffix, which are drawn on layer 6 and would normally cover facewear -#define BODYTYPE_ALT_FACEWEAR_LAYER (1<<11) +#define BODYTYPE_ALT_FACEWEAR_LAYER (1<<12) // SKYRAT EDIT END +#define BODYTYPE_BIOSCRAMBLE_COMPATIBLE (BODYTYPE_HUMANOID | BODYTYPE_MONKEY | BODYTYPE_ALIEN) +#define BODYTYPE_CAN_BE_BIOSCRAMBLED(bodytype) (!(bodytype & BODYTYPE_ROBOTIC) && (bodytype & BODYTYPE_BIOSCRAMBLE_COMPATIBLE)) + // Defines for Species IDs. Used to refer to the name of a species, for things like bodypart names or species preferences. #define SPECIES_ABDUCTOR "abductor" #define SPECIES_ANDROID "android" @@ -429,6 +434,7 @@ #define OFFSET_BACK "back" #define OFFSET_SUIT "suit" #define OFFSET_NECK "neck" +#define OFFSET_HELD "held" #define OFFSET_ACCESSORY "accessory" // Skyrat edit - addition //MINOR TWEAKS/MISC diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 89dd504b416..16f1f57fc26 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -159,6 +159,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_CLUMSY "clumsy" /// means that you can't use weapons with normal trigger guards. #define TRAIT_CHUNKYFINGERS "chunkyfingers" +/// Allows you to mine with your bare hands +#define TRAIT_FIST_MINING "fist_mining" #define TRAIT_DUMB "dumb" /// Whether a mob is dexterous enough to use machines and certain items or not. #define TRAIT_ADVANCEDTOOLUSER "advancedtooluser" @@ -218,6 +220,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NOHUNGER "no_hunger" ///This carbon doesn't bleed #define TRAIT_NOBLOOD "noblood" +///This carbon doesn't show an overlay when they have no brain +#define TRAIT_NO_DEBRAIN_OVERLAY "no_debrain_overlay" #define TRAIT_NOMETABOLISM "no_metabolism" // Use when you want a mob to be able to metabolize plasma temporarily (e.g. plasma fixation disease symptom) #define TRAIT_PLASMA_LOVER_METABOLISM "plasma_lover_metabolism" @@ -428,6 +432,14 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai // and emit less heat. Present on /mob or /datum/mind #define TRAIT_SUPERMATTER_SOOTHER "supermatter_soother" +/// Trait which prevents you from becoming overweight +#define TRAIT_NOFAT "cant_get_fat" + +/// Trait which allows you to eat rocks +#define TRAIT_ROCK_EATER "rock_eater" +/// Trait which allows you to gain bonuses from consuming rocks +#define TRAIT_ROCK_METAMORPHIC "rock_metamorphic" + /// `do_teleport` will not allow this atom to teleport #define TRAIT_NO_TELEPORT "no-teleport" /// This atom is a secluded location, which is counted as out of bounds. diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm index 98fc9514729..06a7299a685 100644 --- a/code/_globalvars/phobias.dm +++ b/code/_globalvars/phobias.dm @@ -511,7 +511,6 @@ GLOBAL_LIST_INIT(phobia_species, list( /datum/species/plasmaman, /datum/species/skeleton, )), - "the supernatural" = typecacheof(list(/datum/species/golem/runic)), )) /// Creates a regular expression to match against the given phobia diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index d40be5d92af..85a40fd0688 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_BADDNA" = TRAIT_BADDNA, "TRAIT_CLUMSY" = TRAIT_CLUMSY, "TRAIT_CHUNKYFINGERS" = TRAIT_CHUNKYFINGERS, + "TRAIT_FIST_MINING" = TRAIT_FIST_MINING, "TRAIT_DUMB" = TRAIT_DUMB, "TRAIT_ADVANCEDTOOLUSER" = TRAIT_ADVANCEDTOOLUSER, "TRAIT_DISCOORDINATED_TOOL_USER" = TRAIT_DISCOORDINATED_TOOL_USER, diff --git a/code/datums/atmosphere/planetary.dm b/code/datums/atmosphere/planetary.dm index 044d29d9586..29db62369b9 100644 --- a/code/datums/atmosphere/planetary.dm +++ b/code/datums/atmosphere/planetary.dm @@ -23,7 +23,7 @@ maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1 minimum_temp = BODYTEMP_COLD_DAMAGE_LIMIT + 1 - maximum_temp = 350 + maximum_temp = LAVALAND_MAX_TEMPERATURE /datum/atmosphere/icemoon id = ICEMOON_DEFAULT_ATMOS @@ -47,6 +47,6 @@ minimum_pressure = HAZARD_LOW_PRESSURE + 10 maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1 - minimum_temp = 180 - maximum_temp = 180 + minimum_temp = ICEBOX_MIN_TEMPERATURE + maximum_temp = ICEBOX_MIN_TEMPERATURE diff --git a/code/datums/bodypart_overlays/emote_bodypart_overlay.dm b/code/datums/bodypart_overlays/emote_bodypart_overlay.dm index a110be348bd..4143efb67db 100644 --- a/code/datums/bodypart_overlays/emote_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/emote_bodypart_overlay.dm @@ -3,26 +3,22 @@ icon = 'icons/mob/species/human/emote_visuals.dmi' ///The body zone to attach the overlay to, overlay won't be added if no bodypart can be found with this var/attached_body_zone = BODY_ZONE_CHEST - ///The offset define to use with the overlay (none by default), should correspond with a list(0,0) in a species' offset_features - var/offset - ///X offset of the overlay image, stored here so we can access this even after bodypart we're on gets severed - var/offset_x = 0 - ///Y offset of the overlay image, stored here so we can access this even after bodypart we're on gets severed - var/offset_y = 0 + ///The feature key used to figure out what specific bodily feature we offset this to follow + var/offset_key + ///Datum giving us details about what offset to apply to the limb + var/datum/worn_feature_offset/feature_offset ///The bodypart that the overlay is currently applied to var/datum/weakref/attached_bodypart /datum/bodypart_overlay/simple/emote/get_image(layer, obj/item/bodypart/limb) var/image/image = ..() - image.pixel_x = offset_x - image.pixel_y = offset_y + feature_offset?.apply_offset(image) return image /datum/bodypart_overlay/simple/emote/added_to_limb(obj/item/bodypart/limb) attached_bodypart = WEAKREF(limb) - if(offset in limb.owner?.dna?.species.offset_features) - offset_x = limb.owner.dna.species.offset_features[offset][1] - offset_y = limb.owner.dna.species.offset_features[offset][2] + if (offset_key in limb.feature_offsets) + feature_offset = limb.feature_offsets[offset_key] /datum/bodypart_overlay/simple/emote/removed_from_limb(obj/item/bodypart/limb) attached_bodypart = null @@ -60,12 +56,12 @@ icon_state = "blush" draw_color = COLOR_BLUSH_PINK layers = EXTERNAL_ADJACENT - offset = OFFSET_FACE + offset_key = OFFSET_FACE attached_body_zone = BODY_ZONE_HEAD /datum/bodypart_overlay/simple/emote/cry icon_state = "tears" draw_color = COLOR_DARK_CYAN layers = EXTERNAL_ADJACENT - offset = OFFSET_FACE + offset_key = OFFSET_FACE attached_body_zone = BODY_ZONE_HEAD diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm index 79fbcbba245..b89f7976cc5 100644 --- a/code/datums/components/food/edible.dm +++ b/code/datums/components/food/edible.dm @@ -30,7 +30,7 @@ Behavior that's still missing from this component that original food items had t var/list/eatverbs ///Callback to be ran for when you take a bite of something var/datum/callback/after_eat - ///Callback to be ran for when you take a bite of something + ///Callback to be ran for when you finish eating something var/datum/callback/on_consume ///Callback to be ran for when the code check if the food is liked, allowing for unique overrides for special foods like donuts with cops. var/datum/callback/check_liked @@ -524,6 +524,8 @@ Behavior that's still missing from this component that original food items had t SEND_SIGNAL(parent, COMSIG_FOOD_CONSUMED, eater, feeder) on_consume?.Invoke(eater, feeder) + if (QDELETED(parent)) // might be destroyed by the callback + return to_chat(feeder, span_warning("There is nothing left of [parent], oh no!")) if(isturf(parent)) diff --git a/code/datums/components/food/golem_food.dm b/code/datums/components/food/golem_food.dm new file mode 100644 index 00000000000..0f728caebdb --- /dev/null +++ b/code/datums/components/food/golem_food.dm @@ -0,0 +1,119 @@ +/// Component which allows something to be eaten by a golem +/datum/component/golem_food + /// Whether to destroy our item when we eat it + var/consume_on_eat + /// Reference to the kind of food we provide + var/datum/golem_food_buff/snack_type + /// Our abstract fake food item + var/obj/item/food/golem_food/golem_snack + /// Any extra checks which need to be done when seeing if this is edible + var/datum/callback/extra_validation + +/datum/component/golem_food/Initialize(consume_on_eat = TRUE, golem_food_key, datum/callback/extra_validation) + if (!isatom(parent)) + return COMPONENT_INCOMPATIBLE + if (!golem_food_key || !is_path_in_list(golem_food_key, GLOB.golem_stack_food_directory)) + CRASH("Golem food type not specified, this is a required parameter.") + + src.consume_on_eat = consume_on_eat + snack_type = GLOB.golem_stack_food_directory[golem_food_key] + src.extra_validation = extra_validation + + +/datum/component/golem_food/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(on_attack)) + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) + +/datum/component/golem_food/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_ITEM_ATTACK, COMSIG_PARENT_EXAMINE)) + return ..() + +/datum/component/golem_food/Destroy(force, silent) + QDEL_NULL(golem_snack) + QDEL_NULL(extra_validation) + return ..() + +/// Attempt to feed this item to golem +/datum/component/golem_food/proc/on_attack(atom/source, mob/living/target, mob/living/user, click_parameters) + SIGNAL_HANDLER + if (user.combat_mode || !HAS_TRAIT(target, TRAIT_ROCK_EATER)) + return + if (extra_validation && !extra_validation.Invoke()) + source.balloon_alert(user, "not edible!") + return COMPONENT_CANCEL_ATTACK_CHAIN + if (!snack_type.can_consume(target)) + source.balloon_alert(user, "incompatible mineral!") + return COMPONENT_CANCEL_ATTACK_CHAIN + if (!golem_snack) + golem_snack = new( + /* loc = */ null, + /* name = */ source.name, + /* consume_food = */ consume_on_eat, + /* food_buff = */ snack_type, + /* owner = */ parent, + ) + RegisterSignal(golem_snack, COMSIG_PARENT_QDELETING, PROC_REF(on_food_destroyed)) + golem_snack.attack(target, user, click_parameters) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/// Reference handling for abstract food object +/datum/component/golem_food/proc/on_food_destroyed() + SIGNAL_HANDLER + golem_snack = null + +/// Add extra examine text to people who have golem brains +/datum/component/golem_food/proc/on_examine(datum/source, mob/user, list/examine_text) + SIGNAL_HANDLER + if(!HAS_TRAIT(user, TRAIT_ROCK_METAMORPHIC)) + return + examine_text += span_notice(snack_type.added_info) + +/// Abstract food item used to feed golems +/obj/item/food/golem_food + name = "temporary golem material snack item" + desc = "You shouldn't be able to see this. This is an abstract item which exists to allow you to eat rocks." + bite_consumption = 2 + food_reagents = list(/datum/reagent/consumable/nutriment/mineral = INFINITY) // Destroyed when stack runs out, not when reagents do + foodtypes = STONE + /// If we use up a stack of food on use or not + var/consume_food = TRUE + /// A reference to whatever it is we represent + var/atom/owner + /// Golem food buff to apply on consumption + var/datum/golem_food_buff/food_buff + +/obj/item/food/golem_food/Initialize(mapload, name, consume_food, datum/golem_food_buff/food_buff, atom/owner) + . = ..() + src.name = name + src.consume_food = consume_food + src.food_buff = food_buff + src.owner = owner + + RegisterSignal(owner, COMSIG_PARENT_QDELETING, PROC_REF(on_parent_destroyed)) + +/// Clean ourselves up if our parent dies +/obj/item/food/golem_food/proc/on_parent_destroyed(datum/destroyed_thing) + SIGNAL_HANDLER + qdel(src) + +/obj/item/food/golem_food/make_edible() + . = ..() + AddComponent(/datum/component/edible, after_eat = CALLBACK(src, PROC_REF(took_bite)), volume = INFINITY) + +/// Called when someone bites this food, subtract one charge from our material stack +/obj/item/food/golem_food/proc/took_bite(mob/eater) + if (!owner) + qdel(src) + return + + food_buff.on_consumption(eater, owner) + if (!consume_food) + qdel(src) + return + + if (isstack(owner)) + var/obj/item/stack/stack_owner = owner + stack_owner.use(used = 1) + else + qdel(owner) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index aff22c908f8..79367824e58 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -374,7 +374,7 @@ /atom/proc/handle_ricochet(obj/projectile/ricocheting_projectile) var/turf/p_turf = get_turf(ricocheting_projectile) - var/face_direction = get_dir(src, p_turf) + var/face_direction = get_dir(src, p_turf) || get_dir(src, ricocheting_projectile) var/face_angle = dir2angle(face_direction) var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (ricocheting_projectile.Angle + 180)) var/a_incidence_s = abs(incidence_s) @@ -1169,6 +1169,7 @@ return SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) dir = newdir + SEND_SIGNAL(src, COMSIG_ATOM_POST_DIR_CHANGE, dir, newdir) /** * Called when the atom log's in or out diff --git a/code/game/objects/items/stacks/golem_food/golem_food_buff.dm b/code/game/objects/items/stacks/golem_food/golem_food_buff.dm new file mode 100644 index 00000000000..09c1a335ef1 --- /dev/null +++ b/code/game/objects/items/stacks/golem_food/golem_food_buff.dm @@ -0,0 +1,117 @@ +/// An effect you gain from eating minerals +/datum/golem_food_buff + /// If we can apply this while you already have a different status effect + var/exclusive = TRUE + /// Nutrition to grant per stack consumed + var/nutrition = 2 + /// Typepath of status effect to apply + var/status_effect + /// Extra information to display when a valid food is examined + var/added_info = "" + +/// Returns true if the passed mob can currently gain this buff +/datum/golem_food_buff/proc/can_consume(mob/living/consumer) + if (!exclusive) + return TRUE + var/datum/status_effect/golem/existing = consumer.has_status_effect(/datum/status_effect/golem) + return !existing || istype(existing, status_effect) + +/// Called when someone actually eats this +/datum/golem_food_buff/proc/on_consumption(mob/living/carbon/consumer, atom/movable/consumed) + if (!HAS_TRAIT(consumer, TRAIT_ROCK_METAMORPHIC)) + return + apply_effects(consumer, consumed) + +/// Apply our desired effects to the eater +/datum/golem_food_buff/proc/apply_effects(mob/living/carbon/consumer, atom/movable/consumed) + if (status_effect) + consumer.apply_status_effect(status_effect) + +/// Can eat at any time, but isn't very nutritious +/datum/golem_food_buff/glass + exclusive = FALSE + nutrition = 0.5 + added_info = "This mineral can be consumed at any time, but it's mostly empty calories." + +/// More filling, and heals you +/datum/golem_food_buff/iron + nutrition = 3 + added_info = "This mineral is filling and even heals you a little." + /// Amount by which you heal from eating some iron + var/healed_amount = 3 + /// Order in which to heal damage types + var/list/damage_heal_order = list(BRUTE, BURN, TOX, OXY) + +/datum/golem_food_buff/iron/apply_effects(mob/living/carbon/consumer, atom/movable/consumed) + if (consumer.health == consumer.maxHealth) + return + consumer.heal_ordered_damage(healed_amount, damage_heal_order) + new /obj/effect/temp_visual/heal(get_turf(consumer), COLOR_HEALING_CYAN) + +/datum/golem_food_buff/uranium + status_effect = /datum/status_effect/golem/uranium + added_info = "If consumed this mineral will power you in place of food, pausing your digestion for five minutes." + +/datum/golem_food_buff/silver + status_effect = /datum/status_effect/golem/silver + added_info = "If consumed this mineral will repel the supernatural, affording you resistance to mystical effects." + +/datum/golem_food_buff/plasma + status_effect = /datum/status_effect/golem/plasma + added_info = "If consumed this mineral will allow you to absorb heat and convert it into power." + +/datum/golem_food_buff/plasteel + status_effect = /datum/status_effect/golem/plasteel + added_info = "If consumed this mineral will harden you against the hazards of space." + +/datum/golem_food_buff/gold + status_effect = /datum/status_effect/golem/gold + added_info = "If consumed this mineral will grant you a shiny coating which reflects projectiles." + +/datum/golem_food_buff/diamond + status_effect = /datum/status_effect/golem/diamond + added_info = "If consumed this mineral will reflact light around you, making you faster and harder to see." + +/datum/golem_food_buff/titanium + status_effect = /datum/status_effect/golem/titanium + added_info = "If consumed this mineral will make you tougher and punch harder." + +/datum/golem_food_buff/bananium + status_effect = /datum/status_effect/golem/bananium + added_info = "If consumed this mineral will make you funnier." + +/datum/golem_food_buff/lightbulb + nutrition = 0 + exclusive = FALSE + status_effect = /datum/status_effect/golem_lightbulb + added_info = "Not nutritious, but gives you a healthy glow if eaten." + +/datum/golem_food_buff/gibtonite + exclusive = FALSE + added_info = "After consumption, you can launch this mineral like a rocket. It's a little hard to keep down." + +/datum/golem_food_buff/gibtonite/apply_effects(mob/living/carbon/human/consumer, atom/movable/consumed) + var/obj/item/gibtonite_hand/new_hand = new(null, /* held_gibtonite = */ consumed) + + if(consumer.put_in_hands(new_hand)) + return + consumer.drop_all_held_items() + if(consumer.put_in_hands(new_hand)) + return + + consumed.forceMove(get_turf(consumer)) + new_hand.held_gibtonite = null + qdel(new_hand) + consumer.visible_message(span_warning("[consumer] can't keep [consumed] down, and coughs it onto the ground!")) + +/datum/golem_food_buff/bluespace + exclusive = FALSE + added_info = "After consumption, you can use the stored power to teleport yourself." + +/datum/golem_food_buff/bluespace/apply_effects(mob/living/carbon/human/consumer, obj/item/stack/consumed) + var/obj/item/bluespace_finger/new_hand = new + if (consumed.amount == 1) + consumer.dropItemToGround(consumed) + if (consumer.put_in_hands(new_hand, del_on_fail = TRUE)) + return + consumer.balloon_alert(consumer, "no free hands!") diff --git a/code/game/objects/items/stacks/golem_food/golem_hand_actions.dm b/code/game/objects/items/stacks/golem_food/golem_hand_actions.dm new file mode 100644 index 00000000000..97165b18355 --- /dev/null +++ b/code/game/objects/items/stacks/golem_food/golem_hand_actions.dm @@ -0,0 +1,106 @@ +/// How long can you hold onto gibtonite before you need to let it go? +#define GIBTONITE_GOLEM_HOLD_TIME 2 MINUTES +/// ID for a filter we apply so we can remove it again +#define BLUESPACE_GLOW_FILTER "bluespace_glow_filter" + +/// Lets you hold a gibtonite ore in one hand and shoot it like a gun +/obj/item/gibtonite_hand + name = "stabilised gibtonite fist" + desc = "You had better launch this at something before it comes out the other end of your body." + icon = 'icons/obj/ore.dmi' + icon_state = "Gibtonite ore" + lefthand_file = 'icons/mob/inhands/golem_lefthand.dmi' + righthand_file = 'icons/mob/inhands/golem_righthand.dmi' + inhand_icon_state = "gibtonite_hand" + item_flags = ABSTRACT + w_class = WEIGHT_CLASS_HUGE + /// The bomb we're going to throw + var/obj/item/gibtonite/held_gibtonite + +/obj/item/gibtonite_hand/Initialize(mapload, obj/item/gibtonite/held_gibtonite) + . = ..() + if (!held_gibtonite) + return INITIALIZE_HINT_QDEL + ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT) + src.held_gibtonite = held_gibtonite + held_gibtonite.forceMove(src) + addtimer(CALLBACK(src, PROC_REF(release_gibtonite)), GIBTONITE_GOLEM_HOLD_TIME, TIMER_DELETE_ME) + +/obj/item/gibtonite_hand/afterattack(atom/target, mob/living/user, flag, params) + . = ..() + if (!held_gibtonite) + to_chat(user, span_warning("[src] fizzles, it was a dud!")) + qdel(src) + return TRUE | AFTERATTACK_PROCESSED_ITEM + playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, TRUE) + held_gibtonite.forceMove(get_turf(src)) + held_gibtonite.det_time = 2 SECONDS + held_gibtonite.GibtoniteReaction(user) + held_gibtonite.throw_at(target, range = 10, speed = 3, thrower = user) + held_gibtonite = null + qdel(src) + return TRUE | AFTERATTACK_PROCESSED_ITEM + +/// Called when you can't hold it in any longer and just drop it on the ground +/obj/item/gibtonite_hand/proc/release_gibtonite() + held_gibtonite.forceMove(get_turf(src)) + held_gibtonite.GibtoniteReaction(isliving(loc) ? loc : null) + held_gibtonite = null + qdel(src) + +/obj/item/gibtonite_hand/Destroy() + QDEL_NULL(held_gibtonite) + return ..() + +/// Point at a target and teleport somewhere vaguely close to it +/obj/item/bluespace_finger + name = "bluespace knot" + desc = "Firmly grasp reality and pull yourself to a nearby location." + icon = 'icons/obj/weapons/guns/projectiles.dmi' + icon_state = "bluespace" + lefthand_file = 'icons/mob/inhands/golem_lefthand.dmi' + righthand_file = 'icons/mob/inhands/golem_righthand.dmi' + inhand_icon_state = "bluespace_hand" + item_flags = ABSTRACT | DROPDEL + /// How far away can you point? + var/teleport_range = 7 + /// How long does it take to teleport? + var/teleport_time = 3 SECONDS + /// How accurate are you? + var/teleport_vary = 2 + +/obj/item/bluespace_finger/afterattack(atom/target, mob/living/user, flag, params) + . = ..() + var/turf/target_turf = get_turf(target) + if (get_dist(target_turf, get_turf(src)) > teleport_range) + balloon_alert(user, "too far!") + return TRUE | AFTERATTACK_PROCESSED_ITEM + if (target_turf.is_blocked_turf(exclude_mobs = TRUE)) + balloon_alert(user, "no room!") + return TRUE | AFTERATTACK_PROCESSED_ITEM + + var/obj/effect/temp_visual/teleport_golem/landing_indicator = new(target_turf) + user.add_filter(BLUESPACE_GLOW_FILTER, 2, list("type" = "outline", "color" = COLOR_BRIGHT_BLUE, "alpha" = 0, "size" = 1)) + var/filter = user.get_filter(BLUESPACE_GLOW_FILTER) + animate(filter, alpha = 200, time = 0.5 SECONDS, loop = -1) + animate(alpha = 0, time = 0.5 SECONDS) + + var/did_teleport = do_after(user, delay = teleport_time, target = src, interaction_key = REF(src)) + qdel(landing_indicator) + user.remove_filter(BLUESPACE_GLOW_FILTER) + if (!did_teleport) + return + + var/list/valid_landing_tiles = list(target_turf) + for (var/turf/potential_landing in oview(teleport_vary, target_turf)) + if (potential_landing.is_blocked_turf(exclude_mobs = TRUE)) + continue + valid_landing_tiles += potential_landing + var/turf/final_destination = pick(valid_landing_tiles) + for (var/mob/living/telefrag in final_destination) + telefrag.Knockdown(2 SECONDS) + do_teleport(user, final_destination, asoundin = 'sound/effects/phasein.ogg', no_effects = TRUE) + qdel(src) + +#undef GIBTONITE_GOLEM_HOLD_TIME +#undef BLUESPACE_GLOW_FILTER diff --git a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm new file mode 100644 index 00000000000..03c7af2dc12 --- /dev/null +++ b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm @@ -0,0 +1,446 @@ +/// Abstract holder for golem status effects, you should never have more than one of these active +/datum/status_effect/golem + id = "golem_status" + duration = 5 MINUTES + alert_type = /atom/movable/screen/alert/status_effect/golem_status + /// Icon state prefix for overlay to display on golem limbs + var/overlay_state_prefix + /// Name of the mineral we ate to get this + var/mineral_name = "" + /// Text to display on buff application + var/applied_fluff = "" + /// Overlays we have applied to our mob + var/list/active_overlays = list() + /// Icon used to represent the alert + var/alert_icon = 'icons/obj/stack_objects.dmi' + /// Icon state to display to represent the alert + var/alert_icon_state = "sheet-monkey" + /// Tooltip to display when hovering over the alert + var/alert_desc = "Something went wrong and this tooltip is not displaying correctly." + +/atom/movable/screen/alert/status_effect/golem_status + name = "Metamorphic %SOMETHING%" + desc = "You've enjoyed a tasty meal and are now experiencing a bug." + icon_state = "template" + /// Overlay we show on top of the template icon + var/mutable_appearance/mineral_overlay + /// When we reach this much remaining time we will start animating as a warning + var/early_expiry_warning = 30 SECONDS + /// When we reach this much remaining time we will start animating more urgently as a warning + var/imminent_expiry_warning = 5 SECONDS + +/// Set up how the alert ACTUALLY looks, based on the effect applied +/atom/movable/screen/alert/status_effect/golem_status/proc/update_details(buff_time) + var/datum/status_effect/golem/golem_effect = attached_effect + if (!istype(golem_effect)) + CRASH("Golem status alert attached to invalid status effect.") + name = replacetext(name, "%SOMETHING%", golem_effect.mineral_name) + desc = golem_effect.alert_desc + mineral_overlay = mutable_appearance(golem_effect.alert_icon, golem_effect.alert_icon_state) + update_appearance(UPDATE_ICON) + + if (buff_time > early_expiry_warning) + addtimer(CALLBACK(src, PROC_REF(early_warning)), buff_time - early_expiry_warning, TIMER_DELETE_ME) + if (buff_time > imminent_expiry_warning) + addtimer(CALLBACK(src, PROC_REF(imminent_warning)), buff_time - imminent_expiry_warning, TIMER_DELETE_ME) + +/// Animate to indicate effect is expiring soon +/atom/movable/screen/alert/status_effect/golem_status/proc/early_warning() + animate(src, alpha = 75, time = 1 SECONDS, loop = -1) + animate(alpha = 255, time = 1 SECONDS, loop = -1) + +/// Animate to indicate effect is expiring very soon +/atom/movable/screen/alert/status_effect/golem_status/proc/imminent_warning() + animate(src, alpha = 25, time = 0.5 SECONDS, loop = -1) + animate(alpha = 200, time = 0.5 SECONDS, loop = -1) + +/atom/movable/screen/alert/status_effect/golem_status/update_overlays() + . = ..() + . += mineral_overlay + +/atom/movable/screen/alert/status_effect/golem_status/Destroy() + QDEL_NULL(mineral_overlay) + return ..() + +/datum/status_effect/golem/on_apply() + . = ..() + if (!ishuman(owner)) + return FALSE + if (owner.has_status_effect(/datum/status_effect/golem)) + return FALSE + if (applied_fluff) + to_chat(owner, span_notice(applied_fluff)) + if (!overlay_state_prefix || !iscarbon(owner)) + return TRUE + var/mob/living/carbon/golem_owner = owner + for (var/obj/item/bodypart/part in golem_owner.bodyparts) + if (part.limb_id != SPECIES_GOLEM) + continue + var/datum/bodypart_overlay/simple/golem_overlay/overlay = new() + overlay.add_to_bodypart(overlay_state_prefix, part) + active_overlays += overlay + golem_owner.update_body_parts() + return TRUE + +/datum/status_effect/golem/on_creation(mob/living/new_owner) + . = ..() + if (!.) + return FALSE + var/atom/movable/screen/alert/status_effect/golem_status/status_alert = linked_alert + status_alert?.update_details(buff_time = initial(duration)) + +/datum/status_effect/golem/on_remove() + to_chat(owner, span_warning("The effect of the [mineral_name] fades.")) + QDEL_LIST(active_overlays) + return ..() + +/datum/status_effect/golem/get_examine_text() + return span_notice("[owner.p_their(capitalized = TRUE)] body has been augmented with veins of [mineral_name].") + +/// Body part overlays applied by golem status effects +/datum/bodypart_overlay/simple/golem_overlay + icon = 'icons/mob/species/golems.dmi' + layers = ALL_EXTERNAL_OVERLAYS + ///The bodypart that the overlay is currently applied to + var/datum/weakref/attached_bodypart + +/datum/bodypart_overlay/simple/golem_overlay/proc/add_to_bodypart(prefix, obj/item/bodypart/part) + icon_state = "[prefix]_[part.body_zone]" + attached_bodypart = WEAKREF(part) + part.add_bodypart_overlay(src) + +/datum/bodypart_overlay/simple/golem_overlay/Destroy(force) + var/obj/item/bodypart/referenced_bodypart = attached_bodypart.resolve() + if(!referenced_bodypart) + return ..() + referenced_bodypart.remove_bodypart_overlay(src) + if(referenced_bodypart.owner) //Keep in mind that the bodypart could have been severed from the owner by now + referenced_bodypart.owner.update_body_parts() + else + referenced_bodypart.update_icon_dropped() + return ..() + +/// Freezes hunger for the duration +/datum/status_effect/golem/uranium + overlay_state_prefix = "uranium" + mineral_name = "uranium" + applied_fluff = "Glowing crystals sprout from your body. You feel energised!" + alert_icon_state = "sheet-uranium" + alert_desc = "Internal radiation is providing all of your nutritional needs." + +/datum/status_effect/golem/uranium/on_apply() + . = ..() + if (!.) + return FALSE + ADD_TRAIT(owner, TRAIT_NOHUNGER, TRAIT_STATUS_EFFECT(id)) + owner.remove_movespeed_modifier(/datum/movespeed_modifier/golem_hunger) + owner.remove_status_effect(/datum/status_effect/golem_statued) // Instant fix! + return TRUE + +/datum/status_effect/golem/uranium/on_remove() + REMOVE_TRAIT(owner, TRAIT_NOHUNGER, TRAIT_STATUS_EFFECT(id)) + return ..() + +/// Magic immunity +/datum/status_effect/golem/silver + overlay_state_prefix = "silver" + mineral_name = "silver" + applied_fluff = "Shining plates grace your shoulders. You feel holy!" + alert_icon_state = "sheet-silver" + alert_desc = "Your body repels supernatural influences." + +/datum/status_effect/golem/silver/on_apply() + . = ..() + if (!.) + return FALSE + owner.add_traits(list(TRAIT_ANTIMAGIC, TRAIT_HOLY), TRAIT_STATUS_EFFECT(id)) + return TRUE + +/datum/status_effect/golem/silver/on_remove() + owner.remove_traits(list(TRAIT_ANTIMAGIC, TRAIT_HOLY), TRAIT_STATUS_EFFECT(id)) + return ..() + +/// Heat immunity, turns heat damage into local power +/datum/status_effect/golem/plasma + overlay_state_prefix = "plasma" + mineral_name = "plasma" + applied_fluff = "Plasma cooling rods sprout from your body. You can take the heat!" + alert_icon_state = "sheet-plasma" + alert_desc = "You are protected from high pressure and can convert heat damage into power." + /// What do we multiply our damage by to convert it into power? + var/power_multiplier = 5 + /// Multiplier to apply to burn damage, not 0 so that we can reverse it more easily + var/burn_multiplier = 0.05 + +/datum/status_effect/golem/plasma/on_apply() + . = ..() + if (!.) + return FALSE + owner.add_traits(list(TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTHEAT), TRAIT_STATUS_EFFECT(id)) + RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_burned)) + var/mob/living/carbon/human/human_owner = owner + human_owner.physiology.burn_mod *= burn_multiplier + return TRUE + +/datum/status_effect/golem/plasma/on_remove() + owner.remove_traits(list(TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTHEAT), TRAIT_STATUS_EFFECT(id)) + UnregisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE) + var/mob/living/carbon/human/human_owner = owner + human_owner.physiology.burn_mod /= burn_multiplier + return ..() + +/// When we take fire damage (or... technically also cold damage, we don't differentiate), zap a nearby APC +/datum/status_effect/golem/plasma/proc/on_burned(datum/source, damage, damagetype) + SIGNAL_HANDLER + if(damagetype != BURN) + return + + var/power = damage * power_multiplier + var/obj/machinery/power/energy_accumulator/ground = get_closest_atom(/obj/machinery/power/energy_accumulator, view(4, owner), owner) + if (ground) + zap_effect(ground) + ground.zap_act(damage, ZAP_GENERATES_POWER) + return + var/area/our_area = get_area(owner) + var/obj/machinery/power/apc/our_apc = our_area.apc + if (!our_apc) + return + zap_effect(our_apc) + our_apc.cell?.give(power) + +/// Shoot a beam at the target atom +/datum/status_effect/golem/plasma/proc/zap_effect(atom/target) + owner.Beam(target, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS) + playsound(owner, 'sound/magic/lightningshock.ogg', vol = 50, vary = TRUE) + +/// Makes you spaceproof +/datum/status_effect/golem/plasteel + overlay_state_prefix = "iron" + mineral_name = "plasteel" + applied_fluff = "Plasteel plates seal you tight. You feel insulated!" + alert_icon_state = "sheet-plasteel" + alert_desc = "You are sealed against the cold, and against low pressure environments." + +/datum/status_effect/golem/plasteel/on_apply() + . = ..() + if (!.) + return FALSE + owner.add_traits(list(TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTCOLD), TRAIT_STATUS_EFFECT(id)) + return TRUE + +/datum/status_effect/golem/plasteel/on_remove() + owner.remove_traits(list(TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTCOLD), TRAIT_STATUS_EFFECT(id)) + return ..() + +/// Makes you reflect energy projectiles +/datum/status_effect/golem/gold + overlay_state_prefix = "gold" + mineral_name = "gold" + applied_fluff = "Shining plates form across your body. You feel reflective!" + alert_icon_state = "sheet-gold_2" + alert_desc = "Your shining body reflects energy weapons." + +/datum/status_effect/golem/gold/on_apply() + . = ..() + if (!.) + return FALSE + owner.flags_ricochet |= RICOCHET_SHINY + return TRUE + +/datum/status_effect/golem/gold/on_remove() + owner.flags_ricochet &= ~RICOCHET_SHINY + return ..() + +/// Makes you hard to see +/datum/status_effect/golem/diamond + overlay_state_prefix = "diamond" + mineral_name = "diamonds" + applied_fluff = "Sparkling gems bend light around you. You feel stealthy!" + tick_interval = 0.25 SECONDS + alert_icon_state = "sheet-diamond" + alert_desc = "Light is bending around you, making you hard to see while still and faster while moving." + /// Alpha to remove per second while stood still + var/alpha_per_tick = 25 + /// Alpha to apply while moving + var/moving_alpha = 150 + /// List of arms we have updated + var/list/modified_arms + +/datum/status_effect/golem/diamond/on_apply() + . = ..() + if (!.) + return FALSE + RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) + owner.alpha = moving_alpha + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/light_speed) + + var/mob/living/carbon/carbon_owner = owner + for (var/obj/item/bodypart/arm/arm in carbon_owner.bodyparts) + if (arm.limb_id != SPECIES_GOLEM) + continue + set_arm_fluff(arm) + return TRUE + +/datum/status_effect/golem/diamond/tick(delta_time, times_fired) + owner.alpha = max(owner.alpha - alpha_per_tick, 0) + +/// Reset alpha to starting value +/datum/status_effect/golem/diamond/proc/on_move() + SIGNAL_HANDLER + owner.alpha = moving_alpha + +/// Make our arm do slashing effects +/datum/status_effect/golem/diamond/proc/set_arm_fluff(obj/item/bodypart/arm/arm) + arm.unarmed_attack_verb = "slash" + arm.unarmed_attack_effect = ATTACK_EFFECT_CLAW + arm.unarmed_attack_sound = 'sound/weapons/slash.ogg' + arm.unarmed_miss_sound = 'sound/weapons/slashmiss.ogg' + RegisterSignal(arm, COMSIG_PARENT_QDELETING, PROC_REF(on_arm_destroyed)) + LAZYADD(modified_arms, arm) + +/datum/status_effect/golem/diamond/on_remove() + owner.alpha = initial(owner.alpha) + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/light_speed) + UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + for (var/obj/item/bodypart/arm/arm as anything in modified_arms) + reset_arm_fluff(arm) + LAZYCLEARLIST(modified_arms) + return ..() + +/// Make our arm do whatever it originally did +/datum/status_effect/golem/diamond/proc/reset_arm_fluff(obj/item/bodypart/arm/arm) + if (!arm) + return + arm.unarmed_attack_verb = initial(arm.unarmed_attack_verb) + arm.unarmed_attack_effect = initial(arm.unarmed_attack_effect) + arm.unarmed_attack_sound = initial(arm.unarmed_attack_sound) + arm.unarmed_miss_sound = initial(arm.unarmed_miss_sound) + UnregisterSignal(arm, COMSIG_PARENT_QDELETING) + +/// Remove references to deleted arms +/datum/status_effect/golem/diamond/proc/on_arm_destroyed(obj/item/bodypart/arm/arm) + SIGNAL_HANDLER + modified_arms -= arm + +/// Makes you tougher +/datum/status_effect/golem/titanium + overlay_state_prefix = "platinum" + mineral_name = "titanium" + applied_fluff = "Titanium rings burst from your arms. You feel ready to take on the world!" + alert_icon_state = "sheet-titanium" + alert_desc = "You are more resistant to physical blows, and pack more of a punch yourself." + /// Amount to reduce brute damage by + var/brute_modifier = 0.7 + /// How much extra damage do we do with our fists? + var/damage_increase = 3 + /// Deal this much extra damage to mining mobs, most of which take 0 unarmed damage usually + var/mining_bonus = 30 + /// List of arms we have updated + var/list/modified_arms + +/datum/status_effect/golem/titanium/on_apply() + . = ..() + if (!.) + return FALSE + var/mob/living/carbon/human/human_owner = owner + RegisterSignal(human_owner, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, PROC_REF(on_punched)) + human_owner.physiology.brute_mod *= brute_modifier + for (var/obj/item/bodypart/arm/arm in human_owner.bodyparts) + if (arm.limb_id != SPECIES_GOLEM) + continue + buff_arm(arm) + +/// Give mining mobs an extra slap +/datum/status_effect/golem/titanium/proc/on_punched(mob/living/puncher, atom/punchee, proximity) + SIGNAL_HANDLER + if (!proximity || !isliving(punchee)) + return + var/mob/living/victim = punchee + if (victim.body_position == LYING_DOWN || (!(FACTION_MINING in victim.faction) && !(FACTION_BOSS in victim.faction))) + return + victim.apply_damage(mining_bonus, BRUTE) + +/// Make the targeted arm big and strong +/datum/status_effect/golem/titanium/proc/buff_arm(obj/item/bodypart/arm/arm) + arm.unarmed_damage_low += damage_increase + arm.unarmed_damage_high += damage_increase + arm.unarmed_stun_threshold += damage_increase // We don't want to make knockdown more likely + RegisterSignal(arm, COMSIG_PARENT_QDELETING, PROC_REF(on_arm_destroyed)) + LAZYADD(modified_arms, arm) + +/datum/status_effect/golem/titanium/on_remove() + var/mob/living/carbon/human/human_owner = owner + UnregisterSignal(human_owner, COMSIG_HUMAN_MELEE_UNARMED_ATTACK) + human_owner.physiology.brute_mod /= brute_modifier + for (var/obj/item/bodypart/arm/arm as anything in modified_arms) + debuff_arm(arm) + LAZYCLEARLIST(modified_arms) + return ..() + +/// Make the targeted arm small and weak +/datum/status_effect/golem/titanium/proc/debuff_arm(obj/item/bodypart/arm/arm) + if (!arm) + return + arm.unarmed_damage_low -= damage_increase + arm.unarmed_damage_high -= damage_increase + arm.unarmed_stun_threshold -= damage_increase + UnregisterSignal(arm, COMSIG_PARENT_QDELETING) + +/// Remove references to deleted arms +/datum/status_effect/golem/titanium/proc/on_arm_destroyed(obj/item/bodypart/arm/arm) + SIGNAL_HANDLER + modified_arms -= arm + +/// Makes you slippery +/datum/status_effect/golem/bananium + overlay_state_prefix = "banana" + mineral_name = "bananium" + applied_fluff = "Bananium veins ooze from your crags. You feel a little funny!" + alert_icon_state = "sheet-bananium" + alert_desc = "You feel kind of funny." + /// The slipperiness component which we have applied + var/datum/component/slippery/slipperiness + +/datum/status_effect/golem/bananium/on_apply() + . = ..() + if (!.) + return + owner.AddElement(/datum/element/waddling) + ADD_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id)) + slipperiness = owner.AddComponent(/datum/component/slippery, knockdown = 12 SECONDS, lube_flags = NO_SLIP_WHEN_WALKING) + +/datum/status_effect/golem/bananium/on_remove() + REMOVE_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id)) + owner.RemoveElement(/datum/element/waddling) + QDEL_NULL(slipperiness) + return ..() + +#define LIGHTBULB_FILTER "filter_lightbulb_glow" + +/// Lights up the golem, NOT using the golem subtype because it is not exclusive with other status effects +/datum/status_effect/golem_lightbulb + id = "golem_lightbulb" + status_type = STATUS_EFFECT_REFRESH + duration = 2 MINUTES + alert_type = null + var/glow_range = 3 + var/glow_power = 1 + var/glow_color = LIGHT_COLOR_DEFAULT + var/datum/component/overlay_lighting/lightbulb + +/datum/status_effect/golem_lightbulb/on_apply() + . = ..() + if (!.) + return + to_chat(owner, span_notice("You start to emit a healthy glow.")) + owner.light_system = MOVABLE_LIGHT + lightbulb = owner.AddComponent(/datum/component/overlay_lighting, _range = glow_range, _power = glow_power, _color = glow_color) + owner.add_filter(LIGHTBULB_FILTER, 2, list("type" = "outline", "color" = glow_color, "alpha" = 60, "size" = 1)) + +/datum/status_effect/golem_lightbulb/on_remove() + QDEL_NULL(lightbulb) + owner.remove_filter(LIGHTBULB_FILTER) + to_chat(owner, span_warning("Your glow fades.")) + return ..() + +#undef LIGHTBULB_FILTER diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 4b24b70447b..8f2503847ac 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -120,6 +120,9 @@ ) AddElement(/datum/element/connect_loc, loc_connections) + if(is_path_in_list(merge_type, GLOB.golem_stack_food_directory)) + AddComponent(/datum/component/golem_food, golem_food_key = merge_type) + /** Sets the amount of materials per unit for this stack. * * Arguments: diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index f7fa570452a..a00d6ea8583 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -94,37 +94,40 @@ to_chat(usr, span_warning("You don't have the dexterity to do this!")) return - if(I.tool_behaviour == TOOL_MINING) - var/turf/T = user.loc - if (!isturf(T)) - return + if(I.tool_behaviour != TOOL_MINING) + return + var/turf/T = user.loc + if (!isturf(T)) + return - if(TIMER_COOLDOWN_CHECK(src, REF(user))) //prevents mining turfs in progress - return + if(TIMER_COOLDOWN_CHECK(src, REF(user))) //prevents mining turfs in progress + return - TIMER_COOLDOWN_START(src, REF(user), tool_mine_speed) + TIMER_COOLDOWN_START(src, REF(user), tool_mine_speed) - balloon_alert(user, "picking...") + balloon_alert(user, "picking...") - if(!I.use_tool(src, user, tool_mine_speed, volume=50)) - TIMER_COOLDOWN_END(src, REF(user)) //if we fail we can start again immediately - return - if(ismineralturf(src)) - gets_drilled(user, TRUE) - SSblackbox.record_feedback("tally", "pick_used_mining", 1, I.type) + if(!I.use_tool(src, user, tool_mine_speed, volume=50)) + TIMER_COOLDOWN_END(src, REF(user)) //if we fail we can start again immediately + return + if(ismineralturf(src)) + gets_drilled(user, TRUE) + SSblackbox.record_feedback("tally", "pick_used_mining", 1, I.type) /turf/closed/mineral/attack_hand(mob/user) - if(!weak_turf) + var/mining_arms = HAS_TRAIT(user, TRAIT_FIST_MINING) + if(!weak_turf && !mining_arms) return ..() var/turf/user_turf = user.loc if (!isturf(user_turf)) return if(TIMER_COOLDOWN_CHECK(src, REF(user))) //prevents mining turfs in progress return - TIMER_COOLDOWN_START(src, REF(user), hand_mine_speed) + var/mining_speed = mining_arms ? tool_mine_speed : hand_mine_speed + TIMER_COOLDOWN_START(src, REF(user), mining_speed) var/skill_modifier = user.mind?.get_skill_modifier(/datum/skill/mining, SKILL_SPEED_MODIFIER) || 1 balloon_alert(user, "pulling out pieces...") - if(!do_after(user, hand_mine_speed * skill_modifier, target = src)) + if(!do_after(user, mining_speed * skill_modifier, target = src)) TIMER_COOLDOWN_END(src, REF(user)) //if we fail we can start again immediately return if(ismineralturf(src)) diff --git a/code/modules/antagonists/abductor/machinery/pad.dm b/code/modules/antagonists/abductor/machinery/pad.dm index 515f181fc3c..f6fc659f6d1 100644 --- a/code/modules/antagonists/abductor/machinery/pad.dm +++ b/code/modules/antagonists/abductor/machinery/pad.dm @@ -54,10 +54,16 @@ name = "Huh" icon = 'icons/obj/abductor.dmi' icon_state = "teleport" - duration = 80 + duration = 8 SECONDS /obj/effect/temp_visual/teleport_abductor/Initialize(mapload) . = ..() var/datum/effect_system/spark_spread/S = new S.set_up(10,0,loc) S.start() + +/obj/effect/temp_visual/teleport_golem + name = "bluespace silhouette" + icon = 'icons/obj/abductor.dmi' + icon_state = "teleport" + duration = 6 SECONDS diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index c5d2defd623..498cc8f9c9d 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -208,7 +208,6 @@ GLOBAL_LIST_INIT(clown_mask_options, list( resistance_flags = FLAMMABLE actions_types = list(/datum/action/item_action/adjust) dog_fashion = /datum/dog_fashion/head/clown - species_exception = list(/datum/species/golem/bananium) has_fov = FALSE var/list/clownmask_designs = list() @@ -252,7 +251,6 @@ GLOBAL_LIST_INIT(clown_mask_options, list( righthand_file = 'icons/mob/inhands/clothing/hats_righthand.dmi' flags_cover = MASKCOVERSEYES resistance_flags = FLAMMABLE - species_exception = list(/datum/species/golem/bananium) has_fov = FALSE /obj/item/clothing/mask/gas/mime @@ -375,7 +373,6 @@ GLOBAL_LIST_INIT(clown_mask_options, list( max_integrity = 100 actions_types = list(/datum/action/item_action/adjust) dog_fashion = null - species_exception = list(/datum/species/golem/wood) var/list/tikimask_designs = list() /obj/item/clothing/mask/gas/tiki_mask/Initialize(mapload) diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm index df7f6da84e4..4c3887fec67 100644 --- a/code/modules/clothing/shoes/boots.dm +++ b/code/modules/clothing/shoes/boots.dm @@ -121,7 +121,6 @@ strip_delay = 20 equip_delay_other = 40 lace_time = 8 SECONDS - species_exception = list(/datum/species/golem/uranium) /datum/armor/shoes_workboots bio = 80 diff --git a/code/modules/clothing/shoes/clown.dm b/code/modules/clothing/shoes/clown.dm index 9c68e44cbbe..bc1415de84c 100644 --- a/code/modules/clothing/shoes/clown.dm +++ b/code/modules/clothing/shoes/clown.dm @@ -8,7 +8,6 @@ ///List of possible sounds for the squeak component to use, allows for different clown shoe subtypes to have different sounds. var/list/squeak_sound = list('sound/effects/footstep/clownstep1.ogg'=1,'sound/effects/footstep/clownstep2.ogg'=1) lace_time = 20 SECONDS // how the hell do these laces even work?? - species_exception = list(/datum/species/golem/bananium) /obj/item/clothing/shoes/clown_shoes/Initialize(mapload) . = ..() diff --git a/code/modules/clothing/suits/jacket.dm b/code/modules/clothing/suits/jacket.dm index d441db0efad..cc5909d41c5 100644 --- a/code/modules/clothing/suits/jacket.dm +++ b/code/modules/clothing/suits/jacket.dm @@ -54,7 +54,6 @@ resistance_flags = NONE max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/gun/ballistic/revolver/c38/detective, /obj/item/radio) - species_exception = list(/datum/species/golem/bone) //bad to the bone /obj/item/clothing/suit/jacket/leather/biker name = "biker jacket" @@ -67,7 +66,6 @@ icon_state = "pufferjacket" inhand_icon_state = "hostrench" armor_type = /datum/armor/jacket_puffer - species_exception = list(/datum/species/golem/bone) /datum/armor/jacket_puffer bio = 50 diff --git a/code/modules/clothing/suits/wintercoats.dm b/code/modules/clothing/suits/wintercoats.dm index 2d43655da20..a34759789b8 100644 --- a/code/modules/clothing/suits/wintercoats.dm +++ b/code/modules/clothing/suits/wintercoats.dm @@ -521,7 +521,6 @@ ) armor_type = /datum/armor/wintercoat_engineering hoodtype = /obj/item/clothing/head/hooded/winterhood/engineering - species_exception = list(/datum/species/golem/uranium) /datum/armor/wintercoat_engineering fire = 20 diff --git a/code/modules/clothing/under/jobs/civilian/clown_mime.dm b/code/modules/clothing/under/jobs/civilian/clown_mime.dm index 9d55a30d564..e726ba54689 100644 --- a/code/modules/clothing/under/jobs/civilian/clown_mime.dm +++ b/code/modules/clothing/under/jobs/civilian/clown_mime.dm @@ -29,7 +29,6 @@ desc = "'HONK!'" icon_state = "clown" inhand_icon_state = "clown" - species_exception = list(/datum/species/golem/bananium) female_sprite_flags = FEMALE_UNIFORM_TOP_ONLY can_adjust = FALSE diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm index b99e3fc90ec..e7611612a5b 100644 --- a/code/modules/clothing/under/jobs/engineering.dm +++ b/code/modules/clothing/under/jobs/engineering.dm @@ -54,7 +54,6 @@ name = "engineer's jumpsuit" icon_state = "engine" inhand_icon_state = "engi_suit" - species_exception = list(/datum/species/golem/uranium) /obj/item/clothing/under/rank/engineering/engineer/hazard name = "engineer's hazard jumpsuit" diff --git a/code/modules/explorer_drone/exodrone.dm b/code/modules/explorer_drone/exodrone.dm index 0f3d03f4baa..a33ad0f1430 100644 --- a/code/modules/explorer_drone/exodrone.dm +++ b/code/modules/explorer_drone/exodrone.dm @@ -444,7 +444,7 @@ GLOBAL_LIST_EMPTY(exodrone_launchers) /// The amount of uses left in this fuel pellet. var/uses = 5 -/obj/item/fuel_pellet/use() +/obj/item/fuel_pellet/use(used) uses-- if(uses <= 0) qdel(src) diff --git a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm index 00f2124f13d..73afd60b3a6 100644 --- a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm +++ b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm @@ -32,57 +32,33 @@ /obj/item/golem_shell name = "incomplete free golem shell" icon = 'icons/obj/wizard.dmi' - icon_state = "construct" - desc = "The incomplete body of a golem. Add ten sheets of any mineral to finish." + icon_state = "shell_unfinished" + desc = "The incomplete body of a golem. Add ten sheets of certain minerals to finish." w_class = WEIGHT_CLASS_BULKY - + /// Amount of minerals you need to feed the shell to wake it up + var/required_stacks = 10 + /// Type of shell to create var/shell_type = /obj/effect/mob_spawn/ghost_role/human/golem -/obj/item/golem_shell/attackby(obj/item/I, mob/user, params) +/obj/item/golem_shell/attackby(obj/item/potential_food, mob/user, params) . = ..() - var/static/list/golem_shell_species_types = list( - /obj/item/stack/sheet/iron = /datum/species/golem, - /obj/item/stack/sheet/glass = /datum/species/golem/glass, - /obj/item/stack/sheet/plasteel = /datum/species/golem/plasteel, - /obj/item/stack/sheet/mineral/sandstone = /datum/species/golem/sand, - /obj/item/stack/sheet/mineral/plasma = /datum/species/golem/plasma, - /obj/item/stack/sheet/mineral/diamond = /datum/species/golem/diamond, - /obj/item/stack/sheet/mineral/gold = /datum/species/golem/gold, - /obj/item/stack/sheet/mineral/silver = /datum/species/golem/silver, - /obj/item/stack/sheet/mineral/uranium = /datum/species/golem/uranium, - /obj/item/stack/sheet/mineral/bananium = /datum/species/golem/bananium, - /obj/item/stack/sheet/mineral/titanium = /datum/species/golem/titanium, - /obj/item/stack/sheet/mineral/plastitanium = /datum/species/golem/plastitanium, - /obj/item/stack/sheet/mineral/abductor = /datum/species/golem/alloy, - /obj/item/stack/sheet/mineral/wood = /datum/species/golem/wood, - /obj/item/stack/sheet/bluespace_crystal = /datum/species/golem/bluespace, - /obj/item/stack/sheet/runed_metal = /datum/species/golem/runic, - /obj/item/stack/medical/gauze = /datum/species/golem/cloth, - /obj/item/stack/sheet/cloth = /datum/species/golem/cloth, - /obj/item/stack/sheet/mineral/adamantine = /datum/species/golem/adamantine, - - /obj/item/stack/sheet/bronze = /datum/species/golem/bronze, - /obj/item/stack/sheet/cardboard = /datum/species/golem/cardboard, - /obj/item/stack/sheet/leather = /datum/species/golem/leather, - /obj/item/stack/sheet/bone = /datum/species/golem/bone, - /obj/item/stack/sheet/durathread = /datum/species/golem/durathread, - /obj/item/stack/sheet/cotton/durathread = /datum/species/golem/durathread, - /obj/item/stack/sheet/mineral/snow = /datum/species/golem/snow, - /obj/item/stack/sheet/mineral/metal_hydrogen= /datum/species/golem/mhydrogen, - ) - - if(!isstack(I)) + if(!isstack(potential_food)) + balloon_alert(user, "not a mineral!") return - var/obj/item/stack/stuff_stack = I - var/species = golem_shell_species_types[stuff_stack.merge_type] - if(!species) - to_chat(user, span_warning("You can't build a golem out of this kind of material!")) + var/obj/item/stack/stack_food = potential_food + var/stack_type = stack_food.merge_type + if (!is_path_in_list(stack_type, GLOB.golem_stack_food_directory)) + balloon_alert(user, "incompatible mineral!") return - if(!stuff_stack.use(10)) - to_chat(user, span_warning("You need at least ten sheets to finish a golem!")) + if(stack_food.amount < required_stacks) + balloon_alert(user, "not enough minerals!") return - to_chat(user, span_notice("You finish up the golem shell with ten sheets of [stuff_stack].")) - new shell_type(get_turf(src), species, user) + if(!do_after(user, delay = 4 SECONDS, target = src)) + return + if(!stack_food.use(required_stacks)) + balloon_alert(user, "not enough minerals!") + return + new shell_type(get_turf(src), /* creator = */ user, /* made_of = */ stack_type) qdel(src) ///made with xenobiology, the golem obeys its creator diff --git a/code/modules/mining/equipment/monster_organs/regenerative_core.dm b/code/modules/mining/equipment/monster_organs/regenerative_core.dm index bb56b773d73..f8c95536258 100644 --- a/code/modules/mining/equipment/monster_organs/regenerative_core.dm +++ b/code/modules/mining/equipment/monster_organs/regenerative_core.dm @@ -61,5 +61,3 @@ This process will trigger automatically if you are badly wounded." button_icon_state = "legion_core_stable" check_flags = NONE - -/datum/action/cooldown/monster_core_action/regenerative_core diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 207461167e2..8ad06edb849 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -241,6 +241,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ /obj/item/gibtonite/Initialize(mapload) . = ..() AddComponent(/datum/component/two_handed, require_twohands=TRUE) + AddComponent(/datum/component/golem_food, consume_on_eat = FALSE, golem_food_key = /obj/item/gibtonite) /obj/item/gibtonite/Destroy() qdel(wires) @@ -264,53 +265,61 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ if(I.tool_behaviour == TOOL_MINING || istype(I, /obj/item/resonator) || I.force >= 10) GibtoniteReaction(user) return - if(primed) - if(istype(I, /obj/item/mining_scanner) || istype(I, /obj/item/t_scanner/adv_mining_scanner) || I.tool_behaviour == TOOL_MULTITOOL) - primed = FALSE - if(det_timer) - deltimer(det_timer) - user.visible_message(span_notice("The chain reaction stopped! ...The ore's quality looks diminished."), span_notice("You stopped the chain reaction. ...The ore's quality looks diminished.")) - icon_state = "Gibtonite ore" - quality = GIBTONITE_QUALITY_LOW - return - ..() + + if(istype(I, /obj/item/mining_scanner) || istype(I, /obj/item/t_scanner/adv_mining_scanner) || I.tool_behaviour == TOOL_MULTITOOL) + defuse(user) + return + + return ..() + +/// Stop the reaction and reduce ore explosive power +/obj/item/gibtonite/proc/defuse(mob/defuser) + if (!primed) + return + primed = FALSE + if(det_timer) + deltimer(det_timer) + defuser?.visible_message(span_notice("The chain reaction stopped! ...The ore's quality looks diminished."), span_notice("You stopped the chain reaction. ...The ore's quality looks diminished.")) + icon_state = "Gibtonite ore" + quality = GIBTONITE_QUALITY_LOW /obj/item/gibtonite/attack_self(user) if(wires) wires.interact(user) else - ..() + return ..() /obj/item/gibtonite/bullet_act(obj/projectile/P) GibtoniteReaction(P.firer) - . = ..() + return ..() /obj/item/gibtonite/ex_act() GibtoniteReaction(null, 1) return TRUE /obj/item/gibtonite/proc/GibtoniteReaction(mob/user, triggered_by = 0) - if(!primed) - primed = TRUE - playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,TRUE) - icon_state = "Gibtonite active" - var/notify_admins = FALSE - if(z != 5)//Only annoy the admins ingame if we're triggered off the mining zlevel - notify_admins = TRUE + if(primed) + return + primed = TRUE + playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,TRUE) + icon_state = "Gibtonite active" + var/notify_admins = FALSE + if(!is_mining_level(z))//Only annoy the admins ingame if we're triggered off the mining zlevel + notify_admins = TRUE - if(triggered_by == 1) - log_bomber(null, "An explosion has primed a", src, "for detonation", notify_admins) - else if(triggered_by == 2) - var/turf/bombturf = get_turf(src) - if(notify_admins) - message_admins("A signal has triggered a [name] to detonate at [ADMIN_VERBOSEJMP(bombturf)]. Igniter attacher: [ADMIN_LOOKUPFLW(attacher)]") - var/bomb_message = "A signal has primed a [name] for detonation at [AREACOORD(bombturf)]. Igniter attacher: [key_name(attacher)]." - log_game(bomb_message) - GLOB.bombers += bomb_message - else - user.visible_message(span_warning("[user] strikes \the [src], causing a chain reaction!"), span_danger("You strike \the [src], causing a chain reaction.")) - log_bomber(user, "has primed a", src, "for detonation", notify_admins) - det_timer = addtimer(CALLBACK(src, PROC_REF(detonate), notify_admins), det_time, TIMER_STOPPABLE) + if(triggered_by == 1) + log_bomber(null, "An explosion has primed a", src, "for detonation", notify_admins) + else if(triggered_by == 2) + var/turf/bombturf = get_turf(src) + if(notify_admins) + message_admins("A signal has triggered a [name] to detonate at [ADMIN_VERBOSEJMP(bombturf)]. Igniter attacher: [ADMIN_LOOKUPFLW(attacher)]") + var/bomb_message = "A signal has primed a [name] for detonation at [AREACOORD(bombturf)]. Igniter attacher: [key_name(attacher)]." + log_game(bomb_message) + GLOB.bombers += bomb_message + else + user.visible_message(span_warning("[user] strikes \the [src], causing a chain reaction!"), span_danger("You strike \the [src], causing a chain reaction.")) + log_bomber(user, "has primed a", src, "for detonation", notify_admins) + det_timer = addtimer(CALLBACK(src, PROC_REF(detonate), notify_admins), det_time, TIMER_STOPPABLE) /obj/item/gibtonite/proc/detonate(notify_admins) if(primed) @@ -323,6 +332,14 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ explosion(src, heavy_impact_range = 1, light_impact_range = 3, adminlog = notify_admins) qdel(src) +/obj/item/gibtonite/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if (throwingdatum.dist_travelled < 2 || !isliving(hit_atom)) + return + var/mob/living/hit_mob = hit_atom + hit_mob.Paralyze(1.5 SECONDS) + hit_mob.Knockdown(8 SECONDS) + /obj/item/stack/ore/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) . = ..() pixel_x = base_pixel_x + rand(0, 16) - 8 diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index da0c6d55200..04e754c8d22 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -217,13 +217,13 @@ return FALSE if (merge_stacks) - if (istype(active_stack) && active_stack.can_merge(item_stack)) + if (istype(active_stack) && active_stack.can_merge(item_stack, inhand = TRUE)) if (item_stack.merge(active_stack)) to_chat(usr, span_notice("Your [active_stack.name] stack now contains [active_stack.get_amount()] [active_stack.singular_name]\s.")) return TRUE else var/obj/item/stack/inactive_stack = get_inactive_held_item() - if (istype(inactive_stack) && inactive_stack.can_merge(item_stack)) + if (istype(inactive_stack) && inactive_stack.can_merge(item_stack, inhand = TRUE)) if (item_stack.merge(inactive_stack)) to_chat(usr, span_notice("Your [inactive_stack.name] stack now contains [inactive_stack.get_amount()] [inactive_stack.singular_name]\s.")) return TRUE diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 78d78bcc7cb..2f0397a46b5 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -373,6 +373,14 @@ desc = "This juicy piece of meat has a clearly underdeveloped frontal lobe." organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_PRIMITIVE) // No literacy +/obj/item/organ/internal/brain/golem + name = "crystalline matrix" + desc = "This collection of sparkling gems somehow allows a golem to think." + icon_state = "adamantine_resonator" + color = COLOR_GOLEM_GRAY + status = ORGAN_MINERAL + organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP, TRAIT_ROCK_METAMORPHIC) + ////////////////////////////////////TRAUMAS//////////////////////////////////////// /obj/item/organ/internal/brain/proc/has_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_ABSOLUTE) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index bea0d1dba96..38fda76da2a 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -860,7 +860,7 @@ var/obj/item/bodypart/new_part = pick(GLOB.bioscrambler_valid_parts) var/obj/item/bodypart/picked_user_part = get_bodypart(initial(new_part.body_zone)) - if (!(picked_user_part?.bodytype & BODYTYPE_ROBOTIC)) + if (picked_user_part && BODYTYPE_CAN_BE_BIOSCRAMBLED(picked_user_part.bodytype)) changed_something = TRUE new_part = new new_part() new_part.replace_limb(src, special = TRUE) @@ -878,7 +878,7 @@ /mob/living/carbon/proc/init_bioscrambler_lists() var/list/body_parts = typesof(/obj/item/bodypart/chest) + typesof(/obj/item/bodypart/head) + subtypesof(/obj/item/bodypart/arm) + subtypesof(/obj/item/bodypart/leg) for (var/obj/item/bodypart/part as anything in body_parts) - if (!is_type_in_typecache(part, GLOB.bioscrambler_parts_blacklist) && !(initial(part.bodytype) & BODYTYPE_ROBOTIC)) + if (!is_type_in_typecache(part, GLOB.bioscrambler_parts_blacklist) && BODYTYPE_CAN_BE_BIOSCRAMBLED(initial(part.bodytype))) continue body_parts -= part GLOB.bioscrambler_valid_parts = body_parts diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index a340d7650ea..958c22ab44d 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -32,6 +32,41 @@ if(slot_flags & (ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET)) update_pockets() +/// Updates features and clothing attached to a specific limb with limb-specific offsets +/mob/living/carbon/proc/update_features(feature_key) + switch(feature_key) + if(OFFSET_UNIFORM) + update_worn_undersuit() + if(OFFSET_ID) + update_worn_id() + if(OFFSET_GLOVES) + update_worn_gloves() + if(OFFSET_GLASSES) + update_worn_glasses() + if(OFFSET_EARS) + update_inv_ears() + if(OFFSET_SHOES) + update_worn_shoes() + if(OFFSET_S_STORE) + update_suit_storage() + if(OFFSET_FACEMASK) + update_worn_mask() + if(OFFSET_HEAD) + update_worn_head() + if(OFFSET_FACE) + dna?.species?.handle_body(src) // updates eye icon + update_worn_mask() + if(OFFSET_BELT) + update_worn_belt() + if(OFFSET_BACK) + update_worn_back() + if(OFFSET_SUIT) + update_worn_oversuit() + if(OFFSET_NECK) + update_worn_neck() + if(OFFSET_HELD) + update_held_items() + //IMPORTANT: Multiple animate() calls do not stack well, so try to do them all at once if you can. /mob/living/carbon/perform_update_transform() var/matrix/ntransform = matrix(transform) //aka transform.Copy() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index dcb0d457072..3634807bce3 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1065,78 +1065,6 @@ /mob/living/carbon/human/species/golem race = /datum/species/golem -/mob/living/carbon/human/species/golem/adamantine - race = /datum/species/golem/adamantine - -/mob/living/carbon/human/species/golem/plasma - race = /datum/species/golem/plasma - -/mob/living/carbon/human/species/golem/diamond - race = /datum/species/golem/diamond - -/mob/living/carbon/human/species/golem/gold - race = /datum/species/golem/gold - -/mob/living/carbon/human/species/golem/silver - race = /datum/species/golem/silver - -/mob/living/carbon/human/species/golem/plasteel - race = /datum/species/golem/plasteel - -/mob/living/carbon/human/species/golem/titanium - race = /datum/species/golem/titanium - -/mob/living/carbon/human/species/golem/plastitanium - race = /datum/species/golem/plastitanium - -/mob/living/carbon/human/species/golem/alien_alloy - race = /datum/species/golem/alloy - -/mob/living/carbon/human/species/golem/wood - race = /datum/species/golem/wood - -/mob/living/carbon/human/species/golem/uranium - race = /datum/species/golem/uranium - -/mob/living/carbon/human/species/golem/sand - race = /datum/species/golem/sand - -/mob/living/carbon/human/species/golem/glass - race = /datum/species/golem/glass - -/mob/living/carbon/human/species/golem/bluespace - race = /datum/species/golem/bluespace - -/mob/living/carbon/human/species/golem/bananium - race = /datum/species/golem/bananium - -/mob/living/carbon/human/species/golem/blood_cult - race = /datum/species/golem/runic - -/mob/living/carbon/human/species/golem/cloth - race = /datum/species/golem/cloth - -/mob/living/carbon/human/species/golem/plastic - race = /datum/species/golem/plastic - -/mob/living/carbon/human/species/golem/bronze - race = /datum/species/golem/bronze - -/mob/living/carbon/human/species/golem/cardboard - race = /datum/species/golem/cardboard - -/mob/living/carbon/human/species/golem/leather - race = /datum/species/golem/leather - -/mob/living/carbon/human/species/golem/bone - race = /datum/species/golem/bone - -/mob/living/carbon/human/species/golem/durathread - race = /datum/species/golem/durathread - -/mob/living/carbon/human/species/golem/snow - race = /datum/species/golem/snow - /mob/living/carbon/human/species/jelly race = /datum/species/jelly diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm index b733568f8dd..9fc659eb5b4 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -105,13 +105,13 @@ There are several things that need to be remembered: var/mutant_styles = NONE // SKYRAT EDIT ADDITON - mutant styles to pass down to build_worn_icon. //BEGIN SPECIES HANDLING if((dna?.species.bodytype & BODYTYPE_MONKEY) && (uniform.supports_variations_flags & CLOTHING_MONKEY_VARIATION)) - icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_UNIFORM, w_uniform) + icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_UNIFORM, w_uniform, src) else if((dna?.species.bodytype & BODYTYPE_DIGITIGRADE) && (uniform.supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION)) icon_file = uniform.worn_icon_digi || DIGITIGRADE_UNIFORM_FILE // SKYRAT EDIT CHANGE // SKYRAT EDIT ADDITION - birbs else if(dna.species.bodytype & BODYTYPE_CUSTOM) - icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_UNIFORM, w_uniform) + icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_UNIFORM, w_uniform, src) // SKYRAT EDIT END //Female sprites have lower priority than digitigrade sprites @@ -139,9 +139,8 @@ There are several things that need to be remembered: mutant_styles = mutant_styles, // SKYRAT EDIT ADDITION - Taur-friendly uniforms! ) - if(!handled_by_bodytype && (OFFSET_UNIFORM in dna.species.offset_features)) // SKYRAT EDIT CHANGE - uniform_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1] - uniform_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2] + var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_uniform_offset?.apply_offset(uniform_overlay) overlays_standing[UNIFORM_LAYER] = uniform_overlay apply_overlay(UNIFORM_LAYER) @@ -163,9 +162,9 @@ There are several things that need to be remembered: id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = icon_file) if(!id_overlay) return - if(OFFSET_ID in dna.species.offset_features) - id_overlay.pixel_x += dna.species.offset_features[OFFSET_ID][1] - id_overlay.pixel_y += dna.species.offset_features[OFFSET_ID][2] + + var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_id_offset?.apply_offset(id_overlay) overlays_standing[ID_LAYER] = id_overlay apply_overlay(ID_LAYER) @@ -203,7 +202,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_GLOVES, gloves) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_GLOVES, gloves, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -211,9 +210,13 @@ There are several things that need to be remembered: var/mutable_appearance/gloves_overlay = gloves.build_worn_icon(default_layer = GLOVES_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_GLOVES in dna.species.offset_features)) // SKYRAT EDIT CHANGE - gloves_overlay.pixel_x += dna.species.offset_features[OFFSET_GLOVES][1] - gloves_overlay.pixel_y += dna.species.offset_features[OFFSET_GLOVES][2] + var/feature_y_offset = 0 + for (var/obj/item/bodypart/arm/my_hand as anything in hand_bodyparts) + var/list/glove_offset = my_hand.worn_glove_offset?.get_offset() + if (glove_offset && glove_offset["y"] > feature_y_offset) + feature_y_offset = glove_offset["y"] + + gloves_overlay.pixel_y += feature_y_offset overlays_standing[GLOVES_LAYER] = gloves_overlay apply_overlay(GLOVES_LAYER) @@ -221,7 +224,8 @@ There are several things that need to be remembered: /mob/living/carbon/human/update_worn_glasses() remove_overlay(GLASSES_LAYER) - if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated + var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD) + if(isnull(my_head)) //decapitated return if(client && hud_used) @@ -240,7 +244,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_GLASSES, glasses) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_GLASSES, glasses, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -248,9 +252,7 @@ There are several things that need to be remembered: var/mutable_appearance/glasses_overlay = glasses.build_worn_icon(default_layer = GLASSES_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_GLASSES in dna.species.offset_features)) // SKYRAT EDIT CHANGE - glasses_overlay.pixel_x += dna.species.offset_features[OFFSET_GLASSES][1] - glasses_overlay.pixel_y += dna.species.offset_features[OFFSET_GLASSES][2] + my_head.worn_glasses_offset?.apply_offset(glasses_overlay) overlays_standing[GLASSES_LAYER] = glasses_overlay apply_overlay(GLASSES_LAYER) @@ -258,7 +260,8 @@ There are several things that need to be remembered: /mob/living/carbon/human/update_inv_ears() remove_overlay(EARS_LAYER) - if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated + var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD) + if(isnull(my_head)) //decapitated return if(client && hud_used) @@ -277,7 +280,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_EARS, ears) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_EARS, ears, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -285,9 +288,7 @@ There are several things that need to be remembered: var/mutable_appearance/ears_overlay = ears.build_worn_icon(default_layer = EARS_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_EARS in dna.species.offset_features)) // SKYRAT EDIT CHANGE - ears_overlay.pixel_x += dna.species.offset_features[OFFSET_EARS][1] - ears_overlay.pixel_y += dna.species.offset_features[OFFSET_EARS][2] + my_head.worn_ears_offset?.apply_offset(ears_overlay) overlays_standing[EARS_LAYER] = ears_overlay apply_overlay(EARS_LAYER) @@ -310,7 +311,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_NECK, wear_neck) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_NECK, wear_neck, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -318,9 +319,8 @@ There are several things that need to be remembered: var/mutable_appearance/neck_overlay = worn_item.build_worn_icon(default_layer = NECK_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_NECK in dna.species.offset_features)) // SKYRAT EDIT CHANGE - neck_overlay.pixel_x += dna.species.offset_features[OFFSET_NECK][1] - neck_overlay.pixel_y += dna.species.offset_features[OFFSET_NECK][2] + var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_belt_offset?.apply_offset(neck_overlay) overlays_standing[NECK_LAYER] = neck_overlay apply_overlay(NECK_LAYER) @@ -353,7 +353,7 @@ There are several things that need to be remembered: icon_file = worn_item.worn_icon_digi || DIGITIGRADE_SHOES_FILE // SKYRAT EDIT CHANGE mutant_override = TRUE // SKYRAT EDIT ADDITION else if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_SHOES, shoes) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_SHOES, shoes, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -363,9 +363,19 @@ There are several things that need to be remembered: var/mutable_appearance/shoes_overlay = shoes.build_worn_icon(default_layer = SHOES_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_SHOES in dna.species.offset_features)) // SKYRAT EDIT CHANGE - shoes_overlay.pixel_x += dna.species.offset_features[OFFSET_SHOES][1] - shoes_overlay.pixel_y += dna.species.offset_features[OFFSET_SHOES][2] + if(!shoes_overlay) + return + + var/feature_y_offset = 0 + for (var/body_zone in list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) + var/obj/item/bodypart/leg/my_leg = get_bodypart(body_zone) + if(isnull(my_leg)) + continue + var/list/foot_offset = my_leg.worn_foot_offset?.get_offset() + if (foot_offset && foot_offset["y"] > feature_y_offset) + feature_y_offset = foot_offset["y"] + + shoes_overlay.pixel_y += feature_y_offset overlays_standing[SHOES_LAYER] = shoes_overlay apply_overlay(SHOES_LAYER) @@ -388,10 +398,8 @@ There are several things that need to be remembered: return var/mutable_appearance/s_store_overlay = worn_item.build_worn_icon(default_layer = SUIT_STORE_LAYER, default_icon_file = 'icons/mob/clothing/belt_mirror.dmi') - - if(OFFSET_S_STORE in dna.species.offset_features) - s_store_overlay.pixel_x += dna.species.offset_features[OFFSET_S_STORE][1] - s_store_overlay.pixel_y += dna.species.offset_features[OFFSET_S_STORE][2] + var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_suit_storage_offset?.apply_offset(s_store_overlay) overlays_standing[SUIT_STORE_LAYER] = s_store_overlay apply_overlay(SUIT_STORE_LAYER) @@ -413,7 +421,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION - This needs to be refactored. var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_HEAD, head) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_HEAD, head, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -426,9 +434,8 @@ There are several things that need to be remembered: var/mutable_appearance/head_overlay = head.build_worn_icon(default_layer = HEAD_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_HEAD in dna.species.offset_features)) // SKYRAT EDIT CHANGE - head_overlay.pixel_x += dna.species.offset_features[OFFSET_HEAD][1] - head_overlay.pixel_y += dna.species.offset_features[OFFSET_HEAD][2] + var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD) + my_head?.worn_head_offset?.apply_offset(head_overlay) overlays_standing[HEAD_LAYER] = head_overlay update_mutant_bodyparts() @@ -453,7 +460,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_BELT, belt) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_BELT, belt, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -461,9 +468,8 @@ There are several things that need to be remembered: var/mutable_appearance/belt_overlay = belt.build_worn_icon(default_layer = BELT_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_BELT in dna.species.offset_features)) // SKYRAT EDIT CHANGE - belt_overlay.pixel_x += dna.species.offset_features[OFFSET_BELT][1] - belt_overlay.pixel_y += dna.species.offset_features[OFFSET_BELT][2] + var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_belt_offset?.apply_offset(belt_overlay) overlays_standing[BELT_LAYER] = belt_overlay apply_overlay(BELT_LAYER) @@ -480,23 +486,22 @@ There are several things that need to be remembered: update_hud_wear_suit(worn_item) var/icon_file = DEFAULT_SUIT_FILE - var/mutant_override = FALSE // SKYRAT EDIT ADDITION - var/mutant_styles = NONE // SKYRAT EDIT ADDITON - mutant styles to pass down to build_worn_icon. + // SKYRAT EDIT ADDITION + var/mutant_override = FALSE + var/mutant_styles = NONE + //More currently unused digitigrade handling if(dna.species.bodytype & BODYTYPE_DIGITIGRADE) if(worn_item.supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION) icon_file = worn_item.worn_icon_digi || DIGITIGRADE_SUIT_FILE // SKYRAT EDIT CHANGE - mutant_override = TRUE // SKYRAT EDIT ADDITION + mutant_override = TRUE - // SKYRAT EDIT ADDITION else if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_SUIT, wear_suit) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_SUIT, wear_suit, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE - // SKYRAT EDIT END - // SKYRAT EDIT ADDITION START - Taur-friendly suits! if(dna.species.bodytype & BODYTYPE_TAUR) var/obj/item/clothing/suit/worn_suit = wear_suit if(istype(worn_suit) && worn_suit.gets_cropped_on_taurs) @@ -505,9 +510,8 @@ There are several things that need to be remembered: var/mutable_appearance/suit_overlay = wear_suit.build_worn_icon(default_layer = SUIT_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null, mutant_styles = mutant_styles) // SKYRAT EDIT CHANGE - Mutant bodytypes and Taur-friendly suits! - if(!mutant_override && (OFFSET_UNIFORM in dna.species.offset_features)) // SKYRAT EDIT CHANGE - suit_overlay.pixel_x += dna.species.offset_features[OFFSET_SUIT][1] - suit_overlay.pixel_y += dna.species.offset_features[OFFSET_SUIT][2] + var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_suit_offset?.apply_offset(suit_overlay) overlays_standing[SUIT_LAYER] = suit_overlay update_body_parts() update_mutant_bodyparts() @@ -539,7 +543,8 @@ There are several things that need to be remembered: /mob/living/carbon/human/update_worn_mask() remove_overlay(FACEMASK_LAYER) - if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated + var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD) + if(isnull(my_head)) //Decapitated return if(client && hud_used && hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_MASK) + 1]) @@ -558,7 +563,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_MASK, wear_mask) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_MASK, wear_mask, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -571,9 +576,7 @@ There are several things that need to be remembered: var/mutable_appearance/mask_overlay = wear_mask.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = icon_file, override_file = mutant_override ? icon_file : null) // SKYRAT EDIT CHANGE - if(!mutant_override && (OFFSET_FACEMASK in dna.species.offset_features)) // SKYRAT EDIT CHANGE - mask_overlay.pixel_x += dna.species.offset_features[OFFSET_FACEMASK][1] - mask_overlay.pixel_y += dna.species.offset_features[OFFSET_FACEMASK][2] + my_head.worn_mask_offset?.apply_offset(mask_overlay) overlays_standing[FACEMASK_LAYER] = mask_overlay apply_overlay(FACEMASK_LAYER) @@ -595,7 +598,7 @@ There are several things that need to be remembered: // SKYRAT EDIT ADDITION var/mutant_override = FALSE if(dna.species.bodytype & BODYTYPE_CUSTOM) - var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_MISC, back) + var/species_icon_file = dna.species.generate_custom_worn_icon(LOADOUT_ITEM_MISC, back, src) if(species_icon_file) icon_file = species_icon_file mutant_override = TRUE @@ -605,9 +608,8 @@ There are several things that need to be remembered: if(!back_overlay) return - if(!mutant_override && (OFFSET_BACK in dna.species.offset_features)) // SKYRAT EDIT CHANGE - back_overlay.pixel_x += dna.species.offset_features[OFFSET_BACK][1] - back_overlay.pixel_y += dna.species.offset_features[OFFSET_BACK][2] + var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_back_offset?.apply_offset(back_overlay) overlays_standing[BACK_LAYER] = back_overlay apply_overlay(BACK_LAYER) @@ -627,8 +629,9 @@ There are several things that need to be remembered: var/list/hands = list() for(var/obj/item/worn_item in held_items) + var/held_index = get_held_index_of_item(worn_item) if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) - worn_item.screen_loc = ui_hand_position(get_held_index_of_item(worn_item)) + worn_item.screen_loc = ui_hand_position(held_index) client.screen += worn_item if(observers?.len) for(var/M in observers) @@ -645,13 +648,11 @@ There are several things that need to be remembered: if(!t_state) t_state = worn_item.icon_state - var/icon_file = worn_item.lefthand_file var/mutable_appearance/hand_overlay - if(get_held_index_of_item(worn_item) % 2 == 0) - icon_file = worn_item.righthand_file - hand_overlay = worn_item.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) - else - hand_overlay = worn_item.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) + var/icon_file = held_index % 2 == 0 ? worn_item.righthand_file : worn_item.lefthand_file + hand_overlay = worn_item.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) + var/obj/item/bodypart/arm/held_in_hand = hand_bodyparts[held_index] + held_in_hand?.held_hand_offset?.apply_offset(hand_overlay) hands += hand_overlay overlays_standing[HANDS_LAYER] = hands @@ -903,24 +904,22 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E if(!dna?.species) return - var/obj/item/bodypart/HD = get_bodypart("head") + var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD) - if (!istype(HD)) + if (!istype(my_head)) return - HD.update_limb(is_creating = update_limb_data) + my_head.update_limb(is_creating = update_limb_data) - add_overlay(HD.get_limb_icon()) + add_overlay(my_head.get_limb_icon()) update_damage_overlays() - if(HD && !(HAS_TRAIT(src, TRAIT_HUSK))) + if(my_head && !(HAS_TRAIT(src, TRAIT_HUSK))) // lipstick if(lip_style && (LIPS in dna.species.species_traits)) var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/species/human/human_face.dmi', "lips_[lip_style]", -BODY_LAYER) lip_overlay.color = lip_color - if(OFFSET_FACE in dna.species.offset_features) - lip_overlay.pixel_x += dna.species.offset_features[OFFSET_FACE][1] - lip_overlay.pixel_y += dna.species.offset_features[OFFSET_FACE][2] + my_head.worn_face_offset?.apply_offset(lip_overlay) add_overlay(lip_overlay) // eyes @@ -930,18 +929,14 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E add_overlay(parent_eyes.generate_body_overlay(src)) else var/mutable_appearance/missing_eyes = mutable_appearance('icons/mob/species/human/human_face.dmi', "eyes_missing", -BODY_LAYER) - if(OFFSET_FACE in dna.species.offset_features) - missing_eyes.pixel_x += dna.species.offset_features[OFFSET_FACE][1] - missing_eyes.pixel_y += dna.species.offset_features[OFFSET_FACE][2] + my_head.worn_face_offset?.apply_offset(missing_eyes) add_overlay(missing_eyes) //SKYRAT EDIT ADDITION if (parent_eyes && parent_eyes.is_emissive) var/mutable_appearance/emissive_appearance = emissive_appearance('icons/mob/species/human/human_face.dmi', parent_eyes ? parent_eyes.eye_icon_state : "eyes_missing", -BODY_LAYER) emissive_appearance.appearance_flags &= ~RESET_TRANSFORM - if(OFFSET_FACE in dna.species.offset_features) - emissive_appearance.pixel_x += dna.species.offset_features[OFFSET_FACE][1] - emissive_appearance.pixel_y += dna.species.offset_features[OFFSET_FACE][2] + my_head.worn_face_offset?.apply_offset(emissive_appearance) add_overlay(emissive_appearance) //SKYRAT EDIT END diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 14ae96e8f14..4c55c40c5fd 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -25,24 +25,6 @@ GLOBAL_LIST_EMPTY(features_by_species) var/sexes = TRUE ///A bitfield of "bodytypes", updated by /datum/obj/item/bodypart/proc/synchronize_bodytypes() var/bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC - ///Clothing offsets. If a species has a different body than other species, you can offset clothing so they look less weird. - var/list/offset_features = list( - OFFSET_UNIFORM = list(0,0), - OFFSET_ID = list(0,0), - OFFSET_GLOVES = list(0,0), - OFFSET_GLASSES = list(0,0), - OFFSET_EARS = list(0,0), - OFFSET_SHOES = list(0,0), - OFFSET_S_STORE = list(0,0), - OFFSET_FACEMASK = list(0,0), - OFFSET_HEAD = list(0,0), - OFFSET_FACE = list(0,0), - OFFSET_BELT = list(0,0), - OFFSET_BACK = list(0,0), - OFFSET_SUIT = list(0,0), - OFFSET_NECK = list(0,0), - OFFSET_ACCESSORY = list(0, 0), // SKYRAT EDIT ADDITION - ) ///The maximum number of bodyparts this species can have. var/max_bodypart_count = 6 @@ -635,9 +617,7 @@ GLOBAL_LIST_EMPTY(features_by_species) if(species_human.lip_style && (LIPS in species_traits)) var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/species/human/human_face.dmi', "lips_[species_human.lip_style]", -BODY_LAYER) lip_overlay.color = species_human.lip_color - if(OFFSET_FACE in species_human.dna.species.offset_features) - lip_overlay.pixel_x += species_human.dna.species.offset_features[OFFSET_FACE][1] - lip_overlay.pixel_y += species_human.dna.species.offset_features[OFFSET_FACE][2] + noggin.worn_face_offset?.apply_offset(lip_overlay) lip_overlay.pixel_y += height_offset standing += lip_overlay @@ -650,24 +630,22 @@ GLOBAL_LIST_EMPTY(features_by_species) //cut any possible vis overlays if(body_vis_overlays.len) SSvis_overlays.remove_vis_overlay(species_human, body_vis_overlays) - - if(OFFSET_FACE in species_human.dna.species.offset_features) - add_pixel_x = species_human.dna.species.offset_features[OFFSET_FACE][1] - add_pixel_y = species_human.dna.species.offset_features[OFFSET_FACE][2] + var/list/feature_offset = noggin.worn_face_offset?.get_offset() + if(feature_offset) + add_pixel_x = feature_offset["x"] + add_pixel_y = feature_offset["y"] add_pixel_y += height_offset - if(!eye_organ) + if(eye_organ) + eye_organ.refresh(call_update = FALSE) + for(var/mutable_appearance/eye_overlay in eye_organ.generate_body_overlay(species_human)) + eye_overlay.pixel_y += height_offset + standing += eye_overlay + else if (!(NOEYEHOLES in species_traits)) no_eyeslay = mutable_appearance('icons/mob/species/human/human_face.dmi', "eyes_missing", -BODY_LAYER) no_eyeslay.pixel_x += add_pixel_x no_eyeslay.pixel_y += add_pixel_y standing += no_eyeslay - else - eye_organ.refresh(call_update = FALSE) - - if(!no_eyeslay) - for(var/mutable_appearance/eye_overlay in eye_organ.generate_body_overlay(species_human)) - eye_overlay.pixel_y += height_offset - standing += eye_overlay // organic body markings if(HAS_MARKINGS in species_traits) diff --git a/code/modules/mob/living/carbon/human/species_types/abductors.dm b/code/modules/mob/living/carbon/human/species_types/abductors.dm index 3706d114457..e1387adf0a1 100644 --- a/code/modules/mob/living/carbon/human/species_types/abductors.dm +++ b/code/modules/mob/living/carbon/human/species_types/abductors.dm @@ -11,6 +11,7 @@ TRAIT_NOHUNGER, TRAIT_VIRUSIMMUNE, TRAIT_NOBLOOD, + TRAIT_NO_DEBRAIN_OVERLAY, ) mutanttongue = /obj/item/organ/internal/tongue/abductor mutantstomach = null diff --git a/code/modules/mob/living/carbon/human/species_types/android.dm b/code/modules/mob/living/carbon/human/species_types/android.dm index 152d5609b45..41828bdf1b4 100644 --- a/code/modules/mob/living/carbon/human/species_types/android.dm +++ b/code/modules/mob/living/carbon/human/species_types/android.dm @@ -23,6 +23,7 @@ TRAIT_RESISTHIGHPRESSURE, TRAIT_TOXIMMUNE, TRAIT_NOBLOOD, + TRAIT_NO_DEBRAIN_OVERLAY, ) inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index a13272c23ae..dfa61321d58 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -1,44 +1,46 @@ +/// Animated beings of stone. They have increased defenses, and do not need to breathe. They must eat minerals to live, which give additional buffs. /datum/species/golem - // Animated beings of stone. They have increased defenses, and do not need to breathe. They're also slow as fuuuck. name = "Golem" id = SPECIES_GOLEM species_traits = list( - NOTRANSSTING, - MUTCOLORS, - NO_UNDERWEAR, NO_DNA_COPY, + NOTRANSSTING, + NO_UNDERWEAR, + NOEYEHOLES, ) inherent_traits = list( TRAIT_GENELESS, + TRAIT_LAVA_IMMUNE, TRAIT_NOBREATH, TRAIT_NODISMEMBER, + TRAIT_NOBLOOD, TRAIT_NOFIRE, TRAIT_PIERCEIMMUNE, TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHEAT, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_NOBLOOD, ) mutantheart = null mutantlungs = null inherent_biotypes = MOB_HUMANOID|MOB_MINERAL - mutant_organs = list(/obj/item/organ/internal/adamantine_resonator) - speedmod = 2 + liked_food = STONE payday_modifier = 0.75 armor = 55 siemens_coeff = 0 no_equip_flags = ITEM_SLOT_MASK | ITEM_SLOT_OCLOTHING | ITEM_SLOT_GLOVES | ITEM_SLOT_FEET | ITEM_SLOT_ICLOTHING | ITEM_SLOT_SUITSTORE nojumpsuit = 1 changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC - sexes = 1 + sexes = FALSE meat = /obj/item/food/meat/slab/human/mutant/golem species_language_holder = /datum/language_holder/golem - // To prevent golem subtypes from overwhelming the odds when random species - // changes, only the Random Golem type can be chosen - fixed_mut_color = "#aaaaaa" + bodytemp_heat_damage_limit = BODYTEMP_HEAT_LAVALAND_SAFE + bodytemp_cold_damage_limit = BODYTEMP_COLD_ICEBOX_SAFE + + mutant_organs = list(/obj/item/organ/internal/adamantine_resonator) + mutanteyes = /obj/item/organ/internal/eyes/golem + mutantbrain = /obj/item/organ/internal/brain/golem + mutanttongue = /obj/item/organ/internal/tongue/golem + mutantstomach = /obj/item/organ/internal/stomach/golem + mutantappendix = /obj/item/organ/internal/appendix/golem bodypart_overrides = list( BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem, BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem, @@ -48,1333 +50,55 @@ BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem, ) - var/info_text = "As an Iron Golem, you don't have any special traits." - var/random_eligible = TRUE //If false, the golem subtype can't be made through golem mutation toxin - - var/prefix = "Iron" - var/list/special_names = list("Tarkus") + /// Chance that we will generate a human surname, for lore reasons var/human_surname_chance = 3 - var/special_name_chance = 5 /datum/species/golem/random_name(gender,unique,lastname) - var/golem_surname = pick(GLOB.golem_names) - // 3% chance that our golem has a human surname, because - // cultural contamination - if(prob(human_surname_chance)) - golem_surname = pick(GLOB.last_names) - else if(special_names?.len && prob(special_name_chance)) - golem_surname = pick(special_names) - - var/golem_name = "[prefix] [golem_surname]" - return golem_name + var/name = pick(GLOB.golem_names) + if (prob(human_surname_chance)) + name += " [pick(GLOB.last_names)]" + return name /datum/species/golem/create_pref_unique_perks() var/list/to_add = list() to_add += list(list( SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK, - SPECIES_PERK_ICON = "gem", + SPECIES_PERK_ICON = "user-shield", SPECIES_PERK_NAME = "Lithoid", - SPECIES_PERK_DESC = "Lithoids are creatures made out of elements instead of \ - blood and flesh. Because of this, they're generally stronger, slower, \ - and mostly immune to environmental dangers and dangers to their health, \ - such as viruses and dismemberment.", + SPECIES_PERK_DESC = "Lithoids are creatures made out of minerals instead of \ + blood and flesh. They are strong and immune to many environmental and personal dangers \ + such as fire, radiation, lack of air, lava, viruses, and dismemberment.", )) - return to_add - -/datum/species/golem/adamantine - name = "Adamantine Golem" - id = SPECIES_GOLEM_ADAMANTINE - meat = /obj/item/food/meat/slab/human/mutant/golem/adamantine - mutant_organs = list(/obj/item/organ/internal/adamantine_resonator, /obj/item/organ/internal/vocal_cords/adamantine) - fixed_mut_color = "#44eedd" - info_text = "As an Adamantine Golem, you possess special vocal cords allowing you to \"resonate\" messages to all golems. Your unique mineral makeup makes you immune to most types of magic." - prefix = "Adamantine" - special_names = null - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/adamantine/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - ADD_TRAIT(C, TRAIT_ANTIMAGIC, SPECIES_TRAIT) - -/datum/species/golem/adamantine/on_species_loss(mob/living/carbon/C) - REMOVE_TRAIT(C, TRAIT_ANTIMAGIC, SPECIES_TRAIT) - ..() - -//The suicide bombers of golemkind -/datum/species/golem/plasma - name = "Plasma Golem" - id = SPECIES_GOLEM_PLASMA - fixed_mut_color = "#aa33dd" - meat = /obj/item/stack/ore/plasma - //Can burn and takes damage from heat - //no RESISTHEAT, NOFIRE - inherent_traits = list( - TRAIT_GENELESS, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - ) - info_text = "As a Plasma Golem, you burn easily. Be careful, if you get hot enough while burning, you'll blow up!" - heatmod = 0 //fine until they blow up - prefix = "Plasma" - special_names = list("Flood","Fire","Bar","Man") - examine_limb_id = SPECIES_GOLEM - var/boom_warning = FALSE - var/datum/action/innate/ignite/ignite - -/datum/species/golem/plasma/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired) - if(H.bodytemperature > 750) - if(!boom_warning && H.on_fire) - to_chat(H, span_userdanger("You feel like you could blow up at any moment!")) - boom_warning = TRUE - else - if(boom_warning) - to_chat(H, span_notice("You feel more stable.")) - boom_warning = FALSE - - if(H.bodytemperature > 850 && H.on_fire && prob(25)) - explosion(H, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 4, flame_range = 5, explosion_cause = src) - if(H) - H.investigate_log("has been gibbed as [H.p_their()] body explodes.", INVESTIGATE_DEATHS) - H.gib() - if(H.fire_stacks < 2) //flammable - H.adjust_fire_stacks(0.5 * seconds_per_tick) - ..() - -/datum/species/golem/plasma/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - if(ishuman(C)) - ignite = new - ignite.Grant(C) - -/datum/species/golem/plasma/on_species_loss(mob/living/carbon/C) - if(ignite) - ignite.Remove(C) - ..() - -/datum/action/innate/ignite - name = "Ignite" - desc = "Set yourself aflame, bringing yourself closer to exploding!" - check_flags = AB_CHECK_CONSCIOUS - button_icon_state = "sacredflame" - -/datum/action/innate/ignite/Activate() - if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - if(H.fire_stacks) - to_chat(owner, span_notice("You ignite yourself!")) - else - to_chat(owner, span_warning("You try to ignite yourself, but fail!")) - H.ignite_mob() //firestacks are already there passively - -//Harder to hurt -/datum/species/golem/diamond - name = "Diamond Golem" - id = SPECIES_GOLEM_DIAMOND - fixed_mut_color = "#00ffff" - armor = 70 //up from 55 - meat = /obj/item/stack/ore/diamond - info_text = "As a Diamond Golem, you are more resistant than the average golem." - prefix = "Diamond" - special_names = list("Back","Grill") - examine_limb_id = SPECIES_GOLEM - -//Faster but softer and less armoured -/datum/species/golem/gold - name = "Gold Golem" - id = SPECIES_GOLEM_GOLD - fixed_mut_color = "#cccc00" - speedmod = 1 - armor = 25 //down from 55 - meat = /obj/item/stack/ore/gold - info_text = "As a Gold Golem, you are faster but less resistant than the average golem." - prefix = "Golden" - special_names = list("Boy") - examine_limb_id = SPECIES_GOLEM - -//Heavier, thus higher chance of stunning when punching -/datum/species/golem/silver - name = "Silver Golem" - id = SPECIES_GOLEM_SILVER - fixed_mut_color = "#dddddd" - meat = /obj/item/stack/ore/silver - info_text = "As a Silver Golem, your attacks have a higher chance of stunning. Being made of silver, your body is immune to spirits of the damned and runic golems." - prefix = "Silver" - special_names = list("Surfer", "Chariot", "Lining") - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/silver/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - ADD_TRAIT(C, TRAIT_HOLY, SPECIES_TRAIT) - -/datum/species/golem/silver/on_species_loss(mob/living/carbon/C) - REMOVE_TRAIT(C, TRAIT_HOLY, SPECIES_TRAIT) - ..() - -//Harder to stun, deals more damage, massively slowpokes, but gravproof and obstructive. Basically, The Wall. -/datum/species/golem/plasteel - name = "Plasteel Golem" - id = SPECIES_GOLEM_PLASTEEL - fixed_mut_color = "#bbbbbb" - stunmod = 0.4 - speedmod = 4 //pretty fucking slow - meat = /obj/item/stack/ore/iron - info_text = "As a Plasteel Golem, you are slower, but harder to stun, and hit very hard when punching. You also magnetically attach to surfaces and so don't float without gravity and cannot have positions swapped with other beings." - prefix = "Plasteel" - special_names = null - examine_limb_id = SPECIES_GOLEM - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/plasteel, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/plasteel, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/plasteel, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/plasteel, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem, - ) - -/datum/species/golem/plasteel/negates_gravity(mob/living/carbon/human/H) - return TRUE - -/datum/species/golem/plasteel/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - ADD_TRAIT(C, TRAIT_NOMOBSWAP, SPECIES_TRAIT) //THE WALL THE WALL THE WALL - -/datum/species/golem/plasteel/on_species_loss(mob/living/carbon/C) - REMOVE_TRAIT(C, TRAIT_NOMOBSWAP, SPECIES_TRAIT) //NOTHING ON ERF CAN MAKE IT FALL - ..() - -//Immune to ash storms -/datum/species/golem/titanium - name = "Titanium Golem" - id = SPECIES_GOLEM_TITANIUM - fixed_mut_color = "#ffffff" - meat = /obj/item/stack/ore/titanium - info_text = "As a Titanium Golem, you are immune to ash storms, and slightly more resistant to burn damage." - burnmod = 0.9 - prefix = "Titanium" - special_names = list("Dioxide") - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/titanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - ADD_TRAIT(C, TRAIT_ASHSTORM_IMMUNE, SPECIES_TRAIT) - -/datum/species/golem/titanium/on_species_loss(mob/living/carbon/C) - . = ..() - REMOVE_TRAIT(C, TRAIT_ASHSTORM_IMMUNE, SPECIES_TRAIT) - -//Immune to ash storms and lava -/datum/species/golem/plastitanium - name = "Plastitanium Golem" - id = SPECIES_GOLEM_PLASTITANIUM - fixed_mut_color = "#888888" - meat = /obj/item/stack/ore/titanium - info_text = "As a Plastitanium Golem, you are immune to both ash storms and lava, and slightly more resistant to burn damage." - burnmod = 0.8 - prefix = "Plastitanium" - special_names = null - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/plastitanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - C.add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), SPECIES_TRAIT) - -/datum/species/golem/plastitanium/on_species_loss(mob/living/carbon/C) - . = ..() - C.remove_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), SPECIES_TRAIT) - -//Fast and regenerates... but can only speak like an abductor -/datum/species/golem/alloy - name = "Alien Alloy Golem" - id = SPECIES_GOLEM_ALIEN - fixed_mut_color = "#333333" - meat = /obj/item/stack/sheet/mineral/abductor - mutanttongue = /obj/item/organ/internal/tongue/abductor - speedmod = 1 //faster - info_text = "As an Alloy Golem, you are made of advanced alien materials: you are faster and regenerate over time. You are, however, only able to be heard by other alloy golems." - prefix = "Alien" - special_names = list("Outsider", "Technology", "Watcher", "Stranger") //ominous and unknown - examine_limb_id = SPECIES_GOLEM - -//Regenerates because self-repairing super-advanced alien tech -/datum/species/golem/alloy/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired) - if(H.stat == DEAD) - return - H.heal_overall_damage(brute = 1 * seconds_per_tick, burn = 1 * seconds_per_tick, required_bodytype = BODYTYPE_ORGANIC) - H.adjustToxLoss(-1 * seconds_per_tick) - H.adjustOxyLoss(-1 * seconds_per_tick) - -//Since this will usually be created from a collaboration between podpeople and free golems, wood golems are a mix between the two races -/datum/species/golem/wood - name = "Wood Golem" - id = SPECIES_GOLEM_WOOD - fixed_mut_color = "#9E704B" - meat = /obj/item/stack/sheet/mineral/wood - //Can burn and take damage from heat - inherent_traits = list( - TRAIT_GENELESS, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTLOWPRESSURE, - TRAIT_RESISTHIGHPRESSURE, - ) - inherent_biotypes = MOB_ORGANIC | MOB_HUMANOID | MOB_PLANT - armor = 30 - burnmod = 1.25 - heatmod = 1.5 - info_text = "As a Wooden Golem, you have plant-like traits: you take damage from extreme temperatures, can be set on fire, and have lower armor than a normal golem. You regenerate when in the light and wither in the darkness." - prefix = "Wooden" - special_names = list("Bark", "Willow", "Catalpa", "Woody", "Oak", "Sap", "Twig", "Branch", "Maple", "Birch", "Elm", "Basswood", "Cottonwood", "Larch", "Aspen", "Ash", "Beech", "Buckeye", "Cedar", "Chestnut", "Cypress", "Fir", "Hawthorn", "Hazel", "Hickory", "Ironwood", "Juniper", "Leaf", "Mangrove", "Palm", "Pawpaw", "Pine", "Poplar", "Redwood", "Redbud", "Sassafras", "Spruce", "Sumac", "Trunk", "Walnut", "Yew") - human_surname_chance = 0 - special_name_chance = 100 - inherent_factions = list(FACTION_PLANTS, FACTION_VINES) - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/wood/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired) - if(H.stat == DEAD) - return - var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing - if(isturf(H.loc)) //else, there's considered to be no light - var/turf/T = H.loc - light_amount = min(1, T.get_lumcount()) - 0.5 - H.adjust_nutrition(5 * light_amount * seconds_per_tick) - if(H.nutrition > NUTRITION_LEVEL_ALMOST_FULL) - H.set_nutrition(NUTRITION_LEVEL_ALMOST_FULL) - if(light_amount > 0.2) //if there's enough light, heal - H.heal_overall_damage(brute = 0.5 * seconds_per_tick, burn = 0.5 * seconds_per_tick, required_bodytype = BODYTYPE_ORGANIC) - H.adjustToxLoss(-0.5 * seconds_per_tick) - H.adjustOxyLoss(-0.5 * seconds_per_tick) - - if(H.nutrition < NUTRITION_LEVEL_STARVING + 50) - H.take_overall_damage(brute = 2, required_bodytype = BODYTYPE_ORGANIC) - -/datum/species/golem/wood/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H, seconds_per_tick, times_fired) - if(chem.type == /datum/reagent/toxin/plantbgone) - H.adjustToxLoss(3 * REM * seconds_per_tick) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM * seconds_per_tick) - return TRUE - return ..() - -//Radioactive puncher, hits for burn but only as hard as human, slightly more durable against brute but less against everything else -/datum/species/golem/uranium - name = "Uranium Golem" - id = SPECIES_GOLEM_URANIUM - fixed_mut_color = "#77ff00" - meat = /obj/item/stack/ore/uranium - info_text = "As an Uranium Golem, your very touch burns and irradiates organic lifeforms. You don't hit as hard as most golems, but you are far more durable against blunt force trauma." - var/last_event = 0 - var/active = null - armor = 40 - brutemod = 0.5 - prefix = "Uranium" - special_names = list("Oxide", "Rod", "Meltdown", "235") - COOLDOWN_DECLARE(radiation_emission_cooldown) - examine_limb_id = SPECIES_GOLEM - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/uranium, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/uranium, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem, - ) - -/datum/species/golem/uranium/proc/radiation_emission(mob/living/carbon/human/H) - if(!COOLDOWN_FINISHED(src, radiation_emission_cooldown)) - return - else - radiation_pulse(H, max_range = 1, threshold = RAD_VERY_LIGHT_INSULATION, chance = 3) - COOLDOWN_START(src, radiation_emission_cooldown, 2 SECONDS) - -/datum/species/golem/uranium/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) - ..() - if(COOLDOWN_FINISHED(src, radiation_emission_cooldown) && M != H && M.combat_mode) - radiation_emission(H) - -/datum/species/golem/uranium/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, mob/living/carbon/human/H) - ..() - if(COOLDOWN_FINISHED(src, radiation_emission_cooldown) && user != H) - radiation_emission(H) - -/datum/species/golem/uranium/on_hit(obj/projectile/P, mob/living/carbon/human/H) - ..() - if(COOLDOWN_FINISHED(src, radiation_emission_cooldown)) - radiation_emission(H) - -//Immune to physical bullets and resistant to brute, but very vulnerable to burn damage. Dusts on death. -/datum/species/golem/sand - name = "Sand Golem" - id = SPECIES_GOLEM_SAND - fixed_mut_color = "#ffdc8f" - meat = /obj/item/stack/ore/glass //this is sand - armor = 0 - burnmod = 3 //melts easily - brutemod = 0.25 - info_text = "As a Sand Golem, you are immune to physical bullets and take very little brute damage, but are extremely vulnerable to burn damage and energy weapons. You will also turn to sand when dying, preventing any form of recovery." - prefix = "Sand" - special_names = list("Castle", "Bag", "Dune", "Worm", "Storm") - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/sand/spec_death(gibbed, mob/living/carbon/human/H) - H.visible_message(span_danger("[H] turns into a pile of sand!")) - for(var/obj/item/W in H) - H.dropItemToGround(W) - for(var/i in 1 to rand(3,5)) - new /obj/item/stack/ore/glass(get_turf(H)) - qdel(H) - -/datum/species/golem/sand/bullet_act(obj/projectile/P, mob/living/carbon/human/H) - if(!(P.original == H && P.firer == H)) - if(P.armor_flag == BULLET || P.armor_flag == BOMB) - playsound(H, 'sound/effects/shovel_dig.ogg', 70, TRUE) - H.visible_message(span_danger("The [P.name] sinks harmlessly in [H]'s sandy body!"), \ - span_userdanger("The [P.name] sinks harmlessly in [H]'s sandy body!")) - return BULLET_ACT_BLOCK - return ..() - -//Reflects lasers and resistant to burn damage, but very vulnerable to brute damage. Shatters on death. -/datum/species/golem/glass - name = "Glass Golem" - id = SPECIES_GOLEM_GLASS - fixed_mut_color = "#5a96b4aa" //transparent body - meat = /obj/item/shard - armor = 0 - brutemod = 3 //very fragile - burnmod = 0.25 - info_text = "As a Glass Golem, you reflect lasers and energy weapons, and are very resistant to burn damage. However, you are extremely vulnerable to brute damage. On death, you'll shatter beyond any hope of recovery." - prefix = "Glass" - special_names = list("Lens", "Prism", "Fiber", "Bead") - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/glass/spec_death(gibbed, mob/living/carbon/human/H) - playsound(H, SFX_SHATTER, 70, TRUE) - H.visible_message(span_danger("[H] shatters!")) - for(var/obj/item/W in H) - H.dropItemToGround(W) - for(var/i in 1 to rand(3,5)) - new /obj/item/shard(get_turf(H)) - qdel(H) - -/datum/species/golem/glass/bullet_act(obj/projectile/P, mob/living/carbon/human/H) - if(!(P.original == H && P.firer == H)) //self-shots don't reflect - if(P.armor_flag == LASER || P.armor_flag == ENERGY) - H.visible_message(span_danger("The [P.name] gets reflected by [H]'s glass skin!"), \ - span_userdanger("The [P.name] gets reflected by [H]'s glass skin!")) - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - // redirect the projectile - P.firer = H - P.preparePixelProjectile(locate(clamp(new_x, 1, world.maxx), clamp(new_y, 1, world.maxy), H.z), H) - return BULLET_ACT_FORCE_PIERCE - return ..() - -//Teleports when hit or when it wants to -/datum/species/golem/bluespace - name = "Bluespace Golem" - id = SPECIES_GOLEM_BLUESPACE - fixed_mut_color = "#3333ff" - meat = /obj/item/stack/ore/bluespace_crystal - info_text = "As a Bluespace Golem, you are spatially unstable: You will teleport when hit, and you can teleport manually at a long distance." - prefix = "Bluespace" - special_names = list("Crystal", "Polycrystal") - examine_limb_id = SPECIES_GOLEM - - var/datum/action/cooldown/unstable_teleport/unstable_teleport - var/teleport_cooldown = 100 - var/last_teleport = 0 - -/datum/species/golem/bluespace/proc/reactive_teleport(mob/living/carbon/human/H) - H.visible_message(span_warning("[H] teleports!"), span_danger("You destabilize and teleport!")) - new /obj/effect/particle_effect/sparks(get_turf(H)) - playsound(get_turf(H), SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - do_teleport(H, get_turf(H), 6, asoundin = 'sound/weapons/emitter2.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) - last_teleport = world.time - -/datum/species/golem/bluespace/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) - ..() - var/obj/item/I - if(isitem(AM)) - I = AM - if(I.thrownby == WEAKREF(H)) //No throwing stuff at yourself to trigger the teleport - return 0 - else - reactive_teleport(H) - -/datum/species/golem/bluespace/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) - ..() - if(world.time > last_teleport + teleport_cooldown && M != H && M.combat_mode) - reactive_teleport(H) - -/datum/species/golem/bluespace/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, mob/living/carbon/human/H) - ..() - if(world.time > last_teleport + teleport_cooldown && user != H) - reactive_teleport(H) - -/datum/species/golem/bluespace/on_hit(obj/projectile/P, mob/living/carbon/human/H) - ..() - if(world.time > last_teleport + teleport_cooldown) - reactive_teleport(H) - -/datum/species/golem/bluespace/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - if(ishuman(C)) - unstable_teleport = new - unstable_teleport.Grant(C) - last_teleport = world.time - -/datum/species/golem/bluespace/on_species_loss(mob/living/carbon/C) - if(unstable_teleport) - unstable_teleport.Remove(C) - ..() - -/datum/action/cooldown/unstable_teleport - name = "Unstable Teleport" - check_flags = AB_CHECK_CONSCIOUS - button_icon_state = "jaunt" - button_icon = 'icons/mob/actions/actions_spells.dmi' - cooldown_time = 17.5 SECONDS - -/datum/action/cooldown/unstable_teleport/Activate() - var/mob/living/carbon/human/H = owner - H.visible_message(span_warning("[H] starts vibrating!"), span_danger("You start charging your bluespace core...")) - playsound(get_turf(H), 'sound/weapons/flash.ogg', 25, TRUE) - addtimer(CALLBACK(src, PROC_REF(teleport), H), 1.5 SECONDS) - return TRUE - -/datum/action/cooldown/unstable_teleport/proc/teleport(mob/living/carbon/human/H) - StartCooldown() - H.visible_message(span_warning("[H] disappears in a shower of sparks!"), span_danger("You teleport!")) - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(10, 0, src) - spark_system.attach(H) - spark_system.start() - do_teleport(H, get_turf(H), 12, asoundin = 'sound/weapons/emitter2.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) - -//honk -/datum/species/golem/bananium - name = "Bananium Golem" - id = SPECIES_GOLEM_BANANIUM - fixed_mut_color = "#ffff00" - inherent_traits = list( - TRAIT_CLUMSY, - TRAIT_GENELESS, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_NOFIRE, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHEAT, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - ) - mutanttongue = /obj/item/organ/internal/tongue/bananium - meat = /obj/item/stack/ore/bananium - info_text = "As a Bananium Golem, you are made for pranking. Your body emits natural honks, and you can barely even hurt people when punching them. Your skin also bleeds banana peels when damaged." - prefix = "Bananium" - special_names = null - examine_limb_id = SPECIES_GOLEM - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/bananium, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/bananium, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/bananium, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/bananium, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem, - ) - - /// Cooldown for producing honks - COOLDOWN_DECLARE(honkooldown) - /// Cooldown for producing bananas - COOLDOWN_DECLARE(banana_cooldown) - /// Time between possible banana productions - var/banana_delay = 10 SECONDS - /// Same as the uranium golem. I'm pretty sure this is vestigial. - var/active = FALSE - -/datum/species/golem/bananium/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - COOLDOWN_START(src, honkooldown, 0) - COOLDOWN_START(src, banana_cooldown, banana_delay) - RegisterSignal(C, COMSIG_MOB_SAY, PROC_REF(handle_speech)) - var/obj/item/organ/internal/liver/liver = C.get_organ_slot(ORGAN_SLOT_LIVER) - if(liver) - ADD_TRAIT(liver, TRAIT_COMEDY_METABOLISM, SPECIES_TRAIT) - -/datum/species/golem/bananium/on_species_loss(mob/living/carbon/C) - . = ..() - UnregisterSignal(C, COMSIG_MOB_SAY) - - var/obj/item/organ/internal/liver/liver = C.get_organ_slot(ORGAN_SLOT_LIVER) - if(liver) - REMOVE_TRAIT(liver, TRAIT_COMEDY_METABOLISM, SPECIES_TRAIT) - -/datum/species/golem/bananium/random_name(gender,unique,lastname) - var/clown_name = pick(GLOB.clown_names) - var/golem_name = "[uppertext(clown_name)]" - return golem_name - -/datum/species/golem/bananium/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) - ..() - if(COOLDOWN_FINISHED(src, banana_cooldown) && M != H && M.combat_mode) - new /obj/item/grown/bananapeel/specialpeel(get_turf(H)) - COOLDOWN_START(src, banana_cooldown, banana_delay) - -/datum/species/golem/bananium/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, mob/living/carbon/human/H) - ..() - if((user != H) && COOLDOWN_FINISHED(src, banana_cooldown)) - new /obj/item/grown/bananapeel/specialpeel(get_turf(H)) - COOLDOWN_START(src, banana_cooldown, banana_delay) - -/datum/species/golem/bananium/on_hit(obj/projectile/P, mob/living/carbon/human/H) - ..() - if(COOLDOWN_FINISHED(src, banana_cooldown)) - new /obj/item/grown/bananapeel/specialpeel(get_turf(H)) - COOLDOWN_START(src, banana_cooldown, banana_delay) - -/datum/species/golem/bananium/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) - ..() - var/obj/item/I - if(isitem(AM)) - I = AM - if(I.thrownby == WEAKREF(H)) //No throwing stuff at yourself to make bananas - return 0 - else - new/obj/item/grown/bananapeel/specialpeel(get_turf(H)) - COOLDOWN_START(src, banana_cooldown, banana_delay) - -/datum/species/golem/bananium/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired) - if(!active && COOLDOWN_FINISHED(src, honkooldown)) - active = TRUE - playsound(get_turf(H), 'sound/items/bikehorn.ogg', 50, TRUE) - COOLDOWN_START(src, honkooldown, rand(2 SECONDS, 8 SECONDS)) - active = FALSE - ..() - -/datum/species/golem/bananium/spec_death(gibbed, mob/living/carbon/human/H) - playsound(get_turf(H), 'sound/misc/sadtrombone.ogg', 70, FALSE) - -/datum/species/golem/bananium/proc/handle_speech(datum/source, list/speech_args) - SIGNAL_HANDLER - speech_args[SPEECH_SPANS] |= SPAN_CLOWN - -/datum/species/golem/runic - name = "Runic Golem" - id = SPECIES_GOLEM_CULT - sexes = FALSE - info_text = "As a Runic Golem, you possess eldritch powers granted by the Elder Goddess Nar'Sie." - species_traits = list(NO_UNDERWEAR,NOEYESPRITES) //no mutcolors - inherent_traits = list( - TRAIT_GENELESS, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_NOFIRE, - TRAIT_NOFLASH, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTHEAT, - TRAIT_RESISTCOLD, - TRAIT_RESISTLOWPRESSURE, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_NOBLOOD, - ) - inherent_biotypes = MOB_HUMANOID|MOB_MINERAL - prefix = "Runic" - special_names = null - inherent_factions = list(FACTION_CULT) - species_language_holder = /datum/language_holder/golem/runic - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/cult, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/cult, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem/cult, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/cult, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/cult, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem/cult, - ) - - /// A ref to our jaunt spell that we get on species gain. - var/datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/golem/jaunt - /// A ref to our gaze spell that we get on species gain. - var/datum/action/cooldown/spell/pointed/abyssal_gaze/abyssal_gaze - /// A ref to our dominate spell that we get on species gain. - var/datum/action/cooldown/spell/pointed/dominate/dominate - -/datum/species/golem/runic/random_name(gender,unique,lastname) - var/edgy_first_name = pick("Razor","Blood","Dark","Evil","Cold","Pale","Black","Silent","Chaos","Deadly","Coldsteel") - var/edgy_last_name = pick("Edge","Night","Death","Razor","Blade","Steel","Calamity","Twilight","Shadow","Nightmare") //dammit Razor Razor - var/golem_name = "[edgy_first_name] [edgy_last_name]" - return golem_name - -/datum/species/golem/runic/on_species_gain(mob/living/carbon/grant_to, datum/species/old_species) - . = ..() - // Create our species specific spells here. - // Note we link them to the mob, not the mind, - // so they're not moved around on mindswaps - jaunt = new(grant_to) - jaunt.StartCooldown() - jaunt.Grant(grant_to) - - abyssal_gaze = new(grant_to) - abyssal_gaze.StartCooldown() - abyssal_gaze.Grant(grant_to) - - dominate = new(grant_to) - dominate.StartCooldown() - dominate.Grant(grant_to) - -/datum/species/golem/runic/on_species_loss(mob/living/carbon/C) - // Aaand cleanup our species specific spells. - // No free rides. - QDEL_NULL(jaunt) - QDEL_NULL(abyssal_gaze) - QDEL_NULL(dominate) - return ..() - -/datum/species/golem/runic/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H, seconds_per_tick, times_fired) - . = ..() - if(istype(chem, /datum/reagent/water/holywater)) - H.adjustFireLoss(4 * REM * seconds_per_tick) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM * seconds_per_tick) - - if(chem.type == /datum/reagent/fuel/unholywater) - H.adjustBruteLoss(-4 * REM * seconds_per_tick) - H.adjustFireLoss(-4 * REM * seconds_per_tick) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM * seconds_per_tick) - -/datum/species/golem/cloth - name = "Cloth Golem" - id = SPECIES_GOLEM_CLOTH - sexes = FALSE - info_text = "As a Cloth Golem, you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable. \ - Being made of cloth, your body is immune to spirits of the damned and runic golems. You are faster than that of other golems, but weaker and less resilient." - species_traits = list(NO_UNDERWEAR) //no mutcolors, and can burn - inherent_traits = list( - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_GENELESS, - TRAIT_LITERATE, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_NOBLOOD, - ) - inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID - armor = 15 //feels no pain, but not too resistant - burnmod = 2 // don't get burned - speedmod = 1 // not as heavy as stone - prefix = "Cloth" - special_names = null - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/cloth, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/cloth, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem/cloth, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/cloth, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/cloth, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem/cloth, - ) - -/datum/species/golem/cloth/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - ADD_TRAIT(C, TRAIT_HOLY, SPECIES_TRAIT) - -/datum/species/golem/cloth/on_species_loss(mob/living/carbon/C) - REMOVE_TRAIT(C, TRAIT_HOLY, SPECIES_TRAIT) - ..() - -/datum/species/golem/cloth/check_roundstart_eligible() - if(check_holidays(HALLOWEEN)) - return TRUE - return ..() - -/datum/species/golem/cloth/random_name(gender,unique,lastname) - var/pharaoh_name = pick("Neferkare", "Hudjefa", "Khufu", "Mentuhotep", "Ahmose", "Amenhotep", "Thutmose", "Hatshepsut", "Tutankhamun", "Ramses", "Seti", \ - "Merenptah", "Djer", "Semerkhet", "Nynetjer", "Khafre", "Pepi", "Intef", "Ay") //yes, Ay was an actual pharaoh - var/golem_name = "[pharaoh_name] \Roman[rand(1,99)]" - return golem_name - -/datum/species/golem/cloth/spec_life(mob/living/carbon/human/H) - if(H.fire_stacks < 1) - H.adjust_fire_stacks(1) //always prone to burning - ..() - -/datum/species/golem/cloth/spec_death(gibbed, mob/living/carbon/human/H) - if(gibbed) - return - if(H.on_fire) - H.visible_message(span_danger("[H] burns into ash!")) - H.dust(just_ash = TRUE) - return - - H.visible_message(span_danger("[H] falls apart into a pile of bandages!")) - new /obj/structure/cloth_pile(get_turf(H), H) - ..() - -/datum/species/golem/cloth/get_species_description() - return "A wrapped up Mummy! They descend upon Space Station Thirteen every year to spook the crew! \"Return the slab!\"" - -/datum/species/golem/cloth/get_species_lore() - return list( - "Mummies are very self conscious. They're shaped weird, they walk slow, and worst of all, \ - they're considered the laziest halloween costume. But that's not even true, they say.", - - "Making a mummy costume may be easy, but making a CONVINCING mummy costume requires \ - things like proper fabric and purposeful staining to achieve the look. Which is FAR from easy. Gosh.", - ) - -// Calls parent, as Golems have a species-wide perk we care about. -/datum/species/golem/cloth/create_pref_unique_perks() - var/list/to_add = ..() - to_add += list(list( SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK, - SPECIES_PERK_ICON = "recycle", - SPECIES_PERK_NAME = "Reformation", - SPECIES_PERK_DESC = "A boon quite similar to Ethereals, Mummies collapse into \ - a pile of bandages after they die. If left alone, they will reform back \ - into themselves. The bandages themselves are very vulnerable to fire.", + SPECIES_PERK_ICON = "gem", + SPECIES_PERK_NAME = "Metamorphic Rock", + SPECIES_PERK_DESC = "Consuming minerals can grant Lithoids temporary benefits based on the type consumed.", )) - return to_add - -// Override to add a perk elaborating on just how dangerous fire is. -/datum/species/golem/cloth/create_pref_temperature_perks() - var/list/to_add = list() + to_add += list(list( + SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK, + SPECIES_PERK_ICON = "pickaxe", + SPECIES_PERK_NAME = "Natural Miners", + SPECIES_PERK_DESC = "Golems can see dimly in the dark, sense minerals, and mine stone with their bare hands. \ + They can even smelt ores in an internal furnace, if their surrounding environment is hot enough.", + )) to_add += list(list( SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK, - SPECIES_PERK_ICON = "fire-alt", - SPECIES_PERK_NAME = "Incredibly Flammable", - SPECIES_PERK_DESC = "Mummies are made entirely of cloth, which makes them \ - very vulnerable to fire. They will not reform if they die while on \ - fire, and they will easily catch alight. If your bandages burn to ash, you're toast!", + SPECIES_PERK_ICON = "bolt", + SPECIES_PERK_NAME = "Anima", + SPECIES_PERK_DESC = "Maintaining the force animating stone is taxing. Lithoids must eat frequently \ + in order to avoid returning to inanimate statues, and only derive nutrition from eating minerals.", )) return to_add -/obj/structure/cloth_pile - name = "pile of bandages" - desc = "It emits a strange aura, as if there was still life within it..." - max_integrity = 50 - armor_type = /datum/armor/structure_cloth_pile - icon = 'icons/obj/objects.dmi' - icon_state = "pile_bandages" - resistance_flags = FLAMMABLE - - var/revive_time = 90 SECONDS - var/mob/living/carbon/human/cloth_golem - -/datum/armor/structure_cloth_pile - melee = 90 - bullet = 90 - laser = 25 - energy = 80 - bomb = 50 - fire = -50 - acid = -50 - -/obj/structure/cloth_pile/Initialize(mapload, mob/living/carbon/human/H) - . = ..() - if(!QDELETED(H) && is_species(H, /datum/species/golem/cloth)) - H.unequip_everything() - H.forceMove(src) - cloth_golem = H - to_chat(cloth_golem, span_notice("You start gathering your life energy, preparing to rise again...")) - addtimer(CALLBACK(src, PROC_REF(revive)), revive_time) - else - return INITIALIZE_HINT_QDEL - -/obj/structure/cloth_pile/Destroy() - if(cloth_golem) - QDEL_NULL(cloth_golem) - return ..() - -/obj/structure/cloth_pile/burn() - visible_message(span_danger("[src] burns into ash!")) - new /obj/effect/decal/cleanable/ash(get_turf(src)) - ..() - -/obj/structure/cloth_pile/proc/revive() - if(QDELETED(src) || QDELETED(cloth_golem)) //QDELETED also checks for null, so if no cloth golem is set this won't runtime - return - if(HAS_TRAIT_FROM_ONLY(cloth_golem, TRAIT_SUICIDED, REF(cloth_golem))) - QDEL_NULL(cloth_golem) - return - - invisibility = INVISIBILITY_MAXIMUM //disappear before the animation - new /obj/effect/temp_visual/mummy_animation(get_turf(src)) - cloth_golem.revive(ADMIN_HEAL_ALL) - sleep(2 SECONDS) - cloth_golem.forceMove(get_turf(src)) - cloth_golem.visible_message(span_danger("[src] rises and reforms into [cloth_golem]!"),span_userdanger("You reform into yourself!")) - cloth_golem = null - qdel(src) - -/obj/structure/cloth_pile/attackby(obj/item/P, mob/living/carbon/human/user, params) - . = ..() - - if(resistance_flags & ON_FIRE) - return - - if(P.get_temperature()) - visible_message(span_danger("[src] bursts into flames!")) - fire_act() - -/datum/species/golem/plastic - name = "Plastic Golem" - id = SPECIES_GOLEM_PLASTIC - inherent_traits = list( - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_GENELESS, - TRAIT_LITERATE, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_NOFIRE, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHEAT, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_VENTCRAWLER_NUDE, - ) - prefix = "Plastic" - special_names = list("Sheet", "Bag", "Bottle") - fixed_mut_color = "fffa" - info_text = "As a Plastic Golem, you are capable of ventcrawling and passing through plastic flaps as long as you are naked." - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/bronze - name = "Bronze Golem" - id = SPECIES_GOLEM_BRONZE - prefix = "Bronze" - special_names = list("Bell") - fixed_mut_color = "#cd7f32" - info_text = "As a Bronze Golem, you are very resistant to loud noises, and make loud noises if something hard hits you, however this ability does hurt your hearing." - special_step_sounds = list('sound/machines/clockcult/integration_cog_install.ogg', 'sound/magic/clockwork/fellowship_armory.ogg' ) - mutantears = /obj/item/organ/internal/ears/bronze - examine_limb_id = SPECIES_GOLEM - var/last_gong_time = 0 - var/gong_cooldown = 150 - -/datum/species/golem/bronze/bullet_act(obj/projectile/P, mob/living/carbon/human/H) - if(!(world.time > last_gong_time + gong_cooldown)) - return ..() - if(P.armor_flag == BULLET || P.armor_flag == BOMB) - gong(H) - return ..() - -/datum/species/golem/bronze/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) - ..() - if(world.time > last_gong_time + gong_cooldown) - gong(H) - -/datum/species/golem/bronze/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) - ..() - if(world.time > last_gong_time + gong_cooldown && M.combat_mode) - gong(H) - -/datum/species/golem/bronze/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, mob/living/carbon/human/H) - ..() - if(world.time > last_gong_time + gong_cooldown) - gong(H) - -/datum/species/golem/bronze/on_hit(obj/projectile/P, mob/living/carbon/human/H) - ..() - if(world.time > last_gong_time + gong_cooldown) - gong(H) - -/datum/species/golem/bronze/proc/gong(mob/living/carbon/human/H) - last_gong_time = world.time - for(var/mob/living/M in get_hearers_in_view(7,H)) - if(M.stat == DEAD) //F - continue - if(M == H) - H.show_message(span_narsiesmall("You cringe with pain as your body rings around you!"), MSG_AUDIBLE) - H.playsound_local(H, 'sound/effects/gong.ogg', 100, TRUE) - H.soundbang_act(2, 0, 100, 1) - H.adjust_jitter(14 SECONDS) - var/distance = max(0,get_dist(get_turf(H),get_turf(M))) - switch(distance) - if(0 to 1) - M.show_message(span_narsiesmall("GONG!"), MSG_AUDIBLE) - M.playsound_local(H, 'sound/effects/gong.ogg', 100, TRUE) - M.soundbang_act(1, 0, 30, 3) - M.adjust_confusion(10 SECONDS) - M.adjust_jitter(8 SECONDS) - M.add_mood_event("gonged", /datum/mood_event/loud_gong) - if(2 to 3) - M.show_message(span_cult("GONG!"), MSG_AUDIBLE) - M.playsound_local(H, 'sound/effects/gong.ogg', 75, TRUE) - M.soundbang_act(1, 0, 15, 2) - M.adjust_jitter(6 SECONDS) - M.add_mood_event("gonged", /datum/mood_event/loud_gong) - else - M.show_message(span_warning("GONG!"), MSG_AUDIBLE) - M.playsound_local(H, 'sound/effects/gong.ogg', 50, TRUE) - - -/datum/species/golem/cardboard //Faster but weaker, can also make new shells on its own - name = "Cardboard Golem" - id = SPECIES_GOLEM_CARDBOARD - prefix = "Cardboard" - special_names = list("Box") - info_text = "As a Cardboard Golem, you aren't very strong, but you are a bit quicker and can easily create more brethren by using cardboard on yourself. Cardboard makes a poor building material for tongues, so you'll have difficulty speaking." - species_traits = list(NO_UNDERWEAR,NOEYESPRITES) - inherent_traits = list( - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_GENELESS, - TRAIT_LITERATE, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_NOFLASH, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_NOBLOOD, - ) - mutanttongue = null - fixed_mut_color = null - armor = 25 - burnmod = 1.25 - heatmod = 2 - speedmod = 1.5 - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/cardboard, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/cardboard, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem/cardboard, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/cardboard, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/cardboard, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem/cardboard, - ) - var/last_creation = 0 - var/brother_creation_cooldown = 300 - -/datum/species/golem/cardboard/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, mob/living/carbon/human/H) - . = ..() - if(user != H) - return FALSE //forced reproduction is rape. - if(istype(I, /obj/item/stack/sheet/cardboard)) - var/obj/item/stack/sheet/cardboard/C = I - if(last_creation + brother_creation_cooldown > world.time) //no cheesing dork - return - if(C.amount < 10) - to_chat(H, span_warning("You do not have enough cardboard!")) - return FALSE - to_chat(H, span_notice("You attempt to create a new cardboard brother.")) - if(do_after(user, 30, target = user)) - if(last_creation + brother_creation_cooldown > world.time) //no cheesing dork - return - if(!C.use(10)) - to_chat(H, span_warning("You do not have enough cardboard!")) - return FALSE - to_chat(H, span_notice("You create a new cardboard golem shell.")) - create_brother(H, H.loc) - -/datum/species/golem/cardboard/proc/create_brother(mob/living/carbon/human/golem, atom/location) - var/mob/living/master = golem.mind.enslaved_to?.resolve() - if(master) - new /obj/effect/mob_spawn/ghost_role/human/golem/servant(location, /datum/species/golem/cardboard, master) - else - new /obj/effect/mob_spawn/ghost_role/human/golem(location, /datum/species/golem/cardboard) - - last_creation = world.time - -/datum/species/golem/leather - name = "Leather Golem" - id = SPECIES_GOLEM_LEATHER - special_names = list("Face", "Man", "Belt") //Ah dude 4 strength 4 stam leather belt AHHH - inherent_traits = list( - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_GENELESS, - TRAIT_LITERATE, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_STRONG_GRABBER, - ) - prefix = "Leather" - fixed_mut_color = "#624a2e" - info_text = "As a Leather Golem, you are flammable, but you can grab things with incredible ease, allowing all your grabs to start at a strong level." - grab_sound = 'sound/weapons/whipgrab.ogg' - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/durathread - name = "Durathread Golem" - id = SPECIES_GOLEM_DURATHREAD - prefix = "Durathread" - special_names = list("Boll","Weave") - species_traits = list(NO_UNDERWEAR,NOEYESPRITES) - fixed_mut_color = null - inherent_traits = list( - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_GENELESS, - TRAIT_LITERATE, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_NOFLASH, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_NOBLOOD, - ) - info_text = "As a Durathread Golem, your strikes will cause those your targets to start choking, but your woven body won't withstand fire as well." - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/durathread, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/durathread, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem/durathread, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/durathread, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/durathread, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem/durathread, - ) - -/datum/species/golem/durathread/spec_unarmedattacked(mob/living/carbon/human/user, mob/living/carbon/human/target) - . = ..() - target.apply_status_effect(/datum/status_effect/strandling) - -/datum/species/golem/bone - name = "Bone Golem" - id = SPECIES_GOLEM_BONE - prefix = "Bone" - special_names = list("Head", "Broth", "Fracture", "Rattler", "Appetit") - liked_food = GROSS | MEAT | RAW | GORE - toxic_food = null - inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID - mutanttongue = /obj/item/organ/internal/tongue/bone - mutantstomach = /obj/item/organ/internal/stomach/bone - sexes = FALSE - fixed_mut_color = null - species_traits = list( - NO_UNDERWEAR, - NOEYESPRITES, - ) - inherent_traits = list( - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_FAKEDEATH, - TRAIT_GENELESS, - TRAIT_LITERATE, - TRAIT_NOBREATH, - TRAIT_NOFIRE, - TRAIT_NODISMEMBER, - TRAIT_NOFLASH, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHEAT, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_NOBLOOD, - ) - species_language_holder = /datum/language_holder/golem/bone - info_text = "As a Bone Golem, You have a powerful spell that lets you chill your enemies with fear, and milk heals you! Just make sure to watch our for bone-hurting juice." - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/bone, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/bone, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem/bone, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/bone, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/bone, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem/bone, - ) - var/datum/action/innate/bonechill/bonechill - -/datum/species/golem/bone/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - if(ishuman(C)) - bonechill = new - bonechill.Grant(C) - -/datum/species/golem/bone/on_species_loss(mob/living/carbon/C) - if(bonechill) - bonechill.Remove(C) - ..() - -/datum/species/golem/bone/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H, seconds_per_tick, times_fired) - . = ..() - if(chem.type == /datum/reagent/toxin/bonehurtingjuice) - H.adjustStaminaLoss(7.5 * REM * seconds_per_tick, 0) - H.adjustBruteLoss(0.5 * REM * seconds_per_tick, 0) - if(SPT_PROB(10, seconds_per_tick)) - switch(rand(1, 3)) - if(1) - H.say(pick("oof.", "ouch.", "my bones.", "oof ouch.", "oof ouch my bones."), forced = /datum/reagent/toxin/bonehurtingjuice) - if(2) - H.manual_emote(pick("oofs silently.", "looks like [H.p_their()] bones hurt.", "grimaces, as though [H.p_their()] bones hurt.")) - if(3) - to_chat(H, span_warning("Your bones hurt!")) - if(chem.overdosed) - if(SPT_PROB(2, seconds_per_tick) && iscarbon(H)) //big oof - var/selected_part = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) //God help you if the same limb gets picked twice quickly. - var/obj/item/bodypart/bp = H.get_bodypart(selected_part) //We're so sorry skeletons, you're so misunderstood - if(bp) - playsound(H, get_sfx(SFX_DESECRATION), 50, TRUE, -1) //You just want to socialize - H.visible_message(span_warning("[H] rattles loudly and flails around!!"), span_danger("Your bones hurt so much that your missing muscles spasm!!")) - H.say("OOF!!", forced=/datum/reagent/toxin/bonehurtingjuice) - bp.receive_damage(200, 0, 0) //But I don't think we should - else - to_chat(H, span_warning("Your missing arm aches from wherever you left it.")) - H.emote("sigh") - H.reagents.remove_reagent(chem.type, chem.metabolization_rate * seconds_per_tick) - return TRUE - -/datum/action/innate/bonechill - name = "Bone Chill" - desc = "Rattle your bones and strike fear into your enemies!" - check_flags = AB_CHECK_CONSCIOUS - button_icon = 'icons/mob/actions/actions_spells.dmi' - button_icon_state = "bonechill" - var/cooldown = 600 - var/last_use - var/snas_chance = 3 - -/datum/action/innate/bonechill/Activate() - if(world.time < last_use + cooldown) - to_chat(owner, span_warning("You aren't ready yet to rattle your bones again!")) - return - owner.visible_message(span_warning("[owner] rattles [owner.p_their()] bones harrowingly."), span_notice("You rattle your bones")) - last_use = world.time - if(prob(snas_chance)) - playsound(get_turf(owner),'sound/magic/RATTLEMEBONES2.ogg', 100) - if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - var/mutable_appearance/badtime = mutable_appearance('icons/mob/species/human/bodyparts.dmi', "b_golem_eyes", -FIRE_LAYER-0.5) - badtime.appearance_flags = RESET_COLOR - H.overlays_standing[FIRE_LAYER+0.5] = badtime - H.apply_overlay(FIRE_LAYER+0.5) - addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living/carbon/, remove_overlay), FIRE_LAYER+0.5), 25) - else - playsound(get_turf(owner),'sound/magic/RATTLEMEBONES.ogg', 100) - for(var/mob/living/L in orange(7, get_turf(owner))) - if((L.mob_biotypes & MOB_UNDEAD) || isgolem(L) || HAS_TRAIT(L, TRAIT_RESISTCOLD)) - continue //Do not affect our brothers - - to_chat(L, span_cultlarge("A spine-chilling sound chills you to the bone!")) - L.apply_status_effect(/datum/status_effect/bonechill) - L.add_mood_event("spooked", /datum/mood_event/spooked) - -/datum/species/golem/snow - name = "Snow Golem" - id = SPECIES_GOLEM_SNOW - fixed_mut_color = null //custom sprites - armor = 45 //down from 55 - burnmod = 3 //melts easily - info_text = "As a Snow Golem, you are extremely vulnerable to burn damage, but you can generate snowballs and shoot cryokinetic beams. You will also turn to snow when dying, preventing any form of recovery." - prefix = "Snow" - special_names = list("Flake", "Blizzard", "Storm") - species_traits = list(NO_UNDERWEAR,NOEYESPRITES) //no mutcolors, no eye sprites - inherent_traits = list( - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_GENELESS, - TRAIT_LITERATE, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_NOBLOOD, - ) - bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/golem/snow, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/golem/snow, - BODY_ZONE_HEAD = /obj/item/bodypart/head/golem/snow, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/golem/snow, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/golem/snow, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem/snow, - ) - - /// A ref to our "throw snowball" spell we get on species gain. - var/datum/action/cooldown/spell/conjure_item/snowball/snowball - /// A ref to our cryobeam spell we get on species gain. - var/datum/action/cooldown/spell/pointed/projectile/cryo/cryo - -/datum/species/golem/snow/spec_death(gibbed, mob/living/carbon/human/H) - H.visible_message(span_danger("[H] turns into a pile of snow!")) - for(var/obj/item/W in H) - H.dropItemToGround(W) - for(var/i in 1 to rand(3,5)) - new /obj/item/stack/sheet/mineral/snow(get_turf(H)) - new /obj/item/food/grown/carrot(get_turf(H)) - qdel(H) - -/datum/species/golem/snow/on_species_gain(mob/living/carbon/grant_to, datum/species/old_species) - . = ..() - ADD_TRAIT(grant_to, TRAIT_SNOWSTORM_IMMUNE, SPECIES_TRAIT) - - snowball = new(grant_to) - snowball.StartCooldown() - snowball.Grant(grant_to) - - cryo = new(grant_to) - cryo.StartCooldown() - cryo.Grant(grant_to) - -/datum/species/golem/snow/on_species_loss(mob/living/carbon/remove_from) - REMOVE_TRAIT(remove_from, TRAIT_SNOWSTORM_IMMUNE, SPECIES_TRAIT) - QDEL_NULL(snowball) - QDEL_NULL(cryo) - return ..() - -/datum/species/golem/mhydrogen //Effectively most other metal-based golem types rolled into one - immune to all weather, lava, flashes, and magic, while being just as hardened as diamond golems. - name = "Metallic Hydrogen Golem" - id = SPECIES_GOLEM_HYDROGEN - fixed_mut_color = "#535469" - armor = 70 //equal to a diamond golem - info_text = "As a Metallic Hydrogen Golem, you were forged in the highest pressures and the highest heats. Your unique material makeup makes you immune to magic and most environmental damage, while helping you resist most other attacks." - prefix = "Metallic Hydrogen" - special_names = list("Pressure","Crush") - inherent_traits = list( - TRAIT_GENELESS, - TRAIT_NOBREATH, - TRAIT_NODISMEMBER, - TRAIT_NOFIRE, - TRAIT_NOFLASH, - TRAIT_PIERCEIMMUNE, - TRAIT_RADIMMUNE, - TRAIT_RESISTCOLD, - TRAIT_RESISTHEAT, - TRAIT_RESISTHIGHPRESSURE, - TRAIT_RESISTLOWPRESSURE, - TRAIT_NOBLOOD, - TRAIT_ADVANCEDTOOLUSER, - TRAIT_CAN_STRIP, - TRAIT_LITERATE, - TRAIT_WEATHER_IMMUNE, - TRAIT_LAVA_IMMUNE, - ) - examine_limb_id = SPECIES_GOLEM - -/datum/species/golem/mhydrogen/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - ADD_TRAIT(C, TRAIT_ANTIMAGIC, SPECIES_TRAIT) - -/datum/species/golem/mhydrogen/on_species_loss(mob/living/carbon/C) - REMOVE_TRAIT(C, TRAIT_ANTIMAGIC, SPECIES_TRAIT) +/// Remove nutrient value from non-mineral food, wish this was on an organ and not species but such is life +/datum/species/golem/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H, delta_time, times_fired) + if (istype(chem, /datum/reagent/consumable) && !istype(chem, /datum/reagent/consumable/nutriment/mineral)) + var/datum/reagent/consumable/yummy_chem = chem + yummy_chem.nutriment_factor = 0 return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index 1dbe2d34189..aa14852dd98 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -39,7 +39,7 @@ digitigrade_customization = DIGITIGRADE_OPTIONAL // Lizards are coldblooded and can stand a greater temperature range than humans - bodytemp_heat_damage_limit = (BODYTEMP_HEAT_DAMAGE_LIMIT + 20) // This puts lizards 10 above lavaland max heat for ash lizards. + bodytemp_heat_damage_limit = BODYTEMP_HEAT_LAVALAND_SAFE bodytemp_cold_damage_limit = (BODYTEMP_COLD_DAMAGE_LIMIT - 10) ass_image = 'icons/ass/asslizard.png' diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index f4889c352a7..34f0310c39b 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -14,6 +14,7 @@ TRAIT_RADIMMUNE, TRAIT_RESISTCOLD, TRAIT_NOBLOOD, + TRAIT_NO_DEBRAIN_OVERLAY, ) inherent_biotypes = MOB_HUMANOID|MOB_MINERAL diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index d96be568b44..73a41863145 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -28,6 +28,7 @@ TRAIT_TOXIMMUNE, TRAIT_XENO_IMMUNE, TRAIT_NOBLOOD, + TRAIT_NO_DEBRAIN_OVERLAY, ) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID mutanttongue = /obj/item/organ/internal/tongue/bone diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 03b69159c06..479a468dc6c 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -537,3 +537,10 @@ for(var/reagent in reagents) var/datum/reagent/R = reagent . |= R.expose_mob(src, methods, reagents[R], show_message, touch_protection) + +/// Simplified ricochet angle calculation for mobs (also the base version doesn't work on mobs) +/mob/living/handle_ricochet(obj/projectile/ricocheting_projectile) + var/face_angle = get_angle_raw(ricocheting_projectile.x, ricocheting_projectile.pixel_x, ricocheting_projectile.pixel_y, ricocheting_projectile.p_y, x, y, pixel_x, pixel_y) + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + GET_ANGLE_OF_INCIDENCE(face_angle, (ricocheting_projectile.Angle + 180))) + ricocheting_projectile.set_angle(new_angle_s) + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 4830aa995bc..bbea6b6fe00 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -151,13 +151,12 @@ icon_state = initial(icon_state) /mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/victim) - if(ishuman(victim)) - var/mob/living/carbon/human/human_victim = victim - if(human_victim.mind) - if(istype(human_victim.mind.martial_art, /datum/martial_art/the_sleeping_carp)) - . = TRUE - if (is_species(human_victim, /datum/species/golem/sand)) - . = TRUE + if(!ishuman(victim)) + return FALSE + if(isgolem(victim) && victim.has_status_effect(/datum/status_effect/golem/gold)) + return TRUE + var/mob/living/carbon/human/human_victim = victim + return human_victim.mind && istype(human_victim.mind.martial_art, /datum/martial_art/the_sleeping_carp) /mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/victim) visible_message(span_colossus("[src] disintegrates [victim]!")) diff --git a/code/modules/mob_spawn/corpses/mining_corpses.dm b/code/modules/mob_spawn/corpses/mining_corpses.dm index c6e7525a92c..f43351f8261 100644 --- a/code/modules/mob_spawn/corpses/mining_corpses.dm +++ b/code/modules/mob_spawn/corpses/mining_corpses.dm @@ -201,16 +201,7 @@ /datum/outfit/consumed_golem/pre_equip(mob/living/carbon/human/golem, visualsOnly = FALSE) if(!visualsOnly) - golem.set_species(pick( - /datum/species/golem/adamantine, - /datum/species/golem/diamond, - /datum/species/golem/gold, - /datum/species/golem/plasma, - /datum/species/golem/plasteel, - /datum/species/golem/plastitanium, - /datum/species/golem/silver, - /datum/species/golem/titanium, - )) + golem.set_species(/datum/species/golem) if(prob(30)) glasses = pick_weight(list( /obj/item/clothing/glasses/hud/diagnostic = 2, diff --git a/code/modules/mob_spawn/ghost_roles/golem_roles.dm b/code/modules/mob_spawn/ghost_roles/golem_roles.dm index 483646f98ad..9feff309ef4 100644 --- a/code/modules/mob_spawn/ghost_roles/golem_roles.dm +++ b/code/modules/mob_spawn/ghost_roles/golem_roles.dm @@ -6,7 +6,7 @@ name = "inert free golem shell" desc = "A humanoid shape, empty, lifeless, and full of potential." icon = 'icons/obj/wizard.dmi' - icon_state = "construct" + icon_state = "shell_complete" mob_species = /datum/species/golem anchored = FALSE move_resist = MOVE_FORCE_NORMAL @@ -15,22 +15,20 @@ you_are_text = "You are a Free Golem. Your family worships The Liberator." flavour_text = "In his infinite and divine wisdom, he set your clan free to travel the stars with a single declaration: \"Yeah go do whatever.\"" spawner_job_path = /datum/job/free_golem - /// If TRUE, other golems can touch us to swap into this shell. - var/can_transfer = TRUE /// Weakref to the creator of this golem shell. var/datum/weakref/owner_ref + /// Typepath to a material to feed to the golem as soon as it is built + var/initial_type -/obj/effect/mob_spawn/ghost_role/human/golem/Initialize(mapload, datum/species/golem/species, mob/creator) +/obj/effect/mob_spawn/ghost_role/human/golem/Initialize(mapload, mob/creator, made_of) if(creator) name = "inert servant golem shell" prompt_name = "servant golem" - if(species) //spawners list uses object name to register so this goes before ..() - name += " ([initial(species.prefix)])" - mob_species = species + initial_type = made_of . = ..() var/area/init_area = get_area(src) if(!mapload && init_area) - notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in \the [init_area.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM) + notify_ghosts("\A golem shell has been completed in \the [init_area.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM) if(creator) you_are_text = "You are a golem." flavour_text = "You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools." @@ -40,31 +38,35 @@ /obj/effect/mob_spawn/ghost_role/human/golem/name_mob(mob/living/spawned_mob, forced_name) - if(!forced_name) - var/datum/species/golem/golem_species = mob_species - if(owner_ref?.resolve()) - forced_name = "[initial(golem_species.prefix)] Golem ([rand(1,999)])" - else - golem_species = new mob_species - forced_name = golem_species.random_name() + if(forced_name || !iscarbon(spawned_mob)) + return ..() + + if(owner_ref?.resolve()) + forced_name = "Golem ([rand(1,999)])" + return ..() + + var/datum/species/golem/golem_species = new() + forced_name = golem_species.random_name() return ..() /obj/effect/mob_spawn/ghost_role/human/golem/special(mob/living/new_spawn, mob/mob_possessor) . = ..() + if(is_path_in_list(initial_type, GLOB.golem_stack_food_directory)) + var/datum/golem_food_buff/initial_buff = GLOB.golem_stack_food_directory[initial_type] + initial_buff.apply_effects(new_spawn) + var/mob/living/real_owner = owner_ref?.resolve() - var/datum/species/golem/golem_species = mob_species - to_chat(new_spawn, "[initial(golem_species.info_text)]") if(isnull(real_owner)) if(!is_station_level(new_spawn.z)) to_chat(new_spawn, "Build golem shells in the autolathe, and feed refined mineral sheets to the shells to bring them to life! \ You are generally a peaceful group unless provoked.") try_keep_home(new_spawn) + new_spawn.log_message("possessed a free golem shell.", LOG_GAME) log_admin("[key_name(new_spawn)] possessed a free golem shell.") else if(new_spawn.mind) new_spawn.mind.enslave_mind_to_creator(real_owner) - else stack_trace("[type] created a golem without a mind.") @@ -78,32 +80,6 @@ ADD_TRAIT(new_spawn, TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION, INNATE_TRAIT) new_spawn.AddComponent(/datum/component/hazard_area, area_whitelist = allowed_areas) -/obj/effect/mob_spawn/ghost_role/human/golem/attack_hand(mob/user, list/modifiers) - . = ..() - if(.) - return - if(!isgolem(user) || !can_transfer) - return - - var/mob/living/carbon/human/golem = user - var/transfer_choice = tgui_alert(usr, "Transfer your soul to [src]? (Warning, your old body will die!)",,list("Yes","No")) - if(transfer_choice != "Yes") - return - if(QDELETED(src) || uses <= 0) - return - uses -= 1 - golem.log_message("golem-swapped into [src].", LOG_GAME) - golem.visible_message( - span_notice("A faint light leaves [golem], moving to [src] and animating it!"), - span_notice("You leave your old body behind, and transfer into [src]!"), - ) - show_flavor = FALSE - var/mob/living/carbon/human/newgolem = create(user, golem.real_name) - golem.transfer_quirk_datums(newgolem) - golem.death() - check_uses() - return TRUE - /obj/effect/mob_spawn/ghost_role/human/golem/servant name = "inert servant golem shell" prompt_name = "servant golem" @@ -112,5 +88,11 @@ name = "dust-caked free golem shell" desc = "A humanoid shape, empty, lifeless, and full of potential." prompt_name = "free golem" - can_transfer = FALSE - mob_species = /datum/species/golem/adamantine + +/obj/effect/mob_spawn/ghost_role/human/golem/adamantine/special(mob/living/new_spawn, mob/mob_possessor) + . = ..() + if(!ishuman(new_spawn)) + return + var/mob/living/carbon/human/new_golem = new_spawn + var/obj/item/organ/internal/vocal_cords/adamantine/free_golem_radio = new() + free_golem_radio.Insert(new_golem) diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm index b16b7174c45..3856a52784a 100644 --- a/code/modules/movespeed/modifiers/mobs.dm +++ b/code/modules/movespeed/modifiers/mobs.dm @@ -13,6 +13,9 @@ /datum/movespeed_modifier/hunger variable = TRUE +/datum/movespeed_modifier/golem_hunger + variable = TRUE + /datum/movespeed_modifier/slaughter multiplicative_slowdown = -1 diff --git a/code/modules/movespeed/modifiers/status_effects.dm b/code/modules/movespeed/modifiers/status_effects.dm index d2120541cfa..734a45b95cd 100644 --- a/code/modules/movespeed/modifiers/status_effects.dm +++ b/code/modules/movespeed/modifiers/status_effects.dm @@ -31,3 +31,6 @@ /datum/movespeed_modifier/status_effect/inflated multiplicative_slowdown = 3.5 + +/datum/movespeed_modifier/status_effect/light_speed + multiplicative_slowdown = -0.2 // lighting is pretty slow in BYOND diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 0ce4f33ac4b..e7ba3be1e1f 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -138,13 +138,13 @@ return 100 * charge / maxcharge // use power from a cell -/obj/item/stock_parts/cell/use(amount, force) - if(rigged && amount > 0) +/obj/item/stock_parts/cell/use(used, force) + if(rigged && used > 0) explode() return FALSE - if(!force && charge < amount) + if(!force && charge < used) return FALSE - charge = max(charge - amount, 0) + charge = max(charge - used, 0) if(!istype(loc, /obj/machinery/power/apc)) SSblackbox.record_feedback("tally", "cell_used", 1, type) return TRUE @@ -392,7 +392,7 @@ chargerate = INFINITY ratingdesc = FALSE -/obj/item/stock_parts/cell/infinite/use() +/obj/item/stock_parts/cell/infinite/use(used) return TRUE /obj/item/stock_parts/cell/infinite/abductor diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index ebab72ba3df..6cfdcb2c762 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -25,7 +25,7 @@ ///Basically the alpha of the emitted light source var/bulb_power = 1 ///Default colour of the light. - var/bulb_colour = "#f3fffa" + var/bulb_colour = LIGHT_COLOR_DEFAULT ///LIGHT_OK, _EMPTY, _BURNED or _BROKEN var/status = LIGHT_OK ///Should we flicker? diff --git a/code/modules/power/lighting/light_items.dm b/code/modules/power/lighting/light_items.dm index fb74f3b77f7..44a2cc2d89a 100644 --- a/code/modules/power/lighting/light_items.dm +++ b/code/modules/power/lighting/light_items.dm @@ -21,6 +21,11 @@ /obj/item/light/Initialize(mapload) . = ..() AddElement(/datum/element/update_icon_updates_onmob, ITEM_SLOT_HANDS) + AddComponent(/datum/component/golem_food, golem_food_key = /obj/item/light, extra_validation = CALLBACK(src, PROC_REF(is_intact))) + +/// Returns true if bulb is intact +/obj/item/light/proc/is_intact() + return status == LIGHT_OK /obj/item/light/suicide_act(mob/living/carbon/user) if (status == LIGHT_BROKEN) diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 017844db70b..eea60afd8cf 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -171,8 +171,8 @@ return TRUE -/obj/item/gun/energy/plasmacutter/use(amount) - return (!QDELETED(cell) && cell.use(amount ? amount * charge_weld : charge_weld)) +/obj/item/gun/energy/plasmacutter/use(used) + return (!QDELETED(cell) && cell.use(used ? used * charge_weld : charge_weld)) /obj/item/gun/energy/plasmacutter/use_tool(atom/target, mob/living/user, delay, amount=1, volume=0, datum/callback/extra_checks) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index d99a795c3e0..3f092bffac3 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -164,6 +164,23 @@ M.adjust_nutrition(-delayed_satiety_drain) return ..() +/datum/reagent/consumable/nutriment/mineral + name = "Mineral Slurry" + description = "Minerals pounded into a paste, nutritious only if you too are made of rocks." + color = COLOR_WEBSAFE_DARK_GRAY + chemical_flags = NONE + brute_heal = 0 + burn_heal = 0 + +/datum/reagent/consumable/nutriment/mineral/on_mob_life(mob/living/carbon/eater, delta_time, times_fired) + current_cycle++ + if (HAS_TRAIT(eater, TRAIT_ROCK_EATER) && !HAS_TRAIT(eater, TRAIT_NOHUNGER) && ishuman(eater)) + var/mob/living/carbon/human/golem_eater = eater + golem_eater.adjust_nutrition(nutriment_factor * REM * delta_time) + if(length(reagent_removal_skip_list)) + return + holder.remove_reagent(type, metabolization_rate * delta_time) + /datum/reagent/consumable/cooking_oil name = "Cooking Oil" description = "A variety of cooking oil derived from fat or plants. Used in food preparation and frying." diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 8b12b9e1c19..a99c6f4d608 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -722,16 +722,6 @@ taste_description = "rocks" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE -/datum/reagent/mutationtoxin/golem/on_mob_metabolize() - var/static/list/random_golem_types - random_golem_types = subtypesof(/datum/species/golem) - type - for(var/i in random_golem_types) - var/datum/species/golem/golem = i - if(!initial(golem.random_eligible)) - random_golem_types -= golem - race = pick(random_golem_types) - ..() - /datum/reagent/mutationtoxin/abductor name = "Abductor Mutation Toxin" description = "An alien toxin." diff --git a/code/modules/research/xenobiology/crossbreeding/charged.dm b/code/modules/research/xenobiology/crossbreeding/charged.dm index dff26b1c0b1..f9d59193a66 100644 --- a/code/modules/research/xenobiology/crossbreeding/charged.dm +++ b/code/modules/research/xenobiology/crossbreeding/charged.dm @@ -266,7 +266,7 @@ Charged extracts: /obj/item/slimecross/charged/adamantine/do_effect(mob/user) user.visible_message(span_notice("[src] produces a fully formed golem shell!")) - new /obj/effect/mob_spawn/ghost_role/human/golem/servant(get_turf(src), /datum/species/golem/adamantine, user) + new /obj/effect/mob_spawn/ghost_role/human/golem/servant(get_turf(src), /datum/species/golem, user) ..() /obj/item/slimecross/charged/rainbow diff --git a/code/modules/research/xenobiology/crossbreeding/chilling.dm b/code/modules/research/xenobiology/crossbreeding/chilling.dm index 18f5a2849ca..b2bf3684661 100644 --- a/code/modules/research/xenobiology/crossbreeding/chilling.dm +++ b/code/modules/research/xenobiology/crossbreeding/chilling.dm @@ -295,20 +295,13 @@ Chilling extracts: /obj/item/slimecross/chilling/black colour = "black" - effect_desc = "Transforms the user into a random type of golem." + effect_desc = "Transforms the user into a golem." /obj/item/slimecross/chilling/black/do_effect(mob/user) if(ishuman(user)) - user.visible_message(span_notice("[src] crystallizes along [user]'s skin, turning into metallic scales!")) + user.visible_message(span_notice("[src] crystallizes along [user]'s skin, turning into solid stone!")) var/mob/living/carbon/human/H = user - - var/static/list/random_golem_types - random_golem_types = subtypesof(/datum/species/golem) - type - - for(var/datum/species/golem/golem as anything in random_golem_types) - if(!initial(golem.random_eligible)) - random_golem_types -= golem - H.set_species(pick(random_golem_types)) + H.set_species(/datum/species/golem) ..() /obj/item/slimecross/chilling/lightpink diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index c07004ed824..da012a95fe6 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -482,10 +482,10 @@ return 450 if(SLIME_ACTIVATE_MAJOR) - to_chat(user, span_warning("You feel your body rapidly crystallizing...")) + to_chat(user, span_warning("You feel your body rapidly hardening...")) if(do_after(user, 120, target = user)) to_chat(user, span_warning("You feel solid.")) - user.set_species(pick(/datum/species/golem/adamantine)) + user.set_species(/datum/species/golem) return to_chat(user, span_notice("You stop feeding [src], and your body returns to its slimelike state.")) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 4d263ff88cb..1f6bd734fee 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -195,6 +195,8 @@ var/list/bodypart_traits = list() /// The name of the trait source that the organ gives. Should not be altered during the events of gameplay, and will cause problems if it is. var/bodypart_trait_source = BODYPART_TRAIT + /// List of the above datums which have actually been instantiated, managed automatically + var/list/feature_offsets = list() /obj/item/bodypart/Initialize(mapload) . = ..() @@ -233,6 +235,7 @@ qdel(external_organ) // It handles removing its references to this limb on its own. external_organs = list() + QDEL_LIST_ASSOC_VAL(feature_offsets) return ..() @@ -689,8 +692,11 @@ return FALSE //`null` is a valid option, so we need to use a num var to make it clear no change was made. var/mob/living/carbon/old_owner = owner owner = new_owner + SEND_SIGNAL(src, COMSIG_BODYPART_CHANGED_OWNER, new_owner, old_owner) var/needs_update_disabled = FALSE //Only really relevant if there's an owner if(old_owner) + if(length(bodypart_traits)) + old_owner.remove_traits(bodypart_traits, bodypart_trait_source) if(initial(can_be_disabled)) if(HAS_TRAIT(old_owner, TRAIT_NOLIMBDISABLE)) if(!owner || !HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE)) @@ -704,6 +710,8 @@ )) UnregisterSignal(old_owner, COMSIG_ATOM_RESTYLE) if(owner) + if(length(bodypart_traits)) + owner.add_traits(bodypart_traits, bodypart_trait_source) if(initial(can_be_disabled)) if(HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE)) set_can_be_disabled(FALSE) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 7b0d04bc02e..37204dae53b 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -133,9 +133,6 @@ continue organ.transfer_to_limb(src, phantom_owner) - if(length(bodypart_traits)) - phantom_owner.remove_traits(bodypart_traits, bodypart_trait_source) - update_icon_dropped() synchronize_bodytypes(phantom_owner) phantom_owner.update_health_hud() //update the healthdoll @@ -327,6 +324,7 @@ return FALSE SEND_SIGNAL(new_limb_owner, COMSIG_CARBON_ATTACH_LIMB, src, special) + SEND_SIGNAL(src, COMSIG_BODYPART_ATTACHED, new_limb_owner, special) moveToNullspace() set_owner(new_limb_owner) new_limb_owner.add_bodypart(src) @@ -367,9 +365,6 @@ if(can_be_disabled) update_disabled() - if(length(bodypart_traits)) - owner.add_traits(bodypart_traits, bodypart_trait_source) - // Bodyparts need to be sorted for leg masking to be done properly. It also will allow for some predictable // behavior within said bodyparts list. We sort it here, as it's the only place we make changes to bodyparts. new_limb_owner.bodyparts = sort_list(new_limb_owner.bodyparts, GLOBAL_PROC_REF(cmp_bodypart_by_body_part_asc)) diff --git a/code/modules/surgery/bodyparts/hair.dm b/code/modules/surgery/bodyparts/hair.dm index 80fc4a38297..8b76bc2e12b 100644 --- a/code/modules/surgery/bodyparts/hair.dm +++ b/code/modules/surgery/bodyparts/hair.dm @@ -48,7 +48,7 @@ hair_hidden = TRUE facial_hair_hidden = TRUE - if(!hair_hidden && !owner.get_organ_slot(ORGAN_SLOT_BRAIN) && !HAS_TRAIT(owner, TRAIT_NOBLOOD)) + if(!hair_hidden && !owner.get_organ_slot(ORGAN_SLOT_BRAIN) && !HAS_TRAIT(owner, TRAIT_NO_DEBRAIN_OVERLAY)) show_debrained = TRUE else show_debrained = FALSE diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index a0017241ae4..2dc654fa7af 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -67,6 +67,17 @@ var/lip_style var/lip_color = "white" + /// Offset to apply to equipment worn on the ears + var/datum/worn_feature_offset/worn_ears_offset + /// Offset to apply to equipment worn on the eyes + var/datum/worn_feature_offset/worn_glasses_offset + /// Offset to apply to equipment worn on the mouth + var/datum/worn_feature_offset/worn_mask_offset + /// Offset to apply to equipment worn on the head + var/datum/worn_feature_offset/worn_head_offset + /// Offset to apply to overlays placed on the face + var/datum/worn_feature_offset/worn_face_offset + var/stored_lipstick_trait ///The image for hair var/mutable_appearance/hair_overlay @@ -77,13 +88,18 @@ ///The image for facial hair gradient var/mutable_appearance/facial_gradient_overlay - /obj/item/bodypart/head/Destroy() QDEL_NULL(brainmob) //order is sensitive, see warning in handle_atom_del() below QDEL_NULL(brain) QDEL_NULL(eyes) QDEL_NULL(ears) QDEL_NULL(tongue) + + QDEL_NULL(worn_ears_offset) + QDEL_NULL(worn_glasses_offset) + QDEL_NULL(worn_mask_offset) + QDEL_NULL(worn_head_offset) + QDEL_NULL(worn_face_offset) return ..() /obj/item/bodypart/head/handle_atom_del(atom/head_atom) @@ -200,22 +216,14 @@ facial_overlay.alpha = hair_alpha . += facial_overlay - if(!eyes) - . += image('icons/mob/species/human/human_face.dmi', "eyes_missing", -BODY_LAYER, SOUTH) + if(!eyes && !(NOEYEHOLES in species_flags_list) && !(NOEYESPRITES in species_flags_list)) + var/image/no_eyes = image('icons/mob/species/human/human_face.dmi', "eyes_missing", -BODY_LAYER, SOUTH) + worn_glasses_offset?.apply_offset(no_eyes) + . += no_eyes //Applies the debrained overlay if there is no brain if(!brain) - var/image/debrain_overlay = image(layer = -HAIR_LAYER, dir = SOUTH) - if(bodytype & BODYTYPE_ALIEN) - debrain_overlay.icon = 'icons/mob/species/alien/bodyparts.dmi' - debrain_overlay.icon_state = "debrained_alien" - else if(bodytype & BODYTYPE_LARVA_PLACEHOLDER) - debrain_overlay.icon = 'icons/mob/species/alien/bodyparts.dmi' - debrain_overlay.icon_state = "debrained_larva" - else if(!(TRAIT_NOBLOOD in species_flags_list)) - debrain_overlay.icon = 'icons/mob/species/human/human_face.dmi' - debrain_overlay.icon_state = "debrained" - . += debrain_overlay + . += get_debrain_overlay(can_rotate = FALSE) else var/datum/sprite_accessory/sprite2 = GLOB.hairstyles_list[hair_style] if(sprite2 && (HAIR in species_flags_list)) @@ -232,7 +240,7 @@ . += lips_overlay // eyes - if(eyes) // This is a bit of copy/paste code from eyes.dm:generate_body_overlay + if(eyes && eyes.eye_icon_state && !(NOEYESPRITES in species_flags_list)) // This is a bit of copy/paste code from eyes.dm:generate_body_overlay var/image/eye_left = image('icons/mob/species/human/human_face.dmi', "[eyes.eye_icon_state]_l", -BODY_LAYER, SOUTH) var/image/eye_right = image('icons/mob/species/human/human_face.dmi', "[eyes.eye_icon_state]_r", -BODY_LAYER, SOUTH) if(eyes.eye_color_left) @@ -256,7 +264,7 @@ . += facial_gradient_overlay if(show_debrained) - . += mutable_appearance('icons/mob/species/human/human_face.dmi', "debrained", HAIR_LAYER) + . += get_debrain_overlay(can_rotate = TRUE) else if(!hair_hidden && hair_overlay && (HAIR in species_flags_list)) hair_overlay.alpha = hair_alpha @@ -264,6 +272,29 @@ if(hair_gradient_overlay) . += hair_gradient_overlay +/// Returns an appropriate debrained icon state +/obj/item/bodypart/head/proc/get_debrain_overlay(can_rotate = TRUE) + var/debrain_icon = 'icons/mob/species/human/human_face.dmi' + var/debrain_icon_state = "debrained" + if(bodytype & BODYTYPE_ALIEN) + debrain_icon = 'icons/mob/species/alien/bodyparts.dmi' + debrain_icon_state = "debrained_alien" + else if(bodytype & BODYTYPE_LARVA_PLACEHOLDER) + debrain_icon = 'icons/mob/species/alien/bodyparts.dmi' + debrain_icon_state = "debrained_larva" + else if(bodytype & BODYTYPE_GOLEM) + debrain_icon = 'icons/mob/species/golems.dmi' + debrain_icon_state = "debrained" + else if((TRAIT_NOBLOOD in species_flags_list)) + return null + + var/image/debrain_overlay + if (can_rotate) + debrain_overlay = mutable_appearance(debrain_icon, debrain_icon_state, HAIR_LAYER) + else + debrain_overlay = image(debrain_icon, debrain_icon_state, -HAIR_LAYER, SOUTH) + return debrain_overlay + /mob/living/proc/set_haircolor(hex_string, override) return diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm index 4ecf2f3481e..882f07ad075 100644 --- a/code/modules/surgery/bodyparts/parts.dm +++ b/code/modules/surgery/bodyparts/parts.dm @@ -18,6 +18,21 @@ var/obj/item/cavity_item + /// Offset to apply to equipment worn as a uniform + var/datum/worn_feature_offset/worn_uniform_offset + /// Offset to apply to equipment worn on the id slot + var/datum/worn_feature_offset/worn_id_offset + /// Offset to apply to equipment worn in the suit slot + var/datum/worn_feature_offset/worn_suit_storage_offset + /// Offset to apply to equipment worn on the hips + var/datum/worn_feature_offset/worn_belt_offset + /// Offset to apply to overlays placed on the back + var/datum/worn_feature_offset/worn_back_offset + /// Offset to apply to equipment worn as a suit + var/datum/worn_feature_offset/worn_suit_offset + /// Offset to apply to equipment worn on the neck + var/datum/worn_feature_offset/worn_neck_offset + /obj/item/bodypart/chest/can_dismember(obj/item/item) if(owner.stat < HARD_CRIT || !get_organs()) return FALSE @@ -25,6 +40,13 @@ /obj/item/bodypart/chest/Destroy() QDEL_NULL(cavity_item) + QDEL_NULL(worn_uniform_offset) + QDEL_NULL(worn_id_offset) + QDEL_NULL(worn_suit_storage_offset) + QDEL_NULL(worn_belt_offset) + QDEL_NULL(worn_back_offset) + QDEL_NULL(worn_suit_offset) + QDEL_NULL(worn_neck_offset) return ..() /obj/item/bodypart/chest/drop_organs(mob/user, violent_removal) @@ -87,6 +109,15 @@ unarmed_damage_high = 10 unarmed_stun_threshold = 10 body_zone = BODY_ZONE_L_ARM + /// Datum describing how to offset things worn on the hands of this arm, note that an x offset won't do anything here + var/datum/worn_feature_offset/worn_glove_offset + /// Datum describing how to offset things held in the hands of this arm, the x offset IS functional here + var/datum/worn_feature_offset/held_hand_offset + +/obj/item/bodypart/arm/Destroy() + QDEL_NULL(worn_glove_offset) + QDEL_NULL(held_hand_offset) + return ..() /obj/item/bodypart/arm/left name = "left arm" @@ -310,6 +341,12 @@ unarmed_damage_low = 2 unarmed_damage_high = 15 unarmed_stun_threshold = 10 + /// Datum describing how to offset things worn on the foot of this leg, note that an x offset won't do anything here + var/datum/worn_feature_offset/worn_foot_offset + +/obj/item/bodypart/leg/Destroy() + QDEL_NULL(worn_foot_offset) + return ..() /obj/item/bodypart/leg/left name = "left leg" diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index d41d0ea4ec1..3967323cf48 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -358,344 +358,131 @@ unarmed_damage_high = 21 unarmed_stun_threshold = 14 -///GOLEMS (i hate xenobio SO FUCKING MUCH) (from 2022: Yeah I fucking feel your pain brother) /obj/item/bodypart/head/golem + icon = 'icons/mob/species/golems.dmi' + icon_static = 'icons/mob/species/golems.dmi' + icon_state = "golem_head" biological_state = BIO_INORGANIC + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC limb_id = SPECIES_GOLEM is_dimorphic = FALSE + should_draw_greyscale = FALSE dmg_overlay_type = null +/obj/item/bodypart/head/golem/Initialize(mapload) + worn_ears_offset = new( + attached_part = src, + feature_key = OFFSET_EARS, + offset_x = list("north" = 1, "south" = -1, "east" = 1, "west" = -1), + offset_y = list("south" = 1), + ) + worn_glasses_offset = new( + attached_part = src, + feature_key = OFFSET_GLASSES, + offset_x = list("north" = 1, "south" = -1, "east" = 1, "west" = -1), + ) + worn_head_offset = new( + attached_part = src, + feature_key = OFFSET_HEAD, + offset_x = list("north" = 1, "south" = -1, "east" = 1, "west" = -1), + offset_y = list("south" = 1), + ) + worn_face_offset = new( + attached_part = src, + feature_key = OFFSET_FACE, + offset_x = list("north" = 1, "south" = -1, "east" = 1, "west" = -1), + ) + return ..() + /obj/item/bodypart/chest/golem + icon = 'icons/mob/species/golems.dmi' + icon_static = 'icons/mob/species/golems.dmi' + icon_state = "golem_chest" biological_state = BIO_INORGANIC + acceptable_bodytype = BODYTYPE_GOLEM + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC limb_id = SPECIES_GOLEM - is_dimorphic = TRUE + is_dimorphic = FALSE + should_draw_greyscale = FALSE dmg_overlay_type = null bodypart_traits = list(TRAIT_NO_JUMPSUIT) -/obj/item/bodypart/arm/left/golem - biological_state = BIO_INORGANIC - limb_id = SPECIES_GOLEM - dmg_overlay_type = null - bodypart_traits = list(TRAIT_CHUNKYFINGERS) - unarmed_damage_low = 5 // I'd like to take the moment that maintaining all of these random ass golem speciese is hell and oranges was right - unarmed_damage_high = 14 - unarmed_stun_threshold = 11 +/obj/item/bodypart/chest/golem/Initialize(mapload) + worn_belt_offset = new( + attached_part = src, + feature_key = OFFSET_BELT, + offset_x = list("north" = 1, "south" = -1, "east" = 1, "west" = -1), + ) + return ..() -/obj/item/bodypart/arm/right/golem +/obj/item/bodypart/arm/left/golem + icon = 'icons/mob/species/golems.dmi' + icon_static = 'icons/mob/species/golems.dmi' + icon_state = "golem_l_arm" biological_state = BIO_INORGANIC + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC limb_id = SPECIES_GOLEM + should_draw_greyscale = FALSE dmg_overlay_type = null - bodypart_traits = list(TRAIT_CHUNKYFINGERS) + bodypart_traits = list(TRAIT_CHUNKYFINGERS, TRAIT_FIST_MINING) unarmed_damage_low = 5 unarmed_damage_high = 14 unarmed_stun_threshold = 11 -/obj/item/bodypart/leg/left/golem +/obj/item/bodypart/arm/left/golem/Initialize(mapload) + held_hand_offset = new( + attached_part = src, + feature_key = OFFSET_HELD, + offset_x = list("north" = -1, "south" = 2, "east" = 0, "west" = -3), + offset_y = list("south" = -2), + ) + return ..() + +/obj/item/bodypart/arm/right/golem + icon = 'icons/mob/species/golems.dmi' + icon_static = 'icons/mob/species/golems.dmi' + icon_state = "golem_r_arm" biological_state = BIO_INORGANIC + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC limb_id = SPECIES_GOLEM + should_draw_greyscale = FALSE + dmg_overlay_type = null + bodypart_traits = list(TRAIT_CHUNKYFINGERS, TRAIT_FIST_MINING) + unarmed_damage_low = 5 + unarmed_damage_high = 14 + unarmed_stun_threshold = 11 + +/obj/item/bodypart/arm/right/golem/Initialize(mapload) + held_hand_offset = new( + attached_part = src, + feature_key = OFFSET_HELD, + offset_x = list("north" = 2, "south" = -2, "east" = 3, "west" = 0), + offset_y = list("south" = -2), + ) + return ..() + +/obj/item/bodypart/leg/left/golem + icon = 'icons/mob/species/golems.dmi' + icon_static = 'icons/mob/species/golems.dmi' + icon_state = "golem_l_leg" + biological_state = BIO_INORGANIC + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC + limb_id = SPECIES_GOLEM + should_draw_greyscale = FALSE dmg_overlay_type = null unarmed_damage_low = 7 unarmed_damage_high = 21 unarmed_stun_threshold = 11 /obj/item/bodypart/leg/right/golem + icon = 'icons/mob/species/golems.dmi' + icon_static = 'icons/mob/species/golems.dmi' + icon_state = "golem_r_leg" biological_state = BIO_INORGANIC + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC limb_id = SPECIES_GOLEM + should_draw_greyscale = FALSE dmg_overlay_type = null unarmed_damage_low = 7 unarmed_damage_high = 21 unarmed_stun_threshold = 11 - -///CULT GOLEM -/obj/item/bodypart/head/golem/cult - limb_id = SPECIES_GOLEM_CULT - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/chest/golem/cult - limb_id = SPECIES_GOLEM_CULT - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/left/golem/cult - limb_id = SPECIES_GOLEM_CULT - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/right/golem/cult - limb_id = SPECIES_GOLEM_CULT - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/left/golem/cult - limb_id = SPECIES_GOLEM_CULT - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/right/golem/cult - limb_id = SPECIES_GOLEM_CULT - should_draw_greyscale = FALSE - -///CLOTH GOLEM -/obj/item/bodypart/head/golem/cloth - limb_id = SPECIES_GOLEM_CLOTH - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/chest/golem/cloth - limb_id = SPECIES_GOLEM_CLOTH - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/left/golem/cloth - limb_id = SPECIES_GOLEM_CLOTH - should_draw_greyscale = FALSE - unarmed_damage_low = 4 - unarmed_stun_threshold = 7 - unarmed_damage_high = 8 - -/obj/item/bodypart/arm/right/golem/cloth - limb_id = SPECIES_GOLEM_CLOTH - should_draw_greyscale = FALSE - unarmed_damage_low = 4 - unarmed_stun_threshold = 7 - unarmed_damage_high = 8 - -/obj/item/bodypart/leg/left/golem/cloth - limb_id = SPECIES_GOLEM_CLOTH - should_draw_greyscale = FALSE - unarmed_damage_low = 6 - unarmed_stun_threshold = 7 - unarmed_damage_high = 12 - -/obj/item/bodypart/leg/right/golem/cloth - limb_id = SPECIES_GOLEM_CLOTH - should_draw_greyscale = FALSE - unarmed_damage_low = 6 - unarmed_stun_threshold = 7 - unarmed_damage_high = 12 - -///CARDBOARD GOLEM -/obj/item/bodypart/head/golem/cardboard - limb_id = SPECIES_GOLEM_CARDBOARD - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/chest/golem/cardboard - limb_id = SPECIES_GOLEM_CARDBOARD - is_dimorphic = TRUE - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/left/golem/cardboard - limb_id = SPECIES_GOLEM_CARDBOARD - should_draw_greyscale = FALSE - unarmed_attack_verb = "whip" - unarmed_attack_sound = 'sound/weapons/whip.ogg' - unarmed_miss_sound = 'sound/weapons/etherealmiss.ogg' - unarmed_damage_low = 4 - unarmed_stun_threshold = 7 - unarmed_damage_high = 8 - -/obj/item/bodypart/arm/right/golem/cardboard - limb_id = SPECIES_GOLEM_CARDBOARD - should_draw_greyscale = FALSE - unarmed_attack_verb = "whip" - unarmed_attack_sound = 'sound/weapons/whip.ogg' - unarmed_miss_sound = 'sound/weapons/etherealmiss.ogg' - unarmed_damage_low = 4 - unarmed_stun_threshold = 7 - unarmed_damage_high = 8 - -/obj/item/bodypart/leg/left/golem/cardboard - limb_id = SPECIES_GOLEM_CARDBOARD - should_draw_greyscale = FALSE - unarmed_attack_sound = 'sound/weapons/whip.ogg' - unarmed_miss_sound = 'sound/weapons/etherealmiss.ogg' - unarmed_damage_low = 6 - unarmed_stun_threshold = 7 - unarmed_damage_high = 12 - -/obj/item/bodypart/leg/right/golem/cardboard - limb_id = SPECIES_GOLEM_CARDBOARD - should_draw_greyscale = FALSE - unarmed_attack_sound = 'sound/weapons/whip.ogg' - unarmed_miss_sound = 'sound/weapons/etherealmiss.ogg' - unarmed_damage_low = 6 - unarmed_stun_threshold = 7 - unarmed_damage_high = 12 - -///DURATHREAD GOLEM -/obj/item/bodypart/head/golem/durathread - limb_id = SPECIES_GOLEM_DURATHREAD - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/chest/golem/durathread - limb_id = SPECIES_GOLEM_DURATHREAD - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/left/golem/durathread - limb_id = SPECIES_GOLEM_DURATHREAD - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/right/golem/durathread - limb_id = SPECIES_GOLEM_DURATHREAD - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/left/golem/durathread - limb_id = SPECIES_GOLEM_DURATHREAD - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/right/golem/durathread - limb_id = SPECIES_GOLEM_DURATHREAD - should_draw_greyscale = FALSE - -///BONE GOLEM -/obj/item/bodypart/head/golem/bone - biological_state = BIO_BONE - limb_id = SPECIES_GOLEM_BONE - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/chest/golem/bone - biological_state = BIO_BONE - limb_id = SPECIES_GOLEM_BONE - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/left/golem/bone - biological_state = BIO_BONE - limb_id = SPECIES_GOLEM_BONE - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/right/golem/bone - biological_state = BIO_BONE - limb_id = SPECIES_GOLEM_BONE - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/left/golem/bone - biological_state = BIO_BONE - limb_id = SPECIES_GOLEM_BONE - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/right/golem/bone - biological_state = BIO_BONE - limb_id = SPECIES_GOLEM_BONE - should_draw_greyscale = FALSE - -///SNOW GOLEM -/obj/item/bodypart/head/golem/snow - limb_id = SPECIES_GOLEM_SNOW - is_dimorphic = FALSE - should_draw_greyscale = FALSE - -/obj/item/bodypart/chest/golem/snow - limb_id = SPECIES_GOLEM_SNOW - is_dimorphic = TRUE //WHO MADE SNOW BREASTS? - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/left/golem/snow - limb_id = SPECIES_GOLEM_SNOW - should_draw_greyscale = FALSE - -/obj/item/bodypart/arm/right/golem/snow - limb_id = SPECIES_GOLEM_SNOW - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/left/golem/snow - limb_id = SPECIES_GOLEM_SNOW - should_draw_greyscale = FALSE - -/obj/item/bodypart/leg/right/golem/snow - limb_id = SPECIES_GOLEM_SNOW - should_draw_greyscale = FALSE - -///URANIUM GOLEM -/obj/item/bodypart/arm/left/golem/uranium - attack_type = BURN - unarmed_attack_verb = "burn" - unarmed_attack_sound = 'sound/weapons/sear.ogg' - unarmed_damage_low = 1 - unarmed_damage_high = 10 - unarmed_stun_threshold = 9 - -/obj/item/bodypart/arm/right/golem/uranium - attack_type = BURN - unarmed_attack_verb = "burn" - unarmed_attack_sound = 'sound/weapons/sear.ogg' - unarmed_damage_low = 1 - unarmed_damage_high = 10 - unarmed_stun_threshold = 9 - -/obj/item/bodypart/leg/left/golem/uranium - attack_type = BURN - unarmed_attack_sound = 'sound/weapons/sear.ogg' - unarmed_damage_low = 2 - unarmed_damage_high = 15 - unarmed_stun_threshold = 9 - -/obj/item/bodypart/leg/right/golem/uranium - attack_type = BURN - unarmed_attack_sound = 'sound/weapons/sear.ogg' - unarmed_damage_low = 2 - unarmed_damage_high = 15 - unarmed_stun_threshold = 9 - -///PLASTEEL GOLEM -/obj/item/bodypart/arm/left/golem/plasteel - unarmed_attack_verb = "smash" - unarmed_attack_effect = ATTACK_EFFECT_SMASH - unarmed_attack_sound = 'sound/effects/meteorimpact.ogg' //hits pretty hard - unarmed_damage_low = 12 - unarmed_damage_high = 21 - unarmed_stun_threshold = 18 - -/obj/item/bodypart/arm/right/golem/plasteel - unarmed_attack_verb = "smash" - unarmed_attack_effect = ATTACK_EFFECT_SMASH - unarmed_attack_sound = 'sound/effects/meteorimpact.ogg' - unarmed_damage_low = 12 - unarmed_damage_high = 21 - unarmed_stun_threshold = 18 - - -/obj/item/bodypart/leg/left/golem/plasteel - unarmed_attack_effect = ATTACK_EFFECT_SMASH - unarmed_attack_sound = 'sound/effects/meteorimpact.ogg' - unarmed_damage_low = 18 - unarmed_damage_high = 32 - unarmed_stun_threshold = 18 - -/obj/item/bodypart/leg/right/golem/plasteel - unarmed_attack_effect = ATTACK_EFFECT_SMASH - unarmed_attack_sound = 'sound/effects/meteorimpact.ogg' - unarmed_damage_low = 18 - unarmed_damage_high = 32 - unarmed_stun_threshold = 18 - -///BANANIUM GOLEM -/obj/item/bodypart/arm/left/golem/bananium - unarmed_attack_verb = "honk" - unarmed_attack_sound = 'sound/items/airhorn2.ogg' - unarmed_damage_low = 0 - unarmed_damage_high = 1 - unarmed_stun_threshold = 2 //Harmless and can't stun - -/obj/item/bodypart/arm/right/golem/bananium - unarmed_attack_verb = "honk" - unarmed_attack_sound = 'sound/items/airhorn2.ogg' - unarmed_damage_low = 0 - unarmed_damage_high = 1 - unarmed_stun_threshold = 2 - -/obj/item/bodypart/leg/right/golem/bananium - unarmed_attack_verb = "honk" - unarmed_attack_sound = 'sound/items/airhorn2.ogg' - unarmed_damage_low = 0 - unarmed_damage_high = 1 - unarmed_stun_threshold = 2 - -/obj/item/bodypart/leg/left/golem/bananium - unarmed_attack_verb = "honk" - unarmed_attack_sound = 'sound/items/airhorn2.ogg' - unarmed_damage_low = 0 - unarmed_damage_high = 1 - unarmed_stun_threshold = 2 diff --git a/code/modules/surgery/bodyparts/worn_feature_offset.dm b/code/modules/surgery/bodyparts/worn_feature_offset.dm new file mode 100644 index 00000000000..3e2a9c7c2d7 --- /dev/null +++ b/code/modules/surgery/bodyparts/worn_feature_offset.dm @@ -0,0 +1,67 @@ +/// A datum for controlling how to position items on an unusually offset body part +/// For instance if you have an asymmetrical head, hats might need to be offset to one side +/datum/worn_feature_offset + /// Owner of mob we are attached to, could be null on a severed limb + var/mob/living/carbon/owner + /// What are we attached to + var/obj/item/bodypart/attached_part + /// Key used to identify what this offset applies to + var/feature_key + /// Offsets to apply on the x axis for each direction + var/list/offset_x + /// Offsets to apply on the y axis for each direction + var/list/offset_y + +/datum/worn_feature_offset/New( + obj/item/bodypart/attached_part, + feature_key, + list/offset_x = list("south" = 0), + list/offset_y = list("south" = 0), +) + attached_part.feature_offsets[feature_key] = src + owner = attached_part.owner + src.attached_part = attached_part + src.feature_key = feature_key + src.offset_x = offset_x + src.offset_y = offset_y + + if (length(offset_x) <= 1 && length(offset_y) <= 1) + return // We don't need to do any extra signal handling + + if (!isnull(owner)) + changed_owner(owner) + RegisterSignal(attached_part, COMSIG_BODYPART_CHANGED_OWNER, PROC_REF(changed_owner)) + +/// Returns the current offset which should be used for this feature +/datum/worn_feature_offset/proc/get_offset() + var/current_dir = owner ? owner.dir : SOUTH + current_dir = dir2text(current_dir) + var/x = length(offset_x) ? ((current_dir in offset_x) ? offset_x[current_dir] : offset_x["south"]) : 0 + var/y = length(offset_y) ? ((current_dir in offset_y) ? offset_y[current_dir] : offset_y["south"]) : 0 + return list("x" = x, "y" = y) + +/// Applies the current offset to a provided overlay image +/datum/worn_feature_offset/proc/apply_offset(image/overlay) + var/list/offset = get_offset() + overlay.pixel_x += offset["x"] + overlay.pixel_y += offset["y"] + +/// When the owner of the bodypart changes, update our signal registrations +/datum/worn_feature_offset/proc/changed_owner(obj/item/bodypart/part, mob/living/new_owner, mob/living/old_owner) + SIGNAL_HANDLER + owner = new_owner + if (!isnull(old_owner)) + UnregisterSignal(old_owner, COMSIG_ATOM_POST_DIR_CHANGE) + if (!isnull(new_owner)) + RegisterSignal(new_owner, COMSIG_ATOM_POST_DIR_CHANGE, PROC_REF(on_dir_change)) + RegisterSignal(new_owner, COMSIG_PARENT_QDELETING, PROC_REF(on_owner_deleted)) + +/// If the owner is deleted, stop updating +/datum/worn_feature_offset/proc/on_owner_deleted(mob/living/host) + SIGNAL_HANDLER + owner = null + +/// When we change direction, re-apply the offset +/datum/worn_feature_offset/proc/on_dir_change(mob/living/carbon/owner, olddir, newdir) + SIGNAL_HANDLER + owner.update_features(feature_key) diff --git a/code/modules/surgery/organs/appendix_golem.dm b/code/modules/surgery/organs/appendix_golem.dm new file mode 100644 index 00000000000..eb89112cbd0 --- /dev/null +++ b/code/modules/surgery/organs/appendix_golem.dm @@ -0,0 +1,82 @@ + +/obj/item/organ/internal/appendix/golem + name = "internal forge" + desc = "This expanded digestive chamber allows golems to smelt minerals, provided that they are immersed in lava." + icon_state = "ethereal_heart" + color = COLOR_GOLEM_GRAY + status = ORGAN_MINERAL + /// Action which performs smelting + var/datum/action/cooldown/internal_smelting/smelter + +/obj/item/organ/internal/appendix/golem/Initialize(mapload) + . = ..() + smelter = new(src) + +/obj/item/organ/internal/appendix/golem/on_insert(mob/living/carbon/organ_owner) + . = ..() + RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(check_for_lava)) + +/// Give the action while in lava +/obj/item/organ/internal/appendix/golem/proc/check_for_lava(mob/living/owner) + SIGNAL_HANDLER + if (!islava(owner.loc)) + smelter.Remove(owner) + return + if (smelter.owner != owner) + smelter.Grant(owner) + +/obj/item/organ/internal/appendix/golem/on_remove(mob/living/carbon/organ_owner) + UnregisterSignal(organ_owner, COMSIG_MOVABLE_MOVED) + smelter?.Remove(organ_owner) // Might have been deleted by Destroy already + return ..() + +/obj/item/organ/internal/appendix/golem/Destroy() + QDEL_NULL(smelter) + return ..() + +/// Lets golems smelt ore with their organs +/datum/action/cooldown/internal_smelting + name = "Internal Forge" + desc = "While stood in lava you can use your internal forge to smelt ores into processed bars." + button_icon = 'icons/obj/stack_objects.dmi' + button_icon_state = "sheet-adamantine_2" + check_flags = AB_CHECK_HANDS_BLOCKED | AB_CHECK_LYING | AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED + /// Time it takes to smelt one ore into one result + var/smelt_speed = 2 SECONDS + /// Amount to speed up by every completion + var/speed_up_interval = 0.2 SECONDS + /// Minimum time to take to smelt one ore into one result + var/minimum_smelt_speed = 1 SECONDS + +/datum/action/cooldown/internal_smelting/IsAvailable(feedback) + . = ..() + if (!.) + return FALSE + if (!islava(owner.loc)) + if (feedback) + owner.balloon_alert(owner, "requires lava!") + return FALSE + return TRUE + +/datum/action/cooldown/internal_smelting/Activate(mob/target) + . = ..() + smelt_speed = initial(smelt_speed) + smelt_held(target) + +/// Smelt an item held in one hand and put the result in the other +/datum/action/cooldown/internal_smelting/proc/smelt_held(mob/target) + var/obj/item/stack/ore/held_ore = locate(/obj/item/stack/ore) in target.held_items + if (!held_ore?.refined_type) + target.balloon_alert(target, "nothing to smelt!") + return + target.balloon_alert(owner, "smelting...") + if (!do_after(target, smelt_speed, target = held_ore, timed_action_flags = IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(IsAvailable), FALSE), interaction_key = REF(src))) + return + var/obj/item/smelted = new held_ore.refined_type + held_ore.use(1) + target.put_in_hands(smelted) + if (!held_ore) + target.balloon_alert(target, "no ore left!") + return + smelt_speed = max(smelt_speed - speed_up_interval, minimum_smelt_speed) + smelt_held(target) diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index 93be04d6348..0f02f71153f 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -103,12 +103,6 @@ to_chat(ear_owner, span_notice("Your sense of balance comes back to you.")) ear_owner.RemoveElement(/datum/element/waddling) -/obj/item/organ/internal/ears/bronze - name = "tin ears" - desc = "The robust ears of a bronze golem. " - damage_multiplier = 0.1 //STRONK - bang_protect = 1 //Fear me weaklings. - /obj/item/organ/internal/ears/cybernetic name = "cybernetic ears" icon_state = "ears-c" diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 262de707378..c5e8b6e803e 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -125,6 +125,8 @@ if(!istype(parent) || parent.get_organ_by_type(/obj/item/organ/internal/eyes) != src) CRASH("Generating a body overlay for [src] targeting an invalid parent '[parent]'.") + if(isnull(eye_icon_state)) + return list() var/eye_icon = parent.dna?.species.eyes_icon || 'icons/mob/species/human/human_face.dmi' // SKYRAT EDIT ADDITION var/mutable_appearance/eye_left = mutable_appearance(eye_icon, "[eye_icon_state]_l", -eyes_layer) // SKYRAT EDIT CHANGE - Customization - ORIGINAL: var/mutable_appearance/eye_left = mutable_appearance('icons/mob/human_face.dmi', "[eye_icon_state]_l", -BODY_LAYER) @@ -140,11 +142,10 @@ eye_left.overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, parent, alpha = eye_left.alpha) eye_right.overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, parent, alpha = eye_right.alpha) - if(OFFSET_FACE in parent.dna?.species.offset_features) - var/offset = parent.dna.species.offset_features[OFFSET_FACE] + var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD) + if(my_head?.worn_face_offset) for(var/mutable_appearance/overlay in overlays) - overlay.pixel_x += offset[OFFSET_X] - overlay.pixel_y += offset[OFFSET_Y] + my_head.worn_face_offset.apply_offset(overlay) // SKYRAT EDIT START - Customization (darn synths I swear) if(eye_icon_state == "None") @@ -266,6 +267,30 @@ sight_flags = SEE_MOBS color_cutoffs = list(25, 5, 42) +/obj/item/organ/internal/eyes/golem + name = "resonating crystal" + icon_state = "adamantine_cords" + eye_icon_state = null + desc = "Golems somehow measure external light levels and detect nearby ore using this sensitive mineral lattice." + color = COLOR_GOLEM_GRAY + visual = FALSE + status = ORGAN_MINERAL + color_cutoffs = list(10, 15, 5) + actions_types = list(/datum/action/cooldown/golem_ore_sight) + +/// Send an ore detection pulse on a cooldown +/datum/action/cooldown/golem_ore_sight + name = "Ore Resonance" + desc = "Causes nearby ores to vibrate, revealing their location." + button_icon = 'icons/obj/device.dmi' + button_icon_state = "manual_mining" + check_flags = AB_CHECK_CONSCIOUS + cooldown_time = 10 SECONDS + +/datum/action/cooldown/golem_ore_sight/Activate(atom/target) + . = ..() + mineral_scan_pulse(get_turf(target)) + ///Robotic /obj/item/organ/internal/eyes/robotic diff --git a/code/modules/surgery/organs/stomach/_stomach.dm b/code/modules/surgery/organs/stomach/_stomach.dm index 98b662e31c1..345027ef5d7 100644 --- a/code/modules/surgery/organs/stomach/_stomach.dm +++ b/code/modules/surgery/organs/stomach/_stomach.dm @@ -158,7 +158,7 @@ human.adjust_nutrition(-hunger_rate * seconds_per_tick) var/nutrition = human.nutrition - if(nutrition > NUTRITION_LEVEL_FULL) + if(nutrition > NUTRITION_LEVEL_FULL && !HAS_TRAIT(human, TRAIT_NOFAT)) if(human.overeatduration < 20 MINUTES) //capped so people don't take forever to unfat human.overeatduration = min(human.overeatduration + (1 SECONDS * seconds_per_tick), 20 MINUTES) else diff --git a/code/modules/surgery/organs/stomach/stomach_golem.dm b/code/modules/surgery/organs/stomach/stomach_golem.dm new file mode 100644 index 00000000000..9a085a5bbfb --- /dev/null +++ b/code/modules/surgery/organs/stomach/stomach_golem.dm @@ -0,0 +1,98 @@ +/obj/item/organ/internal/stomach/golem + name = "silicate grinder" + icon_state = "stomach-p" + desc = "A rocklike organ which grinds and processes nutrition from minerals." + color = COLOR_GOLEM_GRAY + status = ORGAN_MINERAL + organ_traits = list(TRAIT_ROCK_EATER) + /// Multiplier for the hunger rate, golems burn fuel quickly + var/hunger_mod = 10 + /// How slow are you when the "hungry" icon appears? + var/min_hunger_slowdown = 0.5 + /// How slow are you if you have absolutely nothing in the tank? + var/max_hunger_slowdown = 4 + +/obj/item/organ/internal/stomach/golem/on_insert(mob/living/carbon/organ_owner, special) + . = ..() + RegisterSignal(owner, COMSIG_CARBON_ATTEMPT_EAT, PROC_REF(try_eating)) + if (!ishuman(organ_owner)) + return + if (organ_owner.flags_1 & INITIALIZED_1) + setup_physiology(organ_owner) + else + RegisterSignal(owner, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE, PROC_REF(setup_physiology)) + +/// Physiology doesn't exist yet if this is added on initialisation of a golem, so we need to wait until it does +/obj/item/organ/internal/stomach/golem/proc/setup_physiology(mob/living/carbon/human/human_owner) + SIGNAL_HANDLER + human_owner.physiology?.hunger_mod *= hunger_mod + UnregisterSignal(human_owner, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE) + +/obj/item/organ/internal/stomach/golem/on_remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_CARBON_ATTEMPT_EAT) + organ_owner.remove_movespeed_modifier(/datum/movespeed_modifier/golem_hunger) + organ_owner.remove_status_effect(/datum/status_effect/golem_statued) + if (!ishuman(organ_owner)) + return + var/mob/living/carbon/human/human_owner = organ_owner + human_owner.physiology?.hunger_mod /= hunger_mod + +/// Reject food, rocks only +/obj/item/organ/internal/stomach/golem/proc/try_eating(mob/living/carbon/source, atom/eating) + SIGNAL_HANDLER + if (istype(eating, /obj/item/food/golem_food)) + return + source.balloon_alert(source, "minerals only!") + return COMSIG_CARBON_BLOCK_EAT + +/// Golem stomach cannot process nutriment except from minerals +/obj/item/organ/internal/stomach/golem/on_life(delta_time, times_fired) + for(var/datum/reagent/consumable/food in reagents.reagent_list) + if (istype(food, /datum/reagent/consumable/nutriment/mineral)) + continue + food.nutriment_factor = 0 + return ..() + +/// Slow down based on how full you are +/obj/item/organ/internal/stomach/golem/handle_hunger(mob/living/carbon/human/human, delta_time, times_fired) + if(HAS_TRAIT(human, TRAIT_NOHUNGER)) + return + . = ..() + var/hunger = (NUTRITION_LEVEL_HUNGRY - human.nutrition) / NUTRITION_LEVEL_HUNGRY // starving = 1, satisfied = 0 + if(hunger > 0) + var/slowdown = LERP(min_hunger_slowdown, max_hunger_slowdown, hunger) + human.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/golem_hunger, multiplicative_slowdown = slowdown) + else + human.remove_movespeed_modifier(/datum/movespeed_modifier/golem_hunger) + + if (hunger >= 1) + human.apply_status_effect(/datum/status_effect/golem_statued) + else + human.remove_status_effect(/datum/status_effect/golem_statued) + +/// Uh oh, you can't move, yell for help +/datum/status_effect/golem_statued + id = "golem_statued" + alert_type = /atom/movable/screen/alert/status_effect/golem_statued + +/atom/movable/screen/alert/status_effect/golem_statued + name = "Statued" + desc = "You no longer have the energy to move your body!" + icon_state = "golem_statued" + +/datum/status_effect/golem_statued/on_apply() + . = ..() + if (!.) + return FALSE + owner.visible_message(span_warning("[owner] siezes up and becomes as rigid as a statue!"), span_warning("Your limbs fall still. You no longer have enough energy to move!")) + owner.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_FORCED_STANDING, TRAIT_HANDS_BLOCKED, TRAIT_INCAPACITATED), TRAIT_STATUS_EFFECT(id)) + return TRUE + +/datum/status_effect/golem_statued/get_examine_text() + return span_warning("[owner.p_they(TRUE)] are as still as a statue!") + +/datum/status_effect/golem_statued/on_remove() + owner.visible_message(span_notice("[owner] slowly stirs back into motion!"), span_notice("You have gathered enough strength to move your body once more.")) + owner.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_FORCED_STANDING, TRAIT_HANDS_BLOCKED, TRAIT_INCAPACITATED), TRAIT_STATUS_EFFECT(id)) + return ..() diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 5e682ae1c75..1ba3260011d 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -469,15 +469,6 @@ GLOBAL_LIST_INIT(english_to_zombie, list()) desc = "A fleshy muscle mostly used for meowing." say_mod = "meows" -/obj/item/organ/internal/tongue/bananium - name = "bananium tongue" - desc = "A bananium geode mostly used for honking." - say_mod = "honks" - icon = 'icons/obj/weapons/horn.dmi' - icon_state = "gold_horn" - lefthand_file = 'icons/mob/inhands/equipment/horns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/horns_righthand.dmi' - /obj/item/organ/internal/tongue/jelly name = "jelly tongue" desc = "Ah... That's not the sound I expected it to make. Sounds like a Space Autumn Bird." @@ -505,3 +496,11 @@ GLOBAL_LIST_INIT(english_to_zombie, list()) icon = 'icons/obj/hydroponics/seeds.dmi' icon_state = "mycelium-angel" + +/obj/item/organ/internal/tongue/golem + name = "golem tongue" + color = COLOR_WEBSAFE_DARK_GRAY + desc = "This silicate plate doesn't seem particularly mobile, but golems use it to form sounds." + say_mod = "rumbles" + sense_of_taste = FALSE + status = ORGAN_MINERAL diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 68f6e758cbf..9183c7eb809 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -18,41 +18,6 @@ /obj/item/organ/internal/vocal_cords/proc/handle_speech(message) //actually say the message owner.say(message, spans = spans, sanitize = FALSE) -/obj/item/organ/internal/adamantine_resonator - visual = FALSE - name = "adamantine resonator" - desc = "Fragments of adamantine exist in all golems, stemming from their origins as purely magical constructs. These are used to \"hear\" messages from their leaders." - zone = BODY_ZONE_HEAD - slot = ORGAN_SLOT_ADAMANTINE_RESONATOR - icon_state = "adamantine_resonator" - -/obj/item/organ/internal/vocal_cords/adamantine - name = "adamantine vocal cords" - desc = "When adamantine resonates, it causes all nearby pieces of adamantine to resonate as well. Adamantine golems use this to broadcast messages to nearby golems." - actions_types = list(/datum/action/item_action/organ_action/use/adamantine_vocal_cords) - icon_state = "adamantine_cords" - -/datum/action/item_action/organ_action/use/adamantine_vocal_cords/Trigger(trigger_flags) - if(!IsAvailable(feedback = TRUE)) - return - var/message = tgui_input_text(owner, "Resonate a message to all nearby golems", "Resonate") - if(!message) - return - if(QDELETED(src) || QDELETED(owner)) - return - owner.say(".x[message]") - -/obj/item/organ/internal/vocal_cords/adamantine/handle_speech(message) - var/msg = span_resonate(span_name("[owner.real_name] resonates, \"[message]\"")) - for(var/player in GLOB.player_list) - if(iscarbon(player)) - var/mob/living/carbon/speaker = player - if(speaker.get_organ_slot(ORGAN_SLOT_ADAMANTINE_RESONATOR)) - to_chat(speaker, msg) - if(isobserver(player)) - var/link = FOLLOW_LINK(player, owner) - to_chat(player, "[link] [msg]") - //Colossus drop, forces the listeners to obey certain commands /obj/item/organ/internal/vocal_cords/colossus name = "divine vocal cords" @@ -120,3 +85,38 @@ /obj/item/organ/internal/vocal_cords/colossus/speak_with(message) var/cooldown = voice_of_god(uppertext(message), owner, spans, base_multiplier) next_command = world.time + (cooldown * cooldown_mod) + +/obj/item/organ/internal/adamantine_resonator + visual = FALSE + name = "adamantine resonator" + desc = "Fragments of adamantine exist in all golems, stemming from their origins as purely magical constructs. These are used to \"hear\" messages from their leaders." + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_ADAMANTINE_RESONATOR + icon_state = "adamantine_resonator" + +/obj/item/organ/internal/vocal_cords/adamantine + name = "adamantine vocal cords" + desc = "When adamantine resonates, it causes all nearby pieces of adamantine to resonate as well. Golems containing these formations use this to broadcast messages to nearby golems." + actions_types = list(/datum/action/item_action/organ_action/use/adamantine_vocal_cords) + icon_state = "adamantine_cords" + +/datum/action/item_action/organ_action/use/adamantine_vocal_cords/Trigger(trigger_flags) + if(!IsAvailable(feedback = TRUE)) + return + var/message = tgui_input_text(owner, "Resonate a message to all nearby golems", "Resonate") + if(!message) + return + if(QDELETED(src) || QDELETED(owner)) + return + owner.say(".x[message]") + +/obj/item/organ/internal/vocal_cords/adamantine/handle_speech(message) + var/msg = span_resonate("[span_name("[owner.real_name]")] resonates, \"[message]\"") + for(var/player in GLOB.player_list) + if(iscarbon(player)) + var/mob/living/carbon/speaker = player + if(speaker.get_organ_slot(ORGAN_SLOT_ADAMANTINE_RESONATOR)) + to_chat(speaker, msg) + if(isobserver(player)) + var/link = FOLLOW_LINK(player, owner) + to_chat(player, "[link] [msg]") diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_blobinfection.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_blobinfection.png index e3d7acbf5d8..6f0663e347f 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_blobinfection.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_blobinfection.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_sentiencepotionspawn.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_sentiencepotionspawn.png index 02cdb1cb31a..2e21dd7d409 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_sentiencepotionspawn.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_sentiencepotionspawn.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem.png index 1f5e06af140..b6ceaeaec20 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_adamantine.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_adamantine.png deleted file mode 100644 index e3ad49b18ad..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_adamantine.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_alloy.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_alloy.png deleted file mode 100644 index fb6935569cb..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_alloy.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bananium.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bananium.png deleted file mode 100644 index 341d03e2327..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bananium.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bluespace.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bluespace.png deleted file mode 100644 index 5f172fa8ab5..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bluespace.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bone.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bone.png deleted file mode 100644 index 4dc3eb2ae28..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bone.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bronze.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bronze.png deleted file mode 100644 index b62d312616a..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_bronze.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_cardboard.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_cardboard.png deleted file mode 100644 index da0e0d65240..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_cardboard.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_cloth.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_cloth.png deleted file mode 100644 index f66a531659e..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_cloth.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_diamond.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_diamond.png deleted file mode 100644 index f5c99146a68..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_diamond.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_durathread.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_durathread.png deleted file mode 100644 index 7c968e546c6..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_durathread.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_glass.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_glass.png deleted file mode 100644 index f9a2ace1ed0..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_glass.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_gold.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_gold.png deleted file mode 100644 index 093d50727c0..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_gold.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_leather.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_leather.png deleted file mode 100644 index 849c63191e5..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_leather.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_mhydrogen.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_mhydrogen.png deleted file mode 100644 index d41b7fdb91e..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_mhydrogen.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plasma.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plasma.png deleted file mode 100644 index 7cb8a3cf45d..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plasma.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plasteel.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plasteel.png deleted file mode 100644 index 4ce70ac48c3..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plasteel.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plastic.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plastic.png deleted file mode 100644 index 2dfec2d7a84..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plastic.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plastitanium.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plastitanium.png deleted file mode 100644 index 5dc6ac923bf..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_plastitanium.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_runic.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_runic.png deleted file mode 100644 index 1f284cf0d73..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_runic.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_sand.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_sand.png deleted file mode 100644 index 9cf198e2ae5..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_sand.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_silver.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_silver.png deleted file mode 100644 index 3e7755fe787..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_silver.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_snow.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_snow.png deleted file mode 100644 index 667766c2525..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_snow.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_titanium.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_titanium.png deleted file mode 100644 index 2dfec2d7a84..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_titanium.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_uranium.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_uranium.png deleted file mode 100644 index ccc87493421..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_uranium.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_wood.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_wood.png deleted file mode 100644 index fe7cc0e4bbc..00000000000 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_golem_wood.png and /dev/null differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png index aec29b7c0c8..453f283728e 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png differ diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi index 24fa2193e2b..76b22abecde 100755 Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ diff --git a/icons/mob/inhands/golem_lefthand.dmi b/icons/mob/inhands/golem_lefthand.dmi new file mode 100644 index 00000000000..71dad57145e Binary files /dev/null and b/icons/mob/inhands/golem_lefthand.dmi differ diff --git a/icons/mob/inhands/golem_righthand.dmi b/icons/mob/inhands/golem_righthand.dmi new file mode 100644 index 00000000000..b61d560b4e6 Binary files /dev/null and b/icons/mob/inhands/golem_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/guns_righthand.dmi b/icons/mob/inhands/weapons/guns_righthand.dmi index 89a9d42f280..2aca6d31727 100644 Binary files a/icons/mob/inhands/weapons/guns_righthand.dmi and b/icons/mob/inhands/weapons/guns_righthand.dmi differ diff --git a/icons/mob/species/golems.dmi b/icons/mob/species/golems.dmi new file mode 100644 index 00000000000..c29fb38be43 Binary files /dev/null and b/icons/mob/species/golems.dmi differ diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi index 6b8309b9038..ac99107f393 100644 Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ diff --git a/modular_skyrat/modules/bodyparts/code/ghoul_bodyparts.dm b/modular_skyrat/modules/bodyparts/code/ghoul_bodyparts.dm index 0c16b363fa3..37a9cc982df 100644 --- a/modular_skyrat/modules/bodyparts/code/ghoul_bodyparts.dm +++ b/modular_skyrat/modules/bodyparts/code/ghoul_bodyparts.dm @@ -4,11 +4,47 @@ is_dimorphic = FALSE limb_id = SPECIES_GHOUL +/obj/item/bodypart/head/mutant/ghoul/Initialize(mapload) + worn_ears_offset = new( + attached_part = src, + feature_key = OFFSET_EARS, + offset_y = list("north" = 1, "south" = 1, "east" = 1, "west" = 1), + ) + worn_head_offset = new( + attached_part = src, + feature_key = OFFSET_HEAD, + offset_x = list("north" = 1, "south" = 1, "east" = 1, "west" = 1), + ) + worn_mask_offset = new( + attached_part = src, + feature_key = OFFSET_FACEMASK, + offset_y = list("north" = 1, "south" = 1, "east" = 1, "west" = 1), + ) + worn_glasses_offset = new( + attached_part = src, + feature_key = OFFSET_GLASSES, + offset_y = list("north" = 1, "south" = 1, "east" = 1, "west" = 1), + ) + worn_face_offset = new( + attached_part = src, + feature_key = OFFSET_FACE, + offset_y = list("north" = 1, "south" = 1, "east" = 1, "west" = 1), + ) + return ..() + /obj/item/bodypart/chest/mutant/ghoul icon_greyscale = BODYPART_ICON_GHOUL limb_id = SPECIES_GHOUL is_dimorphic = FALSE +/obj/item/bodypart/chest/mutant/ghoul/Initialize(mapload) + worn_neck_offset = new( + attached_part = src, + feature_key = OFFSET_NECK, + offset_y = list("north" = 1, "south" = 1, "east" = 1, "west" = 1), + ) + return ..() + /obj/item/bodypart/arm/left/mutant/ghoul icon_greyscale = BODYPART_ICON_GHOUL limb_id = SPECIES_GHOUL @@ -20,6 +56,8 @@ unarmed_miss_sound = 'sound/weapons/punchmiss.ogg' + + /obj/item/bodypart/arm/right/mutant/ghoul icon_greyscale = BODYPART_ICON_GHOUL limb_id = SPECIES_GHOUL diff --git a/modular_skyrat/modules/bodyparts/code/parts.dm b/modular_skyrat/modules/bodyparts/code/parts.dm new file mode 100644 index 00000000000..af75cc9a032 --- /dev/null +++ b/modular_skyrat/modules/bodyparts/code/parts.dm @@ -0,0 +1,7 @@ +/obj/item/bodypart/chest + /// The offset datum for our accessory overlay. + var/datum/worn_feature_offset/worn_accessory_offset + +/obj/item/bodypart/chest/Destroy(force) + QDEL_NULL(worn_accessory_offset) + return ..() diff --git a/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm b/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm index 1be2e49268a..800cc978cc1 100644 --- a/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm +++ b/modular_skyrat/modules/bodyparts/code/teshari_bodyparts.dm @@ -7,11 +7,45 @@ bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC | BODYTYPE_CUSTOM limb_id = SPECIES_TESHARI +/obj/item/bodypart/head/mutant/teshari/Initialize(mapload) + worn_ears_offset = new( + attached_part = src, + feature_key = OFFSET_EARS, + offset_y = list("north" = -4, "south" = -4, "east" = -4, "west" = -4), + ) + worn_head_offset = new( + attached_part = src, + feature_key = OFFSET_HEAD, + offset_x = list("north" = 1, "south" = 1, "east" = 1, "west" = 1), + offset_y = list("north" = -4, "south" = -4, "east" = -4, "west" = -4), + ) + worn_mask_offset = new( + attached_part = src, + feature_key = OFFSET_FACEMASK, + offset_y = list("north" = -5, "south" = -5, "east" = -5, "west" = -5), + ) + return ..() + + /obj/item/bodypart/chest/mutant/teshari icon_greyscale = BODYPART_ICON_TESHARI bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC | BODYTYPE_CUSTOM limb_id = SPECIES_TESHARI +/obj/item/bodypart/chest/mutant/teshari/Initialize(mapload) + worn_back_offset = new( + attached_part = src, + feature_key = OFFSET_BACK, + offset_y = list("north" = -4, "south" = -4, "east" = -4, "west" = -4), + ) + worn_accessory_offset = new( + attached_part = src, + feature_key = OFFSET_ACCESSORY, + offset_y = list("north" = -4, "south" = -4, "east" = -4, "west" = -4), + ) + return ..() + + /obj/item/bodypart/arm/left/mutant/teshari icon_greyscale = BODYPART_ICON_TESHARI bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC | BODYTYPE_CUSTOM diff --git a/modular_skyrat/modules/customization/modules/clothing/masks/gasmask.dm b/modular_skyrat/modules/customization/modules/clothing/masks/gasmask.dm index 75bd6b05d4a..9174700e738 100644 --- a/modular_skyrat/modules/customization/modules/clothing/masks/gasmask.dm +++ b/modular_skyrat/modules/customization/modules/clothing/masks/gasmask.dm @@ -55,7 +55,6 @@ clothing_flags = MASKINTERNALS flags_cover = MASKCOVERSEYES resistance_flags = FLAMMABLE - species_exception = list(/datum/species/golem/bananium) has_fov = FALSE greyscale_config = /datum/greyscale_config/clown_mask greyscale_config_worn = /datum/greyscale_config/clown_mask/worn @@ -72,7 +71,6 @@ inhand_icon_state = null flags_cover = MASKCOVERSEYES resistance_flags = FLAMMABLE - species_exception = list(/datum/species/golem/bananium) /obj/item/clothing/mask/gas/respirator name = "half mask respirator" diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/custom_bodytype.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/custom_bodytype.dm index 0639d1fbcdf..f3ce6b8b109 100644 --- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/custom_bodytype.dm +++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/custom_bodytype.dm @@ -57,8 +57,9 @@ * Arguments: * * item_slot: The slot we're updating. One of LOADOUT_ITEM_HEAD, etc. * * item: The item being rendered. + * * human_owner: The human wearing the item. */ -/datum/species/proc/generate_custom_worn_icon(item_slot, obj/item/item) +/datum/species/proc/generate_custom_worn_icon(item_slot, obj/item/item, mob/living/carbon/human/human_owner) // If already set (possibly by us, or manually, use it.) var/icon/final_icon = get_custom_worn_icon(item_slot, item) if(final_icon && icon_exists(final_icon, item.worn_icon_state || item.icon_state)) @@ -82,7 +83,7 @@ /** * Generate a fallback worn icon, if the species supports it. You must call it in an override of generate_custom_worn_icon() */ -/datum/species/proc/generate_custom_worn_icon_fallback(item_slot, obj/item/item) +/datum/species/proc/generate_custom_worn_icon_fallback(item_slot, obj/item/item, mob/living/carbon/human/human_owner) var/icon/human_icon = item.worn_icon || item.icon var/human_icon_state = item.worn_icon_state || item.icon_state diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm index 926667b8acb..efa851a5431 100644 --- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm +++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species.dm @@ -105,16 +105,14 @@ GLOBAL_LIST_EMPTY(customizable_races) var/height_offset = species_human.get_top_offset() // From high changed by varying limb height var/list/standing = list() - var/obj/item/bodypart/head/HD = species_human.get_bodypart(BODY_ZONE_HEAD) + var/obj/item/bodypart/head/noggin = species_human.get_bodypart(BODY_ZONE_HEAD) - if(HD && !(HAS_TRAIT(species_human, TRAIT_HUSK))) + if(noggin && !(HAS_TRAIT(species_human, TRAIT_HUSK))) // lipstick if(species_human.lip_style && (LIPS in species_traits)) var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/species/human/human_face.dmi', "lips_[species_human.lip_style]", -BODY_LAYER) lip_overlay.color = species_human.lip_color - if(OFFSET_FACE in species_human.dna.species.offset_features) - lip_overlay.pixel_x += species_human.dna.species.offset_features[OFFSET_FACE][1] - lip_overlay.pixel_y += species_human.dna.species.offset_features[OFFSET_FACE][2] + noggin.worn_face_offset?.apply_offset(lip_overlay) lip_overlay.pixel_y += height_offset standing += lip_overlay @@ -122,17 +120,15 @@ GLOBAL_LIST_EMPTY(customizable_races) if(!(NOEYESPRITES in species_traits)) var/obj/item/organ/internal/eyes/eye_organ = species_human.get_organ_slot(ORGAN_SLOT_EYES) var/mutable_appearance/no_eyeslay - var/add_pixel_x = 0 var/add_pixel_y = 0 //cut any possible vis overlays - if(length(body_vis_overlays)) + if(body_vis_overlays.len) SSvis_overlays.remove_vis_overlay(species_human, body_vis_overlays) - - if(OFFSET_FACE in species_human.dna.species.offset_features) - add_pixel_x = species_human.dna.species.offset_features[OFFSET_FACE][1] - add_pixel_y = species_human.dna.species.offset_features[OFFSET_FACE][2] - + var/list/feature_offset = noggin.worn_face_offset?.get_offset() + if(feature_offset) + add_pixel_x = feature_offset["x"] + add_pixel_y = feature_offset["y"] add_pixel_y += height_offset if(!eye_organ) @@ -149,8 +145,8 @@ GLOBAL_LIST_EMPTY(customizable_races) standing += eye_overlay if(eye_organ.is_emissive) var/mutable_appearance/eye_emissive = emissive_appearance_copy(eye_overlay, species_human) - eye_emissive.pixel_x += species_human.dna.species.offset_features[OFFSET_FACE][1] - eye_emissive.pixel_y += species_human.dna.species.offset_features[OFFSET_FACE][2] + eye_emissive.pixel_x += add_pixel_x + eye_emissive.pixel_y += add_pixel_y standing += eye_emissive //Underwear, Undershirts & Socks diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm index 6943d5381b2..e30f1138bfe 100644 --- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm +++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/ghoul.dm @@ -24,22 +24,6 @@ TRAIT_EASILY_WOUNDED, //theyre like fuckin skin and bones TRAIT_LITERATE, ) - offset_features = list( - OFFSET_UNIFORM = list(0,0), - OFFSET_ID = list(0,0), - OFFSET_GLOVES = list(0,0), - OFFSET_GLASSES = list(0,1), - OFFSET_EARS = list(0,1), - OFFSET_SHOES = list(0,0), - OFFSET_S_STORE = list(0,0), - OFFSET_FACEMASK = list(0,1), - OFFSET_HEAD = list(0,1), - OFFSET_FACE = list(0,1), - OFFSET_BELT = list(0,0), - OFFSET_BACK = list(0,0), - OFFSET_SUIT = list(0,0), - OFFSET_NECK = list(0,1), - ) toxic_food = DAIRY | PINEAPPLE disliked_food = VEGETABLES | FRUIT | CLOTH liked_food = RAW | MEAT diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/worn_overlays.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/worn_overlays.dm index 91660ed9367..a35a261439a 100644 --- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/worn_overlays.dm +++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/worn_overlays.dm @@ -16,12 +16,7 @@ // Make new appearance so we don't break the real accessory overlay for other species, and treat it as final. return mutable_appearance(custom_accessory_icon, accessory_overlay.icon_state) - // Apply an offset only if we didn't apply a different appearance. - if(OFFSET_ACCESSORY in human_wearer.dna.species.offset_features) - accessory_overlay.pixel_x = human_wearer.dna.species.offset_features[OFFSET_ACCESSORY][1] - accessory_overlay.pixel_y = human_wearer.dna.species.offset_features[OFFSET_ACCESSORY][2] - else - accessory_overlay.pixel_x = 0 - accessory_overlay.pixel_y = 0 + var/obj/item/bodypart/chest/my_chest = human_wearer.get_bodypart(BODY_ZONE_CHEST) + my_chest?.worn_accessory_offset?.apply_offset(accessory_overlay) return accessory_overlay diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm index 18161d119d0..ce6aa14dfb2 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm @@ -251,9 +251,8 @@ if(!vagina_overlay) vagina_overlay = sex_toy?.build_worn_icon(default_layer = VAGINA_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file) - if(OFFSET_UNIFORM in dna.species.offset_features) - vagina_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1] - vagina_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2] + var/obj/item/bodypart/chest/chest_part = get_bodypart(BODY_ZONE_CHEST) + chest_part?.worn_uniform_offset?.apply_offset(vagina_overlay) // every day we stray further and further from god overlays_standing[VAGINA_LAYER] = vagina_overlay apply_overlay(VAGINA_LAYER) @@ -275,9 +274,9 @@ if(!anus_overlay) anus_overlay = sex_toy?.build_worn_icon(default_layer = ANUS_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file) - if(OFFSET_UNIFORM in dna.species.offset_features) - anus_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1] - anus_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2] + var/obj/item/bodypart/chest/chest_part = get_bodypart(BODY_ZONE_CHEST) + + chest_part?.worn_uniform_offset?.apply_offset(anus_overlay) // and i keep on asking myself... why? why do we do this? overlays_standing[ANUS_LAYER] = anus_overlay apply_overlay(ANUS_LAYER) @@ -299,9 +298,9 @@ if(!nipples_overlay) nipples_overlay = sex_toy?.build_worn_icon(default_layer = NIPPLES_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file) - if(OFFSET_UNIFORM in dna.species.offset_features) - nipples_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1] - nipples_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2] + var/obj/item/bodypart/chest/chest_part = get_bodypart(BODY_ZONE_CHEST) + chest_part?.worn_uniform_offset?.apply_offset(nipples_overlay) // then i realised something, something horrific + overlays_standing[NIPPLES_LAYER] = nipples_overlay apply_overlay(NIPPLES_LAYER) @@ -323,9 +322,9 @@ if(!penis_overlay) penis_overlay = sex_toy?.build_worn_icon(default_layer = PENIS_LAYER, default_icon_file = 'icons/mob/clothing/under/default.dmi', isinhands = FALSE, override_file = icon_file) - if(OFFSET_UNIFORM in dna.species.offset_features) - penis_overlay?.pixel_x += dna.species.offset_features[OFFSET_UNIFORM][1] - penis_overlay?.pixel_y += dna.species.offset_features[OFFSET_UNIFORM][2] + var/obj/item/bodypart/chest/chest_part = get_bodypart(BODY_ZONE_CHEST) + chest_part?.worn_uniform_offset?.apply_offset(penis_overlay) // we can never escape, we are forever governed by sex(two) + overlays_standing[PENIS_LAYER] = penis_overlay apply_overlay(PENIS_LAYER) diff --git a/modular_skyrat/modules/salon/code/misc_items.dm b/modular_skyrat/modules/salon/code/misc_items.dm index f5f4727a719..56b035d29f0 100644 --- a/modular_skyrat/modules/salon/code/misc_items.dm +++ b/modular_skyrat/modules/salon/code/misc_items.dm @@ -20,6 +20,9 @@ to_chat(user, span_warning("Where are the lips on that?")) return + INVOKE_ASYNC(src, PROC_REF(async_set_color), attacked_mob, user) + +/obj/item/lipstick/quantum/proc/async_set_color(mob/attacked_mob, mob/user) var/new_color = input( user, "Select lipstick color", diff --git a/modular_skyrat/modules/teshari/code/_teshari.dm b/modular_skyrat/modules/teshari/code/_teshari.dm index 3a8e8bd358e..ddc5391533e 100644 --- a/modular_skyrat/modules/teshari/code/_teshari.dm +++ b/modular_skyrat/modules/teshari/code/_teshari.dm @@ -42,7 +42,6 @@ LOADOUT_ITEM_ACCESSORY = TESHARI_ACCESSORIES_ICON, LOADOUT_ITEM_EARS = TESHARI_EARS_ICON ) - offset_features = list(OFFSET_UNIFORM = list(0,0), OFFSET_ID = list(0,0), OFFSET_GLOVES = list(0,0), OFFSET_GLASSES = list(0,0), OFFSET_EARS = list(0,-4), OFFSET_SHOES = list(0,0), OFFSET_S_STORE = list(0,0), OFFSET_FACEMASK = list(0,-5), OFFSET_HEAD = list(1,-4), OFFSET_FACE = list(0,0), OFFSET_BELT = list(0,0), OFFSET_BACK = list(0,-4), OFFSET_SUIT = list(0,0), OFFSET_NECK = list(0,0), OFFSET_ACCESSORY = list(0, -4)) coldmod = TESHARI_COLDMOD heatmod = TESHARI_HEATMOD brutemod = TESHARI_BRUTEMOD diff --git a/modular_skyrat/modules/teshari/code/teshari_bodytype.dm b/modular_skyrat/modules/teshari/code/teshari_bodytype.dm index db0882c55aa..1cefbd6dc55 100644 --- a/modular_skyrat/modules/teshari/code/teshari_bodytype.dm +++ b/modular_skyrat/modules/teshari/code/teshari_bodytype.dm @@ -18,30 +18,29 @@ return item.greyscale_config_worn_teshari_fallback -/datum/species/teshari/generate_custom_worn_icon(item_slot, obj/item/item) +/datum/species/teshari/generate_custom_worn_icon(item_slot, obj/item/item, mob/living/carbon/human/human_owner) . = ..() if(.) return // Use the fancy fallback sprites. - . = generate_custom_worn_icon_fallback(item_slot, item) + . = generate_custom_worn_icon_fallback(item_slot, item, human_owner) if(.) return // If there isn't even a fallback, use snouted sprites for masks and helmets, but offsetted if((item_slot == LOADOUT_ITEM_MASK || item_slot == LOADOUT_ITEM_HEAD) && (item.supports_variations_flags & CLOTHING_SNOUTED_VARIATION)) + var/obj/item/bodypart/head/my_head = human_owner.get_bodypart(BODY_ZONE_HEAD) + var/datum/worn_feature_offset/selected_offset var/human_icon - var/offset_x - var/offset_y var/human_icon_state = item.worn_icon_state || item.icon_state if(item_slot == LOADOUT_ITEM_HEAD) + human_icon = item.worn_icon_muzzled || SNOUTED_HEAD_FILE - offset_x = offset_features[OFFSET_HEAD][1] - offset_y = offset_features[OFFSET_HEAD][2] + selected_offset = my_head?.worn_head_offset else human_icon = item.worn_icon_muzzled || SNOUTED_MASK_FILE - offset_x = offset_features[OFFSET_FACEMASK][1] + MASK_SNOUT_EXTRA_OFFSET_X - offset_y = offset_features[OFFSET_FACEMASK][2] + MASK_SNOUT_EXTRA_OFFSET_Y + selected_offset = my_head?.worn_mask_offset // Did the snout variation flag lie to us? if(!icon_exists(human_icon, human_icon_state)) @@ -55,7 +54,8 @@ // Generate muzzled icon, but offset var/icon/new_icon = icon('icons/blanks/32x32.dmi', "nothing") - new_icon.Blend( icon(human_icon, human_icon_state), ICON_OVERLAY, x = offset_x, y = offset_y) + selected_offset?.apply_offset(new_icon) + new_icon.Blend(icon(human_icon, human_icon_state), ICON_OVERLAY) new_icon.Insert(new_icon, human_icon_state) set_custom_worn_icon_cached(human_icon, human_icon_state, "m", new_icon) return new_icon diff --git a/tgstation.dme b/tgstation.dme index 02a16a22a8c..674b1313253 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1145,6 +1145,7 @@ #include "code\datums\components\fantasy\suffixes.dm" #include "code\datums\components\food\decomposition.dm" #include "code\datums\components\food\edible.dm" +#include "code\datums\components\food\golem_food.dm" #include "code\datums\components\food\ice_cream_holder.dm" #include "code\datums\components\pet_commands\fetch.dm" #include "code\datums\components\pet_commands\obeys_commands.dm" @@ -2147,6 +2148,9 @@ #include "code\game\objects\items\stacks\telecrystal.dm" #include "code\game\objects\items\stacks\tickets.dm" #include "code\game\objects\items\stacks\wrap.dm" +#include "code\game\objects\items\stacks\golem_food\golem_food_buff.dm" +#include "code\game\objects\items\stacks\golem_food\golem_hand_actions.dm" +#include "code\game\objects\items\stacks\golem_food\golem_status_effects.dm" #include "code\game\objects\items\stacks\sheets\glass.dm" #include "code\game\objects\items\stacks\sheets\hot_ice.dm" #include "code\game\objects\items\stacks\sheets\leather.dm" @@ -5039,6 +5043,7 @@ #include "code\modules\surgery\bodyparts\helpers.dm" #include "code\modules\surgery\bodyparts\parts.dm" #include "code\modules\surgery\bodyparts\robot_bodyparts.dm" +#include "code\modules\surgery\bodyparts\worn_feature_offset.dm" #include "code\modules\surgery\bodyparts\wounds.dm" #include "code\modules\surgery\bodyparts\species_parts\android_parts.dm" #include "code\modules\surgery\bodyparts\species_parts\ethereal_bodyparts.dm" @@ -5048,6 +5053,7 @@ #include "code\modules\surgery\bodyparts\species_parts\plasmaman_bodyparts.dm" #include "code\modules\surgery\organs\_organ.dm" #include "code\modules\surgery\organs\appendix.dm" +#include "code\modules\surgery\organs\appendix_golem.dm" #include "code\modules\surgery\organs\augments_arms.dm" #include "code\modules\surgery\organs\augments_chest.dm" #include "code\modules\surgery\organs\augments_eyes.dm" @@ -5071,6 +5077,7 @@ #include "code\modules\surgery\organs\external\wings\wings.dm" #include "code\modules\surgery\organs\stomach\_stomach.dm" #include "code\modules\surgery\organs\stomach\stomach_ethereal.dm" +#include "code\modules\surgery\organs\stomach\stomach_golem.dm" #include "code\modules\tgchat\message.dm" #include "code\modules\tgchat\to_chat.dm" #include "code\modules\tgs\includes.dm" @@ -5820,6 +5827,7 @@ #include "modular_skyrat\modules\bodyparts\code\luminescent_bodyparts.dm" #include "modular_skyrat\modules\bodyparts\code\moth_bodyparts.dm" #include "modular_skyrat\modules\bodyparts\code\mushperson_bodyparts.dm" +#include "modular_skyrat\modules\bodyparts\code\parts.dm" #include "modular_skyrat\modules\bodyparts\code\podperson_bodyparts.dm" #include "modular_skyrat\modules\bodyparts\code\roundstartslime_bodyparts.dm" #include "modular_skyrat\modules\bodyparts\code\skrell_bodyparts.dm" diff --git a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx index a9f7e5e0e3c..fa255709c2f 100644 --- a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx +++ b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx @@ -27,6 +27,7 @@ const TYPE_ICONS = { [Food.Pineapple]: 'apple-alt', [Food.Raw]: 'drumstick-bite', [Food.Seafood]: 'fish', + [Food.Stone]: 'gem', [Food.Sugar]: 'candy-cane', [Food.Toxic]: 'biohazard', [Food.Vegetables]: 'carrot', diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx index afe4166c576..0ba890e18bc 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/SpeciesPage.tsx @@ -19,6 +19,7 @@ const FOOD_ICONS = { [Food.Nuts]: 'seedling', [Food.Raw]: 'drumstick-bite', [Food.Seafood]: 'fish', + [Food.Stone]: 'gem', [Food.Sugar]: 'candy-cane', [Food.Toxic]: 'biohazard', [Food.Vegetables]: 'carrot', @@ -38,6 +39,7 @@ const FOOD_NAMES: Record = { [Food.Nuts]: 'Nuts', [Food.Raw]: 'Raw', [Food.Seafood]: 'Seafood', + [Food.Stone]: 'Rocks', [Food.Sugar]: 'Sugar', [Food.Toxic]: 'Toxic food', [Food.Vegetables]: 'Vegetables', diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts index 3d6d5734316..5f3f2bfbf73 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts @@ -20,6 +20,7 @@ export enum Food { Pineapple = 'PINEAPPLE', Raw = 'RAW', Seafood = 'SEAFOOD', + Stone = 'STONE', Sugar = 'SUGAR', Toxic = 'TOXIC', Vegetables = 'VEGETABLES',