Files
Joshua Kidder 7a3ad79506 All camelCase (Brute|Burn|Fire|Tox|Oxy|Organ|Stamina)(Loss) procs now use snake_case. UNDERSCORES RULE! (#94111)
## About The Pull Request
It's just a partial cleanup of
anti-[STYLE](https://github.com/tgstation/tgstation/blob/master/.github/guides/STYLE.md)
code from /tg/'s ancient history. I compiled & tested with my helpful
assistant and damage is still working.

<img width="1920" height="1040" alt="image"
src="https://github.com/user-attachments/assets/26dabc17-088f-4008-b299-3ff4c27142c3"
/>


I'll upload the .cs script I used to do it shortly.

## Why It's Good For The Game
Just minor code cleanup.

Script used is located at https://metek.tech/camelTo-Snake.7z

EDIT 11/23/25: Updated the script to use multithreading and sequential
scan so it works a hell of a lot faster
```
/*
//
Copyright 2025 Joshua 'Joan Metekillot' Kidder

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
//
*/
using System.Text.RegularExpressions;
class Program
{
    static async Task Main(string[] args)
    {
        var readFile = new FileStreamOptions
        {
            Access = FileAccess.Read,
            Share = FileShare.ReadWrite,
            Options = FileOptions.Asynchronous | FileOptions.SequentialScan
        };
        FileStreamOptions writeFile = new FileStreamOptions
        {
            Share = FileShare.ReadWrite,
            Access = FileAccess.ReadWrite,
            Mode = FileMode.Truncate,
            Options = FileOptions.Asynchronous
        };
        RegexOptions regexOptions = RegexOptions.Multiline | RegexOptions.Compiled;
        Dictionary<string, int> changedProcs = new();
        string regexPattern = @"(?<=\P{L})([a-z]+)([A-Z]{1,2}[a-z]+)*(Brute|Burn|Fire|Tox|Oxy|Organ|Stamina)(Loss)([A-Z]{1,2}[a-z]+)*";
        Regex camelCaseProcRegex = new(regexPattern, regexOptions);

        string snakeify(Match matchingRegex)
        {
            var vals =
            matchingRegex.Groups.Cast<Group>().SelectMany(_ => _.Captures).Select(_ => _.Value).ToArray();
            var newVal = string.Join("_", vals.Skip(1).ToArray()).ToLower();
            string logString = $"{vals[0]} => {newVal}";
            if (changedProcs.TryGetValue(logString, out int value))
            {
                changedProcs[logString] = value + 1;
            }
            else
            {
                changedProcs.Add(logString, 1);
            }
            return newVal;
        }
        var dmFiles = Directory.EnumerateFiles(".", "*.dm", SearchOption.AllDirectories).ToAsyncEnumerable<string>();

        // uses default ParallelOptions
        // https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.paralleloptions?view=net-10.0#main
        await Parallel.ForEachAsync(dmFiles, async (filePath, UnusedCancellationToken) =>
        {
            var reader = new StreamReader(filePath, readFile);
            string oldContent = await reader.ReadToEndAsync();
            string newContent = camelCaseProcRegex.Replace(oldContent, new MatchEvaluator((Func<Match, string>)snakeify));
            if (oldContent != newContent)
            {
                var writer = new StreamWriter(filePath, writeFile);
                await writer.WriteAsync(newContent);
                await writer.DisposeAsync();
            }
            reader.Dispose();
        });
        var logToList = changedProcs.Cast<KeyValuePair<string, int>>().ToList();
        foreach (var pair in logToList)
        {
            Console.WriteLine($"{pair.Key}: {pair.Value} locations");
        }
    }
}

```

## Changelog
🆑 Bisar
code: All (Brute|Burn|Fire|Tox|Oxy|Organ|Stamina)(Loss) procs now use
snake_case, in-line with the STYLE guide. Underscores rule!
/🆑
2025-11-27 15:50:23 -05:00

407 lines
14 KiB
Plaintext

#define SLIME_EXTRA_SHOCK_COST 3
#define SLIME_EXTRA_SHOCK_THRESHOLD 8
#define SLIME_BASE_SHOCK_PERCENTAGE 10
#define SLIME_SHOCK_PERCENTAGE_PER_LEVEL 7
/mob/living/basic/slime
name = "grey baby slime (123)"
icon = 'icons/mob/simple/slimes.dmi'
icon_state = "grey-baby"
pass_flags = PASSTABLE | PASSGRILLE
gender = NEUTER
faction = list(FACTION_SLIME, FACTION_NEUTRAL)
icon_living = "grey-baby"
icon_dead = "grey-baby-dead"
attack_sound = 'sound/items/weapons/bite.ogg'
//Base physiology
maxHealth = 150
health = 150
mob_biotypes = MOB_SLIME
melee_damage_lower = 7
melee_damage_upper = 17
wound_bonus = -45
can_buckle_to = FALSE
damage_coeff = list(BRUTE = 1, BURN = -1, TOX = 1, STAMINA = 1, OXY = 1) //Healed by fire
unsuitable_cold_damage = 15
unsuitable_heat_damage = 0
maximum_survivable_temperature = INFINITY
habitable_atmos = null
//Messages
attack_verb_simple = "glomp"
attack_verb_continuous = "glomps"
response_help_continuous = "pets"
response_help_simple = "pet"
response_disarm_continuous = "shoos"
response_disarm_simple = "shoo"
response_harm_continuous = "stomps on"
response_harm_simple = "stomp on"
//Speech
speak_emote = list("blorbles")
bubble_icon = "slime"
initial_language_holder = /datum/language_holder/slime
verb_say = "blorbles"
verb_ask = "inquisitively blorbles"
verb_exclaim = "loudly blorbles"
verb_yell = "loudly blorbles"
//AI controller
ai_controller = /datum/ai_controller/basic_controller/slime
//Slime physiology
///What is our current lifestage?
var/life_stage = SLIME_LIFE_STAGE_BABY
///Our slime's current mood
var/current_mood = SLIME_MOOD_NONE
///If the slime is currently overcrowded and unable to reproduce.
var/overcrowded = FALSE
///The number of /obj/item/slime_extract's the slime has left inside
var/cores = 1
///Chance of mutating, should be between 25 and 35
var/mutation_chance = 30
///1-10 controls how much electricity they are generating
var/powerlevel = SLIME_MIN_POWER
///Controls how long the slime has been overfed, if 10, grows or reproduces
var/amount_grown = 0
/// No hunger
var/hunger_disabled = FALSE
///Has a mutator been used on the slime? Only one is allowed
var/mutator_used = FALSE
// The datum that handles the slime colour's core and possible mutations
var/datum/slime_type/slime_type = null
// After a slime is initialized, a list of all possible initialized datums of slime_types
var/static/list/possible_slime_types = null
//CORE-CROSSING CODE
///What cross core modification is being used.
var/crossbreed_modification
///How many extracts of the modtype have been applied.
var/applied_crossbreed_amount = 0
//AI related traits
/// Instructions you can give to slimes
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/attack/slime,
)
/// Our evolve action
var/datum/action/innate/slime/evolve/evolve_action
/// Our reproduction action
var/datum/action/innate/slime/reproduce/reproduce_action
/mob/living/basic/slime/Initialize(mapload, new_type = /datum/slime_type/grey, new_life_stage = SLIME_LIFE_STAGE_BABY)
. = ..()
evolve_action = new (src)
evolve_action.Grant(src)
reproduce_action = new (src)
reproduce_action.Grant(src)
if(isnull(possible_slime_types))
possible_slime_types = list()
for(var/datum/slime_type/slime_type as anything in subtypesof(/datum/slime_type))
possible_slime_types[slime_type] = new slime_type
set_life_stage(new_life_stage, TRUE)
set_slime_type(new_type)
set_nutrition(SLIME_STARTING_NUTRITION)
AddComponent(/datum/component/health_scaling_effects, min_health_slowdown = 2)
AddComponent(/datum/component/obeys_commands, pet_commands)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/basic_health_examine, light_damage_message = "It has some punctures in its flesh!", heavy_damage_message = span_bold("It has severe punctures and tears in its flesh!"), heavy_threshold = 0.4)
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_SLIME)
AddElement(/datum/element/soft_landing)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLIME, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
add_traits(list(TRAIT_CANT_RIDE, TRAIT_CAN_MOUNT_HUMANS, TRAIT_VENTCRAWLER_ALWAYS), INNATE_TRAIT)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_slime_pre_attack))
RegisterSignal(src, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand) )
ai_controller.set_blackboard_key(BB_SLIME_EVOLVE, evolve_action)
ai_controller.set_blackboard_key(BB_SLIME_REPRODUCE, reproduce_action)
/mob/living/basic/slime/Destroy()
QDEL_NULL(evolve_action)
QDEL_NULL(reproduce_action)
slime_type = null
return ..()
///Random slime subtype
/mob/living/basic/slime/random
/mob/living/basic/slime/random/Initialize(mapload, new_colour, new_life_stage)
return ..(mapload, null, prob(50) ? SLIME_LIFE_STAGE_ADULT : SLIME_LIFE_STAGE_BABY)
///Friendly docile subtype
/mob/living/basic/slime/pet
hunger_disabled = TRUE
/mob/living/basic/slime/pet/Initialize(mapload, new_colour, new_life_stage)
. = ..()
set_pacified_behaviour()
//Hilbert subtype
/mob/living/basic/slime/hilbert
/mob/living/basic/slime/hilbert/Initialize(mapload, new_colour, new_life_stage)
. = ..(mapload, /datum/slime_type/bluespace)
ai_controller?.set_blackboard_key(BB_SLIME_RABID, TRUE)
/mob/living/basic/slime/adjust_nutrition(change, forced)
. = ..()
nutrition = min(nutrition, SLIME_MAX_NUTRITION)
/mob/living/basic/slime/set_nutrition(set_to, forced = FALSE)
. = ..()
nutrition = min(nutrition, SLIME_MAX_NUTRITION)
/mob/living/basic/slime/update_name()
///Checks if the slime has a generic name, in the format of baby/adult slime (123)
var/static/regex/slime_name_regex = new("\\w+ (baby|adult) slime \\(\\d+\\)")
if(slime_name_regex.Find(name))
var/slime_id = rand(1, 1000)
name = "[slime_type.colour] [life_stage] slime ([slime_id])"
real_name = name
return ..()
/mob/living/basic/slime/regenerate_icons()
cut_overlays()
if(slime_type.transparent)
alpha = SLIME_TRANSPARENCY_ALPHA
icon_dead = !cores ? "[slime_type.colour]-cut" : "[slime_type.colour]-[life_stage]-dead"
if(stat != DEAD)
icon_state = "[slime_type.colour]-[life_stage]"
if(current_mood && current_mood != SLIME_MOOD_NONE && !stat)
add_overlay("aslime-[current_mood]")
else
icon_state = icon_dead
return ..()
/mob/living/basic/slime/get_status_tab_items()
. = ..()
if(!hunger_disabled)
. += "Nutrition: [nutrition]/[SLIME_MAX_NUTRITION]"
. += "Growth: [amount_grown]/[SLIME_EVOLUTION_THRESHOLD]"
. += "Power Level: [powerlevel]/[SLIME_MAX_POWER]"
/mob/living/basic/slime/mouse_drop_dragged(atom/target_atom, mob/user)
if(isliving(target_atom) && target_atom != src && user == src)
var/mob/living/food = target_atom
if(can_feed_on(food))
start_feeding(food)
///Slimes can hop off mobs they have latched onto
/mob/living/basic/slime/resist_buckle()
if(isliving(buckled))
buckled.unbuckle_mob(src,force=TRUE)
//slimes can not pull
/mob/living/basic/slime/start_pulling(atom/movable/moveable_atom, state, force = move_force, supress_message = FALSE)
return
/mob/living/basic/slime/examine(mob/user)
. = ..()
switch(powerlevel)
if(SLIME_MIN_POWER to SLIME_EXTRA_SHOCK_COST)
. += "It is flickering gently with harmless levels of electrical activity."
if(SLIME_EXTRA_SHOCK_COST to SLIME_MEDIUM_POWER)
. += "It is glowing brightly with medium levels electrical activity."
if(SLIME_MEDIUM_POWER to SLIME_MAX_POWER)
. += "It is glowing alarmingly with high levels of electrical activity."
if(SLIME_MAX_POWER)
. += span_boldwarning("It is radiating with massive levels of electrical activity!")
if(overcrowded)
. += span_warning("It seems too overcroweded to properly reproduce!")
///Changes the slime's current life state
/mob/living/basic/slime/proc/set_life_stage(new_life_stage = SLIME_LIFE_STAGE_BABY, initial = FALSE)
life_stage = new_life_stage
if(life_stage == SLIME_LIFE_STAGE_ADULT)
health /= 0.75
maxHealth /= 0.75
melee_damage_lower *= 2
melee_damage_upper *= 2
obj_damage = 15
wound_bonus = -90
else if(!initial)
health *= 0.75
maxHealth *= 0.75
melee_damage_lower *= 0.5
melee_damage_upper *= 0.5
obj_damage = initial(obj_damage)
wound_bonus = initial(wound_bonus)
ai_controller.set_blackboard_key(BB_SLIME_LIFE_STAGE, life_stage)
update_mob_action_buttons()
/// Sets the slime's type, name and its icons.
/// If not provided with a type it will instead be random
/mob/living/basic/slime/proc/set_slime_type(new_type = null)
if(isnull(new_type))
new_type = pick(subtypesof(/datum/slime_type))
slime_type = possible_slime_types[new_type]
update_name()
regenerate_icons()
///Handles slime attacking restrictions, and any extra effects that would trigger
/mob/living/basic/slime/proc/on_slime_pre_attack(mob/living/basic/slime/our_slime, atom/target, proximity, modifiers)
SIGNAL_HANDLER
if(LAZYACCESS(modifiers, RIGHT_CLICK) && isliving(target) && target != src)
if(our_slime.can_feed_on(target))
our_slime.start_feeding(target)
return COMPONENT_HOSTILE_NO_ATTACK
if(isAI(target)) //The aI is not tasty!
target.balloon_alert(our_slime, "not tasty!")
return COMPONENT_HOSTILE_NO_ATTACK
if(our_slime.buckled == target) //If you try to attack the creature you are latched on, you instead cancel feeding
our_slime.stop_feeding()
return COMPONENT_HOSTILE_NO_ATTACK
if(iscyborg(target))
var/mob/living/silicon/robot/borg_target = target
borg_target.flash_act()
do_sparks(5, TRUE, borg_target)
var/stunprob = our_slime.powerlevel * SLIME_SHOCK_PERCENTAGE_PER_LEVEL + SLIME_BASE_SHOCK_PERCENTAGE
if(prob(stunprob) && our_slime.powerlevel >= SLIME_EXTRA_SHOCK_COST)
our_slime.powerlevel = clamp(our_slime.powerlevel - SLIME_EXTRA_SHOCK_COST, SLIME_MIN_POWER, SLIME_MAX_POWER)
borg_target.apply_damage(our_slime.powerlevel * rand(6, 10), BRUTE, spread_damage = TRUE, wound_bonus = CANT_WOUND)
borg_target.visible_message(span_danger("\The [our_slime] shocks [borg_target]!"), span_userdanger("\The [our_slime] shocks you!"))
else
borg_target.visible_message(span_danger("\The [our_slime] fails to hurt [borg_target]!"), span_userdanger("\The [our_slime] failed to hurt you!"))
return COMPONENT_HOSTILE_NO_ATTACK
if(iscarbon(target) && our_slime.powerlevel > SLIME_MIN_POWER)
var/mob/living/carbon/carbon_target = target
var/stunprob = our_slime.powerlevel * SLIME_SHOCK_PERCENTAGE_PER_LEVEL + SLIME_BASE_SHOCK_PERCENTAGE // 17 at level 1, 80 at level 10
if(!prob(stunprob))
return NONE // normal attack
carbon_target.visible_message(span_danger("\The [our_slime] shocks [carbon_target]!"), span_userdanger("\The [our_slime] shocks you!"))
do_sparks(5, TRUE, carbon_target)
var/power = our_slime.powerlevel + rand(0,3)
carbon_target.Paralyze(2 SECONDS)
carbon_target.Knockdown(power * 2 SECONDS)
carbon_target.set_stutter_if_lower(power * 2 SECONDS)
if (prob(stunprob) && our_slime.powerlevel >= SLIME_EXTRA_SHOCK_COST)
our_slime.powerlevel = clamp(our_slime.powerlevel - SLIME_EXTRA_SHOCK_COST, SLIME_MIN_POWER, SLIME_MAX_POWER)
carbon_target.apply_damage(our_slime.powerlevel * rand(6, 10), BURN, spread_damage = TRUE, wound_bonus = CANT_WOUND)
if(isslime(target))
if(target == our_slime)
return COMPONENT_HOSTILE_NO_ATTACK
var/mob/living/basic/slime/target_slime = target
if(target_slime.buckled)
target_slime.stop_feeding(silent = TRUE)
visible_message(span_danger("[our_slime] pulls [target_slime] off!"), \
span_danger("You pull [target_slime] off!"))
return NONE // normal attack
var/is_adult_slime = our_slime.life_stage == SLIME_LIFE_STAGE_ADULT
if(target_slime.nutrition >= 100) //steal some nutrition. negval handled in life()
var/stolen_nutrition = min(is_adult_slime ? 90 : 50, target_slime.nutrition)
target_slime.adjust_nutrition(-stolen_nutrition)
our_slime.adjust_nutrition(stolen_nutrition)
if(target_slime.health > 0)
our_slime.adjust_brute_loss(is_adult_slime ? -20 : -10)
///Spawns a crossed slimecore item
/mob/living/basic/slime/proc/spawn_corecross()
var/static/list/crossbreeds = subtypesof(/obj/item/slimecross)
visible_message(span_danger("[src] shudders, its mutated core consuming the rest of its body!"))
playsound(src, 'sound/effects/magic/smoke.ogg', 50, TRUE)
var/selected_crossbreed_path
for(var/crossbreed_path in crossbreeds)
var/obj/item/slimecross/cross_item = crossbreed_path
if(initial(cross_item.colour) == slime_type.colour && initial(cross_item.effect) == crossbreed_modification)
selected_crossbreed_path = cross_item
break
if(selected_crossbreed_path)
new selected_crossbreed_path(loc)
else
visible_message(span_warning("The mutated core shudders, and collapses into a puddle, unable to maintain its form."))
qdel(src)
///Proc for slime core removal surgery, tries to remove cores from a dead slime.
/mob/living/basic/slime/proc/try_extract_cores(count = 1)
if(stat != DEAD)
return FALSE
if(count <= 0 || cores < count)
return FALSE
var/core_count = min(count, cores)
for(var/i in 1 to core_count)
new slime_type.core_type(loc)
cores--
regenerate_icons()
return TRUE
///Makes the slime peaceful and content
/mob/living/basic/slime/proc/set_pacified_behaviour()
hunger_disabled = TRUE
ai_controller?.set_blackboard_key(BB_SLIME_RABID, FALSE)
ai_controller?.set_blackboard_key(BB_SLIME_HUNGER_DISABLED, TRUE)
set_nutrition(SLIME_STARTING_NUTRITION)
///Makes the slime angry and hungry
/mob/living/basic/slime/proc/set_enraged_behaviour()
hunger_disabled = FALSE
ai_controller?.set_blackboard_key(BB_SLIME_HUNGER_DISABLED, FALSE)
ai_controller?.set_blackboard_key(BB_SLIME_RABID, TRUE)
///Makes the slime hungry but mostly friendly
/mob/living/basic/slime/proc/set_default_behaviour()
hunger_disabled = FALSE
ai_controller?.set_blackboard_key(BB_SLIME_HUNGER_DISABLED, FALSE)
ai_controller?.set_blackboard_key(BB_SLIME_RABID, FALSE)
#undef SLIME_EXTRA_SHOCK_COST
#undef SLIME_EXTRA_SHOCK_THRESHOLD
#undef SLIME_BASE_SHOCK_PERCENTAGE
#undef SLIME_SHOCK_PERCENTAGE_PER_LEVEL