Page 1 of 1

Spawning Loop

Posted: Thu Apr 20, 2006 1:22 am
by Broadus
As some of you probably know by now, I've been modifying the single player levels of Allied Assault, Spearhead and Breakthrough. On the first level of the Russian mission, I've got twenty Russian conscripts with rifles, zero accuracy and one hit point. All twenty of them follow the player at once. What I want to do is have them respawn right when they die, ensuring that the player is constantly followed by these twenty suicidal cannon fodder soldiers.

How do I get a spawned character to spawn again where it started every single time it dies?

Posted: Thu Apr 20, 2006 1:27 am
by lizardkid
a simple wavespawn thread, like this.

Code: Select all

wavespawn:

while(isAlive $player)
{
       for(local.i = 0; local.i < level.russians.length; local.i++)
       {
              if(!isAlive level.russians[local.i])
              {
                     // respawn him and add to the array again.
                     // level.russians[local.i] = local.spawned
              }
              wait .5
       }
       wait 5
}
end
it's just an outline, but you get the idea. just make local.spawned the new guy, spawn him where you like, and maybe run a spawning script to set him to follow the player and whatnot. all the same as the guys you spawned at first.

Posted: Thu Apr 20, 2006 5:01 am
by bdbodger
In the snowfun map I did I used the spawner system like this

Code: Select all

level.snowguy = exec global/makearray.scr $snowguy

	for (local.i=1;local.i <= level.snowguy.size;local.i++)
	level.snowguy[local.i] = waitthread global/spawner.scr::spawner_create level.snowguy[local.i]

if((getcvar sc_noai) != "1")
{

	for (local.i=1;local.i <= level.snowguy.size;local.i++)
	level.snowdude[local.i] = waitthread global/spawner.scr::spawner_activate level.snowguy[local.i]

	thread respawn_ai
}
that makes the guys targetnamed $snowguy into spawners and level.snowdude is spawned from the spawner information then I did this

Code: Select all

respawn_ai:

while(1)
{
	if((getcvar sc_noai) != "1")
	{
		for(local.i=1;local.i <= level.snowguy.size ;local.i++)
		{
			if!(isalive level.snowdude[local.i])
			{
			wait (getcvar sc_aispawntime)
			level.snowdude[local.i] = waitthread global/spawner.scr::spawner_activate level.snowguy[local.i]
			}
		}
		if (level.aigone == 1)
		level.aigone = 0
	}
	if(((getcvar sc_noai) == "1")&&(local.aigone !=1))
	{
		for(local.i=1;local.i<= level.snowdude.size;local.i++)
		{

		if(level.snowdude[local.i])
		level.snowdude[local.i] remove

		level.aigone = 1
		}
	}

waitframe
}
end
Useing the spawner.scr preserves all the info about the ai includeing his original origin , model and the gun he is useing

Posted: Fri Apr 21, 2006 12:29 am
by Broadus
What do I do if I don't have any level. or local.i characters like that? I have to spawn characters, like this:

local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder1" "gun" "nagant rifle"
$RussianFodder1.destination = $player
$RussianFodder1.health = 1
$RussianFodder1 thread global/friendly.scr::friendlythink
$RussianFodder1.friendtype = 1
$RussianFodder1.distance = 200
$RussianFodder1.accuracy = 0

There are twenty of these guys. What do I do about characters that I have to spawn with scripts?

Posted: Fri Apr 21, 2006 3:11 am
by lizardkid
my wavespawn relies on one spawn point, an example how to actually do it with mine is like this.

Code: Select all

wavespawn:

local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder1" "gun" "nagant rifle" 
local.person.destination = $player 
local.person.health = 1 
local.person thread global/friendly.scr::friendlythink 
local.person.friendtype = 1 
local.person.distance = 200 
local.person.accuracy = 0

