Merge branch 'master' into premium-kas-for-borgs

This commit is contained in:
shellspeed1
2022-09-23 17:00:02 -07:00
committed by GitHub
68 changed files with 828 additions and 201 deletions

View File

@@ -152,6 +152,10 @@
#define ATMOS_PASS_PROC -1 //ask CanAtmosPass()
#define ATMOS_PASS_DENSITY -2 //just check density
// Adjacency flags
#define ATMOS_ADJACENT_ANY (1<<0)
#define ATMOS_ADJACENT_FIRELOCK (1<<1)
#define CANATMOSPASS(A, O) ( A.CanAtmosPass == ATMOS_PASS_PROC ? A.CanAtmosPass(O) : ( A.CanAtmosPass == ATMOS_PASS_DENSITY ? !A.density : A.CanAtmosPass ) )
#define CANVERTICALATMOSPASS(A, O) ( A.CanAtmosPassVertical == ATMOS_PASS_PROC ? A.CanAtmosPass(O, TRUE) : ( A.CanAtmosPassVertical == ATMOS_PASS_DENSITY ? !A.density : A.CanAtmosPassVertical ) )
@@ -323,14 +327,6 @@
#define QUANTIZE(variable) (round(variable,0.0000001))/*I feel the need to document what happens here. Basically this is used to catch most rounding errors, however it's previous value made it so that
once gases got hot enough, most procedures wouldnt occur due to the fact that the mole counts would get rounded away. Thus, we lowered it a few orders of magnititude */
#ifdef TESTING
GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
#define CALCULATE_ADJACENT_TURFS(T) if (SSadjacent_air.queue[T]) { GLOB.atmos_adjacent_savings[1] += 1 } else { GLOB.atmos_adjacent_savings[2] += 1; SSadjacent_air.queue[T] = 1 }
#else
#define CALCULATE_ADJACENT_TURFS(T) SSadjacent_air.queue[T] = 1
#endif
//If you're doing spreading things related to atmos, DO NOT USE CANATMOSPASS, IT IS NOT CHEAP. use this instead, the info is cached after all. it's tweaked just a bit to allow for circular checks
#define TURFS_CAN_SHARE(T1, T2) (LAZYACCESS(T2.atmos_adjacent_turfs, T1) || LAZYLEN(T1.atmos_adjacent_turfs & T2.atmos_adjacent_turfs))

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

View File

@@ -321,6 +321,12 @@
///from base of mob/AltClickOn(): (atom/A)
#define COMSIG_MOB_ALTCLICKON "mob_altclickon"
//Gun signals
///When a gun is switched to automatic fire mode
#define COMSIG_GUN_AUTOFIRE_SELECTED "gun_autofire_selected"
///When a gun is switched off of automatic fire mode
#define COMSIG_GUN_AUTOFIRE_DESELECTED "gun_autofire_deselected"
// Lighting:
///from base of [atom/proc/set_light]: (l_range, l_power, l_color, l_on)
#define COMSIG_ATOM_SET_LIGHT "atom_set_light"
@@ -348,12 +354,22 @@
#define COMSIG_ATOM_UPDATE_LIGHT_FLAGS "atom_update_light_flags"
// /client signals
#define COMSIG_MOB_CLIENT_LOGIN "mob_client_login" //sent when a mob/login() finishes: (client)
#define COMSIG_MOB_CLIENT_LOGOUT "mob_client_logout" //sent when a mob/logout() starts: (client)
#define COMSIG_MOB_CLIENT_MOVE "mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc)
#define COMSIG_MOB_CLIENT_CHANGE_VIEW "mob_client_change_view" //from base of /client/change_view(): (client, old_view, view)
#define COMSIG_MOB_CLIENT_MOUSEMOVE "mob_client_mousemove" //from base of /client/MouseMove(): (object, location, control, params)
///sent when a mob/login() finishes: (client)
#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login"
//from base of client/MouseDown(): (/client, object, location, control, params)
#define COMSIG_CLIENT_MOUSEDOWN "client_mousedown"
//from base of client/MouseUp(): (/client, object, location, control, params)
#define COMSIG_CLIENT_MOUSEUP "client_mouseup"
#define COMPONENT_CLIENT_MOUSEUP_INTERCEPT (1<<0)
//from base of client/MouseUp(): (/client, object, location, control, params)
#define COMSIG_CLIENT_MOUSEDRAG "client_mousedrag"
// /mob/living signals
#define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living)

3
code/__DEFINES/gun.dm Normal file
View File

@@ -0,0 +1,3 @@
#define SELECT_SEMI_AUTOMATIC 1
#define SELECT_BURST_SHOT 2
#define SELECT_FULLY_AUTOMATIC 3

View File

@@ -4,3 +4,6 @@
#define HUD_STYLE_NOHUD 3 //No hud (for screenshots)
#define HUD_VERSIONS 3 //Used in show_hud(); Please ensure this is the same as the maximum index.
#define ui_borg_pda_send "CENTER+5:21,SOUTH:5" // To the right of the alert panel
#define ui_borg_pda_log "CENTER+6:21,SOUTH:5"

View File

