Files
Bubberstation/code/datums/lazy_template.dm
Time-Green 89cf4c7787 The Voidwalker | New Midround Antagonist (#84674)
## The Voidwalker

Adds a new antagonist, the Voidwalker! 
It's a rare antag that spawns in space when there's at least 40 people
Design doc is here: https://hackmd.io/jE4YScP8RPykXo37rTBV2Q (there's
some deviations)
No biddle

## Summary
Spooky space antag that moves around space, ambushing people either in
space or near space. They can move through glass, have an ability to
stamina drain you if they remain in your vision for 8 seconds, can
temporarily remove glass windows to drag you through them and deal
~~stamina damage~~ incredibly violence. Upon being taken, they can be
kidnapped and cursed, muting and pacifying the person and sending them
to the void.

## Passive

![image](https://github.com/tgstation/tgstation/assets/7501474/e2bbac33-bac8-4eb9-9042-994904ebec18)

![image](https://github.com/tgstation/tgstation/assets/7501474/b76df700-84d4-4722-bf6f-2e2049c345b3)

Passive abilities:

- Permanent space flight and indoor flight
- Space regen
- Slowdown when in gravity
- Space camo (very low alpha when in space
- Can freely move through unshocked glass
- Mute, but can hear all station frequencies (excluding binary)
- Will quickly die when on planets or moons (obviously can't roll on
icebox)
- 10 brute armor and 20 burn armor. They can't wear any form of armor
and don't have any get out of jail free cards, so I think it'll help
with their survivability a slight bit

## Abilities
**Void eater:** Literally just a light-eater but instead of eating light
it instantly shatters windows, but restores them after a few seconds

**Space Dive:** New ability that lets you move under the station with a
2 second do_after, so they can still get to closed of space spots
without being as annoying as heretic space shift

**Unsettle:** Remain in view of the target for 8 seconds to give them a
short stun, slurring and 80 stamina damage, but announce your presence
and location to them. Both you and your target can move, as long as you
remain in their view.

**Space Kidnap:** When your target is incapacitated and in space, you
can drag them into the cosmic void. They'll be returned cursed after
undergoing a sort of reverse heretic sacrifice (more on that in the next
section).

[Showcase of all the above abilities!](https://youtu.be/NJ01H28PV9w)

## Voided Crew
A brain trauma received when you get kidnapped. While under it's
influence, you are muted and pacified. You will die in planetary gravity
and cannot enter space.


![image](https://github.com/tgstation/tgstation/assets/7501474/ea90bf9b-b8d1-4d46-8c69-7b0900e8e50c)

It can be cured with a lobotomy or by dying in planetary gravity. 
You get warned to avoid the Voidwalker. The voidwalker now does extra
damage and gets the option to glass gib you if you die, leaving just a
brain

## Loot
On death, the Voidwalker shatters into glass and drops a cosmic skull.
Looking into the skull gives you a stable version of the Voided brain
trauma. It doesn't give you pacifism and doesn't ban you from space. It
also makes you space immune and gives you the ability to walk through
unshocked glass after 2 seconds do_after.

Sprites for the cosmic skull by Justice12354 and Rex9001! I'll throw up
a video showcasing death and their sprites on Sunday

## Why It's Good For The Game
We don't have any space centered antagonists. The closest we have are
Space Dragons, but they have to go deep into the station anyway. I'm
also quite fond of simpler antagonists, like revenant and nightmare.
Give people the tools, and they'll make the fun themselves.

I've been comparing the Voidwalker with the Nightmare. Both are goalles,
simple antagonists, but where the Nightmare's gimmick is darkness, the
Voidwalker's gimmick is space.

They also get a dark tentacle arm so they can murderize people, but it
only gets them to crit. After that they can drag people into the "void"
to kidnap them. This is mostly to encourage the Voidwalker not to just
space them cause that's kinda lame. They still can though, just in-case
it would be very funny

## Changelog
🆑 Time-Green, Justice12354, Rex9001
add: Adds the Voidwalker, a new rare space based midround antagonist!
code: Adds the ability to texture limbs and bodypart_overlays
Sprite: Cosmic skull sprites by Justice12354 and Rex9001
/🆑

- [x] Add a better kidnapping mechanic, instead of just teleporting
someone to a station turf
- [x] Add an armblade or weapon or something, unarmed combat is kinda
ass for this
- [x] Fix the antag preview not rendering textures
- [x] Prevent them from space phasing when in combat so they can
actually be killed in space combat, even if really fucking hard
- [x] Nerf visibility for people with space parallax disabled, probably
also something to improve camo with colored parallax
- [x] Replace the stamina damage stuff
- [x] Cool rework of space phase idea I have
- [x] Update/implement vidual effects
- [x] Implement names

## Considerations
There's a few things that I might change or implement later, depending
on how it actually plays.

It's a space focused antag, but there might not be enough people near
space in a given round. An ability to let them "capture" area's or let
them turn into a meteor or something might be needed later if they turn
out to be too passive.

They might also be _too_ oppressive in space. I designed them for
near-space combat, but may've made them insanely overpowered in in raw
space combat.

Probably wont do biddle, but might add more powers that you can reroll
every few minutes or something (like blob). Either wat it wont be this
PR

---------

Co-authored-by: carlarctg <53100513+carlarctg@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Jacquerel <hnevard@gmail.com>
2024-07-14 18:57:44 +00:00

137 lines
4.7 KiB
Plaintext

/**
* Datum used to designate certain areas that do not need to exist nor be loaded at world start
* but do want to be loaded under certain circumstances. Use this for stuff like the nukie base or wizden, aka stuff that only matters when their antag is rolled.
*/
/datum/lazy_template
/// If this is true each load will increment an index keyed to the type and it will load [map_name]_[index]
var/list/datum/turf_reservation/reservations = list()
var/uses_multiple_allocations = FALSE
/// Key to identify this template - used in caching
var/key
/// Directory of maps to prefix to the filename
var/map_dir = "_maps/templates/lazy_templates"
/// The filename (without extension) of the map to load
var/map_name
/// place_on_top: Whether to use /turf/proc/PlaceOnTop rather than /turf/proc/ChangeTurf
var/place_on_top = FALSE
/// type of turf reservation
var/turf_reservation_type = /datum/turf_reservation
/datum/lazy_template/New()
reservations = list()
..()
/datum/lazy_template/Destroy(force)
if(!force)
stack_trace("Something is trying to delete [type]")
return QDEL_HINT_LETMELIVE
QDEL_LIST(reservations)
GLOB.lazy_templates -= key
return ..()
/**
* Does the grunt work of loading the template.
*/
/datum/lazy_template/proc/lazy_load()
RETURN_TYPE(/turf)
// This is a static assosciative list that is used to ensure maps that have variations are correctly varied when spawned
// I want to make it to where you can make a range and it'll randomly pick'n'take from the available versions at random
// But that can be done later when I have the time
var/static/list/multiple_allocation_hash = list()
var/load_path = "[map_dir]/[map_name].dmm"
if(uses_multiple_allocations)
var/times = multiple_allocation_hash[key] || 0
times += 1
multiple_allocation_hash[key] = times
load_path = "[map_dir]/[map_name]_[times].dmm"
if(!load_path || !fexists(load_path))
CRASH("lazy template [type] has an invalid load_path: '[load_path]', check directory and map name!")
var/datum/parsed_map/parsed_template = load_map(
file(load_path),
measure_only = TRUE,
)
if(isnull(parsed_template.parsed_bounds))
CRASH("Failed to cache lazy template for loading: '[key]'")
var/width = parsed_template.parsed_bounds[MAP_MAXX] - parsed_template.parsed_bounds[MAP_MINX] + 1
var/height = parsed_template.parsed_bounds[MAP_MAXY] - parsed_template.parsed_bounds[MAP_MINY] + 1
var/datum/turf_reservation/reservation = SSmapping.request_turf_block_reservation(
width,
height,
parsed_template.parsed_bounds[MAP_MAXZ],
reservation_type = turf_reservation_type,
)
if(!reservation)
CRASH("Failed to reserve a block for lazy template: '[key]'")
// lists kept for overall loading
var/list/loaded_atom_movables = list()
var/list/loaded_turfs = list()
var/list/loaded_areas = list()
var/list/obj/structure/cable/loaded_cables = list()
var/list/obj/machinery/atmospherics/loaded_atmospherics = list()
for(var/z_idx in parsed_template.parsed_bounds[MAP_MAXZ] to 1 step -1)
var/turf/bottom_left = reservation.bottom_left_turfs[z_idx]
var/turf/top_right = reservation.top_right_turfs[z_idx]
load_map(
file(load_path),
bottom_left.x,
bottom_left.y,
bottom_left.z,
z_upper = z_idx,
z_lower = z_idx,
place_on_top = place_on_top,
)
for(var/turf/turf as anything in block(bottom_left, top_right))
loaded_turfs += turf
loaded_areas |= get_area(turf)
// atoms can actually be in the contents of two or more turfs based on its icon/bound size
// see https://www.byond.com/docs/ref/index.html#/atom/var/contents
for(var/thing in (turf.get_all_contents() - turf))
if(istype(thing, /obj/structure/cable))
loaded_cables += thing
else if(istype(thing, /obj/machinery/atmospherics))
loaded_atmospherics += thing
loaded_atom_movables |= thing
SSatoms.InitializeAtoms(loaded_areas + loaded_atom_movables + loaded_turfs)
SSmachines.setup_template_powernets(loaded_cables)
SSair.setup_template_machinery(loaded_atmospherics)
SEND_SIGNAL(src, COMSIG_LAZY_TEMPLATE_LOADED, loaded_atom_movables, loaded_turfs, loaded_areas)
reservations += reservation
return reservation
/datum/lazy_template/nukie_base
key = LAZY_TEMPLATE_KEY_NUKIEBASE
map_name = "nukie_base"
/datum/lazy_template/wizard_dem
key = LAZY_TEMPLATE_KEY_WIZARDDEN
map_name = "wizard_den"
/datum/lazy_template/ninja_holding_facility
key = LAZY_TEMPLATE_KEY_NINJA_HOLDING_FACILITY
map_name = "ninja_den"
/datum/lazy_template/abductor_ship
key = LAZY_TEMPLATE_KEY_ABDUCTOR_SHIPS
map_name = "abductor_ships"
/datum/lazy_template/heretic_sacrifice_room
key = LAZY_TEMPLATE_KEY_HERETIC_SACRIFICE
map_name = "heretic_sacrifice"
/datum/lazy_template/voidwalker_void
key = LAZY_TEMPLATE_KEY_VOIDWALKER_VOID
map_name = "voidwalker_void"