Files
Joshua KidderandGitHub 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

274 lines
10 KiB
Plaintext

#define FINAL_BUD_GROWTH_ICON 3
/**
* Kudzu Flower Bud
*
* A flower created by flowering kudzu which spawns a venus human trap after a certain amount of time has passed.
*
* A flower created by kudzu with the flowering mutation. Spawns a venus human trap after 2 minutes under normal circumstances.
* Also spawns 4 vines going out in diagonal directions from the bud. Any living creature not aligned with plants is damaged by these vines.
* Once it grows a venus human trap, the bud itself will destroy itself.
*
*/
/// Flower bud structure that ghost role spawns, actual spawn logic handled by /obj/effect/mob_spawn/ghost_role/venus_human_trap
/obj/structure/alien/resin/flower_bud //inheriting basic attack/damage stuff from alien structures
name = "flower bud"
desc = "A large pulsating plant..."
icon = 'icons/mob/spacevines.dmi'
icon_state = "bud0"
layer = SPACEVINE_MOB_LAYER
opacity = FALSE
canSmoothWith = null
smoothing_flags = NONE
density = FALSE
/// The amount of time it takes to create a venus human trap.
var/growth_time = 120 SECONDS
var/growth_icon = 0
/// Used by countdown to check time, this is when the timer will complete and the venus trap will spawn.
var/finish_time
/// The countdown ghosts see to when the plant will hatch
var/obj/effect/countdown/flower_bud/countdown
var/trait_flags = 0
var/list/vines = list()
/// The spawner that actually handles spawning the ghost role in
var/obj/effect/mob_spawn/ghost_role/venus_human_trap/spawner
/obj/structure/alien/resin/flower_bud/Initialize(mapload)
. = ..()
spawner = new(get_turf(loc))
spawner.flower_bud = src
countdown = new(src)
var/list/anchors = list()
anchors += locate(x-2,y+2,z)
anchors += locate(x+2,y+2,z)
anchors += locate(x-2,y-2,z)
anchors += locate(x+2,y-2,z)
for(var/turf/T in anchors)
vines += Beam(T, "vine", maxdistance=5, beam_type=/obj/effect/ebeam/vine)
finish_time = world.time + growth_time
addtimer(CALLBACK(src, PROC_REF(bear_fruit)), growth_time)
addtimer(CALLBACK(src, PROC_REF(progress_growth)), growth_time/4)
countdown.start()
/obj/structure/alien/resin/flower_bud/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
if((trait_flags & SPACEVINE_HEAT_RESISTANT) && damage_type == BURN)
damage_amount = 0
. = ..()
/obj/structure/alien/resin/flower_bud/attacked_by(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers)
LAZYSET(attack_modifiers, SILENCE_DEFAULT_MESSAGES, TRUE)
LAZYSET(attack_modifiers, FORCE_MULTIPLIER, 1)
if(item.damtype == BURN)
MODIFY_ATTACK_FORCE_MULTIPLIER(attack_modifiers, 4)
if(item.get_sharpness())
MODIFY_ATTACK_FORCE_MULTIPLIER(attack_modifiers, 16) // alien resin applies 75% reduction to brute damage so this actually x4 damage
return ..()
/obj/structure/alien/resin/flower_bud/Destroy()
QDEL_LIST(vines)
QDEL_NULL(countdown)
if(spawner) // anti harddel checks
if(!QDELETED(spawner))
qdel(spawner)
spawner = null
return ..()
/// Tells the spawner that the venus human trap is ready
/obj/structure/alien/resin/flower_bud/proc/bear_fruit()
visible_message(span_danger("The plant has borne fruit!"))
if(spawner)
spawner.bear_fruit()
/obj/structure/alien/resin/flower_bud/proc/progress_growth()
growth_icon++
icon_state = "bud[growth_icon]"
if(growth_icon == FINAL_BUD_GROWTH_ICON)
return
addtimer(CALLBACK(src, PROC_REF(progress_growth)), growth_time/4)
/obj/structure/alien/resin/flower_bud/attack_ghost(mob/user)
spawner.attack_ghost(user)
/obj/effect/ebeam/vine
name = "thick vine"
mouse_opacity = MOUSE_OPACITY_ICON
desc = "A thick vine, painful to the touch."
/obj/effect/ebeam/vine/Initialize(mapload)
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/effect/ebeam/vine/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
if(isliving(AM))
var/mob/living/L = AM
if(!isvineimmune(L))
L.adjust_brute_loss(5)
to_chat(L, span_alert("You cut yourself on the thorny vines."))
/**
* Venus Human Trap
*
* The result of a kudzu flower bud, these enemies use vines to drag prey close to them for attack.
*
* A carnivorious plant which uses vines to catch and ensnare prey. Spawns from kudzu flower buds.
* Each one can attach up to two temporary vines to objects or mobs and drag them around with it.
* Attempting to attach a vine to something with a vine already attached to it will pull all movable targets closer on command.
* Once the prey is in melee range, melee attacks from the venus human trap heals itself for 10% of its max health, assuming the target is alive.
* Akin to certain spiders, venus human traps can also be possessed and controlled by ghosts.
*
*/
/mob/living/basic/venus_human_trap
name = "venus human trap"
desc = "Now you know how the fly feels."
icon = 'icons/mob/spacevines.dmi'
icon_state = "venus_human_trap"
health_doll_icon = "venus_human_trap"
mob_biotypes = MOB_ORGANIC | MOB_PLANT
layer = SPACEVINE_MOB_LAYER
health = 100
maxHealth = 100
obj_damage = 60
melee_damage_lower = 10
melee_damage_upper = 20
minimum_survivable_temperature = 100
combat_mode = TRUE
basic_mob_flags = DEL_ON_DEATH
death_message = "collapses into bits of plant matter."
attacked_sound = 'sound/mobs/non-humanoids/venus_trap/venus_trap_hurt.ogg'
death_sound = 'sound/mobs/non-humanoids/venus_trap/venus_trap_death.ogg'
attack_sound = 'sound/mobs/non-humanoids/venus_trap/venus_trap_hit.ogg'
unsuitable_heat_damage = 5 // heat damage is different from cold damage since coldmos is significantly more common than plasmafires
unsuitable_cold_damage = 2 // they now do take cold damage, but this should be sufficiently small that it does not cause major issues
habitable_atmos = null
unsuitable_atmos_damage = 0
/// copied over from the code from eyeballs (the mob) to make it easier for venus human traps to see in kudzu that doesn't have the transparency mutation
sight = SEE_SELF|SEE_MOBS|SEE_OBJS|SEE_TURFS
// Real green, cause of course
lighting_cutoff_red = 10
lighting_cutoff_green = 35
lighting_cutoff_blue = 20
faction = list(FACTION_HOSTILE,FACTION_VINES,FACTION_PLANTS)
initial_language_holder = /datum/language_holder/venus
unique_name = TRUE
speed = 1.2
melee_attack_cooldown = 1.2 SECONDS
ai_controller = /datum/ai_controller/basic_controller/human_trap
///how much damage we take out of weeds
var/no_weed_damage = 12.5
///how much do we heal in weeds
var/weed_heal = 10
///if the balloon alert was shown atleast once, reset after healing in weeds
var/alert_shown = FALSE
/mob/living/basic/venus_human_trap/Initialize(mapload)
. = ..()
AddElement(/datum/element/lifesteal, 5)
var/static/list/innate_actions = list(
/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle = BB_TARGETED_ACTION,
)
grant_actions_by_list(innate_actions)
/mob/living/basic/venus_human_trap/RangedAttack(atom/victim)
if(!combat_mode)
return
var/datum/action/cooldown/mob_cooldown/tangle_ability = ai_controller.blackboard[BB_TARGETED_ACTION]
if(!istype(tangle_ability))
return
tangle_ability.Trigger(target = victim)
/mob/living/basic/venus_human_trap/Life(seconds_per_tick = SSMOBS_DT, times_fired)
. = ..()
if(!.)
return FALSE
var/vines_in_range = locate(/obj/structure/spacevine) in range(2, src)
if(!vines_in_range && !alert_shown)
alert_shown = TRUE
balloon_alert(src, "do not leave vines!")
else if(vines_in_range)
alert_shown = FALSE
adjust_brute_loss(vines_in_range ? -weed_heal : no_weed_damage) //every life tick take 20 damage if not near vines or heal 10 if near vines, 5 times out of weeds = u ded
/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle
name = "Tangle"
button_icon = 'icons/mob/spacevines.dmi'
button_icon_state = "Light1"
desc = "Grabs a target with a sticky vine, allowing you to pull it alongside you."
cooldown_time = 8 SECONDS
/// An assoc list of all the plant's vines (beam = leash)
var/list/datum/beam/vines = list()
/// How far away a plant can attach a vine to something
var/vine_grab_distance = 4
/// how long does a vine attached to something last (and its leash) (lasts twice as long on nonliving things)
var/vine_duration = 2 SECONDS
/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle/Remove(mob/remove_from)
QDEL_LIST(vines)
return ..()
/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle/Activate(atom/movable/target_atom)
if(!ismovable(target_atom) || istype(target_atom, /obj/structure/spacevine))
return
if(target_atom.anchored)
owner.balloon_alert(owner, "can't pull!")
return
if(get_dist(owner, target_atom) > vine_grab_distance)
owner.balloon_alert(owner, "too far!")
return
var/list/target_turfs = get_line(owner, target_atom) - list(get_turf(owner), get_turf(target_atom))
for(var/turf/blockage in target_turfs)
if(blockage.is_blocked_turf(exclude_mobs = TRUE))
owner.balloon_alert(owner, "path blocked!")
return
var/datum/beam/new_vine = owner.Beam(target_atom, icon_state = "vine", time = vine_duration * (ismob(target_atom) ? 1 : 2), beam_type = /obj/effect/ebeam/vine, emissive = FALSE)
var/component = target_atom.AddComponent(/datum/component/leash, owner, vine_grab_distance)
RegisterSignal(new_vine, COMSIG_QDELETING, PROC_REF(remove_vine), new_vine)
vines[new_vine] = component
if(isliving(target_atom))
var/mob/living/victim = target_atom
victim.Knockdown(2 SECONDS)
StartCooldown()
return TRUE
/**
* Removes a vine from the list.
*
* Removes the vine from our list.
* Called specifically when the vine is about to be destroyed, so we don't have any null references.
* Arguments:
* * datum/beam/vine - The vine to be removed from the list.
*/
/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle/proc/remove_vine(datum/beam/vine)
SIGNAL_HANDLER
qdel(vines[vine])
vines -= vine
/datum/ai_controller/basic_controller/human_trap
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
)
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/escape_captivity,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/targeted_mob_ability/continue_planning,
/datum/ai_planning_subtree/attack_obstacle_in_path,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
)
#undef FINAL_BUD_GROWTH_ICON