code:
/**
 * Base class for all random spawners.
 */
/obj/effect/spawner/random
	icon = 'icons/effects/random_spawners.dmi'
	icon_state = "loot"
	layer = OBJ_LAYER
	/// Stops persistent lootdrop spawns from being shoved into lockers
	anchored = TRUE
	/// A list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect)
	var/list/loot
	/// The subtypes AND type to combine with the loot list
	var/loot_type_path
	/// The subtypes (this excludes the provided path) to combine with the loot list
	var/loot_subtype_path
	/// Whether the spawner should immediately spawn loot and cleanup on Initialize()
	var/spawn_on_init = TRUE
	/// How many items will be spawned
	var/spawn_loot_count = 1
	/// If the same item can be spawned twice
	var/spawn_loot_double = TRUE
	/// Whether the items should be distributed to offsets 0,1,-1,2,-2,3,-3.. This overrides pixel_x/y on the spawner itself
	var/spawn_loot_split = FALSE
	/// Whether the spawner should spawn all the loot in the list
	var/spawn_all_loot = FALSE
	/// The chance for the spawner to create loot (ignores spawn_loot_count)
	var/spawn_loot_chance = 100
	/// Determines how big of a range (in tiles) we should scatter things in.
	var/spawn_scatter_radius = 0
	/// Whether the items should have a random pixel_x/y offset (maxium offset distance is ±16 pixels for x/y)
	var/spawn_random_offset = FALSE

/obj/effect/spawner/random/Initialize(mapload)
	. = ..()

	if(should_spawn_on_init())
		spawn_loot()
		return INITIALIZE_HINT_QDEL

/// Helper proc that returns TRUE if the spawner should spawn loot in Initialise() and FALSE otherwise. Override this to change spawning behaviour.
/obj/effect/spawner/random/proc/should_spawn_on_init()
	return spawn_on_init

///If the spawner has any loot defined, randomly picks some and spawns it. Does not cleanup the spawner.
/obj/effect/spawner/random/proc/spawn_loot(lootcount_override)
	if(!prob(spawn_loot_chance))
		return INITIALIZE_HINT_QDEL

	var/list/spawn_locations = get_spawn_locations(spawn_scatter_radius)
	var/spawn_loot_count = isnull(lootcount_override) ? src.spawn_loot_count : lootcount_override

	if(spawn_all_loot)
		spawn_loot_count = INFINITY
		spawn_loot_double = FALSE

	if(loot_type_path)
		loot += typesof(loot_type_path)

	if(loot_subtype_path)
		loot += subtypesof(loot_subtype_path)

	if(loot?.len)
		var/loot_spawned = 0
		while((spawn_loot_count-loot_spawned) && loot.len)
			var/lootspawn = pick_weight(loot)
			while(islist(lootspawn))
				lootspawn = pick_weight(lootspawn)
			if(!spawn_loot_double)
				loot.Remove(lootspawn)
			if(lootspawn && (spawn_scatter_radius == 0 || spawn_locations.len))
				var/turf/spawn_loc = loc
				if(spawn_scatter_radius > 0)
					spawn_loc = pick_n_take(spawn_locations)

				var/atom/movable/spawned_loot = new lootspawn(spawn_loc)
				spawned_loot.setDir(dir)

				if(istype(src, /obj/effect/spawner/random/trash/graffiti))
					var/obj/effect/spawner/random/trash/graffiti/G = src
					G.select_graffiti(spawned_loot)
					//var/obj/graffiti = new /obj/effect/decal/cleanable/crayon(get_turf(src))

				if (!spawn_loot_split && !spawn_random_offset)
					if (pixel_x != 0)
						spawned_loot.pixel_x = pixel_x
					if (pixel_y != 0)
						spawned_loot.pixel_y = pixel_y
				else if (spawn_random_offset)
					spawned_loot.pixel_x = rand(-16, 16)
					spawned_loot.pixel_y = rand(-16, 16)
				else if (spawn_loot_split)
					if (loot_spawned)
						spawned_loot.pixel_x = spawned_loot.pixel_y = ((!(loot_spawned%2)*loot_spawned/2)*-1)+((loot_spawned%2)*(loot_spawned+1)/2*1)
			loot_spawned++

///If the spawner has a spawn_scatter_radius set, this creates a list of nearby turfs available
/obj/effect/spawner/random/proc/get_spawn_locations(radius)
	var/list/scatter_locations = list()

	if(radius >= 0)
		for(var/turf/turf_in_view in view(radius, get_turf(src)))
			if(!turf_in_view.density)
				scatter_locations += turf_in_view

	return scatter_locations

//finds the probabilities of items spawning from a loot spawner's loot pool
/obj/item/loot_table_maker
	icon = 'icons/effects/landmarks_static.dmi'
	icon_state = "random_loot"
	var/spawner_to_test = /obj/effect/spawner/random/maintenance //what lootdrop spawner to use the loot pool of
	var/loot_count = 180 //180 is about how much maint loot spawns per map as of 11/14/2019
	//result outputs
	var/list/spawned_table //list of all items "spawned" and how many
	var/list/stat_table //list of all items "spawned" and their occurrance probability

/obj/item/loot_table_maker/Initialize(mapload)
	. = ..()
	make_table()

/obj/item/loot_table_maker/attack_self(mob/user)
	to_chat(user, "Loot pool re-rolled.")
	make_table()

/obj/item/loot_table_maker/proc/make_table()
	spawned_table = list()
	stat_table = list()
	var/obj/effect/spawner/random/spawner_to_table = new spawner_to_test
	var/lootpool = spawner_to_table.loot
	qdel(spawner_to_table)
	for(var/i in 1 to loot_count)
		var/loot_spawn = pick_loot(lootpool)
		if(!(loot_spawn in spawned_table))
			spawned_table[loot_spawn] = 1
		else
			spawned_table[loot_spawn] += 1
	stat_table += spawned_table
	for(var/item in stat_table)
		stat_table[item] /= loot_count

/obj/item/loot_table_maker/proc/pick_loot(lootpool) //selects path from loot table and returns it
	var/lootspawn = pick_weight(lootpool)
	while(islist(lootspawn))
		lootspawn = pick_weight(lootspawn)
	return lootspawn


code:
# Random Spawners

## About

Random spawners are an organized tool primarily for mapping to enhance replayability.  The spawners can create objects, effects, and structures with different tweakable settings to get the desired outcome.  You can make a spawner determine direction, rarity, number of items to spawn, pixel spacing between items, and even spread it over a large tile radius.  This lets you control the atmosphere of a location.  You could for instance spawn different piles of trash in maint or spawn decoration items for a room to give it more randomized flavor.  The choice is yours!

*(note the audience of this README is directed towards mappers who lack knowledge of coding)*

## Variables

The following variables are defined in `code/game/objects/effects/spawners/random/random.dm` that control how a spawner works.

- `loot` - a list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect)
- `loot_type_path` - this combines the subtypes AND type list with the loot list
- `loot_subtype_path` - this combines ONLY the subtypes (excludes the `loot_subtype_path`) with the loot list
- `spawn_loot_count` - how many items will be spawned
- `spawn_loot_double` - if the same item can be spawned twice from the loot list
- `spawn_loot_split` - whether the items should be distributed to offsets 0,1,-1,2,-2,3,-3.. This overrides pixel_x/y on the spawner itself
- `spawn_all_loot` - whether the spawner should spawn all the loot in the list (ignores `spawn_loot_count`)
- `spawn_loot_chance` - the chance for the spawner to create loot (ignores `spawn_loot_count`)
- `spawn_scatter_radius` - determines how big of a range (in tiles) we should scatter things in

These variables are set to the following default values for the base `random.dm` object that all objects inherit from:

```dm
	/// these three loot values are all empty
	var/list/loot
	var/loot_type_path
	var/loot_subtype_path

	var/spawn_loot_count = 1 // by default one item will be selected from the loot list
	var/spawn_loot_double = TRUE // by default duplicate items CAN be spawned from the loot list
	var/spawn_loot_split = FALSE // by default items will NOT spread out on the same tile
	var/spawn_all_loot = FALSE // by default the spawner will only spawn the number of items set in spawn_loot_count
	var/spawn_loot_chance = 100 // by default the spawner has a 100% chance to spawn the item(s)
	var/spawn_scatter_radius = 0 // by default the spawner will spawn the items ONLY on the tile it is on
```

However there are some categories that overwrite these default values so pay attention to the folder or category you group your spawner in.  For instance the `obj/effect/spawner/random/techstorage` category overwrites the `spawn_all_loot` and the `spawn_loot_split` variables.

```dm
// Tech storage circuit board spawners
/obj/effect/spawner/random/techstorage
	name = "generic circuit board spawner"
	spawn_loot_split = TRUE
	spawn_all_loot = TRUE
```

This means any spawner you create under the techstorage will also have those variables set to that by default.  This can be overridden quite easily just be resetting the variables back to the normal state like so:

```dm
/obj/effect/spawner/random/techstorage/data_disk
	name = "data disk spawner"
	spawn_all_loot = FALSE // now our loot won't all be spawned
	loot = list(
		/obj/item/disk/data = 49,
		/obj/item/disk/nuclear/fake/obvious = 1,
	)
```

## Template

All the random spawners follow the same template format to keep things consistent and unison.

```dm
/obj/effect/spawner/random/INSERT_SPAWNER_GROUP/INSERT_SPAWNER_NAME
	name = "INSERT_SPAWNER_NAME spawner"
	loot = list(
		/obj/item/PATH/INSERT_OBJ_1,
		/obj/item/PATH/INSERT_OBJ_2,
		/obj/item/PATH/INSERT_OBJ_3,
	)
```

All the capitalized code is the parts where you are supposed to swap out with your objects like so:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture,
		/obj/item/stack/medical/mesh,
		/obj/item/stack/medical/gauze,
	)
```

Find the path to different objects and add them to the list but try to be consistent with the types of the object and the spawner.  For example a medical spawner shouldn't have a emag in the loot list. (use an antag spawner for that instead!)

## Probability

Be aware that the `loot` list uses a *weighted chance* formula to determine probability.  So if there are no numbers set in the `loot` list then each object defaults to 1 and has the same probability to be selected. For our above example for the `minor_healing` spawner each medical item has a 1/3 chance to be spawned.  But if we rearranged the values to this:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture = 2,
		/obj/item/stack/medical/mesh = 1,
		/obj/item/stack/medical/gauze = 1,
	)
```

Then now `suture` has a 50% chance of being spawned (2/4), `mesh` has a 25% chance of being spawned (1/4), and `gauze` also has a 25% chance of being spawned (1/4).  If we add another item into the mix then we get the following:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture = 2,
		/obj/item/stack/medical/mesh = 1,
		/obj/item/stack/medical/gauze = 1,
		/obj/item/reagent_containers/syringe = 1,
	)
```

Suture is 40% (2/5), Mesh is 20% (1/5), Gauze is 20% (1/5), and Syringe is 20% (1/5).  A weighted list has the advantage of not needing to update every item in the list when adding a new item.  If the list was based on a straight percent values, then each new item would require to manually go and edit ALL the items in the list.  For big lists that would become very tedious.  This is why we use weighted lists to determine probability!

## Style

Here are some simple guidelines that you should stick to when making a new spawner:

### If ALL the items have the same chance, we should not set a weighted value to the item

Do not put `/obj/item/ = 1` unless other items have different spawn chances

Good:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture = 2,
		/obj/item/stack/medical/mesh = 1,
		/obj/item/stack/medical/gauze = 1,
		/obj/item/reagent_containers/syringe = 1,
	)
```

Also Good:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture,
		/obj/item/stack/medical/mesh,
		/obj/item/stack/medical/gauze,
		/obj/item/reagent_containers/syringe,
	)
```

Bad:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture = 1,
		/obj/item/stack/medical/mesh = 1,
		/obj/item/stack/medical/gauze = 1,
		/obj/item/reagent_containers/syringe = 1,
	)
```

### Sort the list from highest probability to lowest

Sort from top to bottom.  The rarest items for your spawner should be at the bottom of the list.

Good:

```dm
/obj/effect/spawner/random/contraband/armory
	name = "armory loot spawner"
	loot = list(
		/obj/item/gun/ballistic/automatic/pistol = 8,
		/obj/item/gun/ballistic/shotgun/automatic/combat = 5,
		/obj/item/storage/box/syndie_kit/throwing_weapons = 3,
		/obj/item/grenade/clusterbuster/teargas = 2,
		/obj/item/grenade/clusterbuster = 2,
		/obj/item/gun/ballistic/automatic/pistol/deagle = 1,
		/obj/item/gun/ballistic/revolver/mateba = 1,
	)
```

Bad:

