Merge pull request #12518 from kevinz000/pp_fix
refactors projectile ricochet and fixes a runtime with projectile motion
This commit is contained in:
@@ -22,8 +22,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
||||
//FLAGS BITMASK
|
||||
///This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not.
|
||||
#define HEAR_1 (1<<3)
|
||||
///Projectiels will check ricochet on things impacted that have this.
|
||||
#define CHECK_RICOCHET_1 (1<<4)
|
||||
///Projectiles will use default chance-based ricochet handling on things with this.
|
||||
#define DEFAULT_RICOCHET_1 (1<<4)
|
||||
///Conducts electricity (metal etc.).
|
||||
#define CONDUCT_1 (1<<5)
|
||||
///For machines and structures that should not break into parts, eg, holodeck stuff.
|
||||
|
||||
@@ -273,10 +273,3 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
|
||||
* a "inefficiently" prefix will be added to the message.
|
||||
*/
|
||||
#define FEEBLE_ATTACK_MSG_THRESHOLD 0.5
|
||||
|
||||
|
||||
//bullet_act() return values
|
||||
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
|
||||
#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
|
||||
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
|
||||
#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/// This atom should be ricocheted off of from its inherent properties using standard % chance handling.
|
||||
#define PROJECTILE_RICOCHET_YES 1
|
||||
/// This atom should not be ricocheted off of from its inherent properties.
|
||||
#define PROJECTILE_RICOCHET_NO 2
|
||||
/// This atom should prevent any kind of projectile ricochet from its inherent properties.
|
||||
#define PROJECTILE_RICOCHET_PREVENT 3
|
||||
/// This atom should force a projectile ricochet from its inherent properties.
|
||||
#define PROJECTILE_RICOCHET_FORCE 4
|
||||
|
||||
//bullet_act() return values
|
||||
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
|
||||
#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
|
||||
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
|
||||
#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
|
||||
@@ -125,7 +125,7 @@ GLOBAL_LIST_INIT(bitfields, list(
|
||||
"UNUSED_RESERVATION_TURF_1" = UNUSED_RESERVATION_TURF_1,
|
||||
"CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1,
|
||||
"HEAR_1" = HEAR_1,
|
||||
"CHECK_RICOCHET_1" = CHECK_RICOCHET_1,
|
||||
"DEFAULT_RICOCHET_1" = DEFAULT_RICOCHET_1,
|
||||
"CONDUCT_1" = CONDUCT_1,
|
||||
"NO_LAVA_GEN_1" = NO_LAVA_GEN_1,
|
||||
"NODECONSTRUCT_1" = NODECONSTRUCT_1,
|
||||
|
||||
+12
-2
@@ -141,8 +141,18 @@
|
||||
|
||||
return ..()
|
||||
|
||||
/atom/proc/handle_ricochet(obj/item/projectile/P)
|
||||
return
|
||||
/**
|
||||
* Checks if a projectile should ricochet off of us. Projectiles get final say.
|
||||
* [__DEFINES/projectiles.dm] for return values.
|
||||
*/
|
||||
/atom/proc/check_projectile_ricochet(obj/item/projectile/P)
|
||||
return (flags_1 & DEFAULT_RICOCHET_1)? PROJECTILE_RICOCHET_YES : PROJECTILE_RICOCHET_NO
|
||||
|
||||
/**
|
||||
* Handle a projectile ricochet. Return TRUE if we did something to the projectile like reflecting it/whatnot.
|
||||
*/
|
||||
/atom/proc/handle_projectile_ricochet(obj/item/projectile/P)
|
||||
return FALSE
|
||||
|
||||
/atom/proc/CanPass(atom/movable/mover, turf/target)
|
||||
return !density
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
icon = 'icons/turf/walls/shuttle_wall.dmi'
|
||||
icon_state = "map-shuttle"
|
||||
explosion_block = 3
|
||||
flags_1 = CAN_BE_DIRTY_1 | CHECK_RICOCHET_1
|
||||
flags_1 = CAN_BE_DIRTY_1 | DEFAULT_RICOCHET_1
|
||||
sheet_type = /obj/item/stack/sheet/mineral/titanium
|
||||
smooth = SMOOTH_MORE|SMOOTH_DIAGONAL
|
||||
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium)
|
||||
@@ -302,4 +302,4 @@
|
||||
|
||||
/turf/closed/wall/mineral/plastitanium/copyTurf(turf/T)
|
||||
. = ..()
|
||||
T.transform = transform
|
||||
T.transform = transform
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
/turf/closed/wall/attack_tk()
|
||||
return
|
||||
|
||||
/turf/closed/wall/handle_ricochet(obj/item/projectile/P) //A huge pile of shitcode!
|
||||
/turf/closed/wall/handle_projectile_ricochet(obj/item/projectile/P) //A huge pile of shitcode!
|
||||
var/turf/p_turf = get_turf(P)
|
||||
var/face_direction = get_dir(src, p_turf)
|
||||
var/face_angle = dir2angle(face_direction)
|
||||
|
||||
@@ -45,13 +45,15 @@
|
||||
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
|
||||
damaged_desc = "A wall of twitching tendrils with a reflective glow."
|
||||
icon_state = "blob_glow"
|
||||
flags_1 = CHECK_RICOCHET_1
|
||||
point_return = 8
|
||||
max_integrity = 100
|
||||
brute_resist = 1
|
||||
explosion_block = 2
|
||||
|
||||
/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P)
|
||||
/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P)
|
||||
return PROJECTILE_RICOCHET_FORCE
|
||||
|
||||
/obj/structure/blob/shield/reflective/handle_projectile_ricochet(obj/item/projectile/P)
|
||||
var/turf/p_turf = get_turf(P)
|
||||
var/face_direction = get_dir(src, p_turf)
|
||||
var/face_angle = dir2angle(face_direction)
|
||||
@@ -61,4 +63,4 @@
|
||||
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
|
||||
P.setAngle(new_angle_s)
|
||||
visible_message("<span class='warning'>[P] reflects off [src]!</span>")
|
||||
return TRUE
|
||||
return TRUE
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
var/pixels_range_leftover = 0
|
||||
/// "leftover" tick pixels and stuff yeah, so we don't round off things and introducing tracing inaccuracy.
|
||||
var/pixels_tick_leftover = 0
|
||||
/// Used to detect jumps in the middle of a pixel_move. Yes, this is ugly as sin code-wise but it works.
|
||||
var/pixel_move_interrupted = FALSE
|
||||
|
||||
/// Pixels moved per second.
|
||||
var/pixels_per_second = TILES_TO_PIXELS(12.5)
|
||||
@@ -257,27 +259,54 @@
|
||||
else
|
||||
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
|
||||
|
||||
/obj/item/projectile/proc/on_ricochet(atom/A)
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
|
||||
beam_segments[beam_index] = pcache
|
||||
beam_index = pcache
|
||||
beam_segments[beam_index] = null
|
||||
|
||||
/obj/item/projectile/Bump(atom/A)
|
||||
var/turf/T = get_turf(A)
|
||||
if(trajectory && check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
|
||||
var/datum/point/pcache = trajectory.copy_to()
|
||||
ricochets++
|
||||
if(A.handle_ricochet(src))
|
||||
on_ricochet(A)
|
||||
ignore_source_check = TRUE
|
||||
decayedRange = max(0, decayedRange - reflect_range_decrease)
|
||||
range = decayedRange
|
||||
if(hitscan)
|
||||
store_hitscan_collision(pcache)
|
||||
/**
|
||||
* Determines if we should ricochet off of something.
|
||||
* By default, asks the thing if we should ricochet off it, but because we're called first, we get final say.
|
||||
* Returns TRUE or FALSE.
|
||||
*/
|
||||
/obj/item/projectile/proc/check_ricochet(atom/A)
|
||||
if(ricochets > ricochets_max) //safety thing, we don't care about what the other thing says about this.
|
||||
return FALSE
|
||||
var/them = A.check_projectile_ricochet(src)
|
||||
switch(them)
|
||||
if(PROJECTILE_RICOCHET_PREVENT)
|
||||
return FALSE
|
||||
if(PROJECTILE_RICOCHET_FORCE)
|
||||
return TRUE
|
||||
if(PROJECTILE_RICOCHET_NO)
|
||||
return FALSE
|
||||
if(PROJECTILE_RICOCHET_YES)
|
||||
return prob(ricochet_chance)
|
||||
else
|
||||
CRASH("Invalid return value for projectile ricochet check from [A].")
|
||||
|
||||
/**
|
||||
* Handles ricocheting off of something.
|
||||
* By default, also asks the thing to handle it, but because we're called first, we get final say.
|
||||
*/
|
||||
/obj/item/projectile/proc/handle_ricochet(atom/A)
|
||||
ricochets++
|
||||
ignore_source_check = TRUE
|
||||
decayedRange = max(0, decayedRange - reflect_range_decrease)
|
||||
pixel_move_interrupted = TRUE
|
||||
range = decayedRange
|
||||
return A.handle_projectile_ricochet(src)
|
||||
|
||||
/obj/item/projectile/Bump(atom/A)
|
||||
if(!trajectory)
|
||||
return
|
||||
var/turf/T = get_turf(A)
|
||||
if(check_ricochet(A))
|
||||
var/datum/point/pcache = trajectory.copy_to()
|
||||
if(hitscan)
|
||||
store_hitscan_collision(pcache)
|
||||
handle_ricochet(A)
|
||||
return TRUE
|
||||
|
||||
var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
|
||||
if(def_zone && check_zone(def_zone) != BODY_ZONE_CHEST)
|
||||
@@ -352,16 +381,6 @@
|
||||
return T
|
||||
//Returns null if nothing at all was found.
|
||||
|
||||
/obj/item/projectile/proc/check_ricochet()
|
||||
if(prob(ricochet_chance))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/projectile/proc/check_ricochet_flag(atom/A)
|
||||
if(A.flags_1 & CHECK_RICOCHET_1)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// one move is a tile.
|
||||
/obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
|
||||
if(!trajectory && isnull(forced_angle) && isnull(Angle))
|
||||
@@ -439,6 +458,7 @@
|
||||
|
||||
/obj/item/projectile/proc/setAngle(new_angle, hitscan_store_segment = TRUE) //wrapper for overrides.
|
||||
Angle = new_angle
|
||||
pixel_move_interrupted = TRUE
|
||||
if(!nondirectional_sprite)
|
||||
var/matrix/M = new
|
||||
M.Turn(Angle)
|
||||
@@ -465,6 +485,7 @@
|
||||
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
|
||||
if(hitscan)
|
||||
record_hitscan_start(RETURN_PRECISE_POINT(src))
|
||||
pixel_move_interrupted = TRUE
|
||||
if(zc)
|
||||
after_z_change(old, target)
|
||||
|
||||
@@ -513,7 +534,7 @@
|
||||
* The proc to make the projectile go, using a simulated pixel movement line trace.
|
||||
* Note: deciseconds_equivalent is currently only used for homing, times is the number of times to move pixel_increment_amount.
|
||||
* Trajectory multiplier directly modifies the factor of pixel_increment_amount to go per time.
|
||||
* It's complicated, so probably just don'ot mess with this unless you know what you're doing.
|
||||
* It's complicated, so probably just don't mess with this unless you know what you're doing.
|
||||
*/
|
||||
/obj/item/projectile/proc/pixel_move(times, hitscanning = FALSE, deciseconds_equivalent = world.tick_lag, trajectory_multiplier = 1, allow_animation = TRUE)
|
||||
if(!loc || !trajectory)
|
||||
@@ -523,6 +544,7 @@
|
||||
M.Turn(Angle)
|
||||
transform = M
|
||||
var/forcemoved = FALSE
|
||||
pixel_move_interrupted = FALSE // reset that
|
||||
var/turf/oldloc = loc
|
||||
var/old_px = pixel_x
|
||||
var/old_py = pixel_y
|
||||
@@ -558,7 +580,9 @@
|
||||
if(!--safety)
|
||||
CRASH("[type] took too long (allowed: [CEILING(pixel_increment_amount/world.icon_size,1)*2] moves) to get to its location.")
|
||||
step_towards(src, T)
|
||||
if(QDELETED(src))
|
||||
if(QDELETED(src) || pixel_move_interrupted) // this doesn't take into account with pixel_move_interrupted the portion of the move cut off by any forcemoves, but we're opting to ignore that for now
|
||||
// the reason is the entire point of moving to pixel speed rather than tile speed is smoothness, which will be crucial when pixel movement is done in the future
|
||||
// reverting back to tile is more or less the only way of fixing this issue.
|
||||
return
|
||||
pixels_range_leftover += pixel_increment_amount
|
||||
if(pixels_range_leftover > world.icon_size)
|
||||
|
||||
Reference in New Issue
Block a user