diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index abaa2224029..41a9268e0aa 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -4,6 +4,8 @@ #define FISHING_RANDOM_SEED "Random seed" ///Used in the surgery fishing spot to define a random organ reward #define FISHING_RANDOM_ORGAN "Random organ" +///Used in the dimensional rift fishing spot to define influence gain +#define FISHING_INFLUENCE "Influence" // Baseline fishing difficulty levels #define FISHING_DEFAULT_DIFFICULTY 15 @@ -272,6 +274,8 @@ #define FISH_SOURCE_FLAG_NO_BLUESPACE_ROD (1<<1) /// When examined by someone with enough fishing skill, this will also display fish that doesn't have FISH_FLAG_SHOW_IN_CATALOG #define FISH_SOURCE_FLAG_IGNORE_HIDDEN_ON_CATALOG (1<<2) +/// This fish source will not spawn fish on explosions +#define FISH_SOURCE_FLAG_EXPLOSIVE_NONE (1<<3) /** * A macro to ensure the wikimedia filenames of fish icons are unique, especially since there're a couple fish that have diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 8d4010e708c..54c2c3d80db 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -806,6 +806,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ROD_ATTRACT_SHINY_LOVERS "rod_attract_shiny_lovers" /// This rod can be used to fish on lava #define TRAIT_ROD_LAVA_USABLE "rod_lava_usable" +/// This rod was infused by a heretic, making it awesome and improving influence gain +#define TRAIT_ROD_MANSUS_INFUSED "rod_infused" /// Stuff that can go inside fish cases and aquariums #define TRAIT_AQUARIUM_CONTENT "aquarium_content" /// If the item can be used as a bit. @@ -1030,6 +1032,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///fish traits #define TRAIT_FISH_STASIS "fish_stasis" #define TRAIT_FISH_FLOPPING "fish_flopping" +#define TRAIT_RESIST_PSYCHIC "resist_psychic" #define TRAIT_RESIST_EMULSIFY "resist_emulsify" #define TRAIT_FISH_SELF_REPRODUCE "fish_self_reproduce" #define TRAIT_FISH_NO_MATING "fish_no_mating" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index a6849756c82..8aa0ae2e11e 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -667,6 +667,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_FISH_QUICK_GROWTH" = TRAIT_FISH_QUICK_GROWTH, "TRAIT_FISH_TOXIN_IMMUNE" = TRAIT_FISH_TOXIN_IMMUNE, "TRAIT_RESIST_EMULSIFY" = TRAIT_RESIST_EMULSIFY, + "TRAIT_RESIST_PSYCHIC" = TRAIT_RESIST_PSYCHIC, "TRAIT_FISH_WELL_COOKED" = TRAIT_FISH_WELL_COOKED, "TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH, ), @@ -675,6 +676,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_ROD_IGNORE_ENVIRONMENT" = TRAIT_ROD_IGNORE_ENVIRONMENT, "TRAIT_ROD_LAVA_USABLE" = TRAIT_ROD_LAVA_USABLE, "TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD, + "TRAIT_ROD_MANSUS_INFUSED" = TRAIT_ROD_MANSUS_INFUSED, ), /obj/item/integrated_circuit = list( "TRAIT_CIRCUIT_UI_OPEN" = TRAIT_CIRCUIT_UI_OPEN, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 78d0ecbe4a7..c781112ba56 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -379,6 +379,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_ROD_IGNORE_ENVIRONMENT" = TRAIT_ROD_IGNORE_ENVIRONMENT, "TRAIT_ROD_LAVA_USABLE" = TRAIT_ROD_LAVA_USABLE, "TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD, + "TRAIT_ROD_MANSUS_INFUSED" = TRAIT_ROD_MANSUS_INFUSED, ), /obj/item/organ/liver = list( "TRAIT_BALLMER_SCIENTIST" = TRAIT_BALLMER_SCIENTIST, diff --git a/code/datums/components/embedded.dm b/code/datums/components/embedded.dm index 92404e3903e..f6ee85c2372 100644 --- a/code/datums/components/embedded.dm +++ b/code/datums/components/embedded.dm @@ -187,6 +187,7 @@ damagetype = STAMINA, ) to_chat(victim, span_userdanger("[weapon] embedded in your [limb.plaintext_zone] jostles and stings!")) + embed_data.jostle_callback?.Invoke(victim, weapon, embed_data) /// Called when then item randomly falls out of a carbon. This handles the damage and descriptors, then calls safe_remove() diff --git a/code/datums/elements/squish.dm b/code/datums/elements/squish.dm index 2e8e273eb35..872f837d96e 100644 --- a/code/datums/elements/squish.dm +++ b/code/datums/elements/squish.dm @@ -6,37 +6,42 @@ * * It's an element that squishes things. After the duration passes, it reverses the transformation it squished with, taking into account if they are a different orientation than they started (read: rotationally-fluid) * - * Normal squishes apply vertically, as if the target is being squished from above, but you can set reverse to TRUE if you want to squish them from the sides, like if they pancake into a wall from the East or West + * Normal squishes apply vertically, as if the target is being squished from above, but you can set horizontal_squish to TRUE if you want to squish them from the sides, like if they pancake into a wall from the East or West */ /datum/element/squish element_flags = ELEMENT_DETACH_ON_HOST_DESTROY -/datum/element/squish/Attach(datum/target, duration=20 SECONDS, reverse=FALSE) +/datum/element/squish/Attach(atom/target, duration=20 SECONDS, horizontal_squish=FALSE) . = ..() - if(!iscarbon(target)) + + if(!isatom(target)) return ELEMENT_INCOMPATIBLE - var/mob/living/carbon/C = target - var/was_lying = C.body_position == LYING_DOWN - addtimer(CALLBACK(src, PROC_REF(Detach), C, was_lying, reverse), duration) + var/was_lying = FALSE + if(iscarbon(target)) + var/mob/living/carbon/carboniucus = target + was_lying = carboniucus.body_position == LYING_DOWN + addtimer(CALLBACK(src, PROC_REF(Detach), target, was_lying, horizontal_squish), duration) - if(reverse) - C.transform = C.transform.Scale(SHORT, TALL) + if(horizontal_squish) + target.transform = target.transform.Scale(SHORT, TALL) else - C.transform = C.transform.Scale(TALL, SHORT) + target.transform = target.transform.Scale(TALL, SHORT) -/datum/element/squish/Detach(mob/living/carbon/C, was_lying, reverse) +/datum/element/squish/Detach(atom/target, was_lying, horizontal_squish) . = ..() - if(istype(C)) - var/is_lying = C.body_position == LYING_DOWN + var/is_lying = FALSE + if(iscarbon(target)) + var/mob/living/carbon/carboniucus = target + is_lying = carboniucus.body_position == LYING_DOWN - if(reverse) - is_lying = !is_lying + if(horizontal_squish) + is_lying = !is_lying - if(was_lying == is_lying) - C.transform = C.transform.Scale(SHORT, TALL) - else - C.transform = C.transform.Scale(TALL, SHORT) + if(was_lying == is_lying) + target.transform = target.transform.Scale(SHORT, TALL) + else + target.transform = target.transform.Scale(TALL, SHORT) #undef SHORT #undef TALL diff --git a/code/datums/embed_data.dm b/code/datums/embed_data.dm index a82d305c8e6..865b285d09b 100644 --- a/code/datums/embed_data.dm +++ b/code/datums/embed_data.dm @@ -35,6 +35,8 @@ GLOBAL_LIST_INIT(embed_by_type, generate_embed_type_cache()) var/jostle_chance = 5 /// Coefficient of multiplication for the damage the item does while var/jostle_pain_mult = 1 + /// Call this proc on jostling, if it exists! + var/datum/callback/jostle_callback /// This percentage of all pain will be dealt as stam damage rather than brute (0-1) var/pain_stam_pct = 0 @@ -51,5 +53,6 @@ GLOBAL_LIST_INIT(embed_by_type, generate_embed_type_cache()) data.ignore_throwspeed_threshold = !isnull(ignore_throwspeed_threshold) ? ignore_throwspeed_threshold : src.ignore_throwspeed_threshold data.jostle_chance = !isnull(jostle_chance) ? jostle_chance : src.jostle_chance data.jostle_pain_mult = !isnull(jostle_pain_mult) ? jostle_pain_mult : src.jostle_pain_mult + data.jostle_callback = !isnull(jostle_callback) ? jostle_callback : src.jostle_callback data.pain_stam_pct = !isnull(pain_stam_pct) ? pain_stam_pct : src.pain_stam_pct return data diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index cdd28c1855e..ca523984d59 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -122,9 +122,14 @@ /datum/mood_event/heretics description = "THE HIGHER I RISE, THE MORE I SEE." - mood_change = 10 //maybe being a cultist isnt that bad after all + mood_change = 10 //maybe being a heretic isnt that bad after all hidden = TRUE +/datum/mood_event/rift_fishing + description = "THE MORE I FISH, THE HIGHER I RISE." + mood_change = 7 + timeout = 5 MINUTES + /datum/mood_event/family_heirloom description = "My family heirloom is safe with me." mood_change = 1 diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm index 494a8d30521..dead537d18d 100644 --- a/code/modules/antagonists/heretic/influences.dm +++ b/code/modules/antagonists/heretic/influences.dm @@ -85,6 +85,7 @@ /obj/effect/visible_heretic_influence/Initialize(mapload) . = ..() addtimer(CALLBACK(src, PROC_REF(show_presence)), 15 SECONDS) + AddComponent(/datum/component/fishing_spot, GLOB.preset_fish_sources[/datum/fish_source/dimensional_rift]) var/image/silicon_image = image('icons/effects/eldritch.dmi', src, null, OBJ_LAYER) silicon_image.override = TRUE @@ -175,6 +176,7 @@ AddElement(/datum/element/block_turf_fingerprints) AddComponent(/datum/component/redirect_attack_hand_from_turf, interact_check = CALLBACK(src, PROC_REF(verify_user_can_see))) + AddComponent(/datum/component/fishing_spot, GLOB.preset_fish_sources[/datum/fish_source/dimensional_rift]) /obj/effect/heretic_influence/proc/verify_user_can_see(mob/user) return (user.mind in GLOB.reality_smash_track.tracked_heretics) diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm index 6fec632e510..4238b54f915 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm @@ -109,9 +109,10 @@ bloodiest_wound.adjust_blood_flow(-0.5 * seconds_between_ticks) /// Torment the target with a frightening hand -/proc/fire_curse_hand(mob/living/carbon/victim) +/proc/fire_curse_hand(mob/living/carbon/victim, turf/forced_turf) var/grab_dir = turn(victim.dir, pick(-90, 90, 180, 180)) // Not in front, favour behind var/turf/spawn_turf = get_ranged_target_turf(victim, grab_dir, 8) + spawn_turf = forced_turf ? forced_turf : spawn_turf if (isnull(spawn_turf)) return new /obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, victim.dir) diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm index 8fd0c5a5713..4973be945af 100644 --- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm @@ -25,6 +25,39 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) cost = 0 is_starting_knowledge = TRUE +// Heretics can enhance their fishing rods to fish better - fishing content. +// Lasts until succesfully fishing something up. +/datum/heretic_knowledge/spell/basic/on_gain(mob/user, datum/antagonist/heretic/our_heretic) + ..() + RegisterSignal(user, COMSIG_TOUCH_HANDLESS_CAST, PROC_REF(on_grasp_cast)) + +/datum/heretic_knowledge/spell/basic/proc/on_grasp_cast(mob/living/carbon/cast_on, datum/action/cooldown/spell/touch/touch_spell) + SIGNAL_HANDLER + + // Not a grasp, we dont want this to activate with say star or mending touch. + if(!istype(touch_spell, action_to_add)) + return NONE + + var/obj/item/fishing_rod/held_rod = cast_on.get_active_held_item() + if(!istype(held_rod, /obj/item/fishing_rod) || HAS_TRAIT(held_rod, TRAIT_ROD_MANSUS_INFUSED)) + return NONE + + INVOKE_ASYNC(cast_on, TYPE_PROC_REF(/atom/movable, say), message = "R'CH T'H F'SH!", forced = "fishing rod infusion invocation") + playsound(cast_on, /datum/action/cooldown/spell/touch/mansus_grasp::sound, 15) + cast_on.visible_message(span_notice("[cast_on] snaps [cast_on.p_their()] fingers next to [held_rod], covering it in a burst of purple flames!")) + + ADD_TRAIT(held_rod, TRAIT_ROD_MANSUS_INFUSED, REF(held_rod)) + held_rod.difficulty_modifier -= 20 + RegisterSignal(held_rod, COMSIG_FISHING_ROD_CAUGHT_FISH, PROC_REF(unfuse)) + held_rod.add_filter("mansus_infusion", 2, list("type" = "outline", "color" = COLOR_VOID_PURPLE, "size" = 1)) + return COMPONENT_CAST_HANDLESS + +/datum/heretic_knowledge/spell/basic/proc/unfuse(obj/item/fishing_rod/item, reward, mob/user) + if(reward == FISHING_INFLUENCE || prob(35)) + item.remove_filter("mansus_infusion") + REMOVE_TRAIT(item, TRAIT_ROD_MANSUS_INFUSED, REF(item)) + item.difficulty_modifier += 20 + /** * The Living Heart heretic knowledge. * diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index e209cc207ea..ab64652adb1 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -386,6 +386,9 @@ bites_amount++ var/bites_to_finish = weight / FISH_WEIGHT_BITE_DIVISOR adjust_health(health - (initial(health) / bites_to_finish) * 3) + flinch_on_eat(eater, feeder) + +/obj/item/fish/proc/flinch_on_eat(mob/living/eater, mob/living/feeder) if(status == FISH_ALIVE && prob(50) && feeder.is_holding(src) && feeder.dropItemToGround(src)) to_chat(feeder, span_warning("[src] slips out of your hands in pain!")) var/turf/target_turf = get_ranged_target_turf(get_turf(src), pick(GLOB.alldirs), 2) @@ -927,6 +930,9 @@ stop_flopping() /obj/item/fish/process(seconds_per_tick) + do_fish_process(seconds_per_tick) + +/obj/item/fish/proc/do_fish_process(seconds_per_tick) if(HAS_TRAIT(src, TRAIT_FISH_STASIS) || status != FISH_ALIVE) return diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index 72110419154..aef5dd583f1 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -73,7 +73,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /// Proc used by both the predator and necrophage traits. /datum/fish_trait/proc/eat_fish(obj/item/fish/predator, obj/item/fish/prey) - var/message = prey.status == FISH_DEAD ? "[src] eats [prey]'s carcass." : "[src] hunts down and eats [prey]." + var/message = prey.status == FISH_DEAD ? "[predator] eats [prey]'s carcass." : "[predator] hunts down and eats [prey]." predator.loc.visible_message(span_warning(message)) SEND_SIGNAL(prey, COMSIG_FISH_EATEN_BY_OTHER_FISH, predator) qdel(prey) @@ -286,11 +286,13 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/emulsijack name = "Emulsifier" catalog_description = "This fish emits an invisible toxin that emulsifies other fish for it to feed on." + var/list/resistance_traits = list(TRAIT_RESIST_EMULSIFY, TRAIT_FISH_TOXIN_IMMUNE) + var/trait_to_add = TRAIT_RESIST_EMULSIFY /datum/fish_trait/emulsijack/apply_to_fish(obj/item/fish/fish) . = ..() RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(emulsify)) - ADD_TRAIT(fish, TRAIT_RESIST_EMULSIFY, FISH_TRAIT_DATUM) + ADD_TRAIT(fish, trait_to_add, FISH_TRAIT_DATUM) /datum/fish_trait/emulsijack/proc/emulsify(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER @@ -298,8 +300,9 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) return var/emulsified = FALSE for(var/obj/item/fish/victim in source.loc) - if(HAS_TRAIT(victim, TRAIT_RESIST_EMULSIFY) || HAS_TRAIT(victim, TRAIT_FISH_TOXIN_IMMUNE)) //no team killing - continue + for(var/trait in resistance_traits) + if(HAS_TRAIT(victim, trait)) + continue victim.adjust_health(victim.health - 3 * seconds_per_tick) //the victim may heal a bit but this will quickly kill emulsified = TRUE if(emulsified) diff --git a/code/modules/fishing/fish/types/air_space.dm b/code/modules/fishing/fish/types/air_space.dm index 3454d188306..5b73b085131 100644 --- a/code/modules/fishing/fish/types/air_space.dm +++ b/code/modules/fishing/fish/types/air_space.dm @@ -89,6 +89,7 @@ grind_results = list(/datum/reagent/bluespace = 10) fillet_type = null fish_traits = list(/datum/fish_trait/antigrav, /datum/fish_trait/mixotroph) + compatible_types = list(/obj/item/fish/starfish/chrystarfish) beauty = FISH_BEAUTY_GREAT /obj/item/fish/starfish/Initialize(mapload, apply_qualities = TRUE) @@ -97,8 +98,11 @@ /obj/item/fish/starfish/update_overlays() . = ..() + . += add_emissive() + +/obj/item/fish/starfish/proc/add_emissive() if(status == FISH_ALIVE) - . += emissive_appearance(icon, "starfish_emissive", src) + return emissive_appearance(icon, "starfish_emissive", src) ///It spins, and dimly glows in the dark. /obj/item/fish/starfish/flop_animation() diff --git a/code/modules/fishing/fish/types/rift.dm b/code/modules/fishing/fish/types/rift.dm new file mode 100644 index 00000000000..ce7046c1f0c --- /dev/null +++ b/code/modules/fishing/fish/types/rift.dm @@ -0,0 +1,395 @@ +/obj/item/fish/starfish/chrystarfish + name = "chrystarfish" + fish_id = "chrystarfish" + desc = "This is what happens when a cosmostarfish sneaks into the bluespace compartment of a hyperspace engine. Very pointy and damaging - leading cause of spaceship explosions in 2554." + icon = 'icons/obj/aquarium/rift.dmi' + dedicated_in_aquarium_icon = 'icons/obj/aquarium/rift.dmi' + icon_state = "chrystarfish" + force = 12 + sharpness = SHARP_POINTY + wound_bonus = -10 + bare_wound_bonus = 15 + armour_penetration = 6 + demolition_mod = 1.2 + throwforce = 11 + throw_range = 8 + throw_speed = 4 + embed_type = /datum/embed_data/chrystarfish + attack_verb_continuous = list("stabs", "jabs") + attack_verb_simple = list("stab", "jab") + hitsound = SFX_SHATTER + pickup_sound = 'sound/items/handling/materials/glass_pick_up.ogg' + drop_sound = 'sound/items/handling/materials/glass_drop.ogg' + + sprite_width = 7 + sprite_height = 9 + + average_size = 40 + average_weight = 1500 + food = /datum/reagent/bluespace + feeding_frequency = 10 MINUTES + health = 50 + death_text = "%SRC splinters apart into shards!" + random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS + fillet_type = /obj/item/stack/ore/bluespace_crystal + num_fillets = 3 + stable_population = 3 + compatible_types = list(/obj/item/fish/starfish) + fish_traits = list(/datum/fish_trait/antigrav) + beauty = FISH_BEAUTY_EXCELLENT + fish_movement_type = /datum/fish_movement/accelerando + fishing_difficulty_modifier = 15 + favorite_bait = list(/obj/item/stack/ore/bluespace_crystal) + // something something bluespace + electrogenesis_power = 9 MEGA JOULES + +// Basically a ninja star that's highly likely to embed and teleports you around if you don't stop to remove it. However it doesn't deal that much damage! +/datum/embed_data/chrystarfish + pain_mult = 1 + embed_chance = 85 + fall_chance = 3 + pain_chance = 9 + impact_pain_mult = 1 + remove_pain_mult = 2 + rip_time = 1.5 SECONDS + ignore_throwspeed_threshold = TRUE // basically shaped like a shuriken + jostle_chance = 15 + jostle_pain_mult = 1 + // about to be set! + jostle_callback = null + +/datum/embed_data/chrystarfish/New() + ..() + jostle_callback = CALLBACK(src, PROC_REF(teleport)) + +/datum/embed_data/chrystarfish/proc/teleport(mob/victim, atom/embed_parent, datum/embed_data/real_data) + do_teleport(victim, get_turf(victim), 3, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) + victim.visible_message(span_danger("[victim] teleports as [embed_parent] jostles inside [victim.p_them()]!")) + +/obj/item/fish/starfish/chrystarfish/set_status(new_status, silent) + . = ..() + if(new_status == FISH_DEAD) + new fillet_type(get_turf(src)) + playsound(src, SFX_SHATTER, 50) + qdel(src) + +/obj/item/fish/starfish/chrystarfish/add_emissive() + return + +/obj/item/fish/starfish/chrystarfish/get_base_edible_reagents_to_add() + var/list/return_list = ..() + return_list[/datum/reagent/bluespace] = 5 + return return_list + +/obj/item/fish/starfish/chrystarfish/flinch_on_eat(mob/living/eater, mob/living/feeder) + if(status != FISH_ALIVE) + return + to_chat(feeder, span_warning("[src] slips out of the spacetime in pain!")) + + var/tp_range = 6 * clamp(weight/average_weight, 3, 9) // usually 6, plus or minus fish weight + // teleports itself if on a turf otherwise its container - whatever it is + do_teleport(isturf(loc) ? src : loc, get_turf(feeder), tp_range, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) + +/obj/item/fish/starfish/chrystarfish/suicide_act(mob/living/user) + visible_message(span_suicide("[user] swallows [src] whole! It looks like they're trying to commit suicide!")) + forceMove(user) + // *everything* + for(var/obj/thing in user.get_contents()) + if(QDELETED(thing) || istype(thing, /obj/item/bodypart/chest)) + continue // don't want a gib + stoplag(0.1 SECONDS) + playsound(src, 'sound/effects/phasein.ogg', 15, TRUE) + do_teleport(thing, get_turf(user), 2, asoundin = null, channel = TELEPORT_CHANNEL_BLUESPACE) + qdel(src) + return MANUAL_SUICIDE + +// Prevents the first 2 messages from spamming on each patience update. +#define PATIENCE_FLINCH "PATIENCE_FLINCH" +#define PATIENCE_UNCOMFY "PATIENCE_UNCOMFY" + +/obj/item/fish/dolphish + name = "walro-dolphish" + fish_id = "walro-dolphish" + desc = "Strange bloodthirsty apex predator from beyond. A powerful weapon, but it -hates- being held." + icon = 'icons/obj/aquarium/wide.dmi' + dedicated_in_aquarium_icon = 'icons/obj/aquarium/rift.dmi' + icon_state = "dolphish" + lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi' + righthand_file = 'icons/mob/inhands/fish_righthand.dmi' + force = 19 + sharpness = SHARP_POINTY + wound_bonus = -5 + bare_wound_bonus = 20 + armour_penetration = 12 + block_chance = 33 + throwforce = 7 + throw_range = 4 + attack_verb_continuous = list("bites", "impales", "rams") + attack_verb_simple = list("bite", "impale", "ram") + hitsound = 'sound/items/weapons/bladeslice.ogg' + block_sound = 'sound/items/weapons/parry.ogg' + drop_sound = 'sound/mobs/non-humanoids/fish/fish_drop1.ogg' + pickup_sound = SFX_FISH_PICKUP + sound_vary = TRUE + + base_pixel_x = -16 + pixel_x = -16 + sprite_width = 13 + sprite_height = 9 + + required_fluid_type = AQUARIUM_FLUID_AIR + required_temperature_min = BODYTEMP_COLD_DAMAGE_LIMIT // you mean just like a human? that's odd... + required_temperature_max = BODYTEMP_HEAT_DAMAGE_LIMIT + food = /datum/reagent/blood + health = 600 // apex predator + random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS + fillet_type = /obj/item/food/fishmeat/fish_tail + num_fillets = 1 + stable_population = 3 + fish_traits = list(/datum/fish_trait/wary, /datum/fish_trait/carnivore, /datum/fish_trait/necrophage, /datum/fish_trait/predator, /datum/fish_trait/territorial, /datum/fish_trait/stinger) + fish_movement_type = /datum/fish_movement/choppy + fishing_difficulty_modifier = 30 + favorite_bait = list() + disliked_bait = list() + average_size = 150 + average_weight = 2750 + weight_size_deviation = 0.5 + safe_air_limits = list( + /datum/gas/oxygen = list(12, 100), + /datum/gas/nitrogen, + /datum/gas/carbon_dioxide = list(0, 10), + ) + min_pressure = HAZARD_LOW_PRESSURE + max_pressure = HAZARD_HIGH_PRESSURE + beauty = FISH_BEAUTY_GREAT + /// Maximum patience for below var. + var/max_patience = 20 + /// Counter of how much patience the fish currently has before it attacks its wielder. + var/patience = 20 + /// Ensures the last warning fx isn't repeated + var/last_effect + +/obj/item/fish/dolphish/get_force_rank() + var/multiplier = 1 + switch(w_class) + if(WEIGHT_CLASS_TINY) + multiplier = 0.35 + if(WEIGHT_CLASS_SMALL) + multiplier = 0.5 + if(WEIGHT_CLASS_NORMAL) + multiplier = 0.75 + // bulky is avergae + if(WEIGHT_CLASS_HUGE) + multiplier = 1.15 + if(WEIGHT_CLASS_GIGANTIC) + multiplier = 1.35 + + if(status == FISH_DEAD) + multiplier -= 0.35 // huge nerf if dead + + force *= multiplier + attack_speed *= multiplier + demolition_mod *= multiplier + block_chance *= multiplier + armour_penetration *= multiplier + wound_bonus *= multiplier + bare_wound_bonus *= multiplier + +/obj/item/fish/dolphish/do_fish_process(seconds_per_tick) + . = ..() + var/patience_reduction = 1 + + var/turf/onturf = get_turf(loc) + //gas check + var/datum/gas_mixture/turf_gasmix = onturf.return_air() + // likes water, gets sleepy, gets very sleepy + if(turf_gasmix.gases[/datum/gas/water_vapor] && turf_gasmix.gases[/datum/gas/water_vapor][MOLES] >= 5) + patience_reduction *= 0.5 + if(turf_gasmix.gases[/datum/gas/nitrous_oxide] && turf_gasmix.gases[/datum/gas/nitrous_oxide][MOLES] >= 5) + patience_reduction *= 0.25 + if(turf_gasmix.gases[/datum/gas/healium] && turf_gasmix.gases[/datum/gas/healium][MOLES] >= 5) + patience_reduction *= 0.1 + + if(!ismob(loc)) + // dividing by the multiplier nets us an increasing value. happy dolphish gain patience quicker + var/patience_bonus = (1 / patience_reduction) + patience = clamp(patience + patience_bonus * seconds_per_tick, 0, max_patience) + return + + var/mob/living/moc = loc + if(moc.mob_biotypes & MOB_AQUATIC) + patience_reduction *= 0.6 + if(HAS_TRAIT(moc, TRAIT_IS_WET)) + patience_reduction *= 0.6 + + patience = clamp(patience - (patience_reduction * seconds_per_tick), 0, max_patience) + + switch(patience) + if(0) + // No check, we always want sharky to bite jerky on 0 + moc.visible_message(span_bolddanger("[src] bites directly into [moc] and squirms away from [moc.p_their()] grasp!"), span_userdanger("[src] sinks its fangs into you!!")) + moc.apply_damage(force, BRUTE, moc.get_active_hand(), wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness, attacking_item = src) + forceMove(moc.drop_location()) + moc.painful_scream() + patience = max_patience + playsound(src, hitsound, 45) + if(1 to 10) + // No check, final warning as they struggle, also funny. + visible_message(span_bolddanger("[src] thrashes against [moc]'s grasp!")) + moc.shake_up_animation() + if(10 to 15) + if(last_effect == PATIENCE_FLINCH) + return + visible_message(span_danger("[src] flinches away from [moc]!")) + moc.shake_up_animation() + last_effect = PATIENCE_FLINCH + if(15 to 20) + if(last_effect == PATIENCE_UNCOMFY) + return + visible_message(span_notice("[src] seems uncomfortable in [moc]'s grasp.")) + last_effect = PATIENCE_UNCOMFY + + return + +#undef PATIENCE_FLINCH +#undef PATIENCE_UNCOMFY + +/obj/item/fish/flumpulus + name = "flumpulus" + fish_id = "flumpulus" + desc = "You can hardly even guess as to how this possibly counts as a fish. Inexplicably, you get the feeling that it could serve as a fantastic way to cushion a fall." + icon = 'icons/obj/aquarium/rift.dmi' + dedicated_in_aquarium_icon = 'icons/obj/aquarium/rift.dmi' + icon_state = "flumpulus" + lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi' + righthand_file = 'icons/mob/inhands/fish_righthand.dmi' + attack_verb_continuous = list("splats", "splorts") + attack_verb_simple = list("splat", "splort") + + sprite_width = 10 + sprite_height = 9 + + required_fluid_type = AQUARIUM_FLUID_AIR + required_temperature_min = 0 + required_temperature_max = INFINITY + food = /datum/reagent/consumable/nutriment/fat + random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS + fillet_type = /obj/item/food/meat/slab/human/mutant/slime + num_fillets = 2 + stable_population = 2 + fish_traits = list() + fish_movement_type = /datum/fish_movement/slow + fishing_difficulty_modifier = 22 + favorite_bait = list() + disliked_bait = list() + // twice as big, half as heavy + average_size = 100 + average_weight = 500 + material_weight_mult = 1 + weight_size_deviation = 0.6 + safe_air_limits = list( + /datum/gas/nitrogen, + ) + min_pressure = 0 + max_pressure = HAZARD_HIGH_PRESSURE + beauty = FISH_BEAUTY_UGLY + +/obj/item/fish/flumpulus/suicide_act(mob/living/user) + visible_message(span_suicide("[user] swallows [src] whole! It looks like they're trying to commit suicide!")) + forceMove(user) + . = MANUAL_SUICIDE + for(var/i in 1 to rand(5, 15)) + addtimer(CALLBACK(src, PROC_REF(flump_attack), user), 0.4 SECONDS * i) + +/obj/item/fish/flumpulus/proc/flump_attack(mob/living/user) + var/obj/item/organ/eyes/eyes = user.get_organ_slot(ORGAN_SLOT_EYES) + var/obj/item/organ/eyes/new_eyes = pick(list(/obj/item/organ/eyes/snail, /obj/item/organ/eyes/night_vision/mushroom)) + new_eyes = new new_eyes(user) + new_eyes.Insert(user) + playsound(user, 'sound/effects/cartoon_sfx/cartoon_pop.ogg', 50, TRUE) + user.visible_message("[user]'s [eyes ? eyes : "eye holes"] suddenly sprout stalks and turn into [new_eyes]!") + ASYNC + user.emote("scream") + sleep(5 SECONDS) + if(!QDELETED(eyes)) + eyes.visible_message(span_danger("[eyes] rapidly turn to dust.")) + eyes.dust() + +/obj/item/fish/flumpulus/get_base_edible_reagents_to_add() + var/list/return_list = ..() + //return_list[/datum/reagent/flumpulus_extract] = 10 + return_list[/datum/reagent/medicine/oculine/flumpuline] = 10 + return return_list + +/obj/item/fish/flumpulus/intercept_zImpact(list/falling_movables, levels) + . = ..() + if(status == FISH_DEAD) + return . + + for(var/mob/living/fallen_mob in falling_movables) + visible_message(span_danger("[src] flattens like a pancake as [fallen_mob] lands on top of it!")) + adjust_health(initial(health) * 0.1) // very durable + AddElement(/datum/element/squish, 15 SECONDS) + fallen_mob.Paralyze(0.5 SECONDS) + playsound(src, 'sound/effects/cartoon_sfx/cartoon_splat.ogg', 75) + + return FALL_INTERCEPTED | FALL_NO_MESSAGE + +/obj/item/fish/gullion + name = "gullion" + fish_id = "gullion" + desc = "This crystalline fish is actually one of only two known silicon-based lifeforms.\ + It avoids death via oxygen-silicate reactions by organically shielding its exterior, allowing the thick scales to calcify into quartz and diamond, at the cost of rendering the fish functionally blind. \ + How xenomorphs manage is a complete mystery bordering on bullshit." + icon = 'icons/obj/aquarium/rift.dmi' + dedicated_in_aquarium_icon = 'icons/obj/aquarium/rift.dmi' + icon_state = "gullion" + lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi' + righthand_file = 'icons/mob/inhands/fish_righthand.dmi' + attack_verb_continuous = list("stabs", "jabs") + attack_verb_simple = list("stab", "jab") + hitsound = SFX_DEFAULT_FISH_SLAP + pickup_sound = 'sound/items/handling/materials/glass_pick_up.ogg' + drop_sound = 'sound/items/handling/materials/glass_drop.ogg' + sprite_width = 7 + sprite_height = 5 + + food = /datum/reagent/silicon + feeding_frequency = 30 SECONDS + health = 160 + death_text = "%SRC calcifies." + random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS + fillet_type = /obj/item/stack/sheet/mineral/diamond + num_fillets = 2 + stable_population = 3 + fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/parthenogenesis) // this thing is a diamond farm + beauty = FISH_BEAUTY_EXCELLENT + fish_movement_type = /datum/fish_movement/slow + favorite_bait = list(/obj/item/stack/sheet/mineral/diamond) + fishing_difficulty_modifier = 22 + average_size = 30 + average_weight = 2000 + weight_size_deviation = 0.3 + safe_air_limits = list( + /datum/gas/oxygen = list(0, 2), // does NOT like oxygen + /datum/gas/water_vapor, + ) + required_fluid_type = AQUARIUM_FLUID_SULPHWATEVER + required_temperature_min = 0 + required_temperature_max = BODYTEMP_HEAT_DAMAGE_LIMIT + min_pressure = HAZARD_LOW_PRESSURE + max_pressure = WARNING_HIGH_PRESSURE + +/obj/item/fish/gullion/suicide_act(mob/living/user) + visible_message(span_suicide("[user] swallows [src] whole! It looks like they're trying to commit suicide!")) + forceMove(user) + var/datum/gas_mixture/environment = user.loc.return_air() + var/oxygen_in_air = check_gases(environment.gases, list(/datum/gas/oxygen)) + if(!oxygen_in_air || (status == FISH_DEAD)) + visible_message(span_suicide("[user] chokes and dies! (Wait, from the fish or from lack of air?)")) + return OXYLOSS + + user.petrify(statue_timer = INFINITY) + visible_message(span_suicide("[user]'s skin turns into quartz upon contact with the oxygen in the air!'")) + qdel(src) + return MANUAL_SUICIDE diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 8190b21c84c..f527db6e6c3 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -132,6 +132,13 @@ . += "" //add a new line . += span_notice("Thanks to your fishing skills, you can examine it again for more in-depth information.") return + if(HAS_TRAIT(src, TRAIT_ROD_MANSUS_INFUSED)) + if(IS_HERETIC(user)) + . += span_purple("This rod has been infused by a heretic, improving its ability to catch glimpses of the Mansus. And fish.") + else + . += span_purple("It's glowing an eerie purple...") + else if(IS_HERETIC(user)) + . += span_purple("As a Heretic, you can infuse this fishing rod with your Mansus Grasp by activating the spell while wielding it, to enhance its fishing power.") /obj/item/fishing_rod/examine_more(mob/user) . = ..() diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm index cb0eaecc989..00387c1cffe 100644 --- a/code/modules/fishing/sources/_fish_source.dm +++ b/code/modules/fishing/sources/_fish_source.dm @@ -504,6 +504,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /datum/fish_source/proc/spawn_reward_from_explosion(atom/location, severity) SIGNAL_HANDLER + if(fish_source_flags & FISH_SOURCE_FLAG_EXPLOSIVE_NONE) + return var/multiplier = 1 if(fish_source_flags & FISH_SOURCE_FLAG_EXPLOSIVE_MALUS) if(explosive_fishing_score <= 0) diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm index 81776519735..2c3833080f1 100644 --- a/code/modules/fishing/sources/source_types.dm +++ b/code/modules/fishing/sources/source_types.dm @@ -159,6 +159,7 @@ radial_name = "Chasm" overlay_state = "portal_chasm" radial_state = "ground_hole" + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_NONE /datum/fish_source/portal/ocean fish_table = list( @@ -295,7 +296,7 @@ mover.long_jump_velocity_limit += rand(-100, 100) ///Cherry on top, fish caught from the randomizer portal also have (almost completely) random traits -/datum/fish_source/portal/random/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) +/datum/fish_source/portal/random/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot, obj/item/fishing_rod/used_rod) if(!ispath(reward_path, /obj/item/fish)) return ..() @@ -342,9 +343,6 @@ return rod.hook.chasm_detritus_type -/datum/fish_source/chasm/spawn_reward_from_explosion(atom/location, severity) - return //Spawned content would immediately fall back into the chasm, so it wouldn't matter. - /datum/fish_source/lavaland catalog_description = "Lava vents" background = "background_lavaland" @@ -582,7 +580,7 @@ return return ..() -/datum/fish_source/hydro_tray/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) +/datum/fish_source/hydro_tray/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot, obj/item/fishing_rod/used_rod) if(reward_path != FISHING_RANDOM_SEED) var/mob/living/created_reward = ..() if(istype(created_reward)) @@ -662,7 +660,7 @@ //The range for waiting is also a bit narrower, so it cannot take as few as 3 seconds or as many as 25 to snatch an organ. wait_time_range = list(6 SECONDS, 12 SECONDS) -/datum/fish_source/surgery/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) +/datum/fish_source/surgery/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot, obj/item/fishing_rod/used_rod) if(istype(fishing_spot, /obj/machinery/fishing_portal_generator)) var/obj/machinery/fishing_portal_generator/portal = fishing_spot fishing_spot = portal.current_linked_atom @@ -707,7 +705,7 @@ fish_table = list( FISHING_DUD = 10, ) - fish_source_flags = FISH_SOURCE_FLAG_NO_BLUESPACE_ROD|FISH_SOURCE_FLAG_IGNORE_HIDDEN_ON_CATALOG + fish_source_flags = FISH_SOURCE_FLAG_NO_BLUESPACE_ROD|FISH_SOURCE_FLAG_IGNORE_HIDDEN_ON_CATALOG|FISH_SOURCE_FLAG_EXPLOSIVE_NONE fishing_difficulty = FISHING_EASY_DIFFICULTY + 5 #undef RANDOM_AQUARIUM_FISH @@ -725,9 +723,6 @@ return fish_table return table -/datum/fish_source/aquarium/spawn_reward_from_explosion(atom/location, severity) - return //If the aquarium breaks, all fish are released anyway. - /datum/fish_source/aquarium/generate_wiki_contents(datum/autowiki/fish_sources/wiki) var/list/data = list() @@ -775,3 +770,77 @@ ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20 fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS + +/datum/fish_source/dimensional_rift + catalog_description = null //it's a secret (sorta, I know you're reading this) + radial_state = "cursed" // placeholder + overlay_state = "portal_rift_2" // yeah good luck adaptin the rift sprite to this template. recolored randomizer's the best you're getting + fish_table = list( + FISHING_INFLUENCE = 6, + /obj/item/fish/starfish/chrystarfish = 7, + /obj/item/fish/dolphish = 7, + /obj/item/fish/flumpulus = 7, + /obj/item/fish/gullion = 7, + /mob/living/basic/heretic_summon/fire_shark/wild = 3, + /obj/item/eldritch_potion/crucible_soul = 1, + /obj/item/eldritch_potion/duskndawn = 1, + /obj/item/eldritch_potion/wounded = 1, + /obj/item/reagent_containers/cup/beaker/eldritch = 2, + ) + fish_counts = list( + /mob/living/basic/heretic_summon/fire_shark/wild = 3, + /obj/item/eldritch_potion/crucible_soul = 1, + /obj/item/eldritch_potion/duskndawn = 1, + /obj/item/eldritch_potion/wounded = 1, + /obj/item/reagent_containers/cup/beaker/eldritch = 2, + ) + fish_count_regen = list( + /mob/living/basic/heretic_summon/fire_shark/wild = 3 MINUTES, + /obj/item/eldritch_potion/crucible_soul = 5 MINUTES, + /obj/item/eldritch_potion/duskndawn = 5 MINUTES, + /obj/item/eldritch_potion/wounded = 5 MINUTES, + /obj/item/reagent_containers/cup/beaker/eldritch = 2.5 MINUTES, + ) + fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 35 + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_NONE + +/datum/fish_source/dimensional_rift/on_challenge_completed(mob/user, datum/fishing_challenge/challenge, success) + . = ..() + + if(challenge.reward_path != FISHING_INFLUENCE) + return + var/mob/living/carbon/human/human_user + if(ishuman(user)) + human_user = user + + user.visible_message(span_danger("[user] reels [user.p_their()] [challenge.used_rod] in, catching.. nothing?"), span_notice("You catch.. a glimpse into the workings of the Mansus itself!")) + // Heretics that fish in the rift gain knowledge. + if(IS_HERETIC(user)) + human_user?.add_mood_event("rift fishing", /datum/mood_event/rift_fishing) + var/obj/effect/heretic_influence/fishfluence = challenge.location + // But only if it's an open rift + if(!istype(fishfluence)) + to_chat(user, span_notice("You glimpse something fairly uninteresting.")) + return + fishfluence.after_drain(user) + var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user) + if(heretic_datum) + heretic_datum.knowledge_points++ + to_chat(user, "[span_hear("You hear a whisper...")] [span_hypnophrase("THE HIGHER I RISE, THE MORE I FISH.")]") + // They can also gain an extra influence point if they infused their rod. + if(HAS_TRAIT(challenge.used_rod, TRAIT_ROD_MANSUS_INFUSED)) + heretic_datum.knowledge_points++ + to_chat(user, span_boldnotice("Your infused rod improves your knowledge gain!")) + return + + // Non-heretics instead go crazy + human_user?.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 190) + human_user?.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus) + // Hand fires at them from the location + fire_curse_hand(user, get_turf(challenge.location)) + +// Handled above +/datum/fish_source/dimensional_rift/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) + if(reward_path != FISHING_INFLUENCE) + return ..() + return diff --git a/code/modules/mob/living/basic/heretic/fire_shark.dm b/code/modules/mob/living/basic/heretic/fire_shark.dm index 0dfb9a3a157..847584ae41a 100644 --- a/code/modules/mob/living/basic/heretic/fire_shark.dm +++ b/code/modules/mob/living/basic/heretic/fire_shark.dm @@ -21,6 +21,7 @@ mob_size = MOB_SIZE_TINY speak_emote = list("screams") basic_mob_flags = DEL_ON_DEATH + ai_controller = /datum/ai_controller/basic_controller/simple_hostile_obstacles /mob/living/basic/heretic_summon/fire_shark/Initialize(mapload) . = ..() @@ -32,3 +33,6 @@ ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_FREE_HYPERSPACE_MOVEMENT, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) + +/mob/living/basic/heretic_summon/fire_shark/wild + faction = list(FACTION_HOSTILE) diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 4497728db8f..00c186f8e77 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -78,6 +78,8 @@ GLOBAL_LIST_EMPTY(features_by_species) var/obj/item/organ/heart/mutantheart = /obj/item/organ/heart ///Replaces default lungs with a different organ var/obj/item/organ/lungs/mutantlungs = /obj/item/organ/lungs + /// Smoker lungs for the quirk, overriden by certain species + var/obj/item/organ/lungs/smoker_lungs = /obj/item/organ/lungs/smoker_lungs ///Replaces default eyes with a different organ var/obj/item/organ/eyes/mutanteyes = /obj/item/organ/eyes ///Replaces default ears with a different organ diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 2a1b7785ba8..9a31bc6d8c9 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -3,6 +3,7 @@ id = SPECIES_ETHEREAL meat = /obj/item/food/meat/slab/human/mutant/ethereal mutantlungs = /obj/item/organ/lungs/ethereal + smoker_lungs = /obj/item/organ/lungs/ethereal/ethereal_smoker mutantstomach = /obj/item/organ/stomach/ethereal mutanttongue = /obj/item/organ/tongue/ethereal mutantheart = /obj/item/organ/heart/ethereal 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 b27759e594f..f6864ac0d4c 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -19,6 +19,7 @@ inherent_biotypes = MOB_HUMANOID|MOB_MINERAL inherent_respiration_type = RESPIRATION_PLASMA mutantlungs = /obj/item/organ/lungs/plasmaman + smoker_lungs = /obj/item/organ/lungs/plasmaman/plasmaman_smoker mutanttongue = /obj/item/organ/tongue/bone/plasmaman mutantliver = /obj/item/organ/liver/bone/plasmaman mutantstomach = /obj/item/organ/stomach/bone/plasmaman diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 6616736a610..7dc6de58cfc 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -73,15 +73,22 @@ #define DUST_ANIMATION_TIME 1.3 SECONDS /** - * This is the proc for turning a mob into ash. + * This is the proc for turning an atom into ash. * Dusting robots does not eject the MMI, so it's a bit more powerful than gib() * - * Arguments: + * Arguments: (Only used for mobs) * * just_ash - If TRUE, ash will spawn where the mob was, as opposed to remains * * drop_items - Should the mob drop their items before dusting? * * force - Should this mob be FORCABLY dusted? */ -/mob/living/proc/dust(just_ash, drop_items, force) +/atom/movable/proc/dust(just_ash, drop_items, force) + dust_animation() + // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. + // Looks much nicer and doesn't cause multiple runtimes. + QDEL_IN(src, DUST_ANIMATION_TIME) + +/mob/living/dust(just_ash, drop_items, force) + ..() if(body_position == STANDING_UP) // keep us upright so the animation fits. ADD_TRAIT(src, TRAIT_FORCED_STANDING, TRAIT_GENERIC) @@ -93,10 +100,8 @@ if(buckled) buckled.unbuckle_mob(src, force = TRUE) - dust_animation() addtimer(CALLBACK(src, PROC_REF(spawn_dust), just_ash), DUST_ANIMATION_TIME - 0.3 SECONDS) ghostize() - QDEL_IN(src, DUST_ANIMATION_TIME) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes. /// Animates turning into dust. /// Does not delete src afterwards, BUT it will become invisible (and grey), so ensure you handle that yourself diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 6077a845477..2dbcaa6bc96 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -3008,3 +3008,8 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/proc/check_hit_limb_zone_name(hit_zone) if(has_limbs) return hit_zone + +/mob/living/proc/painful_scream(force = FALSE) + if(HAS_TRAIT(src, TRAIT_ANALGESIA) && !force) + return + INVOKE_ASYNC(src, PROC_REF(emote), "scream") diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 8865621d7a0..d69b3712539 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -759,6 +759,63 @@ if(affected_mob.adjustOrganLoss(ORGAN_SLOT_EYES, 1.5 * REM * seconds_per_tick, required_organ_flag = affected_organ_flags)) . = UPDATE_MOB_HEALTH +/datum/reagent/medicine/oculine/flumpuline + name = "Flumpuline" + description = "Often confused for, or sold as, Oculine or a variation thereof. Slowly transmogrifies the eyes of the patient into grotesque stalks - but you'll never need glasses again." + color = "#6c596d" + metabolization_rate = 0.1 * REAGENTS_METABOLISM + overdose_threshold = 5 + taste_description = "fungus" + purity = 1 + ph = 0.01 + chemical_flags = REAGENT_DEAD_PROCESS|REAGENT_IGNORE_STASIS|REAGENT_NO_RANDOM_RECIPE|REAGENT_CAN_BE_SYNTHESIZED + inverse_chem = /datum/reagent/inverse + inverse_chem_val = 0 + var/static/list/eye_types = list(/obj/item/organ/eyes/snail, /obj/item/organ/eyes/night_vision/mushroom) + +/datum/reagent/medicine/oculine/flumpuline/improve_eyesight(mob/living/carbon/affected_mob, obj/item/organ/eyes/eyes) + delta_light = 200 //2x better than pure oculine + eyes.lighting_cutoff += delta_light + affected_mob.update_sight() + +/datum/reagent/medicine/oculine/flumpuline/restore_eyesight(mob/living/carbon/affected_mob, obj/item/organ/eyes/eyes) + eyes.lighting_cutoff -= delta_light + affected_mob.update_sight() + +/datum/reagent/medicine/oculine/flumpuline/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + var/obj/item/organ/eyes/eyes = affected_mob.get_organ_slot(ORGAN_SLOT_EYES) + // if no eyes or inorganic do nothing. we let already changed eyes go because funny + if(!eyes || !IS_ORGANIC_ORGAN(eyes)) + return . + + if(!prob(2)) + return . + + flump_eyes(affected_mob, eyes) + +// Overdose causes constant eye popping +/datum/reagent/medicine/oculine/flumpuline/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + if(!prob(25)) + return + var/obj/item/organ/eyes/eyes = affected_mob.get_organ_slot(ORGAN_SLOT_EYES) + + flump_eyes(affected_mob, eyes) + +/datum/reagent/medicine/oculine/flumpuline/proc/flump_eyes(mob/affected_mob, obj/eyes) + var/obj/item/organ/eyes/new_eyes = pick(eye_types) + new_eyes = new new_eyes(affected_mob) + new_eyes.Insert(affected_mob) + playsound(affected_mob, 'sound/effects/cartoon_sfx/cartoon_pop.ogg', 50, TRUE) + affected_mob.visible_message("[affected_mob]'s [eyes ? eyes : "eye holes"] suddenly sprout stalks and turn into [new_eyes]!") + ASYNC + affected_mob.emote("scream") + sleep(5 SECONDS) + if(!QDELETED(eyes)) + eyes.visible_message(span_danger("[eyes] rapidly turn to dust.")) + eyes.dust() + /datum/reagent/medicine/inacusiate name = "Inacusiate" description = "Rapidly repairs damage to the patient's ears to cure deafness, assuming the source of said deafness isn't from genetic mutations, chronic deafness, or a total defecit of ears." //by "chronic" deafness, we mean people with the "deaf" quirk diff --git a/code/modules/spells/spell_types/touch/_touch.dm b/code/modules/spells/spell_types/touch/_touch.dm index 49d1f24e39c..a783874c14c 100644 --- a/code/modules/spells/spell_types/touch/_touch.dm +++ b/code/modules/spells/spell_types/touch/_touch.dm @@ -153,9 +153,9 @@ return ..() | SPELL_NO_FEEDBACK | SPELL_NO_IMMEDIATE_COOLDOWN /datum/action/cooldown/spell/touch/cast(mob/living/carbon/cast_on) - if(SEND_SIGNAL(cast_on, COMSIG_TOUCH_HANDLESS_CAST) & COMPONENT_CAST_HANDLESS) + if(SEND_SIGNAL(cast_on, COMSIG_TOUCH_HANDLESS_CAST, src) & COMPONENT_CAST_HANDLESS) StartCooldown() - return ..() + return if(!QDELETED(attached_hand) && (attached_hand in cast_on.held_items)) remove_hand(cast_on, reset_cooldown_after = TRUE) diff --git a/icons/obj/aquarium/fish.dmi b/icons/obj/aquarium/fish.dmi index 9f44beed19b..4d18520cd91 100644 Binary files a/icons/obj/aquarium/fish.dmi and b/icons/obj/aquarium/fish.dmi differ diff --git a/icons/obj/aquarium/rift.dmi b/icons/obj/aquarium/rift.dmi new file mode 100644 index 00000000000..63c739fb32f Binary files /dev/null and b/icons/obj/aquarium/rift.dmi differ diff --git a/icons/obj/aquarium/wide.dmi b/icons/obj/aquarium/wide.dmi index b31404d31a2..02393fb3a7f 100644 Binary files a/icons/obj/aquarium/wide.dmi and b/icons/obj/aquarium/wide.dmi differ diff --git a/icons/obj/fishing.dmi b/icons/obj/fishing.dmi index 58ab9944366..59c40c02103 100644 Binary files a/icons/obj/fishing.dmi and b/icons/obj/fishing.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 373972b506b..cee6c9606c7 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4234,6 +4234,7 @@ #include "code\modules\fishing\fish\types\freshwater.dm" #include "code\modules\fishing\fish\types\holographic.dm" #include "code\modules\fishing\fish\types\mining.dm" +#include "code\modules\fishing\fish\types\rift.dm" #include "code\modules\fishing\fish\types\ruins.dm" #include "code\modules\fishing\fish\types\saltwater.dm" #include "code\modules\fishing\fish\types\station.dm"