From 4739cedebc850752cef6add1d60ec2ade7e53d49 Mon Sep 17 00:00:00 2001 From: necromanceranne <40847847+necromanceranne@users.noreply.github.com> Date: Thu, 20 Jun 2024 22:58:21 +1000 Subject: [PATCH] Event Horizon Anti-Existential Beam Rifle. The ultimate conclusion to the arms race and the sniper's art. (#83934) ## About The Pull Request Replaces the Particle Acceleration Rifle, or the Beam Marksman Rifle, with the Event Horizon Anti-Existential Beam Rifle. In short, it shoots black holes. It takes five cores to make. It pretty much kills everything you point it at and their immediate bystanders as well. You can do this from the relative safety of a whole four screens away thanks to it having a scope. There can only ever be one vortex anomaly made in the round. The recipe, however, always requires the maximum number of cores that can be made in a round. If the number goes up for some reason (maybe someone wants to add more vortex items), so does the total number of cores needed to make an AEBR. ## Why It's Good For The Game Firstly; the beam rifle? Awful. Jank code. Bad performance. Ancient balancing considerations. Just too strong to bring out regularly against specifically blobs, but completely fucking useless against anyone else. So I'm hitting all these problems at once. I reduced a 600 line file to less than 60. I've removed all that bad code. I've also made it specifically unable to combat blobs at all because singularities send blobs off to safety. Secondly; this is maybe the first example of 'more cores = bigger power'. The rifle was already utterly trash to have around, and I just didn't think the way it worked currently could really excite anyone enough to go on a big ol' quest to get it. Solution? You will cause a shuttle call if you get this weapon. ## Changelog :cl: add: Replaces the Particle Acceleration Rifle with the Event Horizon anti-existential beam rifle. It shoots black holes. You can make this in-game. That's right, YOU! balance: Only one vortex anomaly can be made in a round. /:cl: --------- Co-authored-by: Jacquerel --- _maps/deathmatch/arena_station.dmm | 2 +- _maps/virtual_domains/xeno_nest.dmm | 2 +- code/__DEFINES/research/anomalies.dm | 2 +- .../datums/components/crafting/guncrafting.dm | 4 +- .../components/crafting/ranged_weapon.dm | 15 +- .../datums/storage/subtypes/bag_of_holding.dm | 2 +- code/modules/power/cell.dm | 15 - .../{boh_tear.dm => reality_tear.dm} | 35 +- code/modules/projectiles/guns/energy.dm | 17 +- .../projectiles/guns/energy/beam_rifle.dm | 584 +----------------- .../research/designs/weapon_designs.dm | 5 +- tgstation.dme | 2 +- 12 files changed, 86 insertions(+), 599 deletions(-) rename code/modules/power/singularity/{boh_tear.dm => reality_tear.dm} (59%) diff --git a/_maps/deathmatch/arena_station.dmm b/_maps/deathmatch/arena_station.dmm index c7654810360..50089df45e8 100644 --- a/_maps/deathmatch/arena_station.dmm +++ b/_maps/deathmatch/arena_station.dmm @@ -930,7 +930,7 @@ /area/deathmatch) "LY" = ( /obj/structure/closet/secure_closet, -/obj/item/gun/energy/beam_rifle, +/obj/item/gun/energy/xray, /turf/open/indestructible/vault, /area/deathmatch) "Mc" = ( diff --git a/_maps/virtual_domains/xeno_nest.dmm b/_maps/virtual_domains/xeno_nest.dmm index 907436758c7..65f183d69c4 100644 --- a/_maps/virtual_domains/xeno_nest.dmm +++ b/_maps/virtual_domains/xeno_nest.dmm @@ -140,7 +140,7 @@ /area/ruin/space/has_grav/powered/virtual_domain) "F" = ( /obj/structure/table/greyscale, -/obj/item/gun/energy/beam_rifle, +/obj/item/gun/energy/xray, /obj/item/gun/energy/laser{ pixel_x = 4; pixel_y = -6 diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm index db605431c61..a1e30cd142f 100644 --- a/code/__DEFINES/research/anomalies.dm +++ b/code/__DEFINES/research/anomalies.dm @@ -2,7 +2,7 @@ #define MAX_CORES_BLUESPACE 3 #define MAX_CORES_GRAVITATIONAL 8 #define MAX_CORES_FLUX 8 -#define MAX_CORES_VORTEX 8 +#define MAX_CORES_VORTEX 1 #define MAX_CORES_PYRO 8 #define MAX_CORES_HALLUCINATION 8 #define MAX_CORES_BIOSCRAMBLER 8 diff --git a/code/datums/components/crafting/guncrafting.dm b/code/datums/components/crafting/guncrafting.dm index 6d4f4713f8b..dcf42ee47b1 100644 --- a/code/datums/components/crafting/guncrafting.dm +++ b/code/datums/components/crafting/guncrafting.dm @@ -84,8 +84,8 @@ desc = "A suitcase containing the necessary gun parts to tranform a standard energy gun into a temperature gun. Fantastic at birthday parties and killing indigenious populations of lizardpeople." /obj/item/weaponcrafting/gunkit/beam_rifle - name = "particle acceleration rifle part kit (lethal)" - desc = "The coup de grace of guncrafting. This suitcase contains the highly experimental rig for a particle acceleration rifle. Requires an energy gun, a stabilized flux anomaly and a stabilized gravity anomaly." + name = "\improper Event Horizon anti-existential beam rifle part kit (DOOMSDAY DEVICE, DO NOT CONSTRUCT)" + desc = "What fevered minds wrought this terrible construction kit? To create a frame to harness the strange energies that flow through the Spinward Sector towards such horrible acts of violence?" /obj/item/weaponcrafting/gunkit/ebow name = "energy crossbow part kit (less lethal)" diff --git a/code/datums/components/crafting/ranged_weapon.dm b/code/datums/components/crafting/ranged_weapon.dm index b646c4472ed..666e84964c5 100644 --- a/code/datums/components/crafting/ranged_weapon.dm +++ b/code/datums/components/crafting/ranged_weapon.dm @@ -72,21 +72,18 @@ blacklist += subtypesof(/obj/item/gun/energy/e_gun) /datum/crafting_recipe/beam_rifle - name = "Particle Acceleration Rifle" - result = /obj/item/gun/energy/beam_rifle + name = "Event Horizon Anti-Existential Beam Rifle" + result = /obj/item/gun/energy/event_horizon reqs = list( - /obj/item/gun/energy/e_gun = 1, - /obj/item/assembly/signaler/anomaly/flux = 1, + /obj/item/assembly/signaler/anomaly/flux = 2, /obj/item/assembly/signaler/anomaly/grav = 1, + /obj/item/assembly/signaler/anomaly/vortex = MAX_CORES_VORTEX, + /obj/item/assembly/signaler/anomaly/bluespace = 1, /obj/item/weaponcrafting/gunkit/beam_rifle = 1, ) - time = 10 SECONDS + time = 30 SECONDS //Maybe the delay will make you reconsider your choices category = CAT_WEAPON_RANGED -/datum/crafting_recipe/beam_rifle/New() - ..() - blacklist += subtypesof(/obj/item/gun/energy/e_gun) - /datum/crafting_recipe/ebow name = "Energy Crossbow" result = /obj/item/gun/energy/recharge/ebow/large diff --git a/code/datums/storage/subtypes/bag_of_holding.dm b/code/datums/storage/subtypes/bag_of_holding.dm index a4ea699e42e..aa812f5d1e0 100644 --- a/code/datums/storage/subtypes/bag_of_holding.dm +++ b/code/datums/storage/subtypes/bag_of_holding.dm @@ -38,7 +38,7 @@ user.investigate_log("has been gibbed by a bag of holding recursive insertion.", INVESTIGATE_DEATHS) user.gib() - var/obj/boh_tear/tear = new(rift_loc) + var/obj/reality_tear/tear = new(rift_loc) tear.start_disaster() qdel(to_insert) qdel(parent) diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index b55593ec913..74352134f59 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -480,21 +480,6 @@ charge_light_type = null connector_type = "slimecore" -/obj/item/stock_parts/cell/beam_rifle - name = "beam rifle capacitor" - desc = "A high powered capacitor that can provide huge amounts of energy in an instant." - maxcharge = STANDARD_CELL_CHARGE * 50 - chargerate = STANDARD_CELL_RATE * 2.5 //Extremely energy intensive - -/obj/item/stock_parts/cell/beam_rifle/corrupt() - return - -/obj/item/stock_parts/cell/beam_rifle/emp_act(severity) - . = ..() - if(. & EMP_PROTECT_SELF) - return - charge = clamp((charge-(10000/severity)),0,maxcharge) - /obj/item/stock_parts/cell/emergency_light name = "miniature power cell" desc = "A tiny power cell with a very low power capacity. Used in light fixtures to power them in the event of an outage." diff --git a/code/modules/power/singularity/boh_tear.dm b/code/modules/power/singularity/reality_tear.dm similarity index 59% rename from code/modules/power/singularity/boh_tear.dm rename to code/modules/power/singularity/reality_tear.dm index a0089a7c94b..e43301541a0 100644 --- a/code/modules/power/singularity/boh_tear.dm +++ b/code/modules/power/singularity/reality_tear.dm @@ -1,4 +1,7 @@ -/obj/boh_tear +/// Tear in the Fabric of Reality /// +// Typically spawned by placing two bags of holding into one another, collapsing into a wandering singularity after a brief period as a stationary singularity. + +/obj/reality_tear name = "tear in the fabric of reality" desc = "As you gaze into the abyss, the only thing you can think is... \"Should I really be this close to it?\"" anchored = TRUE @@ -15,30 +18,36 @@ pixel_y = -32 resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF flags_1 = SUPERMATTER_IGNORES_1 + /// Range that our singularity component consumes objects + var/singularity_consume_range = 1 + /// Ranges that the singularity pulls objects + var/singularity_grav_pull = 21 + /// Time before we begin our bagulo spawn + var/collapse_spawn_time = 9 SECONDS -/obj/boh_tear/proc/start_disaster() +/obj/reality_tear/proc/start_disaster() apply_wibbly_filters(src) playsound(loc, 'sound/effects/clockcult_gateway_disrupted.ogg', vary = 200, extrarange = 3, falloff_exponent = 1, frequency = 0.33, pressure_affected = FALSE, ignore_walls = TRUE, falloff_distance = 7) AddComponent( /datum/component/singularity, \ - consume_range = 1, \ - grav_pull = 21, \ + consume_range = singularity_consume_range, \ + grav_pull = singularity_grav_pull, \ roaming = FALSE, \ singularity_size = STAGE_SIX, \ ) - addtimer(CALLBACK(src, PROC_REF(bagulo_time)), 9 SECONDS, TIMER_DELETE_ME) + addtimer(CALLBACK(src, PROC_REF(reality_collapse)), collapse_spawn_time, TIMER_DELETE_ME) animate(src, time = 7.5 SECONDS, transform = transform.Scale(2), flags = ANIMATION_PARALLEL) animate(time = 2 SECONDS, transform = transform.Scale(0.25), easing = ELASTIC_EASING) animate(time = 0.5 SECONDS, alpha = 0) -/obj/boh_tear/proc/bagulo_time() +/obj/reality_tear/proc/reality_collapse() playsound(loc, 'sound/effects/supermatter.ogg', 200, vary = TRUE, extrarange = 3, falloff_exponent = 1, frequency = 0.5, pressure_affected = FALSE, ignore_walls = TRUE, falloff_distance = 7) var/obj/singularity/bagulo = new(loc) bagulo.expand(STAGE_TWO) bagulo.energy = 400 qdel(src) -/obj/boh_tear/attack_tk(mob/user) +/obj/reality_tear/attack_tk(mob/user) if(!isliving(user)) return var/mob/living/jedi = user @@ -47,3 +56,15 @@ jedi.spawn_dust() addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, attack_hand), jedi), 0.5 SECONDS) return COMPONENT_CANCEL_ATTACK_CHAIN + +//The temporary tears in reality. Collapses into nothing, and has a significantly lower gravity pull range, but consumes more widely. + +/obj/reality_tear/temporary + name = "puncture in the fabric of reality" + desc = "Count your lucky stars that this wasn't anywhere near you." + singularity_consume_range = 2 + singularity_grav_pull = 3 + collapse_spawn_time = 2 SECONDS + +/obj/reality_tear/temporary/reality_collapse() + qdel(src) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 4c00e83b0bb..b977441e9ae 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -26,14 +26,23 @@ var/single_shot_type_overlay = TRUE ///Should we give an overlay to empty guns? var/display_empty = TRUE - var/selfcharge = 0 - var/charge_timer = 0 - var/charge_delay = 8 + ///whether the gun's cell drains the cyborg user's cell to recharge var/use_cyborg_cell = FALSE ///set to true so the gun is given an empty cell var/dead_cell = FALSE + // Self charging vars + + /// Whether or not our gun charges its own cell on a timer. + var/selfcharge = 0 + /// The amount of time between instances of cell self recharge + var/charge_timer = 0 + /// The amount of seconds_per_tick during process() before the gun charges itself + var/charge_delay = 8 + /// The amount restored by the gun to the cell per self charge tick + var/self_charge_amount = STANDARD_ENERGY_GUN_SELF_CHARGE_RATE + /obj/item/gun/energy/fire_sounds() // What frequency the energy gun's sound will make var/pitch_to_use = 1 @@ -153,7 +162,7 @@ if(charge_timer < charge_delay) return charge_timer = 0 - cell.give(STANDARD_ENERGY_GUN_SELF_CHARGE_RATE * seconds_per_tick) + cell.give(self_charge_amount * seconds_per_tick) if(!chambered) //if empty chamber we try to charge a new shot recharge_newshot(TRUE) update_appearance() diff --git a/code/modules/projectiles/guns/energy/beam_rifle.dm b/code/modules/projectiles/guns/energy/beam_rifle.dm index e29e1ef4878..0bda1930c62 100644 --- a/code/modules/projectiles/guns/energy/beam_rifle.dm +++ b/code/modules/projectiles/guns/energy/beam_rifle.dm @@ -1,588 +1,64 @@ - -#define ZOOM_LOCK_AUTOZOOM_FREEMOVE 0 -#define ZOOM_LOCK_AUTOZOOM_ANGLELOCK 1 -#define ZOOM_LOCK_CENTER_VIEW 2 -#define ZOOM_LOCK_OFF 3 - -#define AUTOZOOM_PIXEL_STEP_FACTOR 48 - -#define AIMING_BEAM_ANGLE_CHANGE_THRESHOLD 0.1 - -/obj/item/gun/energy/beam_rifle - name = "particle acceleration rifle" - desc = "An energy-based anti material marksman rifle that uses highly charged particle beams moving at extreme velocities to decimate whatever is unfortunate enough to be targeted by one." - desc_controls = "Hold down left click while scoped to aim, when weapon is fully aimed (Tracer goes from red to green as it charges), release to fire. Moving while aiming or changing where you're pointing at while aiming will delay the aiming process depending on how much you changed." +/obj/item/gun/energy/event_horizon + name = "\improper Event Horizon anti-existential beam rifle" + desc = "The deranged minds of Nanotrasen, in their great hubris and spite, have birthed forth the definitive conclusion to the arms race. Weaponized black holes, and a platform to deliver them.\ + To look upon this existential maleficence is to know that the pursuit of profit has consigned all life to this pathetic conclusion; the destruction of reality itself." icon = 'icons/obj/weapons/guns/energy.dmi' icon_state = "esniper" inhand_icon_state = null worn_icon_state = null fire_sound = 'sound/weapons/beam_sniper.ogg' slot_flags = ITEM_SLOT_BACK - force = 15 + force = 20 //This is maybe the sanest part of this weapon. custom_materials = null - recoil = 4 + recoil = 2 ammo_x_offset = 3 ammo_y_offset = 3 modifystate = FALSE charge_sections = 1 weapon_weight = WEAPON_HEAVY w_class = WEIGHT_CLASS_BULKY - ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan) - actions_types = list(/datum/action/item_action/zoom_lock_action) - cell_type = /obj/item/stock_parts/cell/beam_rifle - var/aiming = FALSE - var/aiming_time = 12 - var/aiming_time_fire_threshold = 5 - var/aiming_time_left = 12 - var/aiming_time_increase_user_movement = 3 - var/scoped_slow = 1 - var/aiming_time_increase_angle_multiplier = 0.3 - var/last_process = 0 + ammo_type = list(/obj/item/ammo_casing/energy/event_horizon) + selfcharge = TRUE + self_charge_amount = STANDARD_ENERGY_GUN_SELF_CHARGE_RATE * 10 - var/lastangle = 0 - var/aiming_lastangle = 0 - var/mob/current_user = null - var/list/obj/effect/projectile/tracer/current_tracers - - var/structure_piercing = 2 //Amount * 2. For some reason structures aren't respecting this unless you have it doubled. Probably with the objects in question's Bump() code instead of this but I'll deal with this later. - var/structure_bleed_coeff = 0.7 - var/wall_pierce_amount = 0 - var/wall_devastate = 0 - var/aoe_structure_range = 1 - var/aoe_structure_damage = 50 - var/aoe_fire_range = 2 - var/aoe_fire_chance = 40 - var/aoe_mob_range = 1 - var/aoe_mob_damage = 30 - var/impact_structure_damage = 60 - var/projectile_damage = 30 - var/projectile_stun = 0 - var/projectile_setting_pierce = TRUE - var/delay = 25 - var/lastfire = 0 - - //ZOOMING - var/zoom_current_view_increase = 0 - ///The radius you want to zoom by - var/zoom_target_view_increase = 9.5 - var/zooming = FALSE - var/zoom_lock = ZOOM_LOCK_OFF - var/zooming_angle - var/current_zoom_x = 0 - var/current_zoom_y = 0 - - var/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/trace = null - -/obj/item/gun/energy/beam_rifle/apply_fantasy_bonuses(bonus) +/obj/item/gun/energy/event_horizon/Initialize(mapload) . = ..() - delay = modify_fantasy_variable("delay", delay, -bonus * 2) - aiming_time = modify_fantasy_variable("aiming_time", aiming_time, -bonus * 2) - recoil = modify_fantasy_variable("recoil", recoil, round(-bonus / 2)) + AddComponent(/datum/component/scope, range_modifier = 4) -/obj/item/gun/energy/beam_rifle/remove_fantasy_bonuses(bonus) - delay = reset_fantasy_variable("delay", delay) - aiming_time = reset_fantasy_variable("aiming_time", aiming_time) - recoil = reset_fantasy_variable("recoil", recoil) - return ..() +/obj/item/gun/energy/event_horizon/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread) -/obj/item/gun/energy/beam_rifle/debug - delay = 0 - cell_type = /obj/item/stock_parts/cell/infinite - aiming_time = 0 - recoil = 0 - pin = /obj/item/firing_pin - -/obj/item/gun/energy/beam_rifle/equipped(mob/user) - set_user(user) - return ..() - -/obj/item/gun/energy/beam_rifle/pickup(mob/user) - set_user(user) - return ..() - -/obj/item/gun/energy/beam_rifle/dropped(mob/user) - set_user() - return ..() - -/obj/item/gun/energy/beam_rifle/ui_action_click(mob/user, actiontype) - if(istype(actiontype, /datum/action/item_action/zoom_lock_action)) - zoom_lock++ - if(zoom_lock > 3) - zoom_lock = 0 - switch(zoom_lock) - if(ZOOM_LOCK_AUTOZOOM_FREEMOVE) - to_chat(user, span_boldnotice("You switch [src]'s zooming processor to free directional.")) - if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK) - to_chat(user, span_boldnotice("You switch [src]'s zooming processor to locked directional.")) - if(ZOOM_LOCK_CENTER_VIEW) - to_chat(user, span_boldnotice("You switch [src]'s zooming processor to center mode.")) - if(ZOOM_LOCK_OFF) - to_chat(user, span_boldnotice("You disable [src]'s zooming system.")) - reset_zooming() + if(!HAS_TRAIT(user, TRAIT_USER_SCOPED)) + balloon_alert(user, "must be scoped!") return - return ..() - -/obj/item/gun/energy/beam_rifle/proc/set_autozoom_pixel_offsets_immediate(current_angle) - if(zoom_lock == ZOOM_LOCK_CENTER_VIEW || zoom_lock == ZOOM_LOCK_OFF) - return - current_zoom_x = sin(current_angle) + sin(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase - current_zoom_y = cos(current_angle) + cos(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase - -/obj/item/gun/energy/beam_rifle/proc/handle_zooming() - if(!zooming || !check_user()) - return - current_user.client.view_size.setTo(zoom_target_view_increase) - zoom_current_view_increase = zoom_target_view_increase - set_autozoom_pixel_offsets_immediate(zooming_angle) - -/obj/item/gun/energy/beam_rifle/proc/start_zooming() - if(zoom_lock == ZOOM_LOCK_OFF) - return - zooming = TRUE - -/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user) - if(zooming) - zooming = FALSE - reset_zooming(user) - -/obj/item/gun/energy/beam_rifle/proc/reset_zooming(mob/user) - if(!user) - user = current_user - if(!user || !user.client) - return FALSE - user.client.view_size.zoomIn() - zoom_current_view_increase = 0 - zooming_angle = 0 - current_zoom_x = 0 - current_zoom_y = 0 - -/obj/item/gun/energy/beam_rifle/attack_self(mob/user) - projectile_setting_pierce = !projectile_setting_pierce - balloon_alert(user, "switched to [projectile_setting_pierce ? "pierce":"impact"] mode") - aiming_beam() - -/obj/item/gun/energy/beam_rifle/proc/update_slowdown() - if(aiming) - slowdown = scoped_slow - else - slowdown = initial(slowdown) - -/obj/item/gun/energy/beam_rifle/Initialize(mapload) . = ..() - fire_delay = delay - current_tracers = list() - START_PROCESSING(SSfastprocess, src) + message_admins("[ADMIN_LOOKUPFLW(user)] has fired an anti-existential beam at [ADMIN_VERBOSEJMP(user)].") -/obj/item/gun/energy/beam_rifle/Destroy() - STOP_PROCESSING(SSfastprocess, src) - set_user(null) - QDEL_LIST(current_tracers) - return ..() - -/obj/item/gun/energy/beam_rifle/emp_act(severity) - . = ..() - if(. & EMP_PROTECT_SELF) - return - chambered = null - recharge_newshot() - -/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE) - var/diff = abs(aiming_lastangle - lastangle) - if(!check_user()) - return - if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update) - return - aiming_lastangle = lastangle - // ONLY ONE at once (since fire can sleep) - if(trace) - QDEL_NULL(trace) - trace = new - trace.gun = src - trace.wall_pierce_amount = wall_pierce_amount - trace.structure_pierce_amount = structure_piercing - trace.do_pierce = projectile_setting_pierce - if(aiming_time) - var/percent = ((100/aiming_time)*aiming_time_left) - trace.color = rgb(255 * percent,255 * ((100 - percent) / 100),0) - else - trace.color = rgb(0, 255, 0) - var/turf/curloc = get_turf(src) - - var/atom/target_atom = current_user.client.mouse_object_ref?.resolve() - var/turf/targloc = get_turf(target_atom) - if(!istype(targloc)) - if(!istype(curloc)) - return - targloc = get_turf_in_angle(lastangle, curloc, 10) - var/mouse_modifiers = params2list(current_user.client.mouseParams) - trace.preparePixelProjectile(targloc, current_user, mouse_modifiers, 0) - trace.fire(lastangle) - trace = null - -/obj/item/gun/energy/beam_rifle/process() - if(!aiming) - last_process = world.time - return - check_user() - handle_zooming() - aiming_time_left = max(0, aiming_time_left - (world.time - last_process)) - aiming_beam(TRUE) - last_process = world.time - -/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE) - if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it! - if(automatic_cleanup) - stop_aiming() - return FALSE - return TRUE - -/obj/item/gun/energy/beam_rifle/proc/process_aim(params) - var/angle = mouse_angle_from_client(current_user?.client, params) - current_user.setDir(angle2dir_cardinal(angle)) - var/difference = abs(closer_angle_difference(lastangle, angle)) - delay_penalty(difference * aiming_time_increase_angle_multiplier) - lastangle = angle - -/obj/item/gun/energy/beam_rifle/proc/on_mob_move() - SIGNAL_HANDLER - check_user() - if(aiming) - delay_penalty(aiming_time_increase_user_movement) - process_aim(current_user?.client?.mouseParams) - INVOKE_ASYNC(src, PROC_REF(aiming_beam), TRUE) - -/obj/item/gun/energy/beam_rifle/proc/start_aiming(params) - aiming_time_left = aiming_time - aiming = TRUE - process_aim(params) - aiming_beam(TRUE) - zooming_angle = lastangle - start_zooming() - -/obj/item/gun/energy/beam_rifle/proc/stop_aiming(mob/user) - set waitfor = FALSE - aiming_time_left = aiming_time - aiming = FALSE - QDEL_LIST(current_tracers) - QDEL_NULL(trace) - stop_zooming(user) - -/obj/item/gun/energy/beam_rifle/proc/set_user(mob/user) - if(user == current_user) - return - stop_aiming(current_user) - if(istype(current_user)) - unregister_client_signals(current_user) - UnregisterSignal(current_user, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT)) - current_user = null - if(!istype(user)) - return - current_user = user - RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_mob_move)) - RegisterSignal(user, COMSIG_MOB_LOGIN, PROC_REF(register_client_signals)) - RegisterSignal(user, COMSIG_MOB_LOGOUT, PROC_REF(unregister_client_signals)) - if(user.client) - register_client_signals(user) - -/obj/item/gun/energy/beam_rifle/proc/register_client_signals(mob/source) - SIGNAL_HANDLER - RegisterSignal(source.client, COMSIG_CLIENT_MOUSEDOWN, PROC_REF(on_mouse_down)) - -/obj/item/gun/energy/beam_rifle/proc/unregister_client_signals(mob/source) - SIGNAL_HANDLER - stop_aiming() - if(QDELETED(source.client)) - return - UnregisterSignal(source.client, list(COMSIG_CLIENT_MOUSEDOWN, COMSIG_CLIENT_MOUSEUP, COMSIG_CLIENT_MOUSEDRAG)) - -///change the aiming beam angle to that of the mouse cursor. -/obj/item/gun/energy/beam_rifle/proc/on_mouse_drag(client/source, src_object, over_object, src_location, over_location, src_control, over_control, params) - SIGNAL_HANDLER - if(aiming) - process_aim(params) - INVOKE_ASYNC(src, PROC_REF(aiming_beam)) - if(zoom_lock == ZOOM_LOCK_AUTOZOOM_FREEMOVE) - zooming_angle = lastangle - set_autozoom_pixel_offsets_immediate(zooming_angle) - -///Start aiming and charging the beam -/obj/item/gun/energy/beam_rifle/proc/on_mouse_down(client/source, atom/movable/object, location, control, params) - SIGNAL_HANDLER - if(source.mob.get_active_held_item() != src) - return - if(!object.IsAutoclickable() || (object in source.mob.contents) || (object == source.mob)) - return - INVOKE_ASYNC(src, PROC_REF(start_aiming), params) - RegisterSignal(source, COMSIG_CLIENT_MOUSEDRAG, PROC_REF(on_mouse_drag)) - RegisterSignal(source, COMSIG_CLIENT_MOUSEUP, PROC_REF(on_mouse_up)) - -///Stop aiming and fire the beam if charged enough -/obj/item/gun/energy/beam_rifle/proc/on_mouse_up(client/source, atom/movable/object, location, control, params) - SIGNAL_HANDLER - if(!object.IsAutoclickable()) - return - process_aim(params) - UnregisterSignal(source, list(COMSIG_CLIENT_MOUSEDRAG, COMSIG_CLIENT_MOUSEUP)) - if(aiming_time_left <= aiming_time_fire_threshold && check_user()) - sync_ammo() - var/atom/target = source.mouse_object_ref?.resolve() - if(target) - INVOKE_ASYNC(src, PROC_REF(try_fire_gun), target, source.mob, source.mouseParams, TRUE) - stop_aiming() - QDEL_LIST(current_tracers) - -/obj/item/gun/energy/beam_rifle/try_fire_gun(atom/target, mob/living/user, params, passthrough = FALSE) - if(user.Adjacent(target)) //It's adjacent, is the user, or is on the user's person - if(target in user.contents) //can't shoot stuff inside us. - return FALSE - if(!ismob(target) || user.combat_mode) //melee attack - return FALSE - if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected) - return FALSE - if(!passthrough && (aiming_time > aiming_time_fire_threshold)) - return FALSE - if(lastfire > world.time + delay) - return FALSE - if(!..()) - return FALSE - lastfire = world.time - stop_aiming() - return TRUE - -/obj/item/gun/energy/beam_rifle/proc/sync_ammo() - for(var/obj/item/ammo_casing/energy/beam_rifle/AC in contents) - AC.sync_stats() - -/obj/item/gun/energy/beam_rifle/proc/delay_penalty(amount) - aiming_time_left = clamp(aiming_time_left + amount, 0, aiming_time) - -/obj/item/ammo_casing/energy/beam_rifle - name = "particle acceleration lens" - desc = "Don't look into barrel!" - var/wall_pierce_amount = 0 - var/wall_devastate = 0 - var/aoe_structure_range = 1 - var/aoe_structure_damage = 30 - var/aoe_fire_range = 2 - var/aoe_fire_chance = 66 - var/aoe_mob_range = 1 - var/aoe_mob_damage = 20 - var/impact_structure_damage = 50 - var/projectile_damage = 40 - var/projectile_stun = 0 - var/structure_piercing = 2 - var/structure_bleed_coeff = 0.7 - var/do_pierce = TRUE - var/obj/item/gun/energy/beam_rifle/host - -/obj/item/ammo_casing/energy/beam_rifle/proc/sync_stats() - var/obj/item/gun/energy/beam_rifle/BR = loc - if(!istype(BR)) - stack_trace("Beam rifle syncing error") - host = BR - do_pierce = BR.projectile_setting_pierce - wall_pierce_amount = BR.wall_pierce_amount - wall_devastate = BR.wall_devastate - aoe_structure_range = BR.aoe_structure_range - aoe_structure_damage = BR.aoe_structure_damage - aoe_fire_range = BR.aoe_fire_range - aoe_fire_chance = BR.aoe_fire_chance - aoe_mob_range = BR.aoe_mob_range - aoe_mob_damage = BR.aoe_mob_damage - impact_structure_damage = BR.impact_structure_damage - projectile_damage = BR.projectile_damage - projectile_stun = BR.projectile_stun - delay = BR.delay - structure_piercing = BR.structure_piercing - structure_bleed_coeff = BR.structure_bleed_coeff - -/obj/item/ammo_casing/energy/beam_rifle/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") - . = ..() - var/obj/projectile/beam/beam_rifle/hitscan/HS_BB = loaded_projectile - if(!istype(HS_BB)) - return - HS_BB.impact_direct_damage = projectile_damage - HS_BB.stun = projectile_stun - HS_BB.impact_structure_damage = impact_structure_damage - HS_BB.aoe_mob_damage = aoe_mob_damage - HS_BB.aoe_mob_range = clamp(aoe_mob_range, 0, 15) //Badmin safety lock - HS_BB.aoe_fire_chance = aoe_fire_chance - HS_BB.aoe_fire_range = aoe_fire_range - HS_BB.aoe_structure_damage = aoe_structure_damage - HS_BB.aoe_structure_range = clamp(aoe_structure_range, 0, 15) //Badmin safety lock - HS_BB.wall_devastate = wall_devastate - HS_BB.wall_pierce_amount = wall_pierce_amount - HS_BB.structure_pierce_amount = structure_piercing - HS_BB.structure_bleed_coeff = structure_bleed_coeff - HS_BB.do_pierce = do_pierce - HS_BB.gun = host - -/obj/item/ammo_casing/energy/beam_rifle/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread, atom/fired_from) - var/turf/curloc = get_turf(user) - if(!istype(curloc) || !loaded_projectile) - return FALSE - var/obj/item/gun/energy/beam_rifle/gun = loc - if(!targloc && gun) - targloc = get_turf_in_angle(gun.lastangle, curloc, 10) - else if(!targloc) - return FALSE - var/firing_dir - if(loaded_projectile.firer) - firing_dir = loaded_projectile.firer.dir - if(!loaded_projectile.suppressed && firing_effect_type) - new firing_effect_type(get_turf(src), firing_dir) - var/modifiers = params2list(params) - loaded_projectile.preparePixelProjectile(target, user, modifiers, spread) - loaded_projectile.fire(gun? gun.lastangle : null, null) - loaded_projectile = null - return TRUE - -/obj/item/ammo_casing/energy/beam_rifle/hitscan - projectile_type = /obj/projectile/beam/beam_rifle/hitscan - select_name = "beam" - e_cost = LASER_SHOTS(5, 50000) // Beam rifle has a custom cell +/obj/item/ammo_casing/energy/event_horizon + projectile_type = /obj/projectile/beam/event_horizon + select_name = "doomsday" + e_cost = LASER_SHOTS(1, STANDARD_CELL_CHARGE) fire_sound = 'sound/weapons/beam_sniper.ogg' -/obj/projectile/beam/beam_rifle - name = "particle beam" +/obj/projectile/beam/event_horizon + name = "anti-existential beam" icon = null hitsound = 'sound/effects/explosion3.ogg' - damage = 0 //Handled manually. + damage = 100 // Does it matter? damage_type = BURN armor_flag = ENERGY range = 150 jitter = 20 SECONDS - var/obj/item/gun/energy/beam_rifle/gun - var/structure_pierce_amount = 0 //All set to 0 so the gun can manually set them during firing. - var/structure_bleed_coeff = 0 - var/structure_pierce = 0 - var/do_pierce = TRUE - var/wall_pierce_amount = 0 - var/wall_pierce = 0 - var/wall_devastate = 0 - var/aoe_structure_range = 0 - var/aoe_structure_damage = 0 - var/aoe_fire_range = 0 - var/aoe_fire_chance = 0 - var/aoe_mob_range = 0 - var/aoe_mob_damage = 0 - var/impact_structure_damage = 0 - var/impact_direct_damage = 0 - var/list/pierced = list() - -/obj/projectile/beam/beam_rifle/proc/AOE(turf/epicenter) - if(!epicenter) - return - new /obj/effect/temp_visual/explosion/fast(epicenter) - for(var/mob/living/L in range(aoe_mob_range, epicenter)) //handle aoe mob damage - L.adjustFireLoss(aoe_mob_damage) - to_chat(L, span_userdanger("\The [src] sears you!")) - for(var/turf/T in RANGE_TURFS(aoe_fire_range, epicenter)) //handle aoe fire - if(prob(aoe_fire_chance)) - new /obj/effect/hotspot(T) - for(var/obj/O in range(aoe_structure_range, epicenter)) - if(!isitem(O)) - O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, LASER, FALSE) - -/obj/projectile/beam/beam_rifle/prehit_pierce(atom/A) - if(isclosedturf(A) && (wall_pierce < wall_pierce_amount)) - if(prob(wall_devastate)) - if(iswallturf(A)) - var/turf/closed/wall/W = A - W.dismantle_wall(TRUE, TRUE) - else - SSexplosions.medturf += A - ++wall_pierce - return PROJECTILE_PIERCE_PHASE // yeah this gun is a snowflakey piece of garbage - if(isobj(A) && (structure_pierce < structure_pierce_amount)) - ++structure_pierce - var/obj/O = A - O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(A), BURN, ENERGY, FALSE) - return PROJECTILE_PIERCE_PHASE // ditto and this could be refactored to on_hit honestly - return ..() - -/obj/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target) - if(istype(target, /obj/machinery/door)) - return 0.4 - if(istype(target, /obj/structure/window)) - return 0.5 - return 1 - -/obj/projectile/beam/beam_rifle/proc/handle_impact(atom/target) - if(isobj(target)) - var/obj/O = target - O.take_damage(impact_structure_damage * get_damage_coeff(target), BURN, LASER, FALSE) - if(isliving(target)) - var/mob/living/L = target - L.adjustFireLoss(impact_direct_damage) - L.emote("scream") - -/obj/projectile/beam/beam_rifle/proc/handle_hit(atom/target, piercing_hit = FALSE) - set waitfor = FALSE - if(!is_hostile_projectile()) - return FALSE - playsound(src, 'sound/effects/explosion3.ogg', 100, TRUE) - if(!do_pierce) - AOE(get_turf(target) || get_turf(src)) - if(!QDELETED(target)) - handle_impact(target) - -/obj/projectile/beam/beam_rifle/on_hit(atom/target, blocked = 0, pierce_hit) - handle_hit(target, pierce_hit) - return ..() - -/obj/projectile/beam/beam_rifle/is_hostile_projectile() - return TRUE // on hit = boom fire - -/obj/projectile/beam/beam_rifle/hitscan - icon_state = "" hitscan = TRUE tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle - var/constant_tracer = FALSE -/obj/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE, highlander) - set waitfor = FALSE - if(isnull(highlander)) - highlander = constant_tracer - if(highlander && istype(gun)) - QDEL_LIST(gun.current_tracers) - for(var/datum/point/p in beam_segments) - gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) - else - for(var/datum/point/p in beam_segments) - generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) - if(cleanup) - QDEL_LIST(beam_segments) - beam_segments = null - QDEL_NULL(beam_index) +/obj/projectile/beam/event_horizon/on_hit(atom/target, blocked, pierce_hit) + . = ..() -/obj/projectile/beam/beam_rifle/hitscan/aiming_beam - tracer_type = /obj/effect/projectile/tracer/tracer/aiming - name = "aiming beam" - hitsound = null - hitsound_wall = null - damage = 0 - constant_tracer = TRUE - hitscan_light_range = 0 - hitscan_light_intensity = 0 - hitscan_light_color_override = "#99ff99" - reflectable = REFLECT_FAKEPROJECTILE + // Where we droppin' boys? + var/turf/rift_loc = get_turf(target) -/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/is_hostile_projectile() - return FALSE // just an aiming reticle - -/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit_pierce(atom/target) - return PROJECTILE_DELETE_WITHOUT_HITTING - -/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit(atom/target, blocked = 0, pierce_hit) - SHOULD_CALL_PARENT(FALSE) // This is some snowflake stuff so whatever - qdel(src) - return BULLET_ACT_BLOCK - -#undef AIMING_BEAM_ANGLE_CHANGE_THRESHOLD -#undef AUTOZOOM_PIXEL_STEP_FACTOR -#undef ZOOM_LOCK_AUTOZOOM_ANGLELOCK -#undef ZOOM_LOCK_AUTOZOOM_FREEMOVE -#undef ZOOM_LOCK_CENTER_VIEW -#undef ZOOM_LOCK_OFF + // Spawn our temporary rift, then activate it. + var/obj/reality_tear/temporary/tear = new(rift_loc) + tear.start_disaster() + message_admins("[ADMIN_LOOKUPFLW(target)] has been hit by an anti-existential beam at [ADMIN_VERBOSEJMP(rift_loc)], creating a singularity.") diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index 00c7dba3946..35d95d82d30 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -218,8 +218,8 @@ autolathe_exportable = FALSE /datum/design/beamrifle - name = "Beam Marksman Rifle Part Kit (Lethal)" - desc = "The gunkit for a powerful long ranged anti-material rifle that fires charged particle beams to obliterate targets." + name = "Event Horizon Anti-Existential Beam Rifle Part Kit (DOOMSDAY DEVICE)" + desc = "The kit that produces a weapon made to end your foes on an existential level. Why the fuck can you make this?" id = "beamrifle" build_type = PROTOLATHE | AWAY_LATHE materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5, /datum/material/glass =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/diamond =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/uranium = SHEET_MATERIAL_AMOUNT * 4, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2.25, /datum/material/gold =SHEET_MATERIAL_AMOUNT * 2.5) @@ -230,7 +230,6 @@ departmental_flags = DEPARTMENT_BITFLAG_SECURITY autolathe_exportable = FALSE - /datum/design/rapidsyringe name = "Rapid Syringe Gun" desc = "A gun that fires many syringes." diff --git a/tgstation.dme b/tgstation.dme index f3bdadf367d..94efbba3082 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5364,12 +5364,12 @@ #include "code\modules\power\lighting\light_items.dm" #include "code\modules\power\lighting\light_mapping_helpers.dm" #include "code\modules\power\lighting\light_wallframes.dm" -#include "code\modules\power\singularity\boh_tear.dm" #include "code\modules\power\singularity\containment_field.dm" #include "code\modules\power\singularity\dark_matter_singularity.dm" #include "code\modules\power\singularity\emitter.dm" #include "code\modules\power\singularity\field_generator.dm" #include "code\modules\power\singularity\narsie.dm" +#include "code\modules\power\singularity\reality_tear.dm" #include "code\modules\power\singularity\singularity.dm" #include "code\modules\power\supermatter\supermatter.dm" #include "code\modules\power\supermatter\supermatter_extra_effects.dm"