ID:1474449
 
Problem description: So I don't really have any code for this problem. I'm trying to figure out what would be the best way to program a wave system.

I don't want to have to define a load of locations for each NPC and I'm not really well versed in lists if at all.

Anyway an example would be, the NPC's all spawn in a line, but the only way I can think of doing this is by having multiple location vars and a lot of new

var
a1 = 1
a2 = 2
a3 = 3
a4 = 4
a5 = 5

proc
waveexample()
var/M = /mob/npc/
while(1)
new M(locate(a1,1,1))
new M(locate(a2,1,1))
//etc
sleep(10)


What is a wave system?

I can help you figure out what you need to do, but you are going to need to describe what you are trying to do before I can help you.
Gonna take a shot at this since I'm learning to use bitflags more often when necessary, and this seems like the perfect case to try exactly that. Feel free to point out anything I should change.

proc/inline_spawn(turf/center, direction, tiles)
if(!center || !direction || !tiles)
return

var/list/locations = list(center)
var/x = center.x
var/y = center.y
var/z = center.z

for(var/i in 1 to tiles)
if(direction & direction - 1)
if(direction == NORTHEAST)
locations.Add(locate(x + i, y + i, z), locate(x - i, y - i, z))
if(direction == NORTHWEST)
locations.Add(locate(x - i, y + i, z), locate(x + i, y - i, z))
if(direction == SOUTHEAST)
locations.Add(locate(x + i, y - i, z), locate(x - i, y + i, z))
if(direction == SOUTHWEST)
locations.Add(locate(x - i, y - i, z), locate(x + i, y + i, z))
else
if(direction & NORTH || direction & SOUTH) locations.Add(locate(x, y + i, z), locate(x, y - i, z))
//if(direction & SOUTH) locations.Add(locate(x, y - i, z), locate(x, y + i, z))
if(direction & EAST || direction & WEST) locations.Add(locate(x + i, y, z), locate(x - i, y, z))
//if(direction & WEST) locations.Add(locate(x - i, y, z), locate(x + i, y, z))

return locations


I spent about 30 minutes to an hour trying to figure out how I'd use bit comparison (er?) for diagonals rather than directly comparing the variable to each direction, but couldn't come up with anything.
Actually, in this case, you would be better off using get_step(), FKI.

proc
getline(turf/start,dir,dist)
. = list()
for(var/i in 1 to dist)
if(!start) return .
start = get_step(start,dir)
. += start


The reason that your approach last time was wrong to use the loop, was because we didn't need to get each turf, and instead just wanted to get a turf that was at distance X from the user.

So in this case, there's no need to use bitflags at all, because we already have a function that operates just fine without all the extra work of splitting it up and getting deltas.


As for the OP, I'd avoid using the above helper function, and instead use something more like this:

projectile
parent_type = /obj
var
trail
damage = 0
owner
New(turf/t,owner,dir)
src.dir = dir
src.owner = owner
. = ..()
if(src.Move(t))
walk(src,dir)
Move()
var/atom/oldloc = src.loc
. = ..()
if(.)
src.Moved(oldloc)

proc/Moved(atom/oldloc)
if(src.trail&&oldloc)
new src.trail(oldloc)

Bump(atom/obstacle)
if(istype(obstacle,/mob/combatant))
var/mob/combatant/c = obstacle
c.TakeDamage(src.damage,src.owner)
del src


If you want to leave a trail behind the head object, simply set the trail variable to a type.
By wave system I mean enemies, kind of how castle has waves of enemies each round.

I'm only creating a simple game instead of in the past where I've tried to start big to learn DM.

Anyways an example of what i mean by a wave system, I just need to learn how to spawn the enemies in each round. Without having a load of variables for each enemy I spawn at different positions. So enemy 1,2,3 all spawn in a horizontal line.

I'm terrible at explanations but hopefully you understand what I'm going for. Also I already have the movement down for the enemies.

Also FKI, thank you but I have no idea what any of that does.
And that's precisely why we needed clarification, I was starting to think you meant blast/projectile waves.

A wave system simple counts the number of enemies available per wave, and spawns new enemies when a certain criteria is met.

So... Let's take a look at a basic datum that can give you an idea of how you would handle this.

wave
var/tmp
enemies = 0 //number of enemies in the wave
enemies_alive = 0 //number of enemies alive from this wave
list/spawnpoints = list() //a list of places the monsters will spawn.
proc
//creates an enemy if spawnpoints are open, and
//we have enemies left in the wave
spawn_enemy()
if(spawnpoints.len&&enemies)
var/turf/t = spawnpoints[1]
new/mob/enemy(t,src)
enemies--
return 1
return 0