```dm
/obj/effect/spawner/random/contraband/armory
	name = "armory loot spawner"
	loot = list(
		/obj/item/storage/box/syndie_kit/throwing_weapons = 3,
		/obj/item/gun/ballistic/automatic/pistol = 8,
		/obj/item/gun/ballistic/revolver/mateba = 1,
		/obj/item/grenade/clusterbuster/teargas = 2,
		/obj/item/gun/ballistic/automatic/pistol/deagle = 1,
		/obj/item/grenade/clusterbuster = 2,
		/obj/item/gun/ballistic/shotgun/automatic/combat = 5,
	)
```

### Always put the `loot` list at the bottom of your spawner

This is just to keep things organized.

Good:

```dm
/obj/effect/spawner/random/food_or_drink/donkpockets
	name = "donk pocket box spawner"
	spawn_loot_double = FALSE
	loot = list(
		/obj/item/storage/box/donkpockets/donkpocketspicy,
		/obj/item/storage/box/donkpockets/donkpocketteriyaki,
		/obj/item/storage/box/donkpockets/donkpocketpizza,
		/obj/item/storage/box/donkpockets/donkpocketberry,
		/obj/item/storage/box/donkpockets/donkpockethonk,
	)
```

Bad:

```dm
/obj/effect/spawner/random/food_or_drink/donkpockets
	name = "donk pocket box spawner"
	loot = list(
		/obj/item/storage/box/donkpockets/donkpocketspicy,
		/obj/item/storage/box/donkpockets/donkpocketteriyaki,
		/obj/item/storage/box/donkpockets/donkpocketpizza,
		/obj/item/storage/box/donkpockets/donkpocketberry,
		/obj/item/storage/box/donkpockets/donkpockethonk,
	)
	spawn_loot_double = FALSE
```

### Always put a comma at the last item in the `loot` list

This will make it easier for people to add items to your spawner later without getting frustrating code errors.

Good:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture,
		/obj/item/stack/medical/mesh,
		/obj/item/stack/medical/gauze,
	)
```

Bad:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture,
		/obj/item/stack/medical/mesh,
		/obj/item/stack/medical/gauze  // if someone adds an item to the list later it will cause an error
	)
```

### Keep the same tab formatting for the `loot` list (unless there is only one item)

Again, this is just good code organization.  If there is only one item, then encase that item in `loot = list(item)`

Good:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
		/obj/item/stack/medical/suture,
		/obj/item/stack/medical/mesh,
		/obj/item/stack/medical/gauze,
	)
```

Also Good:

```dm
/obj/effect/spawner/random/structure/crate_abandoned
	icon = 'icons/effects/landmarks_static.dmi'
	icon_state = "loot_site"
	spawn_loot_chance = 20
	loot = list(/obj/structure/closet/crate/secure/loot)
```

Bad:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(
	/obj/item/stack/medical/suture,
	/obj/item/stack/medical/mesh,
	/obj/item/stack/medical/gauze,
	)
```

Also Bad:

```dm
/obj/effect/spawner/random/medical/minor_healing
	name = "minor healing spawner"
	loot = list(/obj/item/stack/medical/suture,
		/obj/item/stack/medical/mesh,
		/obj/item/stack/medical/gauze,)
```

### Try to keep the total combined weight of your `loot` list to sane values (Aim for 5, 10, 20, 50, or 100)

This makes the math probability easier for people to calculate.  (this is recommended, but not always possible)

Good:

```dm
/obj/effect/spawner/random/trash/cigbutt
	name = "cigarette butt spawner"
	loot = list(
		/obj/item/cigbutt = 65,
		/obj/item/cigbutt/roach = 20,
		/obj/item/cigbutt/cigarbutt = 15,
	)
```

Also Good:

```dm
/obj/effect/spawner/random/trash/botanical_waste
	name = "botanical waste spawner"
	loot = list(
		/obj/item/grown/bananapeel = 6,
		/obj/item/grown/corncob = 3,
		/obj/item/food/grown/bungopit = 1,
	)
```

Bad:

```dm
/obj/effect/spawner/random/entertainment/money_large
	name = "large money spawner"
	loot = list(
		/obj/item/stack/spacecash/c1 = 521,
		/obj/item/stack/spacecash/c10 = 378,
		/obj/item/stack/spacecash/c20 = 212,
		/obj/item/stack/spacecash/c50 = 205,
		/obj/item/stack/spacecash/c100 = 71,
		/obj/item/stack/spacecash/c200 = 60,
		/obj/item/stack/spacecash/c500 = 57,
		/obj/item/stack/spacecash/c1000 = 41,
		/obj/item/stack/spacecash/c10000 = 12,
	)
```

### Do not put empty items in the loot list

Instead use the `spawn_loot_chance` var to control the chance for the spawner to spawn nothing.

Good:

```dm
/obj/effect/spawner/random/structure/crate_abandoned
	name = "locked crate spawner"
	spawn_loot_chance = 20
	loot = list(/obj/structure/closet/crate/secure/loot)
```

Bad:

```dm
/obj/effect/spawner/lootdrop/crate_spawner
	name = "lootcrate spawner"
	loot = list(
		"" = 80
		/obj/structure/closet/crate/secure/loot = 20,
	)
```

### Avoid making a spawner that is a duplicate

We don't want copy-cat spawners that are almost identical.  Instead merge spawners together if possible.

Good:

```dm
/obj/effect/spawner/random/contraband/armory
	name = "armory loot spawner"
	icon_state = "pistol"
	loot = list(
		/obj/item/gun/ballistic/automatic/pistol = 8,
		/obj/item/gun/ballistic/shotgun/automatic/combat = 5,
		/obj/item/storage/box/syndie_kit/throwing_weapons = 3,
		/obj/item/grenade/clusterbuster/teargas = 2,
		/obj/item/grenade/clusterbuster = 2,
		/obj/item/gun/ballistic/automatic/pistol/deagle,
		/obj/item/gun/ballistic/revolver/mateba,
	)
```

Bad:

```dm
/obj/effect/spawner/lootdrop/armory_contraband
	loot = list(
		/obj/item/gun/ballistic/automatic/pistol = 8,
		/obj/item/gun/ballistic/shotgun/automatic/combat = 5,
		/obj/item/gun/ballistic/automatic/pistol/deagle,
		/obj/item/gun/ballistic/revolver/mateba
	)

/obj/effect/spawner/lootdrop/armory_contraband/metastation
	loot = list(
		/obj/item/gun/ballistic/automatic/pistol = 8,
		/obj/item/gun/ballistic/shotgun/automatic/combat = 5,
		/obj/item/storage/box/syndie_kit/throwing_weapons = 3,
		/obj/item/gun/ballistic/automatic/pistol/deagle,
		/obj/item/gun/ballistic/revolver/mateba
	)

```

repo_name: AnalWerewolf/sundown

license: mit


code:
/obj/effect/spawner/random/structure
	name = "structure spawner"
	desc = "Now you see me, now you don't..."

/obj/effect/spawner/random/structure/crate
	name = "crate spawner"
	icon_state = "crate"
	loot = list(
		/obj/effect/spawner/random/structure/crate_loot = 745,
		/obj/structure/closet/crate/trashcart/filled = 75,
		/obj/effect/spawner/random/trash/moisture_trap = 50,
		/obj/effect/spawner/random/trash/hobo_squat = 30,
		/obj/structure/closet/mini_fridge = 35,
		/obj/effect/spawner/random/trash/mess = 30,
		/obj/item/kirbyplants/fern = 20,
		/obj/structure/closet/crate/decorations = 15,
	)

/obj/effect/spawner/random/structure/crate_abandoned
	name = "locked crate spawner"
	icon_state = "crate_secure"
	spawn_loot_chance = 20
	loot = list(/obj/structure/closet/crate/secure/loot)

/obj/effect/spawner/random/structure/girder
	name = "girder spawner"
	icon_state = "girder"
	loot = list(
		/obj/structure/girder = 4,
		/obj/structure/girder/displaced = 1,
	)

/obj/effect/spawner/random/structure/grille
	name = "grille spawner"
	icon_state = "grille"
	loot = list(
		/obj/structure/grille = 4,
		/obj/structure/grille/broken = 1,
	)

/obj/effect/spawner/random/structure/furniture_parts
	name = "furniture parts spawner"
	icon_state = "table_parts"
	loot = list(
		/obj/structure/table_frame,
		/obj/structure/table_frame/wood,
		/obj/item/rack_parts,
	)

/obj/effect/spawner/random/structure/table_or_rack
	name = "table or rack spawner"
	icon_state = "rack_parts"
	loot = list(
		/obj/effect/spawner/random/structure/table,
		/obj/structure/rack,
	)

/obj/effect/spawner/random/structure/table
	name = "table spawner"
	icon_state = "table"
	loot = list(
		/obj/structure/table = 40,
		/obj/structure/table/wood = 30,
		/obj/structure/table/glass = 20,
		/obj/structure/table/reinforced = 5,
		/obj/structure/table/wood/poker = 5,
	)

/obj/effect/spawner/random/structure/table_fancy
	name = "table spawner"
	icon_state = "table_fancy"
	loot_type_path = /obj/structure/table/wood/fancy
	loot = list()

/obj/effect/spawner/random/structure/tank_holder
	name = "tank holder spawner"
	icon_state = "tank_holder"
	loot = list(
		/obj/structure/tank_holder/oxygen = 40,
		/obj/structure/tank_holder/extinguisher = 40,
		/obj/structure/tank_holder = 20,
		/obj/structure/tank_holder/extinguisher/advanced = 1,
	)

/obj/effect/spawner/random/structure/crate_empty
	name = "empty crate spawner"
	icon_state = "crate"
	loot = list(
		/obj/structure/closet/crate = 20,
		/obj/structure/closet/crate/wooden = 1,
		/obj/structure/closet/crate/internals = 1,
		/obj/structure/closet/crate/medical = 1,
		/obj/structure/closet/crate/freezer = 1,
		/obj/structure/closet/crate/radiation = 1,
		/obj/structure/closet/crate/hydroponics = 1,
		/obj/structure/closet/crate/engineering = 1,
		/obj/structure/closet/crate/engineering/electrical = 1,
		/obj/structure/closet/crate/science = 1,
	)

/obj/effect/spawner/random/structure/crate_loot
	name = "lootcrate spawner"
	icon_state = "crate"
	loot = list(
		/obj/structure/closet/crate/maint = 15,
		/obj/effect/spawner/random/structure/crate_empty = 4,
		/obj/structure/closet/crate/secure/loot = 1,
	)

/obj/effect/spawner/random/structure/closet_private
	name = "private closet spawner"
	icon_state = "cabinet"
	loot = list(
		/obj/structure/closet/secure_closet/personal,
		/obj/structure/closet/secure_closet/personal/cabinet,
	)

/obj/effect/spawner/random/structure/closet_empty
	name = "empty closet spawner"
	icon_state = "locker"
	loot = list(
		/obj/structure/closet = 850,
		/obj/structure/closet/cabinet = 150,
		/obj/structure/closet/acloset = 1,
	)

/obj/effect/spawner/random/structure/closet_maintenance
	name = "maintenance closet spawner"
	icon_state = "locker"
	loot = list( // use these for maintenance areas
		/obj/effect/spawner/random/structure/closet_empty = 10,
		/obj/structure/closet/emcloset = 2,
		/obj/structure/closet/firecloset = 2,
		/obj/structure/closet/toolcloset = 2,
		/obj/structure/closet/l3closet = 1,
		/obj/structure/closet/radiation = 1,
		/obj/structure/closet/bombcloset = 1,
		/obj/structure/closet/mini_fridge = 1,
	)

/obj/effect/spawner/random/structure/chair_flipped
	name = "flipped chair spawner"
	icon_state = "chair"
	loot = list(
		/obj/item/chair/wood,
		/obj/item/chair/stool/bar,
		/obj/item/chair/stool,
		/obj/item/chair,
	)

/obj/effect/spawner/random/structure/chair_comfy
	name = "comfy chair spawner"
	icon_state = "chair"
	loot_type_path = /obj/structure/chair/comfy
	loot = list()

/obj/effect/spawner/random/structure/chair_maintenance
	name = "maintenance chair spawner"
	icon_state = "chair"
	loot = list(
		/obj/structure/chair = 200,
		/obj/structure/chair/stool = 200,
		/obj/structure/chair/stool/bar = 200,
		/obj/effect/spawner/random/structure/chair_flipped = 150,
		/obj/structure/chair/wood = 100,
		/obj/effect/spawner/random/structure/chair_comfy = 50,
		/obj/structure/chair/office/light = 50,
		/obj/structure/chair/office = 50,
		/obj/structure/chair/wood/wings = 1,
		/obj/structure/chair/old = 1,
	)

/obj/effect/spawner/random/structure/barricade
	name = "barricade spawner"
	icon_state = "barricade"
	loot = list(
		/obj/structure/barricade/wooden,
		/obj/structure/barricade/wooden/crude,
	)


code:
// Tech storage circuit board spawners
/obj/effect/spawner/random/techstorage
	name = "generic circuit board spawner"
	icon_state = "circuit"
	spawn_loot_split = TRUE
	spawn_all_loot = TRUE

