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

280 lines
9.9 KiB
Plaintext

/mob/living/basic/mining_drone
name = "\improper Nanotrasen minebot"
desc = "The instructions printed on the side read: This is a small robot used to support miners, can be set to search and collect loose ore, or to help fend off wildlife."
gender = NEUTER
icon = 'icons/mob/silicon/aibots.dmi'
icon_state = "mining_drone"
icon_living = "mining_drone"
basic_mob_flags = DEL_ON_DEATH
status_flags = CANSTUN|CANKNOCKDOWN|CANPUSH
mouse_opacity = MOUSE_OPACITY_ICON
combat_mode = TRUE
habitable_atmos = null
minimum_survivable_temperature = 0
health = 125
maxHealth = 125
melee_damage_lower = 15
melee_damage_upper = 15
obj_damage = 10
attack_verb_continuous = "drills"
attack_verb_simple = "drill"
attack_sound = 'sound/items/weapons/circsawhit.ogg'
sentience_type = SENTIENCE_MINEBOT
speak_emote = list("states")
mob_biotypes = MOB_ROBOTIC
faction = list(FACTION_STATION, FACTION_NEUTRAL)
death_message = "blows apart!"
light_system = OVERLAY_LIGHT
light_range = 6
// I want this to be a bit more dim, for vibes
light_power = 0.6
light_color = "#ff9933"
light_on = FALSE
combat_mode = FALSE
ai_controller = /datum/ai_controller/basic_controller/minebot
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1)
///the gun we use to kill
var/obj/item/gun/energy/recharge/kinetic_accelerator/minebot/stored_gun
///our normal overlay
var/mutable_appearance/neutral_overlay
///our combat mode overlay
var/mutable_appearance/combat_overlay
///our current color, if any
var/selected_color
///the commands our owner can give us
var/static/list/pet_commands = list(
/datum/pet_command/idle/minebot,
/datum/pet_command/move,
/datum/pet_command/protect_owner/minebot,
/datum/pet_command/minebot_ability/light,
/datum/pet_command/minebot_ability/dump,
/datum/pet_command/automate_mining,
/datum/pet_command/free/minebot,
/datum/pet_command/follow/start_active,
/datum/pet_command/attack/minebot,
)
///possible colors the bot can have
var/static/list/possible_colors= list(
"Default" = null, //default color state
"Blue" = "#70d5e7",
"Red" = "#ee7fb9",
"Green" = "#5fea94",
)
/mob/living/basic/mining_drone/Initialize(mapload)
. = ..()
neutral_overlay = mutable_appearance(icon = 'icons/mob/silicon/aibots.dmi', icon_state = "mining_drone_grey")
combat_overlay = mutable_appearance(icon = 'icons/mob/silicon/aibots.dmi', icon_state = "mining_drone_offense_grey")
AddComponent(/datum/component/obeys_commands, pet_commands)
AddElement(/datum/element/death_drops, /obj/effect/decal/cleanable/blood/gibs/robot_debris/old)
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE, TRAIT_MINING_AOE_IMMUNE), INNATE_TRAIT)
AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE)
var/static/list/innate_actions = list(
/datum/action/cooldown/mob_cooldown/missile_launcher = BB_MINEBOT_MISSILE_ABILITY,
/datum/action/cooldown/mob_cooldown/drop_landmine = BB_MINEBOT_LANDMINE_ABILITY,
/datum/action/cooldown/mob_cooldown/minedrone/toggle_light = BB_MINEBOT_LIGHT_ABILITY,
/datum/action/cooldown/mob_cooldown/minedrone/toggle_meson_vision = null,
/datum/action/cooldown/mob_cooldown/minedrone/dump_ore = BB_MINEBOT_DUMP_ABILITY,
)
grant_actions_by_list(innate_actions)
stored_gun = new(src)
var/obj/item/implant/radio/mining/comms = new(src)
comms.implant(src)
assign_access()
/mob/living/basic/mining_drone/set_combat_mode(new_mode, silent = TRUE)
. = ..()
icon_state = combat_mode ? "mining_drone_offense" : "mining_drone"
balloon_alert(src, "now [combat_mode ? "attacking" : "collecting"]")
/mob/living/basic/mining_drone/examine(mob/user)
. = ..()
if(health < maxHealth)
if(health >= maxHealth * 0.5)
. += span_warning("[p_They()] look slightly dented.")
else
. += span_boldwarning("[p_They()] look severely dented!")
if(isnull(stored_gun) || !stored_gun.max_mod_capacity)
return
. += "<b>[stored_gun.get_remaining_mod_capacity()]%</b> mod capacity remaining."
for(var/obj/item/borg/upgrade/modkit/modkit as anything in stored_gun.modkits)
. += span_notice("There is \a [modkit] installed, using <b>[modkit.cost]%</b> capacity.")
if(ai_controller && ai_controller.ai_status == AI_STATUS_IDLE)
. += "The [src] appears to be in <b>sleep mode</b>. You can restore normal functions by <b>tapping</b> it."
/mob/living/basic/mining_drone/welder_act(mob/living/user, obj/item/welder)
if(user.combat_mode)
return FALSE
if(combat_mode)
user.balloon_alert(user, "can't repair in attack mode!")
return TRUE
if(maxHealth == health)
user.balloon_alert(user, "at full integrity!")
return TRUE
if(welder.use_tool(src, user, 0, volume=40))
adjust_brute_loss(-15)
user.balloon_alert(user, "successfully repaired!")
return TRUE
/mob/living/basic/mining_drone/attackby(obj/item/item_used, mob/user, list/modifiers, list/attack_modifiers)
if(item_used.tool_behaviour == TOOL_CROWBAR || istype(item_used, /obj/item/borg/upgrade/modkit))
item_used.melee_attack_chain(user, stored_gun, modifiers)
return
return ..()
/mob/living/basic/mining_drone/attack_hand(mob/living/carbon/human/user, list/modifiers)
if(!user.combat_mode)
if(ai_controller && ai_controller.ai_status == AI_STATUS_IDLE)
ai_controller.set_ai_status(AI_STATUS_ON)
if(LAZYACCESS(modifiers, LEFT_CLICK)) //Lets Right Click be specifically for re-enabling their AI (and avoiding the UI popup), while Left Click simply does both.
ui_interact(user)
return
return ..()
/mob/living/basic/mining_drone/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "MineBot", name)
ui.open()
/mob/living/basic/mining_drone/ui_data(mob/user)
var/list/data = list()
data["auto_defend"] = ai_controller.blackboard[BB_MINEBOT_AUTO_DEFEND]
data["repair_node_drone"] = ai_controller.blackboard[BB_MINEBOT_REPAIR_DRONE]
data["plant_mines"] = ai_controller.blackboard[BB_MINEBOT_PLANT_MINES]
data["bot_maintain_distance"] = ai_controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE]
data["bot_name"] = name
data["bot_mode"] = combat_mode
data["bot_health"] = health
data["bot_maxhealth"] = maxHealth
data["bot_color"] = ""
var/color_value = neutral_overlay.color
for(var/index in possible_colors)
if(possible_colors[index] == color_value)
data["bot_color"] = index
break
return data
/mob/living/basic/mining_drone/ui_static_data(mob/user)
var/list/data = list()
data["bot_icon"] = icon2base64(getFlatIcon(src, no_anim = TRUE))
data["possible_colors"] = list()
for(var/color in possible_colors)
data["possible_colors"] += list(list(
"color_name" = color,
"color_value" = possible_colors[color],
))
return data
/mob/living/basic/mining_drone/ui_act(action, params, datum/tgui/ui)
. = ..()
switch(action)
if("change_min_distance")
var/new_distance = clamp(params["distance"], 0, 5)
ai_controller.set_blackboard_key(BB_MINIMUM_SHOOTING_DISTANCE, new_distance)
if("toggle_defend")
var/new_toggle = !ai_controller.blackboard[BB_MINEBOT_AUTO_DEFEND]
ai_controller.set_blackboard_key(BB_MINEBOT_AUTO_DEFEND, new_toggle)
if("toggle_repair")
var/new_defend = !ai_controller.blackboard[BB_MINEBOT_REPAIR_DRONE]
ai_controller.set_blackboard_key(BB_MINEBOT_REPAIR_DRONE, new_defend)
if("toggle_mines")
var/new_mines = !ai_controller.blackboard[BB_MINEBOT_PLANT_MINES]
ai_controller.set_blackboard_key(BB_MINEBOT_PLANT_MINES, new_mines)
if("set_name")
var/input_name = sanitize_name(params["chosen_name"], allow_numbers = TRUE)
name = (input_name ? input_name : initial(name))
if("toggle_mode")
set_combat_mode(!combat_mode)
if("set_color")
change_color(params["chosen_color"])
update_static_data(ui.user, ui)
return TRUE
/mob/living/basic/mining_drone/proc/change_color(new_color)
selected_color = new_color
if(!isnull(selected_color))
neutral_overlay.color = selected_color
combat_overlay.color = selected_color
update_appearance()
/mob/living/basic/mining_drone/click_alt(mob/living/user)
if(user.combat_mode)
return CLICK_ACTION_BLOCKING
set_combat_mode(!combat_mode)
balloon_alert(user, "now [combat_mode ? "attacking wildlife" : "collecting loose ore"]")
return CLICK_ACTION_SUCCESS
/mob/living/basic/mining_drone/RangedAttack(atom/target, list/modifiers)
if(!combat_mode)
return
stored_gun.try_fire_gun(target, src, list2params(modifiers))
/mob/living/basic/mining_drone/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
. = ..()
if(!. || !proximity_flag || combat_mode)
return
if(istype(attack_target, /obj/item/stack/ore))
var/obj/item/target_ore = attack_target
target_ore.forceMove(src)
/mob/living/basic/mining_drone/proc/drop_ore()
to_chat(src, span_notice("You dump your stored ore."))
for(var/obj/item/stack/ore/dropped_item in contents)
dropped_item.forceMove(get_turf(src))
/mob/living/basic/mining_drone/death(gibbed)
drop_ore()
if(isnull(stored_gun))
return ..()
for(var/obj/item/borg/upgrade/modkit/modkit as anything in stored_gun.modkits)
modkit.uninstall(stored_gun)
return ..()
/mob/living/basic/mining_drone/Destroy()
QDEL_NULL(stored_gun)
return ..()
/mob/living/basic/mining_drone/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(!istype(target, /mob/living/basic/node_drone))
return TRUE
repair_node_drone(target)
return FALSE
/mob/living/basic/mining_drone/proc/repair_node_drone(mob/living/my_target)
do_sparks(5, FALSE, source = my_target)
if(!do_after(src, 6 SECONDS, my_target))
return
my_target.heal_overall_damage(brute = 50)
/mob/living/basic/mining_drone/update_overlays()
. = ..()
if(stat == DEAD || isnull(selected_color))
return
. += combat_mode ? combat_overlay : neutral_overlay
/mob/living/basic/mining_drone/proc/assign_access()
var/static/list/required_access
if(isnull(required_access))
var/datum/id_trim/access_card = SSid_access.trim_singletons_by_path[/datum/id_trim/job/shaft_miner]
required_access = access_card.access
AddElement(/datum/element/mob_access, required_access)