mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-15 12:11:45 +00:00
* Refactors most spans into span procs * AA * a * AAAAAAAAAAAAAAAAAAAAAA * Update species.dm Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com> Co-authored-by: Gandalf <jzo123@hotmail.com>
284 lines
11 KiB
Plaintext
284 lines
11 KiB
Plaintext
/* SKYRAT EDIT REMOVAL - MOVED TO MODULAR FULLAUTO.DM
|
|
#define AUTOFIRE_MOUSEUP 0
|
|
#define AUTOFIRE_MOUSEDOWN 1
|
|
|
|
/datum/component/automatic_fire
|
|
var/client/clicker
|
|
var/mob/living/shooter
|
|
var/atom/target
|
|
var/turf/target_loc //For dealing with locking on targets due to BYOND engine limitations (the mouse input only happening when mouse moves).
|
|
var/autofire_stat = AUTOFIRE_STAT_IDLE
|
|
var/mouse_parameters
|
|
var/autofire_shot_delay = 0.3 SECONDS //Time between individual shots.
|
|
var/mouse_status = AUTOFIRE_MOUSEUP //This seems hacky but there can be two MouseDown() without a MouseUp() in between if the user holds click and uses alt+tab, printscreen or similar.
|
|
|
|
COOLDOWN_DECLARE(next_shot_cd)
|
|
|
|
/datum/component/automatic_fire/Initialize(_autofire_shot_delay)
|
|
. = ..()
|
|
if(!isgun(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
var/obj/item/gun = parent
|
|
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/wake_up)
|
|
if(_autofire_shot_delay)
|
|
autofire_shot_delay = _autofire_shot_delay
|
|
if(autofire_stat == AUTOFIRE_STAT_IDLE && ismob(gun.loc))
|
|
var/mob/user = gun.loc
|
|
wake_up(src, user)
|
|
|
|
|
|
/datum/component/automatic_fire/Destroy()
|
|
autofire_off()
|
|
return ..()
|
|
|
|
/datum/component/automatic_fire/process(delta_time)
|
|
if(autofire_stat != AUTOFIRE_STAT_FIRING)
|
|
STOP_PROCESSING(SSprojectiles, src)
|
|
return
|
|
process_shot()
|
|
|
|
/datum/component/automatic_fire/proc/wake_up(datum/source, mob/user, slot)
|
|
SIGNAL_HANDLER
|
|
|
|
if(autofire_stat == AUTOFIRE_STAT_ALERT)
|
|
return //We've updated the firemode. No need for more.
|
|
if(autofire_stat == AUTOFIRE_STAT_FIRING)
|
|
stop_autofiring() //Let's stop shooting to avoid issues.
|
|
return
|
|
if(iscarbon(user))
|
|
var/mob/living/carbon/arizona_ranger = user
|
|
if(arizona_ranger.is_holding(parent))
|
|
autofire_on(arizona_ranger.client)
|
|
|
|
// There is a gun and there is a user wielding it. The component now waits for the mouse click.
|
|
/datum/component/automatic_fire/proc/autofire_on(client/usercli)
|
|
SIGNAL_HANDLER
|
|
|
|
if(autofire_stat != AUTOFIRE_STAT_IDLE)
|
|
return
|
|
autofire_stat = AUTOFIRE_STAT_ALERT
|
|
if(!QDELETED(usercli))
|
|
clicker = usercli
|
|
shooter = clicker.mob
|
|
RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down)
|
|
if(!QDELETED(shooter))
|
|
RegisterSignal(shooter, COMSIG_MOB_LOGOUT, .proc/autofire_off)
|
|
UnregisterSignal(shooter, COMSIG_MOB_LOGIN)
|
|
RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED), .proc/autofire_off)
|
|
parent.RegisterSignal(src, COMSIG_AUTOFIRE_ONMOUSEDOWN, /obj/item/gun/.proc/autofire_bypass_check)
|
|
parent.RegisterSignal(parent, COMSIG_AUTOFIRE_SHOT, /obj/item/gun/.proc/do_autofire)
|
|
|
|
|
|
/datum/component/automatic_fire/proc/autofire_off(datum/source)
|
|
SIGNAL_HANDLER
|
|
if(autofire_stat == AUTOFIRE_STAT_IDLE)
|
|
return
|
|
if(autofire_stat == AUTOFIRE_STAT_FIRING)
|
|
stop_autofiring()
|
|
|
|
autofire_stat = AUTOFIRE_STAT_IDLE
|
|
|
|
if(!QDELETED(clicker))
|
|
UnregisterSignal(clicker, list(COMSIG_CLIENT_MOUSEDOWN, COMSIG_CLIENT_MOUSEUP, COMSIG_CLIENT_MOUSEDRAG))
|
|
mouse_status = AUTOFIRE_MOUSEUP //In regards to the component there's no click anymore to care about.
|
|
clicker = null
|
|
if(!QDELETED(shooter))
|
|
RegisterSignal(shooter, COMSIG_MOB_LOGIN, .proc/on_client_login)
|
|
UnregisterSignal(shooter, COMSIG_MOB_LOGOUT)
|
|
UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED))
|
|
shooter = null
|
|
parent.UnregisterSignal(parent, COMSIG_AUTOFIRE_SHOT)
|
|
parent.UnregisterSignal(src, COMSIG_AUTOFIRE_ONMOUSEDOWN)
|
|
|
|
/datum/component/automatic_fire/proc/on_client_login(mob/source)
|
|
SIGNAL_HANDLER
|
|
if(!source.client)
|
|
return
|
|
if(source.is_holding(parent))
|
|
autofire_on(source.client)
|
|
|
|
/datum/component/automatic_fire/proc/on_mouse_down(client/source, atom/_target, turf/location, control, params)
|
|
SIGNAL_HANDLER
|
|
var/list/modifiers = params2list(params) //If they're shift+clicking, for example, let's not have them accidentally shoot.
|
|
|
|
if(LAZYACCESS(modifiers, SHIFT_CLICK))
|
|
return
|
|
if(LAZYACCESS(modifiers, CTRL_CLICK))
|
|
return
|
|
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
|
|
return
|
|
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
|
return
|
|
if(LAZYACCESS(modifiers, ALT_CLICK))
|
|
return
|
|
if(source.mob.throw_mode)
|
|
return
|
|
if(!isturf(source.mob.loc)) //No firing inside lockers and stuff.
|
|
return
|
|
if(get_dist(source.mob, _target) < 2) //Adjacent clicking.
|
|
return
|
|
|
|
if(isnull(location)) //Clicking on a screen object.
|
|
if(_target.plane != CLICKCATCHER_PLANE) //The clickcatcher is a special case. We want the click to trigger then, under it.
|
|
return //If we click and drag on our worn backpack, for example, we want it to open instead.
|
|
_target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source)
|
|
if(!_target)
|
|
CRASH("Failed to get the turf under clickcatcher")
|
|
|
|
if(SEND_SIGNAL(src, COMSIG_AUTOFIRE_ONMOUSEDOWN, source, _target, location, control, params) & COMPONENT_AUTOFIRE_ONMOUSEDOWN_BYPASS)
|
|
return
|
|
|
|
source.click_intercept_time = world.time //From this point onwards Click() will no longer be triggered.
|
|
|
|
if(autofire_stat == (AUTOFIRE_STAT_IDLE))
|
|
CRASH("on_mouse_down() called with [autofire_stat] autofire_stat")
|
|
if(autofire_stat == AUTOFIRE_STAT_FIRING)
|
|
stop_autofiring() //This can happen if we click and hold and then alt+tab, printscreen or other such action. MouseUp won't be called then and it will keep autofiring.
|
|
|
|
target = _target
|
|
target_loc = get_turf(target)
|
|
mouse_parameters = params
|
|
INVOKE_ASYNC(src, .proc/start_autofiring)
|
|
|
|
|
|
//Dakka-dakka
|
|
/datum/component/automatic_fire/proc/start_autofiring()
|
|
if(autofire_stat == AUTOFIRE_STAT_FIRING)
|
|
return
|
|
autofire_stat = AUTOFIRE_STAT_FIRING
|
|
|
|
clicker.mouse_override_icon = 'icons/effects/mouse_pointers/weapon_pointer.dmi'
|
|
clicker.mouse_pointer_icon = clicker.mouse_override_icon
|
|
|
|
if(mouse_status == AUTOFIRE_MOUSEUP) //See mouse_status definition for the reason for this.
|
|
RegisterSignal(clicker, COMSIG_CLIENT_MOUSEUP, .proc/on_mouse_up)
|
|
mouse_status = AUTOFIRE_MOUSEDOWN
|
|
|
|
RegisterSignal(shooter, COMSIG_MOB_SWAP_HANDS, .proc/stop_autofiring)
|
|
|
|
if(isgun(parent))
|
|
var/obj/item/gun/shoota = parent
|
|
if(!shoota.on_autofire_start(shooter)) //This is needed because the minigun has a do_after before firing and signals are async.
|
|
stop_autofiring()
|
|
return
|
|
if(autofire_stat != AUTOFIRE_STAT_FIRING)
|
|
return //Things may have changed while on_autofire_start() was being processed, due to do_after's sleep.
|
|
|
|
if(!process_shot()) //First shot is processed instantly.
|
|
return //If it fails, such as when the gun is empty, then there's no need to schedule a second shot.
|
|
|
|
START_PROCESSING(SSprojectiles, src)
|
|
RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDRAG, .proc/on_mouse_drag)
|
|
|
|
|
|
/datum/component/automatic_fire/proc/on_mouse_up(datum/source, atom/object, turf/location, control, params)
|
|
SIGNAL_HANDLER
|
|
UnregisterSignal(clicker, COMSIG_CLIENT_MOUSEUP)
|
|
mouse_status = AUTOFIRE_MOUSEUP
|
|
if(autofire_stat == AUTOFIRE_STAT_FIRING)
|
|
stop_autofiring()
|
|
return COMPONENT_CLIENT_MOUSEUP_INTERCEPT
|
|
|
|
|
|
/datum/component/automatic_fire/proc/stop_autofiring(datum/source, atom/object, turf/location, control, params)
|
|
SIGNAL_HANDLER
|
|
if(autofire_stat != AUTOFIRE_STAT_FIRING)
|
|
return
|
|
STOP_PROCESSING(SSprojectiles, src)
|
|
autofire_stat = AUTOFIRE_STAT_ALERT
|
|
if(clicker)
|
|
clicker.mouse_override_icon = null
|
|
clicker.mouse_pointer_icon = clicker.mouse_override_icon
|
|
UnregisterSignal(clicker, COMSIG_CLIENT_MOUSEDRAG)
|
|
if(!QDELETED(shooter))
|
|
UnregisterSignal(shooter, COMSIG_MOB_SWAP_HANDS)
|
|
target = null
|
|
target_loc = null
|
|
mouse_parameters = null
|
|
|
|
/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.
|
|
var/list/modifiers = params2list(params)
|
|
var/new_target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source)
|
|
mouse_parameters = params
|
|
if(!new_target)
|
|
if(QDELETED(target)) //No new target acquired, and old one was deleted, get us out of here.
|
|
stop_autofiring()
|
|
CRASH("on_mouse_drag failed to get the turf under screen object [over_object.type]. Old target was incidentally QDELETED.")
|
|
target = get_turf(target) //If previous target wasn't a turf, let's turn it into one to avoid locking onto a potentially moving target.
|
|
target_loc = target
|
|
CRASH("on_mouse_drag failed to get the turf under screen object [over_object.type]")
|
|
target = new_target
|
|
target_loc = new_target
|
|
return
|
|
target = over_object
|
|
target_loc = get_turf(over_object)
|
|
mouse_parameters = params
|
|
|
|
|
|
/datum/component/automatic_fire/proc/process_shot()
|
|
if(autofire_stat != AUTOFIRE_STAT_FIRING)
|
|
return FALSE
|
|
if(!COOLDOWN_FINISHED(src, next_shot_cd))
|
|
return TRUE
|
|
if(QDELETED(target) || get_turf(target) != target_loc) //Target moved or got destroyed since we last aimed.
|
|
target = target_loc //So we keep firing on the emptied tile until we move our mouse and find a new target.
|
|
if(get_dist(shooter, target) <= 0)
|
|
target = get_step(shooter, shooter.dir) //Shoot in the direction faced if the mouse is on the same tile as we are.
|
|
target_loc = target
|
|
else if(!in_view_range(shooter, target))
|
|
stop_autofiring() //Elvis has left the building.
|
|
return FALSE
|
|
shooter.face_atom(target)
|
|
COOLDOWN_START(src, next_shot_cd, autofire_shot_delay)
|
|
if(SEND_SIGNAL(parent, COMSIG_AUTOFIRE_SHOT, target, shooter, mouse_parameters) & COMPONENT_AUTOFIRE_SHOT_SUCCESS)
|
|
return TRUE
|
|
stop_autofiring()
|
|
return FALSE
|
|
|
|
// Gun procs.
|
|
|
|
/obj/item/gun/proc/on_autofire_start(mob/living/shooter)
|
|
if(semicd || shooter.stat || !can_trigger_gun(shooter))
|
|
return FALSE
|
|
if(!can_shoot())
|
|
shoot_with_empty_chamber(shooter)
|
|
return FALSE
|
|
var/obj/item/bodypart/other_hand = shooter.has_hand_for_held_index(shooter.get_inactive_hand_index())
|
|
if(weapon_weight == WEAPON_HEAVY && (shooter.get_inactive_held_item() || !other_hand))
|
|
to_chat(shooter, span_warning("You need two hands to fire [src]!"))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
|
|
/obj/item/gun/proc/autofire_bypass_check(datum/source, client/clicker, atom/target, turf/location, control, params)
|
|
SIGNAL_HANDLER
|
|
if(clicker.mob.get_active_held_item() != src)
|
|
return COMPONENT_AUTOFIRE_ONMOUSEDOWN_BYPASS
|
|
|
|
|
|
/obj/item/gun/proc/do_autofire(datum/source, atom/target, mob/living/shooter, params)
|
|
SIGNAL_HANDLER
|
|
if(semicd || shooter.stat)
|
|
return NONE
|
|
if(!can_shoot())
|
|
shoot_with_empty_chamber(shooter)
|
|
return NONE
|
|
INVOKE_ASYNC(src, .proc/do_autofire_shot, source, target, shooter, params)
|
|
return COMPONENT_AUTOFIRE_SHOT_SUCCESS //All is well, we can continue shooting.
|
|
|
|
|
|
/obj/item/gun/proc/do_autofire_shot(datum/source, atom/target, mob/living/shooter, params)
|
|
var/obj/item/gun/akimbo_gun = shooter.get_inactive_held_item()
|
|
var/bonus_spread = 0
|
|
if(istype(akimbo_gun) && weapon_weight < WEAPON_MEDIUM)
|
|
if(akimbo_gun.weapon_weight < WEAPON_MEDIUM && akimbo_gun.can_trigger_gun(shooter))
|
|
bonus_spread = dual_wield_spread
|
|
addtimer(CALLBACK(akimbo_gun, /obj/item/gun.proc/process_fire, target, shooter, TRUE, params, null, bonus_spread), 1)
|
|
process_fire(target, shooter, TRUE, params, null, bonus_spread)
|
|
|
|
#undef AUTOFIRE_MOUSEUP
|
|
#undef AUTOFIRE_MOUSEDOWN
|
|
*/
|