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

292 lines
12 KiB
Plaintext

GLOBAL_LIST_EMPTY(heretic_arenas)
// Invisible effect that doesnt exist outside of containing the prox monitor
/obj/effect/abstract/heretic_arena
icon = null
icon_state = null
alpha = 0
invisibility = INVISIBILITY_ABSTRACT
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
resistance_flags = INDESTRUCTIBLE
/// Proximity monitor that handles the effects we are looking for
var/datum/proximity_monitor/advanced/heretic_arena/arena
/obj/effect/abstract/heretic_arena/Initialize(mapload, range, duration, caster)
. = ..()
arena = new(src, range)
QDEL_IN(src, duration)
arena.set_caster(caster)
GLOB.heretic_arenas += src
/obj/effect/abstract/heretic_arena/Destroy(force)
QDEL_NULL(arena)
GLOB.heretic_arenas -= src
. = ..()
/datum/proximity_monitor/advanced/heretic_arena
/// Reference to the caster, the spell collapses if they leave the arena
var/arena_caster
/// List of mobs inside our arena
var/list/contained_mobs = list()
/// List of border walls we have placed on the edges of the monitor
var/list/border_walls = list()
/// List of blades we've so generously handed out to the participants
var/list/welfare_blades = list()
/// List of immunities given to our combatants
var/static/list/given_immunities = list(
TRAIT_BOMBIMMUNE,
TRAIT_IGNORESLOWDOWN,
TRAIT_NO_SLIP_ALL,
TRAIT_NOBREATH,
TRAIT_PIERCEIMMUNE,
TRAIT_PUSHIMMUNE,
TRAIT_RADIMMUNE,
TRAIT_RESISTCOLD,
TRAIT_RESISTHEAT,
TRAIT_RESISTHIGHPRESSURE,
TRAIT_RESISTLOWPRESSURE,
TRAIT_SHOCKIMMUNE,
TRAIT_SLEEPIMMUNE,
TRAIT_STUNIMMUNE,
TRAIT_FORCED_GRAVITY,
)
/datum/proximity_monitor/advanced/heretic_arena/New(atom/_host, range, _ignore_if_not_on_turf)
. = ..()
recalculate_field(full_recalc = TRUE)
var/list/things_in_range = range(range)
for(var/mob/living/carbon/human/human_in_range in things_in_range)
human_in_range.add_traits(given_immunities, HERETIC_ARENA_TRAIT)
contained_mobs += human_in_range
if(!IS_HERETIC(human_in_range))
var/obj/item/melee/sickly_blade/training/new_blade = new(get_turf(human_in_range))
welfare_blades += new_blade
INVOKE_ASYNC(human_in_range, TYPE_PROC_REF(/mob, put_in_hands), new_blade)
to_chat(human_in_range, span_boldbig("Escape is impossible. The only way out is to defeat another participant in this battle to the death."))
human_in_range.balloon_alert(human_in_range, "start killing!")
human_in_range.apply_status_effect(/datum/status_effect/arena_tracker)
RegisterSignal(human_in_range, COMSIG_CAN_Z_MOVE, PROC_REF(on_try_z_move))
RegisterSignal(human_in_range, COMSIG_LADDER_TRAVEL, PROC_REF(on_try_ladder))
RegisterSignal(human_in_range, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_pre_move))
RegisterSignal(human_in_range, COMSIG_MOVABLE_POST_TELEPORT, PROC_REF(on_teleport))
/datum/proximity_monitor/advanced/heretic_arena/Destroy()
for(var/mob/living/carbon/human/mob in contained_mobs)
mob.remove_traits(given_immunities, HERETIC_ARENA_TRAIT)
mob.remove_status_effect(/datum/status_effect/arena_tracker)
UnregisterSignal(mob, list(COMSIG_CAN_Z_MOVE, COMSIG_LADDER_TRAVEL, COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_POST_TELEPORT))
to_chat(mob, span_boldbig("Your bloodlust is sated."))
mob.balloon_alert(mob, "escape the arena!")
for(var/turf/to_restore in border_walls)
to_restore.ChangeTurf(border_walls[to_restore])
for(var/obj/to_refund as anything in welfare_blades)
qdel(to_refund)
arena_caster = null
return ..()
/datum/proximity_monitor/advanced/heretic_arena/setup_edge_turf(turf/target)
. = ..()
var/old_turf = target.type
target.ChangeTurf(/turf/closed/indestructible/heretic_wall)
border_walls += target
border_walls[target] += old_turf
/datum/proximity_monitor/advanced/heretic_arena/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location)
if(!isliving(movable))
return
var/mob/living/living_mob = movable
addtimer(CALLBACK(living_mob, TYPE_PROC_REF(/mob/living, remove_status_effect), /datum/status_effect/arena_tracker), 10 SECONDS)
living_mob.remove_traits(given_immunities, HERETIC_ARENA_TRAIT)
if(living_mob == arena_caster)
QDEL_IN(host, 3 SECONDS)
/// Prevents using ladders
/datum/proximity_monitor/advanced/heretic_arena/proc/on_try_ladder(mob/climber)
SIGNAL_HANDLER
return LADDER_TRAVEL_BLOCK
/// If we try to enter a space turf that has a mirage, we will block the movement
/datum/proximity_monitor/advanced/heretic_arena/proc/on_pre_move(atom/movable/mover, atom/newloc)
if(locate(/atom/movable/mirage_holder) in newloc.contents)
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
/// Blocks Z movement to new z levels
/datum/proximity_monitor/advanced/heretic_arena/proc/on_try_z_move(atom/movable/source, turf/start, turf/destination)
SIGNAL_HANDLER
if(start.z == destination.z)
return
return COMPONENT_CANT_Z_MOVE
/// If our caster teleports away (after winning presumably) we'll collapse the arena so that it doens't needlessly linger
/datum/proximity_monitor/advanced/heretic_arena/proc/on_teleport(atom/teleportee, atom/destination, channel)
if(teleportee == arena_caster)
qdel(host)
/datum/proximity_monitor/advanced/heretic_arena/proc/set_caster(atom/caster)
arena_caster = caster
/turf/closed/indestructible/heretic_wall
name = "eldritch wall"
desc = "A wall penning in the sheep amongst the wolves. It glows with malevolent energy - prodding it is likely unwise."
icon = 'icons/turf/walls.dmi'
icon_state = "eldritch_forcewall"
opacity = FALSE
pass_flags_self = NONE // No PASSCLOSEDTURF because only arena victors are allowed to go in or out
/turf/closed/indestructible/heretic_wall/CanAllowThrough(atom/movable/mover, border_dir)
if(isliving(mover))
var/mob/living/living_mover = mover
var/datum/status_effect/arena_tracker/tracker = living_mover.has_status_effect(/datum/status_effect/arena_tracker)
if(tracker?.arena_victor)
return TRUE
return ..()
/turf/closed/indestructible/heretic_wall/Bumped(atom/movable/bumped_atom)
. = ..()
if(!isliving(bumped_atom))
return
var/mob/living/living_mob = bumped_atom
var/atom/target = get_edge_target_turf(living_mob, get_dir(src, get_step_away(living_mob, src)))
living_mob.throw_at(target, 4, 5)
to_chat(living_mob, span_userdanger("The wall repels you with tremendous force!"))
/// Called when you crit somebody to update your crown
/datum/status_effect/arena_tracker/proc/on_crit_somebody()
owner.cut_overlay(crown_overlay)
crown_overlay = mutable_appearance('icons/mob/effects/crown.dmi', "arena_victor", -HALO_LAYER)
crown_overlay.pixel_z = 24
owner.add_overlay(crown_overlay)
owner.remove_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id))
// The mansus celebrates your efforts
if(IS_HERETIC(owner))
owner.heal_overall_damage(60, 60, 60)
owner.adjust_tox_loss(-60, forced = TRUE) // Slime heretics everywhere...
owner.adjust_oxy_loss(-60)
if(iscarbon(owner))
var/mob/living/carbon/carbon_owner = owner
for(var/datum/wound/wound as anything in carbon_owner.all_wounds)
wound.remove_wound()
if(arena_victor) // No need to spam if we've already killed at least 1 person
return
if(IS_HERETIC(owner))
to_chat(owner, span_big(span_hypnophrase("The mansus is pleased with your performance, you may leave now.")))
else
to_chat(owner, span_big(span_hypnophrase("You have done well, you may leave now.")))
arena_victor = TRUE
/**
* Status applied to every mob in the heretic arena.
* Tracks the last person to damage owner.
* When owner enters crit, we send a signal to last_attacker status so they can leave the arena
*/
/datum/status_effect/arena_tracker
id = "arena_tracker"
duration = STATUS_EFFECT_PERMANENT
tick_interval = STATUS_EFFECT_NO_TICK
status_type = STATUS_EFFECT_UNIQUE
alert_type = null
/// Tracks the last person who dealt damage to this mob
var/datum/weakref/last_attacker
/// If our mob is free to leave, set to true
var/arena_victor = FALSE
/// The overlay for our mob, changes color to indicate that they are a victor and are free to leave
var/mutable_appearance/crown_overlay
/datum/status_effect/arena_tracker/on_apply()
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), PROC_REF(on_enter_crit))
RegisterSignal(owner, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(on_impact_zone))
RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(damage_taken))
owner.add_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id))
crown_overlay = mutable_appearance('icons/mob/effects/crown.dmi', "arena_fighter", -HALO_LAYER)
crown_overlay.pixel_z = 24
owner.add_overlay(crown_overlay)
return TRUE
/datum/status_effect/arena_tracker/on_remove()
UnregisterSignal(owner, list(SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), COMSIG_MOB_APPLY_DAMAGE))
owner.remove_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id))
owner.cut_overlay(crown_overlay)
crown_overlay = null
// If our last attacker is an arena participant, we let them know they've scored a critical hit
/datum/status_effect/arena_tracker/proc/on_enter_crit(mob/owner)
SIGNAL_HANDLER
if(!last_attacker)
return // Safety check in case they somehow enter crit with *nobody* attacking them
var/mob/living/our_attacker = last_attacker.resolve()
if(!isliving(our_attacker) || our_attacker == owner) // We don't allow people to crit themselves as a valid way to escape
return
var/datum/status_effect/arena_tracker/their_tracker = our_attacker.has_status_effect(/datum/status_effect/arena_tracker)
if(!their_tracker)
return // Somebody killed us who isn't an arena participant
their_tracker.on_crit_somebody()
/datum/status_effect/arena_tracker/proc/damage_taken(
datum/source,
damage_amount,
damagetype,
def_zone,
blocked,
wound_bonus,
exposed_wound_bonus,
sharpness,
attack_direction,
attacking_item,
wound_clothing,
)
SIGNAL_HANDLER
if(isnull(attacking_item))
return
if(!isobj(attacking_item))
return
var/obj/attacking_object = attacking_item
// Track being hit by a mob holding a stick
if(ismob(attacking_object.loc))
last_attacker = WEAKREF(attacking_object.loc)
return
// Edge case. If our attacking_item is a gun which the owner has dropped we need to find out who shot us
// Track being hit by a mob shooting a stick
if(isprojectile(attacking_object))
var/obj/projectile/attacking_projectile = attacking_object
if(ismob(attacking_projectile.firer))
last_attacker = WEAKREF(attacking_projectile.firer)
///Called when impacted by something thrown at us, setting the last attacker to the person throwing the item.
/datum/status_effect/arena_tracker/proc/on_impact_zone(atom/source, mob/living/hitby, zone, blocked, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
// Track being hit by a mob throwing a stick
if(!isitem(throwingdatum.thrownthing))
return
var/thrown_by = throwingdatum?.get_thrower()
if(ismob(thrown_by))
last_attacker = WEAKREF(thrown_by)
/datum/antagonist/heretic_arena_participant
name = "Arena Participant"
show_in_roundend = FALSE
replace_banned = FALSE
objectives = list()
antag_hud_name = "brainwashed"
antag_flags = ANTAG_FAKE
/datum/antagonist/heretic_arena_participant/on_gain()
forge_objectives()
return ..()
/datum/antagonist/heretic_arena_participant/forge_objectives()
var/datum/objective/survive = new /datum/objective
survive.owner = owner
survive.explanation_text = "You have been trapped in an arena. The only way out is to slaughter someone else. Kill your captor, or betray your friends - the choice is yours."
objectives += survive
var/datum/objective/fight_to_escape = new /datum/objective
fight_to_escape.owner = owner
fight_to_escape.explanation_text = "Escape is impossible. The only way out is to defeat another participant in this battle to the death. \
A weapon has been bestowed unto you, granting you a fighting chance, it would be quite a shame were you to attempt to break it."
objectives += fight_to_escape