mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
Grue shadow shunt (#33916)
* Grue shadow shunt. * HUD sprite, sound, and other stuff. * Visual effect, cooldown scaling with sentients eaten. * Update grue.dm * Update grue.dm * Update grue.dm Euclidean distance. * Update grue.dm back to non-euclidean * Update grue.dm Play sound also at new location.
This commit is contained in:
@@ -11,7 +11,7 @@ would spawn and follow the beaker, even if it is carried or thrown.
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
mouse_opacity = 0
|
||||
flags = 0
|
||||
w_type=NOT_RECYCLABLE
|
||||
w_type = NOT_RECYCLABLE
|
||||
pass_flags = PASSTABLE|PASSGRILLE|PASSMACHINE
|
||||
|
||||
/obj/effect/dissolvable()
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
var/bright_limit_drain = 3 //maximum brightness on tile to not drain health and power
|
||||
var/hg_mult = 3 //base multiplier for health gained per tick when on dark tile
|
||||
var/hd_mult = 3 //base multiplier for health drained per tick on bright tile (subject to further modification by how long the grue is exposed via accum_light_expos_mult)
|
||||
var/dark_dim_light=GRUE_DARK //darkness level currently the grue is currently exposed to, GRUE_DARK=nice and dark (heals the grue), GRUE_DIM=passably dim, GRUE_LIGHT=too bright (burns the grue)
|
||||
var/dark_dim_light = GRUE_DARK //darkness level currently the grue is currently exposed to, GRUE_DARK=nice and dark (heals the grue), GRUE_DIM=passably dim, GRUE_LIGHT=too bright (burns the grue)
|
||||
var/current_brightness = 0 //light level of current tile, range from 0 to 10
|
||||
var/accum_light_expos_mult= 1 //used to scale light damage the longer the grue is exposed to light
|
||||
var/list/accum_light_expos_gain_dark_dim_light=list(-3,-1,1) //light damage rate increases the longer the grue is exposed to light, but this effect dissipates after going back into darkness
|
||||
@@ -139,28 +139,28 @@
|
||||
hud_used.grue_hud()
|
||||
|
||||
//health indicator
|
||||
if (health >= maxHealth)
|
||||
if(health >= maxHealth)
|
||||
healths.icon_state = "health0"
|
||||
else if (health >= 4*maxHealth/5)
|
||||
else if(health >= 4*maxHealth/5)
|
||||
healths.icon_state = "health1"
|
||||
else if (health >= 3*maxHealth/5)
|
||||
else if(health >= 3*maxHealth/5)
|
||||
healths.icon_state = "health2"
|
||||
else if (health >= 2*maxHealth/5)
|
||||
else if(health >= 2*maxHealth/5)
|
||||
healths.icon_state = "health3"
|
||||
else if (health >= 1*maxHealth/5)
|
||||
else if(health >= 1*maxHealth/5)
|
||||
healths.icon_state = "health4"
|
||||
else if (health > 0)
|
||||
else if(health > 0)
|
||||
healths.icon_state = "health5"
|
||||
else
|
||||
healths.icon_state = "health6"
|
||||
//darkness level indicator
|
||||
if (lightparams.dark_dim_light==GRUE_DARK)
|
||||
if(lightparams.dark_dim_light==GRUE_DARK)
|
||||
healths2.icon_state= "lightlevel_dark"
|
||||
healths2.name="nice and dark"
|
||||
else if (lightparams.dark_dim_light==GRUE_DIM)
|
||||
else if(lightparams.dark_dim_light==GRUE_DIM)
|
||||
healths2.icon_state= "lightlevel_dim"
|
||||
healths2.name="adequately dim"
|
||||
else if (lightparams.dark_dim_light==GRUE_LIGHT)
|
||||
else if(lightparams.dark_dim_light==GRUE_LIGHT)
|
||||
healths2.icon_state= "lightlevel_bright"
|
||||
healths2.name="painfully bright"
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
..()
|
||||
|
||||
//process nutrienergy and health according to current tile brightness level
|
||||
if (stat!=DEAD)
|
||||
if(stat!=DEAD)
|
||||
|
||||
lightparams.ddl_update(src)
|
||||
|
||||
@@ -317,7 +317,7 @@
|
||||
add_spell(new /spell/aoe_turf/grue_ventcrawl, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
add_spell(new /spell/aoe_turf/grue_moult, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
|
||||
else if (lifestage==GRUE_JUVENILE)
|
||||
else if(lifestage==GRUE_JUVENILE)
|
||||
name = "grue"
|
||||
desc = "A creeping thing that lives in the dark. It is still a juvenile."
|
||||
icon_state = "grueling_living"
|
||||
@@ -336,8 +336,9 @@
|
||||
size = SIZE_BIG
|
||||
pass_flags = 0
|
||||
reagents.maximum_volume = 1000
|
||||
//Juvenile grue spells: eat and moult
|
||||
//Juvenile grue spells: eat, moult, and shadow shunt
|
||||
add_spell(new /spell/aoe_turf/grue_moult, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
add_spell(new /spell/aoe_turf/grue_blink, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
add_spell(new /spell/targeted/grue_eat, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
|
||||
else
|
||||
@@ -360,9 +361,10 @@
|
||||
size = SIZE_BIG
|
||||
pass_flags = 0
|
||||
reagents.maximum_volume = 1500
|
||||
//Adult grue spells: eat, lay eggs, and drain light
|
||||
//Adult grue spells: eat, lay eggs, shadow shunt, and drain light
|
||||
if(config.grue_egglaying)
|
||||
add_spell(new /spell/aoe_turf/grue_egg, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
add_spell(new /spell/aoe_turf/grue_blink, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
add_spell(new /spell/aoe_turf/grue_drainlight/, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
add_spell(new /spell/targeted/grue_eat, "grue_spell_ready", /obj/abstract/screen/movable/spell_master/grue)
|
||||
|
||||
@@ -375,7 +377,7 @@
|
||||
if(!client)
|
||||
return
|
||||
if(dark_plane)
|
||||
if (master_plane)
|
||||
if(master_plane)
|
||||
master_plane.blend_mode = BLEND_ADD
|
||||
dark_plane.alphas["grue"] = 15 // with the master_plane at BLEND_ADD, shadows appear well lit while actually well lit places appear blinding.
|
||||
client.color = list(
|
||||
@@ -407,20 +409,20 @@
|
||||
//Moulting into more mature forms.
|
||||
/mob/living/simple_animal/hostile/grue/proc/moult()
|
||||
if(alert(src,"Would you like to moult? You will become a vulnerable and immobile chrysalis during the process.",,"Moult","Cancel") == "Moult")
|
||||
if (lifestage<GRUE_ADULT)
|
||||
if (nutrienergy<moultcost && (digest*digest_sp<(moultcost-nutrienergy)))
|
||||
if(lifestage<GRUE_ADULT)
|
||||
if(nutrienergy<moultcost && (digest*digest_sp<(moultcost-nutrienergy)))
|
||||
to_chat(src, "<span class='notice'>You need to feed more first.</span>")
|
||||
return
|
||||
if (nutrienergy<moultcost && digest)
|
||||
if(nutrienergy<moultcost && digest)
|
||||
to_chat(src, "<span class='notice'>You are still digesting.</span>")
|
||||
return
|
||||
else if (!isturf(loc))
|
||||
else if(!isturf(loc))
|
||||
to_chat(src, "<span class='notice'>You need more room to moult.</span>")
|
||||
return
|
||||
else if (stat==UNCONSCIOUS)
|
||||
else if(stat==UNCONSCIOUS)
|
||||
to_chat(src, "<span class='notice'>You must be awake to moult.</span>")
|
||||
return
|
||||
else if (busy)
|
||||
else if(busy)
|
||||
to_chat(src, "<span class='notice'>You are already doing something.</span>")
|
||||
return
|
||||
else
|
||||
@@ -489,17 +491,17 @@
|
||||
//Reproduction via egglaying.
|
||||
/mob/living/simple_animal/hostile/grue/proc/reproduce()
|
||||
|
||||
if (lifestage==GRUE_ADULT) //must be adult
|
||||
if (eatencharge<=0)
|
||||
if(lifestage==GRUE_ADULT) //must be adult
|
||||
if(eatencharge<=0)
|
||||
to_chat(src, "<span class='notice'>You need to feed more first.</span>")
|
||||
return
|
||||
else if (!isturf(loc))
|
||||
else if(!isturf(loc))
|
||||
to_chat(src, "<span class='notice'>You need more room to reproduce.</span>")
|
||||
return
|
||||
else if (stat==UNCONSCIOUS)
|
||||
else if(stat==UNCONSCIOUS)
|
||||
to_chat(src, "<span class='notice'>You must be awake to reproduce.</span>")
|
||||
return
|
||||
else if (busy)
|
||||
else if(busy)
|
||||
to_chat(src, "<span class='notice'>You are already doing something.</span>")
|
||||
return
|
||||
else
|
||||
@@ -623,6 +625,11 @@
|
||||
else
|
||||
force_airlock_time=0
|
||||
|
||||
//shadow shunt cooldown
|
||||
for(var/spell/aoe_turf/grue_blink/thisspell in spell_list)
|
||||
var/newcooldown = max(45 SECONDS - eatencount * 2 SECONDS, 8 SECONDS)
|
||||
thisspell.charge_max = newcooldown
|
||||
thisspell.charge_counter = min(newcooldown, thisspell.charge_counter)
|
||||
|
||||
//Drain the light from the surrounding area.
|
||||
|
||||
@@ -661,15 +668,14 @@
|
||||
to_chat(src, "<span class='notice'>You are too big to do that.</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/grue/proc/hide()
|
||||
|
||||
if(lifestage==GRUE_LARVA)
|
||||
if(isUnconscious())
|
||||
return
|
||||
|
||||
if (locked_to && istype(locked_to, /obj/item/critter_cage))
|
||||
if(locked_to && istype(locked_to, /obj/item/critter_cage))
|
||||
return
|
||||
|
||||
if (plane != HIDING_MOB_PLANE)
|
||||
if(plane != HIDING_MOB_PLANE)
|
||||
plane = HIDING_MOB_PLANE
|
||||
to_chat(src, "<span class='notice'>You are now hiding.</span>")
|
||||
else
|
||||
@@ -677,3 +683,74 @@
|
||||
to_chat(src, "<span class='notice'>You have stopped hiding.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='notice'>You are too big to do that.</span>")
|
||||
|
||||
//Shadow shunt, blinks to another nearby dark location.
|
||||
/mob/living/simple_animal/hostile/grue/proc/grueblink()
|
||||
var/list/blinkcandidates = list()
|
||||
//get all sufficiently dark spots in range
|
||||
finding_blink_spots:
|
||||
for(var/turf/thisfloor in orange(25))
|
||||
if(thisfloor.density)
|
||||
continue
|
||||
if(get_ddl(thisfloor) > GRUE_DARK)
|
||||
continue
|
||||
for(var/atom/thiscontent in thisfloor.contents)
|
||||
if(thiscontent.density)
|
||||
continue finding_blink_spots
|
||||
if(ismob(thiscontent))
|
||||
continue finding_blink_spots
|
||||
if(istable(thiscontent))
|
||||
continue finding_blink_spots
|
||||
blinkcandidates += thisfloor
|
||||
if(blinkcandidates.len)
|
||||
//check up to 50 random valid locations and pick the furthest one
|
||||
var/turf/blinktarget
|
||||
var/btdist = -1
|
||||
var/oxy_lower_bound = min_oxy / CELL_VOLUME
|
||||
for(var/bc_index in 1 to min(blinkcandidates.len, 50))
|
||||
var/random_bc_index = rand(1, blinkcandidates.len)
|
||||
var/turf/random_bc = blinkcandidates[random_bc_index]
|
||||
var/thisdist = get_dist(random_bc, src)
|
||||
//weight away from low oxygen spots
|
||||
var/o2content = random_bc.air ? random_bc.air.molar_density(GAS_OXYGEN): 0
|
||||
if(o2content < oxy_lower_bound)
|
||||
thisdist *= o2content
|
||||
else
|
||||
thisdist *= oxy_lower_bound
|
||||
if(thisdist > btdist)
|
||||
btdist = thisdist
|
||||
blinktarget = random_bc
|
||||
playsound(src, 'sound/effects/grue_shadowshunt.ogg', 20, 1)
|
||||
playsound(blinktarget, 'sound/effects/grue_shadowshunt.ogg', 20, 1)
|
||||
to_chat(src, "<span class='notice'>You [pick("shift", "step", "slide", "glide", "push")] [pick("comfortably", "easily", "effortlessly", "readily", "gracefully")] through the [pick("darkness", "dark", "shadows", "shade", "blackness")].</span>")
|
||||
new /obj/effect/gruesparkles (loc)
|
||||
forceMove(blinktarget)
|
||||
new /obj/effect/gruesparkles (loc)
|
||||
return TRUE
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You reach into the darkness, but can't seem to find a way.</span>")
|
||||
//set the remaining cooldown to one second if no valid location was found
|
||||
for(var/spell/aoe_turf/grue_blink/thisspell in spell_list)
|
||||
thisspell.charge_counter = thisspell.charge_max - 1 SECONDS
|
||||
return
|
||||
|
||||
// Dark sparkles when a grue uses shadow shunt.
|
||||
/obj/effect/gruesparkles
|
||||
name = "dark glimmers"
|
||||
icon_state = "gruesparkles"
|
||||
anchored = 1
|
||||
|
||||
/obj/effect/gruesparkles/New()
|
||||
..()
|
||||
spawn(1.5 SECONDS)
|
||||
fade()
|
||||
|
||||
/obj/effect/gruesparkles/proc/fade()
|
||||
if(!src)
|
||||
return
|
||||
if(alpha <= 0)
|
||||
qdel(src)
|
||||
return
|
||||
spawn(0.5 SECONDS)
|
||||
alpha -= 55
|
||||
fade()
|
||||
|
||||
@@ -106,3 +106,33 @@
|
||||
playsound(user, null, 50, channel = CHANNEL_GRUE)
|
||||
..()
|
||||
|
||||
/spell/aoe_turf/grue_blink
|
||||
name = "Shadow Shunt"
|
||||
desc = "Tunnel through the darkness to a nearby location."
|
||||
user_type = USER_TYPE_GRUE
|
||||
panel = "Grue"
|
||||
hud_state = "grue_blink"
|
||||
override_base = "grue"
|
||||
range = 0
|
||||
charge_type = Sp_RECHARGE
|
||||
charge_max = 45 SECONDS
|
||||
still_recharging_msg = "<span class='notice'>You need to reorient yourself before doing that again.</span>"
|
||||
|
||||
/spell/aoe_turf/grue_blink/cast(list/targets, mob/living/simple_animal/hostile/grue/user)
|
||||
user.grueblink(TRUE)
|
||||
|
||||
/spell/aoe_turf/grue_blink/cast_check(skipcharge = 0, mob/user = usr)
|
||||
if(user)
|
||||
var/mob/living/simple_animal/hostile/grue/G = user
|
||||
if(G.stat == UNCONSCIOUS)
|
||||
to_chat(G, "<span class='notice'>You must be awake to do that.</span>")
|
||||
return FALSE
|
||||
else if(G.busy)
|
||||
to_chat(G, "<span class='notice'>You are already doing something.</span>")
|
||||
return FALSE
|
||||
else if(G.get_ddl(get_turf(G)) != GRUE_DARK)
|
||||
to_chat(G, "<span class='warning'>It's too bright here.</span>")
|
||||
return FALSE
|
||||
else
|
||||
return FALSE
|
||||
. = ..()
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 566 KiB After Width: | Height: | Size: 569 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
BIN
sound/effects/grue_shadowshunt.ogg
Normal file
BIN
sound/effects/grue_shadowshunt.ogg
Normal file
Binary file not shown.
Reference in New Issue
Block a user