mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-22 04:24:20 +01:00
Chemsmoke rewrite
- Complete rewrite of the chemsmoke spawning code (everything that happens after the reaction to create the smoke). - Moved it into it's own file since it's a little longer than the old one code-wise. - Tweaked the smoke sprites; gave chemsmoke directions for more randomization, and tweaked the timing of their animations so they are smooth now. Most of the code is documented, so you should be able to follow what's going on just reading through it. In short though: - Chemsmoke now affects a circular area with a variable range depending on the total amount of chemsmoke created (no more 1-of-each-ingredient smoke grenades - sorry). - The carried reagent volume in the cloud balances itself according to how big the affected area is (larger area, less carried reagent per turf). - The carried reagent has more volume the closer you are to the centre of the cloud. - The visual smoke effect scales with how big the affected area is. Conflicts: code/game/objects/effects/effect_system.dm icons/effects/96x96.dmi
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
|
||||
|
||||
/obj/effect/anomaly/proc/anomalyNeutralize()
|
||||
new /obj/effect/effect/bad_smoke(loc)
|
||||
new /obj/effect/effect/smoke/bad(loc)
|
||||
|
||||
for(var/atom/movable/O in src)
|
||||
O.loc = src.loc
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
/obj/effect/effect/smoke/chem
|
||||
icon = 'icons/effects/chemsmoke.dmi'
|
||||
opacity = 0
|
||||
time_to_live = 300
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSGLASS //PASSGLASS is fine here, it's just so the visual effect can "flow" around glass
|
||||
|
||||
/obj/effect/effect/smoke/chem/New()
|
||||
..()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
reagents = R
|
||||
R.my_atom = src
|
||||
return
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem
|
||||
smoke_type = /obj/effect/effect/smoke/chem
|
||||
var/obj/chemholder
|
||||
var/range
|
||||
var/list/targetTurfs
|
||||
var/list/wallList
|
||||
var/floorArea
|
||||
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem/New()
|
||||
..()
|
||||
chemholder = new/obj()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
chemholder.reagents = R
|
||||
R.my_atom = chemholder
|
||||
|
||||
//------------------------------------------
|
||||
//Sets up the chem smoke effect
|
||||
//
|
||||
// Calculates the max range smoke can travel, then gets all turfs in that view range.
|
||||
// Culls the selected turfs to a (roughly) circle shape, then calls smokeFlow() to make
|
||||
// sure the smoke can actually path to the turfs. This culls any turfs it can't reach.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/set_up(var/datum/reagents/carry = null, n = 10, c = 0, loca, direct)
|
||||
range = n * 0.3
|
||||
cardinals = c
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
if(istype(loca, /turf/))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
|
||||
targetTurfs = new()
|
||||
|
||||
//build affected area list
|
||||
for(var/turf/T in view(range, location))
|
||||
//cull turfs to circle
|
||||
if(cheap_pythag(T.x - location.x, T.y - location.y) <= range)
|
||||
targetTurfs += T
|
||||
|
||||
//make secondary list for reagents that affect walls
|
||||
if(chemholder.reagents.has_reagent("thermite") || chemholder.reagents.has_reagent("plantbgone"))
|
||||
wallList = new()
|
||||
|
||||
//pathing check
|
||||
smokeFlow(location, targetTurfs, wallList)
|
||||
|
||||
floorArea = targetTurfs.len
|
||||
|
||||
//Admin messaging
|
||||
var/contained = ""
|
||||
for(var/reagent in carry.reagent_list)
|
||||
contained += " [reagent] "
|
||||
if(contained)
|
||||
contained = "\[[contained]\]"
|
||||
var/area/A = get_area(location)
|
||||
|
||||
var/where = "[A.name] | [location.x], [location.y]"
|
||||
var/whereLink = "<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
|
||||
|
||||
if(carry.my_atom.fingerprintslast)
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "(<A HREF='?_src_=holder;adminmoreinfo=\ref[M]'>?</a>)"
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
//Runs the chem smoke effect
|
||||
//
|
||||
// Spawns damage over time loop for each reagent held in the cloud.
|
||||
// Applies reagents to walls that affect walls (only thermite and plant-b-gone at the moment).
|
||||
// Also calculates target locations to spawn the visual smoke effect on, so the whole area
|
||||
// is covered fairly evenly.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/start()
|
||||
|
||||
//reagent application
|
||||
if(chemholder.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in chemholder.reagents.reagent_list)
|
||||
var/proba = 100
|
||||
var/runs = 5
|
||||
|
||||
//dilute the reagents according to affected area
|
||||
R.volume /= floorArea / 4
|
||||
chemholder.reagents.update_total()
|
||||
|
||||
//apply wall affecting reagents to walls
|
||||
if(R.id in list("thermite", "plantbgone"))
|
||||
for(var/turf/T in wallList)
|
||||
R.reaction_turf(T, R.volume)
|
||||
|
||||
//reagents that should be applied to turfs in a random pattern
|
||||
if(R.id == "carbon")
|
||||
proba = 75
|
||||
else if(R.id in list("blood", "radium", "uranium"))
|
||||
proba = 25
|
||||
|
||||
spawn(0)
|
||||
for(var/i = 0, i < runs, i++)
|
||||
for(var/turf/T in targetTurfs)
|
||||
if(prob(proba))
|
||||
R.reaction_turf(T, R.volume)
|
||||
for(var/atom/A in T.contents)
|
||||
if(istype(A, /obj/effect/effect/smoke/chem)) //skip the item if it is chem smoke
|
||||
continue
|
||||
else if(istype(A, /mob))
|
||||
var/dist = cheap_pythag(T.x - location.x, T.y - location.y)
|
||||
if(!dist)
|
||||
dist = 1
|
||||
R.reaction_mob(A, volume = R.volume / dist)
|
||||
else if(istype(A, /obj))
|
||||
R.reaction_obj(A, R.volume)
|
||||
sleep(30)
|
||||
|
||||
|
||||
//build smoke icon
|
||||
var/color = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
var/icon/I
|
||||
if(color)
|
||||
I = icon('icons/effects/chemsmoke.dmi')
|
||||
I += color
|
||||
else
|
||||
I = icon('icons/effects/96x96.dmi', "smoke")
|
||||
|
||||
|
||||
//distance between each smoke cloud
|
||||
var/const/arcLength = 2.3559
|
||||
|
||||
|
||||
//calculate positions for smoke coverage - then spawn smoke
|
||||
for(var/i = 0, i < range, i++)
|
||||
var/radius = i * 1.5
|
||||
if(!radius)
|
||||
spawn(0)
|
||||
spawnSmoke(location, I, 1)
|
||||
continue
|
||||
|
||||
var/offset = 0
|
||||
var/points = round((radius * 2 * PI) / arcLength)
|
||||
var/angle = round(ToDegrees(arcLength / radius), 1)
|
||||
|
||||
if(!IsInteger(radius))
|
||||
offset = 45 //degrees
|
||||
|
||||
for(var/j = 0, j < points, j++)
|
||||
var/a = (angle * j) + offset
|
||||
var/x = round(radius * cos(a) + location.x, 1)
|
||||
var/y = round(radius * sin(a) + location.y, 1)
|
||||
var/turf/T = locate(x,y,location.z)
|
||||
if(T in targetTurfs)
|
||||
var/dist = cheap_pythag(T.x - location.x, T.y - location.y)
|
||||
if(!dist)
|
||||
dist = 1
|
||||
spawn(0)
|
||||
spawnSmoke(T, I, dist)
|
||||
|
||||
//------------------------------------------
|
||||
// Randomizes and spawns the smoke effect.
|
||||
// Also handles deleting the smoke once the effect is finished.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/spawnSmoke(var/turf/T, var/icon/I, var/dist = 1)
|
||||
var/obj/effect/effect/smoke/chem/smoke = new(location)
|
||||
chemholder.reagents.copy_to(smoke, chemholder.reagents.total_volume / dist, safety = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents
|
||||
smoke.icon = I
|
||||
smoke.layer = 6
|
||||
smoke.dir = pick(cardinal)
|
||||
smoke.pixel_x = -32 + rand(-8,8)
|
||||
smoke.pixel_y = -32 + rand(-8,8)
|
||||
walk_to(smoke, T)
|
||||
smoke.opacity = 1 //switching opacity on after the smoke has spawned, and then
|
||||
sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner
|
||||
smoke.opacity = 0 // lighting and view range updates
|
||||
fadeOut(smoke)
|
||||
smoke.delete()
|
||||
|
||||
//------------------------------------------
|
||||
// Fades out the smoke smoothly using it's alpha variable.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/fadeOut(var/atom/A, var/frames = 16)
|
||||
var/step = A.alpha / frames
|
||||
for(var/i = 0, i < frames, i++)
|
||||
A.alpha -= step
|
||||
sleep(world.tick_lag)
|
||||
return
|
||||
|
||||
//------------------------------------------
|
||||
// Smoke pathfinder. Uses a flood fill method based on zones to
|
||||
// quickly check what turfs the smoke (airflow) can actually reach.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/smokeFlow()
|
||||
|
||||
var/list/pending = new()
|
||||
var/list/complete = new()
|
||||
|
||||
pending += location
|
||||
|
||||
while(pending.len)
|
||||
for(var/turf/simulated/current in pending)
|
||||
for(var/D in cardinal)
|
||||
var/turf/simulated/target = get_step(current, D)
|
||||
|
||||
if(wallList)
|
||||
if(istype(target, /turf/simulated/wall))
|
||||
if(!(target in wallList))
|
||||
wallList += target
|
||||
continue
|
||||
if(!target.zone)
|
||||
continue
|
||||
if(target in pending)
|
||||
continue
|
||||
if(target in complete)
|
||||
continue
|
||||
if(!(target in targetTurfs))
|
||||
continue
|
||||
|
||||
pending += target
|
||||
|
||||
pending -= current
|
||||
complete += current
|
||||
|
||||
targetTurfs = complete
|
||||
|
||||
return
|
||||
@@ -236,7 +236,7 @@ steam.start() -- spawns the effect
|
||||
delete()
|
||||
return
|
||||
|
||||
/obj/effect/effect/smoke/HasEntered(mob/living/carbon/M as mob )
|
||||
/obj/effect/effect/smoke/Crossed(mob/living/carbon/M as mob )
|
||||
..()
|
||||
if(istype(M))
|
||||
affect(M)
|
||||
@@ -382,116 +382,7 @@ steam.start() -- spawns the effect
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/mustard
|
||||
smoke_type = /obj/effect/effect/smoke/mustard
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
/obj/effect/effect/smoke/chem
|
||||
icon = 'icons/effects/chemsmoke.dmi'
|
||||
|
||||
/obj/effect/effect/smoke/chem/New()
|
||||
..()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
reagents = R
|
||||
R.my_atom = src
|
||||
return
|
||||
|
||||
/obj/effect/effect/smoke/chem/Move()
|
||||
..()
|
||||
for(var/atom/A in view(2, src))
|
||||
if(reagents.has_reagent("radium")||reagents.has_reagent("uranium")||reagents.has_reagent("carbon")||reagents.has_reagent("thermite"))//Prevents unholy radium spam by reducing the number of 'greenglows' down to something reasonable -Sieve
|
||||
if(prob(5))
|
||||
reagents.reaction(A)
|
||||
else
|
||||
reagents.reaction(A)
|
||||
|
||||
return
|
||||
|
||||
/obj/effect/effect/smoke/chem/affect(mob/living/carbon/M as mob )
|
||||
reagents.reaction(M)
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem
|
||||
smoke_type = /obj/effect/effect/smoke/chem
|
||||
var/obj/chemholder
|
||||
|
||||
New()
|
||||
..()
|
||||
chemholder = new/obj()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
chemholder.reagents = R
|
||||
R.my_atom = chemholder
|
||||
|
||||
set_up(var/datum/reagents/carry = null, n = 5, c = 0, loca, direct, silent = 0)
|
||||
if(n > 20)
|
||||
n = 20
|
||||
number = n
|
||||
cardinals = c
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
|
||||
if(istype(loca, /turf/))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
if(direct)
|
||||
direction = direct
|
||||
if(!silent)
|
||||
var/contained = ""
|
||||
for(var/reagent in carry.reagent_list)
|
||||
contained += " [reagent] "
|
||||
if(contained)
|
||||
contained = "\[[contained]\]"
|
||||
var/area/A = get_area(location)
|
||||
|
||||
var/where = "[A.name] | [location.x], [location.y]"
|
||||
var/whereLink = "<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
|
||||
|
||||
if(carry.my_atom.fingerprintslast)
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "(<A HREF='?_src_=holder;adminmoreinfo=\ref[M]'>?</a>)"
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
start()
|
||||
var/i = 0
|
||||
|
||||
var/color = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
|
||||
for(i=0, i<src.number, i++)
|
||||
if(src.total_smoke > 20)
|
||||
return
|
||||
spawn(0)
|
||||
if(holder)
|
||||
src.location = get_turf(holder)
|
||||
var/obj/effect/effect/smoke/chem/smoke = new /obj/effect/effect/smoke/chem(src.location)
|
||||
src.total_smoke++
|
||||
var/direction = src.direction
|
||||
if(!direction)
|
||||
if(src.cardinals)
|
||||
direction = pick(cardinal)
|
||||
else
|
||||
direction = pick(alldirs)
|
||||
|
||||
if(chemholder.reagents.total_volume != 1) // can't split 1 very well
|
||||
chemholder.reagents.copy_to(smoke, chemholder.reagents.total_volume / number) // copy reagents to each smoke, divide evenly
|
||||
|
||||
if(color)
|
||||
smoke.icon += color // give the smoke color, if it has any to begin with
|
||||
else
|
||||
// if no color, just use the old smoke icon
|
||||
smoke.icon = 'icons/effects/96x96.dmi'
|
||||
smoke.icon_state = "smoke"
|
||||
|
||||
for(i=0, i<pick(0,1,1,1,2,2,2,3), i++)
|
||||
sleep(10)
|
||||
step(smoke,direction)
|
||||
spawn(150+rand(10,30))
|
||||
if(smoke) smoke.delete()
|
||||
src.total_smoke--
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//////// Attach an Ion trail to any object, that spawns when it moves (like for the jetpack)
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
user << "<span class='notice'>\The [src] is full.</span>"
|
||||
return
|
||||
reagents.remove_reagent(25,"water")
|
||||
var/datum/effect/effect/system/bad_smoke_spread/smoke = new /datum/effect/effect/system/bad_smoke_spread()
|
||||
var/datum/effect/effect/system/smoke_spread/bad/smoke = new /datum/effect/effect/system/smoke_spread/bad()
|
||||
smoke.set_up(5, 0, user.loc)
|
||||
smoke.start()
|
||||
playsound(user.loc, 'sound/effects/bamf.ogg', 50, 2)
|
||||
|
||||
Reference in New Issue
Block a user