@@ -388,6 +388,23 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define BEAT_SLOW 2
#define BEAT_NONE 0
//Mouse buttons pressed/held/released
#define RIGHT_CLICK "right"
#define MIDDLE_CLICK "middle"
#define LEFT_CLICK "left"
//Keys held down during the mouse action
#define CTRL_CLICK "ctrl"
#define ALT_CLICK "alt"
#define SHIFT_CLICK "shift"
//Pixel coordinates within the icon, in the icon's coordinate space
#define ICON_X "icon-x"
#define ICON_Y "icon-y"
//Pixel coordinates in screen_loc format ("[tile_x]:[pixel_x],[tile_y]:[pixel_y]")
#define SCREEN_LOC "screen-loc"
//https://secure.byond.com/docs/ref/info.html#/atom/var/mouse_opacity
#define MOUSE_OPACITY_TRANSPARENT 0
#define MOUSE_OPACITY_ICON 1

View File

@@ -673,6 +673,27 @@ Turf and target are separate in case you want to teleport some distance from a t
if(final_x || final_y)
return locate(final_x, final_y, T.z)
///Returns a turf based on text inputs, original turf and viewing client
/proc/parse_caught_click_modifiers(list/modifiers, turf/origin, client/viewing_client)
if(!modifiers)
return null
var/screen_loc = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",")
var/list/actual_view = getviewsize(viewing_client ? viewing_client.view : world.view)
var/click_turf_x = splittext(screen_loc[1], ":")
var/click_turf_y = splittext(screen_loc[2], ":")
var/click_turf_z = origin.z
var/click_turf_px = text2num(click_turf_x[2])
var/click_turf_py = text2num(click_turf_y[2])
click_turf_x = origin.x + text2num(click_turf_x[1]) - round(actual_view[1] / 2) - 1
click_turf_y = origin.y + text2num(click_turf_y[1]) - round(actual_view[2] / 2) - 1
var/turf/click_turf = locate(clamp(click_turf_x, 1, world.maxx), clamp(click_turf_y, 1, world.maxy), click_turf_z)
LAZYSET(modifiers, ICON_X, "[(click_turf_px - click_turf.pixel_x) + ((click_turf_x - click_turf.x) * world.icon_size)]")
LAZYSET(modifiers, ICON_Y, "[(click_turf_py - click_turf.pixel_y) + ((click_turf_y - click_turf.y) * world.icon_size)]")
return click_turf
//Finds the distance between two atoms, in pixels
//centered = FALSE counts from turf edge to edge
//centered = TRUE counts from turf center to turf center

View File

@@ -320,7 +320,7 @@ GLOBAL_LIST_INIT(redacted_strings, list("\[REDACTED\]", "\[CLASSIFIED\]", "\[ARC
GLOBAL_LIST_INIT(wisdoms, world.file2list("strings/wisdoms.txt"))
//LANGUAGE CHARACTER CUSTOMIZATION
GLOBAL_LIST_INIT(speech_verbs, list("default","says","gibbers", "states", "chitters", "chimpers", "declares", "bellows", "buzzes" ,"beeps", "chirps", "clicks", "hisses" ,"poofs" , "puffs", "rattles", "mewls" ,"barks", "blorbles", "squeaks", "squawks", "flutters", "warbles", "caws", "gekkers", "clucks"))
GLOBAL_LIST_INIT(speech_verbs, list("default","says","gibbers", "states", "chitters", "chimpers", "declares", "bellows", "buzzes" ,"beeps", "chirps", "clicks", "hisses" ,"poofs" , "puffs", "rattles", "mewls" ,"barks", "blorbles", "squeaks", "squawks", "flutters", "warbles", "caws", "gekkers", "clucks","mumbles","crackles"))
GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/organ/tongue, "lizard tongue" = /obj/item/organ/tongue/lizard, "skeleton tongue" = /obj/item/organ/tongue/bone, "fly tongue" = /obj/item/organ/tongue/fly, "ipc tongue" = /obj/item/organ/tongue/robot/ipc, "xeno tongue" = /obj/item/organ/tongue/alien/hybrid))
/proc/get_roundstart_languages()

View File

@@ -23,23 +23,31 @@
SEND_SIGNAL(src, COMSIG_MOUSEDROPPED_ONTO, dropping, user)
return
/client/MouseDown(object, location, control, params)
if (mouse_down_icon)
/client/MouseDown(datum/object, location, control, params)
if(!control)
return
if(QDELETED(object)) //Yep, you can click on qdeleted things before they have time to nullspace. Fun.
return
SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEDOWN, object, location, control, params)
if(mouse_down_icon)
mouse_pointer_icon = mouse_down_icon
var/delay = mob.CanMobAutoclick(object, location, params)
if(delay)
selected_target[1] = object
selected_target[2] = params
while(selected_target[1])
Click(selected_target[1], location, control, selected_target[2], TRUE)
Click(selected_target[1], location, control, selected_target[2])
sleep(delay)
active_mousedown_item = mob.canMobMousedown(object, location, params)
if(active_mousedown_item)
active_mousedown_item.onMouseDown(object, location, params, mob)
/client/MouseUp(object, location, control, params)
if (mouse_up_icon)
if(!control)
return
if(SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEUP, object, location, control, params) & COMPONENT_CLIENT_MOUSEUP_INTERCEPT)
click_intercept_time = world.time
if(mouse_up_icon)
mouse_pointer_icon = mouse_up_icon
selected_target[1] = null
if(active_mousedown_item)
@@ -74,9 +82,6 @@
/obj/item/proc/onMouseUp(object, location, params, mob)
return
/obj/item/gun/CanItemAutoclick(object, location, params)
. = automatic
/atom/proc/IsAutoclickable()
. = 1
@@ -110,6 +115,7 @@
selected_target[2] = params
if(active_mousedown_item)
active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEDRAG, src_object, over_object, src_location, over_location, src_control, over_control, params)
/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
return

