mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-17 21:02:15 +00:00
Refactored the projectile code, mostly in line with TG's now. Refactored various procs that are used or depends on it. Projectiles can now ricochet if enabled to. Damage falloffs with distance. Homing projectiles can now have accuracy falloff with distance. Projectiles have a maximum range. Muzzle flash is configurable per projectile. Impact effect of the projectile is configurable per projectile. Accuracy decreases with distance. Projectiles work with signals and emits them, for easy hooking up from other parts of the code. Meatshielding is now less effective . Impact sound is now configurable per projectile. High risk.
405 lines
14 KiB
Plaintext
405 lines
14 KiB
Plaintext
/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
|
|
if(air_group || (height==0))
|
|
return TRUE
|
|
|
|
if(mover?.movement_type & PHASING)
|
|
return TRUE
|
|
|
|
if(istype(mover,/obj/projectile))
|
|
return (check_cover(mover,target))
|
|
if (flipped == 1)
|
|
if (get_dir(loc, target) == dir)
|
|
return !density
|
|
else
|
|
return 1
|
|
if(istype(mover, /obj/structure/closet/crate))
|
|
return TRUE
|
|
if(istype(mover) && mover.pass_flags & PASSTABLE)
|
|
return 1
|
|
if(locate(/obj/structure/table) in get_turf(mover))
|
|
return 1
|
|
return 0
|
|
|
|
//checks if projectile 'P' from turf 'from' can hit whatever is behind the table. Returns 1 if it can, 0 if bullet stops.
|
|
/obj/structure/table/proc/check_cover(obj/projectile/P, turf/from)
|
|
var/turf/cover
|
|
if(flipped==1)
|
|
cover = get_turf(src)
|
|
else if(flipped==0)
|
|
cover = get_step(loc, get_dir(from, loc))
|
|
if(!cover)
|
|
return 1
|
|
if (get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close
|
|
return 1
|
|
if (get_turf(P.original) == cover)
|
|
var/chance = 20
|
|
if (ismob(P.original))
|
|
var/mob/M = P.original
|
|
if (M.lying)
|
|
chance += 20 //Lying down lets you catch less bullets
|
|
if(flipped==1)
|
|
if(get_dir(loc, from) == dir) //Flipped tables catch mroe bullets
|
|
chance += 20
|
|
else
|
|
return 1 //But only from one side
|
|
if(prob(chance))
|
|
health -= P.damage/2
|
|
if (health > 0)
|
|
visible_message(SPAN_WARNING("[P] hits \the [src]!"))
|
|
return 0
|
|
else
|
|
visible_message(SPAN_WARNING("[src] breaks down!"))
|
|
break_to_parts()
|
|
return 1
|
|
return 1
|
|
|
|
/obj/structure/table/CheckExit(atom/movable/O as mob|obj, target as turf)
|
|
if(istype(O) && O.pass_flags & PASSTABLE)
|
|
return 1
|
|
if (flipped==1)
|
|
if (get_dir(loc, target) == dir)
|
|
return !density
|
|
else
|
|
return 1
|
|
return 1
|
|
|
|
/obj/structure/table/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
SIGNAL_HANDLER
|
|
|
|
if(ishuman(arrived))
|
|
var/mob/living/carbon/human/H = arrived
|
|
if(H.a_intent != I_HELP || H.m_intent == M_RUN)
|
|
INVOKE_ASYNC(src, PROC_REF(throw_things), H)
|
|
else if(H.is_diona() || H.species.get_bodytype() == BODYTYPE_IPC_INDUSTRIAL)
|
|
INVOKE_ASYNC(src, PROC_REF(throw_things), H)
|
|
else if((isliving(arrived) && !issmall(arrived)) || isslime(arrived))
|
|
INVOKE_ASYNC(src, PROC_REF(throw_things), arrived)
|
|
|
|
/obj/structure/table/proc/throw_things(var/mob/living/user)
|
|
var/list/targets = list(get_step(src,dir),get_step(src,turn(dir, 45)),get_step(src,turn(dir, -45)))
|
|
var/turf/T = get_turf(src)
|
|
var/anything_moved = FALSE
|
|
for (var/obj/item/I in T)
|
|
if (I.simulated && !I.anchored)
|
|
INVOKE_ASYNC(I, TYPE_PROC_REF(/atom/movable, throw_at), pick(targets), 1, 1)
|
|
anything_moved = TRUE
|
|
CHECK_TICK
|
|
|
|
if (user && anything_moved)
|
|
user.visible_message(
|
|
SPAN_NOTICE("[user] kicks everything off [src]."),
|
|
SPAN_NOTICE("You kick everything off [src].")
|
|
)
|
|
|
|
|
|
/obj/structure/table/structure_shaken()
|
|
..()
|
|
throw_things()
|
|
|
|
|
|
/obj/structure/table/do_climb(var/mob/living/user)
|
|
if (!can_climb(user))
|
|
return
|
|
|
|
user.visible_message(
|
|
SPAN_WARNING("[user] starts climbing onto \the [src]!"),
|
|
SPAN_WARNING("You start climbing onto \the [src]!")
|
|
)
|
|
LAZYADD(climbers, user)
|
|
|
|
if(!do_after(user, 2.5 SECONDS))
|
|
LAZYREMOVE(climbers, user)
|
|
return
|
|
|
|
if (!can_climb(user, post_climb_check=1))
|
|
LAZYREMOVE(climbers, user)
|
|
return
|
|
|
|
user.forceMove(get_turf(src))
|
|
|
|
if (get_turf(user) == get_turf(src))
|
|
user.visible_message(
|
|
SPAN_WARNING("[user] climbs onto \the [src]!"),
|
|
SPAN_WARNING("You climb onto \the [src]!")
|
|
)
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
if(H.a_intent != I_HELP || H.m_intent == M_RUN)
|
|
throw_things(H)
|
|
else if(H.is_diona() || H.species.get_bodytype() == BODYTYPE_IPC_INDUSTRIAL)
|
|
throw_things(H)
|
|
else if(!issmall(user) || isslime(user))
|
|
throw_things(user)
|
|
LAZYREMOVE(climbers, user)
|
|
|
|
/obj/structure/table/MouseDrop_T(atom/dropping, mob/user, params)
|
|
var/obj/item/stack/material/what = dropping
|
|
if(can_reinforce && isliving(usr) && (!usr.stat) && istype(what) && usr.get_active_hand() == what && Adjacent(usr))
|
|
reinforce_table(what, usr)
|
|
return
|
|
|
|
if(ismob(dropping.loc)) //If placing an item
|
|
if(!isitem(dropping) || user.get_active_hand() != dropping)
|
|
return ..()
|
|
if(isrobot(user))
|
|
return
|
|
user.drop_item()
|
|
if(dropping.loc != src.loc)
|
|
step(dropping, get_dir(dropping, src))
|
|
|
|
else if(isturf(dropping.loc) && isitem(dropping)) //If pushing an item on the tabletop
|
|
var/obj/item/I = dropping
|
|
if(I.anchored)
|
|
return
|
|
|
|
if(!use_check_and_message(user))
|
|
if(I.w_class <= user.can_pull_size)
|
|
I.forceMove(loc)
|
|
auto_align(I, params, TRUE)
|
|
else
|
|
to_chat(user, SPAN_WARNING("\The [I] is too big for you to move!"))
|
|
return
|
|
|
|
return ..()
|
|
|
|
/obj/structure/table/attack_hand(mob/user)
|
|
. = ..()
|
|
if(ishuman(user))
|
|
if(!use_check_and_message(user))
|
|
var/mob/living/carbon/human/H = user
|
|
if((H.zone_sel.selecting in list(BP_R_HAND, BP_L_HAND)))
|
|
if(H.last_special + 1 SECOND < world.time)
|
|
H.last_special = world.time
|
|
switch(H.a_intent)
|
|
if(I_GRAB)
|
|
H.visible_message(SPAN_NOTICE("[H] knocks on the table!"))
|
|
playsound(src, 'sound/effects/table_knock.ogg', 50)
|
|
if(I_HURT)
|
|
H.do_attack_animation(src)
|
|
H.visible_message(SPAN_WARNING("[H] slams [H.get_pronoun("his")] hand on the table!"))
|
|
playsound(src, 'sound/effects/table_slam.ogg', 50)
|
|
if(material.hardness > 15) //15 wood, 60 steel
|
|
var/obj/item/organ/external/hand/hand = H.zone_sel.selecting
|
|
if(!BP_IS_ROBOTIC(hand))
|
|
H.apply_damage(5, DAMAGE_BRUTE, H.zone_sel.selecting, armor_pen = 10)
|
|
to_chat(H, SPAN_WARNING("Ow! That hurt..."))
|
|
else
|
|
for(var/obj/item/O in get_turf(src))
|
|
if(!O.anchored && O.w_class < WEIGHT_CLASS_HUGE)
|
|
animate(O, pixel_y = 3, time = 2, loop = 1, easing = BOUNCE_EASING)
|
|
addtimer(CALLBACK(O, TYPE_PROC_REF(/obj/item, reset_table_position), 2))
|
|
|
|
/obj/item/proc/reset_table_position()
|
|
animate(src, pixel_y = 0, time = 2, loop = 1, easing = BOUNCE_EASING)
|
|
|
|
/obj/structure/table/attackby(obj/item/attacking_item, mob/user, params)
|
|
if (!attacking_item)
|
|
return
|
|
|
|
// Handle harm intent grabbing/tabling.
|
|
if(istype(attacking_item, /obj/item/grab) && get_dist(src,user)<2)
|
|
var/obj/item/grab/G = attacking_item
|
|
if(istype(G.affecting, /mob/living))
|
|
var/mob/living/M = G.affecting
|
|
var/obj/occupied = turf_is_crowded()
|
|
if(occupied)
|
|
to_chat(user, SPAN_DANGER("There's \a [occupied] in the way."))
|
|
return
|
|
if(!user.Adjacent(M))
|
|
return
|
|
if(G.state > GRAB_AGGRESSIVE && world.time >= G.last_action + UPGRADE_COOLDOWN)
|
|
if(user.a_intent == I_HURT)
|
|
var/blocked = M.get_blocked_ratio(BP_HEAD, DAMAGE_BRUTE, damage = 8)
|
|
if (prob(30 * (1 - blocked)))
|
|
M.Weaken(5)
|
|
M.apply_damage(8, DAMAGE_BRUTE, BP_HEAD)
|
|
visible_message(SPAN_DANGER("[G.assailant] slams [G.affecting]'s face against \the [src]!"))
|
|
if(material)
|
|
playsound(loc, material.tableslam_noise, 50, 1)
|
|
else
|
|
playsound(loc, 'sound/weapons/tablehit1.ogg', 50, 1)
|
|
// Shards. Extra damage, plus potentially the fact YOU LITERALLY HAVE A PIECE OF GLASS/METAL/WHATEVER IN YOUR FACE
|
|
var/sanity_counter = 0
|
|
for(var/obj/item/material/shard/S in get_turf(src))
|
|
if(prob(50))
|
|
M.visible_message(SPAN_DANGER("\The [S] slices [M]'s face messily!"),
|
|
SPAN_DANGER("\The [S] slices your face messily!"))
|
|
M.apply_damage(10, DAMAGE_BRUTE, BP_HEAD)
|
|
sanity_counter++
|
|
if(sanity_counter >= 3)
|
|
break
|
|
G.last_action = world.time
|
|
else
|
|
G.affecting.forceMove(src.loc)
|
|
G.affecting.Weaken(rand(2,4))
|
|
visible_message(SPAN_DANGER("[G.assailant] puts [G.affecting] on \the [src]."))
|
|
qdel(attacking_item)
|
|
return
|
|
else
|
|
to_chat(user, SPAN_WARNING("You need a better grip to do that!"))
|
|
return
|
|
|
|
if(!attacking_item.dropsafety())
|
|
return
|
|
|
|
if(reinforced && attacking_item.isscrewdriver())
|
|
remove_reinforced(attacking_item, user)
|
|
if(!reinforced)
|
|
update_desc()
|
|
queue_icon_update()
|
|
update_material()
|
|
return 1
|
|
|
|
if(carpeted && attacking_item.iscrowbar())
|
|
user.visible_message(SPAN_NOTICE("\The [user] removes the carpet from \the [src]."),
|
|
SPAN_NOTICE("You remove the carpet from \the [src]."))
|
|
new /obj/item/stack/tile/carpet(loc)
|
|
carpeted = 0
|
|
queue_icon_update()
|
|
return 1
|
|
|
|
if(!carpeted && material && istype(attacking_item, /obj/item/stack/tile/carpet))
|
|
var/obj/item/stack/tile/carpet/C = attacking_item
|
|
if(C.use(1))
|
|
user.visible_message(SPAN_NOTICE("\The [user] adds \the [C] to \the [src]."),
|
|
SPAN_NOTICE("You add \the [C] to \the [src]."))
|
|
carpeted = 1
|
|
queue_icon_update()
|
|
return 1
|
|
else
|
|
to_chat(user, SPAN_WARNING("You don't have enough carpet!"))
|
|
|
|
if(!reinforced && !carpeted && material && (attacking_item.iswrench() || istype(attacking_item, /obj/item/gun/energy/plasmacutter)))
|
|
remove_material(attacking_item, user)
|
|
if(!material)
|
|
update_connections(1)
|
|
queue_icon_update()
|
|
for(var/obj/structure/table/T in oview(src, 1))
|
|
T.queue_icon_update()
|
|
update_desc()
|
|
update_material()
|
|
return 1
|
|
|
|
if(!carpeted && !reinforced && !material && (attacking_item.iswrench() || istype(attacking_item, /obj/item/gun/energy/plasmacutter)))
|
|
dismantle(attacking_item, user)
|
|
return 1
|
|
|
|
if(health < maxhealth && attacking_item.iswelder())
|
|
var/obj/item/weldingtool/F = attacking_item
|
|
if(F.welding)
|
|
to_chat(user, SPAN_NOTICE("You begin reparing damage to \the [src]."))
|
|
if(!attacking_item.use_tool(src, user, 20, volume = 50) || !F.use(1, user))
|
|
return
|
|
user.visible_message(SPAN_NOTICE("\The [user] repairs some damage to \the [src]."),
|
|
SPAN_NOTICE("You repair some damage to \the [src]."))
|
|
health = max(health+(maxhealth/5), maxhealth) // 20% repair per application
|
|
return 1
|
|
|
|
if(!material && can_plate && istype(attacking_item, /obj/item/stack/material))
|
|
material = common_material_add(attacking_item, user, "plat")
|
|
if(material)
|
|
update_connections(1)
|
|
queue_icon_update()
|
|
update_desc()
|
|
update_material()
|
|
return 1
|
|
|
|
if(!material && can_plate && istype(attacking_item, /obj/item/reagent_containers/cooking_container/board/bowl))
|
|
new /obj/structure/chemkit(loc)
|
|
qdel(attacking_item)
|
|
qdel(src)
|
|
return 1
|
|
|
|
if(istype(attacking_item, /obj/item/melee/energy/blade))
|
|
var/obj/item/melee/energy/blade/blade = attacking_item
|
|
blade.spark_system.queue()
|
|
playsound(src.loc, 'sound/weapons/blade.ogg', 50, 1)
|
|
playsound(src.loc, /singleton/sound_category/spark_sound, 50, 1)
|
|
user.visible_message(SPAN_DANGER("\The [src] was sliced apart by [user]!"))
|
|
break_to_parts()
|
|
return
|
|
|
|
if(can_plate && !material)
|
|
to_chat(user, SPAN_WARNING("There's nothing to put \the [attacking_item] on! Try adding plating to \the [src] first."))
|
|
return
|
|
|
|
|
|
if(attacking_item.ishammer() && user.a_intent != I_HURT)
|
|
var/obj/item/I = usr.get_inactive_hand()
|
|
if(I && istype(I, /obj/item/stack))
|
|
var/obj/item/stack/D = I
|
|
if(D.get_material_name() != material.name)
|
|
return ..()
|
|
if(health < maxhealth)
|
|
if(D.get_amount() < 1)
|
|
to_chat(user, SPAN_WARNING("You need one sheet of [material.display_name] to repair \the [src]."))
|
|
return
|
|
user.visible_message("<b>[user]</b> begins to repair \the [src].", SPAN_NOTICE("You begin to repair \the [src]."))
|
|
if(do_after(user, 2 SECONDS) && health < maxhealth)
|
|
if(D.use(1))
|
|
health = maxhealth
|
|
visible_message("<b>[user]</b> repairs \the [src].", SPAN_NOTICE("You repair \the [src]."))
|
|
|
|
// Placing stuff on tables
|
|
if(user.unEquip(attacking_item, 0, loc)) //Loc is intentional here so we don't forceMove() items into oblivion
|
|
user.make_item_drop_sound(attacking_item)
|
|
auto_align(attacking_item, params)
|
|
return
|
|
|
|
/// Amount of cells per row/column in grid
|
|
#define CELLS 8
|
|
/// Size of a cell in pixels
|
|
#define CELLSIZE (world.icon_size/CELLS)
|
|
/*
|
|
Automatic alignment of items to an invisible grid, defined by CELLS and CELLSIZE.
|
|
Since the grid will be shifted to own a cell that is perfectly centered on the turf, we end up with two 'cell halves'
|
|
on edges of each row/column.
|
|
Each item defines a center_of_mass, which is the pixel of a sprite where its projected center of mass toward a turf
|
|
surface can be assumed. For a piece of paper, this will be in its center. For a bottle, it will be (near) the bottom
|
|
of the sprite.
|
|
auto_align() will then place the sprite so the defined center_of_mass is at the bottom left corner of the grid cell
|
|
closest to where the cursor has clicked on.
|
|
Note: This proc can be overwritten to allow for different types of auto-alignment.
|
|
*/
|
|
/obj/item/var/list/center_of_mass = list("x" = 16,"y" = 16)
|
|
|
|
/obj/structure/table/proc/auto_align(obj/item/W, click_parameters, var/animate = FALSE)
|
|
if(initial(W.layer) <= BELOW_TABLE_LAYER) // stuff below tables should not accidentally be adjusted to be above them
|
|
W.layer = initial(W.layer)
|
|
return TRUE
|
|
|
|
if(!W.center_of_mass)
|
|
W.randpixel_xy()
|
|
W.layer = initial(W.layer) + ((32 - W.pixel_y) / 1000)
|
|
return TRUE
|
|
|
|
if(!click_parameters)
|
|
return FALSE
|
|
|
|
var/list/mouse_control = mouse_safe_xy(click_parameters)
|
|
var/mouse_x = mouse_control["icon-x"]
|
|
var/mouse_y = mouse_control["icon-y"]
|
|
|
|
if(isnum(mouse_x) && isnum(mouse_y))
|
|
var/cell_x = max(0, min(CELLS-1, round(mouse_x/CELLSIZE)))
|
|
var/cell_y = max(0, min(CELLS-1, round(mouse_y/CELLSIZE)))
|
|
|
|
var/target_x = (CELLSIZE * (cell_x + 0.5)) - W.center_of_mass["x"]
|
|
var/target_y = (CELLSIZE * (cell_y + 0.5)) - W.center_of_mass["y"]
|
|
if(animate)
|
|
var/dist_x = abs(W.pixel_x - target_x)
|
|
var/dist_y = abs(W.pixel_y - target_y)
|
|
var/dist = sqrt((dist_x*dist_x)+(dist_y*dist_y))
|
|
animate(W, pixel_x=target_x, pixel_y=target_y,time=dist*0.5)
|
|
else
|
|
W.pixel_x = target_x
|
|
W.pixel_y = target_y
|
|
W.layer = initial(W.layer) + ((32 - W.pixel_y) / 1000)
|
|
return TRUE
|
|
|
|
#undef CELLS
|
|
#undef CELLSIZE
|
|
|
|
/obj/structure/table/do_simple_ranged_interaction(var/mob/user)
|
|
return
|