Killed()

Killed()

Discuss the source code and development of Spring Engine in general from a technical point of view. Patches go here too.

Moderator: Moderators

User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Killed()

Post by Argh »

Sorry to bug y'all, I know you're trying to get things wrapped up for what looks like the most awesome release ever... however, I've found something rather odd (imagine that!).

Very simply, Killed() runs multiple times, all during the same frame. This is probably a result of various checks against the state of Killed, but it's a big problem- among other things, it results in certain scripts getting borked.

I discovered this whilst doing some optimization on PURE today- I have scripts that are firing weapons during death events, and I kept seeing them fire far more often than I specified.

After testing and re-testing several times, with different weapons and attempts at a workaround, I have come to the reluctant conclusion that the only possible cause must be that Killed() is running several times- it appears to be 4, even if all portions of the script return (0). This is, obviously, rather wasteful, and will almost certainly bork death animations that require strict timing.

After further testing, I have determined that the cause is a start-script or call-script command.

I'm using such things as includes, to send the COB to a set of logic trees to generate certain behavior. Now that I know what's causing this, I think I can fix it, for PURE. However, it's still not correct behavior- if Killed() calls another function, then it should wait until the function returns before doing anything else, not run that function multiple times...
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

I've done a couple more tests, and Killed() definitely can run the same command over multiple frames. The problem seems to be that long stacks of commands go over 1 frame (ye old stack-size problem, yay) and that this causes Spring to re-run the entire set of commands over again...

The lowest I've been able to push this is to 4 repeat commands :P This means that if I call twice, I can expect 8, and so forth and so on...
User avatar
Snipawolf
Posts: 4357
Joined: 12 Dec 2005, 01:49

Post by Snipawolf »

I have a no sleep emit weapon/sfx that does it up to 3 times.

Yes, this seems to be a problem. I don't need a single vehicle throwing out 20 pieces of debris @_@
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

Interesting, I haven't observed anything like that, not with exploded debries nor any other code in Killed() or other functions.
User avatar
lurker
Posts: 3842
Joined: 08 Jan 2007, 06:13

Post by lurker »

Argh wrote:I've done a couple more tests, and Killed() definitely can run the same command over multiple frames. The problem seems to be that long stacks of commands go over 1 frame (ye old stack-size problem, yay) and that this causes Spring to re-run the entire set of commands over again...

The lowest I've been able to push this is to 4 repeat commands :P This means that if I call twice, I can expect 8, and so forth and so on...
I have never heard of this before. Link and/or explain in detail please. I would like to try to fix this.
User avatar
det
Moderator
Posts: 737
Joined: 26 Nov 2005, 11:22

Post by det »

Could you explain how to reproduce this?
User avatar
Snipawolf
Posts: 4357
Joined: 12 Dec 2005, 01:49

Post by Snipawolf »

Well I could give some form of proof if I could make a video, but I can't, my comp is phail. My standard debris explosion emits 2~6 flamey debris and 3 smokey debris. One time, it emitted about 9 smokey ones and 12 flamey ones -_-

Reproduce? Call something that is VERY specific. As in, you would be able to see it twice in a row in an instant. Add it to the Killed() script and watch as it repeats multiple times.
User avatar
yuritch
Spring 1944 Developer
Posts: 1018
Joined: 11 Oct 2005, 07:18

Post by yuritch »

Try to add a get PRINT(0); to the Killed() function, that should show exactly how many times it's called.
Btw, I haven't seen any strange behaviours there, but I haven't played with svn Spring for about a month, so it can be something new.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Ok, to reproduce... hmm, this will pretty much require that you're using the latest build of PURE. You can see it there. See my release thread, download the game. I hate to make you do that, but it's in the midst of a lot of complex code.

If you review the BOS include, "ExplosionIncludes_PURE.h", it lists how many times a given "weapon" is supposed to fire, when a Unit runs Killed(). I use a call-script that passes to a function that runs the projectiles, then should return to the main script body of Killed() and remove the Unit.

I found this bug last night after playing a big game on Comet Catcher with AAI, where the low gravity made it abundantly clear that more than 12 projectiles were being spawned at once, which is what the previous version of PURE specifies.

Being somewhat horrified (each projectile calls CEGs and is CPU-heavy, so it dragged PURE down quite a bit when combat got heavy), I started building new code that would call it less frequently.

I discovered that there were several oddities, not all related:

1. Sometimes, Spring would just skip the process entirely. I'm not sure if the value of "severity" is being returned correctly, or if it's getting wiped out before the script finishes or whatever. But something odd's happening there. Return (0) seems to be the main issue- it's like when it hits that, even in a script called by Killed(), it ends Killed(). Probably the way that the death-animation code was done...

2. If I call just one "weapon" via call-script, then I get 4 projectiles. If I call just one weapon, within the main body of Killed(), then I get one. I thought Killed() was supposed to run once, and only stop when it returns 0, period. I've tested that, and it does not seem to work that way.