View File

@@ -342,3 +342,43 @@
if(.)
return
robot.modularInterface?.interact(robot)
//borg pda
/datum/hud/robot/New(mob/owner)
. = ..()
var/atom/movable/screen/using
//PDA message
using = new /atom/movable/screen/robot/pda_msg_send
using.screen_loc = ui_borg_pda_send
using.hud = src
static_inventory += using
//PDA log
using = new /atom/movable/screen/robot/pda_msg_show
using.screen_loc = ui_borg_pda_log
using.hud = src
static_inventory += using
/atom/movable/screen/robot/pda_msg_send
name = "PDA - Send Message"
icon = 'icons/mob/screen_ai.dmi'
icon_state = "pda_send"
/atom/movable/screen/robot/pda_msg_send/Click()
if(..())
return
var/mob/living/silicon/robot/R = usr
R.cmd_send_pdamesg(usr)
/atom/movable/screen/robot/pda_msg_show
name = "PDA - Show Message Log"
icon = 'icons/mob/screen_ai.dmi'
icon_state = "pda_receive"
/atom/movable/screen/robot/pda_msg_show/Click()
if(..())
return
var/mob/living/silicon/robot/R = usr
R.cmd_show_message_log(usr)

View File

@@ -1,39 +0,0 @@
SUBSYSTEM_DEF(adjacent_air)
name = "Atmos Adjacency"
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 10
priority = FIRE_PRIORITY_ATMOS_ADJACENCY
var/list/queue = list()
/datum/controller/subsystem/adjacent_air/stat_entry(msg)
#ifdef TESTING
msg = "P:[length(queue)], S:[GLOB.atmos_adjacent_savings[1]], T:[GLOB.atmos_adjacent_savings[2]]"
#else
msg = "P:[length(queue)]"
#endif
return ..()
/datum/controller/subsystem/adjacent_air/Initialize()
while(length(queue))
fire(mc_check = FALSE)
return ..()
/datum/controller/subsystem/adjacent_air/fire(resumed = FALSE, mc_check = TRUE)
if(SSair.thread_running())
pause()
return
var/list/queue = src.queue
while (length(queue))
var/turf/currT = queue[1]
queue.Cut(1,2)
currT.ImmediateCalculateAdjacentTurfs()
if(mc_check)
if(MC_TICK_CHECK)
break
else
CHECK_TICK

View File

@@ -23,6 +23,8 @@
var/icon_icon = 'icons/mob/actions.dmi' //This is the file for the ACTION icon
var/button_icon_state = "default" //And this is the state for the action icon
var/mob/owner
///List of all mobs that are viewing our action button -> A unique movable for them to view.
var/list/viewers = list()
/datum/action/New(Target)
link_to(Target)
@@ -121,6 +123,11 @@
return FALSE
return TRUE
/datum/action/proc/UpdateButtons(status_only, force)
for(var/datum/hud/hud in viewers)
var/atom/movable/screen/movable/button = viewers[hud]
UpdateButtonIcon(button, status_only, force)
/datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE)
if(!button)
return
@@ -217,6 +224,8 @@
name = "Toggle Hood"
/datum/action/item_action/toggle_firemode
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "fireselect_no"
name = "Toggle Firemode"
/datum/action/item_action/rcl_col

View File

@@ -0,0 +1,278 @@
#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)
RegisterSignal(parent, COMSIG_GUN_AUTOFIRE_SELECTED, .proc/wake_up)
RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED, COMSIG_GUN_AUTOFIRE_DESELECTED), .proc/autofire_off)
if(_autofire_shot_delay)
autofire_shot_delay = _autofire_shot_delay
if(ismob(gun.loc))
var/mob/user = gun.loc
wake_up(src, user)
/datum/component/automatic_fire/Destroy()
UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED, COMSIG_GUN_AUTOFIRE_DESELECTED))
autofire_off()
return ..()
/datum/component/automatic_fire/process(delta_time)
if(!(autofire_stat & AUTOFIRE_STAT_FIRING))
STOP_PROCESSING(SSprojectiles, src)
return
if(!COOLDOWN_FINISHED(src, next_shot_cd))
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
var/obj/item/gun/G = parent
if(iscarbon(user))
var/mob/living/carbon/shooter = user
if(shooter.is_holding(parent) && G.fire_select == SELECT_FULLY_AUTOMATIC)
autofire_on(shooter.client)
else
autofire_off()
// 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_ALERT|AUTOFIRE_STAT_FIRING))
return
autofire_stat = AUTOFIRE_STAT_ALERT
clicker = usercli
shooter = clicker.mob
RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down)
RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT, .proc/autofire_off)
if(!QDELETED(shooter))
UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN)
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
RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN, .proc/on_client_login)
if(!QDELETED(shooter))
UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT)
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.in_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 //Already pew-pewing.
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
switch(autofire_stat)
if(AUTOFIRE_STAT_IDLE, AUTOFIRE_STAT_ALERT)
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
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(!can_shoot(shooter) || !can_trigger_gun(shooter) || semicd)
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_DOES_SLEEP
if(!can_shoot())
shoot_with_empty_chamber(shooter)
return NONE
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)
return COMPONENT_AUTOFIRE_SHOT_SUCCESS //All is well, we can continue shooting.
#undef AUTOFIRE_MOUSEUP
#undef AUTOFIRE_MOUSEDOWN

