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:
Hinaichigo
2023-02-11 05:58:00 +09:00
committed by GitHub
parent 04630bb993
commit dbb2815a98
6 changed files with 137 additions and 30 deletions

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

Binary file not shown.