The Paint & Linen Update [Splinter 2]: Splashplosions (#35528)

* Soft Reset

* gotta set up the FLAXOIL define

* test_reach

* deprecated comment has left the building
This commit is contained in:
DeityLink
2023-12-11 00:01:20 +01:00
committed by GitHub
parent 00e584b333
commit f90d45fb46
16 changed files with 368 additions and 108 deletions

View File

@@ -236,6 +236,8 @@
#define isfloor(A) (istype(A, /turf/simulated/floor) || istype(A, /turf/unsimulated/floor) || istype(A, /turf/simulated/floor/shuttle) || istype(A, /turf/simulated/floor/shuttle/brig)) #define isfloor(A) (istype(A, /turf/simulated/floor) || istype(A, /turf/unsimulated/floor) || istype(A, /turf/simulated/floor/shuttle) || istype(A, /turf/simulated/floor/shuttle/brig))
#define iswall(A) (istype(A, /turf/simulated/wall) || istype(A, /turf/unsimulated/wall))
#define isshuttleturf(A) (istype(A, /turf/simulated/wall/shuttle) || istype(A, /turf/simulated/floor/shuttle)) #define isshuttleturf(A) (istype(A, /turf/simulated/wall/shuttle) || istype(A, /turf/simulated/floor/shuttle))
#define issilent(A) (A.silent || (ishuman(A) && (A.mind && A.mind.miming || A:species:flags & SPECIES_NO_MOUTH))) //Remember that silent is not the same as miming. Miming you can emote, silent you can't gesticulate at all #define issilent(A) (A.silent || (ishuman(A) && (A.mind && A.mind.miming || A:species:flags & SPECIES_NO_MOUTH))) //Remember that silent is not the same as miming. Miming you can emote, silent you can't gesticulate at all

View File

@@ -524,6 +524,8 @@
#define MIMOSA "mimosa" #define MIMOSA "mimosa"
#define LEMONDROP "lemondrop" #define LEMONDROP "lemondrop"
#define FLAXOIL "flax_oil"
// How many units of reagent are consumed per tick, by default. // How many units of reagent are consumed per tick, by default.
#define REAGENTS_METABOLISM 0.2 #define REAGENTS_METABOLISM 0.2

View File

@@ -201,3 +201,14 @@ var/static/list/counterclockwise_perpendicular_dirs = list(EAST,WEST,EAST|WEST,S
return "↙" return "↙"
if(NORTHWEST) if(NORTHWEST)
return "↖" return "↖"
/proc/splitdiagonals(var/dir)
switch(dir)
if(NORTHEAST)
return list(NORTH,EAST)
if(SOUTHEAST)
return list(SOUTH,EAST)
if(SOUTHWEST)
return list(SOUTH,WEST)
if(NORTHWEST)
return list(NORTH,WEST)

View File

@@ -22,6 +22,8 @@
var/vector/origin_floored //the floored origin vector var/vector/origin_floored //the floored origin vector
var/vector/direction //direction of the ray var/vector/direction //direction of the ray
var/original_damage //original damage of the ray when applicable var/original_damage //original damage of the ray when applicable
var/turf/final_turf
var/turf/previous_turf
/ray/proc/toString() /ray/proc/toString()
return "\[Ray\](\n- origin = " + origin.toString() + "\n- origin_floored = "+ origin_floored.toString() + "\n- direction = " + direction.toString() + "\n- z-level = " + num2text(z) + "\n)" return "\[Ray\](\n- origin = " + origin.toString() + "\n- origin_floored = "+ origin_floored.toString() + "\n- direction = " + direction.toString() + "\n- z-level = " + num2text(z) + "\n)"
@@ -112,6 +114,10 @@
//our result //our result
var/list/rayCastHit/hits = list() var/list/rayCastHit/hits = list()
var/turf/T = vector2turf(origin.floored(), z)
previous_turf = T
final_turf = T
while(distance < max_distance) while(distance < max_distance)
//moving one step further //moving one step further
pointer += a_step pointer += a_step
@@ -134,7 +140,10 @@
continue continue
//getting the turf at our current (floored) vector //getting the turf at our current (floored) vector
var/turf/T = vector2turf(new_position, z) T = vector2turf(new_position, z)
if (!T.density)
previous_turf = final_turf
final_turf = T
//trying hit at turf //trying hit at turf
var/rayCastHitInfo/info = new /rayCastHitInfo(src, makeweakref(T), new_position, new_position_unfloored, distance) var/rayCastHitInfo/info = new /rayCastHitInfo(src, makeweakref(T), new_position, new_position_unfloored, distance)

View File

@@ -0,0 +1,128 @@
//test_reach(), by Deity Link
//Basically sends a cheap projectile that moves from a turf A to a turf B in a "straight" line. Returns true if it reaches turf B without hitting anything on the way there (other than turf B itself)
//Useful for instance to check if an item thrown/projectile fired from a A to B would reach its target
/proc/test_reach(var/turf/origin,var/turf/destination,var/_pass_flags=0)
if (!origin || !destination)
return FALSE
if (origin.z != destination.z)
return FALSE
if (origin == destination)
return TRUE
var/obj/test_reach/test = new(origin,destination,_pass_flags)
var/result = test.main()
qdel(test)
return result
//////////////////////////////////////////////////////////////////////////////////////////////
/obj/test_reach
invisibility = 101
pass_flags = 0
var/dist_x
var/dist_y
var/dx = 0
var/dy = 0
var/error = 0
var/target_angle = 0
var/bumped = 0
var/max_range = 100
var/turf/starting
var/turf/target
var/result = FALSE
var/finished = FALSE
/obj/test_reach/New(var/turf/loc,var/turf/destination,var/_pass_flags)
..()
starting = loc
target = destination
pass_flags = _pass_flags
/obj/test_reach/proc/main()
//let's do our first movement by carefully going around any adjacent wall if another cardinal direction toward our destination is possible
//this fixes a quirk with bresenham paths where it may decide to run into adjacent walls on its first step even when it doesn't make "sense"
var/orientation = get_dir(loc,target)
if (orientation in diagonal)
var/turf/gotta_move = null
for (var/direction in splitdiagonals(orientation))
var/turf/T = get_step(loc,direction)
if (!T.density && T.Adjacent(src))
if (!gotta_move)
gotta_move = T
else
gotta_move = null
if (gotta_move)
loc = gotta_move
starting = loc
if (starting == target)
return TRUE
//init
dist_x = abs(target.x - starting.x)
dist_y = abs(target.y - starting.y)
if (target.x > starting.x)
dx = EAST
else
dx = WEST
if (target.y > starting.y)
dy = NORTH
else
dy = SOUTH
if(dist_x > dist_y)
error = dist_x/2 - dist_y
else
error = dist_y/2 - dist_x
//main loop
while(loc && !finished)
loop()
return result
/obj/test_reach/proc/loop()
if(loc && !finished)
if(dist_x > dist_y)
bresenham_step(dist_x,dist_y,dx,dy)
else
bresenham_step(dist_y,dist_x,dy,dx)
bumped = 0
/obj/test_reach/proc/bresenham_step(var/distA, var/distB, var/dA, var/dB)//based on the code I wrote forever ago in projectiles.dm
if(max_range < 1)
finished = TRUE
return
max_range--
if(error < 0)
var/atom/step = get_step(src, dB)
if(!step)
finished = TRUE
return
Move(step)
error += distA
if (loc == target)
result = TRUE
finished = TRUE
else
var/atom/step = get_step(src, dA)
if(!step)
finished = TRUE
return
Move(step)
error -= distB
dir = dA
if(error < 0)
dir = dA + dB
if (loc == target)
result = TRUE
finished = TRUE
/obj/test_reach/to_bump(var/atom/A)
if (A == target)
result = TRUE
finished = TRUE
else
finished = TRUE

View File

@@ -451,6 +451,12 @@
return TRUE return TRUE
if(locate(/obj/effect/unwall_field) in loc) //Annoying workaround for this -kanef if(locate(/obj/effect/unwall_field) in loc) //Annoying workaround for this -kanef
return TRUE return TRUE
if(istype(mover, /obj/item/projectile/beam))
var/obj/item/projectile/beam/B = mover
var/returns = bounds_dist(src, B.previous_turf) >= 0
if (returns && B.previous_turf)
B.final_turf = B.previous_turf
return returns
return bounds_dist(src, mover) >= 0 return bounds_dist(src, mover) >= 0
// harderforce is for things like lighting overlays which should only be moved in EXTREMELY specific sitations. // harderforce is for things like lighting overlays which should only be moved in EXTREMELY specific sitations.

View File

@@ -110,12 +110,16 @@ steam.start() -- spawns the effect
icon_state = "extinguish" icon_state = "extinguish"
density = 0 density = 0
/datum/effect/system/steam_spread/set_up(n = 3, c = 0, turf/loc) /datum/effect/system/steam_spread
var/color
/datum/effect/system/steam_spread/set_up(n = 3, c = 0, turf/loc, var/_color = null)
if(n > 10) if(n > 10)
n = 10 n = 10
number = n number = n
cardinals = c cardinals = c
location = loc location = loc
color = _color
/datum/effect/system/steam_spread/start() /datum/effect/system/steam_spread/start()
var/i = 0 var/i = 0
@@ -124,6 +128,9 @@ steam.start() -- spawns the effect
if(holder) if(holder)
src.location = get_turf(holder) src.location = get_turf(holder)
var/obj/effect/steam/steam = new /obj/effect/steam(src.location) var/obj/effect/steam/steam = new /obj/effect/steam(src.location)
if (color)
steam.icon_state = "extinguish_gray"
steam.color = color
var/direction var/direction
if(src.cardinals) if(src.cardinals)
direction = pick(cardinal) direction = pick(cardinal)

View File

@@ -297,16 +297,8 @@
reservoir.reagents.trans_to(src, reservoir.reagents.total_volume) reservoir.reagents.trans_to(src, reservoir.reagents.total_volume)
if(src.reagents.total_volume) //The possible reactions didnt use up all reagents. if(reagents.total_volume) //The possible reactions didnt use up all reagents.
var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() reagents.splashplosion(affected_area)
steam.set_up(10, 0, get_turf(src))
steam.attach(src)
steam.start()
for(var/atom/A in view(affected_area, get_turf(src)))
if( A == src )
continue
src.reagents.reaction(A, 1, 10)
invisibility = INVISIBILITY_MAXIMUM //Why am i doing this? invisibility = INVISIBILITY_MAXIMUM //Why am i doing this?
spawn(50) //To make sure all reagents can work spawn(50) //To make sure all reagents can work

View File

@@ -223,7 +223,10 @@ var/list/one_way_windows
return TRUE return TRUE
if(!density) if(!density)
return TRUE return TRUE
if(istype(mover)) if(istype(mover, /obj/item/projectile/beam))
var/obj/item/projectile/beam/B = mover
return bounds_dist(border_dummy, B.previous_turf) >= 0
else if(istype(mover))
return bounds_dist(border_dummy, mover) >= 0 return bounds_dist(border_dummy, mover) >= 0
else if(get_dir(loc, target) == dir) else if(get_dir(loc, target) == dir)
return FALSE return FALSE

View File

@@ -12,10 +12,16 @@
w_class = W_CLASS_MEDIUM w_class = W_CLASS_MEDIUM
fire_delay = 1 fire_delay = 1
fire_sound = 'sound/weapons/shotgun.ogg' fire_sound = 'sound/weapons/shotgun.ogg'
empty_sound = 'sound/weapons/magazine_load_click.ogg'
conventional_firearm = 0
var/hard = 1 //When toggled on, the gun's shots will deal damage. When off, they deal no damage, but deliver five times the reagents. var/hard = 1 //When toggled on, the gun's shots will deal damage. When off, they deal no damage, but deliver five times the reagents.
var/max_reagents = 50 var/max_reagents = 50
var/obj/item/projectile/projectile_type = /obj/item/projectile/bullet/liquid_blob var/obj/item/projectile/projectile_type = /obj/item/projectile/bullet/liquid_blob
var/mixed_color = null
var/mixed_alpha = 255
var/radius = 0
/obj/item/weapon/gun/siren/isHandgun() /obj/item/weapon/gun/siren/isHandgun()
return FALSE return FALSE
@@ -24,6 +30,15 @@
create_reagents(max_reagents) create_reagents(max_reagents)
reagents.add_reagent(WATER, max_reagents) reagents.add_reagent(WATER, max_reagents)
/obj/item/weapon/gun/siren/click_empty(mob/user = null)
if (user)
if(empty_sound)
to_chat(user, "<span class='warning'>No liquid left inside \the [src]</span>")
playsound(user, empty_sound, 100, 1)
else
if(empty_sound)
playsound(src, empty_sound, 100, 1)
/obj/item/weapon/gun/siren/verb/flush_reagents() /obj/item/weapon/gun/siren/verb/flush_reagents()
set name = "Flush contents" set name = "Flush contents"
set category = "Object" set category = "Object"
@@ -38,9 +53,13 @@
if(in_chamber) if(in_chamber)
QDEL_NULL(in_chamber) QDEL_NULL(in_chamber)
/obj/item/weapon/gun/siren/on_reagent_change()
mixed_color = mix_color_from_reagents(reagents.reagent_list)
mixed_alpha = mix_alpha_from_reagents(reagents.reagent_list)
/obj/item/weapon/gun/siren/examine(mob/user) /obj/item/weapon/gun/siren/examine(mob/user)
..() ..()
to_chat(user, "<span class='info'>It has [round(reagents.total_volume/10)] round\s remaining.</span>") to_chat(user, "<span class='info'>It has [round(reagents.total_volume/5)] round\s remaining.</span>")
if(hard >= 0) if(hard >= 0)
if(hard) if(hard)
to_chat(user, "<span class='info'>It is set to \"hard liquid\".</span>") to_chat(user, "<span class='info'>It is set to \"hard liquid\".</span>")
@@ -54,11 +73,13 @@
desc = initial(desc) desc = initial(desc)
fire_sound = initial(fire_sound) fire_sound = initial(fire_sound)
recoil = 1 recoil = 1
radius = 0
else else
to_chat(user, "<span class='info'>You set \the [src] to fire soft liquid.</span>") to_chat(user, "<span class='info'>You set \the [src] to fire soft liquid.</span>")
desc = "The most efficient ranged mass reagent delivery system there is." desc = "The most efficient ranged mass reagent delivery system there is."
fire_sound = 'sound/items/egg_squash.ogg' fire_sound = 'sound/items/egg_squash.ogg'
recoil = 0 recoil = 0
radius = 1
/obj/item/weapon/gun/siren/afterattack(atom/A, mob/living/user, flag, params, struggle = 0) /obj/item/weapon/gun/siren/afterattack(atom/A, mob/living/user, flag, params, struggle = 0)
if(flag) if(flag)
@@ -67,7 +88,7 @@
to_chat(user, "<span class='warning'>A label sticks the trigger to the trigger guard!</span>")//Such a new feature, the player might not know what's wrong if it doesn't tell them. to_chat(user, "<span class='warning'>A label sticks the trigger to the trigger guard!</span>")//Such a new feature, the player might not know what's wrong if it doesn't tell them.
return return
if(reagents.total_volume < 10) if(reagents.total_volume < 5)
return click_empty(user) return click_empty(user)
if(in_chamber) if(in_chamber)
if(in_chamber.reagents && in_chamber.reagents.total_volume) if(in_chamber.reagents && in_chamber.reagents.total_volume)
@@ -78,18 +99,18 @@
in_chamber.reagents.remove_reagent(R.id, reagents.get_reagent_amount(R.id)*4) in_chamber.reagents.remove_reagent(R.id, reagents.get_reagent_amount(R.id)*4)
in_chamber.reagents.trans_to(src, in_chamber.reagents.total_volume) in_chamber.reagents.trans_to(src, in_chamber.reagents.total_volume)
QDEL_NULL(in_chamber) QDEL_NULL(in_chamber)
in_chamber = new projectile_type(src, hard) in_chamber = new projectile_type(src, hard, mixed_color, mixed_alpha, radius)
reagents.trans_to(in_chamber, 10) reagents.trans_to(in_chamber, 5)
if(!hard) //When set to no-damage mode, each shot has five times the reagents. if(!hard) //When set to no-damage mode, each shot has ten times the reagents.
for(var/datum/reagent/R in in_chamber.reagents.reagent_list) for(var/datum/reagent/R in in_chamber.reagents.reagent_list)
in_chamber.reagents.add_reagent(R.id, reagents.get_reagent_amount(R.id)*4) R.volume *= 10
Fire(A,user,params, "struggle" = struggle) Fire(A,user,params, "struggle" = struggle)
/obj/item/weapon/gun/siren/process_chambered() /obj/item/weapon/gun/siren/process_chambered()
return in_chamber return in_chamber
/obj/item/weapon/gun/siren/can_discharge() /obj/item/weapon/gun/siren/can_discharge()
if(reagents.total_volume < 10) if(reagents.total_volume < 5)
return 1 return 1
/obj/item/weapon/gun/siren/caduceus /obj/item/weapon/gun/siren/caduceus
@@ -118,7 +139,7 @@
/obj/item/weapon/gun/siren/supersoaker /obj/item/weapon/gun/siren/supersoaker
name = "super soaker" name = "super soaker"
desc = "For ages 10 and up." desc = "For ages 10 and up. Pump it to shoot further."
icon_state = "super_soaker" icon_state = "super_soaker"
item_state = "super_soaker" item_state = "super_soaker"
inhand_states = list("left_hand" = 'icons/mob/in-hand/left/guns.dmi', "right_hand" = 'icons/mob/in-hand/right/guns.dmi') inhand_states = list("left_hand" = 'icons/mob/in-hand/left/guns.dmi', "right_hand" = 'icons/mob/in-hand/right/guns.dmi')
@@ -139,16 +160,18 @@
if(harm_labeled >= min_harm_label) if(harm_labeled >= min_harm_label)
to_chat(user, "<span class='warning'>A label sticks the trigger to the trigger guard!</span>")//Such a new feature, the player might not know what's wrong if it doesn't tell them. to_chat(user, "<span class='warning'>A label sticks the trigger to the trigger guard!</span>")//Such a new feature, the player might not know what's wrong if it doesn't tell them.
return return
if(reagents.total_volume < 10 && !in_chamber) if(reagents.total_volume < 5 && !in_chamber)
return click_empty(user) return click_empty(user)
if(!in_chamber) if(!in_chamber)
in_chamber = new projectile_type(src, max(3+(round(pumps/2)),15)) in_chamber = new projectile_type(src, min(3+(round(pumps/2)),15), mixed_color, mixed_alpha)
reagents.trans_to(in_chamber, 10) reagents.trans_to(in_chamber, 5)
for(var/datum/reagent/R in in_chamber.reagents.reagent_list)
R.volume *= 4//so here we're just doubling our quantity of reagents from 10 to 20//moved from projectile code
in_chamber.firer = user in_chamber.firer = user
Fire(A,user,params, struggle = struggle) Fire(A,user,params, struggle = struggle)
if(reagents.total_volume >= 10) if(pumps > 0)
in_chamber = new projectile_type(src) pumps--
reagents.trans_to(in_chamber, 10) QDEL_NULL(in_chamber)
/obj/item/weapon/gun/siren/supersoaker/attack_self(mob/user) /obj/item/weapon/gun/siren/supersoaker/attack_self(mob/user)
if(world.time - last_pump >= 1) if(world.time - last_pump >= 1)

View File

@@ -46,7 +46,8 @@ var/list/beam_master = list()
var/turf/T = vector2turf(info.point, z) var/turf/T = vector2turf(info.point, z)
T.last_beam_damage = fired_beam.damage T.last_beam_damage = fired_beam.damage
fired_beam.final_turf = final_turf
fired_beam.previous_turf = previous_turf
if(!A.Cross(fired_beam, T) || (!isturf(fired_beam.original) && A == fired_beam.original)) if(!A.Cross(fired_beam, T) || (!isturf(fired_beam.original) && A == fired_beam.original))
var/ret = fired_beam.to_bump(A) var/ret = fired_beam.to_bump(A)
if(ret) if(ret)
@@ -82,6 +83,8 @@ var/list/beam_master = list()
var/beam_color = null var/beam_color = null
var/beam_shift = null// the beam will animate() toward this color after being fired var/beam_shift = null// the beam will animate() toward this color after being fired
var/list/ray/past_rays = list() //full of rays var/list/ray/past_rays = list() //full of rays
var/turf/final_turf = null
var/turf/previous_turf = null
/obj/item/projectile/beam/Destroy() /obj/item/projectile/beam/Destroy()
QDEL_LIST_NULL(past_rays) QDEL_LIST_NULL(past_rays)
@@ -100,7 +103,8 @@ var/list/beam_master = list()
hits = shot_ray.cast(travel_range) hits = shot_ray.cast(travel_range)
else else
hits = shot_ray.cast(MAX_BEAM_DISTANCE) hits = shot_ray.cast(MAX_BEAM_DISTANCE)
final_turf = shot_ray.final_turf
previous_turf = shot_ray.previous_turf
if(!gcDestroyed) if(!gcDestroyed)
past_rays += shot_ray past_rays += shot_ray
else else
@@ -118,11 +122,13 @@ var/list/beam_master = list()
shot_ray.draw(last_hit.distance, icon, icon_state, color_override = beam_color, color_shift = beam_shift) shot_ray.draw(last_hit.distance, icon, icon_state, color_override = beam_color, color_shift = beam_shift)
if(last_hit.hit_type == RAY_CAST_REBOUND) if(last_hit.hit_type == RAY_CAST_REBOUND)
final_turf=null
ASSERT(!gcDestroyed) ASSERT(!gcDestroyed)
spawn() spawn()
rebound(last_hit.hit_atom) rebound(last_hit.hit_atom)
if(last_hit.hit_type == RAY_CAST_PORTAL) if(last_hit.hit_type == RAY_CAST_PORTAL)
final_turf=null
ASSERT(!gcDestroyed) ASSERT(!gcDestroyed)
spawn() spawn()
portal(last_hit.hit_atom) portal(last_hit.hit_atom)
@@ -1156,50 +1162,62 @@ var/list/laser_tag_vests = list(/obj/item/clothing/suit/tag/redtag, /obj/item/cl
penetration = 0 penetration = 0
pass_flags = PASSTABLE pass_flags = PASSTABLE
var/has_splashed = FALSE var/has_splashed = FALSE
var/atom/splashed_atom = null
/obj/item/projectile/beam/liquid_stream/New(atom/A, var/t_range) /obj/item/projectile/beam/liquid_stream/New(atom/A, var/t_range=3, var/m_color, var/m_alpha=255)
..(A) ..(A)
create_reagents(20) create_reagents(20)
if(t_range) travel_range = t_range
travel_range = t_range beam_color = m_color
else alpha = m_alpha
travel_range = 0
/obj/item/projectile/beam/liquid_stream/OnFired()
beam_color = mix_color_from_reagents(reagents.reagent_list)
alpha = mix_alpha_from_reagents(reagents.reagent_list)
..()
/obj/item/projectile/beam/liquid_stream/on_hit(var/atom/A, var/blocked = 0) /obj/item/projectile/beam/liquid_stream/on_hit(var/atom/A, var/blocked = 0)
if(reagents.total_volume) if(reagents.total_volume && istype(A, /mob))
for(var/datum/reagent/R in reagents.reagent_list) if(firer.zone_sel.selecting == TARGET_MOUTH && def_zone == LIMB_HEAD && ishuman(A)) //if aiming at head and is humanoid
reagents.add_reagent(R.id, reagents.get_reagent_amount(R.id))//so here we're just doubling our quantity of reagents from 10 to 20 var/mob/living/carbon/human/victim = A
if(istype(A, /mob)) if(!victim.check_body_part_coverage(MOUTH)) //if not covered with mask or something
if(firer.zone_sel.selecting == TARGET_MOUTH && def_zone == LIMB_HEAD && ishuman(A)) //if aiming at head and is humanoid victim.visible_message("<span class='warning'>[A] swallows \the [src]!</span>",
var/mob/living/carbon/human/victim = A "<span class='warning'>You swallow \the [src]!</span>")
if(!victim.check_body_part_coverage(MOUTH)) //if not covered with mask or something reagents.trans_to(A, reagents.total_volume)
victim.visible_message("<span class='warning'>[A] swallows \the [src]!</span>", has_splashed = TRUE //guess we arent stacking with the splash
"<span class='warning'>You swallow \the [src]!</span>") qdel(src)
reagents.trans_to(A, reagents.total_volume) //20% chance to get in mouth and in system, if mouth targeting was possible at all with projectiles this chance should be scrapped return 1
has_splashed = TRUE //guess we arent stacking with the splash
return 1
else
A.visible_message("<span class='warning'>\The [src] gets blocked from [A]'s mouth!</span>",
"<span class='warning'>\The [src] gets blocked from your mouth!</span>")//just block mouth, no turf splash
else else
var/splash_verb = pick("douses","completely soaks","drenches","splashes") A.visible_message("<span class='warning'>\The [src] gets blocked from [A]'s mouth!</span>",
A.visible_message("<span class='warning'>\The [src] [splash_verb] [A]!</span>", "<span class='warning'>\The [src] gets blocked from your mouth!</span>")//just block mouth, no turf splash
"<span class='warning'>\The [src] [splash_verb] you!</span>")
splash_sub(reagents, get_turf(A), reagents.total_volume/2, firer)//then we splash 10 of those on the turf in front (or under in case of mobs) of the hit atom
else else
splash_sub(reagents, get_turf(src), reagents.total_volume/2, firer) var/splash_verb = pick("douses","completely soaks","drenches","splashes")
splash_sub(reagents, A, reagents.total_volume, firer)//and 10 more on the atom itself A.visible_message("<span class='warning'>\The [src] [splash_verb] [A]!</span>",
has_splashed = TRUE "<span class='warning'>\The [src] [splash_verb] you!</span>")
return 1
/obj/item/projectile/beam/liquid_stream/to_bump(var/atom/A)
splashed_atom = A//doesn't matter if it's actually the atom we end up splashing since we only use that var on bullet_die()
. = ..()
/obj/item/projectile/beam/liquid_stream/bullet_die()//splashes reagents on the hit atom, or in front of it in case of turfs and border objects
if(reagents && reagents.total_volume && !has_splashed && ((bumped && splashed_atom) || final_turf))
if (splashed_atom && !isturf(splashed_atom) && (previous_turf && previous_turf.Adjacent(splashed_atom)))
if (splashed_atom.flow_flags & ON_BORDER)
loc = previous_turf
else
loc = get_turf(splashed_atom)
else if (isturf(splashed_atom))
loc = final_turf
else
loc = previous_turf
playsound(loc, 'sound/effects/slosh.ogg', 20, 1)
reagents.splashplosion(0)
has_splashed = TRUE
..()
//I never want to deal wity ray casts ever again
/obj/item/projectile/beam/liquid_stream/fireto(var/vector/origin, var/vector/direction)//splashes reagents on the turf if the projectile ran out
..()
if (reagents && reagents.total_volume && final_turf && !has_splashed)
loc = final_turf
reagents.splashplosion(0)
has_splashed = TRUE
qdel(src)
/obj/item/projectile/beam/liquid_stream/OnDeath()
if(!has_splashed && loc)
splash_sub(reagents, get_turf(src), reagents.total_volume)
/obj/item/projectile/beam/liquid_stream/proc/adjust_strength(var/t_range) /obj/item/projectile/beam/liquid_stream/proc/adjust_strength(var/t_range)
if(t_range) if(t_range)

View File

@@ -904,8 +904,10 @@
custom_impact = 1 custom_impact = 1
rotate = 0 rotate = 0
var/hard = 0 var/hard = 0
var/radius = 1//big glob of liquid, splashes a bit on surroundings
var/atom/splashed_atom = null
/obj/item/projectile/bullet/liquid_blob/New(atom/T, var/hardness = null) /obj/item/projectile/bullet/liquid_blob/New(atom/T, var/hardness = null, var/mixed_color=null, var/mixed_alpha=255, var/_rad=1)
..(T) ..(T)
hard = hardness hard = hardness
if(hard) if(hard)
@@ -913,37 +915,34 @@
create_reagents(10) create_reagents(10)
else else
create_reagents(50) create_reagents(50)
icon += mixed_color
alpha = mixed_alpha
radius = _rad
/obj/item/projectile/bullet/liquid_blob/OnFired() /obj/item/projectile/bullet/liquid_blob/to_bump(var/atom/A)
src.icon += mix_color_from_reagents(reagents.reagent_list) splashed_atom = A//doesn't matter if it's actually the atom we end up splashing since we only use that var on bullet_die()
src.alpha = mix_alpha_from_reagents(reagents.reagent_list) . = ..()
..() if (. && A)
if ((special_collision == PROJECTILE_COLLISION_DEFAULT) || (special_collision == PROJECTILE_COLLISION_BLOCKED))
if(istype(A, /mob))
if(hard)
var/splash_verb = pick("dousing","completely soaking","drenching","splashing")
A.visible_message("<span class='warning'>\The [src] smashes into [A], [splash_verb] \him!</span>",
"<span class='warning'>\The [src] smashes into you, [splash_verb] you!</span>")
else
var/splash_verb = pick("douses","completely soaks","drenches","splashes")
A.visible_message("<span class='warning'>\The [src] [splash_verb] [A]!</span>",
"<span class='warning'>\The [src] [splash_verb] you!</span>")
/obj/item/projectile/bullet/liquid_blob/on_hit(atom/A as mob|obj|turf|area)
if(!A) /obj/item/projectile/bullet/liquid_blob/bullet_die()
return
..()
if(reagents.total_volume) if(reagents.total_volume)
for(var/datum/reagent/R in reagents.reagent_list) var/turf/T = get_turf(splashed_atom)
reagents.add_reagent(R.id, reagents.get_reagent_amount(R.id)) if (!T.density && T.Adjacent(src))
if(istype(A, /mob)) loc = T
if(hard) playsound(loc, 'sound/effects/slosh.ogg', 20, 1)
var/splash_verb = pick("dousing","completely soaking","drenching","splashing") reagents.splashplosion(radius)
A.visible_message("<span class='warning'>\The [src] smashes into [A], [splash_verb] \him!</span>", ..()
"<span class='warning'>\The [src] smashes into you, [splash_verb] you!</span>")
else
var/splash_verb = pick("douses","completely soaks","drenches","splashes")
A.visible_message("<span class='warning'>\The [src] [splash_verb] [A]!</span>",
"<span class='warning'>\The [src] [splash_verb] you!</span>")
splash_sub(reagents, get_turf(A), reagents.total_volume/2)
else
splash_sub(reagents, get_turf(src), reagents.total_volume/2)
splash_sub(reagents, A, reagents.total_volume)
return 1
/obj/item/projectile/bullet/liquid_blob/OnDeath()
if(get_turf(src))
playsound(src, 'sound/effects/slosh.ogg', 20, 1)
/obj/item/projectile/bullet/pellet /obj/item/projectile/bullet/pellet
name = "buckshot pellet" name = "buckshot pellet"

View File

@@ -1989,10 +1989,7 @@
user?.attack_log += text("\[[time_stamp()]\] <span class='danger'>Threw a [lit ? "lit" : "unlit"] molotov to \the [hit_atom], containing [reagents.get_reagent_ids()]</span>") user?.attack_log += text("\[[time_stamp()]\] <span class='danger'>Threw a [lit ? "lit" : "unlit"] molotov to \the [hit_atom], containing [reagents.get_reagent_ids()]</span>")
log_attack("[lit ? "Lit" : "Unlit"] molotov shattered at [formatJumpTo(get_turf(hit_atom))], thrown by [key_name(user)] and containing [reagents.get_reagent_ids()]") log_attack("[lit ? "Lit" : "Unlit"] molotov shattered at [formatJumpTo(get_turf(hit_atom))], thrown by [key_name(user)] and containing [reagents.get_reagent_ids()]")
message_admins("[lit ? "Lit" : "Unlit"] molotov shattered at [formatJumpTo(get_turf(hit_atom))], thrown by [key_name_admin(user)] and containing [reagents.get_reagent_ids()]") message_admins("[lit ? "Lit" : "Unlit"] molotov shattered at [formatJumpTo(get_turf(hit_atom))], thrown by [key_name_admin(user)] and containing [reagents.get_reagent_ids()]")
reagents.reaction(get_turf(src), TOUCH) //splat the floor AND the thing we hit, otherwise fuel wouldn't ignite when hitting anything that wasn't a floor reagents.splashplosion(reagents.total_volume >= (reagents.maximum_volume/2))//splashing everything on the tile hit, and the surrounding ones if we're over half full.
if(hit_atom != get_turf(src)) //prevent spilling on the floor twice though
var/list/hit_zone = user && user.zone_sel ? list(user.zone_sel.selecting) : ALL_LIMBS
reagents.reaction(hit_atom, TOUCH, zone_sels = hit_zone) //maybe this could be improved?
invisibility = INVISIBILITY_MAXIMUM //so it stays a while to ignite any fuel invisibility = INVISIBILITY_MAXIMUM //so it stays a while to ignite any fuel
if(molotov == 1) //for molotovs if(molotov == 1) //for molotovs
@@ -2000,7 +1997,6 @@
new /obj/effect/decal/cleanable/ash(get_turf(src)) new /obj/effect/decal/cleanable/ash(get_turf(src))
var/turf/loca = get_turf(src) var/turf/loca = get_turf(src)
if(loca) if(loca)
// to_chat(world, "<span class='warning'>Burning...</span>")
loca.hotspot_expose(700, 1000,surfaces=istype(loc,/turf)) loca.hotspot_expose(700, 1000,surfaces=istype(loc,/turf))
else else
new /obj/item/weapon/reagent_containers/glass/rag(get_turf(src)) new /obj/item/weapon/reagent_containers/glass/rag(get_turf(src))

View File

@@ -0,0 +1,44 @@
//Hits every turfs in view and their content with our reagents (provided they are not blocked by windows or other turfs)
//Can be set to inject hit mob with some of the reagents based on their permeability. False by default.
/datum/reagents/proc/splashplosion(var/range=3,var/allow_permeability=FALSE)
if (reagent_list.len <= 0)
return
var/list/hit_turfs = list()
var/turf/epicenter = get_turf(my_atom)
var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread()
steam.set_up(max(range,1)*3, 0, epicenter, mix_color_from_reagents(reagent_list))
steam.start()
playsound(epicenter, 'sound/effects/bamf.ogg', max(10,15*range), 0, range-4)
if (!range)
hit_turfs = list(epicenter)
else
for(var/turf/T in dview(range, epicenter, INVISIBILITY_MAXIMUM))
if (cheap_pythag(T.x - epicenter.x,T.y - epicenter.y) <= range + 0.5)
if (test_reach(epicenter,T,PASSTABLE|PASSGRILLE|PASSMOB|PASSMACHINE|PASSGIRDER))
hit_turfs += T
for(var/datum/reagent/R in reagent_list)
var/min_volume_per_tile = max(1,R.volume/hit_turfs.len)
//the volume is affected by the number of turfs hit. The less turfs hit, the more concentrated the splashing.
//and the closer to the epicenter, the more splashing as well
for (var/turf/T in hit_turfs)
var/volume_for_this_tile = round((R.volume - min_volume_per_tile) / max(1,get_dist(epicenter,T))) + min_volume_per_tile
if (!T.density)
for (var/atom/movable/AM in T.contents)
if (ismob(AM))
if (isanimal(AM))
R.reaction_animal(AM, TOUCH, volume_for_this_tile,hit_turfs)
else
R.reaction_mob(AM, TOUCH, volume_for_this_tile, ALL_LIMBS, allow_permeability, hit_turfs)
else if (isobj(AM) && !istype(AM,/atom/movable/lighting_overlay))
R.reaction_obj(AM, volume_for_this_tile,hit_turfs)
R.reaction_turf(T, volume_for_this_tile,hit_turfs)
clear_reagents()

View File

@@ -539,20 +539,38 @@
else else
to_chat(mob, "<span class = 'notice'>Your pupils dilate further.</span>") to_chat(mob, "<span class = 'notice'>Your pupils dilate further.</span>")
/datum/disease2/effect/colorsmoke /datum/disease2/effect/colorsplash
name = "Colorful Syndrome" name = "Colorful Syndrome"
desc = "Causes the infected to synthesize smoke & rainbow colourant." desc = "Causes the infected to expulse bursts of paint from their pores."
encyclopedia = "The paint can be washed off items, and removed from floors and walls using bleach or acetone. The infected's own skin color will match the color of their last paint burst, but they can recover their original color with a shower, or exposure to space cleaner."
stage = 3 stage = 3
badness = EFFECT_DANGER_HINDRANCE badness = EFFECT_DANGER_HINDRANCE
var/list/random_color_list = list("#00aedb","#a200ff","#f47835","#d41243","#d11141","#00b159","#00aedb","#f37735","#ffc425","#008744","#0057e7","#d62d20","#ffa700")
max_multiplier = 3
/datum/disease2/effect/colorsmoke/activate(var/mob/living/mob) /datum/disease2/effect/colorsplash/activate(var/mob/living/mob)
if (ismouse(mob))//people don't like infected mice ruining maint var/obj/item/weapon/reagent_containers/R = new(get_turf(mob))
var/mob/living/simple_animal/mouse/M = mob R.invisibility = 101
if (!initial(M.infectable)) var/list/colors_to_use = random_color_list.Copy()
return var/range = 2 + round(max(1,multiplier))
var/color_count = round(max(1,multiplier))
if (ismouse(mob))
range = 0
color_count = 1
var/color_to_use = ""
for(var/i = 1 to color_count)
color_to_use = pick(colors_to_use)
colors_to_use -= color_to_use
R.reagents.add_reagent(FLAXOIL, 10 * max(1,range), list("color" = color_to_use, "alpha" = 255))
R.reagents.splashplosion(range)
range--
qdel(R)
to_chat(mob, "<span class='notice'>You feel colorful!</span>") to_chat(mob, "<span class='notice'>You feel colorful!</span>")
mob.reagents.add_reagent(COLORFUL_REAGENT, 5) mob.color = color_to_use
mob.reagents.add_reagent(PAISMOKE, 5)
/datum/disease2/effect/cleansmoke /datum/disease2/effect/cleansmoke
name = "Cleaning Syndrome" name = "Cleaning Syndrome"

View File

@@ -110,6 +110,7 @@
#include "code\__HELPERS\path.dm" #include "code\__HELPERS\path.dm"
#include "code\__HELPERS\priority_queue.dm" #include "code\__HELPERS\priority_queue.dm"
#include "code\__HELPERS\sanitize_values.dm" #include "code\__HELPERS\sanitize_values.dm"
#include "code\__HELPERS\test_reach.dm"
#include "code\__HELPERS\text.dm" #include "code\__HELPERS\text.dm"
#include "code\__HELPERS\time.dm" #include "code\__HELPERS\time.dm"
#include "code\__HELPERS\timed_alerts.dm" #include "code\__HELPERS\timed_alerts.dm"
@@ -2423,6 +2424,7 @@
#include "code\modules\reagents\randomized_reagent.dm" #include "code\modules\reagents\randomized_reagent.dm"
#include "code\modules\reagents\reagent_containers.dm" #include "code\modules\reagents\reagent_containers.dm"
#include "code\modules\reagents\reagent_dispenser.dm" #include "code\modules\reagents\reagent_dispenser.dm"
#include "code\modules\reagents\splashplosion.dm"
#include "code\modules\reagents\syringe_gun.dm" #include "code\modules\reagents\syringe_gun.dm"
#include "code\modules\reagents\machinery\centrifuge.dm" #include "code\modules\reagents\machinery\centrifuge.dm"
#include "code\modules\reagents\machinery\chem_dispenser.dm" #include "code\modules\reagents\machinery\chem_dispenser.dm"