while(isAlive $player) 
{ 
       for(local.i = 0; local.i < level.russians.length; local.i++) 
       { 
              if(!isAlive level.russians[local.i]) 
              { 
                     // respawn him and add to the array again. 
                     level.russians[local.i] = local.person
                     local.person.origin = ( local.person.origin + ( (randomint(4) *50 ) (randomint(4) *50 ) 0 ) )
              } 
              wait .5 
       } 
       wait 5 
}
TO DO THIS, you need a level.russians array, easiest way to do this (in my view) is to just add them as you spawn them by adding these lines(assuming they're all local.person)

Code: Select all

level.russians[local.i] = local.person
local.i++
and putting this BEFORE you spawn the russians.

Code: Select all

local.i = 0
that's jsut how i'd do it.

Posted: Fri Apr 21, 2006 2:48 pm
by Broadus
From what you said, all I could come up with was this:
level waittill spawn
thread wavespawn

. . .

wavespawn:
local.i = 0
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder1" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder2" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder3" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder4" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder5" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder6" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder7" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder8" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder9" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder10" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder11" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder12" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder13" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder14" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder15" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder16" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder17" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder18" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder19" "gun" "nagant rifle"
local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder20" "gun" "nagant rifle"
$RussianFodder1.destination = $player
$RussianFodder1.health = 1
$RussianFodder1 thread global/friendly.scr::friendlythink
$RussianFodder1.friendtype = 1
$RussianFodder1.distance = 200
$RussianFodder1.accuracy = 0
$RussianFodder1 type_attack "running"
$RussianFodder2.destination = $player
$RussianFodder2.health = 1
$RussianFodder2 thread global/friendly.scr::friendlythink
$RussianFodder2.friendtype = 1
$RussianFodder2.distance = 200
$RussianFodder2.accuracy = 0
$RussianFodder2 type_attack "running"
$RussianFodder3.destination = $player
$RussianFodder3.health = 1
$RussianFodder3 thread global/friendly.scr::friendlythink
$RussianFodder3.friendtype = 1
$RussianFodder3.distance = 200
$RussianFodder3.accuracy = 0
$RussianFodder3 type_attack "running"
$RussianFodder4.destination = $player
$RussianFodder4.health = 1
$RussianFodder4 thread global/friendly.scr::friendlythink
$RussianFodder4.friendtype = 1
$RussianFodder4.distance = 200
$RussianFodder4.accuracy = 0
$RussianFodder4 type_attack "running"
$RussianFodder5.destination = $player
$RussianFodder5.health = 1
$RussianFodder5 thread global/friendly.scr::friendlythink
$RussianFodder5.friendtype = 1
$RussianFodder5.distance = 200
$RussianFodder5.accuracy = 0
$RussianFodder5 type_attack "running"
$RussianFodder6.destination = $player
$RussianFodder6.health = 1
$RussianFodder6 thread global/friendly.scr::friendlythink
$RussianFodder6.friendtype = 1
$RussianFodder6.distance = 200
$RussianFodder6.accuracy = 0
$RussianFodder6 type_attack "running"
$RussianFodder7.destination = $player
$RussianFodder7.health = 1
$RussianFodder7 thread global/friendly.scr::friendlythink
$RussianFodder7.friendtype = 1
$RussianFodder7.distance = 200
$RussianFodder7.accuracy = 0
$RussianFodder7 type_attack "running"
$RussianFodder8.destination = $player
$RussianFodder8.health = 1
$RussianFodder8 thread global/friendly.scr::friendlythink
$RussianFodder8.friendtype = 1
$RussianFodder8.distance = 200
$RussianFodder8.accuracy = 0
$RussianFodder8 type_attack "running"
$RussianFodder9.destination = $player
$RussianFodder9.health = 1
$RussianFodder9 thread global/friendly.scr::friendlythink
$RussianFodder9.friendtype = 1
$RussianFodder9.distance = 200
$RussianFodder9.accuracy = 0
$RussianFodder9 type_attack "running"
$RussianFodder10.destination = $player
$RussianFodder10.health = 1
$RussianFodder10 thread global/friendly.scr::friendlythink
$RussianFodder10.friendtype = 1
$RussianFodder10.distance = 200
$RussianFodder10.accuracy = 0
$RussianFodder10 type_attack "running"
$RussianFodder11.destination = $player
$RussianFodder11.health = 1
$RussianFodder11 thread global/friendly.scr::friendlythink
$RussianFodder11.friendtype = 1
$RussianFodder11.distance = 200
$RussianFodder11.accuracy = 0
$RussianFodder11 type_attack "running"
$RussianFodder12.destination = $player
$RussianFodder12.health = 1
$RussianFodder12 thread global/friendly.scr::friendlythink
$RussianFodder12.friendtype = 1
$RussianFodder12.distance = 200
$RussianFodder12.accuracy = 0
$RussianFodder12 type_attack "running"
$RussianFodder13.destination = $player
$RussianFodder13.health = 1
$RussianFodder13 thread global/friendly.scr::friendlythink
$RussianFodder13.friendtype = 1
$RussianFodder13.distance = 200
$RussianFodder13.accuracy = 0
$RussianFodder13 type_attack "running"
$RussianFodder14.destination = $player
$RussianFodder14.health = 1
$RussianFodder14 thread global/friendly.scr::friendlythink
$RussianFodder14.friendtype = 1
$RussianFodder14.distance = 200
$RussianFodder14.accuracy = 0
$RussianFodder14 type_attack "running"
$RussianFodder15.destination = $player
$RussianFodder15.health = 1
$RussianFodder15 thread global/friendly.scr::friendlythink
$RussianFodder15.friendtype = 1
$RussianFodder15.distance = 200
$RussianFodder15.accuracy = 0
$RussianFodder15 type_attack "running"
$RussianFodder16.destination = $player
$RussianFodder16.health = 1
$RussianFodder16 thread global/friendly.scr::friendlythink
$RussianFodder16.friendtype = 1
$RussianFodder16.distance = 200
$RussianFodder16.accuracy = 0
$RussianFodder16 type_attack "running"
$RussianFodder17.destination = $player
$RussianFodder17.health = 1
$RussianFodder17 thread global/friendly.scr::friendlythink
$RussianFodder17.friendtype = 1
$RussianFodder17.distance = 200
$RussianFodder17.accuracy = 0
$RussianFodder17 type_attack "running"
$RussianFodder18.destination = $player
$RussianFodder18.health = 1
$RussianFodder18 thread global/friendly.scr::friendlythink
$RussianFodder18.friendtype = 1
$RussianFodder18.distance = 200
$RussianFodder18.accuracy = 0
$RussianFodder18 type_attack "running"
$RussianFodder19.destination = $player
$RussianFodder19.health = 1
$RussianFodder19 thread global/friendly.scr::friendlythink
$RussianFodder19.friendtype = 1
$RussianFodder19.distance = 200
$RussianFodder19.accuracy = 0
$RussianFodder19 type_attack "running"
$RussianFodder20.destination = $player
$RussianFodder20.health = 1
$RussianFodder20 thread global/friendly.scr::friendlythink
$RussianFodder20.friendtype = 1
$RussianFodder20.distance = 200
$RussianFodder20.accuracy = 0
$RussianFodder20 type_attack "running"
level.russians[local.i] = local.person
local.i++
while(isAlive $player)
{
for(local.i = 0; local.i < level.russians.length; local.i++)
{
if(!isAlive level.russians[local.i])
{
level.russians[local.i] = local.person
local.person.origin = ( local.person.origin + ( (randomint(4) *50 ) (randomint(4) *50 ) 0 ) )
}
wait .5
}
wait 5
}
end
The allies will spawn, but they won't REspawn.

By the way, is type_attack "running" an actual type_attack? I thought I saw some Germans that used it, before, and I wanted to get the fodder to run as they attacked (rather than stopping and crouching) to ensure that they would die faster, but "running" didn't do anything. Or maybe they just have to actually be running in order to use it?

Posted: Sat Apr 22, 2006 6:16 am
by lizardkid
very close, just missed the part where you actually fill the array.
after each local.person is created put this in

Code: Select all

level.russians[local.i] = $RussianFodder#
but it's a nightmare for me to try to find start and end places in that block of letters, so i'll just give the first one as an example.

Code: Select all

local.person = spawn models/human/sovietlevel1.tik "origin" "5131.96 7600.09 -159.88" "targetname" "RussianFodder1" "gun" "nagant rifle" 

level.russians[local.i] = $RussianFodder#
do that after each russian and it should work. it adds them to a little database that the loops use to keep track of whos alive and who isnt.

on second thought i spotted an error i made, DUR... here, copy/paste that in.

Code: Select all

$RussianFodder20.destination = $player 
$RussianFodder20.health = 1 
$RussianFodder20 thread global/friendly.scr::friendlythink 
$RussianFodder20.friendtype = 1 
$RussianFodder20.distance = 200 
$RussianFodder20.accuracy = 0 
$RussianFodder20 type_attack "running" 
level.russians[local.i] = local.person 
local.i++ 

local.originalOrig = 5131.96 7600.09 -159.88

while(isAlive $player) 
{ 
for(local.i = 0; local.i < level.russians.length; local.i++) 
{ 
if(!isAlive level.russians[local.i]) 
{ 
level.russians[local.i] = local.person 
local.person.origin = ( local.orignalOrig + ( (randomint(4) *50 ) (randomint(4) *50 ) 0 ) )
} 
wait .5 
} 
wait 5 
} 
end

sorry if thats confusing, i just added the 20th russian in for reference on where to put it.

Posted: Sat Apr 22, 2006 7:23 am
by jv_map
Broadus to me it looks someone needs to learn how to use methods :wink: not that you can't do it the way you try to do it but it's 3x the work and asking for trouble.

A method can be used to execute the same bit of code at any time, usually with different parameters. As you basically have 20x the same code, the repeating bits should be put in a method. Only one method is then required, and called 20x.

A small lesson in script structuring

A script naturally is divided into threads, pieces of code that start with a label and end with 'end'. A label is the name of the thread followed by a :. Example of a full script structure:

Code: Select all

main:
   // code here
end

somethreadname1:
    // code here
end

somethreadname2:
    // code here
end
Up to as many threads as you need. Although mohaa allows you to be sloppy without complaining, it's very wise and practical to stick to this structure at all times. All code between thread start and end should be indented, so you can identify the start and end of each thread.

The first thread is run automatically when the script is called. Any other threads are not run unless called.

Common mistakes:
Forgetting a label (especially the first one ('main')).
Forgetting an 'end' (especially for the first thread).
Putting code between threads (very bad).
Using the labels to 'goto' to (very bad).

Although you will find these mistakes in the stock scripts, it doesn't justify them. I think most of the scripts in mohaa were made by mappers with none or limited programming experience, and many of their practices are best not followed.

Threads

Now on to how to use threads.

Call a thread using 'thread'. This will launch the thread as a seperate process.

Call a thread using 'waitthread'. This way it is a method in the same process. The caller thread will wait till the callee has finished executing. A method may also return a value it calculated to the caller thread.

The commands 'exec' and 'waitexec' are best not used. Especially waitexec is tricky.. it waits for the called thread group, instead of waiting for a specific thread, which is usually unexpected. If you want to know more about thread groups, do a search ;)

