mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-30 11:01:35 +00:00
## About The Pull Request This is the first PR in a series attempting to modernize our damage and armor, both from a code and a gameplay perspective. This part implements unique attack animations, adds alternate attack modes for items and fixes some minor oversights. Items now have unique attack animation based on their sharpness - sharp items are now swung in an arc, while pointy items are thrust forward. This change is ***purely visual***, this is not swing combat. (However, this does assign icon rotation data to many items, which should help swing combat later down the line). Certain items like knives and swords now have secondary attacks - right clicks will perform stabbing attacks instead of slashing for a chance to leave piercing wounds, albeit with slightly lower damage - trying to stick a katana through someone won't get you very far! https://github.com/user-attachments/assets/1f92bbcd-9aa1-482f-bc26-5e84fe2a07e1 Turns out that spears acted as oversized knives this entire time, being SHARP_EDGED instead of SHARP_POINTY - in order for their animations to make sense, they're now once again pointy (according to comment, originally they were made sharp because piercing wounds weren't very threatening, which is no longer the case) Another major change is that structure damage is now influenced by armor penetration - I am not sure if this is intentional or not, but attacking item's AP never applied to non-mob damage. Additionally, also fixes an issue where attack verbs for you and everyone else may differ.
302 lines
9.7 KiB
Plaintext
302 lines
9.7 KiB
Plaintext
#define PROXIMITY_NONE ""
|
|
#define PROXIMITY_ON_SCREEN "_red"
|
|
#define PROXIMITY_NEAR "_yellow"
|
|
|
|
/**
|
|
* Multitool -- A multitool is used for hacking electronic devices.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
|
|
/obj/item/multitool
|
|
name = "multitool"
|
|
desc = "Used for pulsing wires to test which to cut. Not recommended by doctors. You can activate it in-hand to locate the nearest APC."
|
|
icon = 'icons/obj/devices/tool.dmi'
|
|
icon_state = "multitool"
|
|
inhand_icon_state = "multitool"
|
|
icon_angle = -90
|
|
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
|
|
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
|
|
force = 5
|
|
w_class = WEIGHT_CLASS_SMALL
|
|
tool_behaviour = TOOL_MULTITOOL
|
|
throwforce = 0
|
|
throw_range = 7
|
|
throw_speed = 3
|
|
drop_sound = 'sound/items/handling/tools/multitool_drop.ogg'
|
|
pickup_sound = 'sound/items/handling/tools/multitool_pickup.ogg'
|
|
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 0.2)
|
|
custom_premium_price = PAYCHECK_COMMAND * 3
|
|
toolspeed = 1
|
|
usesound = 'sound/items/weapons/empty.ogg'
|
|
var/datum/buffer // simple machine buffer for device linkage
|
|
var/mode = 0
|
|
var/apc_scanner = TRUE
|
|
COOLDOWN_DECLARE(next_apc_scan)
|
|
|
|
/obj/item/multitool/Destroy()
|
|
if(buffer)
|
|
remove_buffer(buffer)
|
|
return ..()
|
|
|
|
/obj/item/multitool/examine(mob/user)
|
|
. = ..()
|
|
. += span_notice("Its buffer [buffer ? "contains [buffer]." : "is empty."]")
|
|
|
|
/obj/item/multitool/attack_self(mob/user, list/modifiers)
|
|
. = ..()
|
|
|
|
if(. || !apc_scanner)
|
|
return
|
|
|
|
scan_apc(user)
|
|
|
|
/obj/item/multitool/attack_self_secondary(mob/user, modifiers)
|
|
. = ..()
|
|
|
|
if(. || !apc_scanner)
|
|
return
|
|
|
|
scan_apc(user)
|
|
|
|
/obj/item/multitool/proc/scan_apc(mob/user)
|
|
if(!COOLDOWN_FINISHED(src, next_apc_scan))
|
|
return
|
|
|
|
COOLDOWN_START(src, next_apc_scan, 1 SECONDS)
|
|
|
|
var/area/local_area = get_area(src)
|
|
var/power_controller = local_area.apc
|
|
if(power_controller)
|
|
user.balloon_alert(user, "[get_dist(src, power_controller)]m [dir2text(get_dir(src, power_controller))]")
|
|
else
|
|
user.balloon_alert(user, "couldn't find apc!")
|
|
|
|
/obj/item/multitool/suicide_act(mob/living/carbon/user)
|
|
user.visible_message(span_suicide("[user] puts the [src] to [user.p_their()] chest. It looks like [user.p_theyre()] trying to pulse [user.p_their()] heart off!"))
|
|
return OXYLOSS//there's a reason it wasn't recommended by doctors
|
|
|
|
/**
|
|
* Sets the multitool internal object buffer
|
|
*
|
|
* Arguments:
|
|
* * buffer - the new object to assign to the multitool's buffer
|
|
*/
|
|
/obj/item/multitool/proc/set_buffer(datum/buffer)
|
|
if(src.buffer)
|
|
UnregisterSignal(src.buffer, COMSIG_QDELETING)
|
|
remove_buffer(src.buffer)
|
|
src.buffer = buffer
|
|
if(!QDELETED(buffer))
|
|
RegisterSignal(buffer, COMSIG_QDELETING, PROC_REF(remove_buffer))
|
|
|
|
/**
|
|
* Called when the buffer's stored object is deleted
|
|
*
|
|
* This proc does not clear the buffer of the multitool, it is here to
|
|
* handle the deletion of the object the buffer references
|
|
*/
|
|
/obj/item/multitool/proc/remove_buffer(datum/source)
|
|
SIGNAL_HANDLER
|
|
SEND_SIGNAL(src, COMSIG_MULTITOOL_REMOVE_BUFFER, source)
|
|
buffer = null
|
|
|
|
// Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby.
|
|
|
|
/obj/item/multitool/ai_detect
|
|
apc_scanner = FALSE
|
|
/// How close the AI is to us
|
|
var/detect_state = PROXIMITY_NONE
|
|
/// Range at which the closest AI makes the multitool glow red
|
|
var/rangealert = 8 //Glows red when inside
|
|
/// Range at which the closest AI makes the multitool glow yellow
|
|
var/rangewarning = 20 //Glows yellow when inside
|
|
/// Is our HUD on
|
|
var/hud_on = FALSE
|
|
|
|
// static scan stuff
|
|
/// hud object that the fake static images use
|
|
var/obj/effect/overlay/ai_detect_hud/camera_unseen/hud_obj
|
|
/// fake static image
|
|
var/list/image/static_images = list()
|
|
/// the client that we shoved those images to
|
|
var/datum/weakref/static_viewer
|
|
/// timerid for the timer that makes em disappear
|
|
var/static_disappear_timer
|
|
/// cooldown for actually doing a static scan
|
|
COOLDOWN_DECLARE(static_scan_cd)
|
|
|
|
/obj/item/multitool/ai_detect/examine(mob/user)
|
|
. = ..()
|
|
if(!hud_on)
|
|
return
|
|
. += span_notice("You can right-click to scan for nearby unseen spots. They will be shown for exactly 8 seconds due to battery limitations.")
|
|
switch(detect_state)
|
|
if(PROXIMITY_NONE)
|
|
. += span_green("No AI should be currently looking at you. Keep on your clandestine activities.")
|
|
if(PROXIMITY_NEAR)
|
|
. += span_warning("An AI is getting uncomfortably close. Maybe time to drop what youre doing.")
|
|
if(PROXIMITY_ON_SCREEN)
|
|
. += span_danger("An AI is (probably) looking at you. You should probably hide this.")
|
|
|
|
/obj/item/multitool/ai_detect/Destroy()
|
|
if(hud_on && ismob(loc))
|
|
remove_hud(loc)
|
|
cleanup_static()
|
|
return ..()
|
|
|
|
/obj/item/multitool/ai_detect/attack_self(mob/user, modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
toggle_hud(user)
|
|
|
|
/obj/item/multitool/ai_detect/attack_self_secondary(mob/user, modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
scan_unseen(user)
|
|
|
|
/obj/item/multitool/ai_detect/equipped(mob/living/carbon/human/user, slot)
|
|
. = ..()
|
|
if(hud_on)
|
|
show_hud(user)
|
|
|
|
/obj/item/multitool/ai_detect/dropped(mob/living/carbon/human/user)
|
|
. = ..()
|
|
if(hud_on)
|
|
remove_hud(user)
|
|
cleanup_static()
|
|
|
|
/obj/item/multitool/ai_detect/update_icon_state()
|
|
. = ..()
|
|
icon_state = "[initial(icon_state)][detect_state]"
|
|
|
|
/obj/item/multitool/ai_detect/process()
|
|
var/old_detect_state = detect_state
|
|
multitool_detect()
|
|
if(detect_state != old_detect_state)
|
|
update_appearance()
|
|
|
|
/obj/item/multitool/ai_detect/proc/toggle_hud(mob/user)
|
|
hud_on = !hud_on
|
|
if(user)
|
|
to_chat(user, span_notice("You toggle the ai detection feature on [src] [hud_on ? "on" : "off"]."))
|
|
if(hud_on)
|
|
START_PROCESSING(SSfastprocess, src)
|
|
show_hud(user)
|
|
else
|
|
STOP_PROCESSING(SSfastprocess, src)
|
|
detect_state = PROXIMITY_NONE
|
|
update_appearance(UPDATE_ICON)
|
|
remove_hud(user)
|
|
|
|
/obj/item/multitool/ai_detect/proc/show_hud(mob/user)
|
|
var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_AI_DETECT]
|
|
hud.show_to(user)
|
|
|
|
/obj/item/multitool/ai_detect/proc/remove_hud(mob/user)
|
|
var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_AI_DETECT]
|
|
hud.hide_from(user)
|
|
|
|
/obj/item/multitool/ai_detect/proc/multitool_detect()
|
|
var/turf/our_turf = get_turf(src)
|
|
detect_state = PROXIMITY_NONE
|
|
|
|
for(var/mob/eye/camera/ai/AI_eye as anything in GLOB.camera_eyes)
|
|
if(!AI_eye.ai_detector_visible)
|
|
continue
|
|
|
|
var/turf/ai_turf = get_turf(AI_eye)
|
|
var/distance = get_dist(our_turf, ai_turf)
|
|
|
|
if(distance == -1) //get_dist() returns -1 for distances greater than 127 (and for errors, so assume -1 is just max range)
|
|
if(ai_turf == our_turf)
|
|
detect_state = PROXIMITY_ON_SCREEN
|
|
break
|
|
continue
|
|
|
|
if(distance < rangealert) //ai should be able to see us
|
|
detect_state = PROXIMITY_ON_SCREEN
|
|
break
|
|
if(distance < rangewarning) //ai can't see us but is close
|
|
detect_state = PROXIMITY_NEAR
|
|
|
|
/obj/item/multitool/ai_detect/proc/scan_unseen(mob/user)
|
|
if(isnull(user?.client)) // the monkey incident of 2564
|
|
return
|
|
if(!COOLDOWN_FINISHED(src, static_scan_cd))
|
|
balloon_alert(user, "recharging!")
|
|
return
|
|
cleanup_static()
|
|
var/turf/our_turf = get_turf(src)
|
|
var/list/datum/camerachunk/chunks = surrounding_chunks(our_turf)
|
|
|
|
if(!hud_obj)
|
|
hud_obj = new()
|
|
SET_PLANE_W_SCALAR(hud_obj, PLANE_TO_TRUE(hud_obj.plane), GET_TURF_PLANE_OFFSET(our_turf))
|
|
|
|
var/list/new_images = list()
|
|
for(var/datum/camerachunk/chunk as anything in chunks)
|
|
for(var/turf/seen_turf as anything in chunk.obscuredTurfs)
|
|
var/image/img = image(loc = seen_turf, layer = ABOVE_ALL_MOB_LAYER)
|
|
img.vis_contents += hud_obj
|
|
SET_PLANE(img, GAME_PLANE, seen_turf)
|
|
new_images += img
|
|
user.client.images |= new_images
|
|
static_viewer = WEAKREF(user.client)
|
|
balloon_alert(user, "nearby unseen spots shown")
|
|
static_disappear_timer = addtimer(CALLBACK(src, PROC_REF(cleanup_static)), 8 SECONDS, TIMER_STOPPABLE)
|
|
COOLDOWN_START(src, static_scan_cd, 4 SECONDS)
|
|
|
|
// copied from camera chunks but we are doing a really big edge case here though
|
|
/obj/item/multitool/ai_detect/proc/surrounding_chunks(turf/epicenter)
|
|
. = list()
|
|
var/static_range = /mob/eye/camera/ai::static_visibility_range
|
|
var/x1 = max(1, epicenter.x - static_range)
|
|
var/y1 = max(1, epicenter.y - static_range)
|
|
var/x2 = min(world.maxx, epicenter.x + static_range)
|
|
var/y2 = min(world.maxy, epicenter.y + static_range)
|
|
|
|
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
|
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
|
var/datum/camerachunk/chunk = GLOB.cameranet.getCameraChunk(x, y, epicenter.z)
|
|
// removing cameras in build mode didnt affect it and i guess it needs an AI eye to update so we have to do this manually
|
|
// unless we only want to see static in a jank manner only if an eye updates it
|
|
chunk?.update() // UPDATE THE FUCK NOW
|
|
. |= chunk
|
|
|
|
/obj/item/multitool/ai_detect/proc/cleanup_static()
|
|
if(isnull(hud_obj)) //we never did anything
|
|
return
|
|
var/client/viewer = static_viewer?.resolve()
|
|
viewer?.images -= static_images
|
|
static_images.Cut()
|
|
QDEL_NULL(hud_obj)
|
|
viewer = null
|
|
deltimer(static_disappear_timer)
|
|
static_disappear_timer = null
|
|
|
|
/obj/item/multitool/abductor
|
|
name = "alien multitool"
|
|
desc = "An omni-technological interface."
|
|
icon = 'icons/obj/antags/abductor.dmi'
|
|
icon_state = "multitool"
|
|
inside_belt_icon_state = "multitool_alien"
|
|
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 1.25, /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/titanium = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT)
|
|
toolspeed = 0.1
|
|
|
|
/obj/item/multitool/cyborg
|
|
name = "electronic multitool"
|
|
desc = "Optimised version of a regular multitool. Streamlines processes handled by its internal microchip."
|
|
icon = 'icons/obj/items_cyborg.dmi'
|
|
icon_state = "toolkit_engiborg_multitool"
|
|
icon_angle = 0
|
|
toolspeed = 0.5
|
|
|
|
#undef PROXIMITY_NEAR
|
|
#undef PROXIMITY_NONE
|
|
#undef PROXIMITY_ON_SCREEN
|