Files
Bubberstation/code/modules/procedural_mapping
SkyratBot 57272c8d22 [MIRROR] Basic Watchers & Basilisks [MDB IGNORE] (#23137)
* Basic Watchers & Basilisks (#77630)

## About The Pull Request

This one is a double feature because Watchers and Basilisks share the
same typepath. You might see a couple more of those.
As is tradition I decided to fuck with them rather than just port them.
Here's what's up.

**Basilisks**

![image](https://github.com/tgstation/tgstation/assets/7483112/9e4b0115-65dd-4df7-b62a-21c7be8549bf)

![image](https://github.com/tgstation/tgstation/assets/7483112/59162e68-7d73-4659-9531-5078ff751228)

- Have a new soulless sprite which looks less like a living blue hedge.
- Walk at you and shoot you while you are not in range (just like
before).
- Become supercharged if they become "heated" by lava, lasers, or
temperature weapons. This was a feature they also previously had but
they would never encounter lava, so now it also works if you use the
wrong gun on them.
- Lose their supercharge if you cool them down.
- Otherwise pretty normal mobs.

**Watchers**

https://www.youtube.com/watch?v=kOq_Bf78k5A
Here's a traditional video of me intentionally getting hit by mechanics
(trust me its definitely on purpose)

- They glow emmissively a little bit so you can see them from further
away.
- Their eyes light up about 0.5 seconds before they are able to shoot at
you.
- No longer melee attack, instead try to stay out of melee.
- Will occasionally put you into "Overwatch", meaning they will shoot
you rapidly if you move or act while they're staring at you for a brief
time period (after which you become immune for 12 seconds, and during
which other watchers will play fair and stop shooting at you).
- If they start taking damage they will also start using their "Gaze"
attack, look away or suffer some kind of negative effect!
- - Normal watcher gaze flashes and confuses you.
- - Magmawing watcher gaze obviously burns (and briefly stuns) you.
- - Icewing watcher gaze freezes you and throws you backwards.
- Magnetically attract and eat diamonds. They also used to do this, but
just if they happened to coincidentally walk past some.

**Other accompanying changes**

All basic mobs will now adopt the "stop gliding" trait if they get
slowed down too much.
I moved behaviour for "fire a projectile from this atom" into a helper
proc because I was using it in three places and I will probably use it
in more places. There are probably other places in the existing code
which could be using this.
I think I made the basic mob melee attack forecast default a little more
forgiving, they were fucking me up too much and I am the playtester.

## Why It's Good For The Game

Another one off the list.
New tricks for old dogs.
Framework for making mobs with ranged attacks "fairer" (you can see when
they are ready to shoot you).
More (hopefully) versatile AI behaviours which we will reuse later (I
hope I'm not duplicating one someone already made).
If our players "enjoy" them enough we can give more mobs "don't look at
me" mechanics.
Removes some soul sprites.

## Changelog

🆑
refactor: Basilisks and Watchers now use the basic mob framework. Please
bug report any unusual behaviour.
sprite: Basilisks have new sprites.
add: Basilisks will go into a frenzy if heated by energy weapons or
temperature beams as well as by lava.
add: Watcher eyes will be illuminated briefly when they are ready to
fire at you.
add: Watchers can now briefly put you into "Overwatch" and penalise you
for moving while they can see you.
add: Wounded watchers will occasionally punish players who look at them.
balance: Unusual watcher variants are more likely to appear.
/🆑

* Basic Watchers & Basilisks

* Modular paths

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com>
2023-08-16 20:20:53 -04:00
..

Procedural Mapping

With Regards To RemieRichards


Coder Informative Readme

mapGenerator:

Desc: a mapGenerator is a master datum that collects and syncs all mapGeneratorModules in its modules list.

defineRegion(var/turf/Start, turf/End, replace = 0)

Example: defineRegion(locate(1,1,1),locate(5,5,5),0)

Desc: Sets the bounds of the mapGenerator's "map".

defineCircularRegion(var/turf/Start, turf/End, replace = 0)

Example: defineCircularRegion(locate(1,1,1),locate(5,5,5),0)

Desc: Sets the mapGenerator's "map" as a circle, with center in the middle of Start and End's X,Y,Z coordinates.

undefineRegion()

Example: undefineRegion()

Desc: Empties the map generator list.

checkRegion(var/turf/Start, turf/End)

Example: checkRegion(locate(1,1,1), locate(5,5,5))

Desc: Checks if a rectangle between Start's coords and End's coords is valid.

Existing Calls: mapGenerator/defineRegion(), mapGenerator/defineCircularRegion()

generate()

Example: generate()

Desc: Orders all mapGeneratorModules in the modules list to generate().

generateOneTurf(var/turf/T)

Example: generateOneTurf(locate(1,1,1))

Desc: Orders all mapGeneratorModules in the modules list to place(T) on this turf.

initialiseModules()

Example: initialiseModules()

Desc: Replaces all typepaths in the modules list with actual /datum/map_generator/Module types.

Existing Calls: mapGenerator/New()

syncModules()

Example: syncModules()

Desc: Sets the Mother variable on all mapGeneratorModules in the modules list to this mapGenerator.

Existing Calls: initialiseModules(),generate(),generateOneTurf()


mapGeneratorModule

Desc: a mapGeneratorModule has spawnableAtoms and spawnableTurfs lists which it will generate on turfs in it's mother's map based on cluster variables.

sync(var/datum/map_generator/mum)

Example: sync(a_mapGenerator_as_a_variable)

Desc: Sets the Mother variable to the mum argument.

Existing Calls: mapGenerator/syncModules()

generate()

Example: generate()

Desc: Calls place(T) on all turfs in it's mother's map

Existing Calls: mapGenerator/generate()

place(var/turf/T)

Example: place(locate(1,1,1)) Desc: Run this mapGeneratorModule's effects on this turf (Spawning atoms, Changing turfs).

Existing Calls: mapGenerator/generate(), mapGenerator/generateOneTurf()

checkPlaceAtom(var/turf/T)

Example: checkPlace(locate(1,1,1))

Desc: Checks if the turf is valid for placing atoms.

Existing Calls: place()


Mapper Friendly Readme

Simple Workflow:

  1. Define a/some mapGeneratorModule(s) to your liking, choosing atoms and turfs to spawn
  • I chose to split Turfs and Atoms off into separate modules, but this is NOT required.
  • A mapGeneratorModule may have turfs AND atoms, so long as each is in it's appropriate list
  1. Define a mapGenerator type who's modules list contains the typepath(s) of all the module(s) you wish to use
  • The order of the typepaths in the modules list is the order they will happen in, this is important for clusterCheckFlags.
  1. Take notes of the Bottom Left and Top Right turfs of your rectangular "map"'s coordinates
  • X, Y, AND Z. Yes, you can create 3D "maps" by having differing Z coordinates
  1. Create the mapGenerator type you created

  2. Call yourMapGeneratorType.defineRegion(locate(X,Y,Z), locate(X,Y,Z))

  • The above X/Y/Zs are the coordinates of the start and end turfs, the locate() simply finds the turf for the code
  1. Call yourMapGeneratorType.generate(), this will cause all the modules in the generator to build within the map bounds

Option Suggestions:

  • Have separate modules for Turfs and Atoms, this is not enforced, but it is how I have structured my nature example.
  • If your map doesn't look quite to your liking, simply jiggle with the variables on your modules and the type probabilities.
  • You can mix and map premade areas with the procedural generation, for example mapping an entire flat land but having code generate just the grass tufts.

Using the Modules list

Simply think of it like each module is a layer in a graphics editing program!

To help you do this templates such as /mapGeneratorModule/bottomLayer have been provided with appropriate default settings.

These are located near the bottom of mapGeneratorModule.dm. You would order your list left to right, top to bottom. For example: modules = list(bottomLayer,nextLayer,nextNextLayer), etc.

Variable Breakdown (For Mappers):

mapGenerator

  • map - INTERNAL, do not touch
  • modules - A list of typepaths of mapGeneratorModules

mapGeneratorModule

  • mother - INTERNAL, do not touch

  • spawnableAtoms - A list of typepaths and their probability to spawn, eg: spawnableAtoms = list(/obj/structure/flora/tree/pine = 30)

  • spawnableTurfs - A list of typepaths and their probability to spawn, eg: spawnableTurfs = list(/turf/unsimulated/floor/grass = 100)

  • clusterMax - The max range to check for something being "too close" for this atom/turf to spawn, the true value is random between clusterMin and clusterMax

  • clusterMin - The min range to check for something being "too close" for this atom/turf to spawn, the true value is random between clusterMin and clusterMax

  • clusterCheckFlags - A Bitfield that controls how the cluster checks work, All based on clusterMin and clusterMax guides

  • allowAtomsOnSpace - A Boolean for if we allow atoms to spawn on space tiles

clusterCheckFlags flags:

CLUSTER_CHECK_NONE				0   //No checks are done, cluster as much as possible
CLUSTER_CHECK_DIFFERENT_TURFS			2  //Don't let turfs of DIFFERENT types cluster
CLUSTER_CHECK_DIFFERENT_ATOMS			4  //Don't let atoms of DIFFERENT types cluster
CLUSTER_CHECK_SAME_TURFS			8  //Don't let turfs of the SAME type cluster
CLUSTER_CHECK_SAME_ATOMS			16 //Don't let atoms of the SAME type cluster

CLUSTER_CHECK_SAMES				24 //Don't let any of the same type cluster
CLUSTER_CHECK_DIFFERENTS			6  //Don't let any different types cluster
CLUSTER_CHECK_ALL_TURFS				10 //Don't let ANY turfs cluster same and different types
CLUSTER_CHECK_ALL_ATOMS				20 //Don't let ANY atoms cluster same and different types

CLUSTER_CHECK_ALL				30 //Don't let anything cluster, like, at all