I tried PRINT, and it's not generating the results I'm expecting. It's something within Killed()'s loop in COBHandler, I think, not a BOS problem per se- trust me, I've been up, down and through all of that code, trying many variants.


Lastly, DGun doesn't use the MyGravity parameter... this proved to be a problem on low-grav maps, since it's the only weaponprojectile type that can currently be used to make animated rubble with. I really, really wish that Cannons firing from a script could just get fixed, and obey dir if force-fired instead of failing, even if that requires a parameter- they already have all of the things that would be ideal, such as MyGravity, good bounce behaviors, etc.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Found another issue. After doing some tests...

1. The value of severity is being returned correctly. Test after test, PRINT shows it returning correctly at the start and end of Killed().

2. corpsetype defaults to 0.

3. When corpsetype <> 0, usually you're supposed to see a different corpse. However, in my tests, I have not seen Spring call the lower-level corpses. It always calls corpsetype 1.

Why is this occuring? I would assume that the corpsetype is determined by the Feature's chain to other Features as it "degrades", but it looks like this chain is broken, at least with my unit I'm testing... and it's only broken in BOS- it works otherwise.

Here is the feature:

Code: Select all

[RadarTank_Dead]
{
	description=Dead RadarTank;
	object=DEAD_UNITS/RadarTank_Dead.s3o;
	featuredead=misc_wreckage_3x3a;
	footprintx=3;
	footprintz=3;
	height=4;
	blocking=1;
	hitdensity=100;
	metal=250;
	damage=40000;
	reclaimable=1;
}
So far as I know, it should call "misc_wreckage_3x3a", if the value of Severity > whatever I've set up, however, it does not do so. "misc_wreckage_3x3a" is in another file- is that borking things? It works if I just blow the corpse up... it just doesn't work when Killed() returns a corpsetype of 2...

Here's the function:

Code: Select all

Killed(severity, corpsetype)
{
	GET PRINT (severity, corpsetype);
	if (severity <= 50)
	{
	corpsetype = 1;
	call-script TinyExplosion();
	} else
	{	
	corpsetype = 2;
	call-script SmallExplosion();
	}
	GET PRINT (severity, corpsetype);
	return(0);
}
And I've definately seen the value of both severity and corpsetype match the results expected- it's simply that Spring isn't fetching the second corpse on the "chain"...
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

