Koding Konversation: Spawning random enemies using a database

This conversation has been recreated from the Project Spark Forum using The Wayback Machine.
Date: OP Jan 5th 2016

Original Post

Kohldar: I know I talk about this a lot on the forums and not real sure if creators even go about it this way, but… I wanted to post a quick snippet of what I’m working on and if there’s anyone whose remotely interested in how it will turn out.

Basically, I am creating a database full of monsters using Arrays, or at least, how PS currently handles them. It will contain all sorts of information from stats, descriptions, etc. Once said database is built, I will be working on methods to “call” monsters from the database.

Consider the scenario: Running around, random encounter occurs. The system I envision will:

1. Spawn 1 to 5 enemies based on a percentage chance (5 being rare, 1 common).
2. Once the number of enemies is determined, the system would then look to see what global location the player is in.
3. Based on that location, the system will look only at certain enemies within the DB that can spawn in that location and randomly select one of those monsters for each of the number of enemies (duplicates can happen).

Now the only downside to this system is the scalability. Technically, you could just add another DB, but I think the system would do better if the DB was split up into locations initially. Then your “Random Encounter” Kode simply would run through a list of locations and when it matches, it would pull from that DB.

Now I feel like i’m getting to far into, but what do you think? Sound interesting? Anyone attempted this before? Anyone want to know what happens?


ZedSymsy: Many players have created similar systems in to their games as far as random spawning enemies etc, however I like this idea and I’m very interested.

Kohldar:Totally agree. The random spawning isn’t anything new by any means. The magic is in the details so to speak. Thank you for the support!

Zeefles: I have created several systems using RNG. I created a very advanced item system. I really like the idea and have done similar things.
I would get the following somehow:
WHERE are you?
I use trigger zones that determine where “Combat” will take place. If i am in a field of snow, when combat starts, the player will be relocated to a battlefield that is covered with snow. Same with desert and grass.
Now that we know where you are WHAT spawns there?
Based on what area you are in assign what monster 1,2,3,etc… are:
For example, if you are in the desert-
[in world picker: monster generator][object: monster 1][=][desert goblin]
[in world picker: monster generator][object: monster 2][=][desert wolf]
[in world picker: monster generator][object: monster 3][=][desert dragon]
now in “Monster Generator” I would do something like this:
When: [once] Do:[number of monsters][=][random number][1][to][5]
When: [for each of][number of monsters]
Do: [call page][values page]

(get a random number)
When: [entered page]Do:[which monster][=][random number][1][to][100]
(the higher the number the stronger the monster)
When: [which monster][>]90 Do:[create monster 3]
When: [which monster][>]39[and][which monster][<]91 Do:[create monster 2]
When: [which monster][<]40 Do:[create monster 1]

that’s a very rough, but i would do that. Then in each of the monster page I would do something like this:
When: [ONCE] Do: [call page][values]