View File

@@ -86,6 +86,9 @@
return FALSE
return TRUE
/datum/dynamic_ruleset/midround/from_ghosts/ready(forced = FALSE)
return ..() && (length(dead_players) + length(list_observers) >= required_applicants)
/datum/dynamic_ruleset/midround/from_ghosts/execute()
var/list/possible_candidates = list()
possible_candidates.Add(dead_players)

View File

@@ -34,6 +34,8 @@
/obj/machinery/door/firedoor/Initialize(mapload)
. = ..()
CalculateAffectingAreas()
UpdateAdjacencyFlags()
/obj/machinery/door/firedoor/examine(mob/user)
. = ..()
@@ -53,6 +55,20 @@
var/area/A = I
LAZYADD(A.firedoors, src)
/obj/machinery/door/firedoor/proc/UpdateAdjacencyFlags()
var/turf/T = get_turf(src)
if(flags_1 & ON_BORDER_1)
for(var/t in T.atmos_adjacent_turfs)
if(get_dir(loc, t) == dir)
var/turf/open/T2 = t
T.atmos_adjacent_turfs[T2] |= ATMOS_ADJACENT_FIRELOCK
T2.atmos_adjacent_turfs[T] |= ATMOS_ADJACENT_FIRELOCK
else
for(var/t in T.atmos_adjacent_turfs)
var/turf/open/T2 = t
T.atmos_adjacent_turfs[T2] |= ATMOS_ADJACENT_FIRELOCK
T2.atmos_adjacent_turfs[T] |= ATMOS_ADJACENT_FIRELOCK
/obj/machinery/door/firedoor/closed
icon_state = "door_closed"
opacity = TRUE

View File

@@ -0,0 +1,55 @@
/obj/machinery/posialert
name = "automated positronic alert console"
desc = "A console that will ping when a positronic personality is available for download."
icon = 'icons/obj/machines/terminals.dmi'
icon_state = "posialert"
var/inuse = FALSE
var/online = TRUE
var/obj/item/radio/radio
var/radio_key = /obj/item/encryptionkey/headset_sci
var/science_channel = "Science"
/obj/machinery/posialert/Initialize(mapload, ndir, building)
. = ..()
radio = new(src)
radio.keyslot = new radio_key
radio.listening = 0
radio.recalculateChannels()
if(building)
setDir(ndir)
pixel_x = (dir & 3)? 0 : (dir == 4 ? -32 : 32)
pixel_y = (dir & 3)? (dir ==1 ? -32 : 32) : 0
/obj/machinery/posialert/Destroy()
QDEL_NULL(radio)
return ..()
/obj/item/wallframe/posialert
name = "automated positronic alert console frame"
desc = "Used to build positronic alert consoles, just secure to the wall."
icon_state = "newscaster"
custom_materials = list(/datum/material/iron=14000, /datum/material/glass=8000)
result_path = /obj/machinery/posialert
/obj/machinery/posialert/attack_hand(mob/living/user)
online = !online
to_chat(user, "<span class='warning'>You turn the posi-alert system [online ? "on" : "off"]!</span>")
return
/obj/machinery/posialert/attack_ghost(mob/user)
. = ..()
if(!online)
return
if(inuse)
return
inuse = TRUE
flick("posialertflash",src)
visible_message("There are positronic personalities available!")
radio.talk_into(src, "There are positronic personalities available!", science_channel)
playsound(loc, 'sound/machines/ping.ogg', 50)
addtimer(CALLBACK(src, .proc/liftcooldown), 300)
/obj/machinery/posialert/proc/liftcooldown()
inuse = FALSE

View File

@@ -1232,6 +1232,51 @@ GLOBAL_LIST_EMPTY(PDAs)
continue
. += P
//borg pda stuff
/mob/living/silicon/robot/proc/cmd_send_pdamesg(mob/user)
var/list/plist = list()
var/list/namecounts = list()
if(aiPDA.toff)
to_chat(user, "Turn on your receiver in order to send messages.")
return
for (var/obj/item/pda/P in get_viewable_pdas())
if (P == src)
continue
else if (P == aiPDA)
continue
plist[avoid_assoc_duplicate_keys(P.owner, namecounts)] = P
var/c = input(user, "Please select a PDA") as null|anything in sortList(plist)
if (!c)
return
var/selected = plist[c]
if(aicamera.stored.len)
var/add_photo = input(user,"Do you want to attach a photo?","Photo","No") as null|anything in list("Yes","No")
if(add_photo=="Yes")
var/datum/picture/Pic = aicamera.selectpicture(user)
aiPDA.picture = Pic
if(incapacitated())
return
aiPDA.create_message(src, selected)
/mob/living/silicon/robot/proc/cmd_show_message_log(mob/user)
if(incapacitated())
return
if(!isnull(aiPDA))
var/HTML = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>AI PDA Message Log</title></head><body>[aiPDA.tnote]</body></html>"
user << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0")
else
to_chat(user, "You do not have a PDA. You should make an issue report about this.")
#undef PDA_SCANNER_NONE
#undef PDA_SCANNER_MEDICAL
#undef PDA_SCANNER_FORENSICS