/obj/effect/spawner/random/techstorage/data_disk
	name = "data disk spawner"
	icon_state = "disk"
	spawn_all_loot = FALSE
	loot = list(
		/obj/item/disk/data = 49,
		/obj/item/disk/nuclear/fake/obvious = 1,
	)

/obj/effect/spawner/random/techstorage/arcade_boards
	name = "arcade board spawner"
	spawn_all_loot = FALSE
	spawn_loot_count = 1
	loot = list(
		/obj/item/circuitboard/computer/arcade/amputation,
		/obj/item/circuitboard/computer/arcade/battle,
		/obj/item/circuitboard/computer/arcade/orion_trail,
	)

/obj/effect/spawner/random/techstorage/service_all
	name = "service circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/arcade/battle,
		/obj/item/circuitboard/computer/arcade/orion_trail,
		/obj/item/circuitboard/machine/autolathe,
		/obj/item/circuitboard/computer/mining,
		/obj/item/circuitboard/machine/ore_redemption,
		/obj/item/circuitboard/machine/mining_equipment_vendor,
		/obj/item/circuitboard/machine/microwave,
		/obj/item/circuitboard/machine/chem_dispenser/drinks,
		/obj/item/circuitboard/machine/chem_dispenser/drinks/beer,
		/obj/item/circuitboard/computer/slot_machine,
	)

/obj/effect/spawner/random/techstorage/rnd_all
	name = "RnD circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/aifixer,
		/obj/item/circuitboard/machine/rdserver,
		/obj/item/circuitboard/machine/mechfab,
		/obj/item/circuitboard/machine/circuit_imprinter/department,
		/obj/item/circuitboard/computer/teleporter,
		/obj/item/circuitboard/machine/destructive_analyzer,
		/obj/item/circuitboard/computer/rdconsole,
		/obj/item/circuitboard/computer/scan_consolenew,
		/obj/item/circuitboard/machine/dnascanner,
	)

/obj/effect/spawner/random/techstorage/security_all
	name = "security circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/secure_data,
		/obj/item/circuitboard/computer/security,
		/obj/item/circuitboard/computer/prisoner,
	)

/obj/effect/spawner/random/techstorage/engineering_all
	name = "engineering circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/atmos_alert,
		/obj/item/circuitboard/computer/stationalert,
		/obj/item/circuitboard/computer/powermonitor,
	)

/obj/effect/spawner/random/techstorage/tcomms_all
	name = "tcomms circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/message_monitor,
		/obj/item/circuitboard/machine/telecomms/broadcaster,
		/obj/item/circuitboard/machine/telecomms/bus,
		/obj/item/circuitboard/machine/telecomms/server,
		/obj/item/circuitboard/machine/telecomms/receiver,
		/obj/item/circuitboard/machine/telecomms/processor,
		/obj/item/circuitboard/machine/announcement_system,
		/obj/item/circuitboard/computer/comm_server,
		/obj/item/circuitboard/computer/comm_monitor,
	)

/obj/effect/spawner/random/techstorage/medical_all
	name = "medical circuit board spawner"
	loot = list(
		/obj/item/circuitboard/machine/chem_dispenser,
		/obj/item/circuitboard/computer/med_data,
		/obj/item/circuitboard/machine/smoke_machine,
		/obj/item/circuitboard/machine/chem_master,
		/obj/item/circuitboard/computer/pandemic,
	)

/obj/effect/spawner/random/techstorage/ai_all
	name = "secure AI circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/aiupload,
		/obj/item/circuitboard/computer/borgupload,
		/obj/item/circuitboard/aicore,
	)

/obj/effect/spawner/random/techstorage/command_all
	name = "secure command circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/crew,
		/obj/item/circuitboard/computer/communications,
	)

/obj/effect/spawner/random/techstorage/rnd_secure_all
	name = "secure RnD circuit board spawner"
	loot = list(
		/obj/item/circuitboard/computer/mecha_control,
		/obj/item/circuitboard/computer/apc_control,
		/obj/item/circuitboard/computer/robotics,
	)


code:
/obj/effect/spawner/random/trash
	name = "trash spawner"
	desc = "Ewwwwwww gross."
	icon_state = "trash"

/obj/effect/spawner/random/trash/garbage
	name = "garbage spawner"
	loot = list(
		/obj/effect/spawner/random/trash/food_packaging = 56,
		/obj/item/trash/can = 8,
		/obj/item/shard = 8,
		/obj/effect/spawner/random/trash/botanical_waste = 8,
		/obj/effect/spawner/random/trash/cigbutt = 8,
		/obj/item/reagent_containers/syringe = 5,
		/obj/item/light/tube/broken = 3,
		/obj/item/food/deadmouse = 2,
		/obj/item/light/tube/broken = 1,
		/obj/item/trash/candle = 1,
	)

/obj/effect/spawner/random/trash/cigbutt
	name = "cigarette butt spawner"
	loot = list(
		/obj/item/cigbutt = 50,
		/obj/item/cigbutt/roach = 30,
		/obj/item/food/candy_trash = 10,
		/obj/item/cigbutt/cigarbutt = 10,
	)

/obj/effect/spawner/random/trash/food_packaging
	name = "empty food packaging spawner"
	loot = list(
		/obj/item/trash/raisins = 2,
		/obj/item/trash/cheesie = 2,
		/obj/item/trash/candy = 2,
		/obj/item/trash/chips = 2,
		/obj/item/trash/sosjerky = 2,
		/obj/item/trash/pistachios = 2,
		/obj/item/trash/peanuts = 2,
		/obj/item/trash/boritos = 1,
		/obj/item/trash/can/food/beans = 1,
		/obj/item/trash/popcorn = 1,
		/obj/item/trash/energybar = 1,
		/obj/item/trash/can/food/peaches/maint = 1,
		/obj/item/trash/semki = 1,
		/obj/item/trash/cnds = 1,
		/obj/item/trash/syndi_cakes = 1,
	)

/obj/effect/spawner/random/trash/botanical_waste
	name = "botanical waste spawner"
	icon_state = "peel"
	loot = list(
		/obj/item/grown/bananapeel = 6,
		/obj/item/grown/corncob = 3,
		/obj/item/food/grown/bungopit = 1,
	)

/obj/effect/spawner/random/trash/grille_or_waste
	name = "grille or waste spawner"
	icon_state = "grille"
	loot = list(
		/obj/structure/grille = 5,
		/obj/effect/spawner/random/trash/food_packaging = 3,
		/obj/effect/spawner/random/trash/cigbutt = 1,
		/obj/item/food/deadmouse = 1,
	)

/obj/effect/spawner/random/trash/hobo_squat
	name = "hobo squat spawner"
	icon_state = "dirty_mattress"
	spawn_all_loot = TRUE
	loot = list(
		/obj/structure/bed/maint,
		/obj/effect/spawner/random/trash/grime,
		/obj/effect/spawner/random/entertainment/drugs,
	)

/obj/effect/spawner/random/trash/moisture_trap
	name = "moisture trap spawner"
	icon_state = "moisture_trap"
	spawn_all_loot = TRUE
	loot = list(
		/obj/effect/spawner/random/trash/moisture,
		/obj/structure/moisture_trap,
	)

/obj/effect/spawner/random/trash/mess
	name = "gross decal spawner"
	icon_state = "vomit"
	loot = list(
		/obj/effect/decal/cleanable/dirt = 6,
		/obj/effect/decal/cleanable/garbage = 3,
		/obj/effect/decal/cleanable/vomit/old = 3,
		/obj/effect/decal/cleanable/blood/gibs/old = 3,
		/obj/effect/decal/cleanable/insectguts = 1,
		/obj/effect/decal/cleanable/greenglow/ecto = 1,
		/obj/effect/decal/cleanable/wrapping = 1,
		/obj/effect/decal/cleanable/plastic = 1,
		/obj/effect/decal/cleanable/glass = 1,
		/obj/effect/decal/cleanable/ants = 1,
	)

/obj/effect/spawner/random/trash/grime
	name = "trash and grime spawner"
	spawn_loot_count = 5
	spawn_scatter_radius = 2
	loot = list( // This spawner will scatter garbage around a dirty site.
		/obj/effect/spawner/random/trash/garbage = 6,
		/mob/living/basic/cockroach = 5,
		/obj/effect/decal/cleanable/garbage = 4,
		/obj/effect/decal/cleanable/vomit/old = 3,
		/obj/effect/spawner/random/trash/cigbutt = 2,
	)

/obj/effect/spawner/random/trash/moisture
	name = "water hazard spawner"
	icon_state = "caution"
	spawn_loot_count = 2
	spawn_scatter_radius = 1
	loot = list( // This spawner will scatter water related items around a moist site.
		/obj/item/clothing/head/cone = 7,
		/obj/item/clothing/suit/caution = 3,
		/mob/living/simple_animal/hostile/retaliate/frog = 2,
		/obj/item/reagent_containers/glass/rag = 2,
		/obj/item/reagent_containers/glass/bucket = 2,
		/obj/effect/decal/cleanable/blood/old = 2,
		/obj/structure/mopbucket = 2,
	)

/obj/effect/spawner/random/trash/graffiti
	name = "random graffiti spawner"
	icon_state = "rune"
	loot = list(/obj/effect/decal/cleanable/crayon)
	var/graffiti_icons = list(
		"rune1", "rune2", "rune3", "rune4", "rune5", "rune6",
		"amyjon", "face", "matt", "revolution", "engie", "guy",
		"end", "dwarf", "uboa", "body", "cyka", "star", "poseur tag",
		"prolizard", "antilizard", "danger", "firedanger", "electricdanger",
		"biohazard", "radiation", "safe", "evac", "space", "med", "trade", "shop",
		"food", "peace", "like", "skull", "nay", "heart", "credit",
		"smallbrush", "brush", "largebrush", "splatter", "snake", "stickman",
		"carp", "ghost", "clown", "taser", "disk", "fireaxe", "toolbox",
		"corgi", "cat", "toilet", "blueprint", "beepsky", "scroll", "bottle",
		"shotgun", "arrow", "line", "thinline", "shortline", "body", "chevron",
		"footprint", "clawprint", "pawprint",
	)
	color = COLOR_WHITE //sets the color of the graffiti (used for mapedits)
	var/random_color = TRUE //whether the graffiti will spawn with a random color (used for mapedits)
	var/random_icon = TRUE // whether the graffiti will spawn with the same icon

/obj/effect/spawner/random/trash/graffiti/proc/select_graffiti(graffiti_decal)
	var/obj/effect/decal/cleanable/crayon/decal = graffiti_decal
	color = random_color && "#[random_short_color()]" || color
	icon_state = random_icon && pick(graffiti_icons) || icon_state

	decal.add_atom_colour(color, FIXED_COLOUR_PRIORITY)
	decal.icon_state = icon_state

/obj/effect/spawner/random/trash/mopbucket
	name = "mop bucket spawner"
	icon_state = "mopbucket"
	spawn_loot_count = 2
	spawn_loot_double = FALSE
	loot = list(
		/obj/structure/mopbucket = 10,
		/obj/item/mop = 5,
		/obj/item/clothing/suit/caution = 3,
		/obj/item/reagent_containers/glass/bucket = 1,
		/obj/item/reagent_containers/glass/bucket/wooden = 1,
	)

/obj/effect/spawner/random/trash/caution_sign
	name = "caution sign spawner"
	icon_state = "caution"
	loot = list(
		/obj/item/clothing/suit/caution = 40,
		/obj/structure/holosign/wetsign = 5,
		/obj/structure/holosign/barrier = 3,
		/obj/structure/holosign/barrier/wetsign = 2,
	)

/obj/effect/spawner/random/structure/bucket
	name = "bucket spawner"
	icon_state = "caution"
	loot = list(
		/obj/item/reagent_containers/glass/bucket,
		/obj/item/reagent_containers/glass/bucket/wooden,
	)

/obj/effect/spawner/random/trash/soap
	name = "soap spawner"
	icon_state = "soap"
	loot = list(
		/obj/item/soap = 25,
		/obj/item/bikehorn/rubberducky = 20,
		/obj/item/soap/homemade = 20,
		/obj/item/soap/deluxe = 15,
		/obj/item/soap/nanotrasen = 10,
		/obj/item/food/urinalcake = 5,
		/obj/item/bikehorn/rubberducky/plasticducky = 5,
	)

/obj/effect/spawner/random/trash/box
	name = "box spawner"
	icon_state = "box"
	loot = list(
		/obj/structure/closet/cardboard = 9,
		/obj/structure/closet/cardboard/metal = 1,
	)

/obj/effect/spawner/random/trash/bin
	name = "trashbin spawner"
	icon_state = "trash_bin"
	loot = list(
		/obj/structure/closet/crate/bin = 10,
		/obj/structure/closet/crate/trashcart = 3,
		/obj/structure/closet/crate/trashcart/filled = 3,
		/obj/effect/spawner/random/trash/box = 3,
		/obj/structure/closet/crate/trashcart/laundry = 1,
	)

