diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index 8c8e03d865..dae3df6f0b 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -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. diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index e7f79596b5..bf053635af 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -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. diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm new file mode 100644 index 0000000000..1bd67fbe02 --- /dev/null +++ b/code/__DEFINES/projectiles.dm @@ -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. diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 9419000e02..c3004a4501 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -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, diff --git a/code/game/atoms.dm b/code/game/atoms.dm index a83eaed9d3..5875334ed6 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -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 diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm index b04f4f0aa0..5c74eb4ecc 100644 --- a/code/game/turfs/simulated/wall/mineral_walls.dm +++ b/code/game/turfs/simulated/wall/mineral_walls.dm @@ -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 \ No newline at end of file + T.transform = transform diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 5243341ac1..da5d3c643d 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -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) diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm index bc4e517ced..384df935f5 100644 --- a/code/modules/antagonists/blob/blob/blobs/shield.dm +++ b/code/modules/antagonists/blob/blob/blobs/shield.dm @@ -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("[P] reflects off [src]!") - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 3705f7902c..d2643eeeb7 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -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) diff --git a/tgstation.dme b/tgstation.dme index 39f79a0b71..41bd154ebc 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -83,6 +83,7 @@ #include "code\__DEFINES\preferences.dm" #include "code\__DEFINES\procpath.dm" #include "code\__DEFINES\profile.dm" +#include "code\__DEFINES\projectiles.dm" #include "code\__DEFINES\qdel.dm" #include "code\__DEFINES\radiation.dm" #include "code\__DEFINES\radio.dm"