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

302 lines
11 KiB
Plaintext

/**
* Maintenance Drone
*
* Small player controlled fixer-upper
*
* The maintenace drone is a ghost role with the objective to repair and
* maintain the station.
*
* Featuring two dexterous hands, and a built in toolbox stocked with
* tools.
*
* They have laws to prevent them from doing anything else.
*
*/
/mob/living/basic/drone
name = "Drone"
desc = "A maintenance drone, an expendable robot built to perform station repairs."
icon = 'icons/mob/silicon/drone.dmi'
icon_state = "drone_maint_grey"
icon_living = "drone_maint_grey"
icon_dead = "drone_maint_dead"
health = 45
maxHealth = 45
unsuitable_atmos_damage = 0
unsuitable_cold_damage = 0
unsuitable_heat_damage = 0
speed = 0
density = FALSE
pass_flags = PASSTABLE | PASSMOB
sight = SEE_TURFS | SEE_OBJS
status_flags = (CANPUSH | CANSTUN | CANKNOCKDOWN)
gender = NEUTER
mob_biotypes = MOB_ROBOTIC
speak_emote = list("chirps")
speech_span = SPAN_ROBOT
bubble_icon = "machine"
initial_language_holder = /datum/language_holder/drone
mob_size = MOB_SIZE_SMALL
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, STAMINA = 0, OXY = 0)
hud_possible = list(DIAG_STAT_HUD, DIAG_HUD, ANTAG_HUD)
unique_name = TRUE
faction = list(FACTION_NEUTRAL,FACTION_SILICON,FACTION_TURRET)
hud_type = /datum/hud/dextrous/drone
// Going for a sort of pale green here
lighting_cutoff_red = 30
lighting_cutoff_green = 35
lighting_cutoff_blue = 25
can_be_held = TRUE
worn_slot_flags = ITEM_SLOT_HEAD
inhand_holder_type = /obj/item/mob_holder/drone
/// `TRUE` if we have picked our visual appearance, `FALSE` otherwise (default)
var/picked = FALSE
/// Stored drone color, restored when unhacked
var/colour = "grey"
var/list/drone_overlays[DRONE_TOTAL_LAYERS]
/// Drone laws announced on spawn
var/laws = \
"1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.\n"+\
"2. You may not harm any being, regardless of intent or circumstance.\n"+\
"3. Your goals are to actively build, maintain, repair, improve, and provide power to the best of your abilities within the facility that housed your activation." //for derelict drones so they don't go to station.
/// Amount of damage sustained if hit by a heavy EMP pulse
var/heavy_emp_damage = 25
///Alarm listener datum, handes caring about alarm events and such
var/datum/alarm_listener/listener
/// Internal storage slot. Fits any item
var/obj/item/internal_storage
/// Headwear slot
var/obj/item/head
/// Default [/mob/living/basic/drone/var/internal_storage] item
var/obj/item/default_storage = /obj/item/storage/drone_tools
/// Default [/mob/living/basic/drone/var/head] item
var/obj/item/default_headwear
///The camera built into the drone which allows it to be seen through cameras.
var/obj/machinery/camera/silicon/built_in_camera
/**
* icon_state of drone from icons/mobs/drone.dmi
*
* Possible states are:
*
* - [MAINTDRONE]
* - [REPAIRDRONE]
* - [SCOUTDRONE]
* - [CLOCKDRONE]
*/
var/visualAppearance = MAINTDRONE
/// Hacked state, see [/mob/living/basic/drone/proc/update_drone_hack]
var/hacked = FALSE
/// Whether this drone can be un-hacked. Used for subtypes that cannot be meaningfully "fixed".
var/can_unhack = TRUE
/// If we have laws to minimize bothering others. Enables or disables drone laws enforcement components (use [/mob/living/basic/drone/proc/set_shy] to set)
var/shy = TRUE
/// Flavor text announced to drones on [/mob/proc/Login]
var/flavortext = \
"\n<big><span class='warning'>DO NOT INTERFERE WITH THE ROUND AS A DRONE OR YOU WILL BE DRONE BANNED</span></big>\n"+\
"<span class='notice'>Drones are a ghost role that are allowed to fix the station and build things. Interfering with the round as a drone is against the rules.</span>\n"+\
"<span class='notice'>Actions that constitute interference include, but are not limited to:</span>\n"+\
"<span class='notice'> - Interacting with round critical objects (IDs, weapons, contraband, powersinks, bombs, etc.)</span>\n"+\
"<span class='notice'> - Interacting with living beings (communication, attacking, healing, etc.)</span>\n"+\
"<span class='notice'> - Interacting with non-living beings (dragging bodies, looting bodies, etc.)</span>\n"+\
"<span class='warning'>These rules are at admin discretion and will be heavily enforced.</span>\n"+\
"<span class='warning'><u>If you do not have the regular drone laws, follow your laws to the best of your ability.</u></span>\n"+\
"<span class='notice'>Prefix your message with :b to speak in Drone Chat.</span>\n"
/mob/living/basic/drone/Initialize(mapload)
. = ..()
GLOB.drones_list += src
AddElement(/datum/element/dextrous, hud_type = hud_type)
AddComponent(/datum/component/basic_inhands, y_offset = getItemPixelShiftY())
AddComponent(/datum/component/simple_access, SSid_access.get_region_access_list(list(REGION_ALL_GLOBAL)))
AddComponent(/datum/component/personal_crafting) // Kind of hard to be a drone and not be able to make tiles
LoadComponent(/datum/component/bloodysoles/bot)
// Only station drones get a camera.
if(is_station_level(src.loc.z))
built_in_camera = new(src)
built_in_camera.c_tag = real_name
built_in_camera.network = list(CAMERANET_NETWORK_SS13)
if(default_storage)
var/obj/item/storage = new default_storage(src)
equip_to_slot_or_del(storage, ITEM_SLOT_DEX_STORAGE)
for(var/holiday_name in GLOB.holidays)
var/datum/holiday/holiday_today = GLOB.holidays[holiday_name]
var/obj/item/potential_hat = holiday_today.holiday_hat
if(!isnull(potential_hat) && isnull(default_headwear)) //If our drone type doesn't start with a hat, we take the holiday one.
default_headwear = potential_hat
if(default_headwear)
var/obj/item/new_hat = new default_headwear(src)
equip_to_slot_or_del(new_hat, ITEM_SLOT_HEAD)
shy_update()
alert_drones(DRONE_NET_CONNECT)
var/datum/atom_hud/data/diagnostic/diag_hud = GLOB.huds[DATA_HUD_DIAGNOSTIC]
diag_hud.add_atom_to_hud(src)
add_traits(list(
TRAIT_VENTCRAWLER_ALWAYS,
TRAIT_NEGATES_GRAVITY,
TRAIT_LITERATE,
TRAIT_KNOW_ENGI_WIRES,
TRAIT_ADVANCEDTOOLUSER,
TRAIT_SILICON_ACCESS,
TRAIT_REAGENT_SCANNER,
TRAIT_UNOBSERVANT,
TRAIT_SILICON_EMOTES_ALLOWED,
), INNATE_TRAIT)
listener = new(list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), list(z))
RegisterSignal(listener, COMSIG_ALARM_LISTENER_TRIGGERED, PROC_REF(alarm_triggered))
RegisterSignal(listener, COMSIG_ALARM_LISTENER_CLEARED, PROC_REF(alarm_cleared))
listener.RegisterSignal(src, COMSIG_LIVING_DEATH, TYPE_PROC_REF(/datum/alarm_listener, prevent_alarm_changes))
listener.RegisterSignal(src, COMSIG_LIVING_REVIVE, TYPE_PROC_REF(/datum/alarm_listener, allow_alarm_changes))
/mob/living/basic/drone/med_hud_set_health()
set_hud_image_state(DIAG_HUD, "huddiag[RoundDiagBar(health/maxHealth)]")
/mob/living/basic/drone/med_hud_set_status()
if(stat == DEAD)
set_hud_image_state(DIAG_STAT_HUD, "huddead2")
return
if(incapacitated)
set_hud_image_state(DIAG_STAT_HUD, "hudoffline")
return
set_hud_image_state(DIAG_STAT_HUD, "hudstat")
/mob/living/basic/drone/Destroy()
GLOB.drones_list -= src
QDEL_NULL(listener)
QDEL_NULL(built_in_camera)
return ..()
/mob/living/basic/drone/Login()
. = ..()
if(!. || !client)
return FALSE
check_laws()
if(flavortext)
to_chat(src, "[flavortext]")
if(!picked)
pickVisualAppearance()
/mob/living/basic/drone/auto_deadmin_on_login()
if(!client?.holder)
return TRUE
if(CONFIG_GET(flag/auto_deadmin_silicons) || (client.prefs?.toggles & DEADMIN_POSITION_SILICON))
return client.holder.auto_deadmin()
return ..()
/mob/living/basic/drone/death(gibbed)
..(gibbed)
if(internal_storage)
dropItemToGround(internal_storage)
if(head)
dropItemToGround(head)
alert_drones(DRONE_NET_DISCONNECT)
/mob/living/basic/drone/gib()
dust()
/mob/living/basic/drone/get_butt_sprite()
return icon('icons/mob/butts.dmi', BUTT_SPRITE_DRONE)
/mob/living/basic/drone/examine(mob/user)
. = list()
//Hands
for(var/obj/item/held_thing in held_items)
if((held_thing.item_flags & (ABSTRACT|HAND_ITEM)) || HAS_TRAIT(held_thing, TRAIT_EXAMINE_SKIP))
continue
. += "It has [held_thing.examine_title(user)] in its [get_held_index_name(get_held_index_of_item(held_thing))]."
//Internal storage
if(internal_storage && !(internal_storage.item_flags & ABSTRACT))
. += "It is holding [internal_storage.examine_title(user)] in its internal storage."
//Cosmetic hat - provides no function other than looks
if(head && !(head.item_flags & ABSTRACT))
. += "It is wearing [head.examine_title(user)] on its head."
//Braindead
if(!client && stat != DEAD)
. += "Its status LED is blinking at a steady rate."
//Hacked
if(hacked)
. += span_warning("Its display is glowing red!")
//Damaged
if(health != maxHealth)
if(health > maxHealth * 0.33) //Between maxHealth and about a third of maxHealth, between 30 and 10 for normal drones
. += span_warning("Its screws are slightly loose.")
else //otherwise, below about 33%
. += span_boldwarning("Its screws are very loose!")
//Dead
if(stat == DEAD)
if(client)
. += span_deadsay("A message repeatedly flashes on its display: \"REBOOT -- REQUIRED\".")
else
. += span_deadsay("A message repeatedly flashes on its display: \"ERROR -- OFFLINE\".")
/mob/living/basic/drone/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //Secbots won't hunt maintenance drones.
return -10
/mob/living/basic/drone/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
Stun(70)
to_chat(src, span_danger("<b>ER@%R: MME^RY CO#RU9T!</b> R&$b@0tin)..."))
if(severity == 1)
adjust_brute_loss(heavy_emp_damage)
to_chat(src, span_userdanger("HeAV% DA%^MMA+G TO I/O CIR!%UUT!"))
/mob/living/basic/drone/proc/alarm_triggered(datum/source, alarm_type, area/source_area)
SIGNAL_HANDLER
to_chat(src, "--- [alarm_type] alarm detected in [source_area.name]!")
/mob/living/basic/drone/proc/alarm_cleared(datum/source, alarm_type, area/source_area)
SIGNAL_HANDLER
to_chat(src, "--- [alarm_type] alarm in [source_area.name] has been cleared.")
/mob/living/basic/drone/proc/set_shy(new_shy)
shy = new_shy
shy_update()
/mob/living/basic/drone/proc/shy_update()
if(shy)
REMOVE_TRAIT(src, TRAIT_CAN_STRIP, DRONE_SHY_TRAIT) // To shy to touch someone elses hat
ADD_TRAIT(src, TRAIT_PACIFISM, DRONE_SHY_TRAIT)
else
ADD_TRAIT(src, TRAIT_CAN_STRIP, DRONE_SHY_TRAIT) // ...I wonder if I can ware pants like a hat
REMOVE_TRAIT(src, TRAIT_PACIFISM, DRONE_SHY_TRAIT)
/mob/living/basic/drone/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25)
if(affect_silicon)
return ..()
/mob/living/basic/drone/bee_friendly()
// Why would bees pay attention to drones?
return TRUE
/mob/living/basic/drone/electrocute_act(shock_damage, source, siemens_coeff, flags = NONE)
return FALSE //So they don't die trying to fix wiring
/mob/living/basic/drone/can_track(mob/living/user)
if(built_in_camera?.can_use())
return TRUE
return ..()
/mob/living/basic/drone/hypnosis_vulnerable()
return FALSE //It obeys its laws