View File

@@ -5,6 +5,7 @@
item_state = "walkietalkie"
desc = "A basic handheld radio that communicates with local telecommunication networks."
dog_fashion = /datum/dog_fashion/back
var/list/extra_channels = list() //Allows indivudal channels, used for borgs
flags_1 = CONDUCT_1 | HEAR_1
slot_flags = ITEM_SLOT_BELT
@@ -69,6 +70,11 @@
if(keyslot.independent)
independent = TRUE
if(extra_channels)
for(var/ch_name in extra_channels)
if(!(ch_name in channels))
channels[ch_name] = extra_channels[ch_name]
for(var/ch_name in channels)
secure_radio_connections[ch_name] = add_radio(src, GLOB.radiochannels[ch_name])

View File

@@ -15,7 +15,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
if(turf_type)
var/turf/newT = ChangeTurf(turf_type, baseturf_type, flags)
CALCULATE_ADJACENT_TURFS(newT)
newT.ImmediateCalculateAdjacentTurfs()
/turf/proc/copyTurf(turf/T)
if(T.type != type)
@@ -285,10 +285,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
//If you modify this function, ensure it works correctly with lateloaded map templates.
/turf/proc/AfterChange(flags) //called after a turf has been replaced in ChangeTurf()
levelupdate()
if(flags & CHANGETURF_RECALC_ADJACENT)
ImmediateCalculateAdjacentTurfs()
else
CALCULATE_ADJACENT_TURFS(src)
ImmediateCalculateAdjacentTurfs()
//update firedoor adjacency
var/list/turfs_to_check = get_adjacent_open_turfs(src) | src

View File

@@ -75,7 +75,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
add_overlay(/obj/effect/fullbright)
if(requires_activation)
CALCULATE_ADJACENT_TURFS(src)
ImmediateCalculateAdjacentTurfs()
if (light_power && light_range)
update_light()
@@ -111,7 +111,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/proc/set_temperature()
/turf/proc/Initalize_Atmos(times_fired)
CALCULATE_ADJACENT_TURFS(src)
ImmediateCalculateAdjacentTurfs()
/turf/Destroy(force)
. = QDEL_HINT_IWILLGC

View File

@@ -40,21 +40,19 @@
return FALSE
/turf/proc/ImmediateCalculateAdjacentTurfs()
if(SSair.thread_running())
CALCULATE_ADJACENT_TURFS(src)
return
var/canpass = CANATMOSPASS(src, src)
var/canvpass = CANVERTICALATMOSPASS(src, src)
for(var/direction in GLOB.cardinals_multiz)
var/turf/T = get_step_multiz(src, direction)
if(!istype(T))
continue
var/opp_dir = REVERSE_DIR(direction)
if(isopenturf(T) && !(blocks_air || T.blocks_air) && ((direction & (UP|DOWN))? (canvpass && CANVERTICALATMOSPASS(T, src)) : (canpass && CANATMOSPASS(T, src))) )
LAZYINITLIST(atmos_adjacent_turfs)
LAZYINITLIST(T.atmos_adjacent_turfs)
atmos_adjacent_turfs[T] = direction
T.atmos_adjacent_turfs[src] = opp_dir
atmos_adjacent_turfs[T] = ATMOS_ADJACENT_ANY
T.atmos_adjacent_turfs[src] = ATMOS_ADJACENT_ANY
for(var/obj/machinery/door/firedoor/FD in T)
FD.UpdateAdjacencyFlags()
else
if (atmos_adjacent_turfs)
atmos_adjacent_turfs -= T
@@ -65,7 +63,8 @@
T.__update_auxtools_turf_adjacency_info(isspaceturf(T.get_z_base_turf()), -1)
UNSETEMPTY(atmos_adjacent_turfs)
src.atmos_adjacent_turfs = atmos_adjacent_turfs
set_sleeping(blocks_air)
for(var/obj/machinery/door/firedoor/FD in src)
FD.UpdateAdjacencyFlags()
__update_auxtools_turf_adjacency_info(isspaceturf(get_z_base_turf()))
/turf/proc/set_sleeping(should_sleep)

View File

@@ -280,7 +280,11 @@ we use a hook instead
parse_gas_string(model.initial_gas_mix)
return 1
/datum/gas_mixture/proc/__auxtools_parse_gas_string(gas_string)
/datum/gas_mixture/parse_gas_string(gas_string)
return __auxtools_parse_gas_string(gas_string)
/*
gas_string = SSair.preprocess_gas_string(gas_string)
var/list/gas = params2list(gas_string)
@@ -295,6 +299,7 @@ we use a hook instead
set_moles(id, text2num(gas[id]))
archive()
return 1
*/
/*
/datum/gas_mixture/react(datum/holder)
. = NO_REACTION

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

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"

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")
@@ -871,6 +871,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return
last_activity = world.time
last_click = world.time
//fullauto stuff
/*
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"])

View File

@@ -138,7 +138,7 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars_by_type, typecacheof_assoc_list(list(
if(toupdate.len)
for(var/turf/T1 in toupdate)
CALCULATE_ADJACENT_TURFS(T1)
T1.ImmediateCalculateAdjacentTurfs()
return copiedobjs

View File

@@ -132,7 +132,7 @@
. = list()
var/mob/living/carbon/human/H
var/obj/item/card/id/C
if(ishuman(user))
if(ishuman(user) || iscyborg(user))
H = user
C = H.get_idcard(TRUE)
if(C)

View File

@@ -4,6 +4,7 @@
icon = 'icons/mob/robots.dmi'
icon_state = "robot"
bubble_icon = "robot"
var/obj/item/pda/ai/aiPDA
/mob/living/silicon/robot/get_cell()
return cell
@@ -27,6 +28,12 @@
inv2 = new /atom/movable/screen/robot/module2()
inv3 = new /atom/movable/screen/robot/module3()
if(!shell)
aiPDA = new/obj/item/pda/ai(src)
aiPDA.owner = real_name
aiPDA.ownjob = "Cyborg"
aiPDA.name = real_name + " (" + aiPDA.ownjob + ")"
previous_health = health
if(ispath(cell))
@@ -968,6 +975,9 @@
notify_ai(RENAME, oldname, newname)
if(!QDELETED(builtInCamera))
builtInCamera.c_tag = real_name
if(aiPDA && !shell)
aiPDA.owner = newname
aiPDA.name = newname + " (" + aiPDA.ownjob + ")"
custom_name = newname

View File

@@ -16,6 +16,7 @@
var/list/modules = list() //holds all the usable modules
var/list/added_modules = list() //modules not inherient to the robot module, are kept when the module changes
var/list/storages = list()
var/list/added_channels = list() //Borg radio stuffs
var/cyborg_base_icon = "robot" //produces the icon for the borg and, if no special_light_key is set, the lights
var/special_light_key //if we want specific lights, use this instead of copying lights in the dmi
@@ -236,6 +237,8 @@
R.typing_indicator_state = /obj/effect/overlay/typing_indicator/machine/dogborg
else
R.typing_indicator_state = /obj/effect/overlay/typing_indicator/machine
R.radio.extra_channels = RM.added_channels
R.radio.recalculateChannels()
R.maxHealth = borghealth
R.health = min(borghealth, R.health)
qdel(src)
@@ -322,6 +325,7 @@
/obj/item/robot_module/medical
name = "Medical"
added_channels = list(RADIO_CHANNEL_MEDICAL = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -432,6 +436,7 @@
/obj/item/robot_module/engineering
name = "Engineering"
added_channels = list(RADIO_CHANNEL_ENGINEERING = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/borg/sight/meson,
@@ -546,6 +551,7 @@
/obj/item/robot_module/security
name = "Security"
added_channels = list(RADIO_CHANNEL_SECURITY = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -648,6 +654,7 @@
/obj/item/robot_module/peacekeeper
name = "Peacekeeper"
added_channels = list(RADIO_CHANNEL_SECURITY = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -734,6 +741,7 @@
/obj/item/robot_module/clown
name = "Clown"
added_channels = list(RADIO_CHANNEL_SERVICE = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -766,6 +774,7 @@
/obj/item/robot_module/butler
name = "Service"
added_channels = list(RADIO_CHANNEL_SERVICE = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -909,6 +918,7 @@
/obj/item/robot_module/miner
name = "Miner"
added_channels = list(RADIO_CHANNEL_SUPPLY = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -999,6 +1009,7 @@
/obj/item/robot_module/syndicate
name = "Syndicate Assault"
added_channels = list(RADIO_CHANNEL_SYNDICATE = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -1029,6 +1040,7 @@
/obj/item/robot_module/syndicate_medical
name = "Syndicate Medical"
added_channels = list(RADIO_CHANNEL_SYNDICATE = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/extinguisher/mini,
@@ -1061,6 +1073,7 @@
/obj/item/robot_module/saboteur
name = "Syndicate Saboteur"
added_channels = list(RADIO_CHANNEL_SYNDICATE = 1)
basic_modules = list(
/obj/item/assembly/flash/cyborg,
/obj/item/borg/sight/thermal,

View File

@@ -878,10 +878,12 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
if (!client)
return
client.mouse_pointer_icon = initial(client.mouse_pointer_icon)
if (ismecha(loc))
var/obj/vehicle/sealed/mecha/M = loc
if(M.mouse_pointer)
client.mouse_pointer_icon = M.mouse_pointer
if(istype(loc, /obj/vehicle/sealed))
var/obj/vehicle/sealed/mecha/E = loc
if(E.mouse_pointer)
client.mouse_pointer_icon = E.mouse_pointer
if(client.mouse_override_icon)
client.mouse_pointer_icon = client.mouse_override_icon
/mob/proc/is_literate()
return 0

View File

@@ -952,7 +952,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
playsound(T, 'sound/effects/supermatter.ogg', 50, 1)
T.visible_message("<span class='danger'>[T] smacks into [src] and rapidly flashes to ash.</span>",\
"<span class='italics'>You hear a loud crack as you are washed with a wave of heat.</span>")
CALCULATE_ADJACENT_TURFS(T)
T.ImmediateCalculateAdjacentTurfs()
//Do not blow up our internal radio
/obj/machinery/power/supermatter_crystal/contents_explosion(severity, target, origin)

View File

@@ -22,6 +22,7 @@
var/ranged_attack_speed = CLICK_CD_RANGE
var/melee_attack_speed = CLICK_CD_MELEE
var/gun_flags = NONE
var/fire_sound = "gunshot"
var/suppressed = null //whether or not a message is displayed when fired
var/can_suppress = FALSE
@@ -32,6 +33,7 @@
trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers
var/sawn_desc = null //description change if weapon is sawn-off
var/sawn_off = FALSE
var/firing_burst = 0 //Prevent the weapon from firing again while already firing
/// can we be put into a turret
var/can_turret = TRUE
@@ -57,6 +59,8 @@
var/burst_spread = 0 //Spread induced by the gun itself during burst fire per iteration. Only checked if spread is 0.
var/randomspread = 1 //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once.
var/inaccuracy_modifier = 1
var/semicd = 0 //cooldown handler
var/dual_wield_spread = 24 //additional spread when dual wielding
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
@@ -87,27 +91,64 @@
var/zoom_out_amt = 0
var/datum/action/item_action/toggle_scope_zoom/azoom
//Firemodes
var/datum/action/item_action/toggle_firemode/firemode_action
/// Current fire selection, can choose between burst, single, and full auto.
var/fire_select = SELECT_SEMI_AUTOMATIC
var/fire_select_index = 1
/// What modes does this weapon have? Put SELECT_FULLY_AUTOMATIC in here to enable fully automatic behaviours.
var/list/fire_select_modes = list(SELECT_SEMI_AUTOMATIC)
/// if i`1t has an icon for a selector switch indicating current firemode.
var/selector_switch_icon = FALSE
var/dualwield_spread_mult = 1 //dualwield spread multiplier
/// 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
/obj/item/gun/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_firemode))
fire_select()
else if(istype(action, /datum/action/item_action/toggle_scope_zoom))
zoom(user, user.dir)
else if(istype(action, alight))
toggle_gunlight()
else
..()
/obj/item/gun/Initialize(mapload)
. = ..()
if(no_pin_required)
pin = null
else if(pin)
if(pin)
pin = new pin(src)
if(gun_light)
alight = new (src)
alight = new(src)
if(zoomable)
azoom = new (src)
if(burst_size > 1 && !(SELECT_BURST_SHOT in fire_select_modes))
fire_select_modes.Add(SELECT_BURST_SHOT)
else if(burst_size <= 1 && (SELECT_BURST_SHOT in fire_select_modes))
fire_select_modes.Remove(SELECT_BURST_SHOT)
burst_size = 1
sortList(fire_select_modes, /proc/cmp_numeric_asc)
if(fire_select_modes.len > 1)
firemode_action = new(src)
firemode_action.button_icon_state = "fireselect_[fire_select]"
firemode_action.UpdateButtonIcon()
/obj/item/gun/ComponentInitialize()
. = ..()
if(SELECT_FULLY_AUTOMATIC in fire_select_modes)
AddComponent(/datum/component/automatic_fire, fire_delay)
/obj/item/gun/Destroy()
if(pin)
QDEL_NULL(pin)
@@ -117,6 +158,10 @@
QDEL_NULL(bayonet)
if(chambered)
QDEL_NULL(chambered)
if(azoom)
QDEL_NULL(azoom)
if(firemode_action)
QDEL_NULL(firemode_action)
return ..()
/obj/item/gun/examine(mob/user)
@@ -142,6 +187,41 @@
else if(can_bayonet)
. += "It has a <b>bayonet</b> lug on it."
/obj/item/gun/proc/fire_select()
var/mob/living/carbon/human/user = usr
var/max_mode = fire_select_modes.len
if(max_mode <= 1)
to_chat(user, "<span class='warning'>[src] is not capable of switching firemodes!</span>")
return
fire_select_index = 1 + fire_select_index % max_mode //Magic math to cycle through this shit!
fire_select = fire_select_modes[fire_select_index]
switch(fire_select)
if(SELECT_SEMI_AUTOMATIC)
burst_size = 1
fire_delay = 0
SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_DESELECTED, user)
to_chat(user, "<span class='notice'>You switch [src] to semi-automatic.</span>")
if(SELECT_BURST_SHOT)
burst_size = initial(burst_size)
fire_delay = initial(fire_delay)
SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_DESELECTED, user)
to_chat(user, "<span class='notice'>You switch [src] to [burst_size]-round burst.</span>")
if(SELECT_FULLY_AUTOMATIC)
burst_size = 1
SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_SELECTED, user)
to_chat(user, "<span class='notice'>You switch [src] to automatic.</span>")
playsound(user, 'sound/weapons/empty.ogg', 100, TRUE)
update_appearance()
firemode_action.button_icon_state = "fireselect_[fire_select]"
firemode_action.UpdateButtonIcon()
return TRUE
/obj/item/gun/equipped(mob/living/user, slot)
. = ..()
if(zoomed && user.get_active_held_item() != src)
@@ -567,12 +647,6 @@
gun_light = new_light
/obj/item/gun/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_scope_zoom))
zoom(user, user.dir)
else if(istype(action, alight))
toggle_gunlight()
/obj/item/gun/proc/toggle_gunlight()
if(!gun_light)
return

View File

@@ -5,8 +5,8 @@
var/automatic_burst_overlay = TRUE
can_suppress = TRUE
burst_size = 3
burst_shot_delay = 2
actions_types = list(/datum/action/item_action/toggle_firemode)
fire_delay = 2
fire_select_modes = list(SELECT_SEMI_AUTOMATIC, SELECT_BURST_SHOT, SELECT_FULLY_AUTOMATIC)
/obj/item/gun/ballistic/automatic/proto
name = "\improper Nanotrasen Saber SMG"
@@ -15,6 +15,7 @@
fire_sound = "sound/weapons/gunshot_smg_alt.ogg"
mag_type = /obj/item/ammo_box/magazine/smgm9mm
pin = null
burst_size = 1
/obj/item/gun/ballistic/automatic/proto/unrestricted
pin = /obj/item/firing_pin
@@ -55,34 +56,6 @@
else
to_chat(user, "<span class='warning'>You cannot seem to get \the [src] out of your hands!</span>")
/obj/item/gun/ballistic/automatic/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_firemode))
burst_select()
else
return ..()
/obj/item/gun/ballistic/automatic/proc/burst_select()
var/mob/living/carbon/human/user = usr
select = !select
if(!select)
disable_burst()
to_chat(user, "<span class='notice'>You switch to semi-automatic.</span>")
else
enable_burst()
to_chat(user, "<span class='notice'>You switch to [burst_size]-rnd burst.</span>")
playsound(user, 'sound/weapons/empty.ogg', 100, 1)
update_icon()
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
/obj/item/gun/ballistic/automatic/proc/enable_burst()
burst_size = initial(burst_size)
/obj/item/gun/ballistic/automatic/proc/disable_burst()
burst_size = 1
/obj/item/gun/ballistic/automatic/can_shoot()
return get_ammo()
@@ -136,18 +109,10 @@
knife_y_offset = 12
automatic_burst_overlay = FALSE
/obj/item/gun/ballistic/automatic/wt550/enable_burst()
. = ..()
spread = 15
/obj/item/gun/ballistic/automatic/wt550/afterattack()
. = ..()
empty_alarm()
/obj/item/gun/ballistic/automatic/wt550/disable_burst()
. = ..()
spread = 0
/obj/item/gun/ballistic/automatic/wt550/update_icon_state()
icon_state = "wt550[magazine ? "-[CEILING(((get_ammo(FALSE) / magazine.max_ammo) * 20) /4, 1)*4]" : "-0"]" //Sprites only support up to 20.
@@ -211,6 +176,7 @@
/obj/item/gun/ballistic/automatic/m90/update_icon_state()
icon_state = "[initial(icon_state)][magazine ? "" : "-e"]"
/*
/obj/item/gun/ballistic/automatic/m90/burst_select()
var/mob/living/carbon/human/user = usr
switch(select)
@@ -228,6 +194,7 @@
playsound(user, 'sound/weapons/empty.ogg', 100, 1)
update_icon()
return
*/
/obj/item/gun/ballistic/automatic/tommygun
name = "\improper Thompson SMG"
@@ -304,14 +271,17 @@
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/Initialize()
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
AddComponent(/datum/component/automatic_fire, 0.2 SECONDS)
/obj/item/gun/ballistic/automatic/l6_saw/unrestricted
pin = /obj/item/firing_pin