Example of thread usage

This one is obviously very basic.

Code: Select all

main:
  waitthread example_method
end

example_method:
  println "example_method"
end
You can also send parameters to a thread. This is done by expanding the thread definition. You can return a value by expanding the thread end line.

Code: Select all

main:
  local.three = waitthread method_add 1 2
  local.five = waitthread method_add 2 3

  println "1 + 2 = " local.three "; 2 + 3 = " local.five
end

// returns number1 + number2
method_add local.number1 local.number2:
  local.result = local.number1 + local.number2
end local.result
Ofcourse this is a bit of a useless thread, but I think it makes sense as an example.

The labels 'number1', 'number2' and 'result' are arbitrary.

Again some hints: :idea:
Be very careful calling the same thread from within it. This creates a recursive structure, which is usually not desired. In many cases it's far better to place the contents of a thread entirely in a while loop.

A recursive structure with 'waitthread' will crash with a stack overflow unless there is a way out of the loop, for example and end inside the thread which is executed when certain conditions are met.

A recursive structure with 'thread' will not crash, but is very sloppy and inefficient. Always replace such a construction with a while loop, it is always possible.

Never use the goto command at all. It violates thread structure and is a lazy way to avoid learning something ;)

Now to your case. This requires using both waitthread and thread. Put all the repetitive code inside a method:

Code: Select all

// makes a guy with specified model, origin gun, and returns him
spawn_a_guy local.model local.origin local.gun:
  local.guy = spawn local.model origin local.origin gun local.gun
  local.guy.health = 1
  local.guy thread global/friendly.scr::friendlythink
  local.guy.friendtype = 1
  local.guy.distance = 200
  local.guy.accuracy = 0
  local.guy type_attack "running"
end local.guy
'end local.guy' returns a reference to the caller script.

Now you can easily spawn lots of guys by putting this in your main thread (or some other active thread):

Code: Select all

  for(local.i = 1; local.i <= 20; local.i++)
  {
    local.person[local.i] = waitthread spawn_a_guy "human/sovietlevel1.tik" (5131.96 7600.09 -159.88) "nagant rifle"
  }
If you now want to have em respawn, you can either add a thread to the for loop or to the spawn_a_guy method. Best do it like this:

Code: Select all

  for(local.i = 1; local.i <= 20; local.i++)
  {
    thread respawning_guy "human/sovietlevel1.tik" (5131.96 7600.09 -159.88) "nagant rifle"
  }

  (...)

end

respawning_guy local.model local.origin local.gun:

  local.last_position = local.origin

  while(1)
  {
    local.guy = waitthread spawn_a_guy local.model local.last_position local.gun

    while(isAlive local.guy)
    {
      local.last_position = local.guy.origin
      waitframe
    }
  
    // respawn with a small delay (2-4 secs)
    wait 2.0
    wait (randomfloat 2.0)
  }