If Killed() does not return immediately the return value is used instead of the corpsetype var (call by reference doesn't work with delays).
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

So... if I set up the IF / THEN / ELSE tree before calling the function, it should work, at least for solving that? Ok, will test...
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

<tests> It doesn't work.

Oh, wait... you're basically saying that unless I terminate with a return (0) within the script body immediately, it won't work, period?

That's borked! That means that any time we have enough instructions that COBhandler needs to wait another frame to finish the stack, it won't produce the correct Corpse!

Killed really should run like this:

1. "I'm dead. Stop everything else. Run Killed()".

2. "I'm running Killed(), just like any other script. If I don't have a value I want yet, I'll wait until I get it, and I'll keep running I reach a return (0), terminating the script, or 10000 frames, which ever happens first."

If it doesn't work that way, all sorts of things get borked. It's better to have Spring halt if it doesn't reach a return (0), and return an error message. Given that having it "know" when it's looping endlessly is impossible... give it a static limit of frames- 10000, 100000- something really long, to allow for lengthy animation sequences. Then if it reaches that point without returning (0), post an error message to chat / infolog, and remove the Unit.
Last edited by Argh on 18 Dec 2007, 18:41, edited 1 time in total.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Tested KDR_11's statement.

It's true- if a script returns 0 immediately, then the right Corpse is generated. For example, this results in the "correct" behavior, but does NOT run my explosion script, because after the return (0) it quits (like it should):

Code: Select all

Killed(severity, corpsetype)
{
	GET PRINT (severity, corpsetype);
	if (severity <= 50)
	{
		corpsetype = 1;
		return (0);
	} else
	{	
		corpsetype = 2;
		return (0);
	}
	
	if (corpsetype == 1)
	{
		call-script TinyExplosion();
	}
	if (corpsetype == 2)
	{
		call-script SmallExplosion();
	}		
	
	GET PRINT (severity, corpsetype);
	return(0);
}


So, I'm right- the problem there is Spring's handling of "immediately".
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

If you delay it the return value is used, haven't you read my post? Return 0 means corpsetype = 0 if you had any sleeps in there. If you want another corpse type return 1 or something.

Why bother checking for nonterminating Killed() functions? That should be obvious to the mod maker.

WTF is that with stacks causing frame changes? I've never had that, I've had scripts that locked up completely with a sleepless loop and the whole game halted, no "after x commands go to the next frame". Also I'm seeing traces of evidence that you don't really understand what a stack does in an interpreter. It doesn't grow with the number of commands, it only grows with function calls and after those functions return it shrinks again. The stack just stores which address to return to and a bunch of local vars. If you want my guess, call-script probably causes a sleep.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

KDR, write a script that tries to execute 20+ instructions without a sleep. Watch what it does.

Come back to arguing with me after trying that. Spring doesn't always execute COB instructions on the frame they're called, period. We've known about this problem for ages. It's just a part of how things work.

It's not the same as an endless loop, which short of being halted by Killed() will run until Spring locks. Which is how it should be, frankly.

I'll try the return 1. I definitely don't know why that's supposed to work, but meh, if it fixes it, great. I don't think that's going to address the fundamental problem here, which is that the call-script occurs multiple times in the frame...
Why bother checking for nonterminating Killed() functions? That should be obvious to the mod maker.
There isn't any "obvious" way to see that, right now. Nor does the code work like I've described. Using a static upper limit would cut out a lot of crap in that whole code- end with a return (0) like everything else, instead of having to use special cases or whatnot. Go look at the way the code's handling these events- it appears that it starts a ticker if it encounters any significant delay at all... why not just start a ticker every time, then halt the ticker if the script returns (0), or X number of frames has passed? The way it's written right now is backwards, imo.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

This did not result in the behavior you stated it would. I get corpsetype 1 regardless that severity >50 (i.e., I self-destructed the Unit).

Code: Select all

Killed(severity, corpsetype)
{
	GET PRINT (severity, corpsetype);
	if (severity <= 50)
	{
		corpsetype = 1;
		call-script TinyExplosion();	
		return (0);
	} else
	{	
		corpsetype = 2;
		call-script SmallExplosion();		
		return (1);
	}
	GET PRINT (severity, corpsetype);
	return(1);
}
So, is that just mis-written BOS, or does it just not work? I'm not trying to pick a fight with you, KDR- there are a number of fundamental problems here, and it could be I'm just stupid, and haven't written my code correctly. The multiple-projectiles thing is definitely real, and this part isn't working right, either.

In case anybody cares what TinyExplosion, et al look like, here's a sample of the function being called:

Code: Select all

TinyExplosion()
{
var Random;
Random = RAND(1,4);

	if (Random == 1);
	{
		emit-sfx Rubble_Gun from EXPLODEPIECE;
		//emit-sfx Rubble_Gun2 from EXPLODEPIECE;
	}
	if (Random == 2);
	{
		emit-sfx Rubble_Gun from EXPLODEPIECE;
		//emit-sfx Rubble_Gun2 from EXPLODEPIECE;
		//emit-sfx Rubble_Gun3 from EXPLODEPIECE;
	}
	if (Random == 3);
	{
		emit-sfx Rubble_Gun2 from EXPLODEPIECE;
		//emit-sfx Rubble_Gun3 from EXPLODEPIECE;
		//emit-sfx Rubble_Gun2 from EXPLODEPIECE;
		//emit-sfx Rubble_Gun from EXPLODEPIECE;
	}
	if (Random == 4);
	{
		emit-sfx Rubble_Gun from EXPLODEPIECE;
		//emit-sfx Rubble_Gun from EXPLODEPIECE;
		//emit-sfx Rubble_Gun2 from EXPLODEPIECE;
		//emit-sfx Rubble_Gun3 from EXPLODEPIECE;
		//emit-sfx Rubble_Gun3 from EXPLODEPIECE;
	}

call-script RandomDeathSound();
}
Note that, yes, it calls another function, and won't terminate until it returns. RandomDeathSound() is a very similar function that uses a random number to pick sounds and play them. No rocket-science stuff.

This shouldn't matter, either. But it does.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

1 is 1, no? Try return 3.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Ok, that works! Good, then that solves that problem. It's not how it should work, imo- the value of corpsetype should always be determined the way it was in OTA. But meh, it works. The more fundamental problem of why I am seeing, for example, 4 projectiles when TinyExplosion calls for 1... still happening.

Ok, for future reference, for anybody who needs to know how this works in the future, if you're going to have a Killed() that is delayed even one frame, then the following functions properly:

Code: Select all

Killed(severity, corpsetype)
{
	if (severity <= 50)
	{
		corpsetype = 1;
		call-script TinyExplosion();	
		return (corpsetype);
	} else
	{	
		corpsetype = 2;
		call-script SmallExplosion();		
		return (corpsetype);
	}
	return(0);
}
Modify values for severity, etc. to set up the whole range of corpsetypes. The return value in the IF / THEN actually controls what Spring is going to use.
Last edited by Argh on 18 Dec 2007, 19:22, edited 1 time in total.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

It can't always work like in OTA because the reference is lost after one frame, only return works after that. We could change it to always use return but that'd break backwards compatibility.

My experiment:

Code: Select all

static-var time;
#include "exptype.h"

CLK() {
	while(1) {
		++time;
		sleep 1;
	}
}

Create() {
	start-script CLK();
	sleep 200;
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
	get PRINT(time);
}
output:

Code: Select all

Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
Value 1: 6, 2: 0, 3: 0, 4: 0
:?:

(output is twice as many lines as the code says because that's in a commander unit which gets spawned twice)
Post Reply

Return to “Engine”