/obj/effect/spawner/random/trash/janitor_supplies
	name = "janitor supplies spawner"
	icon_state = "box_small"
	loot = list(
		/obj/item/storage/box/mousetraps,
		/obj/item/storage/box/lights/tubes,
		/obj/item/storage/box/lights/mixed,
		/obj/item/storage/box/lights/bulbs,
	)


code:
/obj/effect/spawner/random/vending
	name = "machinery spawner"
	desc = "Randomized electronics for extra fun."
	var/hacked = FALSE //whether it hacks the vendor on spawn (only used for mapedits)

/obj/effect/spawner/random/vending/Initialize(mapload)
	. = ..()
	if(istype(., /obj/machinery/vending))
		var/obj/machinery/vending/vending = .
		vending.extended_inventory = hacked

	return INITIALIZE_HINT_QDEL

/obj/effect/spawner/random/vending/snackvend
	name = "spawn random snack vending machine"
	desc = "Automagically transforms into a random snack vendor. If you see this while in a shift, please create a bug report."
	icon_state = "snack"
	loot_type_path = /obj/machinery/vending/snack
	loot = list()

/obj/effect/spawner/random/vending/colavend
	name = "spawn random cola vending machine"
	desc = "Automagically transforms into a random cola vendor. If you see this while in a shift, please create a bug report."
	icon_state = "cola"
	loot_type_path = /obj/machinery/vending/cola
	loot = list()


code:
/*
Because mapping is already tedious enough this spawner let you spawn generic
"sets" of objects rather than having to make the same object stack again and
again.
*/

/obj/effect/spawner/structure
	name = "map structure spawner"
	var/list/spawn_list

/obj/effect/spawner/structure/Initialize(mapload)
	. = ..()
	if(spawn_list?.len)
		for(var/I in spawn_list)
			new I(get_turf(src))
	return INITIALIZE_HINT_QDEL

//normal windows

/obj/effect/spawner/structure/window
	icon = 'icons/obj/structures_spawners.dmi'
	icon_state = "window_spawner"
	name = "window spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/fulltile)
	dir = SOUTH

/obj/effect/spawner/structure/window/Initialize(mapload)
	. = ..()

	if (is_station_level(z))
		var/turf/current_turf = get_turf(src)
		current_turf.rcd_memory = RCD_MEMORY_WINDOWGRILLE

/obj/effect/spawner/structure/window/hollow
	name = "hollow window spawner"
	icon_state = "hwindow_spawner_full"
	spawn_list = list(/obj/structure/grille, /obj/structure/window, /obj/structure/window/spawner/north, /obj/structure/window/spawner/east, /obj/structure/window/spawner/west)

/obj/effect/spawner/structure/window/hollow/end
	icon_state = "hwindow_spawner_end"

/obj/effect/spawner/structure/window/hollow/end/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/spawner/north, /obj/structure/window/spawner/east, /obj/structure/window/spawner/west)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window, /obj/structure/window/spawner/north, /obj/structure/window/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window, /obj/structure/window/spawner/east, /obj/structure/window/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window, /obj/structure/window/spawner/north, /obj/structure/window/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/middle
	icon_state = "hwindow_spawner_middle"

/obj/effect/spawner/structure/window/hollow/middle/Initialize(mapload)
	switch(dir)
		if(NORTH,SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window, /obj/structure/window/spawner/north)
		if(EAST,WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/spawner/east, /obj/structure/window/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/directional
	icon_state = "hwindow_spawner_directional"

/obj/effect/spawner/structure/window/hollow/directional/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/spawner/north)
		if(NORTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/spawner/north, /obj/structure/window/spawner/east)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/spawner/east)
		if(SOUTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window, /obj/structure/window/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window)
		if(SOUTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window, /obj/structure/window/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/spawner/west)
		if(NORTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/spawner/north, /obj/structure/window/spawner/west)
	. = ..()

//reinforced

/obj/effect/spawner/structure/window/reinforced
	name = "reinforced window spawner"
	icon_state = "rwindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/fulltile)

/obj/effect/spawner/structure/window/hollow/reinforced
	name = "hollow reinforced window spawner"
	icon_state = "hrwindow_spawner_full"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced, /obj/structure/window/reinforced/spawner/north, /obj/structure/window/reinforced/spawner/east, /obj/structure/window/reinforced/spawner/west)

/obj/effect/spawner/structure/window/hollow/reinforced/end
	icon_state = "hrwindow_spawner_end"

/obj/effect/spawner/structure/window/hollow/reinforced/end/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/north, /obj/structure/window/reinforced/spawner/east, /obj/structure/window/reinforced/spawner/west)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced, /obj/structure/window/reinforced/spawner/north, /obj/structure/window/reinforced/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced, /obj/structure/window/reinforced/spawner/east, /obj/structure/window/reinforced/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced, /obj/structure/window/reinforced/spawner/north, /obj/structure/window/reinforced/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/reinforced/middle
	icon_state = "hrwindow_spawner_middle"

/obj/effect/spawner/structure/window/hollow/reinforced/middle/Initialize(mapload)
	switch(dir)
		if(NORTH,SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced, /obj/structure/window/reinforced/spawner/north)
		if(EAST,WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/east, /obj/structure/window/reinforced/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/reinforced/directional
	icon_state = "hrwindow_spawner_directional"

/obj/effect/spawner/structure/window/hollow/reinforced/directional/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/north)
		if(NORTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/north, /obj/structure/window/reinforced/spawner/east)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/east)
		if(SOUTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced, /obj/structure/window/reinforced/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced)
		if(SOUTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced, /obj/structure/window/reinforced/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/west)
		if(NORTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/north, /obj/structure/window/reinforced/spawner/west)
	. = ..()

//tinted

/obj/effect/spawner/structure/window/reinforced/tinted
	name = "tinted reinforced window spawner"
	icon_state = "twindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/tinted/fulltile)

//shuttle window

/obj/effect/spawner/structure/window/reinforced/shuttle
	name = "shuttle window spawner"
	icon_state = "swindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle)

//plastitanium window

/obj/effect/spawner/structure/window/reinforced/plasma/plastitanium
	name = "plastitanium window spawner"
	icon_state = "plastitaniumwindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/plastitanium)

//ice window

/obj/effect/spawner/structure/window/ice
	name = "ice window spawner"
	icon_state = "icewindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/fulltile/ice)

//survival pod window

/obj/effect/spawner/structure/window/survival_pod
	name = "pod window spawner"
	icon_state = "podwindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod)

/obj/effect/spawner/structure/window/hollow/survival_pod
	name = "hollow pod window spawner"
	icon_state = "podwindow_spawner_full"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)

/obj/effect/spawner/structure/window/hollow/survival_pod/end
	icon_state = "podwindow_spawner_end"

/obj/effect/spawner/structure/window/hollow/survival_pod/end/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/survival_pod/middle
	icon_state = "podwindow_spawner_middle"

/obj/effect/spawner/structure/window/hollow/survival_pod/middle/Initialize(mapload)
	switch(dir)
		if(NORTH,SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north)
		if(EAST,WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/survival_pod/directional
	icon_state = "podwindow_spawner_directional"

/obj/effect/spawner/structure/window/hollow/survival_pod/directional/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north)
		if(NORTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east)
		if(SOUTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod)
		if(SOUTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)
		if(NORTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/north, /obj/structure/window/reinforced/shuttle/survival_pod/spawner/west)
	. = ..()

//plasma windows

/obj/effect/spawner/structure/window/plasma
	name = "plasma window spawner"
	icon_state = "pwindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/fulltile)

/obj/effect/spawner/structure/window/hollow/plasma
	name = "hollow plasma window spawner"
	icon_state = "phwindow_spawner_full"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma, /obj/structure/window/plasma/spawner/north, /obj/structure/window/plasma/spawner/east, /obj/structure/window/plasma/spawner/west)

/obj/effect/spawner/structure/window/hollow/plasma/end
	icon_state = "phwindow_spawner_end"

/obj/effect/spawner/structure/window/hollow/plasma/end/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/spawner/north, /obj/structure/window/plasma/spawner/east, /obj/structure/window/plasma/spawner/west)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma, /obj/structure/window/plasma/spawner/north, /obj/structure/window/plasma/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma, /obj/structure/window/plasma/spawner/east, /obj/structure/window/plasma/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma, /obj/structure/window/plasma/spawner/north, /obj/structure/window/plasma/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/plasma/middle
	icon_state = "phwindow_spawner_middle"

/obj/effect/spawner/structure/window/hollow/plasma/middle/Initialize(mapload)
	switch(dir)
		if(NORTH,SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma, /obj/structure/window/plasma/spawner/north)
		if(EAST,WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/spawner/east, /obj/structure/window/plasma/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/plasma/directional
	icon_state = "phwindow_spawner_directional"

/obj/effect/spawner/structure/window/hollow/plasma/directional/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/spawner/north)
		if(NORTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/spawner/north, /obj/structure/window/plasma/spawner/east)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/spawner/east)
		if(SOUTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma, /obj/structure/window/plasma/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma)
		if(SOUTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma, /obj/structure/window/plasma/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/spawner/west)
		if(NORTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/plasma/spawner/north, /obj/structure/window/plasma/spawner/west)
	. = ..()

//reinforced plasma

/obj/effect/spawner/structure/window/reinforced/plasma
	name = "reinforced plasma window spawner"
	icon_state = "prwindow_spawner"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/fulltile)

/obj/effect/spawner/structure/window/hollow/reinforced/plasma
	name = "hollow reinforced plasma window spawner"
	icon_state = "phrwindow_spawner_full"
	spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma, /obj/structure/window/reinforced/plasma/spawner/north, /obj/structure/window/reinforced/plasma/spawner/east, /obj/structure/window/reinforced/plasma/spawner/west)

/obj/effect/spawner/structure/window/hollow/reinforced/plasma/end
	icon_state = "phrwindow_spawner_end"

/obj/effect/spawner/structure/window/hollow/reinforced/plasma/end/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/spawner/north, /obj/structure/window/reinforced/plasma/spawner/east, /obj/structure/window/reinforced/plasma/spawner/west)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma, /obj/structure/window/reinforced/plasma/spawner/north, /obj/structure/window/reinforced/plasma/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma, /obj/structure/window/reinforced/plasma/spawner/east, /obj/structure/window/reinforced/plasma/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma, /obj/structure/window/reinforced/plasma/spawner/north, /obj/structure/window/reinforced/plasma/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/reinforced/plasma/middle
	icon_state = "phrwindow_spawner_middle"

/obj/effect/spawner/structure/window/hollow/reinforced/plasma/middle/Initialize(mapload)
	switch(dir)
		if(NORTH,SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma, /obj/structure/window/reinforced/plasma/spawner/north)
		if(EAST,WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/spawner/east, /obj/structure/window/reinforced/plasma/spawner/west)
	. = ..()

/obj/effect/spawner/structure/window/hollow/reinforced/plasma/directional
	icon_state = "phrwindow_spawner_directional"

/obj/effect/spawner/structure/window/hollow/reinforced/plasma/directional/Initialize(mapload)
	switch(dir)
		if(NORTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/spawner/north)
		if(NORTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/spawner/north, /obj/structure/window/reinforced/plasma/spawner/east)
		if(EAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/spawner/east)
		if(SOUTHEAST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma, /obj/structure/window/reinforced/plasma/spawner/east)
		if(SOUTH)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma)
		if(SOUTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma, /obj/structure/window/reinforced/plasma/spawner/west)
		if(WEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/spawner/west)
		if(NORTHWEST)
			spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/plasma/spawner/north, /obj/structure/window/reinforced/plasma/spawner/west)
	. = ..()


code:
/obj/effect/spawner/xeno_egg_delivery
	name = "xeno egg delivery"
	icon = 'icons/mob/alien.dmi'
	icon_state = "egg_growing"
	var/announcement_time = 1200

/obj/effect/spawner/xeno_egg_delivery/Initialize(mapload)
	..()
	var/turf/T = get_turf(src)

	new /obj/structure/alien/egg(T)
	new /obj/effect/temp_visual/gravpush(T)
	playsound(T, 'sound/items/party_horn.ogg', 50, TRUE, -1)

	message_admins("An alien egg has been delivered to [ADMIN_VERBOSEJMP(T)].")
	log_game("An alien egg has been delivered to [AREACOORD(T)]")
	var/message = "Attention [station_name()], we have entrusted you with a research specimen in [get_area_name(T, TRUE)]. Remember to follow all safety precautions when dealing with the specimen."
	SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/_addtimer, CALLBACK(GLOBAL_PROC, /proc/print_command_report, message), announcement_time))
	return INITIALIZE_HINT_QDEL


code:
//generic procs copied from obj/effect/alien
/obj/structure/spider
	name = "web"
	icon = 'icons/effects/effects.dmi'
	desc = "It's stringy and sticky."
	anchored = TRUE
	density = FALSE
	max_integrity = 15