//this will allow you to place wavespawner objects
//with named tags: castle1, castle2, castle3
get_spawnpoints(spawn_tag)
var/count = 1
var/obj/wavespawner/w = locate("[spawn_tag][count]") in world
while(w)
src.spawnpoints += w.loc
w.wave = src
w = locate("[spawn_tag][++count]") in world

//this will allow you to run the wave until all enemies are dead.
run_wave()
while(enemies)
while(spawn_enemy())
sleep(10)
while(enemies_alive)
sleep(10)
wave_complete()

//do whatever you want here.
wave_complete()

//wavespawner objects are special objects that we place in the world
//These objects will determine when a turf is occupied so we
//don't bunch mobs up
obj/wavespawner
var/tmp
wave/wave
Crossed(atom/movable/o)
if(wave&&o.density)
wave.spawnpoints -= src.loc
Uncrossed(atom/movable/o)
if(wave&&o.density)
wave.spawnpoints += src.loc

//enemies need to know they are part of a wave, and the wave needs to know the enemy exists.
mob/enemy
var/tmp
wave/wave
New(atom/nloc,wave/wave=null)
if(nloc)
src.Move(nloc)
src.wave = wave
if(src.wave)
src.wave.enemies_alive++
. = src
Del()
if(src.wave)
src.wave.enemies_alive--
. = ..()


There are better ways to handle this, but this is a basic example of one implementation of a wave-based enemy spawner.

You will of course, need to rewrite all of this suited to your needs, and perform the necessary setup to get it working in-game.

Nobody is going to give you much more than the above without effort on your behalf.

You will note, however, that my system uses tags to locate spawnpoints. I add special objects to the map that mark where enemies will spawn when a wave is currently in progress, and it will not spawn enemies while the spawnpoints are occupied.

This approach would be one of many ways to set up a dynamic approach to defining enemy spawnpoints for a wave-based minigame. You just need to add the objects on the map, and set their tags to the proper format that the wave system expects.
Thank you very much, I wasn't expecting you to do that much to be honest and I've been on BYOND long enough now to know no ones going to do it for me.

What I'll do is just start a new environment, study it and then change it to meet my demands.
Rickoshay, the snippet in your first post has a very obvious pattern to it. You want to spawn NPCs at a location given by "(x, 1, 1), where x goes from 1 to 5." That translates pretty simply to a for() loop, for x in 1 to 5.
proc/spawn_wave(type = /mob/npc)
for(var/x in 1 to 5)
new type (locate(x, 1, 1))
In response to Kaiochao
Kaiochao wrote:
Rickoshay, the snippet in your first post has a very obvious pattern to it. You want to spawn NPCs at a location given by "(x, 1, 1), where x goes from 1 to 5." That translates pretty simply to a for() loop, for x in 1 to 5.
> proc/spawn_wave(type = /mob/npc)
> for(var/x in 1 to 5)
> new type (locate(x, 1, 1))
>


Wow, I can be so narrow minded sometimes and really try to over complicate things. Thanks Kaiochao.
Ter, I used the sample above to practice making a minigame. Could you explain what you mean by these special markers?

I placed down serval wavespawners and I gave it a unique tag. Is that correct?

Also after I placed the spawners, I made a start verb and when it spawned the monsters, they all spawned on the first spawner instead of rotating around the spawners, so I made it choose a spawner out of spawners.len
You need to set the wave variable of the wave spawners to the current wave datum when you initialize your minigame.

I made a small adjustment to my example. Take a look.
At first my plan was to, create a good bit of areas and then after a certain ammount of waves past, you get switched to a different area. Each area will have 2-5 spawners, for the variety of spawning.

But while I was writing the comments, I thought about it, and I'm starting to think this was intended for 1 area with like 20 different spawners.

Anyways, I added these comments because I wanted to make sure I understood this to the fullest. I also added a question for you, under 'get_spawnpoints'.


// the comments with "/**/" are Ter13's
// the comments with "//" are Flysbad's


wave

var/tmp

// The total ammount of enemies the round will spawn.
enemies = 0

// The ammount of enemies that are alive.
enemies_alive = 0

// a list of spawn-points the monsters will be able to spawn at.
list/spawnpoints = list()

proc

/* creates an enemy if spawnpoints are open,
and we have enemies left in the wave. */

spawn_enemy()

// this proc checks the wave datum's spawnpoints list and checks to see if there are enemies to spawn.
// every time an enemy is spawned, the 'src.enemies' var is decreased..
// when 'src.enemies' reaches 0, this proc will return false.
// if 'src.enemies' is greater than 0, this proc will return true.
if(spawnpoints.len && src.enemies)