View File

@@ -39,7 +39,7 @@
mag_type = /obj/item/ammo_box/magazine/m75
burst_size = 1
fire_delay = 0
actions_types = list()
fire_select_modes = list(SELECT_SEMI_AUTOMATIC)
casing_ejector = FALSE
/obj/item/gun/ballistic/automatic/gyropistol/update_icon_state()

View File

@@ -7,7 +7,7 @@
can_suppress = TRUE
burst_size = 1
fire_delay = 0
actions_types = list()
fire_select_modes = list(SELECT_SEMI_AUTOMATIC)
automatic_burst_overlay = FALSE
/obj/item/gun/ballistic/automatic/pistol/no_mag
@@ -104,7 +104,7 @@
mag_type = /obj/item/ammo_box/magazine/pistolm9mm
burst_size = 3
fire_delay = 2
actions_types = list(/datum/action/item_action/toggle_firemode)
fire_select_modes = list(SELECT_SEMI_AUTOMATIC, SELECT_BURST_SHOT, SELECT_FULLY_AUTOMATIC)
/obj/item/gun/ballistic/automatic/pistol/stickman
name = "flat gun"
@@ -137,7 +137,7 @@
burst_size = 1
can_suppress = FALSE
w_class = WEIGHT_CLASS_NORMAL
actions_types = list()
fire_select_modes = list(SELECT_SEMI_AUTOMATIC)
fire_sound = 'sound/weapons/noscope.ogg'
spread = 20 //damn thing has no rifling.
automatic_burst_overlay = FALSE

View File

@@ -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
)

View File

@@ -75,6 +75,14 @@
build_path = /obj/item/wallframe/newscaster
category = list("initial", "Construction")
/datum/design/posialert
name = "Posialert Frame"
id = "posialert_frame"
build_type = AUTOLATHE
materials = list(/datum/material/iron = 14000, /datum/material/glass = 8000)
build_path = /obj/item/wallframe/posialert
category = list("initial", "Construction")
/datum/design/status_display_frame
name = "Status Display Frame"
id = "status_display_frame"