initial port

This commit is contained in:
shellspeed1
2022-09-09 00:29:20 -07:00
parent 33ee9f5a19
commit fbc80cf829
11 changed files with 321 additions and 15 deletions
+14
View File
@@ -206,6 +206,19 @@
///Time to spend without clicking on other things required for your shots to become accurate.
#define GUN_AIMING_TIME (2 SECONDS)
//Autofire component
/// Compatible firemode is in the gun. Wait until it's held in the user hands.
#define AUTOFIRE_STAT_IDLE (1<<0)
/// Gun is active and in the user hands. Wait until user does a valid click.
#define AUTOFIRE_STAT_ALERT (1<<1)
/// Gun is shooting.
#define AUTOFIRE_STAT_FIRING (1<<2)
#define COMSIG_AUTOFIRE_ONMOUSEDOWN "autofire_onmousedown"
#define COMPONENT_AUTOFIRE_ONMOUSEDOWN_BYPASS (1<<0)
#define COMSIG_AUTOFIRE_SHOT "autofire_shot"
#define COMPONENT_AUTOFIRE_SHOT_SUCCESS (1<<0)
//Object/Item sharpness
#define SHARP_NONE 0
#define SHARP_EDGED 1
@@ -261,3 +274,4 @@
* a "inefficiently" prefix will be added to the message.
*/
#define FEEBLE_ATTACK_MSG_THRESHOLD 0.5
-3
View File
@@ -74,9 +74,6 @@
/obj/item/proc/onMouseUp(object, location, params, mob)
return
/obj/item/gun/CanItemAutoclick(object, location, params)
. = automatic
/atom/proc/IsAutoclickable()
. = 1
+280
View File
@@ -0,0 +1,280 @@
#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)
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
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 class='warning'>You need two hands to fire [src]!</span>")
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
+2 -1
View File
@@ -567,7 +567,8 @@
else if(picking_dropoff_turf)
holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi' //Icon for when mouse is released
holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi' //Icon for when mouse is pressed
holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released)
holder.mouse_override_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released)
holder.mouse_pointer_icon = holder.mouse_override_icon
holder.click_intercept = src //Create a click_intercept so we know where the user is clicking
else
var/mob/holder_mob = holder.mob
+4
View File
@@ -22,6 +22,8 @@
///Contains admin info. Null if client is not an admin.
var/datum/admins/holder = null
var/datum/click_intercept = null // Needs to implement InterceptClickOn(user,params,atom) proc
///Time when the click was intercepted
var/click_intercept_time = 0
var/AI_Interact = 0
var/jobbancache = null //Used to cache this client's jobbans to save on DB queries
@@ -78,6 +80,8 @@
//These two vars are used to make a special mouse cursor, with a unique icon for clicking
var/mouse_up_icon = null
var/mouse_down_icon = null
///used to override the mouse cursor so it doesnt get reset
var/mouse_override_icon = null
var/ip_intel = "Disabled"
+10 -3
View File
@@ -422,7 +422,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if( (world.address == address || !address) && !GLOB.host )
GLOB.host = key
world.update_status()
if(holder)
add_admin_verbs()
var/admin_memo_note = get_message_output("memo")
@@ -867,10 +867,17 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
ip_intel = res.intel
/client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE, extra_info)
if(last_click > world.time - world.tick_lag)
/*if(last_click > world.time - world.tick_lag)
return
last_activity = world.time
last_click = world.time
last_click = world.time*/
if(!control)
return
if(click_intercept_time)
if(click_intercept_time >= world.time)
click_intercept_time = 0 //Reset and return. Next click should work, but not this one.
return
click_intercept_time = 0 //Just reset. Let's not keep re-checking forever.
var/list/L = params2list(params)
if(L["drag"])
+4 -2
View File
@@ -875,13 +875,15 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
L.alpha = lighting_alpha
/mob/proc/update_mouse_pointer()
if (!client)
if(!client)
return
client.mouse_pointer_icon = initial(client.mouse_pointer_icon)
if (ismecha(loc))
if(istype(loc, /obj/vehicle/sealed))
var/obj/vehicle/sealed/mecha/M = loc
if(M.mouse_pointer)
client.mouse_pointer_icon = M.mouse_pointer
if(client.mouse_override_icon)
client.mouse_pointer_icon = client.mouse_override_icon
/mob/proc/is_literate()
return 0
-1
View File
@@ -92,7 +92,6 @@
/// Just 'slightly' snowflakey way to modify projectile damage for projectiles fired from this gun.
var/projectile_damage_multiplier = 1
var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds
/// directional recoil multiplier
var/dir_recoil_amp = 10
@@ -304,17 +304,21 @@
slot_flags = 0
mag_type = /obj/item/ammo_box/magazine/mm712x82
weapon_weight = WEAPON_HEAVY
var/cover_open = FALSE
can_suppress = FALSE
burst_size = 3
burst_shot_delay = 1
burst_size = 1
actions_types = list()
spread = 7
pin = /obj/item/firing_pin/implant/pindicate
automatic_burst_overlay = FALSE
var/cover_open = FALSE
/obj/item/gun/ballistic/automatic/l6_saw/unrestricted
pin = /obj/item/firing_pin
/obj/item/gun/ballistic/automatic/l6_saw/ComponentInitialize()
. = ..()
AddComponent(/datum/component/automatic_fire, 0.2 SECONDS)
/obj/item/gun/ballistic/automatic/l6_saw/examine(mob/user)
. = ..()
if(cover_open && magazine)
@@ -101,8 +101,6 @@
slot_flags = null
w_class = WEIGHT_CLASS_HUGE
custom_materials = null
automatic = 0.5
fire_delay = 2
ammo_type = list(
/obj/item/ammo_casing/energy/laser
)