Difference between revisions of "Cave spawning"

From Pikmin Technical Knowledge Base
Jump to navigation Jump to search
m (This needs to be expanded further...)
(Completely reworked this page. This is still a WIP.)
Line 1: Line 1:
This page explains the game's process behind spawning enemies in a cave. This is obtained through observation, and is subject to have some parts incorrect.
+
This page explains the game's process behind spawning enemies in a cave. This is obtained through code analysis, both of the game code and the [https://github.com/JHaack4/CaveGen CaveGen] project.
  
{{note|Until further notice, this only applies to spawning enemies.}}
+
{{credits|JHawk}}
  
 
==Process==
 
==Process==
Some time after the cave is built out of its [[Cave unit list file|available units]], the game will run this "main algorithm" in order to spawn objects.
+
Some time after the cave is built out of its [[Cave unit list file|available units]], the game will run this "main algorithm" in order to spawn objects. Note that this outline will only include information about which objects spawn, and how many. It does not have information on ''where'' they spawn, using the "score" mechanic.
  
 
===Main algorithm===
 
===Main algorithm===
# For each <code>TekiInfo</code> entry in the [[Cave definition file|sublevel's configuration]], in order...
+
{{todo|Document the algorithms that are linked here, but don't exist in the page.}}
## Run the [[#Spawn algorithm|spawn algorithm]] using this entry's class, minimum spawn amount, and spawn coordinate type.
+
# Run the [[#Main category allocation algorithm|main category allocation algorithm]] to figure out how many "main" category entries of each spawn type will be spawned.
# If the number of enemies hasn't reached <code>{f002}</code> yet...
+
# Run the [[#Spawn type 5, 8, 1 algorithm|spawn type 5, 8, 1 algorithm]] to spawn all "main" category objects of type 5 (seams).
## For each <code>TekiInfo</code> entry in the sublevel's configuration...
+
# Run the [[#Spawn type 5, 8, 1 algorithm|same algorithm]] to spawn all "main" category objects of type 8 ("special" enemies).
### Grab its random distribution weight and add it to a list.
+
# Run the [[#Spawn type 5, 8, 1 algorithm|same algorithm]] to spawn all "main" category objects of type 1 ("hard" enemies).
## While the number of enemies hasn't reached <code>{f002}</code>...
+
# Run the [[#Spawn type 0 algorithm|spawn type 0 algorithm]] to spawn all "main" category objects of type 0 ("easy" enemies).
### Randomly decide what enemy to spawn next, using the weighted list built in the previous step.
+
# Run the [[#Spawn type 6 algorithm|spawn type 6 algorithm]] to spawn all "decorative" category objects.
### Store the enemy's type.
+
# Run the [[#Spawn treasure algorithm|spawn treasure algorithm]] to spawn all "treasure" category objects.
### Store the spawn coordinate type.
+
# Run the [[#Spawn dead end algorithm|spawn dead end algorithm]] to spawn all "dead end" category objects.
### Run the [[#Spawn algorithm|spawn algorithm]] using this entry's class, amount of 1, and this entry's spawn coordinate type.
 
# Remove every spawned object that is not meant to be there (collected treasures, Candypop Buds that disappear when you have 20+, etc.)
 
  
===Spawn algorithm===
+
=== Main category allocation algorithm ===
This algorithm takes as parameters an object class, a spawn amount, and a spawn coordinate type.
+
This algorithm saves how many objects of type 0 we want to spawn, how many of type 1, of type 5, and of type 8.
  
# While the intended amount of enemies hasn't been spawned yet...
+
# Save the number of allocations of spawn type 0 ("easy" enemies) as 0. Repeat for types 1 ("hard" enemies), 5 (seams), and 8 ("special" enemies).
## Pick a [[#Coordinate check|valid spawn coordinate]] randomly.
+
# Save the weight of each spawn type as 0.
## If there is no valid spawn coordinate remaining, leave this algorithm.
+
# For each "main" category entry in the [[Cave definition file|sublevel's configuration]]...
## Mark this spawn coordinate as used.
+
## Add the minimum amount of that entry to the number of allocations of the relevant spawn type.
## Decide how many objects to spawn here, picking randomly between the spawn coordinate's amount limits.
+
## Add the weight of that entry to the weight of the relevant spawn type.
## For each one in that amount...
+
# Repeat until the number of allocations reaches the "main" category limit in <code>{f002}</code>:
### If the number of objects spawned here surpasses the intended amount, leave this algorithm.
+
## With [[#Weighted distribution|weighted randomness]], pick a spawn type, using the spawn type weights.
### Spawn that object in these coordinates (randomly translated inside the circle decided by the spawn coordinate's radius).
+
## Add +1 allocation to the chosen spawn type.
  
===Coordinate check===
+
=== Spawn type 5, 8, 1 algorithm ===
A spawn coordinate is valid if all of the following are true:
+
This algorithm spawns objects of types 5, 8, or 1.
# It is not in a dead end cave unit.
+
 
# It is of the intended spawn coordinate type (Enemy Group A, Plant, etc.)
+
# Repeat n times, n being the number of allocations for the spawn type.
# It is in-bounds.
+
## Pick a free spawn point of the given type.
# It has not been used already.
+
### For type 5, this means picking a free seam. Note that not all seams have the same chance of being picked.
# It is far enough away from the ship's pod (300 units for Enemy Group A, B, and C).
+
### For types 8 or 1, this means a standard spawn point. Note that some points cannot be picked due to being too close to important gameplay features.
# It is far enough away from the hole (200 units for Enemy Group A, 150 units for Enemy Group C, not applicable for Enemy Group B).
+
## Run the [[#Entry picking algorithm|entry picking algorithm]] with all entries of this type, and the number of spawned objects of this type so far.
# It is far enough away from the geyser (200 units for Enemy Group A, 150 units for Enemy Group C, not applicable for Enemy Group B).
+
## If there is no spawn point or entry that can be picked, finish now.
 +
## Spawn one object of that entry on that spawn point.
 +
## Mark that spawn point as used.
 +
 
 +
=== Spawn type 0 algorithm ===
 +
This algorithm spawns objects of type 0.
 +
 
 +
# Repeat n times, n being the number of allocations for spawn type 0.
 +
## Pick a free spawn point of type 0. Note that some points cannot be picked due to being too close to important gameplay features.
 +
## Run the [[#Entry picking algorithm|entry picking algorithm]] with all entries of type 0, and the number of spawned objects type 0 so far.
 +
## If we picked an entry because we still have minimum amounts to fill, then:
 +
### Save the number of candidates as the number of objects we still have left to spawn of the given entry to meet its minimum.
 +
## Otherwise:
 +
### Save the number of candidates as the number of "main" category objects we can still spawn before meeting the "main" category limit in <code>{f002}</code>.
 +
## Save the maximum number to spawn as the spawn point's maximum amount, or the number of candidates, whichever is smaller.
 +
## If the maximum number to spawn is greater than the spawn point's minimum amount, then:
 +
### Save the number to spawn as a random number between the the spawn point's minimum amount, and our maximum number to spawn.
 +
## Otherwise:
 +
### Save the number to spawn as the maximum number to spawn.
 +
## If there is no spawn point or no entry that can be picked, finish now.
 +
## If the number to spawn is 0, finish now.
 +
## Spawn however many objects we wanted to spawn on that spawn point.
 +
## Randomly move the new objects around the spawn point, using the spawn point's "radius" property.
 +
## Push the new objects around out of the way of each other.
 +
## Mark that spawn point as used.
 +
 
 +
=== Entry picking algorithm ===
 +
This algorithm picks an object entry from a given set, and chooses an object to spawn. It checks how many of this type have been spawned so far to decide which one to pick next.
 +
 
 +
# If we haven't spawned all necessary minimum amounts for objects of the given type, then:
 +
## Pick an entry whose minimum amounts haven't been met yet. Prioritize the order in the sublevel configuration file.
 +
# Otherwise, if there are entries of that type with any weight, then:
 +
## Pick an entry of that type using [[#Weighted distribution|weighted randomness]], using the weights of the entries of that type.
 +
# Otherwise:
 +
## Pick none.
  
 
===Notes===
 
===Notes===
 
* If a spawn coordinate has a radius of 0, all objects to spawn will be placed in the same spot, and will physically push each other apart. Otherwise, the objects will be forced to maintain a fixed minimum distance from one another, even if that distance will push them beyond the spawn coordinate's radius.
 
* If a spawn coordinate has a radius of 0, all objects to spawn will be placed in the same spot, and will physically push each other apart. Otherwise, the objects will be forced to maintain a fixed minimum distance from one another, even if that distance will push them beyond the spawn coordinate's radius.
 +
* A spawn coordinate is valid if all of the following are true:
 +
*# It is not in a dead end cave unit.
 +
*# It is of the intended spawn coordinate type (Enemy Group A, Plant, etc.)
 +
*# It is in-bounds.
 +
*# It has not been used already.
 +
*# It is far enough away from the ship's pod (300 units for Enemy Group A, B, and C).
 +
*# It is far enough away from the hole (200 units for Enemy Group A, 150 units for Enemy Group C, not applicable for Enemy Group B).
 +
*# It is far enough away from the geyser (200 units for Enemy Group A, 150 units for Enemy Group C, not applicable for Enemy Group B).
  
 
{{credits|[[User:Espyo|Espyo]], [[User:Jimble|Jimble]]}}
 
{{credits|[[User:Espyo|Espyo]], [[User:Jimble|Jimble]]}}

Revision as of 19:56, 21 January 2022

This page explains the game's process behind spawning enemies in a cave. This is obtained through code analysis, both of the game code and the CaveGen project.

Credits: JHawk

Process

Some time after the cave is built out of its available units, the game will run this "main algorithm" in order to spawn objects. Note that this outline will only include information about which objects spawn, and how many. It does not have information on where they spawn, using the "score" mechanic.

Main algorithm

To do: Document the algorithms that are linked here, but don't exist in the page.

  1. Run the main category allocation algorithm to figure out how many "main" category entries of each spawn type will be spawned.
  2. Run the spawn type 5, 8, 1 algorithm to spawn all "main" category objects of type 5 (seams).
  3. Run the same algorithm to spawn all "main" category objects of type 8 ("special" enemies).
  4. Run the same algorithm to spawn all "main" category objects of type 1 ("hard" enemies).
  5. Run the spawn type 0 algorithm to spawn all "main" category objects of type 0 ("easy" enemies).
  6. Run the spawn type 6 algorithm to spawn all "decorative" category objects.
  7. Run the spawn treasure algorithm to spawn all "treasure" category objects.
  8. Run the spawn dead end algorithm to spawn all "dead end" category objects.

Main category allocation algorithm

This algorithm saves how many objects of type 0 we want to spawn, how many of type 1, of type 5, and of type 8.

  1. Save the number of allocations of spawn type 0 ("easy" enemies) as 0. Repeat for types 1 ("hard" enemies), 5 (seams), and 8 ("special" enemies).
  2. Save the weight of each spawn type as 0.
  3. For each "main" category entry in the sublevel's configuration...
    1. Add the minimum amount of that entry to the number of allocations of the relevant spawn type.
    2. Add the weight of that entry to the weight of the relevant spawn type.
  4. Repeat until the number of allocations reaches the "main" category limit in {f002}:
    1. With weighted randomness, pick a spawn type, using the spawn type weights.
    2. Add +1 allocation to the chosen spawn type.

Spawn type 5, 8, 1 algorithm

This algorithm spawns objects of types 5, 8, or 1.

  1. Repeat n times, n being the number of allocations for the spawn type.
    1. Pick a free spawn point of the given type.
      1. For type 5, this means picking a free seam. Note that not all seams have the same chance of being picked.
      2. For types 8 or 1, this means a standard spawn point. Note that some points cannot be picked due to being too close to important gameplay features.
    2. Run the entry picking algorithm with all entries of this type, and the number of spawned objects of this type so far.
    3. If there is no spawn point or entry that can be picked, finish now.
    4. Spawn one object of that entry on that spawn point.
    5. Mark that spawn point as used.

Spawn type 0 algorithm

This algorithm spawns objects of type 0.

  1. Repeat n times, n being the number of allocations for spawn type 0.
    1. Pick a free spawn point of type 0. Note that some points cannot be picked due to being too close to important gameplay features.
    2. Run the entry picking algorithm with all entries of type 0, and the number of spawned objects type 0 so far.
    3. If we picked an entry because we still have minimum amounts to fill, then:
      1. Save the number of candidates as the number of objects we still have left to spawn of the given entry to meet its minimum.
    4. Otherwise:
      1. Save the number of candidates as the number of "main" category objects we can still spawn before meeting the "main" category limit in {f002}.
    5. Save the maximum number to spawn as the spawn point's maximum amount, or the number of candidates, whichever is smaller.
    6. If the maximum number to spawn is greater than the spawn point's minimum amount, then:
      1. Save the number to spawn as a random number between the the spawn point's minimum amount, and our maximum number to spawn.
    7. Otherwise:
      1. Save the number to spawn as the maximum number to spawn.
    8. If there is no spawn point or no entry that can be picked, finish now.
    9. If the number to spawn is 0, finish now.
    10. Spawn however many objects we wanted to spawn on that spawn point.
    11. Randomly move the new objects around the spawn point, using the spawn point's "radius" property.
    12. Push the new objects around out of the way of each other.
    13. Mark that spawn point as used.

Entry picking algorithm

This algorithm picks an object entry from a given set, and chooses an object to spawn. It checks how many of this type have been spawned so far to decide which one to pick next.

  1. If we haven't spawned all necessary minimum amounts for objects of the given type, then:
    1. Pick an entry whose minimum amounts haven't been met yet. Prioritize the order in the sublevel configuration file.
  2. Otherwise, if there are entries of that type with any weight, then:
    1. Pick an entry of that type using weighted randomness, using the weights of the entries of that type.
  3. Otherwise:
    1. Pick none.

Notes

  • If a spawn coordinate has a radius of 0, all objects to spawn will be placed in the same spot, and will physically push each other apart. Otherwise, the objects will be forced to maintain a fixed minimum distance from one another, even if that distance will push them beyond the spawn coordinate's radius.
  • A spawn coordinate is valid if all of the following are true:
    1. It is not in a dead end cave unit.
    2. It is of the intended spawn coordinate type (Enemy Group A, Plant, etc.)
    3. It is in-bounds.
    4. It has not been used already.
    5. It is far enough away from the ship's pod (300 units for Enemy Group A, B, and C).
    6. It is far enough away from the hole (200 units for Enemy Group A, 150 units for Enemy Group C, not applicable for Enemy Group B).
    7. It is far enough away from the geyser (200 units for Enemy Group A, 150 units for Enemy Group C, not applicable for Enemy Group B).
Credits: Espyo, Jimble

Weighted distribution

Some types of object may come up more often than others. For instance, a developer could want a cave where the enemy slots are filled randomly, with each one having a 90% chance of being a Red Bulborb, and 10% chance of being a Wollywog. This is known as weighted random distribution. The way Pikmin 2 does this is that every object definition has a weight number that goes from 0 to 9. Any object definition with a weight of 0 will not be picked, though.

To know what the chances are of the game picking a given object, all of the weight numbers are summed up, and the chance of that object is <object's weight> / <sum>. So for example, if you have the following list:

Object Weight
Red Bulborb 4
Wollywog 7
Water Dumple 3

The chances of spawning a Red Bulborb are 28.5% (4 in 14), the chances of a Wollywog are 50% (7 in 14), and the chances of a Water Dumple are 21.4% (3 in 14).