Files
Bubberstation/code/game/objects/effects/spawners/random
necromanceranne cfd40aeef5 Imports and Contraband 2: Landfill Gacha Addiction (I put trash randomizers into cargo crates and called it content) (#76771)
## About The Pull Request

This is a followup on my previous PR involving cargo imports. I've made
a number of changes and new additions to cargo's imports and contraband.
But I've also changed how Smuggler's Satchels generate loot as well.

### New:
**Abandoned Crates:** You can now order in abandoned crates at a steep
price. Obviously these are just your standard fare abandoned crates, so
they've got a pretty long list of potential contents. Some great, some
utterly not worth the price you paid for the crate. Since they're quite
pricey, you can't order very many quickly. But this does allow cargo
techs the opportunity to spend the round solving puzzles to get
interesting loot.

**Dumpster of Maint Garbage:** This dumpster (similarly named to another
dumpster you can order) is filled with maint trash and potential maint
random spawns. This list is extensive enough that anything spawned in
this crate is likely to be mostly garbage. But, it is more affordable
than abandoned crates. I'd consider this the literally trashier version
of the abandoned crate.

**Shamber's Juice Eldritch Energy! Crate:** A crate with one can of the
extremely rare, short run edition of Shambler's Juice, Eldritch Energy!
This contains 5 units of eldritch essence. Heals heretics, hurts
everyone else! This is a VERY potent poison, but it also happens to be a
handy way for a Cargonian heretic to get a potent healing item without
having to waste knowledge points.

**Animal Hide Crate:** It's a cargo crate full of animal hides! This can
include fairly rare hides and some icebox creature hides as well, like
polar bear hides and wolf sinew. It's not too expensive, and mostly
spits out leather.

**Dreadnog Carton Crate:** A carton full of the worst eggnog imaginable.
This is just something to troll people with. Drink it and you'll get a
massive mood penalty. Dreadnog! May or may not contain space cola!

### Updated:

**Contraband Crate and Smuggler's Satchels:** This has had it's price
increased considerably. But, for good reason. It now contains some more
controlled random items, but also some more valuable contraband as well
as a very rare spawn. The upper end on his contraband can be extremely
valuable, but the majority of the items gained from contraband crates
will probably be either what you can get now (quite weak), or something
a bit more middle of the road (some more unique narcotics).

As a consequence, I've also passed this change onto smuggler's satchels,
as they used the crate to generate its contents. (it literally spawned
and then deleted a contraband crate to generate the contents hoo haa).

I've also increased the number of items in the smuggler's satchel. Since
the randomly spawned smuggler's satchels are quite a bit rarer now there
is only ever two spawned in the map, and spending actual TC on these is
somewhat counterproductive, I don't imagine this will be more beneficial
for scavenger hunters hoping for some interesting goodies.

**Russian Crate (the normal one):** The mosins now spawn in ancient gun
cases. These determine what kind of mosin you can get. 79% of the time,
you get the crap mosin. 20% of the time, you get a good mosin. And 1% of
the time, you get rations. This more tightly controls how many good
mosins are entering into the round and how much of a value purchase the
Russian crate actually is for getting ballistics. Since the process is
even more unlikely than before, it isn't necessarily as guaranteed that
you will get a good mosin. Hell, you might not even get a gun if you're
that unlucky.

**Shocktrooper Crate:** It now has an armor vest and helmet. So, not
only do you get some grenades, you get some protection as well. Since
this is the 'loud' crate, I felt it appropriate to make it slightly more
useful for enabling that.

**Special Ops Crate:** It now contains five mirage grenades and a
chameleon belt, and has had the survival knife improved to a
switchblade. This is probably the weakest of the two crates STILL, but
hopefully these make them a little more interesting and novel by giving
them pretty fun grenade to toy with.

## Why It's Good For The Game

My initial PR hoped to add in a few more interesting purchases for
cargo. I think currently cargo has a slight issue of not having enough
valuable or interesting uses for their money. I think it still has that
problem, but by including more unique crates that allow cargo to provide
some oddities into the round, that might slowly work itself out.

This PR hopes to provide another way to waste their money if they have
an excess amount. Landfill Trash Gambling. Spending it away on complete
junk, which I think is absolutely hilarious when it doesn't work out, as
it is soulful in its design. Definitely not inspired by my recent thrift
shop excursions this month buying and scrounging for furniture and
interesting clothing.

[Relevant](https://www.youtube.com/watch?v=QK8mJJJvaes)

Also, I wanted to buff some of the crates I introduced a bit last time,
and nerf the mosin production somewhat via a more controllable method
that I can actually adjust as necessary down the line.

## Changelog
🆑
fix: Stops manifest generation runtiming when a cargo crate is empty.
add: Abandoned crates are now available via cargo imports.
add: Dumpsters full of maintenance trash are now available via cargo
imports.
add: An ultra-rare can of Shambler's Juice is now available via cargo
imports.
add: Animal hides and leathers can be (unreliably) ordered via cargo
imports.
add: The Dreadnog has entered this realm. To consume, purchase it via
cargo imports.
balance: Contraband Crates (and as a consequence, smuggler's satchels)
now generate more varied goods. Mostly the same, but sometimes you get
something quite different or even valuable.
balance: Mosins generated via the Russian supply crate are a bit more
random, weighing more heavily towards bad mosins than good mosins.
balance: Buffed both the shocktrooper and special op crate. Shocktrooper
now has an armored helmet and vest, and special op now has 5 mirage
grenades and a chameleon belt. The survival knife in the special op
crate is now a switchblade.
/🆑
2023-07-15 16:27:39 +01:00
..
2023-05-27 19:00:55 -04:00

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:

	/// 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.

// 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:

/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.

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

/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:

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

Bad:

/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:

/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:

/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
	)