/obj/structure/spider/Initialize(mapload)
	. = ..()
	AddElement(/datum/element/atmos_sensitive, mapload)

/obj/structure/spider/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
	if(damage_type == BURN)//the stickiness of the web mutes all attack sounds except fire damage type
		playsound(loc, 'sound/items/welder.ogg', 100, TRUE)

/obj/structure/spider/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
	if(damage_flag == MELEE)
		switch(damage_type)
			if(BURN)
				damage_amount *= 2
			if(BRUTE)
				damage_amount *= 0.25
	. = ..()

/obj/structure/spider/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
	return exposed_temperature > 300

/obj/structure/spider/atmos_expose(datum/gas_mixture/air, exposed_temperature)
	take_damage(5, BURN, 0, 0)

/obj/structure/spider/stickyweb
	///Whether or not the web is from the genetics power
	var/genetic = FALSE
	///Whether or not the web is a sealed web
	var/sealed = FALSE
	icon_state = "stickyweb1"

/obj/structure/spider/stickyweb/attack_hand(mob/user, list/modifiers)
	.= ..()
	if(.)
		return
	if(!HAS_TRAIT(user,TRAIT_WEB_WEAVER))
		return
	user.visible_message(span_notice("[user] begins weaving [src] into cloth."), span_notice("You begin weaving [src] into cloth."))
	if(!do_after(user, 2 SECONDS))
		return
	qdel(src)
	var/obj/item/stack/sheet/cloth/woven_cloth = new /obj/item/stack/sheet/cloth
	user.put_in_hands(woven_cloth)

/obj/structure/spider/stickyweb/Initialize(mapload)
	if(!sealed && prob(50))
		icon_state = "stickyweb2"
	. = ..()

/obj/structure/spider/stickyweb/CanAllowThrough(atom/movable/mover, border_dir)
	. = ..()
	if(genetic)
		return
	if(sealed)
		return FALSE
	if(istype(mover, /mob/living/simple_animal/hostile/giant_spider))
		return TRUE
	else if(isliving(mover))
		if(istype(mover.pulledby, /mob/living/simple_animal/hostile/giant_spider))
			return TRUE
		if(prob(50))
			to_chat(mover, span_danger("You get stuck in \the [src] for a moment."))
			return FALSE
	else if(istype(mover, /obj/projectile))
		return prob(30)

/obj/structure/spider/stickyweb/sealed
	name = "sealed web"
	desc = "A solid thick wall of web, airtight enough to block air flow."
	icon_state = "sealedweb"
	sealed = TRUE
	can_atmos_pass = ATMOS_PASS_NO

/obj/structure/spider/stickyweb/genetic //for the spider genes in genetics
	genetic = TRUE
	var/mob/living/allowed_mob

/obj/structure/spider/stickyweb/genetic/Initialize(mapload, allowedmob)
	allowed_mob = allowedmob
	. = ..()

/obj/structure/spider/stickyweb/genetic/CanAllowThrough(atom/movable/mover, border_dir)
	. = ..() //this is the normal spider web return aka a spider would make this TRUE
	if(mover == allowed_mob)
		return TRUE
	else if(isliving(mover)) //we change the spider to not be able to go through here
		if(mover.pulledby == allowed_mob)
			return TRUE
		if(prob(50))
			to_chat(mover, span_danger("You get stuck in \the [src] for a moment."))
			return FALSE
	else if(istype(mover, /obj/projectile))
		return prob(30)

/obj/structure/spider/eggcluster
	name = "egg cluster"
	desc = "They seem to pulse slightly with an inner life."
	icon_state = "eggs"
	/// Mob spawner handling the actual spawn of the spider
	var/obj/effect/mob_spawn/spider/spawner

/obj/structure/spider/eggcluster/Initialize(mapload)
	pixel_x = base_pixel_x + rand(3,-3)
	pixel_y = base_pixel_y + rand(3,-3)
	return ..()

/obj/structure/spider/eggcluster/Destroy()
	if(spawner)
		QDEL_NULL(spawner)
	return ..()

/obj/structure/spider/eggcluster/attack_ghost(mob/user)
	if(spawner)
		spawner.attack_ghost(user)
	return ..()

/obj/structure/spider/eggcluster/enriched
	name = "enriched egg cluster"
	color = rgb(148, 0, 211)

/obj/structure/spider/eggcluster/bloody
	name = "bloody egg cluster"
	color = rgb(255, 0, 0)

/obj/structure/spider/eggcluster/midwife
	name = "midwife egg cluster"

/obj/effect/mob_spawn/spider
	name = "egg cluster"
	desc = "They seem to pulse slightly with an inner life."
	mob_name = "a spider"
	icon = 'icons/effects/effects.dmi'
	icon_state = "eggs"
	roundstart = FALSE
	death = FALSE
	move_resist = MOVE_FORCE_NORMAL
	density = FALSE
	random = TRUE
	show_flavour = FALSE
	short_desc = "You are a spider."
	important_info = "Follow your directives at all costs."
	faction = list("spiders")
	spawner_job_path = /datum/job/spider
	banType = ROLE_ALIEN
	ready = FALSE
	radial_based = TRUE
	/// The amount the egg cluster has grown.  Is able to produce a spider when it hits 100.
	var/amount_grown = 0
	/// The mother's directive at the time the egg was produced.  Passed onto the child.
	var/directive = ""
	///	Type of the cluster that the spawner spawns
	var/cluster_type = /obj/structure/spider/eggcluster
	/// Physical structure housing the spawner
	var/obj/structure/spider/eggcluster/egg
	/// The types of spiders that the spawner can produce
	var/list/potentialspawns = list(
		/mob/living/simple_animal/hostile/giant_spider,
		/mob/living/simple_animal/hostile/giant_spider/hunter,
		/mob/living/simple_animal/hostile/giant_spider/nurse,
	)

/obj/effect/mob_spawn/spider/Initialize(mapload)
	. = ..()
	START_PROCESSING(SSobj, src)
	potentialspawns = string_list(potentialspawns)
	egg = new cluster_type(get_turf(loc))
	egg.spawner = src
	forceMove(egg)

/obj/effect/mob_spawn/spider/Destroy()
	egg = null
	return ..()

/obj/effect/mob_spawn/spider/process(delta_time)
	amount_grown += rand(0, 1) * delta_time
	if(amount_grown >= 100 && !ready)
		ready = TRUE
		notify_ghosts("[src] is ready to hatch!", null, enter_link = "(Click to play)", source = src, action = NOTIFY_ORBIT, ignore_key = POLL_IGNORE_SPIDER)
		STOP_PROCESSING(SSobj, src)

/obj/effect/mob_spawn/spider/Topic(href, href_list)
	. = ..()
	if(.)
		return
	if(href_list["activate"])
		var/mob/dead/observer/ghost = usr
		if(istype(ghost))
			ghost.ManualFollow(src)
			attack_ghost(ghost)

/obj/effect/mob_spawn/spider/allow_spawn(mob/user)
	. = ..()
	if(!.)
		return FALSE
	if(!ready)
		to_chat(user, span_warning("\The [src] is not ready to hatch yet!"))
		return FALSE

/obj/effect/mob_spawn/spider/equip(mob/living/simple_animal/hostile/giant_spider/spawned_spider)
	if(spawned_spider)
		spawned_spider.directive = directive

/obj/effect/mob_spawn/spider/special(mob/user)
	egg.spawner = null
	QDEL_NULL(egg)

/obj/effect/mob_spawn/spider/enriched
	name = "enriched egg cluster"
	color = rgb(148, 0, 211)
	short_desc = "You are an enriched spider."
	cluster_type = /obj/structure/spider/eggcluster/enriched
	potentialspawns = list(
		/mob/living/simple_animal/hostile/giant_spider/tarantula,
		/mob/living/simple_animal/hostile/giant_spider/viper,
		/mob/living/simple_animal/hostile/giant_spider/midwife,
	)

/obj/effect/mob_spawn/spider/bloody
	name = "bloody egg cluster"
	color = rgb(255, 0, 0)
	short_desc = "You are a bloody spider."
	directive = "You are the spawn of a vicious changeling. You have no ambitions except to wreak havoc and ensure your own survival. You are aggressive to all living beings outside of your species, including changelings."
	cluster_type = /obj/structure/spider/eggcluster/bloody
	potentialspawns = list(
		/mob/living/simple_animal/hostile/giant_spider/hunter/flesh,
	)

/obj/effect/mob_spawn/spider/midwife
	name = "midwife egg cluster"
	short_desc = "You are a midwife spider."
	directive = "Ensure the survival of the spider species and overtake whatever structure you find yourself in."
	cluster_type = /obj/structure/spider/eggcluster/midwife
	potentialspawns = list(
		/mob/living/simple_animal/hostile/giant_spider/midwife,
	)

/**
 * Makes a ghost into a spider based on the type of egg cluster.
 *
 * Allows a ghost to get a prompt to use the egg cluster to become a spider.
 *
 * Arguments:
 * * user - The ghost attempting to become a spider
 * * newname - If set, renames the mob to this name
 */
/obj/effect/mob_spawn/spider/create(mob/user, newname)
	var/list/spider_list = list()
	var/list/display_spiders = list()
	for(var/choice in potentialspawns)
		var/mob/living/simple_animal/hostile/giant_spider/spider = choice
		spider_list[initial(spider.name)] = choice
		var/datum/radial_menu_choice/option = new
		option.image = image(icon = initial(spider.icon), icon_state = initial(spider.icon_state))
		option.info = span_boldnotice("[initial(spider.menu_description)]")
		display_spiders[initial(spider.name)] = option
	sort_list(display_spiders)
	var/chosen_spider = show_radial_menu(user, egg, display_spiders, radius = 38)
	chosen_spider = spider_list[chosen_spider]
	if(QDELETED(src) || QDELETED(user) || !chosen_spider)
		return FALSE
	mob_type = chosen_spider
	return ..()

/obj/structure/spider/spiderling
	name = "spiderling"
	desc = "It never stays still for long."
	icon_state = "spiderling"
	anchored = FALSE
	layer = PROJECTILE_HIT_THRESHHOLD_LAYER
	max_integrity = 3
	var/amount_grown = 0
	var/grow_as = null
	var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent
	var/travelling_in_vent = 0
	var/directive = "" //Message from the mother
	var/list/faction = list("spiders")

/obj/structure/spider/spiderling/Destroy()
	new/obj/item/food/spiderling(get_turf(src))
	. = ..()

/obj/structure/spider/spiderling/Initialize(mapload)
	. = ..()
	pixel_x = rand(6,-6)
	pixel_y = rand(6,-6)
	START_PROCESSING(SSobj, src)
	AddComponent(/datum/component/swarming)

/obj/structure/spider/spiderling/hunter
	grow_as = /mob/living/simple_animal/hostile/giant_spider/hunter

/obj/structure/spider/spiderling/nurse
	grow_as = /mob/living/simple_animal/hostile/giant_spider/nurse

/obj/structure/spider/spiderling/midwife
	grow_as = /mob/living/simple_animal/hostile/giant_spider/midwife

/obj/structure/spider/spiderling/viper
	grow_as = /mob/living/simple_animal/hostile/giant_spider/viper

/obj/structure/spider/spiderling/tarantula
	grow_as = /mob/living/simple_animal/hostile/giant_spider/tarantula

/obj/structure/spider/spiderling/Bump(atom/user)
	if(istype(user, /obj/structure/table))
		forceMove(user.loc)
	else
		..()

/obj/structure/spider/spiderling/proc/cancel_vent_move()
	forceMove(entry_vent)
	entry_vent = null

/obj/structure/spider/spiderling/proc/vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent)
	if(QDELETED(exit_vent) || exit_vent.welded)
		cancel_vent_move()
		return

	forceMove(exit_vent)
	var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
	addtimer(CALLBACK(src, .proc/do_vent_move, exit_vent, travel_time), travel_time)

/obj/structure/spider/spiderling/proc/do_vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent, travel_time)
	if(QDELETED(exit_vent) || exit_vent.welded)
		cancel_vent_move()
		return

	if(prob(50))
		audible_message(span_hear("You hear something scampering through the ventilation ducts."))

	addtimer(CALLBACK(src, .proc/finish_vent_move, exit_vent), travel_time)

/obj/structure/spider/spiderling/proc/finish_vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent)
	if(QDELETED(exit_vent) || exit_vent.welded)
		cancel_vent_move()
		return
	forceMove(exit_vent.loc)
	entry_vent = null

/obj/structure/spider/spiderling/process()
	if(travelling_in_vent)
		if(isturf(loc))
			travelling_in_vent = 0
			entry_vent = null
	else if(entry_vent)
		if(get_dist(src, entry_vent) <= 1)
			var/list/vents = list()
			var/datum/pipeline/entry_vent_parent = entry_vent.parents[1]
			for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in entry_vent_parent.other_atmos_machines)
				vents.Add(temp_vent)
			if(!vents.len)
				entry_vent = null
				return
			var/obj/machinery/atmospherics/components/unary/vent_pump/exit_vent = pick(vents)
			if(prob(50))
				visible_message("[src] scrambles into the ventilation ducts!", \
								span_hear("You hear something scampering through the ventilation ducts."))

			addtimer(CALLBACK(src, .proc/vent_move, exit_vent), rand(20,60))

	//=================

	else if(prob(33))
		var/list/nearby = oview(10, src)
		if(nearby.len)
			var/target_atom = pick(nearby)
			walk_to(src, target_atom)
			if(prob(40))
				src.visible_message(span_notice("\The [src] skitters[pick(" away"," around","")]."))
	else if(prob(10))
		//ventcrawl!
		for(var/obj/machinery/atmospherics/components/unary/vent_pump/v in view(7,src))
			if(!v.welded)
				entry_vent = v
				walk_to(src, entry_vent, 1)
				break
	if(isturf(loc))
		amount_grown += rand(0,2)
		if(amount_grown >= 100)
			if(!grow_as)
				if(prob(3))
					grow_as = pick(/mob/living/simple_animal/hostile/giant_spider/tarantula, /mob/living/simple_animal/hostile/giant_spider/viper, /mob/living/simple_animal/hostile/giant_spider/midwife)
				else
					grow_as = pick(/mob/living/simple_animal/hostile/giant_spider, /mob/living/simple_animal/hostile/giant_spider/hunter, /mob/living/simple_animal/hostile/giant_spider/nurse)
			var/mob/living/simple_animal/hostile/giant_spider/S = new grow_as(src.loc)
			S.faction = faction.Copy()
			S.directive = directive
			qdel(src)

/obj/structure/spider/cocoon
	name = "cocoon"
	desc = "Something wrapped in silky spider web."
	icon_state = "cocoon1"
	max_integrity = 60

/obj/structure/spider/cocoon/Initialize(mapload)
	icon_state = pick("cocoon1","cocoon2","cocoon3")
	. = ..()

/obj/structure/spider/cocoon/container_resist_act(mob/living/user)
	var/breakout_time = 600
	user.changeNext_move(CLICK_CD_BREAKOUT)
	user.last_special = world.time + CLICK_CD_BREAKOUT
	to_chat(user, span_notice("You struggle against the tight bonds... (This will take about [DisplayTimeText(breakout_time)].)"))
	visible_message(span_notice("You see something struggling and writhing in \the [src]!"))
	if(do_after(user,(breakout_time), target = src))
		if(!user || user.stat != CONSCIOUS || user.loc != src)
			return
		qdel(src)

/obj/structure/spider/cocoon/Destroy()
	var/turf/T = get_turf(src)
	src.visible_message(span_warning("\The [src] splits open."))
	for(var/atom/movable/A in contents)
		A.forceMove(T)
	return ..()


code:
/* Simple object type, calls a proc when "stepped" on by something */

/obj/effect/step_trigger
	var/affect_ghosts = 0
	var/stopper = 1 // stops throwers
	var/mobs_only = FALSE
	invisibility = INVISIBILITY_ABSTRACT // nope cant see this shit
	anchored = TRUE

/obj/effect/step_trigger/Initialize(mapload)
	. = ..()
	var/static/list/loc_connections = list(
		COMSIG_ATOM_ENTERED = .proc/on_entered,
	)
	AddElement(/datum/element/connect_loc, loc_connections)

/obj/effect/step_trigger/proc/Trigger(atom/movable/A)
	return 0

/obj/effect/step_trigger/proc/on_entered(datum/source, H as mob|obj)
	SIGNAL_HANDLER
	if(!H)
		return
	if(isobserver(H) && !affect_ghosts)
		return
	if(!ismob(H) && mobs_only)
		return
	INVOKE_ASYNC(src, .proc/Trigger, H)

/obj/effect/step_trigger/singularity_act()
	return

/obj/effect/step_trigger/singularity_pull()
	return

/* Sends a message to mob when triggered*/

/obj/effect/step_trigger/message
	var/message //the message to give to the mob
	var/once = 1
	mobs_only = TRUE

/obj/effect/step_trigger/message/Trigger(mob/M)
	if(M.client)
		to_chat(M, span_info("[message]"))
		if(once)
			qdel(src)

/* Tosses things in a certain direction */

/obj/effect/step_trigger/thrower
	var/direction = SOUTH // the direction of throw
	var/tiles = 3 // if 0: forever until atom hits a stopper
	var/immobilize = 1 // if nonzero: prevents mobs from moving while they're being flung
	var/speed = 1 // delay of movement
	var/facedir = 0 // if 1: atom faces the direction of movement
	var/nostop = 0 // if 1: will only be stopped by teleporters
	var/list/affecting = list()

/obj/effect/step_trigger/thrower/Trigger(atom/A)
	if(!A || !ismovable(A))
		return
	var/atom/movable/AM = A
	var/curtiles = 0
	var/stopthrow = FALSE
	for(var/obj/effect/step_trigger/thrower/T in orange(2, src))
		if(AM in T.affecting)
			return

	if(immobilize)
		ADD_TRAIT(AM, TRAIT_IMMOBILIZED, src)

	affecting.Add(AM)
	while(AM && !stopthrow)
		if(tiles)
			if(curtiles >= tiles)
				break
		if(AM.z != src.z)
			break

		curtiles++

		sleep(speed)

		// Calculate if we should stop the process
		if(!nostop)
			for(var/obj/effect/step_trigger/T in get_step(AM, direction))
				if(T.stopper && T != src)
					stopthrow = TRUE
		else
			for(var/obj/effect/step_trigger/teleporter/T in get_step(AM, direction))
				if(T.stopper)
					stopthrow = TRUE

		if(AM)
			var/predir = AM.dir
			step(AM, direction)
			if(!facedir)
				AM.setDir(predir)

	affecting.Remove(AM)

	REMOVE_TRAIT(AM, TRAIT_IMMOBILIZED, src)

/* Stops things thrown by a thrower, doesn't do anything */

/obj/effect/step_trigger/stopper

/* Instant teleporter */

/obj/effect/step_trigger/teleporter
	var/teleport_x = 0 // teleportation coordinates (if one is null, then no teleport!)
	var/teleport_y = 0
	var/teleport_z = 0

/obj/effect/step_trigger/teleporter/Trigger(atom/movable/A)
	if(teleport_x && teleport_y && teleport_z)

		var/turf/T = locate(teleport_x, teleport_y, teleport_z)
		A.forceMove(T)

/* Random teleporter, teleports atoms to locations ranging from teleport_x - teleport_x_offset, etc */

/obj/effect/step_trigger/teleporter/random
	var/teleport_x_offset = 0
	var/teleport_y_offset = 0
	var/teleport_z_offset = 0

/obj/effect/step_trigger/teleporter/random/Trigger(atom/movable/A)
	if(teleport_x && teleport_y && teleport_z)
		if(teleport_x_offset && teleport_y_offset && teleport_z_offset)

			var/turf/T = locate(rand(teleport_x, teleport_x_offset), rand(teleport_y, teleport_y_offset), rand(teleport_z, teleport_z_offset))
			if (T)
				A.forceMove(T)

/* Fancy teleporter, creates sparks and smokes when used */

/obj/effect/step_trigger/teleport_fancy
	var/locationx
	var/locationy
	var/uses = 1 //0 for infinite uses
	var/entersparks = 0
	var/exitsparks = 0
	var/entersmoke = 0
	var/exitsmoke = 0

/obj/effect/step_trigger/teleport_fancy/Trigger(mob/M)
	var/dest = locate(locationx, locationy, z)
	M.Move(dest)

	if(entersparks)
		var/datum/effect_system/spark_spread/s = new
		s.set_up(4, 1, src)
		s.start()
	if(exitsparks)
		var/datum/effect_system/spark_spread/s = new
		s.set_up(4, 1, dest)
		s.start()

	if(entersmoke)
		var/datum/effect_system/smoke_spread/s = new
		s.set_up(4, 1, src, 0)
		s.start()
	if(exitsmoke)
		var/datum/effect_system/smoke_spread/s = new
		s.set_up(4, 1, dest, 0)
		s.start()

	uses--
	if(uses == 0)
		qdel(src)

/* Simple sound player, Mapper friendly! */

/obj/effect/step_trigger/sound_effect
	var/sound //eg. path to the sound, inside '' eg: 'growl.ogg'
	var/volume = 100
	var/freq_vary = 1 //Should the frequency of the sound vary?
	var/extra_range = 0 // eg World.view = 7, extra_range = 1, 7+1 = 8, 8 turfs radius
	var/happens_once = 0
	var/triggerer_only = 0 //Whether the triggerer is the only person who hears this

/obj/effect/step_trigger/sound_effect/Trigger(atom/movable/A)
	var/turf/T = get_turf(A)

	if(!T)
		return

	if(triggerer_only && ismob(A))
		var/mob/B = A
		B.playsound_local(T, sound, volume, freq_vary)
	else
		playsound(T, sound, volume, freq_vary, extra_range)

	if(happens_once)
		qdel(src)


code:
//temporary visual effects(/obj/effect/temp_visual) used by cult stuff
/obj/effect/temp_visual/cult
	icon = 'icons/effects/cult_effects.dmi'
	randomdir = FALSE
	duration = 10

/obj/effect/temp_visual/cult/sparks
	randomdir = TRUE
	name = "blood sparks"
	icon_state = "bloodsparkles"

/obj/effect/temp_visual/cult/blood  // The traditional teleport
	name = "blood jaunt"
	duration = 12
	icon_state = "bloodin"

/obj/effect/temp_visual/cult/blood/out
	icon_state = "bloodout"

/obj/effect/temp_visual/dir_setting/cult/phase  // The veil shifter teleport
	icon = 'icons/effects/cult_effects.dmi'
	name = "phase glow"
	duration = 7
	icon_state = "cultin"

/obj/effect/temp_visual/dir_setting/cult/phase/out
	icon = 'icons/effects/cult_effects.dmi'
	icon_state = "cultout"

/obj/effect/temp_visual/cult/sac
	name = "maw of Nar'Sie"
	icon_state = "sacconsume"

/obj/effect/temp_visual/cult/door
	name = "unholy glow"
	icon_state = "doorglow"
	layer = CLOSED_FIREDOOR_LAYER //above closed doors

/obj/effect/temp_visual/cult/door/unruned
	icon_state = "unruneddoorglow"

/obj/effect/temp_visual/cult/turf
	name = "unholy glow"
	icon_state = "wallglow"
	layer = ABOVE_NORMAL_TURF_LAYER

/obj/effect/temp_visual/cult/turf/floor
	icon_state = "floorglow"
	duration = 5
	plane = FLOOR_PLANE

/obj/effect/temp_visual/cult/portal
	icon_state = "space"
	duration = 600
	layer = ABOVE_OBJ_LAYER

//visuals for runes being magically created
/obj/effect/temp_visual/cult/rune_spawn
	icon_state = "runeouter"
	alpha = 0
	var/turnedness = 179 //179 turns counterclockwise, 181 turns clockwise

/obj/effect/temp_visual/cult/rune_spawn/Initialize(mapload, set_duration, set_color)
	if(isnum(set_duration))
		duration = set_duration
	if(set_color)
		add_atom_colour(set_color, FIXED_COLOUR_PRIORITY)
	. = ..()
	var/oldtransform = transform
	transform = matrix()*2
	var/matrix/M = transform
	M.Turn(turnedness)
	transform = M
	animate(src, alpha = 255, time = duration, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL)
	animate(src, transform = oldtransform, time = duration, flags = ANIMATION_PARALLEL)

/obj/effect/temp_visual/cult/rune_spawn/rune1
	icon_state = "rune1words"
	turnedness = 181

/obj/effect/temp_visual/cult/rune_spawn/rune1/inner
	icon_state = "rune1inner"
	turnedness = 179

/obj/effect/temp_visual/cult/rune_spawn/rune1/center
	icon_state = "rune1center"

/obj/effect/temp_visual/cult/rune_spawn/rune2
	icon_state = "rune2words"
	turnedness = 181

/obj/effect/temp_visual/cult/rune_spawn/rune2/inner
	icon_state = "rune2inner"
	turnedness = 179

/obj/effect/temp_visual/cult/rune_spawn/rune2/center
	icon_state = "rune2center"

/obj/effect/temp_visual/cult/rune_spawn/rune3
	icon_state = "rune3words"
	turnedness = 181

/obj/effect/temp_visual/cult/rune_spawn/rune3/inner
	icon_state = "rune3inner"
	turnedness = 179

/obj/effect/temp_visual/cult/rune_spawn/rune3/center
	icon_state = "rune3center"

/obj/effect/temp_visual/cult/rune_spawn/rune4
	icon_state = "rune4words"
	turnedness = 181

/obj/effect/temp_visual/cult/rune_spawn/rune4/inner
	icon_state = "rune4inner"
	turnedness = 179

/obj/effect/temp_visual/cult/rune_spawn/rune4/center
	icon_state = "rune4center"

/obj/effect/temp_visual/cult/rune_spawn/rune5
	icon_state = "rune5words"
	turnedness = 181

/obj/effect/temp_visual/cult/rune_spawn/rune5/inner
	icon_state = "rune5inner"
	turnedness = 179

/obj/effect/temp_visual/cult/rune_spawn/rune5/center
	icon_state = "rune5center"

/obj/effect/temp_visual/cult/rune_spawn/rune6
	icon_state = "rune6words"
	turnedness = 181

/obj/effect/temp_visual/cult/rune_spawn/rune6/inner
	icon_state = "rune6inner"
	turnedness = 179

/obj/effect/temp_visual/cult/rune_spawn/rune6/center
	icon_state = "rune6center"

/obj/effect/temp_visual/cult/rune_spawn/rune7
	icon_state = "rune7words"
	turnedness = 181

/obj/effect/temp_visual/cult/rune_spawn/rune7/inner
	icon_state = "rune7inner"
	turnedness = 179

/obj/effect/temp_visual/cult/rune_spawn/rune7/center
	icon_state = "rune7center"


code:
//unsorted miscellaneous temporary visuals
/obj/effect/temp_visual/dir_setting/bloodsplatter
	icon = 'icons/effects/blood.dmi'
	duration = 5
	randomdir = FALSE
	layer = BELOW_MOB_LAYER
	var/splatter_type = "splatter"

/obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir)
	if(ISDIAGONALDIR(set_dir))
		icon_state = "[splatter_type][pick(1, 2, 6)]"
	else
		icon_state = "[splatter_type][pick(3, 4, 5)]"
	. = ..()
	var/target_pixel_x = 0
	var/target_pixel_y = 0
	switch(set_dir)
		if(NORTH)
			target_pixel_y = 16
		if(SOUTH)
			target_pixel_y = -16
			layer = ABOVE_MOB_LAYER
		if(EAST)
			target_pixel_x = 16
		if(WEST)
			target_pixel_x = -16
		if(NORTHEAST)
			target_pixel_x = 16
			target_pixel_y = 16
		if(NORTHWEST)
			target_pixel_x = -16
			target_pixel_y = 16
		if(SOUTHEAST)
			target_pixel_x = 16
			target_pixel_y = -16
			layer = ABOVE_MOB_LAYER
		if(SOUTHWEST)
			target_pixel_x = -16
			target_pixel_y = -16
			layer = ABOVE_MOB_LAYER
	animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration)

/obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter
	splatter_type = "xsplatter"

/obj/effect/temp_visual/dir_setting/speedbike_trail
	name = "speedbike trails"
	icon_state = "ion_fade"
	layer = BELOW_MOB_LAYER
	duration = 10
	randomdir = 0

/obj/effect/temp_visual/dir_setting/firing_effect
	icon = 'icons/effects/effects.dmi'
	icon_state = "firing_effect"
	duration = 2

/obj/effect/temp_visual/dir_setting/firing_effect/setDir(newdir)
	switch(newdir)
		if(NORTH)
			layer = BELOW_MOB_LAYER
			pixel_x = rand(-3,3)
			pixel_y = rand(4,6)
		if(SOUTH)
			pixel_x = rand(-3,3)
			pixel_y = rand(-1,1)
		else
			pixel_x = rand(-1,1)
			pixel_y = rand(-1,1)
	..()

/obj/effect/temp_visual/dir_setting/firing_effect/energy
	icon_state = "firing_effect_energy"
	duration = 3

/obj/effect/temp_visual/dir_setting/firing_effect/magic
	icon_state = "shieldsparkles"
	duration = 3

/obj/effect/temp_visual/dir_setting/ninja
	name = "ninja shadow"
	icon = 'icons/mob/mob.dmi'
	icon_state = "uncloak"
	duration = 9

/obj/effect/temp_visual/dir_setting/ninja/cloak
	icon_state = "cloak"

/obj/effect/temp_visual/dir_setting/ninja/shadow
	icon_state = "shadow"

/obj/effect/temp_visual/dir_setting/ninja/phase
	name = "ninja energy"
	icon_state = "phasein"

/obj/effect/temp_visual/dir_setting/ninja/phase/out
	icon_state = "phaseout"

/obj/effect/temp_visual/dir_setting/wraith
	name = "shadow"
	icon = 'icons/mob/cult.dmi'
	icon_state = "phase_shift2_cult"
	duration = 0.6 SECONDS

/obj/effect/temp_visual/dir_setting/wraith/angelic
	icon_state = "phase_shift2_holy"

/obj/effect/temp_visual/dir_setting/wraith/mystic
	icon_state = "phase_shift2_wizard"

/obj/effect/temp_visual/dir_setting/wraith/out
	icon_state = "phase_shift_cult"

/obj/effect/temp_visual/dir_setting/wraith/out/angelic
	icon_state = "phase_shift_holy"

/obj/effect/temp_visual/dir_setting/wraith/out/mystic
	icon_state = "phase_shift_wizard"

/obj/effect/temp_visual/dir_setting/tailsweep
	icon_state = "tailsweep"
	duration = 4

/obj/effect/temp_visual/dir_setting/curse
	icon_state = "curse"
	duration = 32
	var/fades = TRUE

/obj/effect/temp_visual/dir_setting/curse/Initialize(mapload, set_dir)
	. = ..()
	if(fades)
		animate(src, alpha = 0, time = 32)

/obj/effect/temp_visual/dir_setting/curse/blob
	icon_state = "curseblob"

/obj/effect/temp_visual/dir_setting/curse/grasp_portal
	icon = 'icons/effects/64x64.dmi'
	layer = LARGE_MOB_LAYER
	pixel_y = -16
	pixel_x = -16
	duration = 32
	fades = FALSE

/obj/effect/temp_visual/dir_setting/curse/grasp_portal/fading
	duration = 32
	fades = TRUE

/obj/effect/temp_visual/dir_setting/curse/hand
	icon_state = "cursehand"

/obj/effect/temp_visual/bsa_splash
	name = "\improper Bluespace energy wave"
	desc = "A massive, rippling wave of bluepace energy, all rapidly exhausting itself the moment it leaves the concentrated beam of light."
	icon = 'icons/effects/beam_splash.dmi'
	icon_state = "beam_splash_l"
	layer = ABOVE_ALL_MOB_LAYER
	pixel_y = -16
	duration = 50

/obj/effect/temp_visual/bsa_splash/Initialize(mapload, dir)
	. = ..()
	switch(dir)
		if(WEST)
			icon_state = "beam_splash_w"
		if(EAST)
			icon_state = "beam_splash_e"

/obj/effect/temp_visual/wizard
	name = "water"
	icon = 'icons/mob/mob.dmi'
	icon_state = "reappear"
	duration = 5

/obj/effect/temp_visual/wizard/out
	icon_state = "liquify"
	duration = 12

/obj/effect/temp_visual/monkeyify
	icon = 'icons/mob/mob.dmi'
	icon_state = "h2monkey"
	duration = 22

/obj/effect/temp_visual/monkeyify/humanify
	icon_state = "monkey2h"

/obj/effect/temp_visual/borgflash
	icon = 'icons/mob/mob.dmi'
	icon_state = "blspell"
	duration = 5

/obj/effect/temp_visual/guardian
	randomdir = 0

/obj/effect/temp_visual/guardian/phase
	duration = 5
	icon_state = "phasein"

/obj/effect/temp_visual/guardian/phase/out
	icon_state = "phaseout"

/obj/effect/temp_visual/decoy
	desc = "It's a decoy!"
	duration = 15

/obj/effect/temp_visual/decoy/Initialize(mapload, atom/mimiced_atom)
	. = ..()
	alpha = initial(alpha)
	if(mimiced_atom)
		name = mimiced_atom.name
		appearance = mimiced_atom.appearance
		setDir(mimiced_atom.dir)
		mouse_opacity = MOUSE_OPACITY_TRANSPARENT

/obj/effect/temp_visual/decoy/fading/Initialize(mapload, atom/mimiced_atom)
	. = ..()
	animate(src, alpha = 0, time = duration)

/obj/effect/temp_visual/decoy/fading/threesecond
	duration = 40

/obj/effect/temp_visual/decoy/fading/fivesecond
	duration = 50

/obj/effect/temp_visual/decoy/fading/halfsecond
	duration = 5

/obj/effect/temp_visual/small_smoke
	icon_state = "smoke"
	duration = 50

/obj/effect/temp_visual/small_smoke/halfsecond
	duration = 5

/obj/effect/temp_visual/fire
	icon = 'icons/effects/fire.dmi'
	icon_state = "3"
	light_range = LIGHT_RANGE_FIRE
	light_color = LIGHT_COLOR_FIRE
	duration = 10

/obj/effect/temp_visual/revenant
	name = "spooky lights"
	icon_state = "purplesparkles"

/obj/effect/temp_visual/revenant/cracks
	name = "glowing cracks"
	icon_state = "purplecrack"
	duration = 6

/obj/effect/temp_visual/gravpush
	name = "gravity wave"
	icon_state = "shieldsparkles"
	duration = 5

/obj/effect/temp_visual/telekinesis
	name = "telekinetic force"
	icon_state = "empdisable"
	duration = 5

/obj/effect/temp_visual/emp
	name = "emp sparks"
	icon_state = "empdisable"

/obj/effect/temp_visual/emp/pulse
	name = "emp pulse"
	icon_state = "emppulse"
	duration = 8
	randomdir = 0

/obj/effect/temp_visual/bluespace_fissure
	name = "bluespace fissure"
	icon_state = "bluestream_fade"
	duration = 9

/obj/effect/temp_visual/gib_animation
	icon = 'icons/mob/mob.dmi'
	duration = 15

/obj/effect/temp_visual/gib_animation/Initialize(mapload, gib_icon)
	icon_state = gib_icon // Needs to be before ..() so icon is correct
	. = ..()

/obj/effect/temp_visual/gib_animation/animal
	icon = 'icons/mob/animal.dmi'

/obj/effect/temp_visual/dust_animation
	icon = 'icons/mob/mob.dmi'
	duration = 15

/obj/effect/temp_visual/dust_animation/Initialize(mapload, dust_icon)
	icon_state = dust_icon // Before ..() so the correct icon is flick()'d
	. = ..()

/obj/effect/temp_visual/mummy_animation
	icon = 'icons/mob/mob.dmi'
	icon_state = "mummy_revive"
	duration = 20

/obj/effect/temp_visual/heal //color is white by default, set to whatever is needed
	name = "healing glow"
	icon_state = "heal"
	duration = 15

/obj/effect/temp_visual/heal/Initialize(mapload, set_color)
	if(set_color)
		add_atom_colour(set_color, FIXED_COLOUR_PRIORITY)
	. = ..()
	pixel_x = rand(-12, 12)
	pixel_y = rand(-9, 0)

/obj/effect/temp_visual/kinetic_blast
	name = "kinetic explosion"
	icon = 'icons/obj/guns/projectiles.dmi'
	icon_state = "kinetic_blast"
	layer = ABOVE_ALL_MOB_LAYER
	duration = 4

/obj/effect/temp_visual/explosion
	name = "explosion"
	icon = 'icons/effects/96x96.dmi'
	icon_state = "explosion"
	pixel_x = -32
	pixel_y = -32
	duration = 8

/obj/effect/temp_visual/explosion/fast
	icon_state = "explosionfast"
	duration = 4

/obj/effect/temp_visual/blob
	name = "blob"
	icon_state = "blob_attack"
	alpha = 140
	randomdir = 0
	duration = 6

/obj/effect/temp_visual/desynchronizer
	name = "desynchronizer field"
	icon_state = "chronofield"
	duration = 3

/obj/effect/temp_visual/impact_effect
	icon_state = "impact_bullet"
	duration = 5

/obj/effect/temp_visual/impact_effect/Initialize(mapload, x, y)
	pixel_x = x
	pixel_y = y
	return ..()

/obj/effect/temp_visual/impact_effect/red_laser
	icon_state = "impact_laser"
	duration = 4

/obj/effect/temp_visual/impact_effect/red_laser/wall
	icon_state = "impact_laser_wall"
	duration = 10

/obj/effect/temp_visual/impact_effect/blue_laser
	icon_state = "impact_laser_blue"
	duration = 4

/obj/effect/temp_visual/impact_effect/green_laser
	icon_state = "impact_laser_green"
	duration = 4

/obj/effect/temp_visual/impact_effect/yellow_laser
	icon_state = "impact_laser_yellow"
	duration = 4

/obj/effect/temp_visual/impact_effect/purple_laser
	icon_state = "impact_laser_purple"
	duration = 4

/obj/effect/temp_visual/impact_effect/shrink
	icon_state = "m_shield"
	duration = 10

/obj/effect/temp_visual/impact_effect/ion
	icon_state = "shieldsparkles"
	duration = 6

/obj/effect/temp_visual/impact_effect/energy
	icon_state = "impact_energy"
	duration = 6