end
This should help some 8-)

Posted: Sat Apr 22, 2006 8:46 am
by bdbodger
What JV is trying to tell you is you have 20 guys do a loop that loops 20 times and spawns a guy each time though the loop or runs a thread that spawns a guy with different coords that you send to the thread . Thats a bit of a simple explanation but may help . learn to use threads that can be used multiple times with values you send to it .

Posted: Sat Apr 22, 2006 8:52 am
by jv_map
bdbodger wrote:What JV is trying to tell you is you have 20 guys do a loop that loops 20 times and spawns a guy each time though the loop or runs a thread that spawns a guy with different coords that you send to the thread . Thats a bit of a simple explanation but may help . learn to use threads that can be used multiple times with values you send to it .
Damn.. saying it that way would have saved me a lotta time :P

Posted: Sat Apr 22, 2006 7:21 pm
by Broadus
Sorry, Lizardkid, but I just couldn't get your method to work. Thanks for the help, though.
I would rather have JV give me the really long explanation, bdbodger, than have him give me something cryptic, short and completely unexplanatory. I got JV's method to work, so thanks a lot for that, JV.
I had to get rid of the "local.origin = local.last_position" because that made the fodder respawn right where they died, kind of defeating the purpose of the fodder (as the player gets further from where the fodder spawns, it takes them longer to reinforce him so the level becomes harder).

Posted: Sun Apr 23, 2006 8:58 am
by bdbodger
well maybe if you combine the two :) I know JV's answer is better . I think maybe he should become a techical manual writer he is good at it lol That was probably his short answer :)

Posted: Sun Apr 23, 2006 10:16 am
by lizardkid
i wish i had the patience JV does. for me it's throwing thoughts at the keyboard and hoping they come out right.