NT researchers make shocking breakthrough in flux anomalogics! Tesla Cannon Resprite / Resound (#92031)
## About The Pull Request  Resprites: Tesla Cannon Tesla Cannon crafting kit ### New SFX / VFX The tesla cannon now uses a new type of beam effect that randomly picks sprite variants for each segment instead of a tracer. This makes the arc look more dynamic and less distorted. Autofire guns can now choose to use a looping sound datum when firing.  #### Balance changes The tesla cannon must now have its stock unfolded before firing, this takes 1.5 seconds and makes the gun bulky. It is still normal sized when folded, and folding it is instant. ### Bug fixes Fixed a bug where looping_sound.stop() would fail to stop sounds. The tesla cannon is an incredibly powerfu ## Why It's Good For The Game ### My reasons for respriting The old sprite was not bad, by all means but I had a few gripes with it. * The old sprite does not incorporate the flux anomaly yellow colour. * The old sprite looks a bit much like a real, professionally produced sci-fi weapon, * The old sprite looks pretty small for such a ultra high dps full auto weapon. * The old inhand is quite indistinct for something that can game end you in like one second. ### My design I think that anomaly items should be very mad science coded and, since anomaly science is by definition a poorly studied field, they should look more like prototypes created by a scientist rather than something professionally made in a factory. ## Changelog 🆑 image: The tesla cannon has new sprites. image: The tesla parts kit has new sprites. image: The tesla cannon has a new shocking beam effect when firing. sound: The tesla cannon has new sounds. balance: The tesla cannon must now be unfolded to fire. fix: looping sounds now stop playing sounds when commaned to do so. /🆑
@@ -18,7 +18,7 @@
|
||||
base_pixel_z = z;
|
||||
|
||||
#define _SET_BASE_PIXEL_VISUAL_NO_OFFSET(w, z) \
|
||||
base_pixel_z = w; \
|
||||
base_pixel_w = w; \
|
||||
base_pixel_z = z;
|
||||
|
||||
/// Much like [SET_BASE_PIXEL], except it will not effect pixel offsets in mapping programs
|
||||
|
||||
@@ -80,13 +80,7 @@
|
||||
*/
|
||||
/datum/beam/proc/Start()
|
||||
visuals = new beam_type()
|
||||
visuals.icon = icon
|
||||
visuals.icon_state = icon_state
|
||||
visuals.color = beam_color
|
||||
visuals.vis_flags = VIS_INHERIT_PLANE|VIS_INHERIT_LAYER
|
||||
visuals.emissive = emissive
|
||||
visuals.layer = beam_layer
|
||||
visuals.update_appearance()
|
||||
set_up_effect(visuals, icon_state)
|
||||
Draw()
|
||||
RegisterSignals(origin, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING), PROC_REF(redrawing))
|
||||
RegisterSignals(target, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING), PROC_REF(redrawing))
|
||||
@@ -146,15 +140,14 @@
|
||||
var/obj/effect/ebeam/segment = new beam_type(origin_turf, src)
|
||||
elements += segment
|
||||
|
||||
//Assign our single visual ebeam to each ebeam's vis_contents
|
||||
//ends are cropped by a transparent box icon of length-N pixel size laid over the visuals obj
|
||||
if(N+32>length) //went past the target, we draw a box of space to cut away from the beam sprite so the icon actually ends at the center of the target sprite
|
||||
var/icon/II = new(icon, icon_state)//this means we exclude the overshooting object from the visual contents which does mean those visuals don't show up for the final bit of the beam...
|
||||
II.DrawBox(null,1,(length-N),32,32)//in the future if you want to improve this, remove the drawbox and instead use a 513 filter to cut away at the final object's icon
|
||||
segment.icon = II
|
||||
var/icon/terminal_icon = new(icon, icon_state)//this means we exclude the overshooting object from the visual contents which does mean those visuals don't show up for the final bit of the beam...
|
||||
terminal_icon.DrawBox(null,1,(length-N),32,32)//in the future if you want to improve this, remove the drawbox and instead use a 513 filter to cut away at the final object's icon
|
||||
segment.icon = terminal_icon
|
||||
segment.color = beam_color
|
||||
else
|
||||
segment.vis_contents += visuals
|
||||
set_subsegment_appearance(segment)
|
||||
segment.transform = rot_matrix
|
||||
|
||||
//Calculate pixel offsets (If necessary)
|
||||
@@ -184,6 +177,50 @@
|
||||
segment.pixel_y = origin_py + Pixel_y
|
||||
CHECK_TICK
|
||||
|
||||
/datum/beam/proc/set_up_effect(obj/effect/ebeam/beam_effect, effect_icon_state)
|
||||
beam_effect.icon = icon
|
||||
beam_effect.icon_state = effect_icon_state
|
||||
beam_effect.color = beam_color
|
||||
beam_effect.vis_flags = VIS_INHERIT_PLANE|VIS_INHERIT_LAYER
|
||||
beam_effect.emissive = emissive
|
||||
beam_effect.layer = beam_layer
|
||||
beam_effect.update_appearance()
|
||||
|
||||
///sets the sprite of the segment, using the more performant viscontents by default
|
||||
/datum/beam/proc/set_subsegment_appearance(obj/effect/ebeam/segment)
|
||||
//Assign our single visual ebeam to each ebeam's vis_contents
|
||||
segment.vis_contents += visuals
|
||||
|
||||
//for when you don't want each segment to look identital
|
||||
/datum/beam/varied
|
||||
//how many variants do we have in addition to the unnumbered state we use as a base icon state and terminal segment
|
||||
var/icon_state_variants = 1
|
||||
|
||||
/datum/beam/varied/New(
|
||||
origin,
|
||||
target,
|
||||
icon = 'icons/effects/beam.dmi',
|
||||
icon_state = "b_beam",
|
||||
time = INFINITY,
|
||||
max_distance = INFINITY,
|
||||
beam_type = /obj/effect/ebeam,
|
||||
beam_color = null,
|
||||
emissive = TRUE,
|
||||
override_origin_pixel_x = null,
|
||||
override_origin_pixel_y = null,
|
||||
override_target_pixel_x = null,
|
||||
override_target_pixel_y = null,
|
||||
beam_layer = ABOVE_ALL_MOB_LAYER,
|
||||
icon_state_variants = 1
|
||||
)
|
||||
. = ..()
|
||||
|
||||
src.icon_state_variants = icon_state_variants
|
||||
|
||||
/datum/beam/varied/set_subsegment_appearance(obj/effect/ebeam/segment)
|
||||
//we use reall ass icon states here.
|
||||
set_up_effect(segment, "[icon_state][rand(1, icon_state_variants)]")
|
||||
|
||||
/obj/effect/ebeam
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
@@ -298,8 +335,14 @@
|
||||
override_origin_pixel_y = null,
|
||||
override_target_pixel_x = null,
|
||||
override_target_pixel_y = null,
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
layer = ABOVE_ALL_MOB_LAYER,
|
||||
icon_state_variants = 0,
|
||||
)
|
||||
var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type, beam_color, emissive, override_origin_pixel_x, override_origin_pixel_y, override_target_pixel_x, override_target_pixel_y, layer)
|
||||
var/datum/beam/newbeam
|
||||
|
||||
if(icon_state_variants <= 0)
|
||||
newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type, beam_color, emissive, override_origin_pixel_x, override_origin_pixel_y, override_target_pixel_x, override_target_pixel_y, layer)
|
||||
else
|
||||
newbeam = new /datum/beam/varied(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type, beam_color, emissive, override_origin_pixel_x, override_origin_pixel_y, override_target_pixel_x, override_target_pixel_y, layer, icon_state_variants)
|
||||
INVOKE_ASYNC(newbeam, TYPE_PROC_REF(/datum/beam/, Start))
|
||||
return newbeam
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
/obj/item/weaponcrafting/gunkit/tesla
|
||||
name = "tesla cannon parts kit (lethal)"
|
||||
desc = "A suitcase containing the necessary gun parts to construct a tesla cannon around a stabilized flux anomaly. Handle with care."
|
||||
icon_state = "weaponskit_tesla"
|
||||
|
||||
/obj/item/weaponcrafting/gunkit/xray
|
||||
name = "x-ray laser gun parts kit (lethal)"
|
||||
|
||||
@@ -28,9 +28,12 @@
|
||||
var/windup_spindown = 3 SECONDS
|
||||
///Timer for tracking the spindown reset timings
|
||||
var/timerid
|
||||
///Looping sound while firing.
|
||||
var/datum/looping_sound/autofire_sound_loop
|
||||
COOLDOWN_DECLARE(next_shot_cd)
|
||||
|
||||
/datum/component/automatic_fire/Initialize(autofire_shot_delay, windup_autofire, windup_autofire_reduction_multiplier, windup_autofire_cap, windup_spindown, allow_akimbo = TRUE)
|
||||
|
||||
/datum/component/automatic_fire/Initialize(autofire_shot_delay, windup_autofire, windup_autofire_reduction_multiplier, windup_autofire_cap, windup_spindown, allow_akimbo = TRUE, firing_sound_loop)
|
||||
. = ..()
|
||||
if(!isgun(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
@@ -48,8 +51,12 @@
|
||||
var/mob/user = gun.loc
|
||||
wake_up(src, user)
|
||||
|
||||
if(firing_sound_loop)
|
||||
autofire_sound_loop = new firing_sound_loop(parent)
|
||||
|
||||
|
||||
/datum/component/automatic_fire/Destroy()
|
||||
QDEL_NULL(autofire_sound_loop)
|
||||
autofire_off()
|
||||
return ..()
|
||||
|
||||
@@ -191,6 +198,8 @@
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDRAG, PROC_REF(on_mouse_drag))
|
||||
|
||||
if(autofire_sound_loop)
|
||||
autofire_sound_loop.start(shooter)
|
||||
|
||||
/datum/component/automatic_fire/proc/on_mouse_up(datum/source, atom/object, turf/location, control, params)
|
||||
SIGNAL_HANDLER
|
||||
@@ -217,6 +226,9 @@
|
||||
target_loc = null
|
||||
mouse_parameters = null
|
||||
|
||||
if(autofire_sound_loop)
|
||||
autofire_sound_loop.stop()
|
||||
|
||||
/datum/component/automatic_fire/proc/on_mouse_drag(client/source, atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params)
|
||||
SIGNAL_HANDLER
|
||||
if(isnull(over_location)) //This happens when the mouse is over an inventory or screen object, or on entering deep darkness, for example.
|
||||
@@ -238,7 +250,6 @@
|
||||
target_loc = get_turf(over_object)
|
||||
mouse_parameters = params
|
||||
|
||||
|
||||
/datum/component/automatic_fire/proc/process_shot()
|
||||
if(autofire_stat != AUTOFIRE_STAT_FIRING)
|
||||
return FALSE
|
||||
|
||||
@@ -63,6 +63,10 @@
|
||||
var/direct
|
||||
/// Sound channel to play on, random if not provided
|
||||
var/sound_channel
|
||||
///If we want to reserve a random channel when we start playing sounds. Good for when there could be several sources of the same looping sound heard by the same
|
||||
var/reserve_random_channel = FALSE
|
||||
//If we reserve a random sound channel, store the channel number here so we can clean it up later.
|
||||
var/reserved_channel
|
||||
|
||||
/datum/looping_sound/New(_parent, start_immediately = FALSE, _direct = FALSE, _skip_starting_sounds = FALSE)
|
||||
if(!mid_sounds)
|
||||
@@ -91,6 +95,11 @@
|
||||
set_parent(on_behalf_of)
|
||||
if(timer_id)
|
||||
return
|
||||
|
||||
if(!sound_channel && reserve_random_channel)
|
||||
sound_channel = SSsounds.reserve_sound_channel_datumless()
|
||||
reserved_channel = sound_channel
|
||||
|
||||
on_start()
|
||||
|
||||
/**
|
||||
@@ -110,6 +119,11 @@
|
||||
timer_id = null
|
||||
loop_started = FALSE
|
||||
|
||||
if(reserved_channel)
|
||||
sound_channel = null
|
||||
SSsounds.free_sound_channel(reserved_channel)
|
||||
|
||||
|
||||
/// The proc that handles starting the actual core sound loop.
|
||||
/datum/looping_sound/proc/start_sound_loop()
|
||||
loop_started = TRUE
|
||||
@@ -165,7 +179,8 @@
|
||||
pressure_affected = pressure_affected,
|
||||
ignore_walls = ignore_walls,
|
||||
falloff_distance = falloff_distance,
|
||||
use_reverb = use_reverb
|
||||
use_reverb = use_reverb,
|
||||
channel = sound_channel || SSsounds.random_available_channel()
|
||||
)
|
||||
|
||||
/// Returns the sound we should now be playing.
|
||||
|
||||
@@ -47,3 +47,15 @@
|
||||
/datum/looping_sound/zipline
|
||||
mid_sounds = list('sound/items/weapons/zipline_mid.ogg' = 1)
|
||||
volume = 5
|
||||
|
||||
/datum/looping_sound/tesla_cannon
|
||||
start_sound = list('sound/items/weapons/gun/tesla/tesla_start.ogg' = 1)
|
||||
start_volume = 100
|
||||
start_length = 200 MILLISECONDS
|
||||
mid_sounds = list('sound/items/weapons/gun/tesla/tesla_loop.ogg' = 1)
|
||||
mid_length = 3.8 SECONDS
|
||||
volume = 100
|
||||
end_sound = list('sound/items/weapons/gun/tesla/power_breaker_fan.ogg' = 1)
|
||||
end_volume = 15
|
||||
ignore_walls = FALSE
|
||||
reserve_random_channel = TRUE
|
||||
|
||||
@@ -92,15 +92,3 @@
|
||||
|
||||
/obj/effect/projectile/tracer/sniper
|
||||
icon_state = "sniper"
|
||||
|
||||
/obj/effect/projectile/tracer/lightning
|
||||
icon = 'icons/effects/beam.dmi'
|
||||
icon_state = "lightning2"
|
||||
|
||||
/obj/effect/projectile/tracer/lightning/Initialize(mapload)
|
||||
. = ..()
|
||||
update_appearance()
|
||||
|
||||
/obj/effect/projectile/tracer/lightning/update_icon_state()
|
||||
. = ..()
|
||||
icon_state = "lightning[rand(1, 12)]"
|
||||
|
||||
@@ -572,6 +572,9 @@
|
||||
return FALSE
|
||||
if(!(flags & SHOCK_ILLUSION))
|
||||
adjustFireLoss(shock_damage)
|
||||
if(getFireLoss() > 100)
|
||||
add_shared_particles(/particles/smoke/burning)
|
||||
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, remove_shared_particles), /particles/smoke/burning), 10 SECONDS)
|
||||
else
|
||||
adjustStaminaLoss(shock_damage)
|
||||
if(!(flags & SHOCK_SUPPRESS_MESSAGE))
|
||||
|
||||
@@ -63,11 +63,11 @@
|
||||
harmful = FALSE
|
||||
|
||||
/obj/item/ammo_casing/energy/tesla_cannon
|
||||
fire_sound = 'sound/effects/magic/lightningshock.ogg'
|
||||
fire_sound = null
|
||||
e_cost = LASER_SHOTS(33, STANDARD_CELL_CHARGE)
|
||||
select_name = "shock"
|
||||
projectile_type = /obj/projectile/energy/tesla_cannon
|
||||
firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/blue
|
||||
firing_effect_type = null
|
||||
|
||||
/obj/item/ammo_casing/energy/shrink
|
||||
projectile_type = /obj/projectile/magic/shrink/alien
|
||||
|
||||
@@ -360,18 +360,72 @@
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/**
|
||||
-----------------Tesla Cannon--------------------------------
|
||||
|
||||
An advanced weapon that provides extremely high dps output at pinpoint accuracy due to its hitscan nature.
|
||||
|
||||
Due to its normal w_class when folded it is suitable as a heavy reinforcement weapon, since the cell drains very quickly when firing.
|
||||
|
||||
The power level is somewhat tempered by several drawbacks such as research requirements, anomalock, two handed firing requirement, and insultation providing damage reduction.
|
||||
|
||||
it is often confused with the mech weapon of the same name, since it is a bit more obscure despite being very powerful. Formerly called the tesla revolver.
|
||||
**/
|
||||
/obj/item/gun/energy/tesla_cannon
|
||||
name = "tesla cannon"
|
||||
icon = 'icons/obj/weapons/guns/wide_guns.dmi'
|
||||
icon_state = "tesla"
|
||||
inhand_icon_state = "tesla"
|
||||
desc = "A gun powered by a flux anomaly that shoots lightning bolts. Electrically insulating clothing may protect from some of the damage."
|
||||
lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi'
|
||||
inhand_icon_state = null //null so we build the correct inhand.
|
||||
desc = "A high voltage flux projector prototype created using the latest advancements in the anomaly science.\n\nThe anomalous nature of the flux core allows the tesla arc to be guided from the electrode to the target without being diverted to stray conductors outside the target field."
|
||||
SET_BASE_VISUAL_PIXEL(-8, 0)
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/tesla_cannon)
|
||||
inhand_x_dimension = 64
|
||||
shaded_charge = TRUE
|
||||
charge_sections = 2
|
||||
display_empty = FALSE
|
||||
weapon_weight = WEAPON_HEAVY
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
///if our stpck is extended and we are ready to fire.
|
||||
var/ready_to_fire = FALSE
|
||||
|
||||
/obj/item/gun/energy/tesla_cannon/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/automatic_fire, 0.1 SECONDS)
|
||||
AddComponent(/datum/component/automatic_fire, autofire_shot_delay = 100 MILLISECONDS, firing_sound_loop = /datum/looping_sound/tesla_cannon)
|
||||
|
||||
/obj/item/gun/energy/tesla_cannon/can_trigger_gun(mob/living/user, akimbo_usage)
|
||||
if(ready_to_fire)
|
||||
return ..()
|
||||
//If we have charge, but the stock is folded, do sparks.
|
||||
if(can_shoot())
|
||||
balloon_alert(user, "electricity arcing to stock!")
|
||||
|
||||
if(prob(75)) //fake sparks to cut on spark spam
|
||||
playsound(user, 'sound/effects/sparks/sparks1.ogg', 50, TRUE)
|
||||
else
|
||||
do_sparks(3, FALSE, user)
|
||||
return FALSE
|
||||
|
||||
/obj/item/gun/energy/tesla_cannon/attack_self(mob/living/user)
|
||||
. = ..()
|
||||
if(ready_to_fire)
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
ready_to_fire = FALSE
|
||||
icon_state = "tesla"
|
||||
playsound(user, 'sound/items/weapons/gun/tesla/squeak_latch.ogg', 100)
|
||||
|
||||
else
|
||||
playsound(user, 'sound/items/weapons/gun/tesla/click_creak.ogg', 100)
|
||||
if(!do_after(user, 1.5 SECONDS))
|
||||
return
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
ready_to_fire = TRUE
|
||||
icon_state = "tesla_unfolded"
|
||||
playsound(user, 'sound/items/weapons/gun/tesla/squeak_latch.ogg', 100)
|
||||
|
||||
update_appearance()
|
||||
balloon_alert_to_viewers("[ready_to_fire ? "unfolded" : "folded"] stock")
|
||||
|
||||
/obj/item/gun/energy/marksman_revolver
|
||||
name = "marksman revolver"
|
||||
|
||||
@@ -29,13 +29,14 @@
|
||||
name = "tesla bolt"
|
||||
icon_state = null
|
||||
hitscan = TRUE
|
||||
tracer_type = /obj/effect/projectile/tracer/lightning
|
||||
impact_effect_type = null
|
||||
damage = 5
|
||||
var/shock_damage = 10
|
||||
|
||||
/obj/projectile/energy/tesla_cannon/on_hit(atom/target, blocked = 0, pierce_hit)
|
||||
. = ..()
|
||||
firer.Beam(target, icon_state = "tesla", time = 1, icon_state_variants = 24)
|
||||
|
||||
if(isliving(target))
|
||||
var/mob/living/victim = target
|
||||
victim.electrocute_act(shock_damage, src, siemens_coeff = 1, flags = SHOCK_NOSTUN|SHOCK_TESLA)
|
||||
|
||||
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 928 B After Width: | Height: | Size: 1.1 KiB |
7
sound/items/weapons/gun/tesla/attributions.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
tesla_loop.ogg - https://freesound.org/people/Department64/sounds/554749/, License: CC BY 4.0
|
||||
tesla_start.ogg - https://freesound.org/people/Department64/sounds/554749/, https://freesound.org/people/iainmccurdy/sounds/759257/, License: CC BY 4.0
|
||||
click_creak.ogg - https://freesound.org/people/Sonicquinn/sounds/435834/, https://freesound.org/people/holisoysilvi/sounds/610182/, License: CC 0
|
||||
squeak_latch.ogg - https://freesound.org/people/Sonicquinn/sounds/435834/, https://freesound.org/people/holisoysilvi/sounds/610182/, License: CC 0
|
||||
power_breaker_fan.ogg - https://freesound.org/people/Yoyodaman234/sounds/183544/, https://freesound.org/people/jessepash/sounds/139970/, https://freesound.org/people/sribubba/sounds/130154/, License: CC 0
|
||||
}
|
||||