mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-15 18:14:22 +01:00
7a3ad79506
## 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! /🆑
291 lines
9.1 KiB
Plaintext
291 lines
9.1 KiB
Plaintext
/// Shoots a random, fake projectile to the hallucinator
|
|
/datum/hallucination/stray_bullet
|
|
random_hallucination_weight = 7
|
|
hallucination_tier = HALLUCINATION_TIER_UNCOMMON
|
|
|
|
/datum/hallucination/stray_bullet/start()
|
|
var/list/turf/starting_locations = list()
|
|
for(var/turf/open/open_out_of_view in view(world.view + 1, hallucinator) - view(world.view, hallucinator))
|
|
starting_locations += open_out_of_view
|
|
if(!length(starting_locations))
|
|
return FALSE
|
|
|
|
var/turf/start = pick(starting_locations)
|
|
var/fake_type = pick(subtypesof(/obj/projectile/hallucination))
|
|
|
|
feedback_details += "Type: [fake_type], Source: ([start.x], [start.y], [start.z])"
|
|
|
|
var/obj/projectile/hallucination/fake_projectile = new fake_type(start, src)
|
|
|
|
fake_projectile.aim_projectile(hallucinator, start)
|
|
fake_projectile.fire()
|
|
|
|
QDEL_IN(src, 10 SECONDS) // Should clean up the projectile if it somehow gets stuck.
|
|
return TRUE
|
|
|
|
/// Hallucinated projectiles.
|
|
/obj/projectile/hallucination
|
|
name = "bullet"
|
|
icon = null
|
|
icon_state = null
|
|
hitsound = ""
|
|
suppressed = SUPPRESSED_VERY
|
|
ricochets_max = 0
|
|
ricochet_chance = 0
|
|
damage = 0
|
|
log_override = TRUE
|
|
do_not_log = TRUE
|
|
/// Our parent hallucination that's created us
|
|
var/datum/hallucination/parent
|
|
/// The image that represents our projectile itself
|
|
var/image/fake_bullet
|
|
|
|
var/hal_icon = 'icons/obj/weapons/guns/projectiles.dmi'
|
|
var/hal_icon_state
|
|
var/hal_fire_sound
|
|
var/hal_hitsound
|
|
var/hal_hitsound_wall
|
|
var/hal_impact_effect
|
|
var/hal_impact_effect_wall
|
|
|
|
var/hit_duration
|
|
var/hit_duration_wall
|
|
|
|
/obj/projectile/hallucination/Initialize(mapload, datum/hallucination/parent)
|
|
. = ..()
|
|
if(!parent)
|
|
stack_trace("[type] was created without a parent hallucination.")
|
|
return INITIALIZE_HINT_QDEL
|
|
|
|
src.parent = parent
|
|
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(parent_deleting))
|
|
|
|
|
|
/obj/projectile/hallucination/Destroy()
|
|
if(!QDELETED(parent.hallucinator))
|
|
parent.hallucinator.client?.images -= fake_bullet
|
|
fake_bullet = null
|
|
|
|
UnregisterSignal(parent, COMSIG_QDELETING)
|
|
parent = null
|
|
return ..()
|
|
|
|
/// Signal proc for [COMSIG_QDELETING], if our associated hallucination deletes, we need to clean up
|
|
/obj/projectile/hallucination/proc/parent_deleting(datum/source)
|
|
SIGNAL_HANDLER
|
|
|
|
qdel(src)
|
|
|
|
/obj/projectile/hallucination/fire()
|
|
if(hal_fire_sound)
|
|
parent.hallucinator.playsound_local(get_turf(src), hal_fire_sound, 60, TRUE)
|
|
|
|
fake_bullet = image(hal_icon, src, hal_icon_state, ABOVE_MOB_LAYER)
|
|
parent.hallucinator.client?.images += fake_bullet
|
|
return ..()
|
|
|
|
/obj/projectile/hallucination/on_hit(atom/target, blocked, pierce_hit)
|
|
. = ..()
|
|
if(. != BULLET_ACT_HIT)
|
|
return
|
|
|
|
if(ismob(target))
|
|
if(hal_hitsound)
|
|
parent.hallucinator.playsound_local(target, hal_hitsound, 100, 1)
|
|
on_mob_hit(target)
|
|
|
|
else
|
|
if(hal_hitsound_wall)
|
|
parent.hallucinator.playsound_local(loc, hal_hitsound_wall, 40, 1)
|
|
if(hal_impact_effect_wall)
|
|
spawn_hit(target, TRUE)
|
|
|
|
qdel(src)
|
|
|
|
/// Called when a mob is hit by the fake projectile
|
|
/obj/projectile/hallucination/proc/on_mob_hit(mob/living/hit_mob)
|
|
if(hit_mob == parent.hallucinator)
|
|
to_chat(parent.hallucinator, span_userdanger("[hit_mob] is hit by \a [src] in the chest!"))
|
|
apply_effect_to_hallucinator(parent.hallucinator)
|
|
|
|
else if(hit_mob in view(parent.hallucinator))
|
|
to_chat(parent.hallucinator, span_danger("[hit_mob] is hit by \a [src] in the chest!"))
|
|
|
|
if(damage_type == BRUTE)
|
|
var/splatter_dir = dir
|
|
if(starting)
|
|
splatter_dir = get_dir(starting, get_turf(hit_mob))
|
|
spawn_blood(hit_mob, splatter_dir)
|
|
|
|
else if(hal_impact_effect)
|
|
spawn_hit(hit_mob, FALSE)
|
|
|
|
/// Called when the hallucinator themselves are hit by the fake projectile
|
|
/obj/projectile/hallucination/proc/apply_effect_to_hallucinator(mob/living/afflicted)
|
|
return
|
|
|
|
/// Called after a mob is hit by the fake projectile, and our fake projectile is of brute type, to create fake blood
|
|
/obj/projectile/hallucination/proc/spawn_blood(mob/living/bleeding, set_dir)
|
|
if(!parent.hallucinator.client) // Purely visual, don't need to do this for clientless mobs
|
|
return
|
|
|
|
var/splatter_icon_state
|
|
if(ISDIAGONALDIR(set_dir))
|
|
splatter_icon_state = "splatter[pick(1, 2, 6)]"
|
|
else
|
|
splatter_icon_state = "splatter[pick(3, 4, 5)]"
|
|
|
|
var/image/blood = image('icons/effects/blood.dmi', bleeding, splatter_icon_state, ABOVE_MOB_LAYER)
|
|
var/target_pixel_x = 0
|
|
var/target_pixel_y = 0
|
|
switch(set_dir)
|
|
if(NORTH)
|
|
target_pixel_y = 16
|
|
if(SOUTH)
|
|
target_pixel_y = -16
|
|
layer = ABOVE_MOB_LAYER
|
|
if(EAST)
|
|
target_pixel_x = 16
|
|
if(WEST)
|
|
target_pixel_x = -16
|
|
if(NORTHEAST)
|
|
target_pixel_x = 16
|
|
target_pixel_y = 16
|
|
if(NORTHWEST)
|
|
target_pixel_x = -16
|
|
target_pixel_y = 16
|
|
if(SOUTHEAST)
|
|
target_pixel_x = 16
|
|
target_pixel_y = -16
|
|
layer = ABOVE_MOB_LAYER
|
|
if(SOUTHWEST)
|
|
target_pixel_x = -16
|
|
target_pixel_y = -16
|
|
layer = ABOVE_MOB_LAYER
|
|
|
|
parent.hallucinator.client?.images |= blood
|
|
animate(blood, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = 5)
|
|
addtimer(CALLBACK(src, PROC_REF(clean_up_blood), blood), 0.5 SECONDS)
|
|
|
|
/obj/projectile/hallucination/proc/clean_up_blood(image/blood)
|
|
parent.hallucinator.client?.images -= blood
|
|
|
|
/// Called with a non-mob atom was hit by our fake projectile, or a mob was hit and our damge type is not brute
|
|
/obj/projectile/hallucination/proc/spawn_hit(atom/hit_atom, is_wall)
|
|
if(!parent.hallucinator.client) // Purely visual, don't need to do this for clientless mobs
|
|
return
|
|
|
|
var/image/hit_effect = image('icons/effects/blood.dmi', hit_atom, is_wall ? hal_impact_effect_wall : hal_impact_effect, ABOVE_MOB_LAYER)
|
|
hit_effect.pixel_w = hit_atom.pixel_x + rand(-4,4)
|
|
hit_effect.pixel_z = hit_atom.pixel_y + rand(-4,4)
|
|
parent.hallucinator.client.images |= hit_effect
|
|
addtimer(CALLBACK(src, PROC_REF(clean_up_hit), hit_effect), is_wall ? hit_duration_wall : hit_duration)
|
|
|
|
/obj/projectile/hallucination/proc/clean_up_hit(image/hit_effect)
|
|
parent.hallucinator.client?.images -= hit_effect
|
|
|
|
/obj/projectile/hallucination/bullet
|
|
name = "bullet"
|
|
hal_icon_state = "bullet"
|
|
hal_fire_sound = "gunshot"
|
|
hal_hitsound = 'sound/items/weapons/pierce.ogg'
|
|
hal_hitsound_wall = SFX_RICOCHET
|
|
hal_impact_effect = "impact_bullet"
|
|
hal_impact_effect_wall = "impact_bullet"
|
|
hit_duration = 5
|
|
hit_duration_wall = 5
|
|
|
|
/obj/projectile/hallucination/bullet/apply_effect_to_hallucinator(mob/living/afflicted)
|
|
afflicted.adjust_stamina_loss(60)
|
|
|
|
/obj/projectile/hallucination/laser
|
|
name = "laser"
|
|
damage_type = BURN
|
|
hal_icon_state = "laser"
|
|
hal_fire_sound = 'sound/items/weapons/laser.ogg'
|
|
hal_hitsound = 'sound/items/weapons/sear.ogg'
|
|
hal_hitsound_wall = 'sound/items/weapons/effects/searwall.ogg'
|
|
hal_impact_effect = "impact_laser"
|
|
hal_impact_effect_wall = "impact_laser_wall"
|
|
hit_duration = 4
|
|
hit_duration_wall = 10
|
|
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
|
|
|
|
ricochets_max = 50
|
|
ricochet_chance = 80
|
|
reflectable = TRUE
|
|
|
|
/obj/projectile/hallucination/laser/apply_effect_to_hallucinator(mob/living/afflicted)
|
|
afflicted.adjust_stamina_loss(20)
|
|
afflicted.adjust_eye_blur(4 SECONDS)
|
|
|
|
/obj/projectile/hallucination/disabler
|
|
name = "disabler beam"
|
|
damage_type = STAMINA
|
|
hal_icon_state = "omnilaser"
|
|
hal_fire_sound = 'sound/items/weapons/taser2.ogg'
|
|
hal_hitsound = 'sound/items/weapons/tap.ogg'
|
|
hal_hitsound_wall = 'sound/items/weapons/effects/searwall.ogg'
|
|
hal_impact_effect = "impact_laser_blue"
|
|
hal_impact_effect_wall = null
|
|
hit_duration = 4
|
|
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
|
|
|
|
ricochets_max = 50
|
|
ricochet_chance = 80
|
|
reflectable = TRUE
|
|
|
|
/obj/projectile/hallucination/disabler/apply_effect_to_hallucinator(mob/living/afflicted)
|
|
afflicted.adjust_stamina_loss(30)
|
|
|
|
/obj/projectile/hallucination/ebow
|
|
name = "bolt"
|
|
damage_type = TOX
|
|
hal_icon_state = "cbbolt"
|
|
hal_fire_sound = 'sound/items/weapons/genhit.ogg'
|
|
hal_hitsound = null
|
|
hal_hitsound_wall = null
|
|
hal_impact_effect = null
|
|
hal_impact_effect_wall = null
|
|
|
|
/obj/projectile/hallucination/ebow/apply_effect_to_hallucinator(mob/living/afflicted)
|
|
afflicted.adjust_slurring(10 SECONDS)
|
|
afflicted.Knockdown(1 SECONDS)
|
|
afflicted.adjust_stamina_loss(75) // 60 stam + 15 tox
|
|
afflicted.adjust_eye_blur(20 SECONDS)
|
|
|
|
/obj/projectile/hallucination/change
|
|
name = "bolt of change"
|
|
damage_type = BURN
|
|
hal_icon_state = "ice_1"
|
|
hal_fire_sound = 'sound/effects/magic/staff_change.ogg'
|
|
hal_hitsound = null
|
|
hal_hitsound_wall = null
|
|
hal_impact_effect = null
|
|
hal_impact_effect_wall = null
|
|
|
|
/obj/projectile/hallucination/change/apply_effect_to_hallucinator(mob/living/afflicted)
|
|
// Future idea: Make it so any other mob hit appear to be polymorphed to the hallucinator
|
|
afflicted.cause_hallucination( \
|
|
get_random_valid_hallucination_subtype(/datum/hallucination/delusion/preset), \
|
|
"fake [name]", \
|
|
duration = 30 SECONDS, \
|
|
affects_us = TRUE, \
|
|
affects_others = FALSE, \
|
|
skip_nearby = FALSE, \
|
|
play_wabbajack = TRUE, \
|
|
)
|
|
|
|
/obj/projectile/hallucination/death
|
|
name = "bolt of death"
|
|
damage_type = BURN
|
|
hal_icon_state = "pulse1_bl"
|
|
hal_fire_sound = 'sound/effects/magic/wandodeath.ogg'
|
|
hal_hitsound = null
|
|
hal_hitsound_wall = null
|
|
hal_impact_effect = null
|
|
hal_impact_effect_wall = null
|
|
|
|
/obj/projectile/hallucination/death/apply_effect_to_hallucinator(mob/living/afflicted)
|
|
afflicted.cause_hallucination(/datum/hallucination/death, "fake [name]")
|