// create a turf var and grab the first wavespawner in the 'src.spawnpoints' list.
var turf/t = src.spawnpoints[1]

// create a list of all the enemies, and remove the parent type of the list.
var/all_enemies = typesof(/mob/enemy/) - /mob/enemy/

// pick an enemy from the list
var/e2spawn = pick(all_enemies)

// create a new 'e2spawn' and set it's location to t and it's 'wave' variable to src.
var/mob/enemy/e = new e2spawn(t, src)

src.enemies --

// return true if everything went according to plan.
return 1

// either there wasn't a wavespawner in 'src.spawnpoints'
// or 'src.enemies' is 0 or below. (which it shouldn't go below.)
// return false
return 0


/* this will allow you to place wavespawner objects
with named tags: castle1, castle2, castle3 */

get_spawnpoints(var/spawn_tag)

// create a var to keep track of a number, starting at 1.
var count = 1

// create another var to keep track of a wavespawner.
// if the var spawn_tag was "Tutorial" and the stage had 3 wavespawners
// it would set w to the first wavespawner it found..
// ex: var/obj/wavespawner/w = locate("Tutorial, 1") in world
var obj/wavespawner/w = locate("[spawn_tag], [count]") in world

// if w was found, it'll initate a loop.
while(w)

// add 'w.loc' to 'src.spawnpoints' list for we know this wave will include this wavespawner.
src.spawnpoints += w.loc

// set 'w.wave' to this wave datum.
w.wave = src

// force w to change into the NEXT wavespawner, if available..
// if it isn't available, the while() loop will auto-break
// ex: w = locate("Tutorial, 2") in world
w = locate("[spawn_tag], [++count]") in world

// NOTE TO TER13: Wouldn't this just keep looking for 2.. it doesn't increase the count var.
// I'm not sure if this was done intentually or you forgot to put the line to increase the var.


/* this will allow you to run the wave until all enemies are dead.
*/

run_wave()

// this proc is all about while loops..
// it starts one while, and under that while, is another while..
// so the second while puts the first while on pause until it's finished exucuting.

// this is the first while
// it checks to see if the wave datum 'src.enemies' is greather than 0.
while(src.enemies)
// if 'src.enemies' is greather than 0, it'll exucute the second while...

// this is the second while.
while(src.spawn_enemy())
// this while exucutes the 'src.spawn_enemy()' proc.
// scroll to the 'spawn_enemy()' proc see what's called..
// sleep for 1 second, and then repeat the second while..
sleep(10)

// if 'spawn_enemy()' returned false, meaning 'src.enemies' is 0..
// the first while will be broken, so it'll continue the proc..

// this is the third while, it's its own while.. so that's good.
while(src.enemies_alive)
// if there are any enemies spawned BUT not killed..
// sleep 10..
// if all enemies are dead, meaning 'src.enemies_alive' is 0..
// this while will be broken, and 'wave_comlete()' will be exucuted.
sleep(10)

src.wave_complete()


/* do whatever you want.
*/

wave_complete()

// leave this alone for we can over-ride it later on.




// WAVES //

w1
enemies = 5

// when you complete wave1, this will happen.
wave_complete()
world << "congratulations, you're now a wave higher!"

w2
enemies = 8

// when you complete wave2, this will happen.
wave_complete()
src.Gold += 200
src.Characters += new/obj/new_character
world << "You unlocked a new character!"

w3
enemies = 10
wave_complete()
world << "You beat the game."
++count is the pre-increment instruction. It will actually increase the variable.
It was being increased by two instead of one?
Fly, how would I use w1, w2 and w3 to start a wave and for the others procedures to know which wave we're on?
In response to Ter13
Ter13 wrote:
++count is the pre-increment instruction. It will actually increase the variable.


Here's a link to expand on Ter's point. The distinction is made when incrementing a variable X and Y is set either to the initial value of X, or to the new value of X+1.
In response to Kalzar
Kalzar wrote:
Ter13 wrote:
++count is the pre-increment instruction. It will actually increase the variable.


Here's a link to expand on Ter's point. The distinction is made when incrementing a variable X and Y is set either to the initial value of X, or to the new value of X+1.

Your link is solid, but your summary after the link is a little misleading.

++x/x++ is a statement. Statements have return values.

++x increments x by 1, and then returns the value of x after it was incremented.

x++ increments x by 1, but returns the value of x before it was incremented.

Meaning:

var/x = 0
world << x++ //will output 0. x is equal to 1
world << x //will output 1
world << ++x //will output 2. x is equal to 2