/obj/effect/temp_visual/impact_effect/neurotoxin
	icon_state = "impact_neurotoxin"

/obj/effect/temp_visual/heart
	name = "heart"
	icon = 'icons/mob/animal.dmi'
	icon_state = "heart"
	duration = 25

/obj/effect/temp_visual/heart/Initialize(mapload)
	. = ..()
	pixel_x = rand(-4,4)
	pixel_y = rand(-4,4)
	animate(src, pixel_y = pixel_y + 32, alpha = 0, time = 25)

/obj/effect/temp_visual/annoyed
	name = "annoyed"
	icon = 'icons/effects/effects.dmi'
	icon_state = "annoyed"
	duration = 25

/obj/effect/temp_visual/annoyed/Initialize(mapload)
	. = ..()
	pixel_x = rand(-4,0)
	pixel_y = rand(8,12)
	animate(src, pixel_y = pixel_y + 16, alpha = 0, time = duration)

/obj/effect/temp_visual/bleed
	name = "bleed"
	icon = 'icons/effects/bleed.dmi'
	icon_state = "bleed0"
	duration = 10
	var/shrink = TRUE

/obj/effect/temp_visual/bleed/Initialize(mapload, atom/size_calc_target)
	. = ..()
	var/size_matrix = matrix()
	if(size_calc_target)
		layer = size_calc_target.layer + 0.01
		var/icon/I = icon(size_calc_target.icon, size_calc_target.icon_state, size_calc_target.dir)
		size_matrix = matrix() * (I.Height()/world.icon_size)
		transform = size_matrix //scale the bleed overlay's size based on the target's icon size
	var/matrix/M = transform
	if(shrink)
		M = size_matrix*0.1
	else
		M = size_matrix*2
	animate(src, alpha = 20, transform = M, time = duration, flags = ANIMATION_PARALLEL)

/obj/effect/temp_visual/bleed/explode
	icon_state = "bleed10"
	duration = 12
	shrink = FALSE

/obj/effect/temp_visual/warp_cube
	duration = 5
	var/outgoing = TRUE

/obj/effect/temp_visual/warp_cube/Initialize(mapload, atom/teleporting_atom, warp_color, new_outgoing)
	. = ..()
	if(teleporting_atom)
		outgoing = new_outgoing
		appearance = teleporting_atom.appearance
		setDir(teleporting_atom.dir)
		if(warp_color)
			color = list(warp_color, warp_color, warp_color, list(0,0,0))
			set_light(1.4, 1, warp_color)
		mouse_opacity = MOUSE_OPACITY_TRANSPARENT
		var/matrix/skew = transform
		skew = skew.Turn(180)
		skew = skew.Interpolate(transform, 0.5)
		if(!outgoing)
			transform = skew * 2
			skew = teleporting_atom.transform
			alpha = 0
			animate(src, alpha = teleporting_atom.alpha, transform = skew, time = duration)
		else
			skew *= 2
			animate(src, alpha = 0, transform = skew, time = duration)
	else
		return INITIALIZE_HINT_QDEL

/obj/effect/temp_visual/cart_space
	icon_state = "launchpad_launch"
	duration = 2 SECONDS

/obj/effect/temp_visual/cart_space/bad
	icon_state = "launchpad_pull"
	duration = 2 SECONDS

/obj/effect/constructing_effect
	icon = 'icons/effects/effects_rcd.dmi'
	icon_state = ""
	layer = ABOVE_ALL_MOB_LAYER
	anchored = TRUE
	mouse_opacity = MOUSE_OPACITY_TRANSPARENT
	var/status = 0
	var/delay = 0

/obj/effect/constructing_effect/Initialize(mapload, rcd_delay, rcd_status)
	. = ..()
	status = rcd_status
	delay = rcd_delay
	if (status == RCD_DECONSTRUCT)
		addtimer(CALLBACK(src, /atom/.proc/update_appearance), 1.1 SECONDS)
		delay -= 11
		icon_state = "rcd_end_reverse"
	else
		update_appearance()

/obj/effect/constructing_effect/update_icon_state()
	icon_state = "rcd"
	if(delay < 10)
		icon_state += "_shortest"
		return ..()
	if (delay < 20)
		icon_state += "_shorter"
		return ..()
	if (delay < 37)
		icon_state += "_short"
		return ..()
	if(status == RCD_DECONSTRUCT)
		icon_state += "_reverse"
	return ..()

/obj/effect/constructing_effect/proc/end_animation()
	if (status == RCD_DECONSTRUCT)
		qdel(src)
	else
		icon_state = "rcd_end"
		addtimer(CALLBACK(src, .proc/end), 15)

/obj/effect/constructing_effect/proc/end()
	qdel(src)

/obj/effect/temp_visual/electricity
	icon_state = "electricity3"
	duration = 0.5 SECONDS

/obj/effect/temp_visual/thunderbolt
	icon_state = "thunderbolt"
	icon = 'icons/effects/32x96.dmi'
	duration = 0.6 SECONDS


code:
/obj/effect/projectile/impact
	name = "beam impact"
	icon = 'icons/obj/guns/projectiles_impact.dmi'

/obj/effect/projectile/impact/laser
	name = "laser impact"
	icon_state = "impact_laser"

/obj/effect/projectile/impact/laser/blue
	name = "laser impact"
	icon_state = "impact_blue"

/obj/effect/projectile/impact/disabler
	name = "disabler impact"
	icon_state = "impact_omni"

/obj/effect/projectile/impact/xray
	name = "\improper X-ray impact"
	icon_state = "impact_xray"

/obj/effect/projectile/impact/pulse
	name = "pulse impact"
	icon_state = "impact_u_laser"

/obj/effect/projectile/impact/plasma_cutter
	name = "plasma impact"
	icon_state = "impact_plasmacutter"

/obj/effect/projectile/impact/stun
	name = "stun impact"
	icon_state = "impact_stun"

/obj/effect/projectile/impact/heavy_laser
	name = "heavy laser impact"
	icon_state = "impact_beam_heavy"

/obj/effect/projectile/impact/wormhole
	icon_state = "wormhole_g"

/obj/effect/projectile/impact/laser/emitter
	name = "emitter impact"
	icon_state = "impact_emitter"

/obj/effect/projectile/impact/solar
	name = "solar impact"
	icon_state = "impact_solar"


code:
/obj/effect/projectile/muzzle
	name = "muzzle flash"
	icon = 'icons/obj/guns/projectiles_muzzle.dmi'

/obj/effect/projectile/muzzle/laser
	icon_state = "muzzle_laser"

/obj/effect/projectile/muzzle/laser/blue
	icon_state = "muzzle_laser_blue"

/obj/effect/projectile/muzzle/disabler
	icon_state = "muzzle_omni"

/obj/effect/projectile/muzzle/xray
	icon_state = "muzzle_xray"

/obj/effect/projectile/muzzle/pulse
	icon_state = "muzzle_u_laser"

/obj/effect/projectile/muzzle/plasma_cutter
	icon_state = "muzzle_plasmacutter"

/obj/effect/projectile/muzzle/stun
	icon_state = "muzzle_stun"

/obj/effect/projectile/muzzle/heavy_laser
	icon_state = "muzzle_beam_heavy"

/obj/effect/projectile/muzzle/wormhole
	icon_state = "wormhole_g"

/obj/effect/projectile/muzzle/laser/emitter
	name = "emitter flash"
	icon_state = "muzzle_emitter"

/obj/effect/projectile/muzzle/solar
	icon_state = "muzzle_solar"


code:
/obj/effect/projectile
	name = "pew"
	icon = 'icons/obj/guns/projectiles.dmi'
	icon_state = "nothing"
	layer = ABOVE_MOB_LAYER
	anchored = TRUE
	mouse_opacity = MOUSE_OPACITY_TRANSPARENT
	appearance_flags = 0

/obj/effect/projectile/singularity_pull()
	return

/obj/effect/projectile/singularity_act()
	return

/obj/effect/projectile/proc/scale_to(nx,ny,override=TRUE)
	var/matrix/M
	if(!override)
		M = transform
	else
		M = new
	M.Scale(nx,ny)
	transform = M

/obj/effect/projectile/proc/turn_to(angle,override=TRUE)
	var/matrix/M
	if(!override)
		M = transform
	else
		M = new
	M.Turn(angle)
	transform = M

/obj/effect/projectile/New(angle_override, p_x, p_y, color_override, scaling = 1)
	if(angle_override && p_x && p_y && color_override && scaling)
		apply_vars(angle_override, p_x, p_y, color_override, scaling)
	return ..()

/obj/effect/projectile/proc/apply_vars(angle_override, p_x = 0, p_y = 0, color_override, scaling = 1, new_loc, increment = 0)
	var/mutable_appearance/look = new(src)
	look.plane = plane
	look.pixel_x = p_x
	look.pixel_y = p_y
	if(color_override)
		look.color = color_override
	appearance = look
	scale_to(1,scaling, FALSE)
	turn_to(angle_override, FALSE)
	if(!isnull(new_loc)) //If you want to null it just delete it...
		forceMove(new_loc)
	for(var/i in 1 to increment)
		pixel_x += round((sin(angle_override)+16*sin(angle_override)*2), 1)
		pixel_y += round((cos(angle_override)+16*cos(angle_override)*2), 1)

/obj/effect/projectile_lighting
	var/owner

/obj/effect/projectile_lighting/Initialize(mapload, color, range, intensity, owner_key)
	. = ..()
	set_light(range, intensity, color)
	owner = owner_key


code:
/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported!
	if(!istype(starting) || !istype(ending) || !ispath(beam_type))
		return
	var/datum/point/midpoint = point_midpoint_points(starting, ending)
	var/obj/effect/projectile/tracer/PB = new beam_type
	if(isnull(light_color_override))
		light_color_override = color
	PB.apply_vars(angle_between_points(starting, ending), midpoint.return_px(), midpoint.return_py(), color, pixel_length_between_points(starting, ending) / world.icon_size, midpoint.return_turf(), 0)
	. = PB
	if(light_range > 0 && light_intensity > 0)
		var/list/turf/line = get_line(starting.return_turf(), ending.return_turf())
		tracing_line:
			for(var/i in line)
				var/turf/T = i
				for(var/obj/effect/projectile_lighting/PL in T)
					if(PL.owner == instance_key)
						continue tracing_line
				QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity, instance_key), qdel_in > 0? qdel_in : 5)
		line = null
	if(qdel_in)
		QDEL_IN(PB, qdel_in)

/obj/effect/projectile/tracer
	name = "beam"
	icon = 'icons/obj/guns/projectiles_tracer.dmi'

/obj/effect/projectile/tracer/laser
	name = "laser"
	icon_state = "beam"

/obj/effect/projectile/tracer/laser/blue
	icon_state = "beam_blue"

/obj/effect/projectile/tracer/disabler
	name = "disabler"
	icon_state = "beam_omni"

/obj/effect/projectile/tracer/xray
	name = "\improper X-ray laser"
	icon_state = "xray"

/obj/effect/projectile/tracer/pulse
	name = "pulse laser"
	icon_state = "u_laser"

/obj/effect/projectile/tracer/plasma_cutter
	name = "plasma blast"
	icon_state = "plasmacutter"

/obj/effect/projectile/tracer/stun
	name = "stun beam"
	icon_state = "stun"

/obj/effect/projectile/tracer/heavy_laser
	name = "heavy laser"
	icon_state = "beam_heavy"

/obj/effect/projectile/tracer/solar
	name = "solar beam"
	icon_state = "solar"

//BEAM RIFLE
/obj/effect/projectile/tracer/tracer/beam_rifle
	icon_state = "tracer_beam"

/obj/effect/projectile/tracer/tracer/aiming
	icon_state = "pixelbeam_greyscale"
	plane = ABOVE_LIGHTING_PLANE

/obj/effect/projectile/tracer/wormhole
	icon_state = "wormhole_g"

/obj/effect/projectile/tracer/laser/emitter
	name = "emitter beam"
	icon_state = "emitter"


code:
///temporary visual effects
/obj/effect/temp_visual
	icon_state = "nothing"
	anchored = TRUE
	layer = ABOVE_MOB_LAYER
	mouse_opacity = MOUSE_OPACITY_TRANSPARENT
	///time, in deciseconds, that this object will exist
	var/duration = 10
	///if true, will pick a random direction when created.
	var/randomdir = TRUE
	///id of the deletion timer
	var/timerid

/obj/effect/temp_visual/Initialize(mapload)
	. = ..()
	if(randomdir)
		setDir(pick(GLOB.cardinals))

	timerid = QDEL_IN(src, duration)

/obj/effect/temp_visual/Destroy()
	. = ..()
	deltimer(timerid)

/obj/effect/temp_visual/singularity_act()
	return

/obj/effect/temp_visual/singularity_pull()
	return

/obj/effect/temp_visual/dir_setting
	randomdir = FALSE

/obj/effect/temp_visual/dir_setting/Initialize(mapload, set_dir)
	if(set_dir)
		setDir(set_dir)
	. = ..()