(get what level you are based on player level)
When: Do: [level adjust][=][random number][negative][2][to[2][as integer]
When: Do: [level][=][player][level][plus][level adjust]
(get the monsters default stats)
When: Do: [health][=][100]
When: Do: [strength][=][20]
When: Do: [agility][=][15]
(adjust the stats depending on the level of the monster)
When: Do: [health][=][health][plus]([10 * level])
When: Do: [strength][=][strength][plus][level]
When: Do: [agility][=][agility][plus][level]

that’s alot of Kode… sorry haha

Kohldar: Thanks for the feedback Zeefles. I won’t be working on the details just yet for each of the monsters, but I have assigned them a temporary health/max health. Props for suggesting the random level assignment. I have a previous level I used that in and I think its probably one of the more satisfying mechanics I’ve worked with. What makes this system so cool is that I can just as easily punch in a few lines of code and each monster will randomly spawn with a level that is similar to the player and have their stats scaled via that level.

As for right now, I’m working on the mechanics. Currently, I have a Raycast detecting the type of terrain and determines if you can encounter monsters or not. When you enter a zone in which you can encounter monsters, there is a random timer that is started to when the encounter occurs. Also, I have 6 monsters currently in the DB and have figured out the process for creating the monsters from the DB. Just need to start setting up more of the RNG side of things.

Up next:
Filter the selection of monsters based on location determined by Raycast
Set number of enemies spawned
Randomly select enemies based on percentages and spawn them at their respective locations
More to come!

Alright, so I’m still working on this. Will likely get more done this weekend. However, I wanted to provide a sneak peek at some of the Kode I’m working with. This is my pseudo-Kode, so there may be errors as I haven’t cross checked it with what I have currently Koded. Once I get the game published you will be able to check it out for yourselves (or I can edit it for accuracy later).

Here is the basis for creating monsters:
Logic Cube – Master Monster DB
Page – @InitializeMonsterIndex
When: [Once] Do:
>>>>>When: Do: [Call Page][@Goblin]

Page – @Goblin
When: Do: [num – monsterID][increment by][1]
When: Do: [MonsterStorage][add brain][BrainVar – Blank Brain][to channel][Monsters][to slot][monsterID]
When: Do: [Brain – AddMonster][equals][MonsterStorage][get brain][in channel][Monsters][monsterID]
When: Do: [AddMonsters][ObjVar – monster][equals][IWP – Goblin]
When: Do: [AddMonsters][monster][Name][equals][Plains Goblin]
When: Do: [AddMonsters][monster][max health][equals][30]
When: Do: [AddMonsters][monster][health][equals][max health]

Logic Cube – Blank Brain
When: [Once] Do:
Logic Cube – MonsterStorage
When: Do:
IWP – Goblin template
When: Do: [Display][Name][plus][health][plus][/][plus][max health]

So to hopefully explain this correctly:
1. The Master Monster DB runs and calls the monster page
2. The monster page increments the monsterID variable, which is used to keep track of how many monsters have been created, so that none of them get overwritten, much like in an SQL db where the identity specification and indexable are set to yes.
3. The MonsterStorage logic cube creates a slot for the new brain to be created at. This would be treated much like a blank record of a db.
4. We create a new brain called [AddMonster], which calls the brain that was stored in MonsterStorage channel “Monsters” @ slot monsterID (in this case, it would be 1)
5. Now we can start writing information about the monster to the brain:
a. The ObjVar – monster is a variable that stores which IWP template to use for this monster
b. We then set the monsters name, max health, and health
6. The code in the IWP – Goblin template is merely to show that our values we assigned to monster, Name, health/max health are working (debug)

As demonstrated in Step 4, we can use this same methodology to call the monster from the DB and spawn them in the world. For complexity’s sake, I will just use an enemy spawner that creates an enemy when “X” is pressed:

Logic Cube – EnemySpawner
When: [Once] Do:
>>>>>When: Do: [NumVar – ID][equals][1]
When: [X][pressed] Do:
>>>>>When: Do: [Brain – SpawnMonster][equals][MonsterStorage][get brain][from channel][Monsters][ID]
>>>>>When: [ID][equal to][1] Do:
>>>>>When: Do: [Create][SpawnMonster][monster][at position][IWP – EnemySpawner][Top side]

1. We hard-Koded the Enemy spawner to select the brain located in the Monsters channel/Slot 1
2. When X is pressed, we create a new brain that grabs the brain from Monsters/Slot 1
3. When the [ID] is equal to 1, we spawn its object variable [monster] at position, top-side of the enemy spawner

Now if we wanted to add more RNG at this point in regards to variety of monsters, we simply add a new page to the Master Monster DB like the @Goblin page, create a new IWP template for our monster (debug code brain is optional), and then change the EnemySpawner Kode like so:

Logic Cube – EnemySpawner
When: [X][pressed] Do:
>>>>>When: Do: [NumVar – ID][equals][ceiling][Random Number][0][to][2]
>>>>>When: Do: [Brain – SpawnMonster][equals][MonsterStorage][get brain][from channel][Monsters][ID]
>>>>>When: [ID][equal to][1] Do:
>>>>>When: Do: [Create][SpawnMonster][monster][at position][IWP – EnemySpawner][Top side]
>>>>>When: [ID][equal to][2] Do:
>>>>>When: Do: [Create][SpawnMonster][monster][at position][IWP – EnemySpawner][Top side]

And there you have it; a method to store and define your monsters’ information and a method of spawning them in the world. The amount of RNG you can add to this system is well, I think fairly unlimited, though as I did mention there is a cap on the number of brains a single object can store. I would recommend that if you use this system on a massive scale, that you create multiple storage logic cubes and adjust your code to point at their respective references.

Questions? Comments? Improvements? Let me know!

bobkidd73: Thanks for posting your progress, makes for good reading.

NightmareUk1989: Useful tip use the raycast ability to detect the terrian paint you standing on to determine monster pool it mean you can have safe spots like paths/ roads that dont spawn stuff, no one wants a rng encouter happen at your spawn. 13 paint slots will be plenty variaty

Kohldar:Totally agreed and I do have a raycast currently doing that. Basically (in theory-Kode), the raycast adjusts a Boolean where you can either encounter monsters or not encounter mosters; When encounterable equals true, it kicks off a random timer for that encounter to occur.

NightmareUk1989: Cool was alot of text must of missed you mention it. It really is a neat feature and can even be used for path finding ai with advanced code like a grid mesh to stop ai leaving terrian e.g falling off a cliff or going near water edges

Kohldar: Update: Ran into a few snags trying to organize and debug the flow of Kode. Had to scrap what I had and restart the encounter process. However I will divulge what I tried and what I found out.

The Monster DB is working great in terms of usability. The snag that I ran into was that with the way things are, I cannot add an object from the DB directly into an object set. It first has to be created and then added. This actually turned out to be okay, as it gives affirmation that the correct enemies are being spawned.

This doesn’t work:
When: [Once] Do:
>>>>>When: Do: [MonID][equals][1]
>>>>>When: Do: [brainvar – SpawnMonster][equals][Storage][get brain][in channel][Monsters][MonID]
>>>>>When: Do: [ObjSet – MonsterPool][increment by][SpawnMonster][monster] // remember that this variable in the @Goblin page in a previous post is used to stored the IWP template of the Goblin monster

What I mean by doesn’t work is that if you were to try to create that monster from the object set, the variable appears to be blank. Some of the other community members may be able to further explain this, but my guess is because it is storing a template and not an existing object.

However, this does work:
When: [Once] Do:
>>>>>When: Do: [MonID][equals][1]
>>>>>When: Do: [brainvar – SpawnMonster][equals][Storage][get brain][in channel][Monsters][MonID]
>>>>>When: Do: [objvar – spawnMonster][equals][create][SpawnMonster][monster][at position][position]
>>>>>When: Do: [ObjSet – MonsterPool][increment by][spawnMonster]

Now, the purpose of the MonsterPool object set is to give us a pool of monsters to randomly select from. Basically, depending on what location you are at, it grabs all the monsters from that location from the DB and loads them into the pool. As mentioned previously, there is a variable that also selects how many monsters are going to spawn for the battle.

So we know the following at this point; how many monsters are going to spawn and which monsters can spawn here.

Side note: I have not started any RNG calcs for monster spawn probability. For simplicity, I will probably have to come back to this. I have also set up a debug mode to show all kinds of information for testing.

Next up: Initiating the combat scene!

Update: got the monster pool object set working as I want it to. Re-working the RNG Kode now in terms of which monsters are spawning. However, I am doing some rethinking on scenarios. I will do my best to explain the concept;

In my many (J)RPGs, the encounters you run into are random in the sense that they could happen anytime, but the monster load outs are generally pre-determined. For example, when running around a zone you encounter a pack of 3 skeletons (easily defeat-able). The next encounter you run into a couple of stronger monsters, werewolves (moderate). The next encounter is a single tough enemy Abomination (Difficult). In my current system, since all of these monsters are in the same zone, they all would spawn into pool. However, the direction that my system is going, you could theoretically run into a scenario of 5 Abominations. Now to some degree, this may seem challenging, but personally I think it wrecks the system. The odds of it happening are calculable and by no means unheard of, yet at the same time this would cause a HUGE spike in game difficulty.

So moving forward, I am designing the system (which I think I am correctly using the term) with more of a “pseudo-RNG” approach, in which a Seed# is provided and can be reproduced by using the same seed value(much how Minecraft and the much-looking-forward-to No Man’s Sky uses seeds to generate worlds/planets.) This is a rough theory Kode of the logic I will be playing around with and testing:

1. When Encounter occurs, a seed is rolled (we’ll use number of enemies as the seed, so a value between 1 and 5)
2. When a random roll occurs and the value is 1,2,3,4, or 5, there could be several different encounters programmed for each

When: [Number of Enemies][equal to][1] Do: [Num-Scenario][equals][ceiling][RandomNumber][0][to][2]
>>>>>When: [Scenario][equals][1] Do: [Call Page][1 Abomination]
>>>>>When: [Scenario][equals][2] Do: [2 Werewolves 2 Skeletons]
When: [Number of Enemies][equal to][2] Do: [Num-Scenario][equals][ceiling][RandomNumber][0][to][2]
>>>>>When: [Scenario][equals][1] Do: [Call Page][2 Skeletons]
>>>>>When: [Scenario][equals][2] Do: [Call Page][2 Werewolves]
When: [Number of Enemies][equal to][3] Do: [Num-Scenario][equals][ceiling][RandomNumber][0][to][2]
>>>>>When: [Scenario][equals][1] Do: [Call Page][3 Skeletons]
>>>>>When: [Scenario][equals][2] Do: [Call Page][2 Skeletons and 1 Abomination]

Then, if we need to adjust the probabilities for each of the encounters, we simply modify the Random Number range for the [Scenario]

When: [Number of Enemies][equal to][1] Do: [Scenario][equals][ceiling][RandomNumber][0][to][100]
>>>>>When: [Scenario][less than][30] Do: [Call Page][1 Abomination] // ~30% chance to occur
>>>>>When: [Scenario][greater than or equal to][30] Do: [2 Werewolves 2 Skeletons] // ~70% chance to occur

I think doing it this way works in the favor of balancing the game and keeping the difficulty relative, instead of getting a MASSIVE spike in difficulty and getting annihilated, simply because the “odds were not in your favor”.

What’s the community’s thoughts on the topic?

Update: In discussing mechanics with some other community members, I realized an improvement to the whole system. However, this will be the last update on this post as, I think I’ve got the code all figured out. Through the Kode, I was able to determine that using arrays to store information on small to much larger scales works really well. I’ll give my review of a few points:

1. Scalability: 7/10
This system doesn’t work for extremely large lists of information. Theoretically, you can only add 100 records to a single “storage” object. Though the amount of information stored within those records, I’m not really sure. However, if you break up your “lists” of information into smaller lists, it works great. (ex. instead of a list of All Items, break it up into Weapons, Consumables, etc, for each storage bank.

2. Repeatability: 9/10
Creating a new record in an array is as easy as copying a page and replacing the raw data. The reason its not a perfect 10/10 is because if you have 100 records and you add or remove something from one record, you’ll have to add it manually to each entry. This is where critical thinking and planning ahead can work wonders to reduce time and effort.

3. Logic: 9/10
Following the Kode is very easy and flows wonderfully.

In closing, I plan on utilizing this system for a bigger WIP, though if anyone is interested, I could take a shot at recording a youtube tutorial on how to set it up and use it. If you’d like to see this, let me know. If more than 1 or 2 people are wanting to see a demo, I’ll do it, else you’ll have to wait till I post a WIP of it in use. Thanks for the continued support fellow sparkers!

Psychokiller188: When building and developing mmorpgs we were mostly using area and zones that were controlled by a specific manager knowing its territory.

-> That said manager can be a brain in PS knowing its limits on the map, whatever form you are able to draw on the map

That manager knows at any point what mobs it can spawn, and howmany of them.

A manager, when created, is always sleeping, until a player enters its territory. That setting is usually for hard memory saving. Online you will mostly encounter game servers that do wake managers when players do enter the thirs neighboor territory, so that they can see from far that monsters are wandering miles away.

Mobs should be aware of what’s beside them. Meaning a manager should be able to sleep and wake a monster if needed to lower both memory usage and cpu cycles on the servers. That is also true in a game as Project Spark.

The database contains informations about mobs. I see more a monster being a logic cube, with a “stats page”, a “spawn page”, a “despawn page”, a “die page” etc etc etc. The manager in the territory has a list of monsters, so logic cubes, it can spawn, with the probability and the amount (both max territory monster and max monster type) and can just call the spawn page of a selected cube to trigger a spawn in a random location chosen within its controlled territory.

I spent two years on developing the perfect spawn engine, did not get to the perfection by far 🙂

Kohldar: Your scope for an MMO sounds awesome! I am merely looking to make my own JRPG, with my tweaks to it to be made. I don’t think there is ever going to be a “perfect” spawn system that is universal. Generally it has to be tailored to the scope of the game. The point of this post was to see if you could create a database containing a ton of information using arrays and multi-brains and then reference that information from it. Not saying its THE BEST or anything else is inferior, but as for me in my Programmer’s walk, I’ve found it easy to use and understand. I’m going to continue to use it to see how it handles other applications and I will likely learn a lot more from it.

Psychokiller188: Sure, that’s also why I didn’t say anything how you should store the info, as this is your vision and it is not wrong. I merely wanted to say how I see the usage of the data after. Because, storing info is quite simple, but before storing them you should already have an idea on how you can reuse them then 🙂

Kohldar: Ah! Now I see 😀 Sorry I can be a bit dense at times!

Comments are closed.

Website Powered by WordPress.com.

Up ↑

%d bloggers like this: